| ... | ... | @@ -111,6 +111,35 @@ prev_stack_alignment_src: ?LazySrcLoc = null, |
| 111 | 111 | /// the struct/enum/union type created should be placed. Otherwise, it is `.none`. |
| 112 | 112 | builtin_type_target_index: InternPool.Index = .none, |
| 113 | 113 | |
| 114 | /// Links every pointer derived from a base `alloc` back to that `alloc`. Used |
| 115 | /// to detect comptime-known `const`s. |
| 116 | /// TODO: ZIR liveness analysis would allow us to remove elements from this map. |
| 117 | base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .{}, |
| 118 | |
| 119 | /// Runtime `alloc`s are placed in this map to track all comptime-known writes |
| 120 | /// before the corresponding `make_ptr_const` instruction. |
| 121 | /// If any store to the alloc depends on a runtime condition or stores a runtime |
| 122 | /// value, the corresponding element in this map is erased, to indicate that the |
| 123 | /// alloc is not comptime-known. |
| 124 | /// If the alloc remains in this map when `make_ptr_const` is reached, its value |
| 125 | /// is comptime-known, and all stores to the pointer must be applied at comptime |
| 126 | /// to determine the comptime value. |
| 127 | /// Backed by gpa. |
| 128 | maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .{}, |
| 129 | |
| 130 | const MaybeComptimeAlloc = struct { |
| 131 | /// The runtime index of the `alloc` instruction. |
| 132 | runtime_index: Value.RuntimeIndex, |
| 133 | /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to |
| 134 | /// RLS, a single comptime-known allocation may have arbitrarily many stores. |
| 135 | /// This may also contain `set_union_tag` instructions. |
| 136 | stores: std.ArrayListUnmanaged(Air.Inst.Index) = .{}, |
| 137 | /// Backed by sema.arena. Contains instructions such as `optional_payload_ptr_set` |
| 138 | /// which have side effects so will not be elided by Liveness: we must rewrite these |
| 139 | /// instructions to be nops instead of relying on Liveness. |
| 140 | non_elideable_pointers: std.ArrayListUnmanaged(Air.Inst.Index) = .{}, |
| 141 | }; |
| 142 | |
| 114 | 143 | const std = @import("std"); |
| 115 | 144 | const math = std.math; |
| 116 | 145 | const mem = std.mem; |
| ... | ... | @@ -840,6 +869,8 @@ pub fn deinit(sema: *Sema) void { |
| 840 | 869 | sema.post_hoc_blocks.deinit(gpa); |
| 841 | 870 | } |
| 842 | 871 | sema.unresolved_inferred_allocs.deinit(gpa); |
| 872 | sema.base_allocs.deinit(gpa); |
| 873 | sema.maybe_comptime_allocs.deinit(gpa); |
| 843 | 874 | sema.* = undefined; |
| 844 | 875 | } |
| 845 | 876 | |
| ... | ... | @@ -2643,6 +2674,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 2643 | 2674 | .placeholder = Air.refToIndex(bitcasted_ptr).?, |
| 2644 | 2675 | }); |
| 2645 | 2676 | |
| 2677 | try sema.checkKnownAllocPtr(ptr, bitcasted_ptr); |
| 2646 | 2678 | return bitcasted_ptr; |
| 2647 | 2679 | }, |
| 2648 | 2680 | .inferred_alloc_comptime => { |
| ... | ... | @@ -2690,7 +2722,9 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 2690 | 2722 | |
| 2691 | 2723 | const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); |
| 2692 | 2724 | const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value); |
| 2693 | | return sema.coerceResultPtr(block, src, ptr, dummy_ptr, dummy_operand, &trash_block); |
| 2725 | const new_ptr = try sema.coerceResultPtr(block, src, ptr, dummy_ptr, dummy_operand, &trash_block); |
| 2726 | try sema.checkKnownAllocPtr(ptr, new_ptr); |
| 2727 | return new_ptr; |
| 2694 | 2728 | } |
| 2695 | 2729 | |
| 2696 | 2730 | fn coerceResultPtr( |
| ... | ... | @@ -3719,7 +3753,13 @@ fn zirAllocExtended( |
| 3719 | 3753 | .address_space = target_util.defaultAddressSpace(target, .local), |
| 3720 | 3754 | }, |
| 3721 | 3755 | }); |
| 3722 | | return block.addTy(.alloc, ptr_type); |
| 3756 | const ptr = try block.addTy(.alloc, ptr_type); |
| 3757 | if (small.is_const) { |
| 3758 | const ptr_inst = Air.refToIndex(ptr).?; |
| 3759 | try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); |
| 3760 | try sema.base_allocs.put(gpa, ptr_inst, ptr_inst); |
| 3761 | } |
| 3762 | return ptr; |
| 3723 | 3763 | } |
| 3724 | 3764 | |
| 3725 | 3765 | const result_index = try block.addInstAsIndex(.{ |
| ... | ... | @@ -3730,6 +3770,10 @@ fn zirAllocExtended( |
| 3730 | 3770 | } }, |
| 3731 | 3771 | }); |
| 3732 | 3772 | try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); |
| 3773 | if (small.is_const) { |
| 3774 | try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); |
| 3775 | try sema.base_allocs.put(gpa, result_index, result_index); |
| 3776 | } |
| 3733 | 3777 | return Air.indexToRef(result_index); |
| 3734 | 3778 | } |
| 3735 | 3779 | |
| ... | ... | @@ -3748,60 +3792,26 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 3748 | 3792 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 3749 | 3793 | const alloc = try sema.resolveInst(inst_data.operand); |
| 3750 | 3794 | const alloc_ty = sema.typeOf(alloc); |
| 3751 | | |
| 3752 | | var ptr_info = alloc_ty.ptrInfo(mod); |
| 3795 | const ptr_info = alloc_ty.ptrInfo(mod); |
| 3753 | 3796 | const elem_ty = ptr_info.child.toType(); |
| 3754 | 3797 | |
| 3755 | | // Detect if all stores to an `.alloc` were comptime-known. |
| 3756 | | ct: { |
| 3757 | | var search_index: usize = block.instructions.items.len; |
| 3758 | | const air_tags = sema.air_instructions.items(.tag); |
| 3759 | | const air_datas = sema.air_instructions.items(.data); |
| 3760 | | |
| 3761 | | const store_inst = while (true) { |
| 3762 | | if (search_index == 0) break :ct; |
| 3763 | | search_index -= 1; |
| 3764 | | |
| 3765 | | const candidate = block.instructions.items[search_index]; |
| 3766 | | switch (air_tags[candidate]) { |
| 3767 | | .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, |
| 3768 | | .store, .store_safe => break candidate, |
| 3769 | | else => break :ct, |
| 3770 | | } |
| 3771 | | }; |
| 3772 | | |
| 3773 | | while (true) { |
| 3774 | | if (search_index == 0) break :ct; |
| 3775 | | search_index -= 1; |
| 3776 | | |
| 3777 | | const candidate = block.instructions.items[search_index]; |
| 3778 | | switch (air_tags[candidate]) { |
| 3779 | | .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, |
| 3780 | | .alloc => { |
| 3781 | | if (Air.indexToRef(candidate) != alloc) break :ct; |
| 3782 | | break; |
| 3783 | | }, |
| 3784 | | else => break :ct, |
| 3785 | | } |
| 3786 | | } |
| 3787 | | |
| 3788 | | const store_op = air_datas[store_inst].bin_op; |
| 3789 | | const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct; |
| 3790 | | if (store_op.lhs != alloc) break :ct; |
| 3791 | | |
| 3792 | | // Remove all the unnecessary runtime instructions. |
| 3793 | | block.instructions.shrinkRetainingCapacity(search_index); |
| 3794 | | |
| 3798 | if (try sema.resolveComptimeKnownAllocValue(block, alloc, null)) |val| { |
| 3795 | 3799 | var anon_decl = try block.startAnonDecl(); |
| 3796 | 3800 | defer anon_decl.deinit(); |
| 3797 | | return sema.analyzeDeclRef(try anon_decl.finish(elem_ty, store_val, ptr_info.flags.alignment)); |
| 3801 | const new_mut_ptr = try sema.analyzeDeclRef(try anon_decl.finish(elem_ty, val.toValue(), ptr_info.flags.alignment)); |
| 3802 | return sema.makePtrConst(block, new_mut_ptr); |
| 3798 | 3803 | } |
| 3799 | 3804 | |
| 3800 | | // If this is already a comptime-mutable allocation, we don't want to emit an error - the stores |
| 3805 | // If this is already a comptime-known allocation, we don't want to emit an error - the stores |
| 3801 | 3806 | // were already performed at comptime! Just make the pointer constant as normal. |
| 3802 | 3807 | implicit_ct: { |
| 3803 | 3808 | const ptr_val = try sema.resolveMaybeUndefVal(alloc) orelse break :implicit_ct; |
| 3804 | | if (ptr_val.isComptimeMutablePtr(mod)) break :implicit_ct; |
| 3809 | if (!ptr_val.isComptimeMutablePtr(mod)) { |
| 3810 | // It could still be a constant pointer to a decl |
| 3811 | const decl_index = ptr_val.pointerDecl(mod) orelse break :implicit_ct; |
| 3812 | const decl_val = mod.declPtr(decl_index).val.toIntern(); |
| 3813 | if (mod.intern_pool.isRuntimeValue(decl_val)) break :implicit_ct; |
| 3814 | } |
| 3805 | 3815 | return sema.makePtrConst(block, alloc); |
| 3806 | 3816 | } |
| 3807 | 3817 | |
| ... | ... | @@ -3812,9 +3822,234 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 3812 | 3822 | return sema.fail(block, init_src, "value with comptime-only type '{}' depends on runtime control flow", .{elem_ty.fmt(mod)}); |
| 3813 | 3823 | } |
| 3814 | 3824 | |
| 3825 | // This is a runtime value. |
| 3815 | 3826 | return sema.makePtrConst(block, alloc); |
| 3816 | 3827 | } |
| 3817 | 3828 | |
| 3829 | /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved |
| 3830 | /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`. |
| 3831 | fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index { |
| 3832 | const mod = sema.mod; |
| 3833 | |
| 3834 | const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc); |
| 3835 | const ptr_info = alloc_ty.ptrInfo(mod); |
| 3836 | const elem_ty = ptr_info.child.toType(); |
| 3837 | |
| 3838 | const alloc_inst = Air.refToIndex(alloc) orelse return null; |
| 3839 | const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null; |
| 3840 | const stores = comptime_info.value.stores.items; |
| 3841 | |
| 3842 | // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known. |
| 3843 | // We will resolve and return its value. |
| 3844 | |
| 3845 | // We expect to have emitted at least one store, unless the elem type is OPV. |
| 3846 | if (stores.len == 0) { |
| 3847 | const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern(); |
| 3848 | return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value); |
| 3849 | } |
| 3850 | |
| 3851 | // In general, we want to create a comptime alloc of the correct type and |
| 3852 | // apply the stores to that alloc in order. However, before going to all |
| 3853 | // that effort, let's optimize for the common case of a single store. |
| 3854 | |
| 3855 | simple: { |
| 3856 | if (stores.len != 1) break :simple; |
| 3857 | const store_inst = stores[0]; |
| 3858 | const store_data = sema.air_instructions.items(.data)[store_inst].bin_op; |
| 3859 | if (store_data.lhs != alloc) break :simple; |
| 3860 | |
| 3861 | const val = Air.refToInterned(store_data.rhs).?; |
| 3862 | assert(mod.intern_pool.typeOf(val) == elem_ty.toIntern()); |
| 3863 | return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value); |
| 3864 | } |
| 3865 | |
| 3866 | // The simple strategy failed: we must create a mutable comptime alloc and |
| 3867 | // perform all of the runtime store operations at comptime. |
| 3868 | |
| 3869 | var anon_decl = try block.startAnonDecl(); |
| 3870 | defer anon_decl.deinit(); |
| 3871 | const decl_index = try anon_decl.finish(elem_ty, try mod.undefValue(elem_ty), ptr_info.flags.alignment); |
| 3872 | |
| 3873 | const decl_ptr = try mod.intern(.{ .ptr = .{ |
| 3874 | .ty = alloc_ty.toIntern(), |
| 3875 | .addr = .{ .mut_decl = .{ |
| 3876 | .decl = decl_index, |
| 3877 | .runtime_index = block.runtime_index, |
| 3878 | } }, |
| 3879 | } }); |
| 3880 | |
| 3881 | // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the mut decl. |
| 3882 | var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena); |
| 3883 | try ptr_mapping.ensureTotalCapacity(@intCast(stores.len)); |
| 3884 | ptr_mapping.putAssumeCapacity(alloc_inst, decl_ptr); |
| 3885 | |
| 3886 | var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len); |
| 3887 | for (stores) |store_inst| { |
| 3888 | const bin_op = sema.air_instructions.items(.data)[store_inst].bin_op; |
| 3889 | to_map.appendAssumeCapacity(Air.refToIndex(bin_op.lhs).?); |
| 3890 | } |
| 3891 | |
| 3892 | const tmp_air = sema.getTmpAir(); |
| 3893 | |
| 3894 | while (to_map.popOrNull()) |air_ptr| { |
| 3895 | if (ptr_mapping.contains(air_ptr)) continue; |
| 3896 | const PointerMethod = union(enum) { |
| 3897 | same_addr, |
| 3898 | opt_payload, |
| 3899 | eu_payload, |
| 3900 | field: u32, |
| 3901 | elem: u64, |
| 3902 | }; |
| 3903 | const inst_tag = tmp_air.instructions.items(.tag)[air_ptr]; |
| 3904 | const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) { |
| 3905 | .struct_field_ptr => blk: { |
| 3906 | const data = tmp_air.extraData( |
| 3907 | Air.StructField, |
| 3908 | tmp_air.instructions.items(.data)[air_ptr].ty_pl.payload, |
| 3909 | ).data; |
| 3910 | break :blk .{ |
| 3911 | data.struct_operand, |
| 3912 | .{ .field = data.field_index }, |
| 3913 | }; |
| 3914 | }, |
| 3915 | .struct_field_ptr_index_0, |
| 3916 | .struct_field_ptr_index_1, |
| 3917 | .struct_field_ptr_index_2, |
| 3918 | .struct_field_ptr_index_3, |
| 3919 | => .{ |
| 3920 | tmp_air.instructions.items(.data)[air_ptr].ty_op.operand, |
| 3921 | .{ .field = switch (inst_tag) { |
| 3922 | .struct_field_ptr_index_0 => 0, |
| 3923 | .struct_field_ptr_index_1 => 1, |
| 3924 | .struct_field_ptr_index_2 => 2, |
| 3925 | .struct_field_ptr_index_3 => 3, |
| 3926 | else => unreachable, |
| 3927 | } }, |
| 3928 | }, |
| 3929 | .ptr_slice_ptr_ptr => .{ |
| 3930 | tmp_air.instructions.items(.data)[air_ptr].ty_op.operand, |
| 3931 | .{ .field = Value.slice_ptr_index }, |
| 3932 | }, |
| 3933 | .ptr_slice_len_ptr => .{ |
| 3934 | tmp_air.instructions.items(.data)[air_ptr].ty_op.operand, |
| 3935 | .{ .field = Value.slice_len_index }, |
| 3936 | }, |
| 3937 | .ptr_elem_ptr => blk: { |
| 3938 | const data = tmp_air.extraData( |
| 3939 | Air.Bin, |
| 3940 | tmp_air.instructions.items(.data)[air_ptr].ty_pl.payload, |
| 3941 | ).data; |
| 3942 | const idx_val = (try sema.resolveMaybeUndefVal(data.rhs)).?; |
| 3943 | break :blk .{ |
| 3944 | data.lhs, |
| 3945 | .{ .elem = idx_val.toUnsignedInt(mod) }, |
| 3946 | }; |
| 3947 | }, |
| 3948 | .bitcast => .{ |
| 3949 | tmp_air.instructions.items(.data)[air_ptr].ty_op.operand, |
| 3950 | .same_addr, |
| 3951 | }, |
| 3952 | .optional_payload_ptr_set => .{ |
| 3953 | tmp_air.instructions.items(.data)[air_ptr].ty_op.operand, |
| 3954 | .opt_payload, |
| 3955 | }, |
| 3956 | .errunion_payload_ptr_set => .{ |
| 3957 | tmp_air.instructions.items(.data)[air_ptr].ty_op.operand, |
| 3958 | .eu_payload, |
| 3959 | }, |
| 3960 | else => unreachable, |
| 3961 | }; |
| 3962 | |
| 3963 | const decl_parent_ptr = ptr_mapping.get(Air.refToIndex(air_parent_ptr).?) orelse { |
| 3964 | // Resolve the parent pointer first. |
| 3965 | // Note that we add in what seems like the wrong order, because we're popping from the end of this array. |
| 3966 | try to_map.appendSlice(&.{ air_ptr, Air.refToIndex(air_parent_ptr).? }); |
| 3967 | continue; |
| 3968 | }; |
| 3969 | const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &mod.intern_pool).toIntern(); |
| 3970 | const new_ptr = switch (method) { |
| 3971 | .same_addr => try mod.intern_pool.getCoerced(sema.gpa, decl_parent_ptr, new_ptr_ty), |
| 3972 | .opt_payload => try mod.intern(.{ .ptr = .{ |
| 3973 | .ty = new_ptr_ty, |
| 3974 | .addr = .{ .opt_payload = decl_parent_ptr }, |
| 3975 | } }), |
| 3976 | .eu_payload => try mod.intern(.{ .ptr = .{ |
| 3977 | .ty = new_ptr_ty, |
| 3978 | .addr = .{ .eu_payload = decl_parent_ptr }, |
| 3979 | } }), |
| 3980 | .field => |field_idx| try mod.intern(.{ .ptr = .{ |
| 3981 | .ty = new_ptr_ty, |
| 3982 | .addr = .{ .field = .{ |
| 3983 | .base = decl_parent_ptr, |
| 3984 | .index = field_idx, |
| 3985 | } }, |
| 3986 | } }), |
| 3987 | .elem => |elem_idx| (try decl_parent_ptr.toValue().elemPtr(new_ptr_ty.toType(), @intCast(elem_idx), mod)).toIntern(), |
| 3988 | }; |
| 3989 | try ptr_mapping.put(air_ptr, new_ptr); |
| 3990 | } |
| 3991 | |
| 3992 | // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime. |
| 3993 | |
| 3994 | for (stores) |store_inst| { |
| 3995 | switch (sema.air_instructions.items(.tag)[store_inst]) { |
| 3996 | .set_union_tag => { |
| 3997 | // If this tag has an OPV payload, there won't be a corresponding |
| 3998 | // store instruction, so we must set the union payload now. |
| 3999 | const bin_op = sema.air_instructions.items(.data)[store_inst].bin_op; |
| 4000 | const air_ptr_inst = Air.refToIndex(bin_op.lhs).?; |
| 4001 | const tag_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?; |
| 4002 | const union_ty = sema.typeOf(bin_op.lhs).childType(mod); |
| 4003 | const payload_ty = union_ty.unionFieldType(tag_val, mod); |
| 4004 | if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| { |
| 4005 | const new_ptr = ptr_mapping.get(air_ptr_inst).?; |
| 4006 | const store_val = try mod.unionValue(union_ty, tag_val, payload_val); |
| 4007 | try sema.storePtrVal(block, .unneeded, new_ptr.toValue(), store_val, union_ty); |
| 4008 | } |
| 4009 | }, |
| 4010 | .store, .store_safe => { |
| 4011 | const bin_op = sema.air_instructions.items(.data)[store_inst].bin_op; |
| 4012 | const air_ptr_inst = Air.refToIndex(bin_op.lhs).?; |
| 4013 | const store_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?; |
| 4014 | const new_ptr = ptr_mapping.get(air_ptr_inst).?; |
| 4015 | try sema.storePtrVal(block, .unneeded, new_ptr.toValue(), store_val, mod.intern_pool.typeOf(store_val.toIntern()).toType()); |
| 4016 | }, |
| 4017 | else => unreachable, |
| 4018 | } |
| 4019 | } |
| 4020 | |
| 4021 | // The value is finalized - load it! |
| 4022 | const val = (try sema.pointerDeref(block, .unneeded, decl_ptr.toValue(), alloc_ty)).?.toIntern(); |
| 4023 | return sema.finishResolveComptimeKnownAllocValue(val, alloc_inst, comptime_info.value); |
| 4024 | } |
| 4025 | |
| 4026 | /// Given the resolved comptime-known value, rewrites the dead AIR to not |
| 4027 | /// create a runtime stack allocation. |
| 4028 | /// Same return type as `resolveComptimeKnownAllocValue` so we can tail call. |
| 4029 | fn finishResolveComptimeKnownAllocValue(sema: *Sema, result_val: InternPool.Index, alloc_inst: Air.Inst.Index, comptime_info: MaybeComptimeAlloc) CompileError!?InternPool.Index { |
| 4030 | // We're almost done - we have the resolved comptime value. We just need to |
| 4031 | // eliminate the now-dead runtime instructions. |
| 4032 | |
| 4033 | // We will rewrite the AIR to eliminate the alloc and all stores to it. |
| 4034 | // This will cause instructions deriving field pointers etc of the alloc to |
| 4035 | // become invalid, however, since we are removing all stores to those pointers, |
| 4036 | // they will be eliminated by Liveness before they reach codegen. |
| 4037 | |
| 4038 | // The specifics of this instruction aren't really important: we just want |
| 4039 | // Liveness to elide it. |
| 4040 | const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{ .ty = .u8_type, .operand = .zero_u8 } } }; |
| 4041 | |
| 4042 | sema.air_instructions.set(alloc_inst, nop_inst); |
| 4043 | for (comptime_info.stores.items) |store_inst| { |
| 4044 | sema.air_instructions.set(store_inst, nop_inst); |
| 4045 | } |
| 4046 | for (comptime_info.non_elideable_pointers.items) |ptr_inst| { |
| 4047 | sema.air_instructions.set(ptr_inst, nop_inst); |
| 4048 | } |
| 4049 | |
| 4050 | return result_val; |
| 4051 | } |
| 4052 | |
| 3818 | 4053 | fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { |
| 3819 | 4054 | const mod = sema.mod; |
| 3820 | 4055 | const alloc_ty = sema.typeOf(alloc); |
| ... | ... | @@ -3868,7 +4103,11 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 3868 | 4103 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 3869 | 4104 | }); |
| 3870 | 4105 | try sema.queueFullTypeResolution(var_ty); |
| 3871 | | return block.addTy(.alloc, ptr_type); |
| 4106 | const ptr = try block.addTy(.alloc, ptr_type); |
| 4107 | const ptr_inst = Air.refToIndex(ptr).?; |
| 4108 | try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); |
| 4109 | try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst); |
| 4110 | return ptr; |
| 3872 | 4111 | } |
| 3873 | 4112 | |
| 3874 | 4113 | fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -3925,6 +4164,8 @@ fn zirAllocInferred( |
| 3925 | 4164 | } }, |
| 3926 | 4165 | }); |
| 3927 | 4166 | try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); |
| 4167 | try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); |
| 4168 | try sema.base_allocs.put(sema.gpa, result_index, result_index); |
| 3928 | 4169 | return Air.indexToRef(result_index); |
| 3929 | 4170 | } |
| 3930 | 4171 | |
| ... | ... | @@ -3992,91 +4233,15 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3992 | 4233 | |
| 3993 | 4234 | if (!ia1.is_const) { |
| 3994 | 4235 | try sema.validateVarType(block, ty_src, final_elem_ty, false); |
| 3995 | | } else ct: { |
| 3996 | | // Detect if the value is comptime-known. In such case, the |
| 3997 | | // last 3 AIR instructions of the block will look like this: |
| 3998 | | // |
| 3999 | | // %a = inferred_alloc |
| 4000 | | // %b = bitcast(%a) |
| 4001 | | // %c = store(%b, %d) |
| 4002 | | // |
| 4003 | | // If `%d` is comptime-known, then we want to store the value |
| 4004 | | // inside an anonymous Decl and then erase these three AIR |
| 4005 | | // instructions from the block, replacing the inst_map entry |
| 4006 | | // corresponding to the ZIR alloc instruction with a constant |
| 4007 | | // decl_ref pointing at our new Decl. |
| 4008 | | // dbg_stmt instructions may be interspersed into this pattern |
| 4009 | | // which must be ignored. |
| 4010 | | if (block.instructions.items.len < 3) break :ct; |
| 4011 | | var search_index: usize = block.instructions.items.len; |
| 4012 | | const air_tags = sema.air_instructions.items(.tag); |
| 4013 | | const air_datas = sema.air_instructions.items(.data); |
| 4014 | | |
| 4015 | | const store_inst = while (true) { |
| 4016 | | if (search_index == 0) break :ct; |
| 4017 | | search_index -= 1; |
| 4018 | | |
| 4019 | | const candidate = block.instructions.items[search_index]; |
| 4020 | | switch (air_tags[candidate]) { |
| 4021 | | .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, |
| 4022 | | .store, .store_safe => break candidate, |
| 4023 | | else => break :ct, |
| 4024 | | } |
| 4025 | | }; |
| 4026 | | |
| 4027 | | const bitcast_inst = while (true) { |
| 4028 | | if (search_index == 0) break :ct; |
| 4029 | | search_index -= 1; |
| 4030 | | |
| 4031 | | const candidate = block.instructions.items[search_index]; |
| 4032 | | switch (air_tags[candidate]) { |
| 4033 | | .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, |
| 4034 | | .bitcast => break candidate, |
| 4035 | | else => break :ct, |
| 4036 | | } |
| 4037 | | }; |
| 4038 | | |
| 4039 | | while (true) { |
| 4040 | | if (search_index == 0) break :ct; |
| 4041 | | search_index -= 1; |
| 4042 | | |
| 4043 | | const candidate = block.instructions.items[search_index]; |
| 4044 | | if (candidate == ptr_inst) break; |
| 4045 | | switch (air_tags[candidate]) { |
| 4046 | | .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue, |
| 4047 | | else => break :ct, |
| 4048 | | } |
| 4049 | | } |
| 4050 | | |
| 4051 | | const store_op = air_datas[store_inst].bin_op; |
| 4052 | | const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct; |
| 4053 | | if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct; |
| 4054 | | if (air_datas[bitcast_inst].ty_op.operand != ptr) break :ct; |
| 4055 | | |
| 4056 | | const new_decl_index = d: { |
| 4057 | | var anon_decl = try block.startAnonDecl(); |
| 4058 | | defer anon_decl.deinit(); |
| 4059 | | const new_decl_index = try anon_decl.finish(final_elem_ty, store_val, ia1.alignment); |
| 4060 | | break :d new_decl_index; |
| 4061 | | }; |
| 4062 | | try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); |
| 4063 | | |
| 4064 | | // Remove the instruction from the block so that codegen does not see it. |
| 4065 | | block.instructions.shrinkRetainingCapacity(search_index); |
| 4066 | | try sema.maybeQueueFuncBodyAnalysis(new_decl_index); |
| 4067 | | |
| 4068 | | if (std.debug.runtime_safety) { |
| 4069 | | // The inferred_alloc should never be referenced again |
| 4070 | | sema.air_instructions.set(ptr_inst, .{ .tag = undefined, .data = undefined }); |
| 4071 | | } |
| 4072 | | |
| 4073 | | const interned = try mod.intern(.{ .ptr = .{ |
| 4074 | | .ty = final_ptr_ty.toIntern(), |
| 4075 | | .addr = .{ .decl = new_decl_index }, |
| 4076 | | } }); |
| 4236 | } else if (try sema.resolveComptimeKnownAllocValue(block, ptr, final_ptr_ty)) |val| { |
| 4237 | var anon_decl = try block.startAnonDecl(); |
| 4238 | defer anon_decl.deinit(); |
| 4239 | const new_decl_index = try anon_decl.finish(final_elem_ty, val.toValue(), ia1.alignment); |
| 4240 | const new_mut_ptr = Air.refToInterned(try sema.analyzeDeclRef(new_decl_index)).?.toValue(); |
| 4241 | const new_const_ptr = (try mod.getCoerced(new_mut_ptr, final_ptr_ty)).toIntern(); |
| 4077 | 4242 | |
| 4078 | 4243 | // Remap the ZIR oeprand to the resolved pointer value |
| 4079 | | sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(interned)); |
| 4244 | sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(new_const_ptr)); |
| 4080 | 4245 | |
| 4081 | 4246 | // Unless the block is comptime, `alloc_inferred` always produces |
| 4082 | 4247 | // a runtime constant. The final inferred type needs to be |
| ... | ... | @@ -4199,6 +4364,7 @@ fn zirArrayBasePtr( |
| 4199 | 4364 | .Array, .Vector => return base_ptr, |
| 4200 | 4365 | .Struct => if (elem_ty.isTuple(mod)) { |
| 4201 | 4366 | // TODO validate element count |
| 4367 | try sema.checkKnownAllocPtr(start_ptr, base_ptr); |
| 4202 | 4368 | return base_ptr; |
| 4203 | 4369 | }, |
| 4204 | 4370 | else => {}, |
| ... | ... | @@ -4225,7 +4391,10 @@ fn zirFieldBasePtr( |
| 4225 | 4391 | |
| 4226 | 4392 | const elem_ty = sema.typeOf(base_ptr).childType(mod); |
| 4227 | 4393 | switch (elem_ty.zigTypeTag(mod)) { |
| 4228 | | .Struct, .Union => return base_ptr, |
| 4394 | .Struct, .Union => { |
| 4395 | try sema.checkKnownAllocPtr(start_ptr, base_ptr); |
| 4396 | return base_ptr; |
| 4397 | }, |
| 4229 | 4398 | else => {}, |
| 4230 | 4399 | } |
| 4231 | 4400 | return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); |
| ... | ... | @@ -4636,7 +4805,8 @@ fn validateUnionInit( |
| 4636 | 4805 | } |
| 4637 | 4806 | |
| 4638 | 4807 | const new_tag = Air.internedToRef(tag_val.toIntern()); |
| 4639 | | _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag); |
| 4808 | const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, new_tag); |
| 4809 | try sema.checkComptimeKnownStore(block, set_tag_inst); |
| 4640 | 4810 | } |
| 4641 | 4811 | |
| 4642 | 4812 | fn validateStructInit( |
| ... | ... | @@ -4939,6 +5109,7 @@ fn validateStructInit( |
| 4939 | 5109 | try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) |
| 4940 | 5110 | else |
| 4941 | 5111 | try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true); |
| 5112 | try sema.checkKnownAllocPtr(struct_ptr, default_field_ptr); |
| 4942 | 5113 | const init = Air.internedToRef(field_values[i]); |
| 4943 | 5114 | try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); |
| 4944 | 5115 | } |
| ... | ... | @@ -5366,6 +5537,7 @@ fn storeToInferredAlloc( |
| 5366 | 5537 | // Create a store instruction as a placeholder. This will be replaced by a |
| 5367 | 5538 | // proper store sequence once we know the stored type. |
| 5368 | 5539 | const dummy_store = try block.addBinOp(.store, ptr, operand); |
| 5540 | try sema.checkComptimeKnownStore(block, dummy_store); |
| 5369 | 5541 | // Add the stored instruction to the set we will use to resolve peer types |
| 5370 | 5542 | // for the inferred allocation. |
| 5371 | 5543 | try inferred_alloc.prongs.append(sema.arena, .{ |
| ... | ... | @@ -8663,7 +8835,8 @@ fn analyzeOptionalPayloadPtr( |
| 8663 | 8835 | // If the pointer resulting from this function was stored at comptime, |
| 8664 | 8836 | // the optional non-null bit would be set that way. But in this case, |
| 8665 | 8837 | // we need to emit a runtime instruction to do it. |
| 8666 | | _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); |
| 8838 | const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); |
| 8839 | try sema.checkKnownAllocPtr(optional_ptr, opt_payload_ptr); |
| 8667 | 8840 | } |
| 8668 | 8841 | return Air.internedToRef((try mod.intern(.{ .ptr = .{ |
| 8669 | 8842 | .ty = child_pointer.toIntern(), |
| ... | ... | @@ -8687,11 +8860,14 @@ fn analyzeOptionalPayloadPtr( |
| 8687 | 8860 | const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); |
| 8688 | 8861 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 8689 | 8862 | } |
| 8690 | | const air_tag: Air.Inst.Tag = if (initializing) |
| 8691 | | .optional_payload_ptr_set |
| 8692 | | else |
| 8693 | | .optional_payload_ptr; |
| 8694 | | return block.addTyOp(air_tag, child_pointer, optional_ptr); |
| 8863 | |
| 8864 | if (initializing) { |
| 8865 | const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); |
| 8866 | try sema.checkKnownAllocPtr(optional_ptr, opt_payload_ptr); |
| 8867 | return opt_payload_ptr; |
| 8868 | } else { |
| 8869 | return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr); |
| 8870 | } |
| 8695 | 8871 | } |
| 8696 | 8872 | |
| 8697 | 8873 | /// Value in, value out. |
| ... | ... | @@ -8851,7 +9027,8 @@ fn analyzeErrUnionPayloadPtr( |
| 8851 | 9027 | // the error union error code would be set that way. But in this case, |
| 8852 | 9028 | // we need to emit a runtime instruction to do it. |
| 8853 | 9029 | try sema.requireRuntimeBlock(block, src, null); |
| 8854 | | _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); |
| 9030 | const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); |
| 9031 | try sema.checkKnownAllocPtr(operand, eu_payload_ptr); |
| 8855 | 9032 | } |
| 8856 | 9033 | return Air.internedToRef((try mod.intern(.{ .ptr = .{ |
| 8857 | 9034 | .ty = operand_pointer_ty.toIntern(), |
| ... | ... | @@ -8878,11 +9055,13 @@ fn analyzeErrUnionPayloadPtr( |
| 8878 | 9055 | try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); |
| 8879 | 9056 | } |
| 8880 | 9057 | |
| 8881 | | const air_tag: Air.Inst.Tag = if (initializing) |
| 8882 | | .errunion_payload_ptr_set |
| 8883 | | else |
| 8884 | | .unwrap_errunion_payload_ptr; |
| 8885 | | return block.addTyOp(air_tag, operand_pointer_ty, operand); |
| 9058 | if (initializing) { |
| 9059 | const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); |
| 9060 | try sema.checkKnownAllocPtr(operand, eu_payload_ptr); |
| 9061 | return eu_payload_ptr; |
| 9062 | } else { |
| 9063 | return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand); |
| 9064 | } |
| 8886 | 9065 | } |
| 8887 | 9066 | |
| 8888 | 9067 | /// Value in, value out |
| ... | ... | @@ -22048,6 +22227,7 @@ fn ptrCastFull( |
| 22048 | 22227 | }); |
| 22049 | 22228 | } else { |
| 22050 | 22229 | assert(dest_ptr_ty.eql(dest_ty, mod)); |
| 22230 | try sema.checkKnownAllocPtr(operand, result_ptr); |
| 22051 | 22231 | return result_ptr; |
| 22052 | 22232 | } |
| 22053 | 22233 | } |
| ... | ... | @@ -22075,7 +22255,9 @@ fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst |
| 22075 | 22255 | } |
| 22076 | 22256 | |
| 22077 | 22257 | try sema.requireRuntimeBlock(block, src, null); |
| 22078 | | return block.addBitCast(dest_ty, operand); |
| 22258 | const new_ptr = try block.addBitCast(dest_ty, operand); |
| 22259 | try sema.checkKnownAllocPtr(operand, new_ptr); |
| 22260 | return new_ptr; |
| 22079 | 22261 | } |
| 22080 | 22262 | |
| 22081 | 22263 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -26161,7 +26343,9 @@ fn fieldPtr( |
| 26161 | 26343 | } |
| 26162 | 26344 | try sema.requireRuntimeBlock(block, src, null); |
| 26163 | 26345 | |
| 26164 | | return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); |
| 26346 | const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); |
| 26347 | try sema.checkKnownAllocPtr(inner_ptr, field_ptr); |
| 26348 | return field_ptr; |
| 26165 | 26349 | } else if (ip.stringEqlSlice(field_name, "len")) { |
| 26166 | 26350 | const result_ty = try sema.ptrType(.{ |
| 26167 | 26351 | .child = .usize_type, |
| ... | ... | @@ -26183,7 +26367,9 @@ fn fieldPtr( |
| 26183 | 26367 | } |
| 26184 | 26368 | try sema.requireRuntimeBlock(block, src, null); |
| 26185 | 26369 | |
| 26186 | | return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); |
| 26370 | const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); |
| 26371 | try sema.checkKnownAllocPtr(inner_ptr, field_ptr); |
| 26372 | return field_ptr; |
| 26187 | 26373 | } else { |
| 26188 | 26374 | return sema.fail( |
| 26189 | 26375 | block, |
| ... | ... | @@ -26295,14 +26481,18 @@ fn fieldPtr( |
| 26295 | 26481 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) |
| 26296 | 26482 | else |
| 26297 | 26483 | object_ptr; |
| 26298 | | return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); |
| 26484 | const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); |
| 26485 | try sema.checkKnownAllocPtr(inner_ptr, field_ptr); |
| 26486 | return field_ptr; |
| 26299 | 26487 | }, |
| 26300 | 26488 | .Union => { |
| 26301 | 26489 | const inner_ptr = if (is_pointer_to) |
| 26302 | 26490 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) |
| 26303 | 26491 | else |
| 26304 | 26492 | object_ptr; |
| 26305 | | return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); |
| 26493 | const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); |
| 26494 | try sema.checkKnownAllocPtr(inner_ptr, field_ptr); |
| 26495 | return field_ptr; |
| 26306 | 26496 | }, |
| 26307 | 26497 | else => {}, |
| 26308 | 26498 | } |
| ... | ... | @@ -27066,21 +27256,24 @@ fn elemPtr( |
| 27066 | 27256 | }; |
| 27067 | 27257 | try checkIndexable(sema, block, src, indexable_ty); |
| 27068 | 27258 | |
| 27069 | | switch (indexable_ty.zigTypeTag(mod)) { |
| 27070 | | .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), |
| 27071 | | .Struct => { |
| 27259 | const elem_ptr = switch (indexable_ty.zigTypeTag(mod)) { |
| 27260 | .Array, .Vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), |
| 27261 | .Struct => blk: { |
| 27072 | 27262 | // Tuple field access. |
| 27073 | 27263 | const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, .{ |
| 27074 | 27264 | .needed_comptime_reason = "tuple field access index must be comptime-known", |
| 27075 | 27265 | }); |
| 27076 | 27266 | const index: u32 = @intCast(index_val.toUnsignedInt(mod)); |
| 27077 | | return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); |
| 27267 | break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init); |
| 27078 | 27268 | }, |
| 27079 | 27269 | else => { |
| 27080 | 27270 | const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); |
| 27081 | 27271 | return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); |
| 27082 | 27272 | }, |
| 27083 | | } |
| 27273 | }; |
| 27274 | |
| 27275 | try sema.checkKnownAllocPtr(indexable_ptr, elem_ptr); |
| 27276 | return elem_ptr; |
| 27084 | 27277 | } |
| 27085 | 27278 | |
| 27086 | 27279 | /// Asserts that the type of indexable is pointer. |
| ... | ... | @@ -27120,20 +27313,20 @@ fn elemPtrOneLayerOnly( |
| 27120 | 27313 | }, |
| 27121 | 27314 | .One => { |
| 27122 | 27315 | const child_ty = indexable_ty.childType(mod); |
| 27123 | | switch (child_ty.zigTypeTag(mod)) { |
| 27124 | | .Array, .Vector => { |
| 27125 | | return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety); |
| 27126 | | }, |
| 27127 | | .Struct => { |
| 27316 | const elem_ptr = switch (child_ty.zigTypeTag(mod)) { |
| 27317 | .Array, .Vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), |
| 27318 | .Struct => blk: { |
| 27128 | 27319 | assert(child_ty.isTuple(mod)); |
| 27129 | 27320 | const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, .{ |
| 27130 | 27321 | .needed_comptime_reason = "tuple field access index must be comptime-known", |
| 27131 | 27322 | }); |
| 27132 | 27323 | const index: u32 = @intCast(index_val.toUnsignedInt(mod)); |
| 27133 | | return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); |
| 27324 | break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false); |
| 27134 | 27325 | }, |
| 27135 | 27326 | else => unreachable, // Guaranteed by checkIndexable |
| 27136 | | } |
| 27327 | }; |
| 27328 | try sema.checkKnownAllocPtr(indexable, elem_ptr); |
| 27329 | return elem_ptr; |
| 27137 | 27330 | }, |
| 27138 | 27331 | } |
| 27139 | 27332 | } |
| ... | ... | @@ -27660,7 +27853,9 @@ fn coerceExtra( |
| 27660 | 27853 | return sema.coerceInMemory(val, dest_ty); |
| 27661 | 27854 | } |
| 27662 | 27855 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 27663 | | return block.addBitCast(dest_ty, inst); |
| 27856 | const new_val = try block.addBitCast(dest_ty, inst); |
| 27857 | try sema.checkKnownAllocPtr(inst, new_val); |
| 27858 | return new_val; |
| 27664 | 27859 | } |
| 27665 | 27860 | |
| 27666 | 27861 | switch (dest_ty.zigTypeTag(mod)) { |
| ... | ... | @@ -29379,8 +29574,9 @@ fn storePtr2( |
| 29379 | 29574 | |
| 29380 | 29575 | // We do this after the possible comptime store above, for the case of field_ptr stores |
| 29381 | 29576 | // to unions because we want the comptime tag to be set, even if the field type is void. |
| 29382 | | if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) |
| 29577 | if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { |
| 29383 | 29578 | return; |
| 29579 | } |
| 29384 | 29580 | |
| 29385 | 29581 | if (air_tag == .bitcast) { |
| 29386 | 29582 | // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr` |
| ... | ... | @@ -29415,10 +29611,65 @@ fn storePtr2( |
| 29415 | 29611 | }); |
| 29416 | 29612 | } |
| 29417 | 29613 | |
| 29418 | | if (is_ret) { |
| 29419 | | _ = try block.addBinOp(.store, ptr, operand); |
| 29420 | | } else { |
| 29421 | | _ = try block.addBinOp(air_tag, ptr, operand); |
| 29614 | const store_inst = if (is_ret) |
| 29615 | try block.addBinOp(.store, ptr, operand) |
| 29616 | else |
| 29617 | try block.addBinOp(air_tag, ptr, operand); |
| 29618 | |
| 29619 | try sema.checkComptimeKnownStore(block, store_inst); |
| 29620 | |
| 29621 | return; |
| 29622 | } |
| 29623 | |
| 29624 | /// Given an AIR store instruction, checks whether we are performing a |
| 29625 | /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs` |
| 29626 | /// accordingly. |
| 29627 | fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref) !void { |
| 29628 | const store_inst = Air.refToIndex(store_inst_ref).?; |
| 29629 | const inst_data = sema.air_instructions.items(.data)[store_inst].bin_op; |
| 29630 | const ptr = Air.refToIndex(inst_data.lhs) orelse return; |
| 29631 | const operand = inst_data.rhs; |
| 29632 | |
| 29633 | const maybe_base_alloc = sema.base_allocs.get(ptr) orelse return; |
| 29634 | const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse return; |
| 29635 | |
| 29636 | ct: { |
| 29637 | if (null == try sema.resolveMaybeUndefVal(operand)) break :ct; |
| 29638 | if (maybe_comptime_alloc.runtime_index != block.runtime_index) break :ct; |
| 29639 | return maybe_comptime_alloc.stores.append(sema.arena, store_inst); |
| 29640 | } |
| 29641 | |
| 29642 | // Store is runtime-known |
| 29643 | _ = sema.maybe_comptime_allocs.remove(maybe_base_alloc); |
| 29644 | } |
| 29645 | |
| 29646 | /// Given an AIR instruction transforming a pointer (struct_field_ptr, |
| 29647 | /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a |
| 29648 | /// local alloc, and updates `base_allocs` accordingly. |
| 29649 | fn checkKnownAllocPtr(sema: *Sema, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void { |
| 29650 | const base_ptr_inst = Air.refToIndex(base_ptr) orelse return; |
| 29651 | const new_ptr_inst = Air.refToIndex(new_ptr) orelse return; |
| 29652 | const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return; |
| 29653 | try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst); |
| 29654 | |
| 29655 | switch (sema.air_instructions.items(.tag)[new_ptr_inst]) { |
| 29656 | .optional_payload_ptr_set, .errunion_payload_ptr_set => { |
| 29657 | const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return; |
| 29658 | try maybe_comptime_alloc.non_elideable_pointers.append(sema.arena, new_ptr_inst); |
| 29659 | }, |
| 29660 | .ptr_elem_ptr => { |
| 29661 | const tmp_air = sema.getTmpAir(); |
| 29662 | const pl_idx = tmp_air.instructions.items(.data)[new_ptr_inst].ty_pl.payload; |
| 29663 | const bin = tmp_air.extraData(Air.Bin, pl_idx).data; |
| 29664 | const index_ref = bin.rhs; |
| 29665 | |
| 29666 | // If the index value is runtime-known, this pointer is also runtime-known, so |
| 29667 | // we must in turn make the alloc value runtime-known. |
| 29668 | if (null == try sema.resolveMaybeUndefVal(index_ref)) { |
| 29669 | _ = sema.maybe_comptime_allocs.remove(alloc_inst); |
| 29670 | } |
| 29671 | }, |
| 29672 | else => {}, |
| 29422 | 29673 | } |
| 29423 | 29674 | } |
| 29424 | 29675 | |
| ... | ... | @@ -30517,7 +30768,9 @@ fn coerceCompatiblePtrs( |
| 30517 | 30768 | } else is_non_zero; |
| 30518 | 30769 | try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null); |
| 30519 | 30770 | } |
| 30520 | | return sema.bitCast(block, dest_ty, inst, inst_src, null); |
| 30771 | const new_ptr = try sema.bitCast(block, dest_ty, inst, inst_src, null); |
| 30772 | try sema.checkKnownAllocPtr(inst, new_ptr); |
| 30773 | return new_ptr; |
| 30521 | 30774 | } |
| 30522 | 30775 | |
| 30523 | 30776 | fn coerceEnumToUnion( |