| author | |
| committer | |
| log | d35cae551ed5f3e6082b2e599f9c258af9d2630e |
| tree | a95a575a2c8bffd80a9d09de50d339460108e51d |
| parent | 2e327d9b63036abf2556e21693350a5169b888fb |
Includes changes/additions to:
* `wrap_optional`
* `optional_payload`
* `isNull` helper7 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 { |
| 1704 | 1704 | ||
| 1705 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | 1705 | fn 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 { |
| 1828 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { | 1852 | fn 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 | } | ||
| 1832 | 1866 | ||
| 1833 | // Optional with a zero-bit payload type is just a boolean true | 1867 | 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 }; | ||
| 1836 | 1869 | ||
| 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; |
| 3763 | 3804 | ||
| 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 | } |
| 3767 | 3815 |
test/behavior/basic.zig-1| ... | @@ -728,7 +728,6 @@ test "thread local variable" { | ... | @@ -728,7 +728,6 @@ test "thread local variable" { |
| 728 | test "result location is optional inside error union" { | 728 | test "result location is optional inside error union" { |
| 729 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 729 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 730 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 730 | 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; // TODO | 731 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 733 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 732 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 734 | 733 |
test/behavior/bugs/2889.zig-1| ... | @@ -27,7 +27,6 @@ fn parseNote() ?i32 { | ... | @@ -27,7 +27,6 @@ fn parseNote() ?i32 { |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | test "fixed" { | 29 | test "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; |
| 33 | 32 |
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" { |
| 237 | 237 | ||
| 238 | test "peer result null and comptime_int" { | 238 | test "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; |
| 241 | 241 | ||
| 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 | } |
| 1084 | 1084 | ||
| 1085 | test "enum literal casting to optional" { | 1085 | test "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; |
| 1089 | 1088 |
test/behavior/optional.zig-4| ... | @@ -7,7 +7,6 @@ const expectEqual = testing.expectEqual; | ... | @@ -7,7 +7,6 @@ const expectEqual = testing.expectEqual; |
| 7 | test "passing an optional integer as a parameter" { | 7 | test "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; // TODO | 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 11 | 10 | ||
| 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 { |
| 60 | test "optional with void type" { | 59 | test "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; // TODO | 61 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 63 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 64 | 62 | ||
| 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" { |
| 171 | test "nested orelse" { | 169 | test "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; // TODO | 171 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 174 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 175 | 172 | ||
| 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; // TODO | 197 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 201 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 198 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 202 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 203 | 199 | ||
| 204 | const S = struct { | 200 | const S = struct { |
| 205 | const Node = struct { | 201 | const Node = struct { |