authorgravatar for 81774659+gracefuu@users.noreply.github.comgracefu <81774659+gracefuu@users.noreply.github.com> 2021-04-09 14:05:53+08:00
committergravatar for 81774659+gracefuu@users.noreply.github.comgracefu <81774659+gracefuu@users.noreply.github.com> 2021-04-16 15:21:17+08:00
log5bd464e386df35bfe38b062190074ce3c2689001
treeb6f8a6f4b6e82814a8a20d7975cd9f1ce5f9b8de
parent36df1526da0e703a9f3d5bd6c8775d3f0e0f0a33
signaturelock-open Commit is signed but in an unrecognized format.

stage2 x86_64: use abi size to determine 64-bit operation

From my very cursory reading, it seems that the register manager doesn't distinguish between registers that are physically the same but have different sizes. In that case, this means that during codegen, we can't rely on `reg.size()` when determining the width of the operations we have to perform. Instead, we must use some form of `ty.abiSize(self.target.*)` to determine the size of the type we're operating with. If this size is 64 bits, then we should enable 64-bit operation. This fixed a bug in the codegen for spilling instructions, which was overwriting the previous stack entry with zeroes. See the modified test case in this commit.

2 files changed, 28 insertions(+), 26 deletions(-)

src/codegen.zig+13-13
...@@ -1687,7 +1687,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1687,7 +1687,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1687 .register => |src_reg| {1687 .register => |src_reg| {
1688 // register, register use mr + 1 addressing mode: r/m16/32/64, r16/32/641688 // register, register use mr + 1 addressing mode: r/m16/32/64, r16/32/64
1689 try self.encodeX8664Instruction(src, Instruction{1689 try self.encodeX8664Instruction(src, Instruction{
1690 .operand_size_64 = dst_reg.size() == 64,1690 .operand_size_64 = dst_ty.abiSize(self.target.*) == 64,
1691 .primary_opcode_1b = mr + 1,1691 .primary_opcode_1b = mr + 1,
1692 // TODO: Explicit optional wrap due to stage 1 miscompilation :(1692 // TODO: Explicit optional wrap due to stage 1 miscompilation :(
1693 // https://github.com/ziglang/zig/issues/65151693 // https://github.com/ziglang/zig/issues/6515
...@@ -1705,7 +1705,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1705,7 +1705,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1705 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.1705 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
1706 if (imm32 <= math.maxInt(u7)) {1706 if (imm32 <= math.maxInt(u7)) {
1707 try self.encodeX8664Instruction(src, Instruction{1707 try self.encodeX8664Instruction(src, Instruction{
1708 .operand_size_64 = dst_reg.size() == 64,1708 .operand_size_64 = dst_ty.abiSize(self.target.*) == 64,
1709 .primary_opcode_1b = 0x83,1709 .primary_opcode_1b = 0x83,
1710 .opcode_extension = opx,1710 .opcode_extension = opx,
1711 // TODO: Explicit optional wrap due to stage 1 miscompilation :(1711 // TODO: Explicit optional wrap due to stage 1 miscompilation :(
...@@ -1719,7 +1719,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1719,7 +1719,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1719 });1719 });
1720 } else {1720 } else {
1721 try self.encodeX8664Instruction(src, Instruction{1721 try self.encodeX8664Instruction(src, Instruction{
1722 .operand_size_64 = dst_reg.size() == 64,1722 .operand_size_64 = dst_ty.abiSize(self.target.*) == 64,
1723 .primary_opcode_1b = 0x81,1723 .primary_opcode_1b = 0x81,
1724 .opcode_extension = opx,1724 .opcode_extension = opx,
1725 // TODO: Explicit optional wrap due to stage 1 miscompilation :(1725 // TODO: Explicit optional wrap due to stage 1 miscompilation :(
...@@ -1743,7 +1743,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1743,7 +1743,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1743 return self.fail(src, "stack offset too large", .{});1743 return self.fail(src, "stack offset too large", .{});
1744 }1744 }
1745 try self.encodeX8664Instruction(src, Instruction{1745 try self.encodeX8664Instruction(src, Instruction{
1746 .operand_size_64 = dst_reg.size() == 64,1746 .operand_size_64 = abi_size == 64,
1747 .primary_opcode_1b = mr + 0x3,1747 .primary_opcode_1b = mr + 0x3,
1748 .reg = dst_reg,1748 .reg = dst_reg,
1749 // TODO: Explicit optional wrap due to stage 1 miscompilation :(1749 // TODO: Explicit optional wrap due to stage 1 miscompilation :(
...@@ -1802,7 +1802,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1802,7 +1802,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1802 return self.fail(src, "stack offset too large", .{});1802 return self.fail(src, "stack offset too large", .{});
1803 }1803 }
1804 try self.encodeX8664Instruction(src, Instruction{1804 try self.encodeX8664Instruction(src, Instruction{
1805 .operand_size_64 = reg.size() == 64,1805 .operand_size_64 = abi_size == 64,
1806 .primary_opcode_1b = opcode,1806 .primary_opcode_1b = opcode,
1807 .reg = reg,1807 .reg = reg,
1808 // TODO: Explicit optional wrap due to stage 1 miscompilation :(1808 // TODO: Explicit optional wrap due to stage 1 miscompilation :(
...@@ -3707,7 +3707,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3707,7 +3707,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37073707
3708 // This is a variant of 8B /r.3708 // This is a variant of 8B /r.
3709 try self.encodeX8664Instruction(src, Instruction{3709 try self.encodeX8664Instruction(src, Instruction{
3710 .operand_size_64 = reg.size() == 64,3710 .operand_size_64 = ty.abiSize(self.target.*) == 64,
37113711
3712 .primary_opcode_1b = 0x8B,3712 .primary_opcode_1b = 0x8B,
37133713
...@@ -3740,7 +3740,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3740,7 +3740,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3740 // LEA reg, [<offset>]3740 // LEA reg, [<offset>]
3741 // manually do this instruction to make sure the offset into the disp32 field won't change.3741 // manually do this instruction to make sure the offset into the disp32 field won't change.
3742 try self.code.ensureCapacity(self.code.items.len + 7);3742 try self.code.ensureCapacity(self.code.items.len + 7);
3743 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });3743 self.rex(.{ .w = ty.abiSize(self.target.*) == 64, .r = reg.isExtended() });
3744 self.code.appendSliceAssumeCapacity(&[_]u8{3744 self.code.appendSliceAssumeCapacity(&[_]u8{
3745 0x8D,3745 0x8D,
3746 0x05 | (@as(u8, reg.id() & 0b111) << 3),3746 0x05 | (@as(u8, reg.id() & 0b111) << 3),
...@@ -3749,7 +3749,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3749,7 +3749,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37493749
3750 // MOV reg, [reg]3750 // MOV reg, [reg]
3751 try self.encodeX8664Instruction(src, Instruction{3751 try self.encodeX8664Instruction(src, Instruction{
3752 .operand_size_64 = reg.size() == 64,3752 .operand_size_64 = ty.abiSize(self.target.*) == 64,
37533753
3754 .primary_opcode_1b = 0x8B,3754 .primary_opcode_1b = 0x8B,
37553755
...@@ -3771,7 +3771,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3771,7 +3771,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3771 // 0b00RRR100, where RRR is the lower three bits of the register ID.3771 // 0b00RRR100, where RRR is the lower three bits of the register ID.
3772 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.3772 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
3773 try self.code.ensureCapacity(self.code.items.len + 8);3773 try self.code.ensureCapacity(self.code.items.len + 8);
3774 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });3774 self.rex(.{ .w = ty.abiSize(self.target.*) == 64, .r = reg.isExtended() });
3775 self.code.appendSliceAssumeCapacity(&[_]u8{3775 self.code.appendSliceAssumeCapacity(&[_]u8{
3776 0x8B,3776 0x8B,
3777 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R3777 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
...@@ -3809,7 +3809,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3809,7 +3809,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3809 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.3809 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
3810 // TODO: determine whether to allow other sized registers, and if so, handle them properly.3810 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
3811 try self.encodeX8664Instruction(src, Instruction{3811 try self.encodeX8664Instruction(src, Instruction{
3812 .operand_size_64 = reg.size() == 64,3812 .operand_size_64 = ty.abiSize(self.target.*) == 64,
3813 .primary_opcode_1b = 0x8B,3813 .primary_opcode_1b = 0x8B,
3814 .reg = reg,3814 .reg = reg,
3815 // TODO: Explicit optional wrap due to stage 1 miscompilation :(3815 // TODO: Explicit optional wrap due to stage 1 miscompilation :(
...@@ -3823,14 +3823,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3823,14 +3823,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3823 }3823 }
3824 },3824 },
3825 .stack_offset => |unadjusted_off| {3825 .stack_offset => |unadjusted_off| {
3826 const size_bytes = @divExact(reg.size(), 8);3826 const abi_size = ty.abiSize(self.target.*);
3827 const off = unadjusted_off + size_bytes;3827 const off = unadjusted_off + abi_size;
3828 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {3828 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
3829 return self.fail(src, "stack offset too large", .{});3829 return self.fail(src, "stack offset too large", .{});
3830 }3830 }
3831 const ioff = -@intCast(i32, off);3831 const ioff = -@intCast(i32, off);
3832 try self.encodeX8664Instruction(src, Instruction{3832 try self.encodeX8664Instruction(src, Instruction{
3833 .operand_size_64 = reg.size() == 64,3833 .operand_size_64 = ty.abiSize(self.target.*) == 64,
3834 .primary_opcode_1b = 0x8B,3834 .primary_opcode_1b = 0x8B,
3835 .reg = reg,3835 .reg = reg,
3836 // TODO: Explicit optional wrap due to stage 1 miscompilation :(3836 // TODO: Explicit optional wrap due to stage 1 miscompilation :(
test/stage2/test.zig+15-13
...@@ -740,7 +740,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -740,7 +740,7 @@ pub fn addCases(ctx: *TestContext) !void {
740 // Spilling registers to the stack.740 // Spilling registers to the stack.
741 case.addCompareOutput(741 case.addCompareOutput(
742 \\export fn _start() noreturn {742 \\export fn _start() noreturn {
743 \\ assert(add(3, 4) == 791);743 \\ assert(add(3, 4) == 1221);
744 \\744 \\
745 \\ exit();745 \\ exit();
746 \\}746 \\}
...@@ -756,19 +756,21 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -756,19 +756,21 @@ pub fn addCases(ctx: *TestContext) !void {
756 \\ const i = g + h; // 100756 \\ const i = g + h; // 100
757 \\ const j = i + d; // 110757 \\ const j = i + d; // 110
758 \\ const k = i + j; // 210758 \\ const k = i + j; // 210
759 \\ const l = k + c; // 217759 \\ const l = j + k; // 320
760 \\ const m = l + d; // 227760 \\ const m = l + c; // 327
761 \\ const n = m + e; // 241761 \\ const n = m + d; // 337
762 \\ const o = n + f; // 265762 \\ const o = n + e; // 351
763 \\ const p = o + g; // 303763 \\ const p = o + f; // 375
764 \\ const q = p + h; // 365764 \\ const q = p + g; // 413
765 \\ const r = q + i; // 465765 \\ const r = q + h; // 475
766 \\ const s = r + j; // 575766 \\ const s = r + i; // 575
767 \\ const t = s + k; // 785767 \\ const t = s + j; // 685
768 \\ break :blk t;768 \\ const u = t + k; // 895
769 \\ const v = u + l; // 1215
770 \\ break :blk v;
769 \\ };771 \\ };
770 \\ const y = x + a; // 788772 \\ const y = x + a; // 1218
771 \\ const z = y + a; // 791773 \\ const z = y + a; // 1221
772 \\ return z;774 \\ return z;
773 \\}775 \\}
774 \\776 \\