authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-04-15 19:17:50+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-04-15 19:17:50+03:00
log7be62f695ffb468e19241a564d544d6a0adab829
tree77d3704190ff9dde13a05767d6214a86136f7bcb
parentdbe0d3d5790d9e21cf42696d4cea8cc477207592

stage2 llvm: fix optional pointers to zero bit payloads


3 files changed, 61 insertions(+), 9 deletions(-)

lib/std/special/c.zig+2-4
...@@ -32,10 +32,8 @@ comptime {...@@ -32,10 +32,8 @@ comptime {
32 @export(wasm_start, .{ .name = "_start", .linkage = .Strong });32 @export(wasm_start, .{ .name = "_start", .linkage = .Strong });
33 }33 }
3434
35 if (builtin.zig_backend == .stage1) { // TODO remove this condition35 if (native_os == .linux) {
36 if (native_os == .linux) {36 @export(clone, .{ .name = "clone" });
37 @export(clone, .{ .name = "clone" });
38 }
39 }37 }
4038
41 @export(memset, .{ .name = "memset", .linkage = .Strong });39 @export(memset, .{ .name = "memset", .linkage = .Strong });
src/codegen/llvm.zig+20-5
...@@ -4732,10 +4732,11 @@ pub const FuncGen = struct {...@@ -4732,10 +4732,11 @@ pub const FuncGen = struct {
4732 var buf: Type.Payload.ElemType = undefined;4732 var buf: Type.Payload.ElemType = undefined;
4733 const payload_ty = optional_ty.optionalChild(&buf);4733 const payload_ty = optional_ty.optionalChild(&buf);
4734 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {4734 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4735 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
4735 if (invert) {4736 if (invert) {
4736 return self.builder.buildNot(operand, "");4737 return self.builder.buildNot(loaded, "");
4737 } else {4738 } else {
4738 return operand;4739 return loaded;
4739 }4740 }
4740 }4741 }
47414742
...@@ -4784,12 +4785,16 @@ pub const FuncGen = struct {...@@ -4784,12 +4785,16 @@ pub const FuncGen = struct {
4784 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4785 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4785 const operand = try self.resolveInst(ty_op.operand);4786 const operand = try self.resolveInst(ty_op.operand);
4786 const optional_ty = self.air.typeOf(ty_op.operand).childType();4787 const optional_ty = self.air.typeOf(ty_op.operand).childType();
4788 const result_ty = self.air.getRefType(ty_op.ty);
4787 var buf: Type.Payload.ElemType = undefined;4789 var buf: Type.Payload.ElemType = undefined;
4788 const payload_ty = optional_ty.optionalChild(&buf);4790 const payload_ty = optional_ty.optionalChild(&buf);
4789 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {4791 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4790 // We have a pointer to a zero-bit value and we need to return4792 // We have a pointer to a zero-bit value and we need to return
4791 // a pointer to a zero-bit value.4793 // a pointer to a zero-bit value.
4792 return operand;4794
4795 // TODO once we update to LLVM 14 this bitcast won't be necessary.
4796 const res_ptr_ty = try self.dg.llvmType(result_ty);
4797 return self.builder.buildBitCast(operand, res_ptr_ty, "");
4793 }4798 }
4794 if (optional_ty.isPtrLikeOptional()) {4799 if (optional_ty.isPtrLikeOptional()) {
4795 // The payload and the optional are the same value.4800 // The payload and the optional are the same value.
...@@ -4807,13 +4812,17 @@ pub const FuncGen = struct {...@@ -4807,13 +4812,17 @@ pub const FuncGen = struct {
4807 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4812 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4808 const operand = try self.resolveInst(ty_op.operand);4813 const operand = try self.resolveInst(ty_op.operand);
4809 const optional_ty = self.air.typeOf(ty_op.operand).childType();4814 const optional_ty = self.air.typeOf(ty_op.operand).childType();
4815 const result_ty = self.air.getRefType(ty_op.ty);
4810 var buf: Type.Payload.ElemType = undefined;4816 var buf: Type.Payload.ElemType = undefined;
4811 const payload_ty = optional_ty.optionalChild(&buf);4817 const payload_ty = optional_ty.optionalChild(&buf);
4812 const non_null_bit = self.context.intType(1).constAllOnes();4818 const non_null_bit = self.context.intType(1).constAllOnes();
4813 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {4819 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4814 // We have a pointer to a i1. We need to set it to 1 and then return the same pointer.4820 // We have a pointer to a i1. We need to set it to 1 and then return the same pointer.
4815 _ = self.builder.buildStore(non_null_bit, operand);4821 _ = self.builder.buildStore(non_null_bit, operand);
4816 return operand;4822
4823 // TODO once we update to LLVM 14 this bitcast won't be necessary.
4824 const res_ptr_ty = try self.dg.llvmType(result_ty);
4825 return self.builder.buildBitCast(operand, res_ptr_ty, "");
4817 }4826 }
4818 if (optional_ty.isPtrLikeOptional()) {4827 if (optional_ty.isPtrLikeOptional()) {
4819 // The payload and the optional are the same value.4828 // The payload and the optional are the same value.
...@@ -4872,7 +4881,13 @@ pub const FuncGen = struct {...@@ -4872,7 +4881,13 @@ pub const FuncGen = struct {
4872 const target = self.dg.module.getTarget();4881 const target = self.dg.module.getTarget();
4873 const offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1;4882 const offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1;
48744883
4875 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null;4884 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4885 if (!operand_is_ptr) return null;
4886
4887 // TODO once we update to LLVM 14 this bitcast won't be necessary.
4888 const res_ptr_ty = try self.dg.llvmType(result_ty);
4889 return self.builder.buildBitCast(operand, res_ptr_ty, "");
4890 }
4876 if (operand_is_ptr or isByRef(payload_ty)) {4891 if (operand_is_ptr or isByRef(payload_ty)) {
4877 return self.builder.buildStructGEP(operand, offset, "");4892 return self.builder.buildStructGEP(operand, offset, "");
4878 }4893 }
test/behavior/optional.zig+39
...@@ -332,3 +332,42 @@ test "array of optional unaligned types" {...@@ -332,3 +332,42 @@ test "array of optional unaligned types" {
332 i += 1;332 i += 1;
333 try expect(Enum.three == values[i].?.Num);333 try expect(Enum.three == values[i].?.Num);
334}334}
335
336test "optional pointer to zero bit optional payload" {
337 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
338 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
341
342 const B = struct {
343 fn foo(_: *@This()) void {}
344 };
345 const A = struct {
346 b: ?B = .{},
347 };
348 var a: A = .{};
349 var a_ptr = &a;
350 if (a_ptr.b) |*some| {
351 some.foo();
352 }
353}
354
355test "optional pointer to zero bit error union payload" {
356 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
357 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
358 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
359 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
360 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
361
362 const B = struct {
363 fn foo(_: *@This()) void {}
364 };
365 const A = struct {
366 b: anyerror!B = .{},
367 };
368 var a: A = .{};
369 var a_ptr = &a;
370 if (a_ptr.b) |*some| {
371 some.foo();
372 } else |_| {}
373}