| ... | @@ -111,6 +111,35 @@ prev_stack_alignment_src: ?LazySrcLoc = null, | ... | @@ -111,6 +111,35 @@ prev_stack_alignment_src: ?LazySrcLoc = null, |
| 111 | /// the struct/enum/union type created should be placed. Otherwise, it is `.none`. | 111 | /// the struct/enum/union type created should be placed. Otherwise, it is `.none`. |
| 112 | builtin_type_target_index: InternPool.Index = .none, | 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 | const std = @import("std"); | 143 | const std = @import("std"); |
| 115 | const math = std.math; | 144 | const math = std.math; |
| 116 | const mem = std.mem; | 145 | const mem = std.mem; |
| ... | @@ -840,6 +869,8 @@ pub fn deinit(sema: *Sema) void { | ... | @@ -840,6 +869,8 @@ pub fn deinit(sema: *Sema) void { |
| 840 | sema.post_hoc_blocks.deinit(gpa); | 869 | sema.post_hoc_blocks.deinit(gpa); |
| 841 | } | 870 | } |
| 842 | sema.unresolved_inferred_allocs.deinit(gpa); | 871 | sema.unresolved_inferred_allocs.deinit(gpa); |
| | 872 | sema.base_allocs.deinit(gpa); |
| | 873 | sema.maybe_comptime_allocs.deinit(gpa); |
| 843 | sema.* = undefined; | 874 | sema.* = undefined; |
| 844 | } | 875 | } |
| 845 | | 876 | |
| ... | @@ -2643,6 +2674,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE | ... | @@ -2643,6 +2674,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 2643 | .placeholder = Air.refToIndex(bitcasted_ptr).?, | 2674 | .placeholder = Air.refToIndex(bitcasted_ptr).?, |
| 2644 | }); | 2675 | }); |
| 2645 | | 2676 | |
| | 2677 | try sema.checkKnownAllocPtr(ptr, bitcasted_ptr); |
| 2646 | return bitcasted_ptr; | 2678 | return bitcasted_ptr; |
| 2647 | }, | 2679 | }, |
| 2648 | .inferred_alloc_comptime => { | 2680 | .inferred_alloc_comptime => { |
| ... | @@ -2690,7 +2722,9 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE | ... | @@ -2690,7 +2722,9 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 2690 | | 2722 | |
| 2691 | const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); | 2723 | const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); |
| 2692 | const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value); | 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 | fn coerceResultPtr( | 2730 | fn coerceResultPtr( |
| ... | @@ -3719,7 +3753,13 @@ fn zirAllocExtended( | ... | @@ -3719,7 +3753,13 @@ fn zirAllocExtended( |
| 3719 | .address_space = target_util.defaultAddressSpace(target, .local), | 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 | const result_index = try block.addInstAsIndex(.{ | 3765 | const result_index = try block.addInstAsIndex(.{ |
| ... | @@ -3730,6 +3770,10 @@ fn zirAllocExtended( | ... | @@ -3730,6 +3770,10 @@ fn zirAllocExtended( |
| 3730 | } }, | 3770 | } }, |
| 3731 | }); | 3771 | }); |
| 3732 | try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); | 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 | return Air.indexToRef(result_index); | 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,60 +3792,26 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 3748 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 3792 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 3749 | const alloc = try sema.resolveInst(inst_data.operand); | 3793 | const alloc = try sema.resolveInst(inst_data.operand); |
| 3750 | const alloc_ty = sema.typeOf(alloc); | 3794 | const alloc_ty = sema.typeOf(alloc); |
| 3751 | | 3795 | const ptr_info = alloc_ty.ptrInfo(mod); |
| 3752 | var ptr_info = alloc_ty.ptrInfo(mod); | | |
| 3753 | const elem_ty = ptr_info.child.toType(); | 3796 | const elem_ty = ptr_info.child.toType(); |
| 3754 | | 3797 | |
| 3755 | // Detect if all stores to an `.alloc` were comptime-known. | 3798 | if (try sema.resolveComptimeKnownAllocValue(block, alloc, null)) |val| { |
| 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 | | | |
| 3795 | var anon_decl = try block.startAnonDecl(); | 3799 | var anon_decl = try block.startAnonDecl(); |
| 3796 | defer anon_decl.deinit(); | 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 | // were already performed at comptime! Just make the pointer constant as normal. | 3806 | // were already performed at comptime! Just make the pointer constant as normal. |
| 3802 | implicit_ct: { | 3807 | implicit_ct: { |
| 3803 | const ptr_val = try sema.resolveMaybeUndefVal(alloc) orelse break :implicit_ct; | 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 | return sema.makePtrConst(block, alloc); | 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,9 +3822,234 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 3812 | return sema.fail(block, init_src, "value with comptime-only type '{}' depends on runtime control flow", .{elem_ty.fmt(mod)}); | 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 | return sema.makePtrConst(block, alloc); | 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 | fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { | 4053 | fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { |
| 3819 | const mod = sema.mod; | 4054 | const mod = sema.mod; |
| 3820 | const alloc_ty = sema.typeOf(alloc); | 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,7 +4103,11 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 3868 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, | 4103 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 3869 | }); | 4104 | }); |
| 3870 | try sema.queueFullTypeResolution(var_ty); | 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 | fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 4113 | fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -3925,6 +4164,8 @@ fn zirAllocInferred( | ... | @@ -3925,6 +4164,8 @@ fn zirAllocInferred( |
| 3925 | } }, | 4164 | } }, |
| 3926 | }); | 4165 | }); |
| 3927 | try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); | 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 | return Air.indexToRef(result_index); | 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,91 +4233,15 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3992 | | 4233 | |
| 3993 | if (!ia1.is_const) { | 4234 | if (!ia1.is_const) { |
| 3994 | try sema.validateVarType(block, ty_src, final_elem_ty, false); | 4235 | try sema.validateVarType(block, ty_src, final_elem_ty, false); |
| 3995 | } else ct: { | 4236 | } else if (try sema.resolveComptimeKnownAllocValue(block, ptr, final_ptr_ty)) |val| { |
| 3996 | // Detect if the value is comptime-known. In such case, the | 4237 | var anon_decl = try block.startAnonDecl(); |
| 3997 | // last 3 AIR instructions of the block will look like this: | 4238 | defer anon_decl.deinit(); |
| 3998 | // | 4239 | const new_decl_index = try anon_decl.finish(final_elem_ty, val.toValue(), ia1.alignment); |
| 3999 | // %a = inferred_alloc | 4240 | const new_mut_ptr = Air.refToInterned(try sema.analyzeDeclRef(new_decl_index)).?.toValue(); |
| 4000 | // %b = bitcast(%a) | 4241 | const new_const_ptr = (try mod.getCoerced(new_mut_ptr, final_ptr_ty)).toIntern(); |
| 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 | } }); | | |
| 4077 | | 4242 | |
| 4078 | // Remap the ZIR oeprand to the resolved pointer value | 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 | // Unless the block is comptime, `alloc_inferred` always produces | 4246 | // Unless the block is comptime, `alloc_inferred` always produces |
| 4082 | // a runtime constant. The final inferred type needs to be | 4247 | // a runtime constant. The final inferred type needs to be |
| ... | @@ -4199,6 +4364,7 @@ fn zirArrayBasePtr( | ... | @@ -4199,6 +4364,7 @@ fn zirArrayBasePtr( |
| 4199 | .Array, .Vector => return base_ptr, | 4364 | .Array, .Vector => return base_ptr, |
| 4200 | .Struct => if (elem_ty.isTuple(mod)) { | 4365 | .Struct => if (elem_ty.isTuple(mod)) { |
| 4201 | // TODO validate element count | 4366 | // TODO validate element count |
| | 4367 | try sema.checkKnownAllocPtr(start_ptr, base_ptr); |
| 4202 | return base_ptr; | 4368 | return base_ptr; |
| 4203 | }, | 4369 | }, |
| 4204 | else => {}, | 4370 | else => {}, |
| ... | @@ -4225,7 +4391,10 @@ fn zirFieldBasePtr( | ... | @@ -4225,7 +4391,10 @@ fn zirFieldBasePtr( |
| 4225 | | 4391 | |
| 4226 | const elem_ty = sema.typeOf(base_ptr).childType(mod); | 4392 | const elem_ty = sema.typeOf(base_ptr).childType(mod); |
| 4227 | switch (elem_ty.zigTypeTag(mod)) { | 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 | else => {}, | 4398 | else => {}, |
| 4230 | } | 4399 | } |
| 4231 | return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); | 4400 | return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); |
| ... | @@ -4636,7 +4805,8 @@ fn validateUnionInit( | ... | @@ -4636,7 +4805,8 @@ fn validateUnionInit( |
| 4636 | } | 4805 | } |
| 4637 | | 4806 | |
| 4638 | const new_tag = Air.internedToRef(tag_val.toIntern()); | 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 | fn validateStructInit( | 4812 | fn validateStructInit( |
| ... | @@ -4939,6 +5109,7 @@ fn validateStructInit( | ... | @@ -4939,6 +5109,7 @@ fn validateStructInit( |
| 4939 | try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) | 5109 | try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) |
| 4940 | else | 5110 | else |
| 4941 | try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true); | 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 | const init = Air.internedToRef(field_values[i]); | 5113 | const init = Air.internedToRef(field_values[i]); |
| 4943 | try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); | 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,6 +5537,7 @@ fn storeToInferredAlloc( |
| 5366 | // Create a store instruction as a placeholder. This will be replaced by a | 5537 | // Create a store instruction as a placeholder. This will be replaced by a |
| 5367 | // proper store sequence once we know the stored type. | 5538 | // proper store sequence once we know the stored type. |
| 5368 | const dummy_store = try block.addBinOp(.store, ptr, operand); | 5539 | const dummy_store = try block.addBinOp(.store, ptr, operand); |
| | 5540 | try sema.checkComptimeKnownStore(block, dummy_store); |
| 5369 | // Add the stored instruction to the set we will use to resolve peer types | 5541 | // Add the stored instruction to the set we will use to resolve peer types |
| 5370 | // for the inferred allocation. | 5542 | // for the inferred allocation. |
| 5371 | try inferred_alloc.prongs.append(sema.arena, .{ | 5543 | try inferred_alloc.prongs.append(sema.arena, .{ |
| ... | @@ -8663,7 +8835,8 @@ fn analyzeOptionalPayloadPtr( | ... | @@ -8663,7 +8835,8 @@ fn analyzeOptionalPayloadPtr( |
| 8663 | // If the pointer resulting from this function was stored at comptime, | 8835 | // If the pointer resulting from this function was stored at comptime, |
| 8664 | // the optional non-null bit would be set that way. But in this case, | 8836 | // the optional non-null bit would be set that way. But in this case, |
| 8665 | // we need to emit a runtime instruction to do it. | 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 | return Air.internedToRef((try mod.intern(.{ .ptr = .{ | 8841 | return Air.internedToRef((try mod.intern(.{ .ptr = .{ |
| 8669 | .ty = child_pointer.toIntern(), | 8842 | .ty = child_pointer.toIntern(), |
| ... | @@ -8687,11 +8860,14 @@ fn analyzeOptionalPayloadPtr( | ... | @@ -8687,11 +8860,14 @@ fn analyzeOptionalPayloadPtr( |
| 8687 | const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); | 8860 | const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); |
| 8688 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); | 8861 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 8689 | } | 8862 | } |
| 8690 | const air_tag: Air.Inst.Tag = if (initializing) | 8863 | |
| 8691 | .optional_payload_ptr_set | 8864 | if (initializing) { |
| 8692 | else | 8865 | const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); |
| 8693 | .optional_payload_ptr; | 8866 | try sema.checkKnownAllocPtr(optional_ptr, opt_payload_ptr); |
| 8694 | return block.addTyOp(air_tag, child_pointer, optional_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 | /// Value in, value out. | 8873 | /// Value in, value out. |
| ... | @@ -8851,7 +9027,8 @@ fn analyzeErrUnionPayloadPtr( | ... | @@ -8851,7 +9027,8 @@ fn analyzeErrUnionPayloadPtr( |
| 8851 | // the error union error code would be set that way. But in this case, | 9027 | // the error union error code would be set that way. But in this case, |
| 8852 | // we need to emit a runtime instruction to do it. | 9028 | // we need to emit a runtime instruction to do it. |
| 8853 | try sema.requireRuntimeBlock(block, src, null); | 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 | return Air.internedToRef((try mod.intern(.{ .ptr = .{ | 9033 | return Air.internedToRef((try mod.intern(.{ .ptr = .{ |
| 8857 | .ty = operand_pointer_ty.toIntern(), | 9034 | .ty = operand_pointer_ty.toIntern(), |
| ... | @@ -8878,11 +9055,13 @@ fn analyzeErrUnionPayloadPtr( | ... | @@ -8878,11 +9055,13 @@ fn analyzeErrUnionPayloadPtr( |
| 8878 | try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); | 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) | 9058 | if (initializing) { |
| 8882 | .errunion_payload_ptr_set | 9059 | const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); |
| 8883 | else | 9060 | try sema.checkKnownAllocPtr(operand, eu_payload_ptr); |
| 8884 | .unwrap_errunion_payload_ptr; | 9061 | return eu_payload_ptr; |
| 8885 | return block.addTyOp(air_tag, operand_pointer_ty, operand); | 9062 | } else { |
| | 9063 | return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand); |
| | 9064 | } |
| 8886 | } | 9065 | } |
| 8887 | | 9066 | |
| 8888 | /// Value in, value out | 9067 | /// Value in, value out |
| ... | @@ -22048,6 +22227,7 @@ fn ptrCastFull( | ... | @@ -22048,6 +22227,7 @@ fn ptrCastFull( |
| 22048 | }); | 22227 | }); |
| 22049 | } else { | 22228 | } else { |
| 22050 | assert(dest_ptr_ty.eql(dest_ty, mod)); | 22229 | assert(dest_ptr_ty.eql(dest_ty, mod)); |
| | 22230 | try sema.checkKnownAllocPtr(operand, result_ptr); |
| 22051 | return result_ptr; | 22231 | return result_ptr; |
| 22052 | } | 22232 | } |
| 22053 | } | 22233 | } |
| ... | @@ -22075,7 +22255,9 @@ fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst | ... | @@ -22075,7 +22255,9 @@ fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst |
| 22075 | } | 22255 | } |
| 22076 | | 22256 | |
| 22077 | try sema.requireRuntimeBlock(block, src, null); | 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 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 22263 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -26161,7 +26343,9 @@ fn fieldPtr( | ... | @@ -26161,7 +26343,9 @@ fn fieldPtr( |
| 26161 | } | 26343 | } |
| 26162 | try sema.requireRuntimeBlock(block, src, null); | 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 | } else if (ip.stringEqlSlice(field_name, "len")) { | 26349 | } else if (ip.stringEqlSlice(field_name, "len")) { |
| 26166 | const result_ty = try sema.ptrType(.{ | 26350 | const result_ty = try sema.ptrType(.{ |
| 26167 | .child = .usize_type, | 26351 | .child = .usize_type, |
| ... | @@ -26183,7 +26367,9 @@ fn fieldPtr( | ... | @@ -26183,7 +26367,9 @@ fn fieldPtr( |
| 26183 | } | 26367 | } |
| 26184 | try sema.requireRuntimeBlock(block, src, null); | 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 | } else { | 26373 | } else { |
| 26188 | return sema.fail( | 26374 | return sema.fail( |
| 26189 | block, | 26375 | block, |
| ... | @@ -26295,14 +26481,18 @@ fn fieldPtr( | ... | @@ -26295,14 +26481,18 @@ fn fieldPtr( |
| 26295 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) | 26481 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) |
| 26296 | else | 26482 | else |
| 26297 | object_ptr; | 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 | .Union => { | 26488 | .Union => { |
| 26301 | const inner_ptr = if (is_pointer_to) | 26489 | const inner_ptr = if (is_pointer_to) |
| 26302 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) | 26490 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) |
| 26303 | else | 26491 | else |
| 26304 | object_ptr; | 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 | else => {}, | 26497 | else => {}, |
| 26308 | } | 26498 | } |
| ... | @@ -27066,21 +27256,24 @@ fn elemPtr( | ... | @@ -27066,21 +27256,24 @@ fn elemPtr( |
| 27066 | }; | 27256 | }; |
| 27067 | try checkIndexable(sema, block, src, indexable_ty); | 27257 | try checkIndexable(sema, block, src, indexable_ty); |
| 27068 | | 27258 | |
| 27069 | switch (indexable_ty.zigTypeTag(mod)) { | 27259 | const elem_ptr = 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), | 27260 | .Array, .Vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), |
| 27071 | .Struct => { | 27261 | .Struct => blk: { |
| 27072 | // Tuple field access. | 27262 | // Tuple field access. |
| 27073 | const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, .{ | 27263 | const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, .{ |
| 27074 | .needed_comptime_reason = "tuple field access index must be comptime-known", | 27264 | .needed_comptime_reason = "tuple field access index must be comptime-known", |
| 27075 | }); | 27265 | }); |
| 27076 | const index: u32 = @intCast(index_val.toUnsignedInt(mod)); | 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 | else => { | 27269 | else => { |
| 27080 | const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); | 27270 | const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); |
| 27081 | return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); | 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 | /// Asserts that the type of indexable is pointer. | 27279 | /// Asserts that the type of indexable is pointer. |
| ... | @@ -27120,20 +27313,20 @@ fn elemPtrOneLayerOnly( | ... | @@ -27120,20 +27313,20 @@ fn elemPtrOneLayerOnly( |
| 27120 | }, | 27313 | }, |
| 27121 | .One => { | 27314 | .One => { |
| 27122 | const child_ty = indexable_ty.childType(mod); | 27315 | const child_ty = indexable_ty.childType(mod); |
| 27123 | switch (child_ty.zigTypeTag(mod)) { | 27316 | const elem_ptr = switch (child_ty.zigTypeTag(mod)) { |
| 27124 | .Array, .Vector => { | 27317 | .Array, .Vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), |
| 27125 | return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety); | 27318 | .Struct => blk: { |
| 27126 | }, | | |
| 27127 | .Struct => { | | |
| 27128 | assert(child_ty.isTuple(mod)); | 27319 | assert(child_ty.isTuple(mod)); |
| 27129 | const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, .{ | 27320 | const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, .{ |
| 27130 | .needed_comptime_reason = "tuple field access index must be comptime-known", | 27321 | .needed_comptime_reason = "tuple field access index must be comptime-known", |
| 27131 | }); | 27322 | }); |
| 27132 | const index: u32 = @intCast(index_val.toUnsignedInt(mod)); | 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 | else => unreachable, // Guaranteed by checkIndexable | 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,7 +27853,9 @@ fn coerceExtra( |
| 27660 | return sema.coerceInMemory(val, dest_ty); | 27853 | return sema.coerceInMemory(val, dest_ty); |
| 27661 | } | 27854 | } |
| 27662 | try sema.requireRuntimeBlock(block, inst_src, null); | 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 | switch (dest_ty.zigTypeTag(mod)) { | 27861 | switch (dest_ty.zigTypeTag(mod)) { |
| ... | @@ -29379,8 +29574,9 @@ fn storePtr2( | ... | @@ -29379,8 +29574,9 @@ fn storePtr2( |
| 29379 | | 29574 | |
| 29380 | // We do this after the possible comptime store above, for the case of field_ptr stores | 29575 | // We do this after the possible comptime store above, for the case of field_ptr stores |
| 29381 | // to unions because we want the comptime tag to be set, even if the field type is void. | 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 | return; | 29578 | return; |
| | 29579 | } |
| 29384 | | 29580 | |
| 29385 | if (air_tag == .bitcast) { | 29581 | if (air_tag == .bitcast) { |
| 29386 | // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr` | 29582 | // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr` |
| ... | @@ -29415,10 +29611,65 @@ fn storePtr2( | ... | @@ -29415,10 +29611,65 @@ fn storePtr2( |
| 29415 | }); | 29611 | }); |
| 29416 | } | 29612 | } |
| 29417 | | 29613 | |
| 29418 | if (is_ret) { | 29614 | const store_inst = if (is_ret) |
| 29419 | _ = try block.addBinOp(.store, ptr, operand); | 29615 | try block.addBinOp(.store, ptr, operand) |
| 29420 | } else { | 29616 | else |
| 29421 | _ = try block.addBinOp(air_tag, ptr, operand); | 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,7 +30768,9 @@ fn coerceCompatiblePtrs( |
| 30517 | } else is_non_zero; | 30768 | } else is_non_zero; |
| 30518 | try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null); | 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 | fn coerceEnumToUnion( | 30776 | fn coerceEnumToUnion( |