authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-02 13:46:34+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-02 14:05:29+01:00
logd35cae551ed5f3e6082b2e599f9c258af9d2630e
treea95a575a2c8bffd80a9d09de50d339460108e51d
parent2e327d9b63036abf2556e21693350a5169b888fb

x64: rectify and add missing optionals bits

Includes changes/additions to: * `wrap_optional` * `optional_payload` * `isNull` helper

7 files changed, 59 insertions(+), 19 deletions(-)

src/arch/x86_64/CodeGen.zig+58-10
...@@ -1704,12 +1704,36 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1704,12 +1704,36 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void {
17041704
1705fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {1705fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1706 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1706 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1707 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1707 if (self.liveness.isUnused(inst)) {
1708 const operand = try self.resolveInst(ty_op.operand);1708 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1709 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {1709 }
1710 break :result operand;1710
1711 const payload_ty = self.air.typeOfIndex(inst);
1712 const optional_ty = self.air.typeOf(ty_op.operand);
1713 const operand = try self.resolveInst(ty_op.operand);
1714 const result: MCValue = result: {
1715 if (!payload_ty.hasRuntimeBits()) break :result MCValue.none;
1716 if (optional_ty.isPtrLikeOptional()) {
1717 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1718 break :result operand;
1719 }
1720 break :result try self.copyToRegisterWithInstTracking(inst, payload_ty, operand);
1721 }
1722
1723 const offset = optional_ty.abiSize(self.target.*) - payload_ty.abiSize(self.target.*);
1724 switch (operand) {
1725 .stack_offset => |off| {
1726 break :result MCValue{ .stack_offset = off - @intCast(i32, offset) };
1727 },
1728 .register => {
1729 // TODO reuse the operand
1730 const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand);
1731 const shift = @intCast(u8, offset * 8);
1732 try self.shiftRegister(result.register, @intCast(u8, shift));
1733 break :result result;
1734 },
1735 else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}),
1711 }1736 }
1712 break :result try self.copyToRegisterWithInstTracking(inst, self.air.typeOfIndex(inst), operand);
1713 };1737 };
1714 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1738 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1715}1739}
...@@ -1828,13 +1852,30 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1828,13 +1852,30 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
1828fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {1852fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1829 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1853 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1830 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1854 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1855 const payload_ty = self.air.typeOf(ty_op.operand);
1856 if (!payload_ty.hasRuntimeBits()) {
1857 break :result MCValue{ .immediate = 1 };
1858 }
1859
1831 const optional_ty = self.air.typeOfIndex(inst);1860 const optional_ty = self.air.typeOfIndex(inst);
1861 const operand = try self.resolveInst(ty_op.operand);
1862 if (optional_ty.isPtrLikeOptional()) {
1863 // TODO should we check if we can reuse the operand?
1864 break :result operand;
1865 }
18321866
1833 // Optional with a zero-bit payload type is just a boolean true1867 operand.freezeIfRegister(&self.register_manager);
1834 if (optional_ty.abiSize(self.target.*) == 1)1868 defer operand.unfreezeIfRegister(&self.register_manager);
1835 break :result MCValue{ .immediate = 1 };
18361869
1837 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});1870 const optional_abi_size = @intCast(u32, optional_ty.abiSize(self.target.*));
1871 const optional_abi_align = optional_ty.abiAlignment(self.target.*);
1872 const payload_abi_size = @intCast(u32, payload_ty.abiSize(self.target.*));
1873 const offset = optional_abi_size - payload_abi_size;
1874
1875 const stack_offset = @intCast(i32, try self.allocMem(inst, optional_abi_size, optional_abi_align));
1876 try self.genSetStack(Type.bool, stack_offset, .{ .immediate = 1 }, .{});
1877 try self.genSetStack(payload_ty, stack_offset - @intCast(i32, offset), operand, .{});
1878 break :result MCValue{ .stack_offset = stack_offset };
1838 };1879 };
1839 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1880 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1840}1881}
...@@ -3761,7 +3802,14 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu...@@ -3761,7 +3802,14 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu
3761 try self.spillCompareFlagsIfOccupied();3802 try self.spillCompareFlagsIfOccupied();
3762 self.compare_flags_inst = inst;3803 self.compare_flags_inst = inst;
37633804
3764 try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 });3805 const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: {
3806 var buf: Type.Payload.ElemType = undefined;
3807 const payload_ty = ty.optionalChild(&buf);
3808 break :blk if (payload_ty.hasRuntimeBits()) Type.bool else ty;
3809 } else ty;
3810
3811 try self.genBinMathOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 });
3812
3765 return MCValue{ .compare_flags_unsigned = .eq };3813 return MCValue{ .compare_flags_unsigned = .eq };
3766}3814}
37673815
test/behavior/basic.zig-1
...@@ -728,7 +728,6 @@ test "thread local variable" {...@@ -728,7 +728,6 @@ test "thread local variable" {
728test "result location is optional inside error union" {728test "result location is optional inside error union" {
729 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO729 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
730 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO730 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
731 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
732 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO731 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
733 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO732 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
734733
test/behavior/bugs/2889.zig-1
...@@ -27,7 +27,6 @@ fn parseNote() ?i32 {...@@ -27,7 +27,6 @@ fn parseNote() ?i32 {
27}27}
2828
29test "fixed" {29test "fixed" {
30 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
31 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;30 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
32 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;31 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3332
test/behavior/bugs/3112.zig-1
...@@ -16,7 +16,6 @@ test "zig test crash" {...@@ -16,7 +16,6 @@ test "zig test crash" {
16 if (builtin.zig_backend == .stage1) return error.SkipZigTest;16 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;17 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
18 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;18 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
19 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
20 var global: State = undefined;19 var global: State = undefined;
21 global.enter = prev;20 global.enter = prev;
22 global.enter(null);21 global.enter(null);
test/behavior/cast.zig+1-1
...@@ -237,7 +237,7 @@ test "@intCast to u0 and use the result" {...@@ -237,7 +237,7 @@ test "@intCast to u0 and use the result" {
237237
238test "peer result null and comptime_int" {238test "peer result null and comptime_int" {
239 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;239 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
240 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;240 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
241241
242 const S = struct {242 const S = struct {
243 fn blah(n: i32) ?i32 {243 fn blah(n: i32) ?i32 {
test/behavior/enum.zig-1
...@@ -1083,7 +1083,6 @@ test "@tagName on enum literals" {...@@ -1083,7 +1083,6 @@ test "@tagName on enum literals" {
1083}1083}
10841084
1085test "enum literal casting to optional" {1085test "enum literal casting to optional" {
1086 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1087 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;1086 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1088 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1087 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10891088
test/behavior/optional.zig-4
...@@ -7,7 +7,6 @@ const expectEqual = testing.expectEqual;...@@ -7,7 +7,6 @@ const expectEqual = testing.expectEqual;
7test "passing an optional integer as a parameter" {7test "passing an optional integer as a parameter" {
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1110
12 const S = struct {11 const S = struct {
13 fn entry() bool {12 fn entry() bool {
...@@ -60,7 +59,6 @@ fn testNullPtrsEql() !void {...@@ -60,7 +59,6 @@ fn testNullPtrsEql() !void {
60test "optional with void type" {59test "optional with void type" {
61 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;60 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
62 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO61 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
63 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
6462
65 const Foo = struct {63 const Foo = struct {
66 x: ?void,64 x: ?void,
...@@ -171,7 +169,6 @@ test "unwrap function call with optional pointer return value" {...@@ -171,7 +169,6 @@ test "unwrap function call with optional pointer return value" {
171test "nested orelse" {169test "nested orelse" {
172 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;170 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
173 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO171 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
174 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
175172
176 const S = struct {173 const S = struct {
177 fn entry() !void {174 fn entry() !void {
...@@ -199,7 +196,6 @@ test "self-referential struct through a slice of optional" {...@@ -199,7 +196,6 @@ test "self-referential struct through a slice of optional" {
199 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;196 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
200 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO197 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
201 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO198 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
202 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
203199
204 const S = struct {200 const S = struct {
205 const Node = struct {201 const Node = struct {