diff options
Diffstat (limited to 'lib/compiler/src/beam_ssa_codegen.erl')
-rw-r--r-- | lib/compiler/src/beam_ssa_codegen.erl | 323 |
1 files changed, 184 insertions, 139 deletions
diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index 1830f9647e..9c430d45b6 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2021. All Rights Reserved. +%% Copyright Ericsson AB 2018-2022. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ -export_type([ssa_register/0]). -include("beam_ssa.hrl"). +-include("beam_asm.hrl"). -import(lists, [foldl/3,keymember/3,keysort/2,map/2,mapfoldl/3, member/2,reverse/1,reverse/2,sort/1, @@ -104,11 +105,7 @@ module(#b_module{name=Mod,exports=Es,attributes=Attrs,body=Fs}, _Opts) -> -type sw_list_item() :: {b_literal(),ssa_label()}. --type reg_num() :: beam_asm:reg_num(). --type xreg() :: {'x',reg_num()}. --type yreg() :: {'y',reg_num()}. - --type ssa_register() :: xreg() | yreg() | {'fr',reg_num()} | {'z',reg_num()}. +-type ssa_register() :: xreg() | yreg() | freg() | zreg(). functions(Forms, AtomMod) -> mapfoldl(fun (F, St) -> function(F, AtomMod, St) end, @@ -252,7 +249,7 @@ need_heap_blks([], H, Acc) -> need_heap_is([#cg_alloc{words=Words}=Alloc0|Is], N, Acc) -> Alloc = Alloc0#cg_alloc{words=add_heap_words(N, Words)}, need_heap_is(Is, #need{}, [Alloc|Acc]); -need_heap_is([#cg_set{anno=Anno,op=bs_init}=I0|Is], N, Acc) -> +need_heap_is([#cg_set{anno=Anno,op=bs_create_bin}=I0|Is], N, Acc) -> Alloc = case need_heap_need(N) of [#cg_alloc{words=Need}] -> alloc(Need); [] -> 0 @@ -284,13 +281,11 @@ need_heap_terminator([{_,#cg_blk{is=Is,last=#cg_br{succ=L}}}|_], L, N) -> [] -> {[],#need{}}; [_|_]=Alloc -> - %% If the preceding instructions are a binary construction, - %% hoist the allocation and incorporate into the bs_init + %% If the preceding instruction is a bs_create_bin instruction, + %% hoist the allocation and incorporate into the bs_create_bin %% instruction. case reverse(Is) of - [#cg_set{op=succeeded},#cg_set{op=bs_init}|_] -> - {[],N}; - [#cg_set{op=succeeded},#cg_set{op=bs_put}|_] -> + [#cg_set{op=succeeded},#cg_set{op=bs_create_bin}|_] -> {[],N}; _ -> %% Not binary construction. Must emit an allocation @@ -342,8 +337,6 @@ add_heap_float(#need{f=F}=N) -> classify_heap_need(put_list, _) -> {put,2}; -classify_heap_need(put_tuple_arity, [#b_literal{val=Words}]) -> - {put,Words+1}; classify_heap_need(put_tuple, Elements) -> {put,length(Elements)+1}; classify_heap_need(make_fun, Args) -> @@ -373,22 +366,16 @@ classify_heap_need(Name, _Args) -> %% Note: Only handle operations in this function that are not handled %% by classify_heap_need/2. -classify_heap_need(bs_add) -> gc; classify_heap_need(bs_get) -> gc; classify_heap_need(bs_get_tail) -> gc; -classify_heap_need(bs_init) -> gc; classify_heap_need(bs_init_writable) -> gc; classify_heap_need(bs_match_string) -> gc; -classify_heap_need(bs_put) -> neutral; -classify_heap_need(bs_restore) -> neutral; -classify_heap_need(bs_save) -> neutral; +classify_heap_need(bs_create_bin) -> gc; classify_heap_need(bs_get_position) -> gc; classify_heap_need(bs_set_position) -> neutral; classify_heap_need(bs_skip) -> gc; classify_heap_need(bs_start_match) -> gc; classify_heap_need(bs_test_tail) -> neutral; -classify_heap_need(bs_utf16_size) -> neutral; -classify_heap_need(bs_utf8_size) -> neutral; classify_heap_need(build_stacktrace) -> gc; classify_heap_need(call) -> gc; classify_heap_need(catch_end) -> gc; @@ -404,12 +391,12 @@ classify_heap_need(is_tagged_tuple) -> neutral; classify_heap_need(kill_try_tag) -> gc; classify_heap_need(landingpad) -> gc; classify_heap_need(match_fail) -> gc; +classify_heap_need(nif_start) -> neutral; classify_heap_need(nop) -> neutral; -classify_heap_need(new_try_tag) -> gc; +classify_heap_need(new_try_tag) -> neutral; classify_heap_need(old_make_fun) -> gc; classify_heap_need(peek_message) -> gc; classify_heap_need(put_map) -> gc; -classify_heap_need(put_tuple_elements) -> neutral; classify_heap_need(raw_raise) -> gc; classify_heap_need(recv_marker_bind) -> neutral; classify_heap_need(recv_marker_clear) -> neutral; @@ -682,8 +669,8 @@ get_live(#cg_set{anno=#{live:=Live}}) -> need_live_anno(Op) -> case Op of {bif,_} -> true; + bs_create_bin -> true; bs_get -> true; - bs_init -> true; bs_get_position -> true; bs_get_tail -> true; bs_start_match -> true; @@ -806,7 +793,7 @@ need_y_init(#cg_set{anno=#{clobbers:=Clobbers}}) -> Clobbers; need_y_init(#cg_set{op=bs_get}) -> true; need_y_init(#cg_set{op=bs_get_position}) -> true; need_y_init(#cg_set{op=bs_get_tail}) -> true; -need_y_init(#cg_set{op=bs_init}) -> true; +need_y_init(#cg_set{op=bs_create_bin}) -> true; need_y_init(#cg_set{op=bs_skip,args=[#b_literal{val=Type}|_]}) -> case Type of utf8 -> true; @@ -892,7 +879,7 @@ opt_allocate_is([]) -> none. %% fix_wait_timeout([Block]) -> [Block]. %% In SSA code, the `wait_timeout` instruction is a three-way branch %% (because there will be an exception for a bad timeout value). In -%% BEAM code, the potential rasing of an exception for a bad timeout +%% BEAM code, the potential raising of an exception for a bad timeout %% duration is not explicitly represented. Thus we will need to %% rewrite the following code: %% @@ -1052,7 +1039,8 @@ cg_block([#cg_set{op=new_try_tag,dst=Tag,args=Args}], {Tag,Fail0}, St) -> {[{Kind,Reg,Fail}],St}; cg_block([#cg_set{anno=Anno,op={bif,Name},dst=Dst0,args=Args0}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail0}, St) -> - [Dst|Args] = beam_args([Dst0|Args0], St), + Args = typed_args(Args0, Anno, St), + Dst = beam_arg(Dst0, St), Line0 = call_line(body, {extfunc,erlang,Name,length(Args)}, Anno), Fail = bif_fail(Fail0), Line = case Fail of @@ -1083,9 +1071,10 @@ cg_block([#cg_set{op={bif,tuple_size},dst=Arity0,args=[Tuple0]}, {Is,St} = cg_block([Eq], Context, St0), {[TupleSize|Is],St} end; -cg_block([#cg_set{op={bif,Name},dst=Dst0,args=Args0}]=Is0, {Dst0,Fail}, St0) -> - [Dst|Args] = beam_args([Dst0|Args0], St0), - case Dst of +cg_block([#cg_set{anno=Anno,op={bif,Name},dst=Dst0,args=Args0}]=Is0, + {Dst0,Fail}, St0) -> + Args = typed_args(Args0, Anno, St0), + case beam_arg(Dst0, St0) of {z,_} -> %% The result of the BIF call will only be used once. Convert to %% a test instruction. @@ -1100,7 +1089,8 @@ cg_block([#cg_set{op={bif,Name},dst=Dst0,args=Args0}]=Is0, {Dst0,Fail}, St0) -> end; cg_block([#cg_set{anno=Anno,op={bif,Name},dst=Dst0,args=Args0}=I|T], Context, St0) -> - [Dst|Args] = beam_args([Dst0|Args0], St0), + Args = typed_args(Args0, Anno, St0), + Dst = beam_arg(Dst0, St0), {Is0,St} = cg_block(T, Context, St0), case is_gc_bif(Name, Args) of true -> @@ -1113,47 +1103,31 @@ cg_block([#cg_set{anno=Anno,op={bif,Name},dst=Dst0,args=Args0}=I|T], Is = [{bif,Name,{f,0},Args,Dst}|Is0], {Is,St} end; -cg_block([#cg_set{op=bs_init,dst=Dst0,args=Args0,anno=Anno}=I, +cg_block([#cg_set{op=bs_create_bin,dst=Dst0,args=Args0,anno=Anno}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail0}, St) -> + Args1 = typed_args(Args0, Anno, St), Fail = bif_fail(Fail0), Line = line(Anno), Alloc = map_get(alloc, Anno), - [#b_literal{val=Kind}|Args1] = Args0, Live = get_live(I), - case Kind of - new -> - [Dst,Size,{integer,Unit}] = beam_args([Dst0|Args1], St), - {[Line|cg_bs_init(Dst, Size, Alloc, Unit, Live, Fail)],St}; - private_append -> - [Dst,Src,Bits,{integer,Unit}] = beam_args([Dst0|Args1], St), - Flags = {field_flags,[]}, - TestHeap = {test_heap,Alloc,Live}, - BsPrivateAppend = {bs_private_append,Fail,Bits,Unit,Src,Flags,Dst}, - Is = [TestHeap,Line,BsPrivateAppend], - {Is,St}; - append -> - [Dst,Src,Bits,{integer,Unit}] = beam_args([Dst0|Args1], St), - Flags = {field_flags,[]}, - Is = [Line,{bs_append,Fail,Bits,Alloc,Live,Unit,Src,Flags,Dst}], - {Is,St} - end; -cg_block([#cg_set{anno=Anno, - op=bs_start_match, + Dst = beam_arg(Dst0, St), + Args = bs_args(Args1), + Unit = case Args of + [{atom,append},_Seg,U|_] -> U; + [{atom,private_append},_Seg,U|_] -> U; + _ -> 1 + end, + Is = [Line,{bs_create_bin,Fail,Alloc,Live,Unit,Dst,{list,Args}}], + {Is,St}; +cg_block([#cg_set{op=bs_start_match, dst=Ctx0, args=[#b_literal{val=new},Bin0]}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> [Dst,Bin1] = beam_args([Ctx0,Bin0], St), {Bin,Pre} = force_reg(Bin1, Dst), Live = get_live(I), - %% num_slots is only set when using the old instructions. - case maps:find(num_slots, Anno) of - {ok, Slots} -> - Is = Pre ++ [{test,bs_start_match2,Fail,Live,[Bin,Slots],Dst}], - {Is,St}; - error -> - Is = Pre ++ [{test,bs_start_match3,Fail,Live,[Bin],Dst}], - {Is,St} - end; + Is = Pre ++ [{test,bs_start_match3,Fail,Live,[Bin],Dst}], + {Is,St}; cg_block([#cg_set{op=bs_get}=Set, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> {cg_bs_get(Fail, Set, St),St}; @@ -1169,10 +1143,6 @@ cg_block([#cg_set{op=bs_match_string,args=[CtxVar,#b_literal{val=String0}]}, Is = [{test,bs_match_string,Fail,[CtxReg,Bits,{string,String}]}], {Is,St}; -cg_block([#cg_set{op=bs_put,args=Args0}, - #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> - Args = beam_args(Args0, St), - {cg_bs_put(bif_fail(Fail), Args),St}; cg_block([#cg_set{dst=Dst0,op=landingpad,args=Args0}|T], Context, St0) -> [Dst,{atom,Kind},Tag] = beam_args([Dst0|Args0], St0), case Kind of @@ -1197,11 +1167,18 @@ cg_block([#cg_set{op=match_fail}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,_Fail}, St) -> %% A match_fail instruction in a try/catch block. cg_block([I], none, St); -cg_block([#cg_set{op=get_map_element,dst=Dst0,args=Args0}, +cg_block([#cg_set{op=get_map_element,dst=Dst0,args=Args0,anno=Anno}, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail0}, St) -> - [Dst,Map,Key] = beam_args([Dst0|Args0], St), + [Map,Key] = typed_args(Args0, Anno, St), + Dst = beam_arg(Dst0, St), Fail = ensure_label(Fail0, St), {[{get_map_elements,Fail,Map,{list,[Key,Dst]}}],St}; +cg_block([#cg_set{op={float,convert},dst=Dst0,args=Args0,anno=Anno}, + #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> + {f,0} = bif_fail(Fail), %Assertion. + [Src] = typed_args(Args0, Anno, St), + Dst = beam_arg(Dst0, St), + {[line(Anno),{fconv,Src,Dst}], St}; cg_block([#cg_set{op=Op,dst=Dst0,args=Args0}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> [Dst|Args] = beam_args([Dst0|Args0], St), @@ -1209,9 +1186,19 @@ cg_block([#cg_set{op=Op,dst=Dst0,args=Args0}=I, cg_block([#cg_set{op=bs_test_tail,dst=Bool,args=Args0}], {Bool,Fail}, St) -> [Ctx,{integer,Bits}] = beam_args(Args0, St), {[{test,bs_test_tail2,bif_fail(Fail),[Ctx,Bits]}],St}; -cg_block([#cg_set{op=is_tagged_tuple,dst=Bool,args=Args0}], {Bool,Fail}, St) -> - [Src,{integer,Arity},Tag] = beam_args(Args0, St), - {[{test,is_tagged_tuple,ensure_label(Fail, St),[Src,Arity,Tag]}],St}; +cg_block([#cg_set{op=is_tagged_tuple,anno=Anno,dst=Bool,args=Args0}], {Bool,Fail}, St) -> + case Anno of + #{constraints := arity} -> + [Src,{integer,Arity},_Tag] = beam_args(Args0, St), + {[{test,test_arity,ensure_label(Fail, St),[Src,Arity]}],St}; + #{constraints := tuple_arity} -> + [Src,{integer,Arity},_Tag] = beam_args(Args0, St), + {[{test,is_tuple,ensure_label(Fail, St),[Src]}, + {test,test_arity,ensure_label(Fail, St),[Src,Arity]}],St}; + #{} -> + [Src,{integer,Arity},Tag] = typed_args(Args0, Anno, St), + {[{test,is_tagged_tuple,ensure_label(Fail, St),[Src,Arity,Tag]}],St} + end; cg_block([#cg_set{op=is_nonempty_list,dst=Bool,args=Args0}], {Bool,Fail}, St) -> Args = beam_args(Args0, St), {[{test,is_nonempty_list,ensure_label(Fail, St),Args}],St}; @@ -1332,6 +1319,32 @@ cg_block([], {Bool0,Fail}, St) -> [Bool] = beam_args([Bool0], St), {[{test,is_eq_exact,Fail,[Bool,{atom,true}]}],St}. +bs_args([{atom,binary},{literal,[1|_]},{literal,Bs},{atom,all}|Args]) + when bit_size(Bs) =:= 0 -> + bs_args(Args); +bs_args([{atom,binary},{literal,[1|_]}=UFs,{literal,Bs},{atom,all}|Args0]) + when is_bitstring(Bs) -> + Bits = bit_size(Bs), + Bytes = Bits div 8, + case Bits rem 8 of + 0 -> + [{atom,string},0,8,nil,{string,Bs},{integer,byte_size(Bs)}|bs_args(Args0)]; + Rem -> + <<Binary:Bytes/bytes,Int:Rem>> = Bs, + Args = [{atom,binary},UFs,{literal,Binary},{atom,all}, + {atom,integer},{literal,[1]},{integer,Int},{integer,Rem}|Args0], + bs_args(Args) + end; +bs_args([Type,{literal,[Unit|Fs0]},Val,Size|Args]) -> + Segment = proplists:get_value(segment, Fs0, 0), + Fs1 = proplists:delete(segment, Fs0), + Fs = case Fs1 of + [] -> nil; + [_|_] -> {literal,Fs1} + end, + [Type,Segment,Unit,Fs,Val,Size|bs_args(Args)]; +bs_args([]) -> []. + cg_copy(T0, St) -> {Copies,T} = splitwith(fun(#cg_set{op=copy}) -> true; (_) -> false @@ -1564,13 +1577,20 @@ cg_call(#cg_set{anno=Anno0,op=call,dst=Dst0,args=[#b_remote{}=Func0|Args0]}, [line(Anno0)] ++ Apply, {Is,St} end; -cg_call(#cg_set{anno=Anno,op=call,dst=Dst0,args=Args0}, - Where, Context, St) -> - [Dst,Func|Args] = beam_args([Dst0|Args0], St), +cg_call(#cg_set{anno=Anno,op=call,dst=Dst0,args=[Func | Args0]}, + Where, Context, St0) -> Line = call_line(Where, Func, Anno), - Arity = length(Args), - Call = build_call(call_fun, Arity, Func, Context, Dst), - Is = setup_args(Args++[Func], Anno, Context, St) ++ Line ++ Call, + Args = beam_args(Args0 ++ [Func], St0), + + Arity = length(Args0), + Dst = beam_arg(Dst0, St0), + + %% Note that we only inspect the (possible) type of the fun while building + %% the call, we don't want the arguments to be typed. + [TypedFunc] = typed_args([Func], Anno, St0), + {Call, St} = build_fun_call(Arity, TypedFunc, Context, Dst, St0), + + Is = setup_args(Args, Anno, Context, St) ++ Line ++ Call, case Anno of #{ result_type := Type } -> Info = {var_info, Dst, [{type,Type}]}, @@ -1615,12 +1635,6 @@ build_stk([V|Vs], TmpReg, Tail) -> build_stk([], _TmpReg, nil) -> [{move,nil,{x,1}}]. -build_call(call_fun, Arity, _Func, none, Dst) -> - [{call_fun,Arity}|copy({x,0}, Dst)]; -build_call(call_fun, Arity, _Func, {return,Dst,N}, Dst) when is_integer(N) -> - [{call_fun,Arity},{deallocate,N},return]; -build_call(call_fun, Arity, _Func, {return,Val,N}, _Dst) when is_integer(N) -> - [{call_fun,Arity},{move,Val,{x,0}},{deallocate,N},return]; build_call(call_ext, 2, {extfunc,erlang,'!',2}, none, Dst) -> [send|copy({x,0}, Dst)]; build_call(call_ext, 2, {extfunc,erlang,'!',2}, {return,Dst,N}, Dst) @@ -1646,6 +1660,42 @@ build_call(I, Arity, Func, {return,Val,N}, _Dst) when is_integer(N) -> build_call(I, Arity, Func, none, Dst) -> [{I,Arity,Func}|copy({x,0}, Dst)]. +build_fun_call(Arity, #tr{}=Func0, none, Dst, St0) -> + %% Func0 was the source register prior to copying arguments, and has been + %% moved to {x, Arity}. Update it to match. + Func = Func0#tr{r={x,Arity}}, + {Tag, St} = fun_call_tag(Arity, Func, St0), + Is = [{call_fun2,Tag,Arity,Func}|copy({x,0}, Dst)], + {Is, St}; +build_fun_call(Arity, #tr{}=Func0, {return,Dst,N}, Dst, St0) + when is_integer(N) -> + Func = Func0#tr{r={x,Arity}}, + {Tag, St} = fun_call_tag(Arity, Func, St0), + Is = [{call_fun2,Tag,Arity,Func},{deallocate,N},return], + {Is, St}; +build_fun_call(Arity, #tr{}=Func0, {return,Val,N}, _Dst, St0) + when is_integer(N) -> + Func = Func0#tr{r={x,Arity}}, + {Tag, St} = fun_call_tag(Arity, Func, St0), + Is = [{call_fun2,Tag,Arity,Func}, + {move,Val,{x,0}}, + {deallocate,N},return], + {Is, St}; +build_fun_call(Arity, _Func, none, Dst, St) -> + {[{call_fun,Arity}|copy({x,0}, Dst)], St}; +build_fun_call(Arity, _Func, {return,Dst,N}, Dst, St) when is_integer(N) -> + {[{call_fun,Arity},{deallocate,N},return], St}; +build_fun_call(Arity, _Func, {return,Val,N}, _Dst, St) when is_integer(N) -> + {[{call_fun,Arity},{move,Val,{x,0}},{deallocate,N},return], St}. + +fun_call_tag(Arity, #tr{t=#t_fun{arity=Arity,target={Name,TotalArity}}}, St0) -> + {FuncLbl, St} = local_func_label(Name, TotalArity, St0), + {{f,FuncLbl}, St}; +fun_call_tag(Arity, #tr{t=#t_fun{arity=Arity}}, St) -> + {{atom,safe}, St}; +fun_call_tag(_Arity, _Func, St) -> + {{atom,unsafe}, St}. + build_apply(Arity, {return,Dst,N}, Dst) when is_integer(N) -> [{apply_last,Arity,N}]; build_apply(Arity, {return,Val,N}, _Dst) when is_integer(N) -> @@ -1669,21 +1719,19 @@ cg_instr(bs_get_position, [Ctx], Dst, Set) -> cg_instr(put_map, [{atom,assoc},SrcMap|Ss], Dst, Set) -> Live = get_live(Set), [{put_map_assoc,{f,0},SrcMap,Dst,Live,{list,Ss}}]; +cg_instr(is_nonempty_list, Ss, Dst, Set) -> + #cg_set{anno=#{was_bif_is_list := true}} = Set, %Assertion. + + %% This instruction was a call to is_list/1, which was rewritten + %% to an is_nonempty_list test by beam_ssa_type. BEAM has no + %% is_nonempty_list instruction that will return a boolean, so + %% we must revert it to an is_list/1 call. + [{bif,is_list,{f,0},Ss,Dst}]; cg_instr(Op, Args, Dst, _Set) -> cg_instr(Op, Args, Dst). cg_instr(bs_init_writable, Args, Dst) -> setup_args(Args) ++ [bs_init_writable|copy({x,0}, Dst)]; -cg_instr(bs_restore, [Ctx,Slot], _Dst) -> - case Slot of - {integer,N} -> - [{bs_restore2,Ctx,N}]; - {atom,start} -> - [{bs_restore2,Ctx,Slot}] - end; -cg_instr(bs_save, [Ctx,Slot], _Dst) -> - {integer,N} = Slot, - [{bs_save2,Ctx,N}]; cg_instr(bs_set_position, [Ctx,Pos], _Dst) -> [{bs_set_position,Ctx,Pos}]; cg_instr(build_stacktrace, Args, Dst) -> @@ -1702,16 +1750,14 @@ cg_instr(get_tuple_element=Op, [Src,{integer,N}], Dst) -> [{Op,Src,N,Dst}]; cg_instr(has_map_field, [Map,Key], Dst) -> [{bif,is_map_key,{f,0},[Key,Map],Dst}]; +cg_instr(nif_start, [], _Dst) -> + [nif_start]; cg_instr(put_list=Op, [Hd,Tl], Dst) -> [{Op,Hd,Tl,Dst}]; cg_instr(nop, [], _Dst) -> []; cg_instr(put_tuple, Elements, Dst) -> [{put_tuple2,Dst,{list,Elements}}]; -cg_instr(put_tuple_arity, [{integer,Arity}], Dst) -> - [{put_tuple,Arity,Dst}]; -cg_instr(put_tuple_elements, Elements, _Dst) -> - [{put,E} || E <- Elements]; cg_instr(raw_raise, Args, Dst) -> setup_args(Args) ++ [raw_raise|copy({x,0}, Dst)]; cg_instr(recv_marker_bind, [Mark, Ref], _Dst) -> @@ -1725,17 +1771,8 @@ cg_instr(remove_message, [], _Dst) -> cg_instr(resume, [A,B], _Dst) -> [{bif,raise,{f,0},[A,B],{x,0}}]. -cg_test(bs_add=Op, Fail, [Src1,Src2,{integer,Unit}], Dst, _I) -> - [{Op,Fail,[Src1,Src2,Unit],Dst}]; cg_test(bs_skip, Fail, Args, _Dst, I) -> cg_bs_skip(Fail, Args, I); -cg_test(bs_utf8_size=Op, Fail, [Src], Dst, _I) -> - [{Op,Fail,Src,Dst}]; -cg_test(bs_utf16_size=Op, Fail, [Src], Dst, _I) -> - [{Op,Fail,Src,Dst}]; -cg_test({float,convert}, Fail, [Src], Dst, #cg_set{anno=Anno}) -> - {f,0} = Fail, %Assertion. - [line(Anno),{fconv,Src,Dst}]; cg_test({float,Op0}, Fail, Args, Dst, #cg_set{anno=Anno}) -> Op = case Op0 of '+' -> fadd; @@ -1749,7 +1786,11 @@ cg_test(peek_message, Fail, [], Dst, _I) -> [{loop_rec,Fail,{x,0}}|copy({x,0}, Dst)]; cg_test(put_map, Fail, [{atom,exact},SrcMap|Ss], Dst, #cg_set{anno=Anno}=Set) -> Live = get_live(Set), - [line(Anno),{put_map_exact,Fail,SrcMap,Dst,Live,{list,Ss}}]. + [line(Anno),{put_map_exact,Fail,SrcMap,Dst,Live,{list,Ss}}]; +cg_test(raw_raise, _Fail, Args, Dst, _I) -> + cg_instr(raw_raise, Args, Dst); +cg_test(resume, _Fail, [_,_]=Args, Dst, _I) -> + cg_instr(resume, Args, Dst). cg_bs_get(Fail, #cg_set{dst=Dst0,args=[#b_literal{val=Type}|Ss0]}=Set, St) -> Op = case Type of @@ -1802,34 +1843,6 @@ field_flags(Flags, #cg_set{anno=#{location:={File,Line}}}) -> field_flags(Flags, _) -> {field_flags,Flags}. -cg_bs_put(Fail, [{atom,Type},{literal,Flags}|Args]) -> - Op = case Type of - integer -> bs_put_integer; - float -> bs_put_float; - binary -> bs_put_binary; - utf8 -> bs_put_utf8; - utf16 -> bs_put_utf16; - utf32 -> bs_put_utf32 - end, - case Args of - [Src,Size,{integer,Unit}] -> - [{Op,Fail,Size,Unit,{field_flags,Flags},Src}]; - [Src] -> - [{Op,Fail,{field_flags,Flags},Src}] - end. - -cg_bs_init(Dst, Size0, Alloc, Unit, Live, Fail) -> - Op = case Unit of - 1 -> bs_init_bits; - 8 -> bs_init2 - end, - Size = cg_bs_init_size(Size0), - [{Op,Fail,Size,Alloc,Live,{field_flags,[]},Dst}]. - -cg_bs_init_size({x,_}=R) -> R; -cg_bs_init_size({y,_}=R) -> R; -cg_bs_init_size({integer,Int}) -> Int. - cg_catch(Agg, T0, Context, St0) -> {Moves,T1} = cg_extract(T0, Agg, St0), {T,St} = cg_block(T1, Context, St0), @@ -1855,6 +1868,9 @@ cg_extract([#cg_set{op=extract,dst=Dst0,args=Args0}|Is0], Agg, St) -> cg_extract(Is, _, _) -> {[],Is}. +-spec copy(Src, Dst) -> [{move,Src,Dst}] when + Src :: beam_reg() | beam_literal(), + Dst :: beam_reg(). copy(Src, Src) -> []; copy(Src, Dst) -> [{move,Src,Dst}]. @@ -1916,13 +1932,30 @@ translate_block(L, #b_blk{anno=Anno,is=Is0,last=Last0}, Blocks) -> translate_is([#b_set{op=phi}|Is], Tail) -> translate_is(Is, Tail); translate_is([#b_set{anno=Anno0,op=Op,dst=Dst,args=Args}=I|Is], Tail) -> - Anno = case beam_ssa:clobbers_xregs(I) of - true -> Anno0#{clobbers=>true}; - false -> Anno0 + Anno1 = case beam_ssa:clobbers_xregs(I) of + true -> Anno0#{clobbers=>true}; + false -> Anno0 end, + Anno = prune_arg_types(Anno1, Args), [#cg_set{anno=Anno,op=Op,dst=Dst,args=Args}|translate_is(Is, Tail)]; translate_is([], Tail) -> Tail. +prune_arg_types(#{arg_types := ArgTypes0}=Anno, Args) -> + ArgTypes = prune_arg_types_1(Args, 0, ArgTypes0), + if + ArgTypes =:= #{} -> + maps:remove(arg_types, Anno); + true -> + Anno#{arg_types := ArgTypes} + end; +prune_arg_types(Anno, _Args) -> Anno. + +prune_arg_types_1([#b_var{}|As], N, ArgTypes) -> + prune_arg_types_1(As, N + 1, ArgTypes); +prune_arg_types_1([_|As], N, ArgTypes) -> + prune_arg_types_1(As, N + 1, maps:remove(N, ArgTypes)); +prune_arg_types_1([], _N, ArgTypes) -> ArgTypes. + translate_terminator(#b_ret{anno=Anno,arg=Arg}) -> Dealloc = case Anno of #{deallocate:=N} -> N; @@ -2094,6 +2127,20 @@ get_register(V, Regs) -> false -> maps:get(V, Regs) end. +typed_args(As, Anno, St) -> + typed_args_1(As, Anno, St, 0). + +typed_args_1([Arg | Args], Anno, St, Index) -> + case Anno of + #{ arg_types := #{ Index := Type } } -> + Typed = #tr{r=beam_arg(Arg, St),t=Type}, + [Typed | typed_args_1(Args, Anno, St, Index + 1)]; + #{} -> + [beam_arg(Arg, St) | typed_args_1(Args, Anno, St, Index + 1)] + end; +typed_args_1([], _Anno, _St, _Index) -> + []. + beam_args(As, St) -> [beam_arg(A, St) || A <- As]. @@ -2163,8 +2210,6 @@ local_func_label(Key, #cg{functable=Map}=St0) -> %% is_gc_bif(Name, Args) -> true|false. %% Determines whether the BIF Name/Arity might do a GC. --spec is_gc_bif(atom(), [beam_ssa:value()]) -> boolean(). - is_gc_bif(hd, [_]) -> false; is_gc_bif(tl, [_]) -> false; is_gc_bif(self, []) -> false; |