| ... | @@ -138,7 +138,7 @@ pub const ResultLoc = union(enum) { | ... | @@ -138,7 +138,7 @@ pub const ResultLoc = union(enum) { |
| 138 | /// There is a pointer for the expression to store its result into, however, its type | 138 | /// There is a pointer for the expression to store its result into, however, its type |
| 139 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. | 139 | /// is inferred based on peer type resolution for a `zir.Inst.Block`. |
| 140 | /// The result instruction from the expression must be ignored. | 140 | /// The result instruction from the expression must be ignored. |
| 141 | block_ptr: *Module.Scope.GenZir, | 141 | block_ptr: *Scope.GenZir, |
| 142 | | 142 | |
| 143 | pub const Strategy = struct { | 143 | pub const Strategy = struct { |
| 144 | elide_store_to_block_ptr_instructions: bool, | 144 | elide_store_to_block_ptr_instructions: bool, |
| ... | @@ -154,6 +154,41 @@ pub const ResultLoc = union(enum) { | ... | @@ -154,6 +154,41 @@ pub const ResultLoc = union(enum) { |
| 154 | break_operand, | 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 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref { | 194 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref { |
| ... | @@ -989,7 +1024,7 @@ fn labeledBlockExpr( | ... | @@ -989,7 +1024,7 @@ fn labeledBlockExpr( |
| 989 | .block_inst = block_inst, | 1024 | .block_inst = block_inst, |
| 990 | }), | 1025 | }), |
| 991 | }; | 1026 | }; |
| 992 | setBlockResultLoc(&block_scope, rl); | 1027 | block_scope.setBreakResultLoc(rl); |
| 993 | defer block_scope.instructions.deinit(mod.gpa); | 1028 | defer block_scope.instructions.deinit(mod.gpa); |
| 994 | defer block_scope.labeled_breaks.deinit(mod.gpa); | 1029 | defer block_scope.labeled_breaks.deinit(mod.gpa); |
| 995 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); | 1030 | defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa); |
| ... | @@ -1003,7 +1038,7 @@ fn labeledBlockExpr( | ... | @@ -1003,7 +1038,7 @@ fn labeledBlockExpr( |
| 1003 | const zir_tags = gz.astgen.instructions.items(.tag); | 1038 | const zir_tags = gz.astgen.instructions.items(.tag); |
| 1004 | const zir_datas = gz.astgen.instructions.items(.data); | 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 | switch (strat.tag) { | 1042 | switch (strat.tag) { |
| 1008 | .break_void => { | 1043 | .break_void => { |
| 1009 | // The code took advantage of the result location as a pointer. | 1044 | // The code took advantage of the result location as a pointer. |
| ... | @@ -1740,7 +1775,7 @@ fn orelseCatchExpr( | ... | @@ -1740,7 +1775,7 @@ fn orelseCatchExpr( |
| 1740 | .force_comptime = parent_gz.force_comptime, | 1775 | .force_comptime = parent_gz.force_comptime, |
| 1741 | .instructions = .{}, | 1776 | .instructions = .{}, |
| 1742 | }; | 1777 | }; |
| 1743 | setBlockResultLoc(&block_scope, rl); | 1778 | block_scope.setBreakResultLoc(rl); |
| 1744 | defer block_scope.instructions.deinit(mod.gpa); | 1779 | defer block_scope.instructions.deinit(mod.gpa); |
| 1745 | | 1780 | |
| 1746 | // This could be a pointer or value depending on the `operand_rl` parameter. | 1781 | // This could be a pointer or value depending on the `operand_rl` parameter. |
| ... | @@ -1856,7 +1891,7 @@ fn finishThenElseBlock( | ... | @@ -1856,7 +1891,7 @@ fn finishThenElseBlock( |
| 1856 | ) InnerError!zir.Inst.Ref { | 1891 | ) InnerError!zir.Inst.Ref { |
| 1857 | // We now have enough information to decide whether the result instruction should | 1892 | // We now have enough information to decide whether the result instruction should |
| 1858 | // be communicated via result location pointer or break instructions. | 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 | const astgen = block_scope.astgen; | 1895 | const astgen = block_scope.astgen; |
| 1861 | switch (strat.tag) { | 1896 | switch (strat.tag) { |
| 1862 | .break_void => { | 1897 | .break_void => { |
| ... | @@ -2035,7 +2070,7 @@ fn ifExpr( | ... | @@ -2035,7 +2070,7 @@ fn ifExpr( |
| 2035 | .force_comptime = parent_gz.force_comptime, | 2070 | .force_comptime = parent_gz.force_comptime, |
| 2036 | .instructions = .{}, | 2071 | .instructions = .{}, |
| 2037 | }; | 2072 | }; |
| 2038 | setBlockResultLoc(&block_scope, rl); | 2073 | block_scope.setBreakResultLoc(rl); |
| 2039 | defer block_scope.instructions.deinit(mod.gpa); | 2074 | defer block_scope.instructions.deinit(mod.gpa); |
| 2040 | | 2075 | |
| 2041 | const cond = c: { | 2076 | const cond = c: { |
| ... | @@ -2190,7 +2225,7 @@ fn whileExpr( | ... | @@ -2190,7 +2225,7 @@ fn whileExpr( |
| 2190 | .force_comptime = parent_gz.force_comptime, | 2225 | .force_comptime = parent_gz.force_comptime, |
| 2191 | .instructions = .{}, | 2226 | .instructions = .{}, |
| 2192 | }; | 2227 | }; |
| 2193 | setBlockResultLoc(&loop_scope, rl); | 2228 | loop_scope.setBreakResultLoc(rl); |
| 2194 | defer loop_scope.instructions.deinit(mod.gpa); | 2229 | defer loop_scope.instructions.deinit(mod.gpa); |
| 2195 | | 2230 | |
| 2196 | var continue_scope: Scope.GenZir = .{ | 2231 | var continue_scope: Scope.GenZir = .{ |
| ... | @@ -2338,7 +2373,7 @@ fn forExpr( | ... | @@ -2338,7 +2373,7 @@ fn forExpr( |
| 2338 | .force_comptime = parent_gz.force_comptime, | 2373 | .force_comptime = parent_gz.force_comptime, |
| 2339 | .instructions = .{}, | 2374 | .instructions = .{}, |
| 2340 | }; | 2375 | }; |
| 2341 | setBlockResultLoc(&loop_scope, rl); | 2376 | loop_scope.setBreakResultLoc(rl); |
| 2342 | defer loop_scope.instructions.deinit(mod.gpa); | 2377 | defer loop_scope.instructions.deinit(mod.gpa); |
| 2343 | | 2378 | |
| 2344 | var cond_scope: Scope.GenZir = .{ | 2379 | var cond_scope: Scope.GenZir = .{ |
| ... | @@ -2520,7 +2555,7 @@ fn switchExpr( | ... | @@ -2520,7 +2555,7 @@ fn switchExpr( |
| 2520 | .force_comptime = parent_gz.force_comptime, | 2555 | .force_comptime = parent_gz.force_comptime, |
| 2521 | .instructions = .{}, | 2556 | .instructions = .{}, |
| 2522 | }; | 2557 | }; |
| 2523 | setBlockResultLoc(&block_scope, rl); | 2558 | block_scope.setBreakResultLoc(rl); |
| 2524 | defer block_scope.instructions.deinit(mod.gpa); | 2559 | defer block_scope.instructions.deinit(mod.gpa); |
| 2525 | | 2560 | |
| 2526 | var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa); | 2561 | var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa); |
| ... | @@ -3911,69 +3946,3 @@ fn rvalue( | ... | @@ -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 | } | | |