authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-21 22:50:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-21 23:49:38-07:00
log9dc98fbabbd5e91622d2459ec6265a8dae2405b8
treee6198957420b30ae672a581d0e25e09771fbbcfc
parent7f48bc3493fbfb4487285c4e532acd6fad8cefa5

Sema: fix comptime union initialization

The mechanism behind initializing a union's tag is a bit complicated, depending on whether the union is initialized at runtime, forced comptime, or implicit comptime. `coerce_result_ptr` now does not force a block to be a runtime context; instead of adding runtime instructions directly, it forwards analysis to the respective functions for initializing optionals and error unions. `validateUnionInit` now has logic to still emit a runtime `set_union_tag` instruction even if the union pointer is comptime-known, for the case of a pointer that is not comptime mutable, such as a variable or the result of `@intToPtr`. `validateStructInit` looks for a completely different pattern now; it now handles the possibility of the corresponding AIR instruction for the `field_ptr` to be missing or the corresponding `store` to be missing. See the new comment added to the function for more details. An equivalent change should probably be made to `validateArrayInit`. `analyzeOptionalPayloadPtr` and `analyzeErrUnionPayloadPtr` functions now emit a `optional_payload_ptr_set` or `errunion_payload_ptr_set` instruction respectively if `initializing` is true and the pointer value is not comptime-mutable. `storePtr2` now tries the comptime pointer store before checking if the element type has one possible value because the comptime pointer store can have side effects of setting a union tag, setting an optional payload non-null, or setting an error union to be non-error. The LLVM backend `lowerParentPtr` function is improved to take into account the differences in how the LLVM values are lowered depending on the Zig type. It now handles unions correctly as well as additionally handling optionals and error unions. In the LLVM backend, the instructions `optional_payload_ptr_set` and `errunion_payload_ptr_set` check liveness analysis and only do the side effects in the case the result of the instruction is unused. A few wasm and C backend test cases regressed, but they are due to TODOs in lowering of constants, so this is progress.

6 files changed, 238 insertions(+), 109 deletions(-)

src/Sema.zig+106-71
...@@ -1586,19 +1586,23 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -1586,19 +1586,23 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1586 }),1586 }),
1587 );1587 );
1588 },1588 },
1589 .decl_ref_mut => {
1590 const ptr_ty = try Type.ptr(sema.arena, .{
1591 .pointee_type = pointee_ty,
1592 .@"addrspace" = addr_space,
1593 });
1594 return sema.addConstant(ptr_ty, ptr_val);
1595 },
1596 else => {},1589 else => {},
1597 }1590 }
1598 }1591 }
1599 }1592 }
16001593
1601 try sema.requireRuntimeBlock(block, src);1594 // We would like to rely on the mechanism below even for comptime values.
1595 // However in the case that the pointer points to comptime-mutable value,
1596 // we cannot do it.
1597 if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {
1598 if (ptr_val.isComptimeMutablePtr()) {
1599 const ptr_ty = try Type.ptr(sema.arena, .{
1600 .pointee_type = pointee_ty,
1601 .@"addrspace" = addr_space,
1602 });
1603 return sema.addConstant(ptr_ty, ptr_val);
1604 }
1605 }
16021606
1603 // Make a dummy store through the pointer to test the coercion.1607 // Make a dummy store through the pointer to test the coercion.
1604 // We will then use the generated instructions to decide what1608 // We will then use the generated instructions to decide what
...@@ -1638,7 +1642,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -1638,7 +1642,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1638 switch (air_tags[trash_inst]) {1642 switch (air_tags[trash_inst]) {
1639 .bitcast => {1643 .bitcast => {
1640 if (Air.indexToRef(trash_inst) == dummy_operand) {1644 if (Air.indexToRef(trash_inst) == dummy_operand) {
1641 return block.addBitCast(ptr_ty, new_ptr);1645 return sema.bitCast(block, ptr_ty, new_ptr, src);
1642 }1646 }
1643 const ty_op = air_datas[trash_inst].ty_op;1647 const ty_op = air_datas[trash_inst].ty_op;
1644 const operand_ty = sema.getTmpAir().typeOf(ty_op.operand);1648 const operand_ty = sema.getTmpAir().typeOf(ty_op.operand);
...@@ -1646,28 +1650,16 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -1646,28 +1650,16 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1646 .pointee_type = operand_ty,1650 .pointee_type = operand_ty,
1647 .@"addrspace" = addr_space,1651 .@"addrspace" = addr_space,
1648 });1652 });
1649 new_ptr = try block.addBitCast(ptr_operand_ty, new_ptr);1653 new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src);
1650 },1654 },
1651 .wrap_optional => {1655 .wrap_optional => {
1652 const ty_op = air_datas[trash_inst].ty_op;1656 new_ptr = try sema.analyzeOptionalPayloadPtr(block, src, new_ptr, false, true);
1653 const payload_ty = sema.getTmpAir().typeOf(ty_op.operand);
1654 const ptr_payload_ty = try Type.ptr(sema.arena, .{
1655 .pointee_type = payload_ty,
1656 .@"addrspace" = addr_space,
1657 });
1658 new_ptr = try block.addTyOp(.optional_payload_ptr_set, ptr_payload_ty, new_ptr);
1659 },1657 },
1660 .wrap_errunion_err => {1658 .wrap_errunion_err => {
1661 return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{});1659 return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{});
1662 },1660 },
1663 .wrap_errunion_payload => {1661 .wrap_errunion_payload => {
1664 const ty_op = air_datas[trash_inst].ty_op;1662 new_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, new_ptr, false, true);
1665 const payload_ty = sema.getTmpAir().typeOf(ty_op.operand);
1666 const ptr_payload_ty = try Type.ptr(sema.arena, .{
1667 .pointee_type = payload_ty,
1668 .@"addrspace" = addr_space,
1669 });
1670 new_ptr = try block.addTyOp(.errunion_payload_ptr_set, ptr_payload_ty, new_ptr);
1671 },1663 },
1672 else => {1664 else => {
1673 if (std.debug.runtime_safety) {1665 if (std.debug.runtime_safety) {
...@@ -2774,9 +2766,16 @@ fn validateUnionInit(...@@ -2774,9 +2766,16 @@ fn validateUnionInit(
2774 const field_index = @intCast(u32, field_index_big);2766 const field_index = @intCast(u32, field_index_big);
27752767
2776 // Handle the possibility of the union value being comptime-known.2768 // Handle the possibility of the union value being comptime-known.
2777 const union_ptr_inst = Air.refToIndex(sema.resolveInst(field_ptr_extra.lhs)).?;2769 const union_ptr_inst = Air.refToIndex(union_ptr).?;
2778 switch (sema.air_instructions.items(.tag)[union_ptr_inst]) {2770 switch (sema.air_instructions.items(.tag)[union_ptr_inst]) {
2779 .constant => return, // In this case the tag has already been set. No validation to do.2771 .constant => {
2772 if (try sema.resolveDefinedValue(block, init_src, union_ptr)) |ptr_val| {
2773 if (ptr_val.isComptimeMutablePtr()) {
2774 // In this case the tag has already been set. No validation to do.
2775 return;
2776 }
2777 }
2778 },
2780 .bitcast => {2779 .bitcast => {
2781 // TODO here we need to go back and see if we need to convert the union2780 // TODO here we need to go back and see if we need to convert the union
2782 // to a comptime-known value. In such case, we must delete all the instructions2781 // to a comptime-known value. In such case, we must delete all the instructions
...@@ -2895,7 +2894,7 @@ fn validateStructInit(...@@ -2895,7 +2894,7 @@ fn validateStructInit(
2895 }2894 }
28962895
2897 var struct_is_comptime = true;2896 var struct_is_comptime = true;
2898 var first_block_index: usize = std.math.maxInt(u32);2897 var first_block_index = block.instructions.items.len;
28992898
2900 const air_tags = sema.air_instructions.items(.tag);2899 const air_tags = sema.air_instructions.items(.tag);
2901 const air_datas = sema.air_instructions.items(.data);2900 const air_datas = sema.air_instructions.items(.data);
...@@ -2904,7 +2903,7 @@ fn validateStructInit(...@@ -2904,7 +2903,7 @@ fn validateStructInit(
2904 // ends up being comptime-known.2903 // ends up being comptime-known.
2905 const field_values = try sema.arena.alloc(Value, fields.len);2904 const field_values = try sema.arena.alloc(Value, fields.len);
29062905
2907 for (found_fields) |field_ptr, i| {2906 field: for (found_fields) |field_ptr, i| {
2908 const field = fields[i];2907 const field = fields[i];
29092908
2910 if (field_ptr != 0) {2909 if (field_ptr != 0) {
...@@ -2919,40 +2918,57 @@ fn validateStructInit(...@@ -2919,40 +2918,57 @@ fn validateStructInit(
29192918
2920 const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;2919 const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;
2921 const field_ptr_air_inst = Air.refToIndex(field_ptr_air_ref).?;2920 const field_ptr_air_inst = Air.refToIndex(field_ptr_air_ref).?;
2922 // Find the block index of the field_ptr so that we can look at the next2921
2923 // instruction after it within the same block.2922 //std.debug.print("validateStructInit (field_ptr_air_inst=%{d}):\n", .{
2923 // field_ptr_air_inst,
2924 //});
2925 //for (block.instructions.items) |item| {
2926 // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[item])});
2927 //}
2928
2929 // We expect to see something like this in the current block AIR:
2930 // %a = field_ptr(...)
2931 // store(%a, %b)
2932 // If %b is a comptime operand, this field is comptime.
2933 //
2934 // However, in the case of a comptime-known pointer to a struct, the
2935 // the field_ptr instruction is missing, so we have to pattern-match
2936 // based only on the store instructions.
2937 // `first_block_index` needs to point to the `field_ptr` if it exists;
2938 // the `store` otherwise.
2939 //
2940 // It's also possible for there to be no store instruction, in the case
2941 // of nested `coerce_result_ptr` instructions. If we see the `field_ptr`
2942 // but we have not found a `store`, treat as a runtime-known field.
2943
2924 // Possible performance enhancement: save the `block_index` between iterations2944 // Possible performance enhancement: save the `block_index` between iterations
2925 // of the for loop.2945 // of the for loop.
2926 const next_air_inst = inst: {2946 var block_index = block.instructions.items.len - 1;
2927 var block_index = block.instructions.items.len - 1;2947 while (block_index > 0) : (block_index -= 1) {
2928 while (block.instructions.items[block_index] != field_ptr_air_inst) {2948 const store_inst = block.instructions.items[block_index];
2929 block_index -= 1;2949 if (store_inst == field_ptr_air_inst) {
2950 struct_is_comptime = false;
2951 continue :field;
2930 }2952 }
2931 first_block_index = @minimum(first_block_index, block_index);2953 if (air_tags[store_inst] != .store) continue;
2932 break :inst block.instructions.items[block_index + 1];2954 const bin_op = air_datas[store_inst].bin_op;
2933 };2955 if (bin_op.lhs != field_ptr_air_ref) continue;
29342956 if (block_index > 0 and
2935 // If the next instructon is a store with a comptime operand, this field2957 field_ptr_air_inst == block.instructions.items[block_index - 1])
2936 // is comptime.2958 {
2937 switch (air_tags[next_air_inst]) {2959 first_block_index = @minimum(first_block_index, block_index - 1);
2938 .store => {2960 } else {
2939 const bin_op = air_datas[next_air_inst].bin_op;2961 first_block_index = @minimum(first_block_index, block_index);
2940 if (bin_op.lhs != field_ptr_air_ref) {2962 }
2941 struct_is_comptime = false;2963 if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| {
2942 continue;2964 field_values[i] = val;
2943 }2965 } else {
2944 if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| {
2945 field_values[i] = val;
2946 } else {
2947 struct_is_comptime = false;
2948 }
2949 continue;
2950 },
2951 else => {
2952 struct_is_comptime = false;2966 struct_is_comptime = false;
2953 continue;2967 }
2954 },2968 continue :field;
2955 }2969 }
2970 struct_is_comptime = false;
2971 continue :field;
2956 }2972 }
29572973
2958 const field_name = struct_obj.fields.keys()[i];2974 const field_name = struct_obj.fields.keys()[i];
...@@ -5355,21 +5371,28 @@ fn analyzeOptionalPayloadPtr(...@@ -5355,21 +5371,28 @@ fn analyzeOptionalPayloadPtr(
5355 .@"addrspace" = optional_ptr_ty.ptrAddressSpace(),5371 .@"addrspace" = optional_ptr_ty.ptrAddressSpace(),
5356 });5372 });
53575373
5358 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {5374 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
5359 if (initializing) {5375 if (initializing) {
5376 if (!ptr_val.isComptimeMutablePtr()) {
5377 // If the pointer resulting from this function was stored at comptime,
5378 // the optional non-null bit would be set that way. But in this case,
5379 // we need to emit a runtime instruction to do it.
5380 try sema.requireRuntimeBlock(block, src);
5381 _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
5382 }
5360 return sema.addConstant(5383 return sema.addConstant(
5361 child_pointer,5384 child_pointer,
5362 try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val),5385 try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val),
5363 );5386 );
5364 }5387 }
5365 if (try sema.pointerDeref(block, src, pointer_val, optional_ptr_ty)) |val| {5388 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
5366 if (val.isNull()) {5389 if (val.isNull()) {
5367 return sema.fail(block, src, "unable to unwrap null", .{});5390 return sema.fail(block, src, "unable to unwrap null", .{});
5368 }5391 }
5369 // The same Value represents the pointer to the optional and the payload.5392 // The same Value represents the pointer to the optional and the payload.
5370 return sema.addConstant(5393 return sema.addConstant(
5371 child_pointer,5394 child_pointer,
5372 try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val),5395 try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val),
5373 );5396 );
5374 }5397 }
5375 }5398 }
...@@ -5379,10 +5402,11 @@ fn analyzeOptionalPayloadPtr(...@@ -5379,10 +5402,11 @@ fn analyzeOptionalPayloadPtr(
5379 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);5402 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
5380 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);5403 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
5381 }5404 }
5382 return block.addTyOp(if (initializing)5405 const air_tag: Air.Inst.Tag = if (initializing)
5383 .optional_payload_ptr_set5406 .optional_payload_ptr_set
5384 else5407 else
5385 .optional_payload_ptr, child_pointer, optional_ptr);5408 .optional_payload_ptr;
5409 return block.addTyOp(air_tag, child_pointer, optional_ptr);
5386}5410}
53875411
5388/// Value in, value out.5412/// Value in, value out.
...@@ -5510,21 +5534,28 @@ fn analyzeErrUnionPayloadPtr(...@@ -5510,21 +5534,28 @@ fn analyzeErrUnionPayloadPtr(
5510 .@"addrspace" = operand_ty.ptrAddressSpace(),5534 .@"addrspace" = operand_ty.ptrAddressSpace(),
5511 });5535 });
55125536
5513 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {5537 if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
5514 if (initializing) {5538 if (initializing) {
5539 if (!ptr_val.isComptimeMutablePtr()) {
5540 // If the pointer resulting from this function was stored at comptime,
5541 // the error union error code would be set that way. But in this case,
5542 // we need to emit a runtime instruction to do it.
5543 try sema.requireRuntimeBlock(block, src);
5544 _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
5545 }
5515 return sema.addConstant(5546 return sema.addConstant(
5516 operand_pointer_ty,5547 operand_pointer_ty,
5517 try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),5548 try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val),
5518 );5549 );
5519 }5550 }
5520 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {5551 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
5521 if (val.getError()) |name| {5552 if (val.getError()) |name| {
5522 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});5553 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
5523 }5554 }
55245555
5525 return sema.addConstant(5556 return sema.addConstant(
5526 operand_pointer_ty,5557 operand_pointer_ty,
5527 try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),5558 try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val),
5528 );5559 );
5529 }5560 }
5530 }5561 }
...@@ -5534,10 +5565,11 @@ fn analyzeErrUnionPayloadPtr(...@@ -5534,10 +5565,11 @@ fn analyzeErrUnionPayloadPtr(
5534 const is_non_err = try block.addUnOp(.is_err, operand);5565 const is_non_err = try block.addUnOp(.is_err, operand);
5535 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);5566 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
5536 }5567 }
5537 return block.addTyOp(if (initializing)5568 const air_tag: Air.Inst.Tag = if (initializing)
5538 .errunion_payload_ptr_set5569 .errunion_payload_ptr_set
5539 else5570 else
5540 .unwrap_errunion_payload_ptr, operand_pointer_ty, operand);5571 .unwrap_errunion_payload_ptr;
5572 return block.addTyOp(air_tag, operand_pointer_ty, operand);
5541}5573}
55425574
5543/// Value in, value out5575/// Value in, value out
...@@ -15242,8 +15274,6 @@ fn storePtr2(...@@ -15242,8 +15274,6 @@ fn storePtr2(
15242 }15274 }
1524315275
15244 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);15276 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
15245 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
15246 return;
1524715277
15248 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {15278 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
15249 const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);15279 const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
...@@ -15257,6 +15287,11 @@ fn storePtr2(...@@ -15257,6 +15287,11 @@ fn storePtr2(
15257 } else break :rs ptr_src;15287 } else break :rs ptr_src;
15258 } else ptr_src;15288 } else ptr_src;
1525915289
15290 // We do this after the possible comptime store above, for the case of field_ptr stores
15291 // to unions because we want the comptime tag to be set, even if the field type is void.
15292 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
15293 return;
15294
15260 // TODO handle if the element type requires comptime15295 // TODO handle if the element type requires comptime
1526115296
15262 try sema.requireRuntimeBlock(block, runtime_src);15297 try sema.requireRuntimeBlock(block, runtime_src);
src/codegen/llvm.zig+121-36
...@@ -1329,32 +1329,25 @@ pub const DeclGen = struct {...@@ -1329,32 +1329,25 @@ pub const DeclGen = struct {
1329 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);1329 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
1330 return llvm_int.constIntToPtr(try dg.llvmType(tv.ty));1330 return llvm_int.constIntToPtr(try dg.llvmType(tv.ty));
1331 },1331 },
1332 .field_ptr => {1332 .field_ptr, .opt_payload_ptr, .eu_payload_ptr => {
1333 const field_ptr = tv.val.castTag(.field_ptr).?.data;1333 const parent = try dg.lowerParentPtr(tv.val);
1334 const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr);1334 return parent.llvm_ptr.constBitCast(try dg.llvmType(tv.ty));
1335 const llvm_u32 = dg.context.intType(32);
1336 const indices: [2]*const llvm.Value = .{
1337 llvm_u32.constInt(0, .False),
1338 llvm_u32.constInt(field_ptr.field_index, .False),
1339 };
1340 const uncasted = parent_ptr.constInBoundsGEP(&indices, indices.len);
1341 return uncasted.constBitCast(try dg.llvmType(tv.ty));
1342 },1335 },
1343 .elem_ptr => {1336 .elem_ptr => {
1344 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;1337 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
1345 const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr);1338 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr);
1346 const llvm_usize = try dg.llvmType(Type.usize);1339 const llvm_usize = try dg.llvmType(Type.usize);
1347 if (parent_ptr.typeOf().getElementType().getTypeKind() == .Array) {1340 if (parent.llvm_ptr.typeOf().getElementType().getTypeKind() == .Array) {
1348 const indices: [2]*const llvm.Value = .{1341 const indices: [2]*const llvm.Value = .{
1349 llvm_usize.constInt(0, .False),1342 llvm_usize.constInt(0, .False),
1350 llvm_usize.constInt(elem_ptr.index, .False),1343 llvm_usize.constInt(elem_ptr.index, .False),
1351 };1344 };
1352 return parent_ptr.constInBoundsGEP(&indices, indices.len);1345 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
1353 } else {1346 } else {
1354 const indices: [1]*const llvm.Value = .{1347 const indices: [1]*const llvm.Value = .{
1355 llvm_usize.constInt(elem_ptr.index, .False),1348 llvm_usize.constInt(elem_ptr.index, .False),
1356 };1349 };
1357 return parent_ptr.constInBoundsGEP(&indices, indices.len);1350 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
1358 }1351 }
1359 },1352 },
1360 .null_value, .zero => {1353 .null_value, .zero => {
...@@ -1800,11 +1793,7 @@ pub const DeclGen = struct {...@@ -1800,11 +1793,7 @@ pub const DeclGen = struct {
1800 llvm_ptr: *const llvm.Value,1793 llvm_ptr: *const llvm.Value,
1801 };1794 };
18021795
1803 fn lowerParentPtrDecl(1796 fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl) Error!ParentPtr {
1804 dg: *DeclGen,
1805 ptr_val: Value,
1806 decl: *Module.Decl,
1807 ) Error!ParentPtr {
1808 decl.markAlive();1797 decl.markAlive();
1809 var ptr_ty_payload: Type.Payload.ElemType = .{1798 var ptr_ty_payload: Type.Payload.ElemType = .{
1810 .base = .{ .tag = .single_mut_pointer },1799 .base = .{ .tag = .single_mut_pointer },
...@@ -1818,42 +1807,134 @@ pub const DeclGen = struct {...@@ -1818,42 +1807,134 @@ pub const DeclGen = struct {
1818 };1807 };
1819 }1808 }
18201809
1821 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!*const llvm.Value {1810 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!ParentPtr {
1822 switch (ptr_val.tag()) {1811 switch (ptr_val.tag()) {
1823 .decl_ref_mut => {1812 .decl_ref_mut => {
1824 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;1813 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
1825 return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;1814 return dg.lowerParentPtrDecl(ptr_val, decl);
1826 },1815 },
1827 .decl_ref => {1816 .decl_ref => {
1828 const decl = ptr_val.castTag(.decl_ref).?.data;1817 const decl = ptr_val.castTag(.decl_ref).?.data;
1829 return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;1818 return dg.lowerParentPtrDecl(ptr_val, decl);
1830 },1819 },
1831 .variable => {1820 .variable => {
1832 const decl = ptr_val.castTag(.variable).?.data.owner_decl;1821 const decl = ptr_val.castTag(.variable).?.data.owner_decl;
1833 return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;1822 return dg.lowerParentPtrDecl(ptr_val, decl);
1834 },1823 },
1835 .field_ptr => {1824 .field_ptr => {
1836 const field_ptr = ptr_val.castTag(.field_ptr).?.data;1825 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
1837 const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr);1826 const parent = try dg.lowerParentPtr(field_ptr.container_ptr);
1827 const field_index = @intCast(u32, field_ptr.field_index);
1838 const llvm_u32 = dg.context.intType(32);1828 const llvm_u32 = dg.context.intType(32);
1839 const indices: [2]*const llvm.Value = .{1829 const target = dg.module.getTarget();
1840 llvm_u32.constInt(0, .False),1830 switch (parent.ty.zigTypeTag()) {
1841 llvm_u32.constInt(field_ptr.field_index, .False),1831 .Union => {
1842 };1832 const fields = parent.ty.unionFields();
1843 return parent_ptr.constInBoundsGEP(&indices, indices.len);1833 const layout = parent.ty.unionGetLayout(target);
1834 const field_ty = fields.values()[field_index].ty;
1835 if (layout.payload_size == 0) {
1836 // In this case a pointer to the union and a pointer to any
1837 // (void) payload is the same.
1838 return ParentPtr{
1839 .llvm_ptr = parent.llvm_ptr,
1840 .ty = field_ty,
1841 };
1842 }
1843 if (layout.tag_size == 0) {
1844 const indices: [2]*const llvm.Value = .{
1845 llvm_u32.constInt(0, .False),
1846 llvm_u32.constInt(0, .False),
1847 };
1848 return ParentPtr{
1849 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1850 .ty = field_ty,
1851 };
1852 }
1853 const llvm_pl_index = @boolToInt(layout.tag_align >= layout.payload_align);
1854 const indices: [2]*const llvm.Value = .{
1855 llvm_u32.constInt(0, .False),
1856 llvm_u32.constInt(llvm_pl_index, .False),
1857 };
1858 return ParentPtr{
1859 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1860 .ty = field_ty,
1861 };
1862 },
1863 .Struct => {
1864 var ty_buf: Type.Payload.Pointer = undefined;
1865 const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?;
1866 const indices: [2]*const llvm.Value = .{
1867 llvm_u32.constInt(0, .False),
1868 llvm_u32.constInt(llvm_field_index, .False),
1869 };
1870 return ParentPtr{
1871 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1872 .ty = parent.ty.structFieldType(field_index),
1873 };
1874 },
1875 else => unreachable,
1876 }
1844 },1877 },
1845 .elem_ptr => {1878 .elem_ptr => {
1846 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;1879 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
1847 const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr);1880 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr);
1848 const llvm_usize = try dg.llvmType(Type.usize);1881 const llvm_usize = try dg.llvmType(Type.usize);
1849 const indices: [2]*const llvm.Value = .{1882 const indices: [2]*const llvm.Value = .{
1850 llvm_usize.constInt(0, .False),1883 llvm_usize.constInt(0, .False),
1851 llvm_usize.constInt(elem_ptr.index, .False),1884 llvm_usize.constInt(elem_ptr.index, .False),
1852 };1885 };
1853 return parent_ptr.constInBoundsGEP(&indices, indices.len);1886 return ParentPtr{
1887 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1888 .ty = parent.ty.childType(),
1889 };
1890 },
1891 .opt_payload_ptr => {
1892 const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
1893 const parent = try dg.lowerParentPtr(opt_payload_ptr);
1894 var buf: Type.Payload.ElemType = undefined;
1895 const payload_ty = parent.ty.optionalChild(&buf);
1896 if (!payload_ty.hasRuntimeBits() or parent.ty.isPtrLikeOptional()) {
1897 // In this case, we represent pointer to optional the same as pointer
1898 // to the payload.
1899 return ParentPtr{
1900 .llvm_ptr = parent.llvm_ptr,
1901 .ty = payload_ty,
1902 };
1903 }
1904
1905 const llvm_u32 = dg.context.intType(32);
1906 const indices: [2]*const llvm.Value = .{
1907 llvm_u32.constInt(0, .False),
1908 llvm_u32.constInt(0, .False),
1909 };
1910 return ParentPtr{
1911 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1912 .ty = payload_ty,
1913 };
1914 },
1915 .eu_payload_ptr => {
1916 const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
1917 const parent = try dg.lowerParentPtr(eu_payload_ptr);
1918 const payload_ty = parent.ty.errorUnionPayload();
1919 if (!payload_ty.hasRuntimeBits()) {
1920 // In this case, we represent pointer to error union the same as pointer
1921 // to the payload.
1922 return ParentPtr{
1923 .llvm_ptr = parent.llvm_ptr,
1924 .ty = payload_ty,
1925 };
1926 }
1927
1928 const llvm_u32 = dg.context.intType(32);
1929 const indices: [2]*const llvm.Value = .{
1930 llvm_u32.constInt(0, .False),
1931 llvm_u32.constInt(1, .False),
1932 };
1933 return ParentPtr{
1934 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1935 .ty = payload_ty,
1936 };
1854 },1937 },
1855 .opt_payload_ptr => return dg.todo("implement lowerParentPtr for optional payload", .{}),
1856 .eu_payload_ptr => return dg.todo("implement lowerParentPtr for error union payload", .{}),
1857 else => unreachable,1938 else => unreachable,
1858 }1939 }
1859 }1940 }
...@@ -3142,7 +3223,9 @@ pub const FuncGen = struct {...@@ -3142,7 +3223,9 @@ pub const FuncGen = struct {
3142 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");3223 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
3143 _ = self.builder.buildStore(non_null_bit, non_null_ptr);3224 _ = self.builder.buildStore(non_null_bit, non_null_ptr);
3144 }3225 }
3145 // Then return the payload pointer.3226 // Then return the payload pointer (only if it's used).
3227 if (self.liveness.isUnused(inst))
3228 return null;
3146 const indices: [2]*const llvm.Value = .{3229 const indices: [2]*const llvm.Value = .{
3147 index_type.constNull(), // dereference the pointer3230 index_type.constNull(), // dereference the pointer
3148 index_type.constNull(), // first field is the payload3231 index_type.constNull(), // first field is the payload
...@@ -3236,7 +3319,9 @@ pub const FuncGen = struct {...@@ -3236,7 +3319,9 @@ pub const FuncGen = struct {
3236 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");3319 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
3237 _ = self.builder.buildStore(non_error_val, non_null_ptr);3320 _ = self.builder.buildStore(non_error_val, non_null_ptr);
3238 }3321 }
3239 // Then return the payload pointer.3322 // Then return the payload pointer (only if it is used).
3323 if (self.liveness.isUnused(inst))
3324 return null;
3240 const indices: [2]*const llvm.Value = .{3325 const indices: [2]*const llvm.Value = .{
3241 index_type.constNull(), // dereference the pointer3326 index_type.constNull(), // dereference the pointer
3242 index_type.constInt(1, .False), // second field is the payload3327 index_type.constInt(1, .False), // second field is the payload
...@@ -5257,7 +5342,7 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca...@@ -5257,7 +5342,7 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca
5257}5342}
52585343
5259/// Take into account 0 bit fields. Returns null if an llvm field could not be found. This only5344/// Take into account 0 bit fields. Returns null if an llvm field could not be found. This only
5260/// happends if you want the field index of a zero sized field at the end of the struct.5345/// happens if you want the field index of a zero sized field at the end of the struct.
5261fn llvmFieldIndex(5346fn llvmFieldIndex(
5262 ty: Type,5347 ty: Type,
5263 field_index: u32,5348 field_index: u32,
test/behavior/bugs/3046.zig+2
...@@ -12,6 +12,8 @@ fn couldFail() anyerror!i32 {...@@ -12,6 +12,8 @@ fn couldFail() anyerror!i32 {
12var some_struct: SomeStruct = undefined;12var some_struct: SomeStruct = undefined;
1313
14test "fixed" {14test "fixed" {
15 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
16
15 some_struct = SomeStruct{17 some_struct = SomeStruct{
16 .field = couldFail() catch @as(i32, 0),18 .field = couldFail() catch @as(i32, 0),
17 };19 };
test/behavior/for.zig+2
...@@ -63,6 +63,8 @@ test "ignore lval with underscore (for loop)" {...@@ -63,6 +63,8 @@ test "ignore lval with underscore (for loop)" {
63}63}
6464
65test "basic for loop" {65test "basic for loop" {
66 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
67
66 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;68 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
6769
68 var buffer: [expected_result.len]u8 = undefined;70 var buffer: [expected_result.len]u8 = undefined;
test/behavior/optional.zig+2
...@@ -72,6 +72,8 @@ test "optional with void type" {...@@ -72,6 +72,8 @@ test "optional with void type" {
72}72}
7373
74test "address of unwrap optional" {74test "address of unwrap optional" {
75 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
75 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;77 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO78 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
77 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO79 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
test/behavior/union.zig+5-2
...@@ -348,6 +348,9 @@ const Foo1 = union(enum) {...@@ -348,6 +348,9 @@ const Foo1 = union(enum) {
348var glbl: Foo1 = undefined;348var glbl: Foo1 = undefined;
349349
350test "global union with single field is correctly initialized" {350test "global union with single field is correctly initialized" {
351 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
352 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
353
351 glbl = Foo1{354 glbl = Foo1{
352 .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },355 .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
353 };356 };
...@@ -363,6 +366,7 @@ var glbl_array: [2]FooUnion = undefined;...@@ -363,6 +366,7 @@ var glbl_array: [2]FooUnion = undefined;
363366
364test "initialize global array of union" {367test "initialize global array of union" {
365 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;368 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
369 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
366370
367 glbl_array[1] = FooUnion{ .U1 = 2 };371 glbl_array[1] = FooUnion{ .U1 = 2 };
368 glbl_array[0] = FooUnion{ .U0 = 1 };372 glbl_array[0] = FooUnion{ .U0 = 1 };
...@@ -487,8 +491,7 @@ test "tagged union with all void fields but a meaningful tag" {...@@ -487,8 +491,7 @@ test "tagged union with all void fields but a meaningful tag" {
487 }491 }
488 };492 };
489 try S.doTheTest();493 try S.doTheTest();
490 // TODO enable the test at comptime too494 comptime try S.doTheTest();
491 //comptime try S.doTheTest();
492}495}
493496
494test "union(enum(u32)) with specified and unspecified tag values" {497test "union(enum(u32)) with specified and unspecified tag values" {