Commit 4d4bbd7624

Jakub Konka <kubkon@jakubkonka.com>
2022-01-15 18:28:39
stage2: refactor handling of immediates in x86_64 backend
Fixes issues with incorrect operand sizes in a handful of cases and allows for usage of differently sized integers in Zig sources.
1 parent a5c7742
Changed files (4)
src
test
stage2
src/arch/x86_64/CodeGen.zig
@@ -497,14 +497,14 @@ fn gen(self: *Self) InnerError!void {
                 .ops = (Mir.Ops{
                     .reg1 = .rsp,
                 }).encode(),
-                .data = .{ .imm = @intCast(i32, aligned_stack_end) + stack_adjustment },
+                .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) },
             });
             self.mir_instructions.set(backpatch_stack_add, .{
                 .tag = .add,
                 .ops = (Mir.Ops{
                     .reg1 = .rsp,
                 }).encode(),
-                .data = .{ .imm = @intCast(i32, aligned_stack_end) + stack_adjustment },
+                .data = .{ .imm = @bitCast(u32, @intCast(i32, aligned_stack_end) + stack_adjustment) },
             });
         }
     } else {
@@ -1347,7 +1347,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
                             .reg2 = .rbp,
                             .flags = 0b01,
                         }).encode(),
-                        .data = .{ .imm = -@intCast(i32, off + 16) },
+                        .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + 16)) },
                     });
                     // add addr, offset
                     _ = try self.addInst(.{
@@ -1555,7 +1555,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
                     try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) });
 
                     return self.genInlineMemcpy(
-                        -@intCast(i32, off + abi_size),
+                        @bitCast(u32, -@intCast(i32, off + abi_size)),
                         registerAlias(addr_reg, @divExact(reg.size(), 8)),
                         count_reg.to64(),
                         tmp_reg.to8(),
@@ -1637,7 +1637,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
                             // introduce new MIR tag specifically for mov [reg + 0], imm
                             const payload = try self.addExtra(Mir.ImmPair{
                                 .dest_off = 0,
-                                .operand = @bitCast(i32, @intCast(u32, imm)),
+                                .operand = @truncate(u32, imm),
                             });
                             _ = try self.addInst(.{
                                 .tag = .mov_mem_imm,
@@ -1872,7 +1872,7 @@ fn genBinMathOpMir(
                         .ops = (Mir.Ops{
                             .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
                         }).encode(),
-                        .data = .{ .imm = @intCast(i32, imm) },
+                        .data = .{ .imm = @truncate(u32, imm) },
                     });
                 },
                 .embedded_in_code, .memory => {
@@ -1891,7 +1891,7 @@ fn genBinMathOpMir(
                             .reg2 = .rbp,
                             .flags = 0b01,
                         }).encode(),
-                        .data = .{ .imm = -@intCast(i32, adj_off) },
+                        .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) },
                     });
                 },
                 .compare_flags_unsigned => {
@@ -1926,7 +1926,7 @@ fn genBinMathOpMir(
                             .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
                             .flags = 0b10,
                         }).encode(),
-                        .data = .{ .imm = -@intCast(i32, adj_off) },
+                        .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) },
                     });
                 },
                 .immediate => |imm| {
@@ -1947,8 +1947,8 @@ fn genBinMathOpMir(
                         else => unreachable,
                     };
                     const payload = try self.addExtra(Mir.ImmPair{
-                        .dest_off = -@intCast(i32, adj_off),
-                        .operand = @bitCast(i32, @intCast(u32, imm)),
+                        .dest_off = @bitCast(u32, -@intCast(i32, adj_off)),
+                        .operand = @truncate(u32, imm),
                     });
                     _ = try self.addInst(.{
                         .tag = tag,
@@ -2015,7 +2015,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
                                 .reg2 = dst_reg.to32(),
                                 .flags = 0b10,
                             }).encode(),
-                            .data = .{ .imm = @intCast(i32, imm) },
+                            .data = .{ .imm = @truncate(u32, imm) },
                         });
                     } else {
                         // TODO verify we don't spill and assign to the same register as dst_mcv
@@ -2088,7 +2088,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
     const mcv = self.args[arg_index];
     const payload = try self.addExtra(Mir.ArgDbgInfo{
         .air_inst = inst,
-        .arg_index = @intCast(u32, arg_index), // TODO can arg_index: u32?
+        .arg_index = @truncate(u32, arg_index), // TODO can arg_index: u32?
     });
     _ = try self.addInst(.{
         .tag = .arg_dbg_info,
@@ -2196,7 +2196,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
                     .ops = (Mir.Ops{
                         .flags = 0b01,
                     }).encode(),
-                    .data = .{ .imm = @bitCast(i32, got_addr) },
+                    .data = .{ .imm = @truncate(u32, got_addr) },
                 });
             } else if (func_value.castTag(.extern_fn)) |_| {
                 return self.fail("TODO implement calling extern functions", .{});
@@ -3121,8 +3121,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                     // offset from rbp, which is at the top of the stack frame.
                     // mov [rbp+offset], immediate
                     const payload = try self.addExtra(Mir.ImmPair{
-                        .dest_off = -@intCast(i32, adj_off),
-                        .operand = @bitCast(i32, @intCast(u32, x_big)),
+                        .dest_off = @bitCast(u32, -@intCast(i32, adj_off)),
+                        .operand = @truncate(u32, x_big),
                     });
                     _ = try self.addInst(.{
                         .tag = .mov_mem_imm,
@@ -3147,8 +3147,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                     // insted just use two 32 bit writes to avoid register allocation
                     {
                         const payload = try self.addExtra(Mir.ImmPair{
-                            .dest_off = negative_offset + 4,
-                            .operand = @bitCast(i32, @truncate(u32, x_big >> 32)),
+                            .dest_off = @bitCast(u32, negative_offset + 4),
+                            .operand = @truncate(u32, x_big >> 32),
                         });
                         _ = try self.addInst(.{
                             .tag = .mov_mem_imm,
@@ -3161,8 +3161,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                     }
                     {
                         const payload = try self.addExtra(Mir.ImmPair{
-                            .dest_off = negative_offset,
-                            .operand = @bitCast(i32, @truncate(u32, x_big)),
+                            .dest_off = @bitCast(u32, negative_offset),
+                            .operand = @truncate(u32, x_big),
                         });
                         _ = try self.addInst(.{
                             .tag = .mov_mem_imm,
@@ -3192,7 +3192,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                     .reg2 = registerAlias(reg, @intCast(u32, abi_size)),
                     .flags = 0b10,
                 }).encode(),
-                .data = .{ .imm = -@intCast(i32, adj_off) },
+                .data = .{ .imm = @bitCast(u32, -@intCast(i32, adj_off)) },
             });
         },
         .memory, .embedded_in_code => {
@@ -3228,14 +3228,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                     .reg1 = addr_reg.to64(),
                     .reg2 = .rbp,
                 }).encode(),
-                .data = .{ .imm = -@intCast(i32, off + abi_size) },
+                .data = .{ .imm = @bitCast(u32, -@intCast(i32, off + abi_size)) },
             });
 
             // TODO allow for abi_size to be u64
             try self.genSetReg(Type.initTag(.u32), count_reg, .{ .immediate = @intCast(u32, abi_size) });
 
             return self.genInlineMemcpy(
-                -@intCast(i32, stack_offset + abi_size),
+                @bitCast(u32, -@intCast(i32, stack_offset + abi_size)),
                 addr_reg.to64(),
                 count_reg.to64(),
                 tmp_reg.to8(),
@@ -3246,7 +3246,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
 
 fn genInlineMemcpy(
     self: *Self,
-    stack_offset: i32,
+    stack_offset: u32,
     addr_reg: Register,
     count_reg: Register,
     tmp_reg: Register,
@@ -3361,7 +3361,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                     .reg1 = registerAlias(reg, @intCast(u32, ptr_abi_size)),
                     .reg2 = .rbp,
                 }).encode(),
-                .data = .{ .imm = -@intCast(i32, off) },
+                .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
             });
         },
         .ptr_embedded_in_code => unreachable,
@@ -3426,7 +3426,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                     .ops = (Mir.Ops{
                         .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
                     }).encode(),
-                    .data = .{ .imm = @intCast(i32, x) },
+                    .data = .{ .imm = @truncate(u32, x) },
                 });
                 return;
             }
@@ -3482,7 +3482,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                         .reg1 = reg,
                         .flags = 0b10,
                     }).encode(),
-                    .data = .{ .got_entry = @intCast(u32, x) },
+                    .data = .{ .got_entry = @truncate(u32, x) },
                 });
                 // MOV reg, [reg]
                 _ = try self.addInst(.{
@@ -3502,7 +3502,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                         .reg1 = reg,
                         .flags = 0b01,
                     }).encode(),
-                    .data = .{ .imm = @intCast(i32, x) },
+                    .data = .{ .imm = @truncate(u32, x) },
                 });
             } else {
                 // If this is RAX, we can use a direct load.
@@ -3561,7 +3561,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                     .reg2 = .rbp,
                     .flags = 0b01,
                 }).encode(),
-                .data = .{ .imm = -@intCast(i32, off) },
+                .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
             });
         },
     }
src/arch/x86_64/Isel.zig
@@ -483,17 +483,26 @@ inline fn setRexWRegister(reg: Register) bool {
     };
 }
 
-inline fn immOpSize(imm: i64) u8 {
-    blk: {
-        _ = math.cast(i8, imm) catch break :blk;
+inline fn immOpSize(u_imm: u32) u8 {
+    const imm = @bitCast(i32, u_imm);
+    if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) {
         return 8;
     }
-    blk: {
-        _ = math.cast(i16, imm) catch break :blk;
+    if (math.minInt(i16) <= imm and imm <= math.maxInt(i16)) {
         return 16;
     }
-    blk: {
-        _ = math.cast(i32, imm) catch break :blk;
+    return 32;
+}
+
+inline fn imm64OpSize(u_imm: u64) u8 {
+    const imm = @bitCast(i64, u_imm);
+    if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) {
+        return 8;
+    }
+    if (math.minInt(i16) <= imm and imm <= math.maxInt(i16)) {
+        return 16;
+    }
+    if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) {
         return 32;
     }
     return 64;
@@ -560,10 +569,10 @@ fn mirMovabs(isel: *Isel, inst: Mir.Inst.Index) InnerError!void {
     const tag = isel.mir.instructions.items(.tag)[inst];
     assert(tag == .movabs);
     const ops = Mir.Ops.decode(isel.mir.instructions.items(.ops)[inst]);
-    const imm: i64 = if (ops.reg1.size() == 64) blk: {
+    const imm: u64 = if (ops.reg1.size() == 64) blk: {
         const payload = isel.mir.instructions.items(.data)[inst].payload;
         const imm = isel.mir.extraData(Mir.Imm64, payload).data;
-        break :blk @bitCast(i64, imm.decode());
+        break :blk imm.decode();
     } else isel.mir.instructions.items(.data)[inst].imm;
     if (ops.flags == 0b00) {
         // movabs reg, imm64
@@ -1233,7 +1242,7 @@ const ScaleIndex = struct {
 const Memory = struct {
     base: ?Register,
     rip: bool = false,
-    disp: i32,
+    disp: u32,
     ptr_size: PtrSize,
     scale_index: ?ScaleIndex = null,
 
@@ -1283,7 +1292,7 @@ const Memory = struct {
                     } else {
                         encoder.sib_baseDisp8(dst);
                     }
-                    encoder.disp8(@intCast(i8, mem_op.disp));
+                    encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp)));
                 } else {
                     encoder.modRm_SIBDisp32(src);
                     if (mem_op.scale_index) |si| {
@@ -1291,17 +1300,17 @@ const Memory = struct {
                     } else {
                         encoder.sib_baseDisp32(dst);
                     }
-                    encoder.disp32(mem_op.disp);
+                    encoder.disp32(@bitCast(i32, mem_op.disp));
                 }
             } else {
                 if (mem_op.disp == 0) {
                     encoder.modRm_indirectDisp0(src, dst);
                 } else if (immOpSize(mem_op.disp) == 8) {
                     encoder.modRm_indirectDisp8(src, dst);
-                    encoder.disp8(@intCast(i8, mem_op.disp));
+                    encoder.disp8(@bitCast(i8, @truncate(u8, mem_op.disp)));
                 } else {
                     encoder.modRm_indirectDisp32(src, dst);
-                    encoder.disp32(mem_op.disp);
+                    encoder.disp32(@bitCast(i32, mem_op.disp));
                 }
             }
         } else {
@@ -1315,16 +1324,16 @@ const Memory = struct {
                     encoder.sib_disp32();
                 }
             }
-            encoder.disp32(mem_op.disp);
+            encoder.disp32(@bitCast(i32, mem_op.disp));
         }
     }
 };
 
-fn encodeImm(encoder: Encoder, imm: i32, size: u64) void {
+fn encodeImm(encoder: Encoder, imm: u32, size: u64) void {
     switch (size) {
-        8 => encoder.imm8(@intCast(i8, imm)),
-        16 => encoder.imm16(@intCast(i16, imm)),
-        32, 64 => encoder.imm32(imm),
+        8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))),
+        16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))),
+        32, 64 => encoder.imm32(@bitCast(i32, imm)),
         else => unreachable,
     }
 }
@@ -1338,7 +1347,7 @@ const RegisterOrMemory = union(enum) {
     }
 
     fn mem(ptr_size: Memory.PtrSize, args: struct {
-        disp: i32,
+        disp: u32,
         base: ?Register = null,
         scale_index: ?ScaleIndex = null,
     }) RegisterOrMemory {
@@ -1352,7 +1361,7 @@ const RegisterOrMemory = union(enum) {
         };
     }
 
-    fn rip(ptr_size: Memory.PtrSize, disp: i32) RegisterOrMemory {
+    fn rip(ptr_size: Memory.PtrSize, disp: u32) RegisterOrMemory {
         return .{
             .memory = .{
                 .base = null,
@@ -1377,12 +1386,12 @@ fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) LoweringError!void {
     opc.encode(encoder);
 }
 
-fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) LoweringError!void {
+fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {
     if (tag == .ret_far or tag == .ret_near) {
         const encoder = try Encoder.init(code, 3);
         const opc = getOpCode(tag, .i, false).?;
         opc.encode(encoder);
-        encoder.imm16(@intCast(i16, imm));
+        encoder.imm16(@bitCast(i16, @truncate(u16, imm)));
         return;
     }
     const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?;
@@ -1410,11 +1419,11 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) LoweringError!
     opc.encodeWithReg(encoder, reg);
 }
 
-fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) LoweringError!void {
+fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {
     const opc = getOpCode(tag, .d, false).?;
     const encoder = try Encoder.init(code, 6);
     opc.encode(encoder);
-    encoder.imm32(imm);
+    encoder.imm32(@bitCast(i32, imm));
 }
 
 fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) LoweringError!void {
@@ -1467,19 +1476,19 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
     }
 }
 
-fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) LoweringError!void {
+fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) LoweringError!void {
     return lowerToTdFdEnc(tag, reg, moffs, code, true);
 }
 
-fn lowerToFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8)) LoweringError!void {
+fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) LoweringError!void {
     return lowerToTdFdEnc(tag, reg, moffs, code, false);
 }
 
-fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), td: bool) LoweringError!void {
+fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) LoweringError!void {
     if (reg.lowId() != Register.rax.lowId()) {
         return error.RaxOperandExpected;
     }
-    if (reg.size() != immOpSize(moffs)) {
+    if (reg.size() != imm64OpSize(moffs)) {
         return error.OperandSizeMismatch;
     }
     const opc = if (td)
@@ -1495,27 +1504,16 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8),
     });
     opc.encode(encoder);
     switch (reg.size()) {
-        8 => {
-            const moffs8 = try math.cast(i8, moffs);
-            encoder.imm8(moffs8);
-        },
-        16 => {
-            const moffs16 = try math.cast(i16, moffs);
-            encoder.imm16(moffs16);
-        },
-        32 => {
-            const moffs32 = try math.cast(i32, moffs);
-            encoder.imm32(moffs32);
-        },
-        64 => {
-            encoder.imm64(@bitCast(u64, moffs));
-        },
+        8 => encoder.imm8(@bitCast(i8, @truncate(u8, moffs))),
+        16 => encoder.imm16(@bitCast(i16, @truncate(u16, moffs))),
+        32 => encoder.imm32(@bitCast(i32, @truncate(u32, moffs))),
+        64 => encoder.imm64(moffs),
         else => unreachable,
     }
 }
 
-fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) LoweringError!void {
-    if (reg.size() != immOpSize(imm)) {
+fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) LoweringError!void {
+    if (reg.size() != imm64OpSize(imm)) {
         return error.OperandSizeMismatch;
     }
     const opc = getOpCode(tag, .oi, reg.size() == 8).?;
@@ -1529,26 +1527,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Low
     });
     opc.encodeWithReg(encoder, reg);
     switch (reg.size()) {
-        8 => {
-            const imm8 = try math.cast(i8, imm);
-            encoder.imm8(imm8);
-        },
-        16 => {
-            const imm16 = try math.cast(i16, imm);
-            encoder.imm16(imm16);
-        },
-        32 => {
-            const imm32 = try math.cast(i32, imm);
-            encoder.imm32(imm32);
-        },
-        64 => {
-            encoder.imm64(@bitCast(u64, imm));
-        },
+        8 => encoder.imm8(@bitCast(i8, @truncate(u8, imm))),
+        16 => encoder.imm16(@bitCast(i16, @truncate(u16, imm))),
+        32 => encoder.imm32(@bitCast(i32, @truncate(u32, imm))),
+        64 => encoder.imm64(imm),
         else => unreachable,
     }
 }
 
-fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) LoweringError!void {
+fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) LoweringError!void {
     const modrm_ext = getModRmExt(tag).?;
     switch (reg_or_mem) {
         .register => |dst_reg| {
@@ -1700,7 +1687,7 @@ fn lowerToRmiEnc(
     tag: Tag,
     reg: Register,
     reg_or_mem: RegisterOrMemory,
-    imm: i32,
+    imm: u32,
     code: *std.ArrayList(u8),
 ) LoweringError!void {
     if (reg.size() == 8) {
@@ -1804,7 +1791,10 @@ test "lower MI encoding" {
     try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", isel.lowered(), "mov rax, 0x10");
     try lowerToMiEnc(.mov, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0, .base = .r11 }), 0x10, isel.code());
     try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", isel.lowered(), "mov dword ptr [r11 + 0], 0x10");
-    try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = -8, .base = .rdx }), 0x10, isel.code());
+    try lowerToMiEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{
+        .disp = @bitCast(u32, @as(i32, -8)),
+        .base = .rdx,
+    }), 0x10, isel.code());
     try expectEqualHexStrings("\x81\x42\xF8\x10\x00\x00\x00", isel.lowered(), "add dword ptr [rdx - 8], 0x10");
     try lowerToMiEnc(.sub, RegisterOrMemory.mem(.dword_ptr, .{
         .disp = 0x10000000,
@@ -1836,15 +1826,24 @@ test "lower MI encoding" {
         isel.lowered(),
         "mov qword ptr [rip + 0x10], 0x10",
     );
-    try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -8, .base = .rbp }), 0x10, isel.code());
+    try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
+        .disp = @bitCast(u32, @as(i32, -8)),
+        .base = .rbp,
+    }), 0x10, isel.code());
     try expectEqualHexStrings(
         "\x48\xc7\x45\xf8\x10\x00\x00\x00",
         isel.lowered(),
         "mov qword ptr [rbp - 8], 0x10",
     );
-    try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{ .disp = -2, .base = .rbp }), 0x10, isel.code());
+    try lowerToMiEnc(.mov, RegisterOrMemory.mem(.word_ptr, .{
+        .disp = @bitCast(u32, @as(i32, -2)),
+        .base = .rbp,
+    }), 0x10, isel.code());
     try expectEqualHexStrings("\x66\xC7\x45\xFE\x10\x00", isel.lowered(), "mov word ptr [rbp - 2], 0x10");
-    try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{ .disp = -1, .base = .rbp }), 0x10, isel.code());
+    try lowerToMiEnc(.mov, RegisterOrMemory.mem(.byte_ptr, .{
+        .disp = @bitCast(u32, @as(i32, -1)),
+        .base = .rbp,
+    }), 0x10, isel.code());
     try expectEqualHexStrings("\xC6\x45\xFF\x10", isel.lowered(), "mov byte ptr [rbp - 1], 0x10");
     try lowerToMiEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
         .disp = 0x10000000,
@@ -1897,12 +1896,15 @@ test "lower RM encoding" {
         isel.lowered(),
         "sub r11, qword ptr [r12 + 0x10000000]",
     );
-    try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -4, .base = .rbp }), isel.code());
+    try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
+        .disp = @bitCast(u32, @as(i32, -4)),
+        .base = .rbp,
+    }), isel.code());
     try expectEqualHexStrings("\x48\x8B\x45\xFC", isel.lowered(), "mov rax, qword ptr [rbp - 4]");
     try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), isel.code());
     try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", isel.lowered(), "lea rax, [rip + 0x10]");
     try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
-        .disp = -8,
+        .disp = @bitCast(u32, @as(i32, -8)),
         .base = .rbp,
         .scale_index = .{
             .scale = 0,
@@ -1911,7 +1913,7 @@ test "lower RM encoding" {
     }), isel.code());
     try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]");
     try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{
-        .disp = -4,
+        .disp = @bitCast(u32, @as(i32, -4)),
         .base = .rbp,
         .scale_index = .{
             .scale = 2,
@@ -1920,7 +1922,7 @@ test "lower RM encoding" {
     }), isel.code());
     try expectEqualHexStrings("\x8B\x44\x95\xFC", isel.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]");
     try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{
-        .disp = -8,
+        .disp = @bitCast(u32, @as(i32, -8)),
         .base = .rbp,
         .scale_index = .{
             .scale = 3,
@@ -1929,7 +1931,7 @@ test "lower RM encoding" {
     }), isel.code());
     try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", isel.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]");
     try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{
-        .disp = -24,
+        .disp = @bitCast(u32, @as(i32, -24)),
         .base = .rsi,
         .scale_index = .{
             .scale = 0,
@@ -1953,7 +1955,10 @@ test "lower MR encoding" {
     defer isel.deinit();
     try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, isel.code());
     try expectEqualHexStrings("\x48\x89\xd8", isel.lowered(), "mov rax, rbx");
-    try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = -4, .base = .rbp }), .r11, isel.code());
+    try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{
+        .disp = @bitCast(u32, @as(i32, -4)),
+        .base = .rbp,
+    }), .r11, isel.code());
     try expectEqualHexStrings("\x4c\x89\x5d\xfc", isel.lowered(), "mov qword ptr [rbp - 4], r11");
     try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, isel.code());
     try expectEqualHexStrings(
@@ -2063,7 +2068,7 @@ test "lower RMI encoding" {
     var isel = TestIsel.init();
     defer isel.deinit();
     try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{
-        .disp = -8,
+        .disp = @bitCast(u32, @as(i32, -8)),
         .base = .rbp,
     }), 0x10, isel.code());
     try expectEqualHexStrings(
@@ -2072,12 +2077,12 @@ test "lower RMI encoding" {
         "imul rax, qword ptr [rbp - 8], 0x10",
     );
     try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{
-        .disp = -4,
+        .disp = @bitCast(u32, @as(i32, -4)),
         .base = .rbp,
     }), 0x10, isel.code());
     try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", isel.lowered(), "imul eax, dword ptr [rbp - 4], 0x10");
     try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{
-        .disp = -2,
+        .disp = @bitCast(u32, @as(i32, -2)),
         .base = .rbp,
     }), 0x10, isel.code());
     try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", isel.lowered(), "imul ax, word ptr [rbp - 2], 0x10");
src/arch/x86_64/Mir.zig
@@ -302,7 +302,7 @@ pub const Inst = struct {
         /// Another instruction.
         inst: Index,
         /// A 32-bit immediate value.
-        imm: i32,
+        imm: u32,
         /// An extern function.
         /// Index into the linker's string table.
         extern_fn: u32,
@@ -324,8 +324,8 @@ pub const Inst = struct {
 };
 
 pub const ImmPair = struct {
-    dest_off: i32,
-    operand: i32,
+    dest_off: u32,
+    operand: u32,
 };
 
 pub const Imm64 = struct {
test/stage2/x86_64.zig
@@ -1700,6 +1700,36 @@ pub fn addCases(ctx: *TestContext) !void {
                 \\    if (!ok) unreachable;
                 \\}
             , "");
+            case.addCompareOutput(
+                \\pub fn main() void {
+                \\    var x: u16 = undefined;
+                \\    set(&x);
+                \\    assert(x == 123);
+                \\}
+                \\
+                \\fn set(x: *u16) void {
+                \\    x.* = 123;
+                \\}
+                \\
+                \\fn assert(ok: bool) void {
+                \\    if (!ok) unreachable;
+                \\}
+            , "");
+            case.addCompareOutput(
+                \\pub fn main() void {
+                \\    var x: u8 = undefined;
+                \\    set(&x);
+                \\    assert(x == 123);
+                \\}
+                \\
+                \\fn set(x: *u8) void {
+                \\    x.* = 123;
+                \\}
+                \\
+                \\fn assert(ok: bool) void {
+                \\    if (!ok) unreachable;
+                \\}
+            , "");
         }
 
         {