1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2018-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%% Purpose: Convert the Kernel Erlang format to the SSA format. 21 22-module(beam_kernel_to_ssa). 23 24%% The main interface. 25-export([module/2]). 26 27-import(lists, [all/2,append/1,flatmap/2,foldl/3, 28 keysort/2,mapfoldl/3,map/2,member/2, 29 reverse/1,reverse/2,sort/1]). 30 31-include("v3_kernel.hrl"). 32-include("beam_ssa.hrl"). 33 34-type label() :: beam_ssa:label(). 35 36%% Main codegen structure. 37-record(cg, {lcount=1 :: label(), %Label counter 38 bfail=1 :: label(), 39 catch_label=none :: 'none' | label(), 40 vars=#{} :: map(), %Defined variables. 41 break=0 :: label(), %Break label 42 recv=0 :: label(), %Receive label 43 ultimate_failure=0 :: label(), %Label for ultimate match failure. 44 labels=#{} :: #{atom() => label()}, 45 no_make_fun3=false :: boolean() 46 }). 47 48%% Internal records. 49-record(cg_break, {args :: [beam_ssa:value()], 50 phi :: label() 51 }). 52-record(cg_phi, {vars :: [beam_ssa:b_var()] 53 }). 54-record(cg_unreachable, {}). 55 56-spec module(#k_mdef{}, [compile:option()]) -> {'ok',#b_module{}}. 57 58module(#k_mdef{name=Mod,exports=Es,attributes=Attr,body=Forms}, Opts) -> 59 NoMakeFun3 = proplists:get_bool(no_make_fun3, Opts), 60 Body = functions(Forms, Mod, NoMakeFun3), 61 Module = #b_module{name=Mod,exports=Es,attributes=Attr,body=Body}, 62 {ok,Module}. 63 64functions(Forms, Mod, NoMakeFun3) -> 65 [function(F, Mod, NoMakeFun3) || F <- Forms]. 66 67function(#k_fdef{anno=Anno0,func=Name,arity=Arity, 68 vars=As0,body=Kb}, Mod, NoMakeFun3) -> 69 try 70 #k_match{} = Kb, %Assertion. 71 72 %% Generate the SSA form immediate format. 73 St0 = #cg{no_make_fun3=NoMakeFun3}, 74 {As,St1} = new_ssa_vars(As0, St0), 75 {Asm,St} = cg_fun(Kb, St1), 76 Anno1 = line_anno(Anno0), 77 Anno = Anno1#{func_info=>{Mod,Name,Arity}}, 78 #b_function{anno=Anno,args=As,bs=Asm,cnt=St#cg.lcount} 79 catch 80 Class:Error:Stack -> 81 io:fwrite("Function: ~w/~w\n", [Name,Arity]), 82 erlang:raise(Class, Error, Stack) 83 end. 84 85%% cg_fun([Lkexpr], [HeadVar], State) -> {[Ainstr],State} 86 87cg_fun(Ke, St0) -> 88 {UltimateFail,FailIs,St1} = make_failure(badarg, St0), 89 ?EXCEPTION_BLOCK = UltimateFail, %Assertion. 90 St2 = St1#cg{bfail=UltimateFail,ultimate_failure=UltimateFail}, 91 {B,St} = cg(Ke, St2), 92 Asm = [{label,0}|B++FailIs], 93 finalize(Asm, St). 94 95make_failure(Reason, St0) -> 96 {Lbl,St1} = new_label(St0), 97 {Dst,St} = new_ssa_var('@ssa_ret', St1), 98 Is = [{label,Lbl}, 99 #b_set{op=call,dst=Dst, 100 args=[#b_remote{mod=#b_literal{val=erlang}, 101 name=#b_literal{val=error}, 102 arity=1}, 103 #b_literal{val=Reason}]}, 104 #b_ret{arg=Dst}], 105 {Lbl,Is,St}. 106 107%% cg(Lkexpr, State) -> {[Ainstr],State}. 108%% Generate code for a kexpr. 109 110cg(#k_match{body=M,ret=Rs}, St) -> 111 do_match_cg(M, Rs, St); 112cg(#k_seq{arg=Arg,body=Body}, St0) -> 113 {ArgIs,St1} = cg(Arg, St0), 114 {BodyIs,St} = cg(Body, St1), 115 {ArgIs++BodyIs,St}; 116cg(#k_call{anno=Le,op=Func,args=As,ret=Rs}, St) -> 117 call_cg(Func, As, Rs, Le, St); 118cg(#k_enter{anno=Le,op=Func,args=As}, St) -> 119 enter_cg(Func, As, Le, St); 120cg(#k_bif{anno=Le}=Bif, St) -> 121 bif_cg(Bif, Le, St); 122cg(#k_try{arg=Ta,vars=Vs,body=Tb,evars=Evs,handler=Th,ret=Rs}, St) -> 123 try_cg(Ta, Vs, Tb, Evs, Th, Rs, St); 124cg(#k_try_enter{arg=Ta,vars=Vs,body=Tb,evars=Evs,handler=Th}, St) -> 125 try_enter_cg(Ta, Vs, Tb, Evs, Th, St); 126cg(#k_catch{body=Cb,ret=[R]}, St) -> 127 do_catch_cg(Cb, R, St); 128cg(#k_put{anno=Le,arg=Con,ret=Var}, St) -> 129 put_cg(Var, Con, Le, St); 130cg(#k_return{args=[Ret0]}, St) -> 131 Ret = ssa_arg(Ret0, St), 132 {[#b_ret{arg=Ret}],St}; 133cg(#k_break{args=Bs}, #cg{break=Br}=St) -> 134 Args = ssa_args(Bs, St), 135 {[#cg_break{args=Args,phi=Br}],St}; 136cg(#k_letrec_goto{label=Label,first=First,then=Then,ret=Rs}, 137 #cg{break=OldBreak,labels=Labels0}=St0) -> 138 {Tf,St1} = new_label(St0), 139 {B,St2} = new_label(St1), 140 Labels = Labels0#{Label=>Tf}, 141 {Fis,St3} = cg(First, St2#cg{labels=Labels,break=B}), 142 {Sis,St4} = cg(Then, St3), 143 St5 = St4#cg{labels=Labels0}, 144 {BreakVars,St} = new_ssa_vars(Rs, St5), 145 Phi = #cg_phi{vars=BreakVars}, 146 {Fis ++ [{label,Tf}] ++ Sis ++ [{label,B},Phi],St#cg{break=OldBreak}}; 147cg(#k_goto{label=Label}, #cg{labels=Labels}=St) -> 148 Branch = map_get(Label, Labels), 149 {[make_uncond_branch(Branch)],St}. 150 151%% match_cg(Matc, [Ret], State) -> {[Ainstr],State}. 152%% Generate code for a match. 153 154do_match_cg(M, Rs, #cg{bfail=Bfail,break=OldBreak}=St0) -> 155 {B,St1} = new_label(St0), 156 {Mis,St2} = match_cg(M, Bfail, St1#cg{break=B}), 157 St3 = St2#cg{break=OldBreak}, 158 {BreakVars,St} = new_ssa_vars(Rs, St3), 159 {Mis ++ [{label,B},#cg_phi{vars=BreakVars}],St}. 160 161%% match_cg(Match, Fail, State) -> {[Ainstr],State}. 162%% Generate code for a match tree. 163 164match_cg(#k_alt{first=F,then=S}, Fail, St0) -> 165 {Tf,St1} = new_label(St0), 166 {Fis,St2} = match_cg(F, Tf, St1), 167 {Sis,St3} = match_cg(S, Fail, St2), 168 {Fis ++ [{label,Tf}] ++ Sis,St3}; 169match_cg(#k_select{var=#k_var{}=V,types=Scs}, Fail, St) -> 170 match_fmf(fun (S, F, Sta) -> 171 select_cg(S, V, F, Fail, Sta) 172 end, Fail, St, Scs); 173match_cg(#k_guard{clauses=Gcs}, Fail, St) -> 174 match_fmf(fun (G, F, Sta) -> 175 guard_clause_cg(G, F, Sta) 176 end, Fail, St, Gcs); 177match_cg(Ke, _Fail, St0) -> 178 cg(Ke, St0). 179 180%% select_cg(Sclause, V, TypeFail, ValueFail, State) -> {Is,State}. 181%% Selecting type and value needs two failure labels, TypeFail is the 182%% label to jump to of the next type test when this type fails, and 183%% ValueFail is the label when this type is correct but the value is 184%% wrong. These are different as in the second case there is no need 185%% to try the next type, it will always fail. 186 187select_cg(#k_type_clause{type=k_binary,values=[S]}, Var, Tf, Vf, St) -> 188 select_binary(S, Var, Tf, Vf, St); 189select_cg(#k_type_clause{type=k_bin_seg,values=Vs}, Var, Tf, _Vf, St) -> 190 select_bin_segs(Vs, Var, Tf, St); 191select_cg(#k_type_clause{type=k_bin_int,values=Vs}, Var, Tf, _Vf, St) -> 192 select_bin_segs(Vs, Var, Tf, St); 193select_cg(#k_type_clause{type=k_bin_end,values=[S]}, Var, Tf, _Vf, St) -> 194 select_bin_end(S, Var, Tf, St); 195select_cg(#k_type_clause{type=k_map,values=Vs}, Var, Tf, Vf, St) -> 196 select_map(Vs, Var, Tf, Vf, St); 197select_cg(#k_type_clause{type=k_cons,values=[S]}, Var, Tf, Vf, St) -> 198 select_cons(S, Var, Tf, Vf, St); 199select_cg(#k_type_clause{type=k_nil,values=[S]}, Var, Tf, Vf, St) -> 200 select_nil(S, Var, Tf, Vf, St); 201select_cg(#k_type_clause{type=k_literal,values=Vs}, Var, Tf, Vf, St) -> 202 select_literal(Vs, Var, Tf, Vf, St); 203select_cg(#k_type_clause{type=Type,values=Scs}, Var, Tf, Vf, St0) -> 204 {Vis,St1} = 205 mapfoldl(fun (S, Sta) -> 206 {Val,Is,Stb} = select_val(S, Var, Vf, Sta), 207 {{Is,[Val]},Stb} 208 end, St0, Scs), 209 OptVls = combine(lists:sort(combine(Vis))), 210 {Vls,Sis,St2} = select_labels(OptVls, St1, [], []), 211 Arg = ssa_arg(Var, St2), 212 {Is,St} = select_val_cg(Type, Arg, Vls, Tf, Vf, Sis, St2), 213 {Is,St}. 214 215select_val_cg(k_atom, {bool,Dst}, Vls, _Tf, _Vf, Sis, St) -> 216 %% Generate a br instruction for a known boolean value from 217 %% the `wait_timeout` instruction. 218 #b_var{} = Dst, %Assertion. 219 [{#b_literal{val=false},Fail},{#b_literal{val=true},Succ}] = sort(Vls), 220 Br = #b_br{bool=Dst,succ=Succ,fail=Fail}, 221 {[Br|Sis],St}; 222select_val_cg(k_atom, {succeeded,Dst}, Vls, _Tf, _Vf, Sis, St0) -> 223 [{#b_literal{val=false},Fail},{#b_literal{val=true},Succ}] = sort(Vls), 224 #b_var{} = Dst, %Assertion. 225 %% Generate a `succeeded` instruction and two-way branch 226 %% following the `peek_message` instruction. 227 {Bool,St} = new_ssa_var('@ssa_bool', St0), 228 Succeeded = #b_set{op={succeeded,guard},dst=Bool,args=[Dst]}, 229 Br = #b_br{bool=Bool,succ=Succ,fail=Fail}, 230 {[Succeeded,Br|Sis],St}; 231select_val_cg(k_tuple, Tuple, Vls, Tf, Vf, Sis, St0) -> 232 {Is0,St1} = make_cond_branch({bif,is_tuple}, [Tuple], Tf, St0), 233 {Arity,St2} = new_ssa_var('@ssa_arity', St1), 234 GetArity = #b_set{op={bif,tuple_size},dst=Arity,args=[Tuple]}, 235 {Is,St} = select_val_cg(k_int, Arity, Vls, Vf, Vf, Sis, St2), 236 {Is0++[GetArity]++Is,St}; 237select_val_cg(Type, R, Vls, Tf, Vf, Sis, St0) -> 238 {TypeIs,St1} = if 239 Tf =:= Vf -> 240 %% The type and value failure labels are the same; 241 %% we don't need a type test. 242 {[],St0}; 243 true -> 244 %% Different labels for type failure and 245 %% label failure; we need a type test. 246 Test = select_type_test(Type), 247 make_cond_branch(Test, [R], Tf, St0) 248 end, 249 case Vls of 250 [{Val,Succ}] -> 251 {Is,St} = make_cond({bif,'=:='}, [R,Val], Vf, Succ, St1), 252 {TypeIs++Is++Sis,St}; 253 [_|_] -> 254 {TypeIs++[#b_switch{arg=R,fail=Vf,list=Vls}|Sis],St1} 255 end. 256 257select_type_test(k_int) -> {bif,is_integer}; 258select_type_test(k_atom) -> {bif,is_atom}; 259select_type_test(k_float) -> {bif,is_float}. 260 261combine([{Is,Vs1},{Is,Vs2}|Vis]) -> combine([{Is,Vs1 ++ Vs2}|Vis]); 262combine([V|Vis]) -> [V|combine(Vis)]; 263combine([]) -> []. 264 265select_labels([{Is,Vs}|Vis], St0, Vls, Sis) -> 266 {Lbl,St1} = new_label(St0), 267 select_labels(Vis, St1, add_vls(Vs, Lbl, Vls), [[{label,Lbl}|Is]|Sis]); 268select_labels([], St, Vls, Sis) -> 269 {Vls,append(Sis),St}. 270 271add_vls([V|Vs], Lbl, Acc) -> 272 add_vls(Vs, Lbl, [{#b_literal{val=V},Lbl}|Acc]); 273add_vls([], _, Acc) -> Acc. 274 275select_literal(S, V, Tf, Vf, St) -> 276 Src = ssa_arg(V, St), 277 F = fun(ValClause, Fail, St0) -> 278 {Val,ValIs,St1} = select_val(ValClause, V, Vf, St0), 279 Args = [Src,#b_literal{val=Val}], 280 {Is,St2} = make_cond_branch({bif,'=:='}, Args, Fail, St1), 281 {Is++ValIs,St2} 282 end, 283 match_fmf(F, Tf, St, S). 284 285select_cons(#k_val_clause{val=#k_cons{hd=Hd,tl=Tl},body=B}, 286 V, Tf, Vf, St0) -> 287 Es = [Hd,Tl], 288 {Eis,St1} = select_extract_cons(V, Es, St0), 289 {Bis,St2} = match_cg(B, Vf, St1), 290 Src = ssa_arg(V, St2), 291 {Is,St} = make_cond_branch(is_nonempty_list, [Src], Tf, St2), 292 {Is ++ Eis ++ Bis,St}. 293 294select_nil(#k_val_clause{val=#k_literal{val=[]},body=B}, V, Tf, Vf, St0) -> 295 {Bis,St1} = match_cg(B, Vf, St0), 296 Src = ssa_arg(V, St1), 297 {Is,St} = make_cond_branch({bif,'=:='}, [Src,#b_literal{val=[]}], Tf, St1), 298 {Is ++ Bis,St}. 299 300select_binary(#k_val_clause{val=#k_binary{segs=#k_var{name=Ctx0}},body=B}, 301 #k_var{}=Src, Tf, Vf, St0) -> 302 {Ctx,St1} = new_ssa_var(Ctx0, St0), 303 {Bis0,St2} = match_cg(B, Vf, St1), 304 {TestIs,St} = make_succeeded(Ctx, {guard, Tf}, St2), 305 Bis1 = [#b_set{op=bs_start_match,dst=Ctx, 306 args=[#b_literal{val=new}, 307 ssa_arg(Src, St)]}] ++ TestIs ++ Bis0, 308 Bis = finish_bs_matching(Bis1), 309 {Bis,St}. 310 311finish_bs_matching([#b_set{op=bs_match, 312 args=[#b_literal{val=string},Ctx,#b_literal{val=BinList}]}=Set|Is]) 313 when is_list(BinList) -> 314 I = Set#b_set{args=[#b_literal{val=string},Ctx, 315 #b_literal{val=list_to_bitstring(BinList)}]}, 316 finish_bs_matching([I|Is]); 317finish_bs_matching([I|Is]) -> 318 [I|finish_bs_matching(Is)]; 319finish_bs_matching([]) -> []. 320 321make_cond(Cond, Args, Fail, Succ, St0) -> 322 {Bool,St} = new_ssa_var('@ssa_bool', St0), 323 Bif = #b_set{op=Cond,dst=Bool,args=Args}, 324 Br = #b_br{bool=Bool,succ=Succ,fail=Fail}, 325 {[Bif,Br],St}. 326 327make_cond_branch(Cond, Args, Fail, St0) -> 328 {Bool,St1} = new_ssa_var('@ssa_bool', St0), 329 {Succ,St} = new_label(St1), 330 Bif = #b_set{op=Cond,dst=Bool,args=Args}, 331 Br = #b_br{bool=Bool,succ=Succ,fail=Fail}, 332 {[Bif,Br,{label,Succ}],St}. 333 334make_uncond_branch(Fail) -> 335 #b_br{bool=#b_literal{val=true},succ=Fail,fail=Fail}. 336 337%% 338%% Success checks need to be treated differently in bodies and guards; a check 339%% in a guard can be safely removed when we know it fails because we know 340%% there's never any side-effects, but in bodies the checked instruction may 341%% throw an exception and we need to ensure it isn't optimized away. 342%% 343%% Checks are expressed as {succeeded,guard} and {succeeded,body} respectively, 344%% where the latter has a side-effect (see beam_ssa:no_side_effect/1) and the 345%% former does not. This ensures that passes like ssa_opt_dead and ssa_opt_live 346%% won't optimize away pure operations that may throw an exception, since their 347%% result is used in {succeeded,body}. 348%% 349%% Other than the above details, the two variants are equivalent and most 350%% passes that care about them can simply match {succeeded,_}. 351%% 352 353make_succeeded(Var, {guard, Fail}, St) -> 354 make_succeeded_1(Var, guard, Fail, St); 355make_succeeded(Var, {in_catch, CatchLbl}, St) -> 356 make_succeeded_1(Var, body, CatchLbl, St); 357make_succeeded(Var, {no_catch, Fail}, St) -> 358 #cg{ultimate_failure=Fail} = St, %Assertion 359 make_succeeded_1(Var, body, Fail, St). 360 361make_succeeded_1(Var, Kind, Fail, St0) -> 362 {Bool,St1} = new_ssa_var('@ssa_bool', St0), 363 {Succ,St} = new_label(St1), 364 365 Check = [#b_set{op={succeeded,Kind},dst=Bool,args=[Var]}, 366 #b_br{bool=Bool,succ=Succ,fail=Fail}], 367 368 {Check ++ [{label,Succ}], St}. 369 370%% Instructions for selection of binary segments. 371 372select_bin_segs(Scs, Ivar, Tf, St) -> 373 match_fmf(fun(S, Fail, Sta) -> 374 select_bin_seg(S, Ivar, Fail, Sta) 375 end, Tf, St, Scs). 376 377select_bin_seg(#k_val_clause{val=#k_bin_seg{size=Size,unit=U,type=T, 378 seg=Seg,flags=Fs,next=Next}, 379 body=B,anno=Anno}, 380 #k_var{}=Src, Fail, St0) -> 381 LineAnno = line_anno(Anno), 382 Ctx = get_context(Src, St0), 383 {Mis,St1} = select_extract_bin(Next, Size, U, T, Fs, Fail, 384 Ctx, LineAnno, St0), 385 {Extracted,St2} = new_ssa_var(Seg#k_var.name, St1), 386 {Bis,St} = match_cg(B, Fail, St2), 387 BsGet = #b_set{op=bs_extract,dst=Extracted,args=[ssa_arg(Next, St)]}, 388 Is = Mis ++ [BsGet] ++ Bis, 389 {Is,St}; 390select_bin_seg(#k_val_clause{val=#k_bin_int{size=Sz,unit=U,flags=Fs, 391 val=Val,next=Next}, 392 body=B}, 393 #k_var{}=Src, Fail, St0) -> 394 Ctx = get_context(Src, St0), 395 {Mis,St1} = select_extract_int(Next, Val, Sz, U, Fs, Fail, 396 Ctx, St0), 397 {Bis,St} = match_cg(B, Fail, St1), 398 Is = case Mis ++ Bis of 399 [#b_set{op=bs_match,args=[#b_literal{val=string},OtherCtx1,Bin1]}, 400 #b_set{op={succeeded,guard},dst=Bool1}, 401 #b_br{bool=Bool1,succ=Succ,fail=Fail}, 402 {label,Succ}, 403 #b_set{op=bs_match,dst=Dst,args=[#b_literal{val=string},_OtherCtx2,Bin2]}| 404 [#b_set{op={succeeded,guard},dst=Bool2}, 405 #b_br{bool=Bool2,fail=Fail}|_]=Is0] -> 406 %% We used to do this optimization later, but it 407 %% turns out that in huge functions with many 408 %% string matching instructions, it's a huge win 409 %% to do the combination now. To avoid copying the 410 %% binary data again and again, we'll combine bitstrings 411 %% in a list and convert all of it to a bitstring later. 412 {#b_literal{val=B1},#b_literal{val=B2}} = {Bin1,Bin2}, 413 Bin = #b_literal{val=[B1,B2]}, 414 Set = #b_set{op=bs_match,dst=Dst,args=[#b_literal{val=string},OtherCtx1,Bin]}, 415 [Set|Is0]; 416 Is0 -> 417 Is0 418 end, 419 {Is,St}. 420 421get_context(#k_var{}=Var, St) -> 422 ssa_arg(Var, St). 423 424select_bin_end(#k_val_clause{val=#k_bin_end{},body=B}, Src, Tf, St0) -> 425 Ctx = get_context(Src, St0), 426 {Bis,St1} = match_cg(B, Tf, St0), 427 {TestIs,St} = make_cond_branch(bs_test_tail, [Ctx,#b_literal{val=0}], Tf, St1), 428 Is = TestIs++Bis, 429 {Is,St}. 430 431select_extract_bin(#k_var{name=Hd}, Size0, Unit, Type, Flags, Vf, 432 Ctx, Anno, St0) -> 433 {Dst,St1} = new_ssa_var(Hd, St0), 434 Size = case {Size0,ssa_arg(Size0, St0)} of 435 {#k_var{},#b_literal{val=all}} -> 436 %% The size `all` is used for the size of the final binary 437 %% segment in a pattern. Using `all` explicitly is not allowed, 438 %% so we convert it to an obvious invalid size. 439 #b_literal{val=bad_size}; 440 {_,Size1} -> 441 Size1 442 end, 443 build_bs_instr(Anno, Type, Vf, Ctx, Size, Unit, Flags, Dst, St1). 444 445select_extract_int(#k_var{name=Tl}, 0, #k_literal{val=0}, _U, _Fs, _Vf, 446 Ctx, St0) -> 447 St = set_ssa_var(Tl, Ctx, St0), 448 {[],St}; 449select_extract_int(#k_var{name=Tl}, Val, #k_literal{val=Sz}, U, Fs, Vf, 450 Ctx, St0) when is_integer(Sz) -> 451 {Dst,St1} = new_ssa_var(Tl, St0), 452 Bits = U*Sz, 453 Bin = case member(big, Fs) of 454 true -> 455 <<Val:Bits>>; 456 false -> 457 true = member(little, Fs), %Assertion. 458 <<Val:Bits/little>> 459 end, 460 Bits = bit_size(Bin), %Assertion. 461 {TestIs,St} = make_succeeded(Dst, {guard, Vf}, St1), 462 Set = #b_set{op=bs_match,dst=Dst, 463 args=[#b_literal{val=string},Ctx,#b_literal{val=Bin}]}, 464 {[Set|TestIs],St}. 465 466build_bs_instr(Anno, Type, Fail, Ctx, Size, Unit0, Flags0, Dst, St0) -> 467 Unit = #b_literal{val=Unit0}, 468 Flags = #b_literal{val=Flags0}, 469 NeedSize = bs_need_size(Type), 470 TypeArg = #b_literal{val=Type}, 471 Get = case NeedSize of 472 true -> 473 #b_set{anno=Anno,op=bs_match,dst=Dst, 474 args=[TypeArg,Ctx,Flags,Size,Unit]}; 475 false -> 476 #b_set{anno=Anno,op=bs_match,dst=Dst, 477 args=[TypeArg,Ctx,Flags]} 478 end, 479 {Is,St} = make_succeeded(Dst, {guard, Fail}, St0), 480 {[Get|Is],St}. 481 482select_val(#k_val_clause{val=#k_tuple{es=Es},body=B}, V, Vf, St0) -> 483 {Eis,St1} = select_extract_tuple(V, Es, St0), 484 {Bis,St2} = match_cg(B, Vf, St1), 485 {length(Es),Eis ++ Bis,St2}; 486select_val(#k_val_clause{val=#k_literal{val=Val},body=B}, _V, Vf, St0) -> 487 {Bis,St1} = match_cg(B, Vf, St0), 488 {Val,Bis,St1}. 489 490%% select_extract_tuple(Src, [V], State) -> {[E],State}. 491%% Extract tuple elements, but only if they are actually used. 492%% 493%% Not extracting tuple elements that are not used is an 494%% optimization for compile time and memory use during compilation. 495%% It is probably worthwhile because it is common to extract only a 496%% few elements from a huge record. 497 498select_extract_tuple(Src, Vs, St0) -> 499 Tuple = ssa_arg(Src, St0), 500 F = fun (#k_var{anno=Anno,name=V}, {Elem,S0}) -> 501 case member(unused, Anno) of 502 true -> 503 {[],{Elem+1,S0}}; 504 false -> 505 Args = [Tuple,#b_literal{val=Elem}], 506 {Dst,S} = new_ssa_var(V, S0), 507 Get = #b_set{op=get_tuple_element, 508 dst=Dst,args=Args}, 509 {[Get],{Elem+1,S}} 510 end 511 end, 512 {Es,{_,St}} = flatmapfoldl(F, {0,St0}, Vs), 513 {Es,St}. 514 515select_map(Scs, V, Tf, Vf, St0) -> 516 MapSrc = ssa_arg(V, St0), 517 {Is,St1} = 518 match_fmf(fun(#k_val_clause{val=#k_map{op=exact,es=Es}, 519 body=B}, Fail, St1) -> 520 select_map_val(V, Es, B, Fail, St1) 521 end, Vf, St0, Scs), 522 {TestIs,St} = make_cond_branch({bif,is_map}, [MapSrc], Tf, St1), 523 {TestIs++Is,St}. 524 525select_map_val(V, Es, B, Fail, St0) -> 526 {Eis,St1} = select_extract_map(Es, V, Fail, St0), 527 {Bis,St2} = match_cg(B, Fail, St1), 528 {Eis++Bis,St2}. 529 530select_extract_map([P|Ps], Src, Fail, St0) -> 531 MapSrc = ssa_arg(Src, St0), 532 #k_map_pair{key=Key0,val=#k_var{name=Dst0}} = P, 533 Key = ssa_arg(Key0, St0), 534 {Dst,St1} = new_ssa_var(Dst0, St0), 535 Set = #b_set{op=get_map_element,dst=Dst,args=[MapSrc,Key]}, 536 {TestIs,St2} = make_succeeded(Dst, {guard, Fail}, St1), 537 {Is,St} = select_extract_map(Ps, Src, Fail, St2), 538 {[Set|TestIs]++Is,St}; 539select_extract_map([], _, _, St) -> 540 {[],St}. 541 542select_extract_cons(Src0, [#k_var{name=Hd},#k_var{name=Tl}], St0) -> 543 Src = ssa_arg(Src0, St0), 544 {HdDst,St1} = new_ssa_var(Hd, St0), 545 {TlDst,St2} = new_ssa_var(Tl, St1), 546 GetHd = #b_set{op=get_hd,dst=HdDst,args=[Src]}, 547 GetTl = #b_set{op=get_tl,dst=TlDst,args=[Src]}, 548 {[GetHd,GetTl],St2}. 549 550guard_clause_cg(#k_guard_clause{guard=G,body=B}, Fail, St0) -> 551 {Gis,St1} = guard_cg(G, Fail, St0), 552 {Bis,St} = match_cg(B, Fail, St1), 553 {Gis ++ Bis,St}. 554 555%% guard_cg(Guard, Fail, State) -> {[Ainstr],State}. 556%% A guard is a boolean expression of tests. Tests return true or 557%% false. A fault in a test causes the test to return false. Tests 558%% never return the boolean, instead we generate jump code to go to 559%% the correct exit point. Primops and tests all go to the next 560%% instruction on success or jump to a failure label. 561 562guard_cg(#k_try{arg=Ts,vars=[],body=#k_break{args=[]}, 563 evars=[],handler=#k_break{args=[]}}, 564 Fail, 565 #cg{bfail=OldBfail,break=OldBreak}=St0) -> 566 %% Do a try/catch without return value for effect. The return 567 %% value is not checked; success passes on to the next instruction 568 %% and failure jumps to Fail. 569 {Next,St1} = new_label(St0), 570 {Tis,St2} = guard_cg(Ts, Fail, St1#cg{bfail=Fail,break=Next}), 571 Is = Tis ++ [{label,Next},#cg_phi{vars=[]}], 572 {Is,St2#cg{bfail=OldBfail,break=OldBreak}}; 573guard_cg(#k_test{op=Test0,args=As}, Fail, St0) -> 574 #k_remote{mod=#k_literal{val=erlang},name=#k_literal{val=Test}} = Test0, 575 test_cg(Test, false, As, Fail, St0); 576guard_cg(#k_seq{arg=Arg,body=Body}, Fail, St0) -> 577 {ArgIs,St1} = guard_cg(Arg, Fail, St0), 578 {BodyIs,St} = guard_cg(Body, Fail, St1), 579 {ArgIs++BodyIs,St}; 580guard_cg(G, _Fail, St) -> 581 cg(G, St). 582 583test_cg('=/=', Inverted, As, Fail, St) -> 584 test_cg('=:=', not Inverted, As, Fail, St); 585test_cg('/=', Inverted, As, Fail, St) -> 586 test_cg('==', not Inverted, As, Fail, St); 587test_cg(Test, Inverted, As0, Fail, St0) -> 588 As = ssa_args(As0, St0), 589 case {Test,ssa_args(As0, St0)} of 590 {is_record,[Tuple,#b_literal{val=Atom}=Tag,#b_literal{val=Int}=Arity]} 591 when is_atom(Atom), is_integer(Int) -> 592 false = Inverted, %Assertion. 593 test_is_record_cg(Fail, Tuple, Tag, Arity, St0); 594 {_,As} -> 595 {Bool,St1} = new_ssa_var('@ssa_bool', St0), 596 {Succ,St} = new_label(St1), 597 Bif = #b_set{op={bif,Test},dst=Bool,args=As}, 598 Br = case Inverted of 599 false -> #b_br{bool=Bool,succ=Succ,fail=Fail}; 600 true -> #b_br{bool=Bool,succ=Fail,fail=Succ} 601 end, 602 {[Bif,Br,{label,Succ}],St} 603 end. 604 605test_is_record_cg(Fail, Tuple, TagVal, ArityVal, St0) -> 606 {Arity,St1} = new_ssa_var('@ssa_arity', St0), 607 {Tag,St2} = new_ssa_var('@ssa_tag', St1), 608 {Is0,St3} = make_cond_branch({bif,is_tuple}, [Tuple], Fail, St2), 609 GetArity = #b_set{op={bif,tuple_size},dst=Arity,args=[Tuple]}, 610 {Is1,St4} = make_cond_branch({bif,'=:='}, [Arity,ArityVal], Fail, St3), 611 GetTag = #b_set{op=get_tuple_element,dst=Tag, 612 args=[Tuple,#b_literal{val=0}]}, 613 {Is2,St} = make_cond_branch({bif,'=:='}, [Tag,TagVal], Fail, St4), 614 Is = Is0 ++ [GetArity] ++ Is1 ++ [GetTag] ++ Is2, 615 {Is,St}. 616 617%% match_fmf(Fun, LastFail, State, [Clause]) -> {Is,State}. 618%% This is a special flatmapfoldl for match code gen where we 619%% generate a "failure" label for each clause. The last clause uses 620%% an externally generated failure label, LastFail. N.B. We do not 621%% know or care how the failure labels are used. 622 623match_fmf(F, LastFail, St, [H]) -> 624 F(H, LastFail, St); 625match_fmf(F, LastFail, St0, [H|T]) -> 626 {Fail,St1} = new_label(St0), 627 {R,St2} = F(H, Fail, St1), 628 {Rs,St3} = match_fmf(F, LastFail, St2, T), 629 {R ++ [{label,Fail}] ++ Rs,St3}. 630 631%% fail_context(State) -> {Where,FailureLabel}. 632%% Where = guard | no_catch | in_catch 633%% Return an indication of which part of a function code is 634%% being generated for and the appropriate failure label to 635%% use. 636%% 637%% Where has the following meaning: 638%% 639%% guard - Inside a guard. 640%% no_catch - In a function body, not in the scope of 641%% a try/catch or catch. 642%% in_catch - In the scope of a try/catch or catch. 643 644fail_context(#cg{catch_label=Catch,bfail=Fail,ultimate_failure=Ult}) -> 645 if 646 Fail =/= Ult -> 647 {guard,Fail}; 648 Catch =:= none -> 649 {no_catch,Fail}; 650 is_integer(Catch) -> 651 {in_catch,Catch} 652 end. 653 654%% call_cg(Func, [Arg], [Ret], Le, State) -> 655%% {[Ainstr],State}. 656%% enter_cg(Func, [Arg], Le, St) -> {[Ainstr],St}. 657%% Generate code for call and enter. 658 659call_cg(Func, As, [], Le, St) -> 660 call_cg(Func, As, [#k_var{name='@ssa_ignored'}], Le, St); 661call_cg(Func, As, [#k_var{name=R}|MoreRs]=Rs, Le, St0) -> 662 case fail_context(St0) of 663 {guard,Fail} -> 664 %% Inside a guard. The only allowed function call is to 665 %% erlang:error/1,2. We will generate a branch to the 666 %% failure branch. 667 #k_remote{mod=#k_literal{val=erlang}, 668 name=#k_literal{val=error}} = Func, %Assertion. 669 St = set_unused_ssa_vars(Rs, St0), 670 {[make_uncond_branch(Fail),#cg_unreachable{}],St}; 671 FailCtx -> 672 %% Ordinary function call in a function body. 673 Args = ssa_args([Func|As], St0), 674 {Ret,St1} = new_ssa_var(R, St0), 675 Call = #b_set{anno=line_anno(Le),op=call,dst=Ret,args=Args}, 676 677 %% If this is a call to erlang:error(), MoreRs could be a 678 %% nonempty list of variables that each need a value. 679 St2 = set_unused_ssa_vars(MoreRs, St1), 680 681 {TestIs,St} = make_succeeded(Ret, FailCtx, St2), 682 {[Call|TestIs],St} 683 end. 684 685enter_cg(Func, As0, Le, St0) -> 686 As = ssa_args([Func|As0], St0), 687 {Ret,St} = new_ssa_var('@ssa_ret', St0), 688 Call = #b_set{anno=line_anno(Le),op=call,dst=Ret,args=As}, 689 {[Call,#b_ret{arg=Ret}],St}. 690 691%% bif_cg(#k_bif{}, Le,State) -> {[Ainstr],State}. 692%% Generate code for a guard BIF or primop. 693 694bif_cg(#k_bif{anno=A,op=#k_internal{name=Name},args=As,ret=Rs}, _Le, St) -> 695 internal_cg(line_anno(A), Name, As, Rs, St); 696bif_cg(#k_bif{op=#k_remote{mod=#k_literal{val=erlang},name=#k_literal{val=Name}}, 697 args=As,ret=Rs}, Le, St) -> 698 bif_cg(Name, As, Rs, Le, St). 699 700%% internal_cg(Bif, [Arg], [Ret], Le, State) -> 701%% {[Ainstr],State}. 702 703internal_cg(_Anno, Op0, As, [#k_var{name=Dst0}], St0) 704 when Op0 =:= raise; Op0 =:= raw_raise -> 705 Args = ssa_args(As, St0), 706 Op = fix_op(Op0, St0), 707 {Dst,St} = new_ssa_var(Dst0, St0), 708 Set = #b_set{op=Op,dst=Dst,args=Args}, 709 case fail_context(St) of 710 {no_catch,_Fail} -> 711 %% No current catch in this function. Follow the raw_raise/resume 712 %% instruction by a return (instead of branching to 713 %% ?EXCEPTION_MARKER) to ensure that the trim optimization can be 714 %% applied. (Allowing control to pass through to the next 715 %% instruction would mean that the type for the try/catch construct 716 %% would be `any`.) 717 Is = [Set,#b_ret{arg=Dst},#cg_unreachable{}], 718 {Is,St}; 719 {in_catch,Fail} -> 720 Is = [Set,make_uncond_branch(Fail),#cg_unreachable{}], 721 {Is,St} 722 end; 723internal_cg(Anno, recv_peek_message, [], [#k_var{name=Succeeded0}, 724 #k_var{name=Dst0}], St0) -> 725 {Dst,St1} = new_ssa_var(Dst0, St0), 726 St = new_succeeded_value(Succeeded0, Dst, St1), 727 Set = #b_set{ anno=Anno, 728 op=peek_message, 729 dst=Dst, 730 args=[#b_literal{val=none}] }, 731 {[Set],St}; 732internal_cg(_Anno, recv_wait_timeout, As, [#k_var{name=Succeeded0}], St0) -> 733 %% Note that the `wait_timeout` instruction can potentially branch in three 734 %% different directions: 735 %% 736 %% * A new message is available in the message queue. `wait_timeout` 737 %% branches to the given label. 738 %% 739 %% * The timeout expired. `wait_timeout` transfers control to the next 740 %% instruction. 741 %% 742 %% * The value for timeout duration is invalid (either not an integer or 743 %% negative or too large). A `timeout_value` exception will be raised. 744 %% 745 %% `wait_timeout` will be represented like this in SSA code: 746 %% 747 %% WaitBool = wait_timeout TimeoutValue 748 %% Succeeded = succeeded:body WaitBool 749 %% br Succeeded, ^good_timeout_value, ^bad_timeout_value 750 %% 751 %% good_timeout_value: 752 %% br WaitBool, ^timeout_expired, ^new_message_received 753 %% 754 Args = ssa_args(As, St0), 755 {Wait,St1} = new_ssa_var('@ssa_wait', St0), 756 {Succ,St2} = make_succeeded(Wait, fail_context(St1), St1), 757 St = new_bool_value(Succeeded0, Wait, St2), 758 Set = #b_set{op=wait_timeout,dst=Wait,args=Args}, 759 {[Set|Succ],St}; 760internal_cg(Anno, Op0, As, [#k_var{name=Dst0}], St0) when is_atom(Op0) -> 761 %% This behaves like a function call. 762 {Dst,St} = new_ssa_var(Dst0, St0), 763 Args = ssa_args(As, St), 764 Op = fix_op(Op0, St), 765 Set = #b_set{anno=Anno,op=Op,dst=Dst,args=Args}, 766 {[Set],St}; 767internal_cg(Anno, Op0, As, [], St0) when is_atom(Op0) -> 768 %% This behaves like a function call. 769 {Dst,St} = new_ssa_var('@ssa_ignored', St0), 770 Args = ssa_args(As, St), 771 Op = fix_op(Op0, St), 772 Set = #b_set{anno=Anno,op=Op,dst=Dst,args=Args}, 773 {[Set],St}. 774 775fix_op(make_fun, #cg{no_make_fun3=true}) -> old_make_fun; 776fix_op(raise, _) -> resume; 777fix_op(Op, _) -> Op. 778 779bif_cg(Bif, As0, [#k_var{name=Dst0}], Le, St0) -> 780 {Dst,St1} = new_ssa_var(Dst0, St0), 781 case {Bif,ssa_args(As0, St0)} of 782 {is_record,[Tuple,#b_literal{val=Atom}=Tag, 783 #b_literal{val=Int}=Arity]} 784 when is_atom(Atom), is_integer(Int) -> 785 bif_is_record_cg(Dst, Tuple, Tag, Arity, St1); 786 {_,As} -> 787 I = #b_set{anno=line_anno(Le),op={bif,Bif},dst=Dst,args=As}, 788 case erl_bifs:is_safe(erlang, Bif, length(As)) of 789 false -> 790 FailCtx = fail_context(St1), 791 {Is,St} = make_succeeded(Dst, FailCtx, St1), 792 {[I|Is],St}; 793 true-> 794 {[I],St1} 795 end 796 end. 797 798bif_is_record_cg(Dst, Tuple, TagVal, ArityVal, St0) -> 799 {Arity,St1} = new_ssa_var('@ssa_arity', St0), 800 {Tag,St2} = new_ssa_var('@ssa_tag', St1), 801 {Phi,St3} = new_label(St2), 802 {False,St4} = new_label(St3), 803 {Is0,St5} = make_cond_branch({bif,is_tuple}, [Tuple], False, St4), 804 GetArity = #b_set{op={bif,tuple_size},dst=Arity,args=[Tuple]}, 805 {Is1,St6} = make_cond_branch({bif,'=:='}, [Arity,ArityVal], False, St5), 806 GetTag = #b_set{op=get_tuple_element,dst=Tag, 807 args=[Tuple,#b_literal{val=0}]}, 808 {Is2,St} = make_cond_branch({bif,'=:='}, [Tag,TagVal], False, St6), 809 Is3 = [#cg_break{args=[#b_literal{val=true}],phi=Phi}, 810 {label,False}, 811 #cg_break{args=[#b_literal{val=false}],phi=Phi}, 812 {label,Phi}, 813 #cg_phi{vars=[Dst]}], 814 Is = Is0 ++ [GetArity] ++ Is1 ++ [GetTag] ++ Is2 ++ Is3, 815 {Is,St}. 816 817%% try_cg(TryBlock, [BodyVar], TryBody, [ExcpVar], TryHandler, [Ret], St) -> 818%% {[Ainstr],St}. 819 820try_cg(Ta, Vs, Tb, Evs, Th, Rs, St0) -> 821 {B,St1} = new_label(St0), %Body label 822 {H,St2} = new_label(St1), %Handler label 823 {E,St3} = new_label(St2), %End label 824 {Next,St4} = new_label(St3), 825 {TryTag,St5} = new_ssa_var('@ssa_catch_tag', St4), 826 {SsaVs,St6} = new_ssa_vars(Vs, St5), 827 {SsaEvs,St7} = new_ssa_vars(Evs, St6), 828 {Ais,St8} = cg(Ta, St7#cg{break=B,catch_label=H}), 829 830 %% We try to avoid constructing a try/catch if the expression to 831 %% be evaluated don't have any side effects and if the error 832 %% reason is not explicitly matched. 833 %% 834 %% Starting in OTP 23, segment sizes in binary matching and keys 835 %% in map matching are allowed to be arbitrary guard 836 %% expressions. Those expressions are evaluated in a try/catch 837 %% so that matching can continue with the next clause if the evaluation 838 %% of such expression fails. 839 %% 840 %% It is not allowed to use try/catch during matching in a receive 841 %% (the try/catch would force the saving of fragile message references 842 %% to the stack frame). Therefore, avoiding creating try/catch is 843 %% not merely an optimization but necessary for correctness. 844 845 case {Vs,Tb,Th,is_guard_cg_safe_list(Ais)} of 846 {[#k_var{name=X}],#k_break{args=[#k_var{name=X}]}, 847 #k_break{args=[#k_literal{}]},true} -> 848 %% There are no instructions that will clobber X registers 849 %% and the exception is not matched. Therefore, a 850 %% try/catch is not needed. This code is probably located 851 %% in a guard. 852 {ProtIs,St9} = guard_cg(Ta, H, St7#cg{break=B,bfail=H}), 853 {His,St10} = cg(Th, St9), 854 {RetVars,St} = new_ssa_vars(Rs, St10), 855 Is = ProtIs ++ [{label,H}] ++ His ++ 856 [{label,B},#cg_phi{vars=RetVars}], 857 {Is,St#cg{break=St0#cg.break,bfail=St7#cg.bfail}}; 858 {[#k_var{name=X}],#k_break{args=[#k_literal{}=SuccLit0,#k_var{name=X}]}, 859 #k_break{args=[#k_literal{val=false},#k_literal{}]},true} -> 860 %% There are no instructions that will clobber X registers 861 %% and the exception is not matched. Therefore, a 862 %% try/catch is not needed. This code probably evaluates 863 %% a key expression in map matching. 864 {FinalLabel,St9} = new_label(St7), 865 {ProtIs,St10} = guard_cg(Ta, H, St9#cg{break=B,bfail=H}), 866 {His,St11} = cg(Th, St10#cg{break=FinalLabel}), 867 {RetVars,St12} = new_ssa_vars(Rs, St11), 868 {Result,St} = new_ssa_var('@ssa_result', St12), 869 SuccLit = ssa_arg(SuccLit0, St), 870 Is = ProtIs ++ [{label,H}] ++ His ++ 871 [{label,B}, 872 #cg_phi{vars=[Result]}, 873 #cg_break{args=[SuccLit,Result],phi=FinalLabel}, 874 {label,FinalLabel}, 875 #cg_phi{vars=RetVars}], 876 {Is,St#cg{break=St0#cg.break,bfail=St7#cg.bfail}}; 877 {_,#k_break{args=[]},#k_break{args=[]},true} -> 878 %% There are no instructions that will clobber X registers 879 %% and the exception is not matched. Therefore, a 880 %% try/catch is not needed. This code probably does the 881 %% size calculation for a segment in binary matching. 882 {ProtIs,St9} = guard_cg(Ta, H, St7#cg{break=B,bfail=H}), 883 {His,St10} = cg(Th, St9), 884 {RetVars,St} = new_ssa_vars(Rs, St10), 885 Is = ProtIs ++ [{label,H}] ++ His ++ 886 [{label,B},#cg_phi{vars=RetVars}], 887 {Is,St#cg{break=St0#cg.break,bfail=St7#cg.bfail}}; 888 {_,_,_,_} -> 889 %% The general try/catch (not in a guard). 890 St9 = St8#cg{break=E,catch_label=St7#cg.catch_label}, 891 {Bis,St10} = cg(Tb, St9), 892 {His,St11} = cg(Th, St10), 893 {BreakVars,St12} = new_ssa_vars(Rs, St11), 894 {CatchedAgg,St13} = new_ssa_var('@ssa_agg', St12), 895 ExtractVs = extract_vars(SsaEvs, CatchedAgg, 0), 896 KillTryTag = #b_set{op=kill_try_tag,args=[TryTag]}, 897 Args = [#b_literal{val='try'},TryTag], 898 Handler = [{label,H}, 899 #b_set{op=landingpad,dst=CatchedAgg,args=Args}] ++ 900 ExtractVs ++ [KillTryTag], 901 {[#b_set{op=new_try_tag,dst=TryTag,args=[#b_literal{val='try'}]}, 902 #b_br{bool=TryTag,succ=Next,fail=H}, 903 {label,Next}] ++ Ais ++ 904 [{label,B},#cg_phi{vars=SsaVs},KillTryTag] ++ Bis ++ 905 Handler ++ His ++ 906 [{label,E},#cg_phi{vars=BreakVars}], 907 St13#cg{break=St0#cg.break}} 908 end. 909 910is_guard_cg_safe_list(Is) -> 911 all(fun is_guard_cg_safe/1, Is). 912 913is_guard_cg_safe(#b_set{op=call,args=Args}) -> 914 case Args of 915 [#b_remote{mod=#b_literal{val=erlang}, 916 name=#b_literal{val=error}, 917 arity=1}|_] -> 918 true; 919 _ -> 920 false 921 end; 922is_guard_cg_safe(#b_set{}=I) -> not beam_ssa:clobbers_xregs(I); 923is_guard_cg_safe(#b_br{}) -> true; 924is_guard_cg_safe(#b_switch{}) -> true; 925is_guard_cg_safe(#cg_break{}) -> true; 926is_guard_cg_safe(#cg_phi{}) -> true; 927is_guard_cg_safe({label,_}) -> true; 928is_guard_cg_safe(#cg_unreachable{}) -> false. 929 930try_enter_cg(Ta, Vs, Tb, Evs, Th, St0) -> 931 {B,St1} = new_label(St0), %Body label 932 {H,St2} = new_label(St1), %Handler label 933 {Next,St3} = new_label(St2), 934 {TryTag,St4} = new_ssa_var('@ssa_catch_tag', St3), 935 {SsaVs,St5} = new_ssa_vars(Vs, St4), 936 {SsaEvs,St6} = new_ssa_vars(Evs, St5), 937 {Ais,St7} = cg(Ta, St6#cg{break=B,catch_label=H}), 938 St8 = St7#cg{catch_label=St6#cg.catch_label}, 939 {Bis,St9} = cg(Tb, St8), 940 {His,St10} = cg(Th, St9), 941 {CatchedAgg,St} = new_ssa_var('@ssa_agg', St10), 942 ExtractVs = extract_vars(SsaEvs, CatchedAgg, 0), 943 KillTryTag = #b_set{op=kill_try_tag,args=[TryTag]}, 944 Args = [#b_literal{val='try'},TryTag], 945 Handler = [{label,H}, 946 #b_set{op=landingpad,dst=CatchedAgg,args=Args}] ++ 947 ExtractVs ++ [KillTryTag], 948 {[#b_set{op=new_try_tag,dst=TryTag,args=[#b_literal{val='try'}]}, 949 #b_br{bool=TryTag,succ=Next,fail=H}, 950 {label,Next}] ++ Ais ++ 951 [{label,B},#cg_phi{vars=SsaVs},KillTryTag] ++ Bis ++ 952 Handler ++ His, 953 St#cg{break=St0#cg.break}}. 954 955extract_vars([V|Vs], Agg, N) -> 956 I = #b_set{op=extract,dst=V,args=[Agg,#b_literal{val=N}]}, 957 [I|extract_vars(Vs, Agg, N+1)]; 958extract_vars([], _, _) -> []. 959 960%% do_catch_cg(CatchBlock, Ret, St) -> {[Ainstr],St}. 961 962do_catch_cg(Block, #k_var{name=R}, St0) -> 963 {B,St1} = new_label(St0), 964 {Next,St2} = new_label(St1), 965 {H,St3} = new_label(St2), 966 {CatchReg,St4} = new_ssa_var('@ssa_catch_tag', St3), 967 {Dst,St5} = new_ssa_var(R, St4), 968 {Succ,St6} = new_label(St5), 969 {Cis,St7} = cg(Block, St6#cg{break=Succ,catch_label=H}), 970 {CatchedVal,St8} = new_ssa_var('@catched_val', St7), 971 {SuccVal,St9} = new_ssa_var('@success_val', St8), 972 {CatchedAgg,St10} = new_ssa_var('@ssa_agg', St9), 973 {CatchEndVal,St} = new_ssa_var('@catch_end_val', St10), 974 Args = [#b_literal{val='catch'},CatchReg], 975 {[#b_set{op=new_try_tag,dst=CatchReg,args=[#b_literal{val='catch'}]}, 976 #b_br{bool=CatchReg,succ=Next,fail=H}, 977 {label,Next}] ++ Cis ++ 978 [{label,H}, 979 #b_set{op=landingpad,dst=CatchedAgg,args=Args}, 980 #b_set{op=extract,dst=CatchedVal, 981 args=[CatchedAgg,#b_literal{val=0}]}, 982 #cg_break{args=[CatchedVal],phi=B}, 983 {label,Succ}, 984 #cg_phi{vars=[SuccVal]}, 985 #cg_break{args=[SuccVal],phi=B}, 986 {label,B},#cg_phi{vars=[CatchEndVal]}, 987 #b_set{op=catch_end,dst=Dst,args=[CatchReg,CatchEndVal]}], 988 St#cg{break=St1#cg.break,catch_label=St1#cg.catch_label}}. 989 990%% put_cg([Var], Constr, Le, Vdb, Bef, St) -> {[Ainstr],St}. 991%% Generate code for constructing terms. 992 993put_cg([#k_var{name=R}], #k_cons{hd=Hd,tl=Tl}, _Le, St0) -> 994 Args = ssa_args([Hd,Tl], St0), 995 {Dst,St} = new_ssa_var(R, St0), 996 PutList = #b_set{op=put_list,dst=Dst,args=Args}, 997 {[PutList],St}; 998put_cg([#k_var{name=R}], #k_tuple{es=Es}, _Le, St0) -> 999 {Ret,St} = new_ssa_var(R, St0), 1000 Args = ssa_args(Es, St), 1001 PutTuple = #b_set{op=put_tuple,dst=Ret,args=Args}, 1002 {[PutTuple],St}; 1003put_cg([#k_var{name=R}], #k_binary{segs=Segs}, Le, St0) -> 1004 FailCtx = fail_context(St0), 1005 {Dst,St1} = new_ssa_var(R, St0), 1006 cg_binary(Dst, Segs, FailCtx, Le, St1); 1007put_cg([#k_var{name=R}], #k_map{op=Op,var=Map, 1008 es=[#k_map_pair{key=#k_var{}=K,val=V}]}, 1009 Le, St0) -> 1010 %% Map: single variable key. 1011 SrcMap = ssa_arg(Map, St0), 1012 LineAnno = line_anno(Le), 1013 List = [ssa_arg(K, St0),ssa_arg(V, St0)], 1014 {Dst,St1} = new_ssa_var(R, St0), 1015 {Is,St} = put_cg_map(LineAnno, Op, SrcMap, Dst, List, St1), 1016 {Is,St}; 1017put_cg([#k_var{name=R}], #k_map{op=Op,var=Map,es=Es}, Le, St0) -> 1018 %% Map: one or more literal keys. 1019 [] = [Var || #k_map_pair{key=#k_var{}=Var} <- Es], %Assertion 1020 SrcMap = ssa_arg(Map, St0), 1021 LineAnno = line_anno(Le), 1022 List = flatmap(fun(#k_map_pair{key=K,val=V}) -> 1023 [ssa_arg(K, St0),ssa_arg(V, St0)] 1024 end, Es), 1025 {Dst,St1} = new_ssa_var(R, St0), 1026 {Is,St} = put_cg_map(LineAnno, Op, SrcMap, Dst, List, St1), 1027 {Is,St}; 1028put_cg([#k_var{name=R}], Con0, _Le, St0) -> 1029 %% Create an alias for a variable or literal. 1030 Con = ssa_arg(Con0, St0), 1031 St = set_ssa_var(R, Con, St0), 1032 {[],St}. 1033 1034put_cg_map(LineAnno, Op, SrcMap, Dst, List, St0) -> 1035 Args = [#b_literal{val=Op},SrcMap|List], 1036 PutMap = #b_set{anno=LineAnno,op=put_map,dst=Dst,args=Args}, 1037 if 1038 Op =:= assoc -> 1039 {[PutMap],St0}; 1040 true -> 1041 FailCtx = fail_context(St0), 1042 {Is,St} = make_succeeded(Dst, FailCtx, St0), 1043 {[PutMap|Is],St} 1044 end. 1045 1046%%% 1047%%% Code generation for constructing binaries. 1048%%% 1049 1050cg_binary(Dst, Segs0, FailCtx, Le, St0) -> 1051 {PutCode0,SzCalc0,St1} = cg_bin_put(Segs0, FailCtx, St0), 1052 LineAnno = line_anno(Le), 1053 Anno = Le, 1054 case PutCode0 of 1055 [#b_set{op=bs_put,dst=Bin,args=[_,_,Src,#b_literal{val=all}|_]}, 1056 #b_set{op={succeeded,_},dst=Bool,args=[Bin]}, 1057 #b_br{bool=Bool}, 1058 {label,_}|_] -> 1059 #k_bin_seg{unit=Unit0,next=Segs} = Segs0, 1060 Unit = #b_literal{val=Unit0}, 1061 {PutCode,SzCalc1,St2} = cg_bin_put(Segs, FailCtx, St1), 1062 {_,SzVar,SzCode0,St3} = cg_size_calc(1, SzCalc1, FailCtx, St2), 1063 SzCode = cg_bin_anno(SzCode0, LineAnno), 1064 Args = case member(single_use, Anno) of 1065 true -> 1066 [#b_literal{val=private_append},Src,SzVar,Unit]; 1067 false -> 1068 [#b_literal{val=append},Src,SzVar,Unit] 1069 end, 1070 BsInit = #b_set{anno=LineAnno,op=bs_init,dst=Dst,args=Args}, 1071 {TestIs,St} = make_succeeded(Dst, FailCtx, St3), 1072 {SzCode ++ [BsInit] ++ TestIs ++ PutCode,St}; 1073 [#b_set{op=bs_put}|_] -> 1074 {Unit,SzVar,SzCode0,St2} = cg_size_calc(8, SzCalc0, FailCtx, St1), 1075 SzCode = cg_bin_anno(SzCode0, LineAnno), 1076 Args = [#b_literal{val=new},SzVar,Unit], 1077 BsInit = #b_set{anno=LineAnno,op=bs_init,dst=Dst,args=Args}, 1078 {TestIs,St} = make_succeeded(Dst, FailCtx, St2), 1079 {SzCode ++ [BsInit] ++ TestIs ++ PutCode0,St} 1080 end. 1081 1082cg_bin_anno([Set|Sets], Anno) -> 1083 [Set#b_set{anno=Anno}|Sets]; 1084cg_bin_anno([], _) -> []. 1085 1086%% cg_size_calc(PreferredUnit, SzCalc, FailCtx, St0) -> 1087%% {ActualUnit,SizeVariable,SizeCode,St}. 1088%% Generate size calculation code. 1089 1090cg_size_calc(Unit, error, _FailCtx, St) -> 1091 {#b_literal{val=Unit},#b_literal{val=badarg},[],St}; 1092cg_size_calc(8, [{1,_}|_]=SzCalc, FailCtx, St) -> 1093 cg_size_calc(1, SzCalc, FailCtx, St); 1094cg_size_calc(8, SzCalc, FailCtx, St0) -> 1095 {Var,Pre,St} = cg_size_calc_1(SzCalc, FailCtx, St0), 1096 {#b_literal{val=8},Var,Pre,St}; 1097cg_size_calc(1, SzCalc0, FailCtx, St0) -> 1098 SzCalc = map(fun({8,#b_literal{val=Size}}) -> 1099 {1,#b_literal{val=8*Size}}; 1100 ({8,{{bif,byte_size},Src}}) -> 1101 {1,{{bif,bit_size},Src}}; 1102 ({8,{_,_}=UtfCalc}) -> 1103 {1,{'*',#b_literal{val=8},UtfCalc}}; 1104 ({_,_}=Pair) -> 1105 Pair 1106 end, SzCalc0), 1107 {Var,Pre,St} = cg_size_calc_1(SzCalc, FailCtx, St0), 1108 {#b_literal{val=1},Var,Pre,St}. 1109 1110cg_size_calc_1(SzCalc, FailCtx, St0) -> 1111 cg_size_calc_2(SzCalc, #b_literal{val=0}, FailCtx, St0). 1112 1113cg_size_calc_2([{_,{'*',Unit,{_,_}=Bif}}|T], Sum0, FailCtx, St0) -> 1114 {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), 1115 {BifDst,Pre1,St2} = cg_size_bif(Bif, FailCtx, St1), 1116 {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, Unit, FailCtx, St2), 1117 {Sum,Pre0++Pre1++Pre2,St}; 1118cg_size_calc_2([{_,#b_literal{}=Sz}|T], Sum0, FailCtx, St0) -> 1119 {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), 1120 {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, FailCtx, St1), 1121 {Sum,Pre0++Pre,St}; 1122cg_size_calc_2([{_,#b_var{}=Sz}|T], Sum0, FailCtx, St0) -> 1123 {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), 1124 {Sum,Pre,St} = cg_size_add(Sum1, Sz, #b_literal{val=1}, FailCtx, St1), 1125 {Sum,Pre0++Pre,St}; 1126cg_size_calc_2([{_,{_,_}=Bif}|T], Sum0, FailCtx, St0) -> 1127 {Sum1,Pre0,St1} = cg_size_calc_2(T, Sum0, FailCtx, St0), 1128 {BifDst,Pre1,St2} = cg_size_bif(Bif, FailCtx, St1), 1129 {Sum,Pre2,St} = cg_size_add(Sum1, BifDst, #b_literal{val=1}, FailCtx, St2), 1130 {Sum,Pre0++Pre1++Pre2,St}; 1131cg_size_calc_2([], Sum, _FailCtx, St) -> 1132 {Sum,[],St}. 1133 1134cg_size_bif(#b_var{}=Var, _FailCtx, St) -> 1135 {Var,[],St}; 1136cg_size_bif({Name,Src}, FailCtx, St0) -> 1137 {Dst,St1} = new_ssa_var('@ssa_bif', St0), 1138 Bif = #b_set{op=Name,dst=Dst,args=[Src]}, 1139 {TestIs,St} = make_succeeded(Dst, FailCtx, St1), 1140 {Dst,[Bif|TestIs],St}. 1141 1142cg_size_add(#b_literal{val=0}, Val, #b_literal{val=1}, _FailCtx, St) -> 1143 {Val,[],St}; 1144cg_size_add(#b_literal{val=A}, #b_literal{val=B}, #b_literal{val=U}, _FailCtx, St) -> 1145 {#b_literal{val=A+B*U},[],St}; 1146cg_size_add(A, B, Unit, FailCtx, St0) -> 1147 {Dst,St1} = new_ssa_var('@ssa_sum', St0), 1148 {TestIs,St} = make_succeeded(Dst, FailCtx, St1), 1149 BsAdd = #b_set{op=bs_add,dst=Dst,args=[A,B,Unit]}, 1150 {Dst,[BsAdd|TestIs],St}. 1151 1152cg_bin_put(Seg, FailCtx, St) -> 1153 cg_bin_put_1(Seg, FailCtx, [], [], St). 1154 1155cg_bin_put_1(#k_bin_seg{size=Size0,unit=U,type=T,flags=Fs,seg=Src0,next=Next}, 1156 FailCtx, Acc, SzCalcAcc, St0) -> 1157 [Src,Size] = ssa_args([Src0,Size0], St0), 1158 NeedSize = bs_need_size(T), 1159 TypeArg = #b_literal{val=T}, 1160 Flags = #b_literal{val=Fs}, 1161 Unit = #b_literal{val=U}, 1162 Args = case NeedSize of 1163 true -> [TypeArg,Flags,Src,Size,Unit]; 1164 false -> [TypeArg,Flags,Src] 1165 end, 1166 {Dst,St1} = new_ssa_var('@ssa_bs_put', St0), 1167 {TestIs,St} = make_succeeded(Dst, FailCtx, St1), 1168 Is = [#b_set{op=bs_put,dst=Dst,args=Args}|TestIs], 1169 SzCalc = bin_size_calc(T, Src, Size, U), 1170 cg_bin_put_1(Next, FailCtx, reverse(Is, Acc), [SzCalc|SzCalcAcc], St); 1171cg_bin_put_1(#k_bin_end{}, _, Acc, SzCalcAcc, St) -> 1172 SzCalc = fold_size_calc(SzCalcAcc, 0, []), 1173 {reverse(Acc),SzCalc,St}. 1174 1175bs_need_size(utf8) -> false; 1176bs_need_size(utf16) -> false; 1177bs_need_size(utf32) -> false; 1178bs_need_size(_) -> true. 1179 1180bin_size_calc(utf8, Src, _Size, _Unit) -> 1181 {8,{bs_utf8_size,Src}}; 1182bin_size_calc(utf16, Src, _Size, _Unit) -> 1183 {8,{bs_utf16_size,Src}}; 1184bin_size_calc(utf32, _Src, _Size, _Unit) -> 1185 {8,#b_literal{val=4}}; 1186bin_size_calc(binary, Src, #b_literal{val=all}, Unit) -> 1187 case Unit rem 8 of 1188 0 -> {8,{{bif,byte_size},Src}}; 1189 _ -> {1,{{bif,bit_size},Src}} 1190 end; 1191bin_size_calc(_Type, _Src, Size, Unit) -> 1192 {Unit,Size}. 1193 1194fold_size_calc([{Unit,#b_literal{val=Size}}|T], Bits, Acc) -> 1195 if 1196 is_integer(Size) -> 1197 fold_size_calc(T, Bits + Unit*Size, Acc); 1198 true -> 1199 error 1200 end; 1201fold_size_calc([{U,#b_var{}}=H|T], Bits, Acc) when U =:= 1; U =:= 8 -> 1202 fold_size_calc(T, Bits, [H|Acc]); 1203fold_size_calc([{U,#b_var{}=Var}|T], Bits, Acc) -> 1204 fold_size_calc(T, Bits, [{1,{'*',#b_literal{val=U},Var}}|Acc]); 1205fold_size_calc([{_,_}=H|T], Bits, Acc) -> 1206 fold_size_calc(T, Bits, [H|Acc]); 1207fold_size_calc([], Bits, Acc) -> 1208 Bytes = Bits div 8, 1209 RemBits = Bits rem 8, 1210 Sizes = sort([{1,#b_literal{val=RemBits}},{8,#b_literal{val=Bytes}}|Acc]), 1211 [Pair || {_,Sz}=Pair <- Sizes, Sz =/= #b_literal{val=0}]. 1212 1213%%% 1214%%% Utilities for creating the SSA types. 1215%%% 1216 1217ssa_args(As, St) -> 1218 [ssa_arg(A, St) || A <- As]. 1219 1220ssa_arg(#k_var{name=V}, #cg{vars=Vars}) -> map_get(V, Vars); 1221ssa_arg(#k_literal{val=V}, _) -> #b_literal{val=V}; 1222ssa_arg(#k_remote{mod=Mod0,name=Name0,arity=Arity}, St) -> 1223 Mod = ssa_arg(Mod0, St), 1224 Name = ssa_arg(Name0, St), 1225 #b_remote{mod=Mod,name=Name,arity=Arity}; 1226ssa_arg(#k_local{name=Name,arity=Arity}, _) when is_atom(Name) -> 1227 #b_local{name=#b_literal{val=Name},arity=Arity}. 1228 1229new_succeeded_value(VarBase, Var, #cg{vars=Vars0}=St) -> 1230 Vars = Vars0#{VarBase=>{succeeded,Var}}, 1231 St#cg{vars=Vars}. 1232 1233new_bool_value(VarBase, Var, #cg{vars=Vars0}=St) -> 1234 Vars = Vars0#{VarBase=>{bool,Var}}, 1235 St#cg{vars=Vars}. 1236 1237new_ssa_vars(Vs, St) -> 1238 mapfoldl(fun(#k_var{name=V}, S) -> 1239 new_ssa_var(V, S) 1240 end, St, Vs). 1241 1242new_ssa_var(VarBase, #cg{lcount=Uniq,vars=Vars}=St0) 1243 when is_atom(VarBase); is_integer(VarBase) -> 1244 case Vars of 1245 #{VarBase:=_} -> 1246 Var = #b_var{name={VarBase,Uniq}}, 1247 St = St0#cg{lcount=Uniq+1,vars=Vars#{VarBase=>Var}}, 1248 {Var,St}; 1249 #{} -> 1250 Var = #b_var{name=VarBase}, 1251 St = St0#cg{vars=Vars#{VarBase=>Var}}, 1252 {Var,St} 1253 end. 1254 1255set_unused_ssa_vars(Vars, St) -> 1256 foldl(fun(#k_var{name=V}, S) -> 1257 set_ssa_var(V, #b_literal{val=unused}, S) 1258 end, St, Vars). 1259 1260set_ssa_var(VarBase, Val, #cg{vars=Vars}=St) 1261 when is_atom(VarBase); is_integer(VarBase) -> 1262 St#cg{vars=Vars#{VarBase=>Val}}. 1263 1264%% new_label(St) -> {L,St}. 1265 1266new_label(#cg{lcount=Next}=St) -> 1267 {Next,St#cg{lcount=Next+1}}. 1268 1269%% line_anno(Le) -> #{} | #{location:={File,Line}}. 1270%% Create a location annotation, containing information about the 1271%% current filename and line number. The annotation should be 1272%% included in any operation that could cause an exception. 1273 1274line_anno([Line,{file,Name}]) when is_integer(Line) -> 1275 line_anno_1(Name, Line); 1276line_anno([{Line,Column},{file,Name}]) when is_integer(Line), 1277 is_integer(Column) -> 1278 line_anno_1(Name, Line); 1279line_anno([_|_]=A) -> 1280 {Name,Line} = find_loc(A, no_file, 0), 1281 line_anno_1(Name, Line); 1282line_anno([]) -> 1283 #{}. 1284 1285line_anno_1(no_file, _) -> 1286 #{}; 1287line_anno_1(_, 0) -> 1288 %% Missing line number or line number 0. 1289 #{}; 1290line_anno_1(Name, Line) -> 1291 #{location=>{Name,Line}}. 1292 1293find_loc([Line|T], File, _) when is_integer(Line) -> 1294 find_loc(T, File, Line); 1295find_loc([{Line, Column}|T], File, _) when is_integer(Line), 1296 is_integer(Column) -> 1297 find_loc(T, File, Line); 1298find_loc([{file,File}|T], _, Line) -> 1299 find_loc(T, File, Line); 1300find_loc([_|T], File, Line) -> 1301 find_loc(T, File, Line); 1302find_loc([], File, Line) -> {File,Line}. 1303 1304flatmapfoldl(F, Accu0, [Hd|Tail]) -> 1305 {R,Accu1} = F(Hd, Accu0), 1306 {Rs,Accu2} = flatmapfoldl(F, Accu1, Tail), 1307 {R++Rs,Accu2}; 1308flatmapfoldl(_, Accu, []) -> {[],Accu}. 1309 1310%%% 1311%%% Finalize the code. 1312%%% 1313 1314finalize(Asm0, St0) -> 1315 Asm1 = fix_phis(Asm0), 1316 {Asm,St} = fix_sets(Asm1, [], St0), 1317 {build_map(Asm),St}. 1318 1319%% fix_phis(Is0) -> Is. 1320%% Rewrite #cg_break{} and #cg_phi{} records to #b_set{} records. 1321%% A #cg_break{} is rewritten to an unconditional branch, and 1322%% and a #cg_phi{} is rewritten to one or more phi nodes. 1323 1324fix_phis(Is) -> 1325 fix_phis_1(Is, none, #{}). 1326 1327fix_phis_1([{label,Lbl},#cg_phi{vars=Vars}|Is0], _Lbl, Map0) -> 1328 case Map0 of 1329 #{Lbl:=Pairs} -> 1330 %% This phi node was referenced by at least one #cg_break{}. 1331 %% Create the phi nodes. 1332 Phis = gen_phis(Vars, Pairs), 1333 Map = maps:remove(Lbl, Map0), 1334 [{label,Lbl}] ++ Phis ++ fix_phis_1(Is0, Lbl, Map); 1335 #{} -> 1336 %% No #cg_break{} instructions reference this label. 1337 %% #cg_break{} instructions must reference the labels for 1338 %% #cg_phi{} instructions; therefore this label is 1339 %% unreachable and can be dropped. 1340 Is = drop_upto_label(Is0), 1341 fix_phis_1(Is, none, Map0) 1342 end; 1343fix_phis_1([{label,L}=I|Is], _Lbl, Map) -> 1344 [I|fix_phis_1(Is, L, Map)]; 1345fix_phis_1([#cg_unreachable{}|Is0], _Lbl, Map) -> 1346 Is = drop_upto_label(Is0), 1347 fix_phis_1(Is, none, Map); 1348fix_phis_1([#cg_break{args=Args,phi=Target}|Is], Lbl, Map) when is_integer(Lbl) -> 1349 %% Pair each argument with the label for this block and save in the map. 1350 Pairs1 = case Map of 1351 #{Target:=Pairs0} -> Pairs0; 1352 #{} -> [] 1353 end, 1354 Pairs = [[{Arg,Lbl} || Arg <- Args]|Pairs1], 1355 I = make_uncond_branch(Target), 1356 [I|fix_phis_1(Is, none, Map#{Target=>Pairs})]; 1357fix_phis_1([I|Is], Lbl, Map) -> 1358 [I|fix_phis_1(Is, Lbl, Map)]; 1359fix_phis_1([], _, Map) -> 1360 [] = maps:to_list(Map), %Assertion. 1361 []. 1362 1363gen_phis([V|Vs], Preds0) -> 1364 {Pairs,Preds} = collect_preds(Preds0, [], []), 1365 [_|_] = Pairs, %Assertion. 1366 [#b_set{op=phi,dst=V,args=Pairs}|gen_phis(Vs, Preds)]; 1367gen_phis([], _) -> []. 1368 1369collect_preds([[First|Rest]|T], ColAcc, RestAcc) -> 1370 collect_preds(T, [First|ColAcc], [Rest|RestAcc]); 1371collect_preds([], ColAcc, RestAcc) -> 1372 {keysort(2, ColAcc),RestAcc}. 1373 1374drop_upto_label([{label,_}|_]=Is) -> Is; 1375drop_upto_label([_|Is]) -> drop_upto_label(Is). 1376 1377%% fix_sets(Is0, Acc, St0) -> {Is,St}. 1378%% Ensure that #b_set.dst is filled in with a proper variable. 1379%% (For convenience, for instructions that don't have a useful return value, 1380%% the code generator would set #b_set.dst to `none`.) 1381 1382fix_sets([#b_set{op=remove_message,dst=Dst}=Set,#b_ret{arg=Dst}=Ret|Is], Acc, St) -> 1383 %% The remove_message instruction, which is an instruction without 1384 %% value, was used in effect context in an `after` block. Example: 1385 %% 1386 %% try 1387 %% . . . 1388 %% after 1389 %% . 1390 %% . 1391 %% . 1392 %% receive _ -> ignored end 1393 %% end, 1394 %% ok. 1395 %% 1396 fix_sets(Is, [Ret#b_ret{arg=#b_literal{val=ok}},Set|Acc], St); 1397fix_sets([#b_set{dst=none}=Set|Is], Acc, St0) -> 1398 {Dst,St} = new_ssa_var('@ssa_ignored', St0), 1399 I = Set#b_set{dst=Dst}, 1400 fix_sets(Is, [I|Acc], St); 1401fix_sets([I|Is], Acc, St) -> 1402 fix_sets(Is, [I|Acc], St); 1403fix_sets([], Acc, St) -> 1404 {reverse(Acc),St}. 1405 1406%% build_map(Is) -> #{}. 1407%% Split up the sequential instruction stream into blocks and 1408%% store them in a map. 1409 1410build_map(Is) -> 1411 Linear0 = build_graph_1(Is, [], []), 1412 Linear = beam_ssa:trim_unreachable(Linear0), 1413 maps:from_list(Linear). 1414 1415build_graph_1([{label,L}|Is], Lbls, []) -> 1416 build_graph_1(Is, [L|Lbls], []); 1417build_graph_1([{label,L}|Is], Lbls, [_|_]=BlockAcc) -> 1418 make_blocks(Lbls, BlockAcc) ++ build_graph_1(Is, [L], []); 1419build_graph_1([I|Is], Lbls, BlockAcc) -> 1420 build_graph_1(Is, Lbls, [I|BlockAcc]); 1421build_graph_1([], Lbls, BlockAcc) -> 1422 make_blocks(Lbls, BlockAcc). 1423 1424make_blocks(Lbls, [Last0|Is0]) -> 1425 Is = reverse(Is0), 1426 Last = beam_ssa:normalize(Last0), 1427 Block = #b_blk{is=Is,last=Last}, 1428 [{L,Block} || L <- Lbls]. 1429