| ... | ... | @@ -138,7 +138,7 @@ pub const ResultLoc = union(enum) { |
| 138 | 138 | /// There is a pointer for the expression to store its result into, however, its type |
| 139 | 139 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. |
| 140 | 140 | /// The result instruction from the expression must be ignored. |
| 141 | | block_ptr: *Module.Scope.GenZir, |
| 141 | block_ptr: *Scope.GenZir, |
| 142 | 142 | |
| 143 | 143 | pub const Strategy = struct { |
| 144 | 144 | elide_store_to_block_ptr_instructions: bool, |
| ... | ... | @@ -154,6 +154,41 @@ pub const ResultLoc = union(enum) { |
| 154 | 154 | break_operand, |
| 155 | 155 | }; |
| 156 | 156 | }; |
| 157 | |
| 158 | fn strategy(rl: ResultLoc, block_scope: *Scope.GenZir) Strategy { |
| 159 | var elide_store_to_block_ptr_instructions = false; |
| 160 | switch (rl) { |
| 161 | // In this branch there will not be any store_to_block_ptr instructions. |
| 162 | .discard, .none, .ty, .ref => return .{ |
| 163 | .tag = .break_operand, |
| 164 | .elide_store_to_block_ptr_instructions = false, |
| 165 | }, |
| 166 | // The pointer got passed through to the sub-expressions, so we will use |
| 167 | // break_void here. |
| 168 | // In this branch there will not be any store_to_block_ptr instructions. |
| 169 | .ptr => return .{ |
| 170 | .tag = .break_void, |
| 171 | .elide_store_to_block_ptr_instructions = false, |
| 172 | }, |
| 173 | .inferred_ptr, .bitcasted_ptr, .block_ptr => { |
| 174 | if (block_scope.rvalue_rl_count == block_scope.break_count) { |
| 175 | // Neither prong of the if consumed the result location, so we can |
| 176 | // use break instructions to create an rvalue. |
| 177 | return .{ |
| 178 | .tag = .break_operand, |
| 179 | .elide_store_to_block_ptr_instructions = true, |
| 180 | }; |
| 181 | } else { |
| 182 | // Allow the store_to_block_ptr instructions to remain so that |
| 183 | // semantic analysis can turn them into bitcasts. |
| 184 | return .{ |
| 185 | .tag = .break_void, |
| 186 | .elide_store_to_block_ptr_instructions = false, |
| 187 | }; |
| 188 | } |
| 189 | }, |
| 190 | } |
| 191 | } |
| 157 | 192 | }; |
| 158 | 193 | |
| 159 | 194 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref { |
| ... | ... | @@ -989,7 +1024,7 @@ fn labeledBlockExpr( |
| 989 | 1024 | .block_inst = block_inst, |
| 990 | 1025 | }), |
| 991 | 1026 | }; |
| 992 | | setBlockResultLoc(&block_scope, rl); |
| 1027 | block_scope.setBreakResultLoc(rl); |
| 993 | 1028 | defer block_scope.instructions.deinit(mod.gpa); |
| 994 | 1029 | defer block_scope.labeled_breaks.deinit(mod.gpa); |
| 995 | 1030 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); |
| ... | ... | @@ -1003,7 +1038,7 @@ fn labeledBlockExpr( |
| 1003 | 1038 | const zir_tags = gz.astgen.instructions.items(.tag); |
| 1004 | 1039 | const zir_datas = gz.astgen.instructions.items(.data); |
| 1005 | 1040 | |
| 1006 | | const strat = rlStrategy(rl, &block_scope); |
| 1041 | const strat = rl.strategy(&block_scope); |
| 1007 | 1042 | switch (strat.tag) { |
| 1008 | 1043 | .break_void => { |
| 1009 | 1044 | // The code took advantage of the result location as a pointer. |
| ... | ... | @@ -1740,7 +1775,7 @@ fn orelseCatchExpr( |
| 1740 | 1775 | .force_comptime = parent_gz.force_comptime, |
| 1741 | 1776 | .instructions = .{}, |
| 1742 | 1777 | }; |
| 1743 | | setBlockResultLoc(&block_scope, rl); |
| 1778 | block_scope.setBreakResultLoc(rl); |
| 1744 | 1779 | defer block_scope.instructions.deinit(mod.gpa); |
| 1745 | 1780 | |
| 1746 | 1781 | // This could be a pointer or value depending on the `operand_rl` parameter. |
| ... | ... | @@ -1856,7 +1891,7 @@ fn finishThenElseBlock( |
| 1856 | 1891 | ) InnerError!zir.Inst.Ref { |
| 1857 | 1892 | // We now have enough information to decide whether the result instruction should |
| 1858 | 1893 | // be communicated via result location pointer or break instructions. |
| 1859 | | const strat = rlStrategy(rl, block_scope); |
| 1894 | const strat = rl.strategy(block_scope); |
| 1860 | 1895 | const astgen = block_scope.astgen; |
| 1861 | 1896 | switch (strat.tag) { |
| 1862 | 1897 | .break_void => { |
| ... | ... | @@ -2035,7 +2070,7 @@ fn ifExpr( |
| 2035 | 2070 | .force_comptime = parent_gz.force_comptime, |
| 2036 | 2071 | .instructions = .{}, |
| 2037 | 2072 | }; |
| 2038 | | setBlockResultLoc(&block_scope, rl); |
| 2073 | block_scope.setBreakResultLoc(rl); |
| 2039 | 2074 | defer block_scope.instructions.deinit(mod.gpa); |
| 2040 | 2075 | |
| 2041 | 2076 | const cond = c: { |
| ... | ... | @@ -2190,7 +2225,7 @@ fn whileExpr( |
| 2190 | 2225 | .force_comptime = parent_gz.force_comptime, |
| 2191 | 2226 | .instructions = .{}, |
| 2192 | 2227 | }; |
| 2193 | | setBlockResultLoc(&loop_scope, rl); |
| 2228 | loop_scope.setBreakResultLoc(rl); |
| 2194 | 2229 | defer loop_scope.instructions.deinit(mod.gpa); |
| 2195 | 2230 | |
| 2196 | 2231 | var continue_scope: Scope.GenZir = .{ |
| ... | ... | @@ -2338,7 +2373,7 @@ fn forExpr( |
| 2338 | 2373 | .force_comptime = parent_gz.force_comptime, |
| 2339 | 2374 | .instructions = .{}, |
| 2340 | 2375 | }; |
| 2341 | | setBlockResultLoc(&loop_scope, rl); |
| 2376 | loop_scope.setBreakResultLoc(rl); |
| 2342 | 2377 | defer loop_scope.instructions.deinit(mod.gpa); |
| 2343 | 2378 | |
| 2344 | 2379 | var cond_scope: Scope.GenZir = .{ |
| ... | ... | @@ -2520,7 +2555,7 @@ fn switchExpr( |
| 2520 | 2555 | .force_comptime = parent_gz.force_comptime, |
| 2521 | 2556 | .instructions = .{}, |
| 2522 | 2557 | }; |
| 2523 | | setBlockResultLoc(&block_scope, rl); |
| 2558 | block_scope.setBreakResultLoc(rl); |
| 2524 | 2559 | defer block_scope.instructions.deinit(mod.gpa); |
| 2525 | 2560 | |
| 2526 | 2561 | var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa); |
| ... | ... | @@ -3911,69 +3946,3 @@ fn rvalue( |
| 3911 | 3946 | }, |
| 3912 | 3947 | } |
| 3913 | 3948 | } |
| 3914 | | |
| 3915 | | fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZir) ResultLoc.Strategy { |
| 3916 | | var elide_store_to_block_ptr_instructions = false; |
| 3917 | | switch (rl) { |
| 3918 | | // In this branch there will not be any store_to_block_ptr instructions. |
| 3919 | | .discard, .none, .ty, .ref => return .{ |
| 3920 | | .tag = .break_operand, |
| 3921 | | .elide_store_to_block_ptr_instructions = false, |
| 3922 | | }, |
| 3923 | | // The pointer got passed through to the sub-expressions, so we will use |
| 3924 | | // break_void here. |
| 3925 | | // In this branch there will not be any store_to_block_ptr instructions. |
| 3926 | | .ptr => return .{ |
| 3927 | | .tag = .break_void, |
| 3928 | | .elide_store_to_block_ptr_instructions = false, |
| 3929 | | }, |
| 3930 | | .inferred_ptr, .bitcasted_ptr, .block_ptr => { |
| 3931 | | if (block_scope.rvalue_rl_count == block_scope.break_count) { |
| 3932 | | // Neither prong of the if consumed the result location, so we can |
| 3933 | | // use break instructions to create an rvalue. |
| 3934 | | return .{ |
| 3935 | | .tag = .break_operand, |
| 3936 | | .elide_store_to_block_ptr_instructions = true, |
| 3937 | | }; |
| 3938 | | } else { |
| 3939 | | // Allow the store_to_block_ptr instructions to remain so that |
| 3940 | | // semantic analysis can turn them into bitcasts. |
| 3941 | | return .{ |
| 3942 | | .tag = .break_void, |
| 3943 | | .elide_store_to_block_ptr_instructions = false, |
| 3944 | | }; |
| 3945 | | } |
| 3946 | | }, |
| 3947 | | } |
| 3948 | | } |
| 3949 | | |
| 3950 | | fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void { |
| 3951 | | // Depending on whether the result location is a pointer or value, different |
| 3952 | | // ZIR needs to be generated. In the former case we rely on storing to the |
| 3953 | | // pointer to communicate the result, and use breakvoid; in the latter case |
| 3954 | | // the block break instructions will have the result values. |
| 3955 | | // One more complication: when the result location is a pointer, we detect |
| 3956 | | // the scenario where the result location is not consumed. In this case |
| 3957 | | // we emit ZIR for the block break instructions to have the result values, |
| 3958 | | // and then rvalue() on that to pass the value to the result location. |
| 3959 | | switch (parent_rl) { |
| 3960 | | .discard, .none, .ty, .ptr, .ref => { |
| 3961 | | block_scope.break_result_loc = parent_rl; |
| 3962 | | }, |
| 3963 | | |
| 3964 | | .inferred_ptr => |ptr| { |
| 3965 | | block_scope.rl_ptr = ptr; |
| 3966 | | block_scope.break_result_loc = .{ .block_ptr = block_scope }; |
| 3967 | | }, |
| 3968 | | |
| 3969 | | .bitcasted_ptr => |ptr| { |
| 3970 | | block_scope.rl_ptr = ptr; |
| 3971 | | block_scope.break_result_loc = .{ .block_ptr = block_scope }; |
| 3972 | | }, |
| 3973 | | |
| 3974 | | .block_ptr => |parent_block_scope| { |
| 3975 | | block_scope.rl_ptr = parent_block_scope.rl_ptr; |
| 3976 | | block_scope.break_result_loc = .{ .block_ptr = block_scope }; |
| 3977 | | }, |
| 3978 | | } |
| 3979 | | } |