1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2016-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(beam_jump_SUITE). 21 22-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, 23 init_per_group/2,end_per_group/2, 24 undefined_label/1,ambiguous_catch_try_state/1, 25 unsafe_move_elimination/1,build_tuple/1, 26 coverage/1,call_sharing/1,undecided_allocation/1]). 27 28suite() -> 29 [{ct_hooks,[ts_install_cth]}]. 30 31all() -> 32 [{group,p}]. 33 34groups() -> 35 [{p,[parallel], 36 [undefined_label, 37 ambiguous_catch_try_state, 38 unsafe_move_elimination, 39 build_tuple, 40 coverage, 41 call_sharing, 42 undecided_allocation 43 ]}]. 44 45init_per_suite(Config) -> 46 test_lib:recompile(?MODULE), 47 Config. 48 49end_per_suite(_Config) -> 50 ok. 51 52init_per_group(_GroupName, Config) -> 53 Config. 54 55end_per_group(_GroupName, Config) -> 56 Config. 57 58undefined_label(_Config) -> 59 {'EXIT',{function_clause,_}} = (catch flights(0, [], [])), 60 ok. 61 62%% Would lose a label when compiled with no_copt. 63 64flights(0, [], []) when [], 0; 0.0, [], false -> 65 clark; 66flights(_, Reproduction, introduction) when false, Reproduction -> 67 responsible. 68 69%% [ERL-209] beam_jump would share 'catch' blocks, causing an 70%% ambiguous_catch_try_state error in beam_validator. 71 72ambiguous_catch_try_state(Config) -> 73 {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} = 74 checks(42), 75 76 {'EXIT',{{try_clause,42},_}} = (catch unsafe_sharing()), 77 78 {'EXIT',{{badmatch,b},_}} = (catch ambiguous_catch_try_state_1(<<>>)), 79 {'EXIT',{{badmatch,b},_}} = (catch ambiguous_catch_try_state_1(Config)), 80 81 {'EXIT',{{badmatch,0},_}} = (catch ambiguous_catch_try_state_2()), 82 {'EXIT',{{badmatch,0},_}} = (catch ambiguous_catch_try_state_3()), 83 84 ok. 85 86river() -> song. 87 88checks(Wanted) -> 89 %% Must be one line to cause the unsafe optimization. 90 {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}. 91 92%% Must be one line to cause the unsafe optimization. Would cause beam_validator to reject the function. 93unsafe_sharing() -> try try id(42) catch parent:215 -> []; education:17 -> try 12 catch _:_ -> a end /= if false -> fy end end of [] -> if false -> a end catch _:_ -> name end. 94 95unsafe_move_elimination(_Config) -> 96 {{left,right,false},false} = unsafe_move_elimination_1(left, right, false), 97 {{false,right,false},false} = unsafe_move_elimination_1(false, right, true), 98 {{true,right,right},right} = unsafe_move_elimination_1(true, right, true), 99 [ok = unsafe_move_elimination_2(I) || I <- lists:seq(0,16)], 100 ok. 101 102unsafe_move_elimination_1(Left, Right, Simple0) -> 103 id(1), 104 105 %% The move at label 29 would be removed by beam_jump, which is unsafe because 106 %% the two select_val instructions have different source registers. 107 %% 108 %% {select_val,{y,0},{f,25},{list,[{atom,true},{f,27},{atom,false},{f,29}]}}. 109 %% ^^^^^ ^^^^^^^^^^^^^^^^^^^ 110 %% {label,27}. 111 %% {kill,{y,0}}. 112 %% {move,{y,2},{x,0}}. 113 %% {line,...}. 114 %% {call,1,{f,31}}. 115 %% {select_val,{x,0},{f,33},{list,[{atom,true},{f,35},{atom,false},{f,29}]}}. 116 %% ^^^^^ ^^^^^^^^^^^^^^^^^^^ 117 %% {label,29}. 118 %% {move,{atom,false},{y,0}}. <=== REMOVED (unsafely). 119 %% {jump,{f,37}}. 120 121 Simple = case case Simple0 of 122 false -> false; 123 true -> id(Left) 124 end 125 of 126 false -> 127 false; 128 true -> 129 id(Right) 130 end, 131 {id({Left,Right,Simple}),Simple}. 132 133unsafe_move_elimination_2(Int) -> 134 %% The type optimization pass would recognize that TagInt can only be 135 %% [0 .. 7], so the first 'case' would select_val over [0 .. 6] and swap 136 %% out the fail label with the block for 7. 137 %% 138 %% A later optimization would merge this block with 'expects_h' in the 139 %% second case, as the latter is only reachable from the former. 140 %% 141 %% ... but this broke down when the move elimination optimization didn't 142 %% take the fail label of the first select_val into account. This caused it 143 %% to believe that the only way to reach 'expects_h' was through the second 144 %% case when 'Tag' =:= 'h', which made it remove the move instruction 145 %% added in the first case, passing garbage to expects_h/2. 146 TagInt = Int band 2#111, 147 Tag = case TagInt of 148 0 -> a; 149 1 -> b; 150 2 -> c; 151 3 -> d; 152 4 -> e; 153 5 -> f; 154 6 -> g; 155 7 -> h 156 end, 157 case Tag of 158 g -> expects_g(TagInt, Tag); 159 h -> expects_h(TagInt, Tag); 160 _ -> Tag = id(Tag), ok 161 end. 162 163expects_g(6, Atom) -> 164 Atom = id(g), 165 ok. 166 167expects_h(7, Atom) -> 168 Atom = id(h), 169 ok. 170 171%% When compiled with +no_copt, beam_validator would complain about 172%% ambigous try/catch state. 173ambiguous_catch_try_state_1(<<42:false>>) -> 174 %% The beam_ssa_bsm pass will duplicate the entire second clause. 175 %% beam_jump will share the blocks with the build_stacktrace 176 %% instructions. 177 []; 178ambiguous_catch_try_state_1(V0) -> 179 try 180 try 181 receive after bad -> timeout end 182 catch 183 _:V0 -> 184 error 185 after 186 ok 187 end 188 of 189 true -> 190 ok 191 catch 192 month:power:V2 -> 193 %% A build_stacktrace instruction would be shared, causing 194 %% an ambiguous try/catch state. 195 V2 196 after 197 a = b 198 end. 199 200ambiguous_catch_try_state_2() -> 201 case 202 try 203 case false = 0 of 204 false -> 205 hand 206 end 207 catch 208 idea:[]:V1 -> 209 V1; 210 country:42 -> 211 %% if_end would be shared in an unsafe way. 212 if 0 -> way end after [] end of [] -> if $X -> "D" end 213 end. 214 215ambiguous_catch_try_state_3() -> 216 case 217 try 218 case false = 0 of 219 false -> 220 hand 221 end 222 catch 223 idea:[]:V1 -> 224 V1; 225 country:42 -> 226 %% case_end would be shared in an unsafe way. 227 case x of y -> way end after [] end of [] -> case x of $X -> "D" end 228 end. 229 230 231-record(message2, {id, p1}). 232-record(message3, {id, p1, p2}). 233 234build_tuple(_Config) -> 235 {'EXIT',{{badrecord,message3},_}} = (catch do_build_tuple(#message2{})), 236 ok. 237 238do_build_tuple(Message) -> 239 if is_record(Message, message2) -> 240 Res = {res, rand:uniform(100)}, 241 {Message#message3.id, Res} 242 end. 243 244coverage(_Config) -> 245 ok = coverage_1(ok), 246 {error,badarg} = coverage_1({error,badarg}), 247 248 gt = coverage_2(100, 42), 249 le = coverage_2(100, 999), 250 le = coverage_2([], []), 251 gt = coverage_2([], xxx), 252 253 error = coverage_3(#{key => <<"child">>}), 254 error = coverage_3(#{}), 255 ok. 256 257coverage_1(Var) -> 258 case id(Var) of 259 ok -> ok; 260 Error -> Error 261 end. 262 263%% Cover beam_jump:invert_test(is_ne_exact). 264coverage_2(Pre1, Pre2) -> 265 case 266 case Pre1 == [] of 267 false -> 268 false; 269 true -> 270 Pre2 /= [] 271 end 272 of 273 true -> 274 gt; 275 false -> 276 case Pre1 > Pre2 of 277 true -> 278 gt; 279 false -> 280 le 281 end 282 end. 283 284coverage_3(#{key := <<child>>}) when false -> 285 ok; 286coverage_3(#{}) -> 287 error. 288 289%% ERIERL-478: The validator failed to validate argument types when calls were 290%% shared and the types at the common block turned out wider than the join of 291%% each individual call site. 292call_sharing(_Config) -> 293 A_2 = {a, 1}, 294 A_3 = {a, 1, 2}, 295 296 A_2 = cs_1(id(A_2)), 297 A_3 = cs_1(id(A_3)), 298 299 B_2 = {b, 1}, 300 B_3 = {b, 1, 2}, 301 B_2 = cs_1(id(B_2)), 302 B_3 = cs_1(id(B_3)), 303 304 C_2 = {c, 1}, 305 C_3 = {c, 1, 2}, 306 {'EXIT',_} = (catch (cs_1(id(C_2)))), 307 {'EXIT',_} = (catch (cs_1(id(C_3)))), 308 309 ok. 310 311cs_1(Key) -> 312 A = case Key of 313 %% Must be a single line to trigger the bug. 314 {Tag, _, _} when Tag == a; Tag == b -> cs_2(Key); {Tag, _} when Tag == a; Tag == b -> cs_2(Key) 315 end, 316 id(A). 317 318cs_2(I) -> I. 319 320undecided_allocation(_Config) -> 321 ok = catch undecided_allocation_1(<<10:(3*7)>>), 322 {'EXIT',{{badrecord,rec},_}} = catch undecided_allocation_1(8), 323 ok. 324 325-record(rec, {}). 326undecided_allocation_1(<<10:3/integer-unit:7>>) -> 327 ok; 328undecided_allocation_1(V) -> 329 %% The record update operation would be duplicated by the beam_ssa_bssm 330 %% pass, and beam_jump would incorrectly share the resulting calls to 331 %% error/1, causing beam_validator to issue the following diagnostic 332 %% when this module was compiled with the no_type_opt option: 333 %% 334 %% Internal consistency check failed - please report this bug. 335 %% Instruction: {call_ext,1,{extfunc,erlang,error,1}} 336 %% Error: {allocated,undecided}: 337 338 << 339 <<0>> || <<0:V>> <= <<0>> 340 >>#rec{}, 341 if whatever -> [] end. 342 343 344id(I) -> 345 I. 346