| ... | @@ -801,77 +801,14 @@ pub const DeclGen = struct { | ... | @@ -801,77 +801,14 @@ pub const DeclGen = struct { |
| 801 | else => unreachable, | 801 | else => unreachable, |
| 802 | }, | 802 | }, |
| 803 | .un => |un| { | 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 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; | 804 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; |
| 815 | const layout = self.unionLayout(ty, active_field); | 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) { | 811 | return try self.unionInit(ty, active_field, payload); |
| 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; | | |
| 875 | }, | 812 | }, |
| 876 | .memoized_call => unreachable, | 813 | .memoized_call => unreachable, |
| 877 | } | 814 | } |
| ... | @@ -1752,6 +1689,8 @@ pub const DeclGen = struct { | ... | @@ -1752,6 +1689,8 @@ pub const DeclGen = struct { |
| 1752 | | 1689 | |
| 1753 | .set_union_tag => return try self.airSetUnionTag(inst), | 1690 | .set_union_tag => return try self.airSetUnionTag(inst), |
| 1754 | .get_union_tag => try self.airGetUnionTag(inst), | 1691 | .get_union_tag => try self.airGetUnionTag(inst), |
| | 1692 | .union_init => try self.airUnionInit(inst), |
| | 1693 | |
| 1755 | .struct_field_val => try self.airStructFieldVal(inst), | 1694 | .struct_field_val => try self.airStructFieldVal(inst), |
| 1756 | | 1695 | |
| 1757 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), | 1696 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), |
| ... | @@ -2573,8 +2512,12 @@ pub const DeclGen = struct { | ... | @@ -2573,8 +2512,12 @@ pub const DeclGen = struct { |
| 2573 | const union_ptr_id = try self.resolve(bin_op.lhs); | 2512 | const union_ptr_id = try self.resolve(bin_op.lhs); |
| 2574 | const new_tag_id = try self.resolve(bin_op.rhs); | 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}); | 2515 | if (layout.payload_size == 0) { |
| 2577 | try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod)); | 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 | fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 2523 | fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | @@ -2594,6 +2537,113 @@ pub const DeclGen = struct { | ... | @@ -2594,6 +2537,113 @@ pub const DeclGen = struct { |
| 2594 | return try self.extractField(tag_ty, union_handle, layout.tag_index); | 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 | fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 2647 | fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2598 | if (self.liveness.isUnused(inst)) return null; | 2648 | if (self.liveness.isUnused(inst)) return null; |
| 2599 | | 2649 | |