summaryrefslogtreecommitdiff
path: root/lib/compiler/src/beam_ssa_codegen.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src/beam_ssa_codegen.erl')
-rw-r--r--lib/compiler/src/beam_ssa_codegen.erl323
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;