| ... | ... | @@ -801,77 +801,14 @@ pub const DeclGen = struct { |
| 801 | 801 | else => unreachable, |
| 802 | 802 | }, |
| 803 | 803 | .un => |un| { |
| 804 | | // To initialize a union, generate a temporary variable with the |
| 805 | | // type that has the right field active, then pointer-cast and store |
| 806 | | // the active field, and finally load and return the entire union. |
| 807 | | |
| 808 | | const union_ty = mod.typeToUnion(ty).?; |
| 809 | | |
| 810 | | if (union_ty.getLayout(ip) == .Packed) { |
| 811 | | return self.todo("packed union types", .{}); |
| 812 | | } |
| 813 | | |
| 814 | 804 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; |
| 815 | 805 | const layout = self.unionLayout(ty, active_field); |
| 806 | const payload = if (layout.active_field_size != 0) |
| 807 | try self.constant(layout.active_field_ty, un.val.toValue(), .indirect) |
| 808 | else |
| 809 | null; |
| 816 | 810 | |
| 817 | | if (layout.payload_size == 0) { |
| 818 | | // No payload, so represent this as just the tag type. |
| 819 | | return try self.constant(ty.unionTagTypeSafety(mod).?, un.tag.toValue(), .indirect); |
| 820 | | } |
| 821 | | |
| 822 | | const un_active_ty_ref = try self.resolveUnionType(ty, active_field); |
| 823 | | const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function); |
| 824 | | const un_general_ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function); |
| 825 | | |
| 826 | | const var_id = self.spv.allocId(); |
| 827 | | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 828 | | .id_result_type = self.typeId(un_active_ptr_ty_ref), |
| 829 | | .id_result = var_id, |
| 830 | | .storage_class = .Function, |
| 831 | | }); |
| 832 | | |
| 833 | | if (layout.tag_size != 0) { |
| 834 | | const tag_ty = ty.unionTagTypeSafety(mod).?; |
| 835 | | const tag_ty_ref = try self.resolveType(tag_ty, .indirect); |
| 836 | | const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function); |
| 837 | | const ptr_id = try self.accessChain(tag_ptr_ty_ref, var_id, &.{@as(u32, @intCast(layout.tag_index))}); |
| 838 | | const tag_id = try self.constant(tag_ty, un.tag.toValue(), .indirect); |
| 839 | | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 840 | | .pointer = ptr_id, |
| 841 | | .object = tag_id, |
| 842 | | }); |
| 843 | | } |
| 844 | | |
| 845 | | if (layout.active_field_size != 0) { |
| 846 | | const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); |
| 847 | | const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function); |
| 848 | | const ptr_id = try self.accessChain(active_field_ptr_ty_ref, var_id, &.{@as(u32, @intCast(layout.active_field_index))}); |
| 849 | | const value_id = try self.constant(layout.active_field_ty, un.val.toValue(), .indirect); |
| 850 | | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 851 | | .pointer = ptr_id, |
| 852 | | .object = value_id, |
| 853 | | }); |
| 854 | | } |
| 855 | | |
| 856 | | // Just leave the padding fields uninitialized... |
| 857 | | // TODO: Or should we initialize them with undef explicitly? |
| 858 | | |
| 859 | | // Now cast the pointer and load it as the 'generic' union type. |
| 860 | | |
| 861 | | const casted_var_id = self.spv.allocId(); |
| 862 | | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 863 | | .id_result_type = self.typeId(un_general_ptr_ty_ref), |
| 864 | | .id_result = casted_var_id, |
| 865 | | .operand = var_id, |
| 866 | | }); |
| 867 | | |
| 868 | | const result_id = self.spv.allocId(); |
| 869 | | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 870 | | .id_result_type = self.typeId(result_ty_ref), |
| 871 | | .id_result = result_id, |
| 872 | | .pointer = casted_var_id, |
| 873 | | }); |
| 874 | | return result_id; |
| 811 | return try self.unionInit(ty, active_field, payload); |
| 875 | 812 | }, |
| 876 | 813 | .memoized_call => unreachable, |
| 877 | 814 | } |
| ... | ... | @@ -1752,6 +1689,8 @@ pub const DeclGen = struct { |
| 1752 | 1689 | |
| 1753 | 1690 | .set_union_tag => return try self.airSetUnionTag(inst), |
| 1754 | 1691 | .get_union_tag => try self.airGetUnionTag(inst), |
| 1692 | .union_init => try self.airUnionInit(inst), |
| 1693 | |
| 1755 | 1694 | .struct_field_val => try self.airStructFieldVal(inst), |
| 1756 | 1695 | |
| 1757 | 1696 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), |
| ... | ... | @@ -2573,8 +2512,12 @@ pub const DeclGen = struct { |
| 2573 | 2512 | const union_ptr_id = try self.resolve(bin_op.lhs); |
| 2574 | 2513 | const new_tag_id = try self.resolve(bin_op.rhs); |
| 2575 | 2514 | |
| 2576 | | const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index}); |
| 2577 | | try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); |
| 2515 | if (layout.payload_size == 0) { |
| 2516 | try self.store(tag_ty, union_ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); |
| 2517 | } else { |
| 2518 | const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index}); |
| 2519 | try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); |
| 2520 | } |
| 2578 | 2521 | } |
| 2579 | 2522 | |
| 2580 | 2523 | fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2594,6 +2537,113 @@ pub const DeclGen = struct { |
| 2594 | 2537 | return try self.extractField(tag_ty, union_handle, layout.tag_index); |
| 2595 | 2538 | } |
| 2596 | 2539 | |
| 2540 | fn unionInit( |
| 2541 | self: *DeclGen, |
| 2542 | ty: Type, |
| 2543 | active_field: u32, |
| 2544 | payload: ?IdRef, |
| 2545 | ) !IdRef { |
| 2546 | // To initialize a union, generate a temporary variable with the |
| 2547 | // type that has the right field active, then pointer-cast and store |
| 2548 | // the active field, and finally load and return the entire union. |
| 2549 | |
| 2550 | const mod = self.module; |
| 2551 | const ip = &mod.intern_pool; |
| 2552 | const union_ty = mod.typeToUnion(ty).?; |
| 2553 | |
| 2554 | if (union_ty.getLayout(ip) == .Packed) { |
| 2555 | unreachable; // TODO |
| 2556 | } |
| 2557 | |
| 2558 | const maybe_tag_ty = ty.unionTagTypeSafety(mod); |
| 2559 | const layout = self.unionLayout(ty, active_field); |
| 2560 | |
| 2561 | const tag_int = if (layout.tag_size != 0) blk: { |
| 2562 | const tag_ty = maybe_tag_ty.?; |
| 2563 | const union_field_name = union_ty.field_names.get(ip)[active_field]; |
| 2564 | const enum_field_index = tag_ty.enumFieldIndex(union_field_name, mod).?; |
| 2565 | const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); |
| 2566 | const tag_int_val = try tag_val.intFromEnum(tag_ty, mod); |
| 2567 | break :blk tag_int_val.toUnsignedInt(mod); |
| 2568 | } else 0; |
| 2569 | |
| 2570 | if (layout.payload_size == 0) { |
| 2571 | const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct); |
| 2572 | return try self.constInt(tag_ty_ref, tag_int); |
| 2573 | } |
| 2574 | |
| 2575 | const un_active_ty_ref = try self.resolveUnionType(ty, active_field); |
| 2576 | const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function); |
| 2577 | const un_general_ty_ref = try self.resolveType(ty, .direct); |
| 2578 | const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function); |
| 2579 | |
| 2580 | const tmp_id = self.spv.allocId(); |
| 2581 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 2582 | .id_result_type = self.typeId(un_active_ptr_ty_ref), |
| 2583 | .id_result = tmp_id, |
| 2584 | .storage_class = .Function, |
| 2585 | }); |
| 2586 | |
| 2587 | if (layout.tag_size != 0) { |
| 2588 | const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct); |
| 2589 | const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function); |
| 2590 | const ptr_id = try self.accessChain(tag_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.tag_index))}); |
| 2591 | const tag_id = try self.constInt(tag_ty_ref, tag_int); |
| 2592 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 2593 | .pointer = ptr_id, |
| 2594 | .object = tag_id, |
| 2595 | }); |
| 2596 | } |
| 2597 | |
| 2598 | if (layout.active_field_size != 0) { |
| 2599 | const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); |
| 2600 | const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function); |
| 2601 | const ptr_id = try self.accessChain(active_field_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.active_field_index))}); |
| 2602 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 2603 | .pointer = ptr_id, |
| 2604 | .object = payload.?, |
| 2605 | }); |
| 2606 | } else { |
| 2607 | assert(payload == null); |
| 2608 | } |
| 2609 | |
| 2610 | // Just leave the padding fields uninitialized... |
| 2611 | // TODO: Or should we initialize them with undef explicitly? |
| 2612 | |
| 2613 | // Now cast the pointer and load it as the 'generic' union type. |
| 2614 | |
| 2615 | const casted_var_id = self.spv.allocId(); |
| 2616 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 2617 | .id_result_type = self.typeId(un_general_ptr_ty_ref), |
| 2618 | .id_result = casted_var_id, |
| 2619 | .operand = tmp_id, |
| 2620 | }); |
| 2621 | |
| 2622 | const result_id = self.spv.allocId(); |
| 2623 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 2624 | .id_result_type = self.typeId(un_general_ty_ref), |
| 2625 | .id_result = result_id, |
| 2626 | .pointer = casted_var_id, |
| 2627 | }); |
| 2628 | |
| 2629 | return result_id; |
| 2630 | } |
| 2631 | |
| 2632 | fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2633 | if (self.liveness.isUnused(inst)) return null; |
| 2634 | |
| 2635 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2636 | const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; |
| 2637 | const ty = self.typeOfIndex(inst); |
| 2638 | const layout = self.unionLayout(ty, extra.field_index); |
| 2639 | |
| 2640 | const payload = if (layout.active_field_size != 0) |
| 2641 | try self.resolve(extra.init) |
| 2642 | else |
| 2643 | null; |
| 2644 | return try self.unionInit(ty, extra.field_index, payload); |
| 2645 | } |
| 2646 | |
| 2597 | 2647 | fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2598 | 2648 | if (self.liveness.isUnused(inst)) return null; |
| 2599 | 2649 | |