aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/src/beam_ssa.erl54
-rw-r--r--lib/compiler/src/beam_ssa_dead.erl33
-rw-r--r--lib/compiler/src/beam_ssa_funs.erl8
-rw-r--r--lib/compiler/src/beam_ssa_opt.erl93
-rw-r--r--lib/compiler/src/beam_ssa_pre_codegen.erl97
-rw-r--r--lib/compiler/src/beam_ssa_recv.erl8
-rw-r--r--lib/compiler/src/beam_ssa_type.erl389
-rw-r--r--lib/compiler/src/beam_validator.erl335
-rw-r--r--lib/compiler/src/sys_core_fold_lists.erl101
-rw-r--r--lib/compiler/test/compile_SUITE.erl164
-rw-r--r--lib/compiler/test/inline_SUITE.erl26
-rw-r--r--lib/compiler/test/inline_SUITE_data/barnes2.erl2
-rw-r--r--lib/compiler/test/match_SUITE.erl15
-rw-r--r--lib/compiler/test/test_lib.erl10
14 files changed, 784 insertions, 551 deletions
diff --git a/lib/compiler/src/beam_ssa.erl b/lib/compiler/src/beam_ssa.erl
index b491e340b7..9c29c98064 100644
--- a/lib/compiler/src/beam_ssa.erl
+++ b/lib/compiler/src/beam_ssa.erl
@@ -142,7 +142,7 @@ add_anno(Key, Val, #b_switch{anno=Anno}=Bl) ->
-spec get_anno(atom(), construct()) -> any().
get_anno(Key, Construct) ->
- maps:get(Key, get_anno(Construct)).
+ map_get(Key, get_anno(Construct)).
-spec get_anno(atom(), construct(),any()) -> any().
@@ -303,7 +303,7 @@ normalize(#b_ret{}=Ret) ->
-spec successors(label(), block_map()) -> [label()].
successors(L, Blocks) ->
- successors(maps:get(L, Blocks)).
+ successors(map_get(L, Blocks)).
-spec def(Ls, Blocks) -> Def when
Ls :: [label()],
@@ -312,7 +312,7 @@ successors(L, Blocks) ->
def(Ls, Blocks) ->
Top = rpo(Ls, Blocks),
- Blks = [maps:get(L, Blocks) || L <- Top],
+ Blks = [map_get(L, Blocks) || L <- Top],
def_1(Blks, []).
-spec def_used(Ls, Blocks) -> {Def,Used} when
@@ -323,9 +323,9 @@ def(Ls, Blocks) ->
def_used(Ls, Blocks) ->
Top = rpo(Ls, Blocks),
- Blks = [maps:get(L, Blocks) || L <- Top],
- Preds = gb_sets:from_list(Top),
- def_used_1(Blks, Preds, [], gb_sets:empty()).
+ Blks = [map_get(L, Blocks) || L <- Top],
+ Preds = cerl_sets:from_list(Top),
+ def_used_1(Blks, Preds, [], []).
-spec dominators(Blocks) -> Result when
Blocks :: block_map(),
@@ -334,7 +334,7 @@ def_used(Ls, Blocks) ->
dominators(Blocks) ->
Preds = predecessors(Blocks),
Top0 = rpo(Blocks),
- Top = [{L,maps:get(L, Preds)} || L <- Top0],
+ Top = [{L,map_get(L, Preds)} || L <- Top0],
%% The flow graph for an Erlang function is reducible, and
%% therefore one traversal in reverse postorder is sufficient.
@@ -365,9 +365,9 @@ mapfold_blocks_rpo(Fun, From, Acc, Blocks) ->
end, {Blocks, Acc}, Successors).
mapfold_blocks_rpo_1(Fun, Lbl, {Blocks0, Acc0}) ->
- Block0 = maps:get(Lbl, Blocks0),
+ Block0 = map_get(Lbl, Blocks0),
{Block, Acc} = Fun(Lbl, Block0, Acc0),
- Blocks = maps:put(Lbl, Block, Blocks0),
+ Blocks = Blocks0#{Lbl:=Block},
{Blocks, Acc}.
-spec mapfold_instrs_rpo(Fun, From, Acc0, Blocks0) -> {Blocks,Acc} when
@@ -581,7 +581,7 @@ used(_) -> [].
-spec definitions(Blocks :: block_map()) -> definition_map().
definitions(Blocks) ->
fold_instrs_rpo(fun(#b_set{ dst = Var }=I, Acc) ->
- maps:put(Var, I, Acc);
+ Acc#{Var => I};
(_Terminator, Acc) ->
Acc
end, [0], #{}, Blocks).
@@ -626,10 +626,10 @@ is_commutative(_) -> false.
def_used_1([#b_blk{is=Is,last=Last}|Bs], Preds, Def0, Used0) ->
{Def,Used1} = def_used_is(Is, Preds, Def0, Used0),
- Used = gb_sets:union(gb_sets:from_list(used(Last)), Used1),
+ Used = ordsets:union(used(Last), Used1),
def_used_1(Bs, Preds, Def, Used);
def_used_1([], _Preds, Def, Used) ->
- {ordsets:from_list(Def),gb_sets:to_list(Used)}.
+ {ordsets:from_list(Def),Used}.
def_used_is([#b_set{op=phi,dst=Dst,args=Args}|Is],
Preds, Def0, Used0) ->
@@ -637,12 +637,12 @@ def_used_is([#b_set{op=phi,dst=Dst,args=Args}|Is],
%% We must be careful to only include variables that will
%% be used when arriving from one of the predecessor blocks
%% in Preds.
- Used1 = [V || {#b_var{}=V,L} <- Args, gb_sets:is_member(L, Preds)],
- Used = gb_sets:union(gb_sets:from_list(Used1), Used0),
+ Used1 = [V || {#b_var{}=V,L} <- Args, cerl_sets:is_element(L, Preds)],
+ Used = ordsets:union(ordsets:from_list(Used1), Used0),
def_used_is(Is, Preds, Def, Used);
def_used_is([#b_set{dst=Dst}=I|Is], Preds, Def0, Used0) ->
Def = [Dst|Def0],
- Used = gb_sets:union(gb_sets:from_list(used(I)), Used0),
+ Used = ordsets:union(used(I), Used0),
def_used_is(Is, Preds, Def, Used);
def_used_is([], _Preds, Def, Used) ->
{Def,Used}.
@@ -661,40 +661,40 @@ iter_dominators([{0,[]}|Ls], _Doms) ->
Dom = [0],
iter_dominators(Ls, #{0=>Dom});
iter_dominators([{L,Preds}|Ls], Doms) ->
- DomPreds = [maps:get(P, Doms) || P <- Preds, maps:is_key(P, Doms)],
+ DomPreds = [map_get(P, Doms) || P <- Preds, is_map_key(P, Doms)],
Dom = ordsets:add_element(L, ordsets:intersection(DomPreds)),
iter_dominators(Ls, Doms#{L=>Dom});
iter_dominators([], Doms) -> Doms.
fold_rpo_1([L|Ls], Fun, Blocks, Acc0) ->
- Block = maps:get(L, Blocks),
+ Block = map_get(L, Blocks),
Acc = Fun(L, Block, Acc0),
fold_rpo_1(Ls, Fun, Blocks, Acc);
fold_rpo_1([], _, _, Acc) -> Acc.
fold_instrs_rpo_1([L|Ls], Fun, Blocks, Acc0) ->
- #b_blk{is=Is,last=Last} = maps:get(L, Blocks),
+ #b_blk{is=Is,last=Last} = map_get(L, Blocks),
Acc1 = foldl(Fun, Acc0, Is),
Acc = Fun(Last, Acc1),
fold_instrs_rpo_1(Ls, Fun, Blocks, Acc);
fold_instrs_rpo_1([], _, _, Acc) -> Acc.
mapfold_instrs_rpo_1([L|Ls], Fun, Blocks0, Acc0) ->
- #b_blk{is=Is0,last=Last0} = Block0 = maps:get(L, Blocks0),
+ #b_blk{is=Is0,last=Last0} = Block0 = map_get(L, Blocks0),
{Is,Acc1} = mapfoldl(Fun, Acc0, Is0),
{Last,Acc} = Fun(Last0, Acc1),
Block = Block0#b_blk{is=Is,last=Last},
- Blocks = maps:put(L, Block, Blocks0),
+ Blocks = Blocks0#{L:=Block},
mapfold_instrs_rpo_1(Ls, Fun, Blocks, Acc);
mapfold_instrs_rpo_1([], _, Blocks, Acc) ->
{Blocks,Acc}.
flatmapfold_instrs_rpo_1([L|Ls], Fun, Blocks0, Acc0) ->
- #b_blk{is=Is0,last=Last0} = Block0 = maps:get(L, Blocks0),
+ #b_blk{is=Is0,last=Last0} = Block0 = map_get(L, Blocks0),
{Is,Acc1} = flatmapfoldl(Fun, Acc0, Is0),
{[Last],Acc} = Fun(Last0, Acc1),
Block = Block0#b_blk{is=Is,last=Last},
- Blocks = maps:put(L, Block, Blocks0),
+ Blocks = Blocks0#{L:=Block},
flatmapfold_instrs_rpo_1(Ls, Fun, Blocks, Acc);
flatmapfold_instrs_rpo_1([], _, Blocks, Acc) ->
{Blocks,Acc}.
@@ -705,7 +705,7 @@ linearize_1([L|Ls], Blocks, Seen0, Acc0) ->
linearize_1(Ls, Blocks, Seen0, Acc0);
false ->
Seen1 = cerl_sets:add_element(L, Seen0),
- Block = maps:get(L, Blocks),
+ Block = map_get(L, Blocks),
Successors = successors(Block),
{Acc,Seen} = linearize_1(Successors, Blocks, Seen1, Acc0),
linearize_1(Ls, Blocks, Seen, [{L,Block}|Acc])
@@ -745,7 +745,7 @@ rpo_1([L|Ls], Blocks, Seen0, Acc0) ->
true ->
rpo_1(Ls, Blocks, Seen0, Acc0);
false ->
- Block = maps:get(L, Blocks),
+ Block = map_get(L, Blocks),
Seen1 = cerl_sets:add_element(L, Seen0),
Successors = successors(Block),
{Acc,Seen} = rpo_1(Successors, Blocks, Seen1, Acc0),
@@ -775,11 +775,11 @@ rename_phi_vars([{Var,L}|As], Preds, Ren) ->
rename_phi_vars([], _, _) -> [].
map_instrs_1([L|Ls], Fun, Blocks0) ->
- #b_blk{is=Is0,last=Last0} = Blk0 = maps:get(L, Blocks0),
+ #b_blk{is=Is0,last=Last0} = Blk0 = map_get(L, Blocks0),
Is = [Fun(I) || I <- Is0],
Last = Fun(Last0),
Blk = Blk0#b_blk{is=Is,last=Last},
- Blocks = maps:put(L, Blk, Blocks0),
+ Blocks = Blocks0#{L:=Blk},
map_instrs_1(Ls, Fun, Blocks);
map_instrs_1([], _, Blocks) -> Blocks.
@@ -790,7 +790,7 @@ flatmapfoldl(F, Accu0, [Hd|Tail]) ->
flatmapfoldl(_, Accu, []) -> {[],Accu}.
split_blocks_1([L|Ls], P, Blocks0, Count0) ->
- #b_blk{is=Is0} = Blk = maps:get(L, Blocks0),
+ #b_blk{is=Is0} = Blk = map_get(L, Blocks0),
case split_blocks_is(Is0, P, []) of
{yes,Bef,Aft} ->
NewLbl = Count0,
diff --git a/lib/compiler/src/beam_ssa_dead.erl b/lib/compiler/src/beam_ssa_dead.erl
index 09b29025c6..2cca9ebadf 100644
--- a/lib/compiler/src/beam_ssa_dead.erl
+++ b/lib/compiler/src/beam_ssa_dead.erl
@@ -375,7 +375,7 @@ is_forbidden(L, St) ->
%% any instruction with potential side effects.
eval_is([#b_set{op=phi,dst=Dst,args=Args}|Is], Bs0, St) ->
- From = maps:get(from, Bs0),
+ From = map_get(from, Bs0),
[Val] = [Val || {Val,Pred} <- Args, Pred =:= From],
Bs = bind_var(Dst, Val, Bs0),
eval_is(Is, Bs, St);
@@ -826,7 +826,7 @@ combine_eqs_1([L|Ls], #st{bs=Blocks0}=St0) ->
%% Everything OK! Combine the lists.
Sw0 = #b_switch{arg=Arg,fail=Fail,list=List},
Sw = beam_ssa:normalize(Sw0),
- Blk0 = maps:get(L, Blocks0),
+ Blk0 = map_get(L, Blocks0),
Blk = Blk0#b_blk{last=Sw},
Blocks = Blocks0#{L:=Blk},
St = St0#st{bs=Blocks},
@@ -850,8 +850,8 @@ combine_eqs_1([], St) -> St.
comb_get_sw(L, Blocks) ->
comb_get_sw(L, true, Blocks).
-comb_get_sw(L, Safe0, #st{bs=Blocks,skippable=Skippable}=St) ->
- #b_blk{is=Is,last=Last} = maps:get(L, Blocks),
+comb_get_sw(L, Safe0, #st{bs=Blocks,skippable=Skippable}) ->
+ #b_blk{is=Is,last=Last} = map_get(L, Blocks),
Safe1 = Safe0 andalso is_map_key(L, Skippable),
case Last of
#b_ret{} ->
@@ -865,8 +865,8 @@ comb_get_sw(L, Safe0, #st{bs=Blocks,skippable=Skippable}=St) ->
{#b_set{},_} ->
none
end;
- #b_br{bool=#b_literal{val=true},succ=Succ} ->
- comb_get_sw(Succ, Safe1, St);
+ #b_br{} ->
+ none;
#b_switch{arg=#b_var{}=Arg,fail=Fail,list=List} ->
{none,Safe} = comb_is(Is, none, Safe1),
{Safe,Arg,L,Fail,List}
@@ -946,7 +946,7 @@ used_vars([{L,#b_blk{is=Is}=Blk}|Bs], UsedVars0, Skip0) ->
%% shortcut_opt/1.
Successors = beam_ssa:successors(Blk),
- Used0 = used_vars_succ(Successors, L, UsedVars0),
+ Used0 = used_vars_succ(Successors, L, UsedVars0, []),
Used = used_vars_blk(Blk, Used0),
UsedVars = used_vars_phis(Is, L, Used, UsedVars0),
@@ -969,19 +969,22 @@ used_vars([{L,#b_blk{is=Is}=Blk}|Bs], UsedVars0, Skip0) ->
used_vars([], UsedVars, Skip) ->
{UsedVars,Skip}.
-used_vars_succ([S|Ss], L, UsedVars) ->
- Live0 = used_vars_succ(Ss, L, UsedVars),
+used_vars_succ([S|Ss], L, LiveMap, Live0) ->
Key = {S,L},
- case UsedVars of
+ case LiveMap of
#{Key:=Live} ->
- ordsets:union(Live, Live0);
+ %% The successor has a phi node, and the value for
+ %% this block in the phi node is a variable.
+ used_vars_succ(Ss, L, LiveMap, ordsets:union(Live, Live0));
#{S:=Live} ->
- ordsets:union(Live, Live0);
+ %% No phi node in the successor, or the value for
+ %% this block in the phi node is a literal.
+ used_vars_succ(Ss, L, LiveMap, ordsets:union(Live, Live0));
#{} ->
- Live0
+ %% A peek_message block which has not been processed yet.
+ used_vars_succ(Ss, L, LiveMap, Live0)
end;
-used_vars_succ([], _, _) ->
- ordsets:new().
+used_vars_succ([], _, _, Acc) -> Acc.
used_vars_phis(Is, L, Live0, UsedVars0) ->
UsedVars = UsedVars0#{L=>Live0},
diff --git a/lib/compiler/src/beam_ssa_funs.erl b/lib/compiler/src/beam_ssa_funs.erl
index 38df50fd74..e77c00fa89 100644
--- a/lib/compiler/src/beam_ssa_funs.erl
+++ b/lib/compiler/src/beam_ssa_funs.erl
@@ -47,14 +47,14 @@ module(#b_module{body=Fs0}=Module, _Opts) ->
%% the same arguments in the same order, we can shave off a call by short-
%% circuiting it.
find_trampolines(#b_function{args=Args,bs=Blocks}=F, Trampolines) ->
- case maps:get(0, Blocks) of
+ case map_get(0, Blocks) of
#b_blk{is=[#b_set{op=call,
args=[#b_local{}=Actual | Args],
dst=Dst}],
last=#b_ret{arg=Dst}} ->
{_, Name, Arity} = beam_ssa:get_anno(func_info, F),
Trampoline = #b_local{name=#b_literal{val=Name},arity=Arity},
- maps:put(Trampoline, Actual, Trampolines);
+ Trampolines#{Trampoline => Actual};
_ ->
Trampolines
end.
@@ -80,7 +80,7 @@ lfo_analyze_is([#b_set{op=make_fun,
lfo_analyze_is([#b_set{op=call,
args=[Fun | CallArgs]} | Is],
LFuns) when is_map_key(Fun, LFuns) ->
- #b_set{args=[#b_local{arity=Arity} | FreeVars]} = maps:get(Fun, LFuns),
+ #b_set{args=[#b_local{arity=Arity} | FreeVars]} = map_get(Fun, LFuns),
case length(CallArgs) + length(FreeVars) of
Arity ->
lfo_analyze_is(Is, maps:without(CallArgs, LFuns));
@@ -133,7 +133,7 @@ lfo_optimize_1([], _LFuns, _Trampolines) ->
lfo_optimize_is([#b_set{op=call,
args=[Fun | CallArgs]}=Call0 | Is],
LFuns, Trampolines) when is_map_key(Fun, LFuns) ->
- #b_set{args=[Local | FreeVars]} = maps:get(Fun, LFuns),
+ #b_set{args=[Local | FreeVars]} = map_get(Fun, LFuns),
Args = [lfo_short_circuit(Local, Trampolines) | CallArgs ++ FreeVars],
Call = beam_ssa:add_anno(local_fun_opt, Fun, Call0#b_set{args=Args}),
[Call | lfo_optimize_is(Is, LFuns, Trampolines)];
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index e528bb4dfb..f177f6d7fe 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -84,7 +84,7 @@ phase([FuncId | Ids], Ps, StMap, FuncDb0) ->
phase(Ids, Ps, StMap#{ FuncId => St }, FuncDb)
catch
Class:Error:Stack ->
- #b_local{name=Name,arity=Arity} = FuncId,
+ #b_local{name=#b_literal{val=Name},arity=Arity} = FuncId,
io:fwrite("Function: ~w/~w\n", [Name,Arity]),
erlang:raise(Class, Error, Stack)
end;
@@ -362,7 +362,7 @@ ssa_opt_coalesce_phis({#st{ssa=Blocks0}=St, FuncDb}) ->
{St#st{ssa=Blocks}, FuncDb}.
c_phis_1([L|Ls], Blocks0) ->
- case maps:get(L, Blocks0) of
+ case map_get(L, Blocks0) of
#b_blk{is=[#b_set{op=phi}|_]}=Blk ->
Blocks = c_phis_2(L, Blk, Blocks0),
c_phis_1(Ls, Blocks);
@@ -401,7 +401,7 @@ c_phis_args_1([{Var,Pred}|As], Blocks) ->
c_phis_args_1([], _Blocks) -> none.
c_get_pred_vars(Var, Pred, Blocks) ->
- case maps:get(Pred, Blocks) of
+ case map_get(Pred, Blocks) of
#b_blk{is=[#b_set{op=phi,dst=Var,args=Args}]} ->
{Var,Pred,Args};
#b_blk{} ->
@@ -422,7 +422,7 @@ c_rewrite_phi([A|As], Info) ->
c_rewrite_phi([], _Info) -> [].
c_fix_branches([{_,Pred}|As], L, Blocks0) ->
- #b_blk{last=Last0} = Blk0 = maps:get(Pred, Blocks0),
+ #b_blk{last=Last0} = Blk0 = map_get(Pred, Blocks0),
#b_br{bool=#b_literal{val=true}} = Last0, %Assertion.
Last = Last0#b_br{bool=#b_literal{val=true},succ=L,fail=L},
Blk = Blk0#b_blk{last=Last},
@@ -692,7 +692,7 @@ record_opt_is([], _Last, _Blocks) -> [].
is_tagged_tuple(#b_var{}=Tuple, Bool,
#b_br{bool=Bool,succ=Succ,fail=Fail},
Blocks) ->
- SuccBlk = maps:get(Succ, Blocks),
+ SuccBlk = map_get(Succ, Blocks),
is_tagged_tuple_1(SuccBlk, Tuple, Fail, Blocks);
is_tagged_tuple(_, _, _, _) -> no.
@@ -706,7 +706,7 @@ is_tagged_tuple_1(#b_blk{is=Is,last=Last}, Tuple, Fail, Blocks) ->
when is_integer(ArityVal) ->
case Last of
#b_br{bool=Bool,succ=Succ,fail=Fail} ->
- SuccBlk = maps:get(Succ, Blocks),
+ SuccBlk = map_get(Succ, Blocks),
case is_tagged_tuple_2(SuccBlk, Tuple, Fail) of
no ->
no;
@@ -757,7 +757,7 @@ ssa_opt_cse({#st{ssa=Linear}=St, FuncDb}) ->
{St#st{ssa=cse(Linear, #{}, M)}, FuncDb}.
cse([{L,#b_blk{is=Is0,last=Last0}=Blk}|Bs], Sub0, M0) ->
- Es0 = maps:get(L, M0),
+ Es0 = map_get(L, M0),
{Is1,Es,Sub} = cse_is(Is0, Es0, Sub0, []),
Last = sub(Last0, Sub),
M = cse_successors(Is1, Blk, Es, M0),
@@ -1002,7 +1002,7 @@ float_conv([{L,#b_blk{is=Is0}=Blk0}|Bs0], Fail, Count0) ->
float_maybe_flush(Blk0, #fs{s=cleared,fail=Fail,bs=Blocks}=Fs0, Count0) ->
#b_blk{last=#b_br{bool=#b_var{},succ=Succ}=Br} = Blk0,
- #b_blk{is=Is} = maps:get(Succ, Blocks),
+ #b_blk{is=Is} = map_get(Succ, Blocks),
case Is of
[#b_set{anno=#{float_op:=_}}|_] ->
%% The next operation is also a floating point operation.
@@ -1149,25 +1149,28 @@ ssa_opt_live({#st{ssa=Linear0}=St, FuncDb}) ->
live_opt([{L,Blk0}|Bs], LiveMap0, Blocks) ->
Blk1 = beam_ssa_share:block(Blk0, Blocks),
Successors = beam_ssa:successors(Blk1),
- Live0 = live_opt_succ(Successors, L, LiveMap0),
+ Live0 = live_opt_succ(Successors, L, LiveMap0, gb_sets:empty()),
{Blk,Live} = live_opt_blk(Blk1, Live0),
LiveMap = live_opt_phis(Blk#b_blk.is, L, Live, LiveMap0),
live_opt(Bs, LiveMap, Blocks#{L:=Blk});
live_opt([], _, Acc) -> Acc.
-live_opt_succ([S|Ss], L, LiveMap) ->
- Live0 = live_opt_succ(Ss, L, LiveMap),
+live_opt_succ([S|Ss], L, LiveMap, Live0) ->
Key = {S,L},
case LiveMap of
#{Key:=Live} ->
- gb_sets:union(Live, Live0);
+ %% The successor has a phi node, and the value for
+ %% this block in the phi node is a variable.
+ live_opt_succ(Ss, L, LiveMap, gb_sets:union(Live, Live0));
#{S:=Live} ->
- gb_sets:union(Live, Live0);
+ %% No phi node in the successor, or the value for
+ %% this block in the phi node is a literal.
+ live_opt_succ(Ss, L, LiveMap, gb_sets:union(Live, Live0));
#{} ->
- Live0
+ %% A peek_message block which has not been processed yet.
+ live_opt_succ(Ss, L, LiveMap, Live0)
end;
-live_opt_succ([], _, _) ->
- gb_sets:empty().
+live_opt_succ([], _, _, Acc) -> Acc.
live_opt_phis(Is, L, Live0, LiveMap0) ->
LiveMap = LiveMap0#{L=>Live0},
@@ -1218,7 +1221,7 @@ live_opt_is([#b_set{op=succeeded,dst=SuccDst=SuccDstVar,
case gb_sets:is_member(SuccDst, Live0) of
true ->
Live1 = gb_sets:add(Dst, Live0),
- Live = gb_sets:delete_any(SuccDst, Live1),
+ Live = gb_sets:delete(SuccDst, Live1),
live_opt_is([I|Is], Live, [SuccI|Acc]);
false ->
live_opt_is([I|Is], Live0, Acc)
@@ -1229,7 +1232,7 @@ live_opt_is([#b_set{dst=Dst}=I|Is], Live0, Acc) ->
case gb_sets:is_member(Dst, Live0) of
true ->
Live1 = gb_sets:union(Live0, gb_sets:from_ordset(beam_ssa:used(I))),
- Live = gb_sets:delete_any(Dst, Live1),
+ Live = gb_sets:delete(Dst, Live1),
live_opt_is(Is, Live, [I|Acc]);
false ->
case beam_ssa:no_side_effect(I) of
@@ -1373,7 +1376,7 @@ bsm_positions([{L,#b_blk{is=Is,last=Last}}|Bs], PosMap0) ->
case {Is,Last} of
{[#b_set{op=bs_test_tail,dst=Bool,args=[Ctx,#b_literal{val=Bits0}]}],
#b_br{bool=Bool,fail=Fail}} ->
- Bits = Bits0 + maps:get(Ctx, PosMap0),
+ Bits = Bits0 + map_get(Ctx, PosMap0),
bsm_positions(Bs, PosMap#{L=>{Bits,Fail}});
{_,_} ->
bsm_positions(Bs, PosMap)
@@ -1465,7 +1468,7 @@ bsm_units_skip_1([#b_set{op=bs_match,
Block0, Units) ->
[#b_set{op=succeeded,dst=Bool,args=[New]}] = Test, %Assertion.
#b_br{bool=Bool} = Last0 = Block0#b_blk.last, %Assertion.
- CtxUnit = maps:get(Ctx, Units),
+ CtxUnit = map_get(Ctx, Units),
if
CtxUnit rem OpUnit =:= 0 ->
Is = takewhile(fun(I) -> I =/= Skip end, Block0#b_blk.is),
@@ -1477,7 +1480,7 @@ bsm_units_skip_1([#b_set{op=bs_match,
end;
bsm_units_skip_1([#b_set{op=bs_match,dst=New,args=Args}|_], Block, Units) ->
[_,Ctx|_] = Args,
- CtxUnit = maps:get(Ctx, Units),
+ CtxUnit = map_get(Ctx, Units),
OpUnit = bsm_op_unit(Args),
{Block, Units#{ New => gcd(OpUnit, CtxUnit) }};
bsm_units_skip_1([_I | Is], Block, Units) ->
@@ -1505,23 +1508,23 @@ bsm_op_unit(_) ->
%% may differ between them, so we can only keep the information that is common
%% to all paths.
bsm_units_join(Lbl, MapA, UnitMaps0) when is_map_key(Lbl, UnitMaps0) ->
- MapB = maps:get(Lbl, UnitMaps0),
+ MapB = map_get(Lbl, UnitMaps0),
Merged = if
map_size(MapB) =< map_size(MapA) ->
bsm_units_join_1(maps:keys(MapB), MapA, MapB);
map_size(MapB) > map_size(MapA) ->
bsm_units_join_1(maps:keys(MapA), MapB, MapA)
end,
- maps:put(Lbl, Merged, UnitMaps0);
+ UnitMaps0#{Lbl := Merged};
bsm_units_join(Lbl, MapA, UnitMaps0) when MapA =/= #{} ->
- maps:put(Lbl, MapA, UnitMaps0);
+ UnitMaps0#{Lbl => MapA};
bsm_units_join(_Lbl, _MapA, UnitMaps0) ->
UnitMaps0.
bsm_units_join_1([Key | Keys], Left, Right) when is_map_key(Key, Left) ->
- UnitA = maps:get(Key, Left),
- UnitB = maps:get(Key, Right),
- bsm_units_join_1(Keys, Left, maps:put(Key, gcd(UnitA, UnitB), Right));
+ UnitA = map_get(Key, Left),
+ UnitB = map_get(Key, Right),
+ bsm_units_join_1(Keys, Left, Right#{Key := gcd(UnitA, UnitB)});
bsm_units_join_1([Key | Keys], Left, Right) ->
bsm_units_join_1(Keys, Left, maps:remove(Key, Right));
bsm_units_join_1([], _MapA, Right) ->
@@ -1941,7 +1944,7 @@ merge_blocks_1([L|Ls], Preds0, Blocks0) ->
Is = Is0 ++ Is1,
Blk = Blk1#b_blk{is=Is},
Blocks1 = maps:remove(L, Blocks0),
- Blocks2 = maps:put(P, Blk, Blocks1),
+ Blocks2 = Blocks1#{P:=Blk},
Successors = beam_ssa:successors(Blk),
Blocks = beam_ssa:update_phi_labels(Successors, L, P, Blocks2),
Preds = merge_update_preds(Successors, L, P, Preds0),
@@ -1955,8 +1958,8 @@ merge_blocks_1([L|Ls], Preds0, Blocks0) ->
merge_blocks_1([], _Preds, Blocks) -> Blocks.
merge_update_preds([L|Ls], From, To, Preds0) ->
- Ps = [rename_label(P, From, To) || P <- maps:get(L, Preds0)],
- Preds = maps:put(L, Ps, Preds0),
+ Ps = [rename_label(P, From, To) || P <- map_get(L, Preds0)],
+ Preds = Preds0#{L:=Ps},
merge_update_preds(Ls, From, To, Preds);
merge_update_preds([], _, _, Preds) -> Preds.
@@ -2003,8 +2006,16 @@ ssa_opt_sink({#st{ssa=Blocks0}=St, FuncDb}) ->
%% Create a map with all variables that define get_tuple_element
%% instructions. The variable name map to the block it is defined in.
- Defs = maps:from_list(def_blocks(Linear)),
+ case def_blocks(Linear) of
+ [] ->
+ %% No get_tuple_element instructions, so there is nothing to do.
+ {St, FuncDb};
+ [_|_]=Defs0 ->
+ Defs = maps:from_list(Defs0),
+ {do_ssa_opt_sink(Linear, Defs, St), FuncDb}
+ end.
+do_ssa_opt_sink(Linear, Defs, #st{ssa=Blocks0}=St) ->
%% Now find all the blocks that use variables defined by get_tuple_element
%% instructions.
Used = used_blocks(Linear, Defs, []),
@@ -2036,10 +2047,10 @@ ssa_opt_sink({#st{ssa=Blocks0}=St, FuncDb}) ->
%% Now move all suitable get_tuple_element instructions to their
%% new blocks.
Blocks = foldl(fun({V,To}, A) ->
- From = maps:get(V, Defs),
+ From = map_get(V, Defs),
move_defs(V, From, To, A)
end, Blocks0, DefLoc),
- {St#st{ssa=Blocks}, FuncDb}.
+ St#st{ssa=Blocks}.
def_blocks([{L,#b_blk{is=Is}}|Bs]) ->
def_blocks_is(Is, L, def_blocks(Bs));
@@ -2106,11 +2117,11 @@ unsuitable_loop(L, Blocks, Predecessors) ->
unsuitable_loop(L, Blocks, Predecessors, []).
unsuitable_loop(L, Blocks, Predecessors, Acc) ->
- Ps = maps:get(L, Predecessors),
+ Ps = map_get(L, Predecessors),
unsuitable_loop_1(Ps, Blocks, Predecessors, Acc).
unsuitable_loop_1([P|Ps], Blocks, Predecessors, Acc0) ->
- case maps:get(P, Blocks) of
+ case map_get(P, Blocks) of
#b_blk{is=[#b_set{op=peek_message}|_]} ->
unsuitable_loop_1(Ps, Blocks, Predecessors, Acc0);
#b_blk{} ->
@@ -2134,7 +2145,7 @@ unsuitable_loop_1([], _, _, Acc) -> Acc.
%% variable will not be included in the result list.
new_def_locations([{V,UsedIn}|Vs], Defs, Dom) ->
- DefIn = maps:get(V, Defs),
+ DefIn = map_get(V, Defs),
case common_dom(UsedIn, DefIn, Dom) of
[] ->
new_def_locations(Vs, Defs, Dom);
@@ -2145,27 +2156,27 @@ new_def_locations([{V,UsedIn}|Vs], Defs, Dom) ->
new_def_locations([], _, _) -> [].
common_dom([L|Ls], DefIn, Dom) ->
- DomBy0 = maps:get(L, Dom),
- DomBy = ordsets:subtract(DomBy0, maps:get(DefIn, Dom)),
+ DomBy0 = map_get(L, Dom),
+ DomBy = ordsets:subtract(DomBy0, map_get(DefIn, Dom)),
common_dom_1(Ls, Dom, DomBy).
common_dom_1(_, _, []) ->
[];
common_dom_1([L|Ls], Dom, [_|_]=DomBy0) ->
- DomBy1 = maps:get(L, Dom),
+ DomBy1 = map_get(L, Dom),
DomBy = ordsets:intersection(DomBy0, DomBy1),
common_dom_1(Ls, Dom, DomBy);
common_dom_1([], _, DomBy) -> DomBy.
most_dominated([L|Ls], Dom) ->
- most_dominated(Ls, L, maps:get(L, Dom), Dom).
+ most_dominated(Ls, L, map_get(L, Dom), Dom).
most_dominated([L|Ls], L0, DomBy, Dom) ->
case member(L, DomBy) of
true ->
most_dominated(Ls, L0, DomBy, Dom);
false ->
- most_dominated(Ls, L, maps:get(L, Dom), Dom)
+ most_dominated(Ls, L, map_get(L, Dom), Dom)
end;
most_dominated([], L, _, _) -> L.
diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl
index fde1118c29..ad57a45ef2 100644
--- a/lib/compiler/src/beam_ssa_pre_codegen.erl
+++ b/lib/compiler/src/beam_ssa_pre_codegen.erl
@@ -272,7 +272,7 @@ make_bs_getpos_map([], _, Count, Acc) ->
{maps:from_list(Acc),Count}.
get_savepoint({_,_}=Ps, SavePoints) ->
- Name = {'@ssa_bs_position', maps:get(Ps, SavePoints)},
+ Name = {'@ssa_bs_position', map_get(Ps, SavePoints)},
#b_var{name=Name}.
make_bs_pos_dict([{Ctx,Pts}|T], Count0, Acc0) ->
@@ -323,7 +323,7 @@ make_restore_map([], _, Count, Acc) ->
make_slot({Same,Same}, _Slots) ->
#b_literal{val=start};
make_slot({_,_}=Ps, Slots) ->
- #b_literal{val=maps:get(Ps, Slots)}.
+ #b_literal{val=map_get(Ps, Slots)}.
make_save_point_dict([{Ctx,Pts}|T], Acc0) ->
Acc = make_save_point_dict_1(Pts, Ctx, 0, Acc0),
@@ -684,7 +684,7 @@ sanitize(#st{ssa=Blocks0,cnt=Count0}=St) ->
St#st{ssa=Blocks,cnt=Count}.
sanitize([L|Ls], Count0, Blocks0, Values0) ->
- #b_blk{is=Is0} = Blk0 = maps:get(L, Blocks0),
+ #b_blk{is=Is0} = Blk0 = map_get(L, Blocks0),
case sanitize_is(Is0, Count0, Values0, false, []) of
no_change ->
sanitize(Ls, Count0, Blocks0, Values0);
@@ -817,7 +817,7 @@ sanitize_badarg(I) ->
I#b_set{op=call,args=[Func,#b_literal{val=badarg}]}.
remove_unreachable([L|Ls], Blocks, Reachable, Acc) ->
- #b_blk{is=Is0} = Blk0 = maps:get(L, Blocks),
+ #b_blk{is=Is0} = Blk0 = map_get(L, Blocks),
case split_phis(Is0) of
{[_|_]=Phis,Rest} ->
Is = [prune_phi(Phi, Reachable) || Phi <- Phis] ++ Rest,
@@ -882,7 +882,7 @@ place_frames(#st{ssa=Blocks}=St) ->
St#st{frames=Frames}.
place_frames_1([L|Ls], Blocks, Doms, Tried0, Frames0) ->
- Blk = maps:get(L, Blocks),
+ Blk = map_get(L, Blocks),
case need_frame(Blk) of
true ->
%% This block needs a frame. Try to place it here.
@@ -993,14 +993,14 @@ place_frame_here(L, Blocks, Doms, Frames) ->
%% Return all predecessors referenced in phi nodes.
phi_predecessors(L, Blocks) ->
- #b_blk{is=Is} = maps:get(L, Blocks),
+ #b_blk{is=Is} = map_get(L, Blocks),
[P || #b_set{op=phi,args=Args} <- Is, {_,P} <- Args].
%% is_dominated_by(Label, DominatedBy, Dominators) -> true|false.
%% Test whether block Label is dominated by block DominatedBy.
is_dominated_by(L, DomBy, Doms) ->
- DominatedBy = maps:get(L, Doms),
+ DominatedBy = map_get(L, Doms),
ordsets:is_element(DomBy, DominatedBy).
%% need_frame(#b_blk{}) -> true|false.
@@ -1137,7 +1137,7 @@ recv_fix_common([Msg0|T], Exit, Rm, Blocks0, Count0) ->
{MsgVars,Count} = new_vars(duplicate(N, '@recv'), Count1),
PhiArgs = fix_exit_phi_args(MsgVars, Rm, Exit, Blocks1),
Phi = #b_set{op=phi,dst=Msg,args=PhiArgs},
- ExitBlk0 = maps:get(Exit, Blocks1),
+ ExitBlk0 = map_get(Exit, Blocks1),
ExitBlk = ExitBlk0#b_blk{is=[Phi|ExitBlk0#b_blk.is]},
Blocks2 = Blocks1#{Exit:=ExitBlk},
Blocks = recv_fix_common_1(MsgVars, Rm, Msg0, Blocks2),
@@ -1148,7 +1148,7 @@ recv_fix_common([], _, _, Blocks, Count) ->
recv_fix_common_1([V|Vs], [Rm|Rms], Msg, Blocks0) ->
Ren = #{Msg=>V},
Blocks1 = beam_ssa:rename_vars(Ren, [Rm], Blocks0),
- #b_blk{is=Is0} = Blk0 = maps:get(Rm, Blocks1),
+ #b_blk{is=Is0} = Blk0 = map_get(Rm, Blocks1),
Copy = #b_set{op=copy,dst=V,args=[Msg]},
Is = insert_after_phis(Is0, [Copy]),
Blk = Blk0#b_blk{is=Is},
@@ -1183,11 +1183,11 @@ fix_receive([L|Ls], Defs, Blocks0, Count0) ->
{NewVars,Count} = new_vars([Base || #b_var{name=Base} <- Used], Count0),
Ren = zip(Used, NewVars),
Blocks1 = beam_ssa:rename_vars(Ren, [L], Blocks0),
- #b_blk{is=Is0} = Blk1 = maps:get(L, Blocks1),
+ #b_blk{is=Is0} = Blk1 = map_get(L, Blocks1),
CopyIs = [#b_set{op=copy,dst=New,args=[Old]} || {Old,New} <- Ren],
Is = insert_after_phis(Is0, CopyIs),
Blk = Blk1#b_blk{is=Is},
- Blocks = maps:put(L, Blk, Blocks1),
+ Blocks = Blocks1#{L:=Blk},
fix_receive(Ls, Defs, Blocks, Count);
fix_receive([], _Defs, Blocks, Count) ->
{Blocks,Count}.
@@ -1212,7 +1212,7 @@ find_loop_exit_1(_, _, Exit) -> Exit.
find_rm_blocks(L, Blocks) ->
Seen = gb_sets:singleton(L),
- Blk = maps:get(L, Blocks),
+ Blk = map_get(L, Blocks),
Succ = beam_ssa:successors(Blk),
find_rm_blocks_1(Succ, Seen, Blocks).
@@ -1222,7 +1222,7 @@ find_rm_blocks_1([L|Ls], Seen0, Blocks) ->
find_rm_blocks_1(Ls, Seen0, Blocks);
false ->
Seen = gb_sets:insert(L, Seen0),
- Blk = maps:get(L, Blocks),
+ Blk = map_get(L, Blocks),
case find_rm_act(Blk#b_blk.is) of
prune ->
%% Looping back. Don't look at any successors.
@@ -1284,16 +1284,16 @@ find_yregs_1([{F,Defs}|Fs], Blocks0) ->
Ls = beam_ssa:rpo([F], Blocks0),
Yregs0 = [],
Yregs = find_yregs_2(Ls, Blocks0, D0, Yregs0),
- Blk0 = maps:get(F, Blocks0),
+ Blk0 = map_get(F, Blocks0),
Blk = beam_ssa:add_anno(yregs, Yregs, Blk0),
Blocks = Blocks0#{F:=Blk},
find_yregs_1(Fs, Blocks);
find_yregs_1([], Blocks) -> Blocks.
find_yregs_2([L|Ls], Blocks0, D0, Yregs0) ->
- Blk0 = maps:get(L, Blocks0),
+ Blk0 = map_get(L, Blocks0),
#b_blk{is=Is,last=Last} = Blk0,
- Ys0 = maps:get(L, D0),
+ Ys0 = map_get(L, D0),
{Yregs1,Ys} = find_yregs_is(Is, Ys0, Yregs0),
Yregs = find_yregs_terminator(Last, Ys, Yregs1),
Successors = beam_ssa:successors(Blk0),
@@ -1320,7 +1320,7 @@ find_defs_1([L|Ls], Blocks, Frames, Seen0, Defs0, Acc0) ->
false ->
Seen1 = gb_sets:insert(L, Seen0),
{Acc,Seen} = find_defs_1(Ls, Blocks, Frames, Seen1, Defs0, Acc0),
- #b_blk{is=Is} = Blk = maps:get(L, Blocks),
+ #b_blk{is=Is} = Blk = map_get(L, Blocks),
Defs = find_defs_is(Is, Defs0),
Successors = beam_ssa:successors(Blk),
find_defs_1(Successors, Blocks, Frames, Seen, Defs, Acc)
@@ -1339,10 +1339,10 @@ find_update_succ([S|Ss], #dk{d=Defs0,k=Killed0}=DK0, D0) ->
Defs = ordsets:intersection(Defs0, Defs1),
Killed = ordsets:union(Killed0, Killed1),
DK = #dk{d=Defs,k=Killed},
- D = maps:put(S, DK, D0),
+ D = D0#{S:=DK},
find_update_succ(Ss, DK0, D);
#{} ->
- D = maps:put(S, DK0, D0),
+ D = D0#{S=>DK0},
find_update_succ(Ss, DK0, D)
end;
find_update_succ([], _, D) -> D.
@@ -1432,7 +1432,7 @@ copy_retval(#st{frames=Frames,ssa=Blocks0,cnt=Count0}=St) ->
St#st{ssa=Blocks,cnt=Count}.
copy_retval_1([F|Fs], Blocks0, Count0) ->
- #b_blk{anno=#{yregs:=Yregs0},is=Is} = maps:get(F, Blocks0),
+ #b_blk{anno=#{yregs:=Yregs0},is=Is} = map_get(F, Blocks0),
Yregs1 = gb_sets:from_list(Yregs0),
Yregs = collect_yregs(Is, Yregs1),
Ls = beam_ssa:rpo([F], Blocks0),
@@ -1451,7 +1451,7 @@ collect_yregs([#b_set{}|Is], Yregs) ->
collect_yregs([], Yregs) -> Yregs.
copy_retval_2([L|Ls], Yregs, Copy0, Blocks0, Count0) ->
- #b_blk{is=Is0,last=Last} = Blk = maps:get(L, Blocks0),
+ #b_blk{is=Is0,last=Last} = Blk = map_get(L, Blocks0),
RC = case {Last,Ls} of
{#b_br{succ=Succ,fail=?BADARG_BLOCK},[Succ|_]} ->
true;
@@ -1593,7 +1593,7 @@ opt_get_list(#st{ssa=Blocks,res=Res}=St) ->
St#st{ssa=opt_get_list_1(Ls, ResMap, Blocks)}.
opt_get_list_1([L|Ls], Res, Blocks0) ->
- #b_blk{is=Is0} = Blk = maps:get(L, Blocks0),
+ #b_blk{is=Is0} = Blk = map_get(L, Blocks0),
case opt_get_list_is(Is0, Res, [], false) of
no ->
opt_get_list_1(Ls, Res, Blocks0);
@@ -1647,12 +1647,12 @@ number_instructions(#st{ssa=Blocks0}=St) ->
St#st{ssa=number_is_1(Ls, 1, Blocks0)}.
number_is_1([L|Ls], N0, Blocks0) ->
- #b_blk{is=Is0,last=Last0} = Bl0 = maps:get(L, Blocks0),
+ #b_blk{is=Is0,last=Last0} = Bl0 = map_get(L, Blocks0),
{Is,N1} = number_is_2(Is0, N0, []),
Last = beam_ssa:add_anno(n, N1, Last0),
N = N1 + 2,
Bl = Bl0#b_blk{is=Is,last=Last},
- Blocks = maps:put(L, Bl, Blocks0),
+ Blocks = Blocks0#{L:=Bl},
number_is_1(Ls, N, Blocks);
number_is_1([], _, Blocks) -> Blocks.
@@ -1693,7 +1693,7 @@ live_interval_blk(L, Blocks, {Vars0,LiveMap0}) ->
Live1 = update_successors(Successors, L, Blocks, LiveMap0, Live0),
%% Add ranges for all variables that are live in the successors.
- #b_blk{is=Is,last=Last} = maps:get(L, Blocks),
+ #b_blk{is=Is,last=Last} = map_get(L, Blocks),
End = beam_ssa:get_anno(n, Last),
Use = [{V,{use,End+1}} || V <- Live1],
@@ -1762,7 +1762,7 @@ first_number([], Last) ->
update_successors([L|Ls], Pred, Blocks, LiveMap, Live0) ->
Live1 = ordsets:union(Live0, get_live(L, LiveMap)),
- #b_blk{is=Is} = maps:get(L, Blocks),
+ #b_blk{is=Is} = map_get(L, Blocks),
Live = update_live_phis(Is, Pred, Live1),
update_successors(Ls, Pred, Blocks, LiveMap, Live);
update_successors([], _, _, _, Live) -> Live.
@@ -1800,7 +1800,7 @@ reserve_yregs(#st{frames=Frames}=St0) ->
foldl(fun reserve_yregs_1/2, St0, Frames).
reserve_yregs_1(L, #st{ssa=Blocks0,cnt=Count0,res=Res0}=St) ->
- Blk = maps:get(L, Blocks0),
+ Blk = map_get(L, Blocks0),
Yregs = beam_ssa:get_anno(yregs, Blk),
{Def,Used} = beam_ssa:def_used([L], Blocks0),
UsedYregs = ordsets:intersection(Yregs, Used),
@@ -1826,7 +1826,7 @@ reserve_try_tags_1([L|Ls], Blocks, Seen0, ActMap0) ->
reserve_try_tags_1(Ls, Blocks, Seen0, ActMap0);
false ->
Seen1 = gb_sets:insert(L, Seen0),
- #b_blk{is=Is} = Blk = maps:get(L, Blocks),
+ #b_blk{is=Is} = Blk = map_get(L, Blocks),
Active0 = get_active(L, ActMap0),
Active = reserve_try_tags_is(Is, Active0),
Successors = beam_ssa:successors(Blk),
@@ -1869,11 +1869,11 @@ rename_vars(Vs, L, Blocks0, Count0) ->
{NewVars,Count} = new_vars([Base || #b_var{name=Base} <- Vs], Count0),
Ren = zip(Vs, NewVars),
Blocks1 = beam_ssa:rename_vars(Ren, [L], Blocks0),
- #b_blk{is=Is0} = Blk0 = maps:get(L, Blocks1),
+ #b_blk{is=Is0} = Blk0 = map_get(L, Blocks1),
CopyIs = [#b_set{op=copy,dst=New,args=[Old]} || {Old,New} <- Ren],
Is = insert_after_phis(Is0, CopyIs),
Blk = Blk0#b_blk{is=Is},
- Blocks = maps:put(L, Blk, Blocks1),
+ Blocks = Blocks1#{L:=Blk},
{NewVars,Blocks,Count}.
insert_after_phis([#b_set{op=phi}=I|Is], InsertIs) ->
@@ -1895,7 +1895,7 @@ frame_size(#st{frames=Frames,regs=Regs,ssa=Blocks0}=St) ->
frame_size_1(L, Regs, Blocks0) ->
Def = beam_ssa:def([L], Blocks0),
- Yregs0 = [maps:get(V, Regs) || V <- Def, is_yreg(maps:get(V, Regs))],
+ Yregs0 = [map_get(V, Regs) || V <- Def, is_yreg(map_get(V, Regs))],
Yregs = ordsets:from_list(Yregs0),
FrameSize = length(ordsets:from_list(Yregs)),
if
@@ -1907,17 +1907,17 @@ frame_size_1(L, Regs, Blocks0) ->
true ->
ok
end,
- Blk0 = maps:get(L, Blocks0),
+ Blk0 = map_get(L, Blocks0),
Blk = beam_ssa:add_anno(frame_size, FrameSize, Blk0),
%% Insert an annotation for frame deallocation on
%% each #b_ret{}.
- Blocks = maps:put(L, Blk, Blocks0),
+ Blocks = Blocks0#{L:=Blk},
Reachable = beam_ssa:rpo([L], Blocks),
frame_deallocate(Reachable, FrameSize, Blocks).
frame_deallocate([L|Ls], Size, Blocks0) ->
- Blk0 = maps:get(L, Blocks0),
+ Blk0 = map_get(L, Blocks0),
Blk = case Blk0 of
#b_blk{last=#b_ret{}=Ret0} ->
Ret = beam_ssa:add_anno(deallocate, Size, Ret0),
@@ -1925,7 +1925,7 @@ frame_deallocate([L|Ls], Size, Blocks0) ->
#b_blk{} ->
Blk0
end,
- Blocks = maps:put(L, Blk, Blocks0),
+ Blocks = Blocks0#{L:=Blk},
frame_deallocate(Ls, Size, Blocks);
frame_deallocate([], _, Blocks) -> Blocks.
@@ -1938,7 +1938,7 @@ frame_deallocate([], _, Blocks) -> Blocks.
turn_yregs(#st{frames=Frames,regs=Regs0,ssa=Blocks}=St) ->
Regs1 = foldl(fun(L, A) ->
- Blk = maps:get(L, Blocks),
+ Blk = map_get(L, Blocks),
FrameSize = beam_ssa:get_anno(frame_size, Blk),
Def = beam_ssa:def([L], Blocks),
[turn_yregs_1(Def, FrameSize, Regs0)|A]
@@ -1947,7 +1947,7 @@ turn_yregs(#st{frames=Frames,regs=Regs0,ssa=Blocks}=St) ->
St#st{regs=Regs}.
turn_yregs_1(Def, FrameSize, Regs) ->
- Yregs0 = [{maps:get(V, Regs),V} || V <- Def, is_yreg(maps:get(V, Regs))],
+ Yregs0 = [{map_get(V, Regs),V} || V <- Def, is_yreg(map_get(V, Regs))],
Yregs1 = rel2fam(Yregs0),
FrameSize = length(Yregs1),
Yregs2 = [{{y,FrameSize-Y-1},Vs} || {{y,Y},Vs} <- Yregs1],
@@ -1993,11 +1993,12 @@ reserve_zregs(Blocks, Intervals, Res) ->
end,
beam_ssa:fold_rpo(F, [0], Res, Blocks).
-reserve_zreg([#b_set{op=call,dst=Dst}],
- #b_br{bool=Dst}, _ShortLived, A) ->
- %% If type optimization has determined that the result of a call can be
- %% used directly in a branch, we must avoid reserving a z register or code
- %% generation will fail.
+reserve_zreg([#b_set{op=Op,dst=Dst}],
+ #b_br{bool=Dst}, _ShortLived, A) when Op =:= call;
+ Op =:= get_tuple_element ->
+ %% If type optimization has determined that the result of these
+ %% instructions can be used directly in a branch, we must avoid reserving a
+ %% z register or code generation will fail.
A;
reserve_zreg([#b_set{op={bif,tuple_size},dst=Dst},
#b_set{op={bif,'=:='},args=[Dst,Val]}], Last, ShortLived, A0) ->
@@ -2356,7 +2357,7 @@ linear_scan(#st{intervals=Intervals0,res=Res}=St0) ->
St#st{regs=maps:from_list(Regs)}.
init_interval({V,[{Start,_}|_]=Rs}, Res) ->
- Info = maps:get(V, Res),
+ Info = map_get(V, Res),
Pool = case Info of
{prefer,{x,_}} -> x;
x -> x;
@@ -2557,16 +2558,16 @@ free_reg(#i{reg={_,_}=Reg}=I, L) ->
update_pool(I, FreeRegs, L).
get_pool(#i{pool=Pool}, #l{free=Free}) ->
- maps:get(Pool, Free).
+ map_get(Pool, Free).
update_pool(#i{pool=Pool}, New, #l{free=Free0}=L) ->
- Free = maps:put(Pool, New, Free0),
+ Free = Free0#{Pool:=New},
L#l{free=Free}.
get_next_free(#i{pool=Pool}, #l{free=Free0}=L0) ->
K = {next,Pool},
- N = maps:get(K, Free0),
- Free = maps:put(K, N+1, Free0),
+ N = map_get(K, Free0),
+ Free = Free0#{K:=N+1},
L = L0#l{free=Free},
if
is_integer(Pool) -> {{y,N},L};
@@ -2602,7 +2603,7 @@ are_overlapping_1({_,_}, []) -> false.
is_loop_header(L, Blocks) ->
%% We KNOW that a loop header must start with a peek_message
%% instruction.
- case maps:get(L, Blocks) of
+ case map_get(L, Blocks) of
#b_blk{is=[#b_set{op=peek_message}|_]} -> true;
_ -> false
end.
diff --git a/lib/compiler/src/beam_ssa_recv.erl b/lib/compiler/src/beam_ssa_recv.erl
index 6e49b128da..1e0e1ecac2 100644
--- a/lib/compiler/src/beam_ssa_recv.erl
+++ b/lib/compiler/src/beam_ssa_recv.erl
@@ -101,7 +101,7 @@ opt([{L,#b_blk{is=[#b_set{op=peek_message}|_]}=Blk0}|Bs], Blocks0, Preds) ->
case recv_opt(Preds, L, Blocks0) of
{yes,Blocks1} ->
Blk = beam_ssa:add_anno(recv_set, L, Blk0),
- Blocks = maps:put(L, Blk, Blocks1),
+ Blocks = Blocks1#{L:=Blk},
opt(Bs, Blocks, []);
no ->
opt(Bs, Blocks0, [])
@@ -111,11 +111,11 @@ opt([{L,_}|Bs], Blocks, Preds) ->
opt([], Blocks, _) -> Blocks.
recv_opt([L|Ls], RecvLbl, Blocks) ->
- #b_blk{is=Is0} = Blk0 = maps:get(L, Blocks),
+ #b_blk{is=Is0} = Blk0 = map_get(L, Blocks),
case recv_opt_is(Is0, RecvLbl, Blocks, []) of
{yes,Is} ->
Blk = Blk0#b_blk{is=Is},
- {yes,maps:put(L, Blk, Blocks)};
+ {yes,Blocks#{L:=Blk}};
no ->
recv_opt(Ls, RecvLbl, Blocks)
end;
@@ -174,7 +174,7 @@ opt_ref_used(RecvLbl, Ref, Blocks) ->
end.
opt_ref_used_1(L, Vs0, Blocks) ->
- #b_blk{is=Is} = Blk = maps:get(L, Blocks),
+ #b_blk{is=Is} = Blk = map_get(L, Blocks),
case opt_ref_used_is(Is, Vs0) of
#{}=Vs ->
opt_ref_used_last(Blk, Vs, Blocks);
diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl
index 8bd6772ac5..e51f8cdcb7 100644
--- a/lib/compiler/src/beam_ssa_type.erl
+++ b/lib/compiler/src/beam_ssa_type.erl
@@ -23,7 +23,7 @@
-include("beam_ssa_opt.hrl").
-import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2,
- partition/2,reverse/1,sort/1]).
+ partition/2,reverse/1,seq/2,sort/1]).
-define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}).
@@ -44,12 +44,13 @@
-record(t_bs_match, {type :: type()}).
-record(t_tuple, {size=0 :: integer(),
exact=false :: boolean(),
- elements=[] :: [any()]
- }).
+ %% Known element types (1-based index), unknown elements are
+ %% are assumed to be 'any'.
+ elements=#{} :: #{ non_neg_integer() => type() }}).
-type type() :: 'any' | 'none' |
#t_atom{} | #t_integer{} | #t_bs_match{} | #t_tuple{} |
- {'binary',pos_integer()} | 'cons' | 'float' | 'list' | 'map' | 'nil' |'number'.
+ {'binary',pos_integer()} | 'cons' | 'float' | 'list' | 'map' | 'nil' | 'number'.
-type type_db() :: #{beam_ssa:var_name():=type()}.
-spec opt_start(Linear, Args, Anno, FuncDb) -> {Linear, FuncDb} when
@@ -166,8 +167,11 @@ opt_finish_1([Arg | Args], [TypeMap | TypeMaps], ParamInfo0) ->
opt_finish_1([], [], ParamInfo) ->
ParamInfo.
-validator_anno(#t_tuple{size=Size,exact=Exact}) ->
- beam_validator:type_anno(tuple, Size, Exact);
+validator_anno(#t_tuple{size=Size,exact=Exact,elements=Elements0}) ->
+ Elements = maps:fold(fun(Index, Type, Acc) ->
+ Acc#{ Index => validator_anno(Type) }
+ end, #{}, Elements0),
+ beam_validator:type_anno(tuple, Size, Exact, Elements);
validator_anno(#t_integer{elements={Same,Same}}) ->
beam_validator:type_anno(integer, Same);
validator_anno(#t_integer{}) ->
@@ -292,6 +296,12 @@ opt_is([#b_set{op=call,args=Args0,dst=Dst}=I0 | Is],
Ds = Ds0#{ Dst => I1 },
opt_is(Is, Ts, Ds, Fdb0, Ls, D, Sub, [I1|Acc])
end;
+opt_is([#b_set{op=set_tuple_element}=I0|Is],
+ Ts0, Ds0, Fdb, Ls, D, Sub, Acc) ->
+ %% This instruction lacks a return value and destructively updates its
+ %% source, so it needs special handling to update the source type.
+ {Ts, Ds, I} = opt_set_tuple_element(I0, Ts0, Ds0, Sub),
+ opt_is(Is, Ts, Ds, Fdb, Ls, D, Sub, [I|Acc]);
opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst}=I],
Ts0, Ds0, Fdb, Ls, D, Sub0, Acc) ->
case Ds0 of
@@ -396,6 +406,28 @@ update_arg_types([Arg | Args], [TypeMap0 | TypeMaps], CallId, Ts) ->
update_arg_types([], [], _CallId, _Ts) ->
[].
+opt_set_tuple_element(#b_set{op=set_tuple_element,args=Args0,dst=Dst}=I0,
+ Ts0, Ds0, Sub) ->
+ Args = simplify_args(Args0, Sub, Ts0),
+ [Val,#b_var{}=Src,#b_literal{val=N}] = Args,
+
+ SrcType0 = get_type(Src, Ts0),
+ ValType = get_type(Val, Ts0),
+ Index = N + 1,
+
+ #t_tuple{size=Size,elements=Es0} = SrcType0,
+ true = Index =< Size, %Assertion.
+
+ Es = set_element_type(Index, ValType, Es0),
+ SrcType = SrcType0#t_tuple{elements=Es},
+
+ I = beam_ssa:normalize(I0#b_set{args=Args}),
+
+ Ts = Ts0#{ Dst => any, Src => SrcType },
+ Ds = Ds0#{ Dst => I },
+
+ {Ts, Ds, I}.
+
simplify(#b_set{op={bif,'and'},args=Args}=I, Ts) ->
case is_safe_bool_op(Args, Ts) of
true ->
@@ -418,12 +450,14 @@ simplify(#b_set{op={bif,'or'},args=Args}=I, Ts) ->
false ->
I
end;
-simplify(#b_set{op={bif,element},args=[#b_literal{val=Index},Tuple]}=I, Ts) ->
+simplify(#b_set{op={bif,element},args=[#b_literal{val=Index},Tuple]}=I0, Ts) ->
case t_tuple_size(get_type(Tuple, Ts)) of
{_,Size} when is_integer(Index), 1 =< Index, Index =< Size ->
- I#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=Index-1}]};
+ I = I0#b_set{op=get_tuple_element,
+ args=[Tuple,#b_literal{val=Index-1}]},
+ simplify(I, Ts);
_ ->
- eval_bif(I, Ts)
+ eval_bif(I0, Ts)
end;
simplify(#b_set{op={bif,hd},args=[List]}=I, Ts) ->
case get_type(List, Ts) of
@@ -485,11 +519,17 @@ simplify(#b_set{op={bif,Op},args=Args}=I, Ts) ->
AnnoArgs = [anno_float_arg(A) || A <- Types],
eval_bif(beam_ssa:add_anno(float_op, AnnoArgs, I), Ts)
end;
-simplify(#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=0}]}=I, Ts) ->
+simplify(#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=N}]}=I, Ts) ->
case get_type(Tuple, Ts) of
- #t_tuple{elements=[First]} ->
- #b_literal{val=First};
- #t_tuple{} ->
+ #t_tuple{size=Size,elements=Es} when Size > N ->
+ ElemType = get_element_type(N + 1, Es),
+ case get_literal_from_type(ElemType) of
+ #b_literal{}=Lit -> Lit;
+ none -> I
+ end;
+ none ->
+ %% Will never be executed because of type conflict.
+ %% #b_literal{val=ignored};
I
end;
simplify(#b_set{op=is_nonempty_list,args=[Src]}=I, Ts) ->
@@ -500,24 +540,8 @@ simplify(#b_set{op=is_nonempty_list,args=[Src]}=I, Ts) ->
_ -> #b_literal{val=false}
end;
simplify(#b_set{op=is_tagged_tuple,
- args=[Src,#b_literal{val=Size},#b_literal{val=Tag}]}=I, Ts) ->
- case get_type(Src, Ts) of
- #t_tuple{exact=true,size=Size,elements=[Tag]} ->
- #b_literal{val=true};
- #t_tuple{exact=true,size=ActualSize,elements=[]} ->
- if
- Size =/= ActualSize ->
- #b_literal{val=false};
- true ->
- I
- end;
- #t_tuple{exact=false} ->
- I;
- any ->
- I;
- _ ->
- #b_literal{val=false}
- end;
+ args=[Src,#b_literal{val=Size},#b_literal{}=Tag]}=I, Ts) ->
+ simplify_is_record(I, get_type(Src, Ts), Size, Tag, Ts);
simplify(#b_set{op=put_list,args=[#b_literal{val=H},
#b_literal{val=T}]}, _Ts) ->
#b_literal{val=[H|T]};
@@ -673,35 +697,36 @@ update_successors(#b_br{bool=#b_var{}=Bool,succ=Succ,fail=Fail}, Ts0, D0) ->
%% no need to include the type database passed on to the
%% successors of this block.
Ts = maps:remove(Bool, Ts0),
- {SuccTs,FailTs} = infer_types(Bool, Ts, D0),
+ {SuccTs,FailTs} = infer_types_br(Bool, Ts, D0),
D = update_successor(Fail, FailTs, D0),
update_successor(Succ, SuccTs, D);
false ->
- {SuccTs,FailTs} = infer_types(Bool, Ts0, D0),
+ {SuccTs,FailTs} = infer_types_br(Bool, Ts0, D0),
D = update_successor_bool(Bool, false, Fail, FailTs, D0),
update_successor_bool(Bool, true, Succ, SuccTs, D)
end;
-update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts0, D0) ->
+update_successors(#b_switch{arg=#b_var{}=V,fail=Fail,list=List}, Ts, D0) ->
case cerl_sets:is_element(V, D0#d.once) of
true ->
%% This variable is defined in this block and is only
%% referenced by this switch terminator. Therefore, there is
- %% no need to include the type database passed on to the
- %% successors of this block.
- Ts = maps:remove(V, Ts0),
+ %% no need to include it in the type database passed on to
+ %% the successors of this block.
D = update_successor(Fail, Ts, D0),
- F = fun({_Val,S}, A) ->
- update_successor(S, Ts, A)
+ F = fun({Val,S}, A) ->
+ SuccTs0 = infer_types_switch(V, Val, Ts, D),
+ SuccTs = maps:remove(V, SuccTs0),
+ update_successor(S, SuccTs, A)
end,
foldl(F, D, List);
false ->
%% V can not be equal to any of the values in List at the fail
%% block.
- FailTs = subtract_sw_list(V, List, Ts0),
+ FailTs = subtract_sw_list(V, List, Ts),
D = update_successor(Fail, FailTs, D0),
F = fun({Val,S}, A) ->
- T = get_type(Val, Ts0),
- update_successor(S, Ts0#{V=>T}, A)
+ SuccTs = infer_types_switch(V, Val, Ts, D),
+ update_successor(S, SuccTs, A)
end,
foldl(F, D, List)
end;
@@ -785,19 +810,40 @@ type(bs_get_tail, _Args, _Ts, _Ds) ->
type(call, [#b_remote{mod=#b_literal{val=Mod},
name=#b_literal{val=Name}}|Args], Ts, _Ds) ->
case {Mod,Name,Args} of
- {erlang,setelement,[Pos,Tuple,_]} ->
+ {erlang,setelement,[Pos,Tuple,Arg]} ->
case {get_type(Pos, Ts),get_type(Tuple, Ts)} of
- {#t_integer{elements={MinIndex,_}},#t_tuple{}=T}
- when MinIndex > 1 ->
- %% First element is not updated. The result
- %% will have the same type.
- T;
+ {#t_integer{elements={Index,Index}},
+ #t_tuple{elements=Es0,size=Size}=T} ->
+ %% This is an exact index, update the type of said element
+ %% or return 'none' if it's known to be out of bounds.
+ Es = set_element_type(Index, get_type(Arg, Ts), Es0),
+ case T#t_tuple.exact of
+ false ->
+ T#t_tuple{size=max(Index, Size),elements=Es};
+ true when Index =< Size ->
+ T#t_tuple{elements=Es};
+ true ->
+ none
+ end;
+ {#t_integer{elements={Min,Max}},
+ #t_tuple{elements=Es0,size=Size}=T} ->
+ %% We know this will land between Min and Max, so kill the
+ %% types for those indexes.
+ Es = maps:without(seq(Min, Max), Es0),
+ case T#t_tuple.exact of
+ false ->
+ T#t_tuple{elements=Es,size=max(Min, Size)};
+ true when Min =< Size ->
+ T#t_tuple{elements=Es,size=Size};
+ true ->
+ none
+ end;
{_,#t_tuple{}=T} ->
- %% Position is 1 or unknown. May update the first
- %% element of the tuple.
- T#t_tuple{elements=[]};
- {#t_integer{elements={MinIndex,_}},_} ->
- #t_tuple{size=MinIndex};
+ %% Position unknown, so we have to discard all element
+ %% information.
+ T#t_tuple{elements=#{}};
+ {#t_integer{elements={Min,_Max}},_} ->
+ #t_tuple{size=Min};
{_,_} ->
#t_tuple{}
end;
@@ -820,6 +866,11 @@ type(call, [#b_remote{mod=#b_literal{val=Mod},
false -> any
end
end;
+type(get_tuple_element, [Tuple, Offset], Ts, _Ds) ->
+ #t_tuple{size=Size,elements=Es} = get_type(Tuple, Ts),
+ #b_literal{val=N} = Offset,
+ true = Size > N, %Assertion.
+ get_element_type(N + 1, Es);
type(is_nonempty_list, [_], _Ts, _Ds) ->
t_boolean();
type(is_tagged_tuple, [_,#b_literal{},#b_literal{}], _Ts, _Ds) ->
@@ -828,13 +879,13 @@ type(put_map, _Args, _Ts, _Ds) ->
map;
type(put_list, _Args, _Ts, _Ds) ->
cons;
-type(put_tuple, Args, _Ts, _Ds) ->
- case Args of
- [#b_literal{val=First}|_] ->
- #t_tuple{exact=true,size=length(Args),elements=[First]};
- _ ->
- #t_tuple{exact=true,size=length(Args)}
- end;
+type(put_tuple, Args, Ts, _Ds) ->
+ {Es, _} = foldl(fun(Arg, {Es0, Index}) ->
+ Type = get_type(Arg, Ts),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, Args),
+ #t_tuple{exact=true,size=length(Args),elements=Es};
type(succeeded, [#b_var{}=Src], Ts, Ds) ->
case maps:get(Src, Ds) of
#b_set{op={bif,Bif},args=BifArgs} ->
@@ -1047,6 +1098,34 @@ eq_ranges([H], H, H) -> true;
eq_ranges([H|T], H, Max) -> eq_ranges(T, H+1, Max);
eq_ranges(_, _, _) -> false.
+simplify_is_record(I, #t_tuple{exact=Exact,
+ size=Size,
+ elements=Es},
+ RecSize, RecTag, Ts) ->
+ TagType = maps:get(1, Es, any),
+ TagMatch = case get_literal_from_type(TagType) of
+ #b_literal{}=RecTag -> yes;
+ #b_literal{} -> no;
+ none ->
+ %% Is it at all possible for the tag to match?
+ case meet(get_type(RecTag, Ts), TagType) of
+ none -> no;
+ _ -> maybe
+ end
+ end,
+ if
+ Size =/= RecSize, Exact; Size > RecSize; TagMatch =:= no ->
+ #b_literal{val=false};
+ Size =:= RecSize, Exact, TagMatch =:= yes ->
+ #b_literal{val=true};
+ true ->
+ I
+ end;
+simplify_is_record(I, any, _Size, _Tag, _Ts) ->
+ I;
+simplify_is_record(_I, _Type, _Size, _Tag, _Ts) ->
+ #b_literal{val=false}.
+
simplify_switch_bool(#b_switch{arg=B,list=List0}=Sw, Ts, Ds) ->
List = sort(List0),
case List of
@@ -1081,44 +1160,51 @@ simplify_not(#b_br{bool=#b_var{}=V,succ=Succ,fail=Fail}=Br0, Ts, Ds) ->
used_once(Linear, Args) ->
Map0 = used_once_1(reverse(Linear), #{}),
Map = maps:without(Args, Map0),
- Used0 = cerl_sets:from_list(maps:keys(Map)),
- Used1 = used_in_terminators(Linear, []),
- cerl_sets:intersection(Used0, Used1).
-
-used_in_terminators([{_,#b_blk{last=Last}}|Bs], Acc) ->
- used_in_terminators(Bs, beam_ssa:used(Last) ++ Acc);
-used_in_terminators([], Acc) ->
- cerl_sets:from_list(Acc).
+ cerl_sets:from_list(maps:keys(Map)).
used_once_1([{L,#b_blk{is=Is,last=Last}}|Bs], Uses0) ->
- Uses = used_once_2([Last|reverse(Is)], L, Uses0),
+ Uses1 = used_once_last_uses(beam_ssa:used(Last), L, Uses0),
+ Uses = used_once_2(reverse(Is), L, Uses1),
used_once_1(Bs, Uses);
used_once_1([], Uses) -> Uses.
-used_once_2([I|Is], L, Uses0) ->
+used_once_2([#b_set{dst=Dst}=I|Is], L, Uses0) ->
Uses = used_once_uses(beam_ssa:used(I), L, Uses0),
- case I of
- #b_set{dst=Dst} ->
- case Uses of
- #{Dst:=[L]} ->
- used_once_2(Is, L, Uses);
- #{} ->
- used_once_2(Is, L, maps:remove(Dst, Uses))
- end;
- _ ->
- used_once_2(Is, L, Uses)
+ case Uses of
+ #{Dst:=[L]} ->
+ used_once_2(Is, L, Uses);
+ #{} ->
+ %% Used more than once or used once in
+ %% in another block.
+ used_once_2(Is, L, maps:remove(Dst, Uses))
end;
used_once_2([], _, Uses) -> Uses.
used_once_uses([V|Vs], L, Uses) ->
case Uses of
- #{V:=Us} ->
- used_once_uses(Vs, L, Uses#{V:=[L|Us]});
+ #{V:=more_than_once} ->
+ used_once_uses(Vs, L, Uses);
#{} ->
- used_once_uses(Vs, L, Uses#{V=>[L]})
+ %% Already used or first use is not in
+ %% a terminator.
+ used_once_uses(Vs, L, Uses#{V=>more_than_once})
end;
used_once_uses([], _, Uses) -> Uses.
+used_once_last_uses([V|Vs], L, Uses) ->
+ case Uses of
+ #{V:=[_]} ->
+ %% Second time this variable is used.
+ used_once_last_uses(Vs, L, Uses#{V:=more_than_once});
+ #{V:=more_than_once} ->
+ %% Used at least twice before.
+ used_once_last_uses(Vs, L, Uses);
+ #{} ->
+ %% First time this variable is used.
+ used_once_last_uses(Vs, L, Uses#{V=>[L]})
+ end;
+used_once_last_uses([], _, Uses) -> Uses.
+
get_types(Values, Ts) ->
[get_type(Val, Ts) || Val <- Values].
@@ -1142,8 +1228,12 @@ get_type(#b_literal{val=Val}, _Ts) ->
Val =:= {} ->
#t_tuple{exact=true};
is_tuple(Val) ->
- #t_tuple{exact=true,size=tuple_size(Val),
- elements=[element(1, Val)]};
+ {Es, _} = foldl(fun(E, {Es0, Index}) ->
+ Type = get_type(#b_literal{val=E}, #{}),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, tuple_to_list(Val)),
+ #t_tuple{exact=true,size=tuple_size(Val),elements=Es};
Val =:= [] ->
nil;
true ->
@@ -1185,7 +1275,7 @@ get_type(#b_literal{val=Val}, _Ts) ->
%% failed and that L is not 'cons'. 'cons' can be subtracted from the
%% previously known type for L and the result put in FailTypes.
-infer_types(#b_var{}=V, Ts, #d{ds=Ds}) ->
+infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) ->
#{V:=#b_set{op=Op,args=Args}} = Ds,
Types0 = infer_type(Op, Args, Ds),
@@ -1206,11 +1296,14 @@ infer_types(#b_var{}=V, Ts, #d{ds=Ds}) ->
Types = Types1 ++ Types0,
{meet_types(EqTypes++Types, Ts),subtract_types(Types, Ts)}.
+infer_types_switch(V, Lit, Ts, #d{ds=Ds}) ->
+ Types = infer_eq_type({bif,'=:='}, [V, Lit], Ts, Ds),
+ meet_types(Types, Ts).
+
infer_eq_type({bif,'=:='}, [#b_var{}=Src,#b_literal{}=Lit], Ts, Ds) ->
Def = maps:get(Src, Ds),
Type = get_type(Lit, Ts),
- [{Src,Type}|infer_tuple_size(Def, Lit) ++
- infer_first_element(Def, Lit)];
+ [{Src,Type} | infer_eq_lit(Def, Lit)];
infer_eq_type({bif,'=:='}, [#b_var{}=Arg0,#b_var{}=Arg1], Ts, _Ds) ->
%% As an example, assume that L1 is known to be 'list', and L2 is
%% known to be 'cons'. Then if 'L1 =:= L2' evaluates to 'true', it can
@@ -1225,6 +1318,17 @@ infer_eq_type({bif,'=:='}, [#b_var{}=Arg0,#b_var{}=Arg1], Ts, _Ds) ->
infer_eq_type(_Op, _Args, _Ts, _Ds) ->
[].
+infer_eq_lit(#b_set{op={bif,tuple_size},args=[#b_var{}=Tuple]},
+ #b_literal{val=Size}) when is_integer(Size) ->
+ [{Tuple,#t_tuple{exact=true,size=Size}}];
+infer_eq_lit(#b_set{op=get_tuple_element,
+ args=[#b_var{}=Tuple,#b_literal{val=N}]},
+ #b_literal{}=Lit) ->
+ Index = N + 1,
+ Es = set_element_type(Index, get_type(Lit, #{}), #{}),
+ [{Tuple,#t_tuple{size=Index,elements=Es}}];
+infer_eq_lit(_, _) -> [].
+
infer_type({bif,element}, [#b_literal{val=Pos},#b_var{}=Tuple], _Ds) ->
if
is_integer(Pos), 1 =< Pos ->
@@ -1258,8 +1362,9 @@ infer_type(bs_start_match, [#b_var{}=Bin], _Ds) ->
infer_type(is_nonempty_list, [#b_var{}=Src], _Ds) ->
[{Src,cons}];
infer_type(is_tagged_tuple, [#b_var{}=Src,#b_literal{val=Size},
- #b_literal{val=Tag}], _Ds) ->
- [{Src,#t_tuple{exact=true,size=Size,elements=[Tag]}}];
+ #b_literal{}=Tag], _Ds) ->
+ Es = set_element_type(1, get_type(Tag, #{}), #{}),
+ [{Src,#t_tuple{exact=true,size=Size,elements=Es}}];
infer_type(succeeded, [#b_var{}=Src], Ds) ->
#b_set{op=Op,args=Args} = maps:get(Src, Ds),
infer_type(Op, Args, Ds);
@@ -1352,17 +1457,6 @@ inferred_bif_type('*', [_,_]) -> number;
inferred_bif_type('/', [_,_]) -> number;
inferred_bif_type(_, _) -> any.
-infer_tuple_size(#b_set{op={bif,tuple_size},args=[#b_var{}=Tuple]},
- #b_literal{val=Size}) when is_integer(Size) ->
- [{Tuple,#t_tuple{exact=true,size=Size}}];
-infer_tuple_size(_, _) -> [].
-
-infer_first_element(#b_set{op=get_tuple_element,
- args=[#b_var{}=Tuple,#b_literal{val=0}]},
- #b_literal{val=First}) ->
- [{Tuple,#t_tuple{size=1,elements=[First]}}];
-infer_first_element(_, _) -> [].
-
is_math_bif(cos, 1) -> true;
is_math_bif(cosh, 1) -> true;
is_math_bif(sin, 1) -> true;
@@ -1461,6 +1555,19 @@ t_tuple_size(_) ->
is_singleton_type(Type) ->
get_literal_from_type(Type) =/= none.
+get_element_type(Index, Es) ->
+ case Es of
+ #{ Index := T } -> T;
+ #{} -> any
+ end.
+
+set_element_type(_Key, none, Es) ->
+ Es;
+set_element_type(Key, any, Es) ->
+ maps:remove(Key, Es);
+set_element_type(Key, Type, Es) ->
+ Es#{ Key => Type }.
+
%% join(Type1, Type2) -> Type
%% Return the "join" of Type1 and Type2. The join is a more general
%% type than Type1 and Type2. For example:
@@ -1508,15 +1615,41 @@ join(#t_integer{}, number) -> number;
join(number, #t_integer{}) -> number;
join(float, number) -> number;
join(number, float) -> number;
-join(#t_tuple{size=Sz,exact=Exact1}, #t_tuple{size=Sz,exact=Exact2}) ->
- Exact = Exact1 and Exact2,
- #t_tuple{size=Sz,exact=Exact};
-join(#t_tuple{size=Sz1}, #t_tuple{size=Sz2}) ->
- #t_tuple{size=min(Sz1, Sz2)};
+join(#t_tuple{size=Sz,exact=ExactA,elements=EsA},
+ #t_tuple{size=Sz,exact=ExactB,elements=EsB}) ->
+ Exact = ExactA and ExactB,
+ Es = join_tuple_elements(Sz, EsA, EsB),
+ #t_tuple{size=Sz,exact=Exact,elements=Es};
+join(#t_tuple{size=SzA,elements=EsA}, #t_tuple{size=SzB,elements=EsB}) ->
+ Sz = min(SzA, SzB),
+ Es = join_tuple_elements(Sz, EsA, EsB),
+ #t_tuple{size=Sz,elements=Es};
join(_T1, _T2) ->
%%io:format("~p ~p\n", [_T1,_T2]),
any.
+join_tuple_elements(MinSize, EsA, EsB) ->
+ Es0 = join_elements(EsA, EsB),
+ maps:filter(fun(Index, _Type) -> Index =< MinSize end, Es0).
+
+join_elements(Es1, Es2) ->
+ Keys = if
+ map_size(Es1) =< map_size(Es2) -> maps:keys(Es1);
+ map_size(Es1) > map_size(Es2) -> maps:keys(Es2)
+ end,
+ join_elements_1(Keys, Es1, Es2, #{}).
+
+join_elements_1([Key | Keys], Es1, Es2, Acc0) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ Acc = set_element_type(Key, join(Type1, Type2), Acc0),
+ join_elements_1(Keys, Es1, Es2, Acc);
+ {#{}, #{}} ->
+ join_elements_1(Keys, Es1, Es2, Acc0)
+ end;
+join_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
gcd(A, B) ->
case A rem B of
0 -> B;
@@ -1613,9 +1746,6 @@ meet(_, _) ->
%% Inconsistent types. There will be an exception at runtime.
none.
-meet_tuples(#t_tuple{elements=[E1]}, #t_tuple{elements=[E2]})
- when E1 =/= E2 ->
- none;
meet_tuples(#t_tuple{size=Sz1,exact=true},
#t_tuple{size=Sz2,exact=true}) when Sz1 =/= Sz2 ->
none;
@@ -1623,12 +1753,31 @@ meet_tuples(#t_tuple{size=Sz1,exact=Ex1,elements=Es1},
#t_tuple{size=Sz2,exact=Ex2,elements=Es2}) ->
Size = max(Sz1, Sz2),
Exact = Ex1 or Ex2,
- Es = case {Es1,Es2} of
- {[],[_|_]} -> Es2;
- {[_|_],[]} -> Es1;
- {_,_} -> Es1
- end,
- #t_tuple{size=Size,exact=Exact,elements=Es}.
+ case meet_elements(Es1, Es2) of
+ none ->
+ none;
+ Es ->
+ #t_tuple{size=Size,exact=Exact,elements=Es}
+ end.
+
+meet_elements(Es1, Es2) ->
+ Keys = maps:keys(Es1) ++ maps:keys(Es2),
+ meet_elements_1(Keys, Es1, Es2, #{}).
+
+meet_elements_1([Key | Keys], Es1, Es2, Acc) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ case meet(Type1, Type2) of
+ none -> none;
+ Type -> meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type })
+ end;
+ {#{ Key := Type1 }, _} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type1 });
+ {_, #{ Key := Type2 }} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type2 })
+ end;
+meet_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
%% verified_type(Type) -> Type
%% Returns the passed in type if it is one of the defined types.
@@ -1667,5 +1816,13 @@ verified_type(map=T) -> T;
verified_type(nil=T) -> T;
verified_type(cons=T) -> T;
verified_type(number=T) -> T;
-verified_type(#t_tuple{}=T) -> T;
+verified_type(#t_tuple{size=Size,elements=Es}=T) ->
+ %% All known elements must have a valid index and type. 'any' is prohibited
+ %% since it's implicit and should never be present in the map.
+ maps:fold(fun(Index, Element, _) when is_integer(Index),
+ 1 =< Index, Index =< Size,
+ Element =/= any, Element =/= none ->
+ verified_type(Element)
+ end, [], Es),
+ T;
verified_type(float=T) -> T.
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index b56d53d4ce..3b197f7bae 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -26,7 +26,7 @@
%% Interface for compiler.
-export([module/2, format_error/1]).
--export([type_anno/1, type_anno/2, type_anno/3]).
+-export([type_anno/1, type_anno/2, type_anno/4]).
-import(lists, [any/2,dropwhile/2,foldl/3,map/2,foreach/2,reverse/1]).
@@ -65,11 +65,12 @@ type_anno(atom, Value) -> {atom, Value};
type_anno(float, Value) -> {float, Value};
type_anno(integer, Value) -> {integer, Value}.
--spec type_anno(term(), term(), term()) -> term().
-type_anno(tuple, Size, Exact) when is_integer(Size) ->
+-spec type_anno(term(), term(), term(), term()) -> term().
+type_anno(tuple, Size, Exact, Elements) when is_integer(Size), Size >= 0,
+ is_map(Elements) ->
case Exact of
- true -> {tuple, Size};
- false -> {tuple, [Size]}
+ true -> {tuple, Size, Elements};
+ false -> {tuple, [Size], Elements}
end.
-spec format_error(term()) -> iolist().
@@ -367,13 +368,18 @@ valfun_1({put_tuple2,Dst,{list,Elements}}, Vst0) ->
_ = [assert_not_fragile(El, Vst0) || El <- Elements],
Size = length(Elements),
Vst = eat_heap(Size+1, Vst0),
- Type = {tuple,Size},
+ {Es,_} = foldl(fun(Val, {Es0, Index}) ->
+ Type = get_term_type(Val, Vst0),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, Elements),
+ Type = {tuple,Size,Es},
create_term(Type, Dst, Vst);
valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
Vst1 = eat_heap(1, Vst0),
Vst = create_term(tuple_in_progress, Dst, Vst1),
#vst{current=St0} = Vst,
- St = St0#st{puts_left={Sz,{Dst,{tuple,Sz}}}},
+ St = St0#st{puts_left={Sz,{Dst,Sz,#{}}}},
Vst#vst{current=St};
valfun_1({put,Src}, Vst0) ->
assert_not_fragile(Src, Vst0),
@@ -382,11 +388,13 @@ valfun_1({put,Src}, Vst0) ->
case St0 of
#st{puts_left=none} ->
error(not_building_a_tuple);
- #st{puts_left={1,{Dst,Type}}} ->
+ #st{puts_left={1,{Dst,Sz,Es}}} ->
St = St0#st{puts_left=none},
- create_term(Type, Dst, Vst#vst{current=St});
- #st{puts_left={PutsLeft,Info}} when is_integer(PutsLeft) ->
- St = St0#st{puts_left={PutsLeft-1,Info}},
+ create_term({tuple,Sz,Es}, Dst, Vst#vst{current=St});
+ #st{puts_left={PutsLeft,{Dst,Sz,Es0}}} when is_integer(PutsLeft) ->
+ Index = Sz - PutsLeft + 1,
+ Es = Es0#{ Index => get_term_type(Src, Vst0) },
+ St = St0#st{puts_left={PutsLeft-1,{Dst,Sz,Es}}},
Vst#vst{current=St}
end;
%% Instructions for optimization of selective receives.
@@ -492,10 +500,11 @@ valfun_1({get_tl,Src,Dst}, Vst) ->
assert_not_literal(Src),
assert_type(cons, Src, Vst),
extract_term(term, [Src], Dst, Vst);
-valfun_1({get_tuple_element,Src,I,Dst}, Vst) ->
+valfun_1({get_tuple_element,Src,N,Dst}, Vst) ->
assert_not_literal(Src),
- assert_type({tuple_element,I+1}, Src, Vst),
- extract_term(term, [Src], Dst, Vst);
+ assert_type({tuple_element,N+1}, Src, Vst),
+ Type = get_element_type(N+1, Src, Vst),
+ extract_term(Type, [Src], Dst, Vst);
valfun_1({jump,{f,Lbl}}, Vst) ->
kill_state(branch_state(Lbl, Vst));
valfun_1(I, Vst) ->
@@ -595,14 +604,18 @@ valfun_4({make_fun2,_,_,_,Live}, Vst) ->
%% Other BIFs
valfun_4({bif,tuple_size,{f,Fail},[Tuple],Dst}=I, Vst0) ->
Vst1 = branch_state(Fail, Vst0),
- Vst = update_type(fun meet/2, {tuple,[0]}, Tuple, Vst1),
+ Vst = update_type(fun meet/2, {tuple,[0],#{}}, Tuple, Vst1),
set_type_reg_expr({integer,[]}, I, Dst, Vst);
valfun_4({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) ->
PosType = get_durable_term_type(Pos, Vst0),
+ ElementType = case PosType of
+ {integer,I} -> get_element_type(I, Tuple, Vst0);
+ _ -> term
+ end,
+ InferredType = {tuple,[get_tuple_size(PosType)],#{}},
Vst1 = branch_state(Fail, Vst0),
- Type = {tuple,[get_tuple_size(PosType)]},
- Vst = update_type(fun meet/2, Type, Tuple, Vst1),
- extract_term(term, [Tuple], Dst, Vst);
+ Vst = update_type(fun meet/2, InferredType, Tuple, Vst1),
+ extract_term(ElementType, [Tuple], Dst, Vst);
valfun_4({bif,raise,{f,0},Src,_Dst}, Vst) ->
validate_src(Src, Vst),
kill_state(Vst);
@@ -671,10 +684,13 @@ valfun_4(timeout, #vst{current=St}=Vst) ->
Vst#vst{current=St#st{x=init_regs(0, term)}};
valfun_4(send, Vst) ->
call(send, 2, Vst);
-valfun_4({set_tuple_element,Src,Tuple,I}, Vst) ->
+valfun_4({set_tuple_element,Src,Tuple,N}, Vst) ->
+ I = N + 1,
assert_not_fragile(Src, Vst),
- assert_type({tuple_element,I+1}, Tuple, Vst),
- Vst;
+ assert_type({tuple_element,I}, Tuple, Vst),
+ {tuple, Sz, Es0} = get_term_type(Tuple, Vst),
+ Es = set_element_type(I, get_term_type(Src, Vst), Es0),
+ set_aliased_type({tuple, Sz, Es}, Tuple, Vst);
%% Match instructions.
valfun_4({select_val,Src,{f,Fail},{list,Choices}}, Vst0) ->
assert_term(Src, Vst0),
@@ -748,7 +764,7 @@ valfun_4({test,is_boolean,{f,Lbl},[Src]}, Vst) ->
valfun_4({test,is_float,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, {float,[]}, Src, Vst);
valfun_4({test,is_tuple,{f,Lbl},[Src]}, Vst) ->
- type_test(Lbl, {tuple,[0]}, Src, Vst);
+ type_test(Lbl, {tuple,[0],#{}}, Src, Vst);
valfun_4({test,is_integer,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, {integer,[]}, Src, Vst);
valfun_4({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) ->
@@ -769,10 +785,11 @@ valfun_4({test,is_map,{f,Lbl},[Src]}, Vst) ->
end;
valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) ->
assert_type(tuple, Tuple, Vst),
- update_type(fun meet/2, {tuple,Sz}, Tuple, branch_state(Lbl, Vst));
-valfun_4({test,is_tagged_tuple,{f,Lbl},[Src,Sz,_Atom]}, Vst) ->
- assert_term(Src, Vst),
- update_type(fun meet/2, {tuple,Sz}, Src, branch_state(Lbl, Vst));
+ update_type(fun meet/2, {tuple,Sz,#{}}, Tuple, branch_state(Lbl, Vst));
+valfun_4({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ update_type(fun meet/2, {tuple,Sz,#{ 1 => Atom }}, Src, Vst);
valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) ->
assert_type(map, Src, Vst),
assert_unique_map_keys(List),
@@ -1229,7 +1246,10 @@ assert_unique_map_keys([]) ->
assert_unique_map_keys([_]) ->
ok;
assert_unique_map_keys([_,_|_]=Ls) ->
- Vs = [get_literal(L) || L <- Ls],
+ Vs = [begin
+ assert_literal(L),
+ L
+ end || L <- Ls],
case length(Vs) =:= sets:size(sets:from_list(Vs)) of
true -> ok;
false -> error(keys_not_unique)
@@ -1308,7 +1328,7 @@ infer_types(Src, Vst) ->
end;
{bif,tuple_size,{f,_},[Tuple],_} ->
fun({integer,Arity}, S) ->
- update_type(fun meet/2, {tuple,Arity}, Tuple, S);
+ update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S);
(_, S) -> S
end;
{bif,'=:=',{f,_},[ArityReg,{integer,_}=Val],_} when ArityReg =/= Src ->
@@ -1407,16 +1427,13 @@ update_eq_types(LHS, RHS, Vst0) ->
assign_1(Src, Dst, Vst0) ->
Type = get_move_term_type(Src, Vst0),
Vst = set_type_reg(Type, Dst, Vst0),
- case Src of
- {Kind,_} when Kind =:= x; Kind =:= y ->
- #vst{current=St0} = Vst,
- #st{aliases=Aliases0} = St0,
- Aliases = Aliases0#{Src=>Dst,Dst=>Src},
- St = St0#st{aliases=Aliases},
- Vst#vst{current=St};
- _ ->
- Vst
- end.
+
+ #vst{current=St0} = Vst,
+ #st{aliases=Aliases0} = St0,
+ Aliases = Aliases0#{Src=>Dst,Dst=>Src},
+ St = St0#st{aliases=Aliases},
+
+ Vst#vst{current=St}.
set_aliased_type(Type, Reg, #vst{current=#st{aliases=Aliases}}=Vst0) ->
Vst1 = set_type(Type, Reg, Vst0),
@@ -1451,7 +1468,6 @@ set_type_reg(Type, Src, Dst, Vst) ->
_ ->
set_type_reg(Type, Dst, Vst)
end.
-
set_type_reg(Type, Reg, Vst) ->
set_type_reg_expr(Type, none, Reg, Vst).
@@ -1551,6 +1567,13 @@ assert_not_fragile(Src, Vst) ->
_ -> ok
end.
+assert_literal(nil) -> ok;
+assert_literal({atom,A}) when is_atom(A) -> ok;
+assert_literal({float,F}) when is_float(F) -> ok;
+assert_literal({integer,I}) when is_integer(I) -> ok;
+assert_literal({literal,_L}) -> ok;
+assert_literal(T) -> error({literal_required,T}).
+
assert_not_literal({x,_}) -> ok;
assert_not_literal({y,_}) -> ok;
assert_not_literal(Literal) -> error({literal_not_allowed,Literal}).
@@ -1597,11 +1620,12 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}).
%%
%% list List: [] or [_|_]
%%
-%% {tuple,[Sz]} Tuple. An element has been accessed using
-%% element/2 or setelement/3 so that it is known that
-%% the type is a tuple of size at least Sz.
+%% {tuple,[Sz],Es} Tuple. An element has been accessed using
+%% element/2 or setelement/3 so that it is known that
+%% the type is a tuple of size at least Sz. Es is a map
+%% containing known types by tuple index.
%%
-%% {tuple,Sz} Tuple. A test_arity instruction has been seen
+%% {tuple,Sz,Es} Tuple. A test_arity instruction has been seen
%% so that it is known that the size is exactly Sz.
%%
%% {atom,[]} Atom.
@@ -1636,6 +1660,10 @@ assert_not_literal(Literal) -> error({literal_not_allowed,Literal}).
meet(Same, Same) ->
Same;
+meet({literal,_}=T1, T2) ->
+ meet_literal(T1, T2);
+meet(T1, {literal,_}=T2) ->
+ meet_literal(T2, T1);
meet(term, Other) ->
Other;
meet(Other, term) ->
@@ -1651,18 +1679,49 @@ meet(T1, T2) ->
{list,nil} -> nil;
{number,{integer,_}=T} -> T;
{number,{float,_}=T} -> T;
- {{tuple,Size1},{tuple,Size2}} ->
- case {Size1,Size2} of
- {[Sz1],[Sz2]} ->
- {tuple,[erlang:max(Sz1, Sz2)]};
- {Sz1,[Sz2]} when Sz2 =< Sz1 ->
- {tuple,Sz1};
- {_,_} ->
+ {{tuple,Size1,Es1},{tuple,Size2,Es2}} ->
+ Es = meet_elements(Es1, Es2),
+ case {Size1,Size2,Es} of
+ {_, _, none} ->
+ none;
+ {[Sz1],[Sz2],_} ->
+ {tuple,[erlang:max(Sz1, Sz2)],Es};
+ {Sz1,[Sz2],_} when Sz2 =< Sz1 ->
+ {tuple,Sz1,Es};
+ {Sz,Sz,_} ->
+ {tuple,Sz,Es};
+ {_,_,_} ->
none
end;
{_,_} -> none
end.
+%% Meets types of literals.
+meet_literal({literal,_}=Lit, T) ->
+ meet_literal(T, get_literal_type(Lit));
+meet_literal(T1, T2) ->
+ %% We're done extracting the types, try merging them again.
+ meet(T1, T2).
+
+meet_elements(Es1, Es2) ->
+ Keys = maps:keys(Es1) ++ maps:keys(Es2),
+ meet_elements_1(Keys, Es1, Es2, #{}).
+
+meet_elements_1([Key | Keys], Es1, Es2, Acc) ->
+ case {Es1, Es2} of
+ {#{ Key := Type1 }, #{ Key := Type2 }} ->
+ case meet(Type1, Type2) of
+ none -> none;
+ Type -> meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type })
+ end;
+ {#{ Key := Type1 }, _} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type1 });
+ {_, #{ Key := Type2 }} ->
+ meet_elements_1(Keys, Es1, Es2, Acc#{ Key => Type2 })
+ end;
+meet_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
%% subtract(Type1, Type2) -> Type
%% Subtract Type2 from Type2. Example:
%% subtract(list, nil) -> cons
@@ -1681,12 +1740,12 @@ assert_type(WantedType, Term, Vst) ->
assert_type(Correct, Correct) -> ok;
assert_type(float, {float,_}) -> ok;
-assert_type(tuple, {tuple,_}) -> ok;
+assert_type(tuple, {tuple,_,_}) -> ok;
assert_type(tuple, {literal,Tuple}) when is_tuple(Tuple) -> ok;
-assert_type({tuple_element,I}, {tuple,[Sz]})
+assert_type({tuple_element,I}, {tuple,[Sz],_})
when 1 =< I, I =< Sz ->
ok;
-assert_type({tuple_element,I}, {tuple,Sz})
+assert_type({tuple_element,I}, {tuple,Sz,_})
when is_integer(Sz), 1 =< I, I =< Sz ->
ok;
assert_type({tuple_element,I}, {literal,Lit}) when I =< tuple_size(Lit) ->
@@ -1696,6 +1755,25 @@ assert_type(cons, {literal,[_|_]}) ->
assert_type(Needed, Actual) ->
error({bad_type,{needed,Needed},{actual,Actual}}).
+get_element_type(Key, Src, Vst) ->
+ get_element_type_1(Key, get_durable_term_type(Src, Vst)).
+
+get_element_type_1(Index, {tuple,Sz,Es}) ->
+ case Es of
+ #{ Index := Type } -> Type;
+ #{} when Index =< Sz -> term;
+ #{} -> none
+ end;
+get_element_type_1(_Index, _Type) ->
+ term.
+
+set_element_type(_Key, none, Es) ->
+ Es;
+set_element_type(Key, term, Es) ->
+ maps:remove(Key, Es);
+set_element_type(Key, Type, Es) ->
+ Es#{ Key => Type }.
+
get_tuple_size({integer,[]}) -> 0;
get_tuple_size({integer,Sz}) -> Sz;
get_tuple_size(_) -> 0.
@@ -1743,16 +1821,6 @@ get_term_type(Src, Vst) ->
get_special_y_type({y,_}=Reg, Vst) -> get_term_type_1(Reg, Vst);
get_special_y_type(Src, _) -> error({source_not_y_reg,Src}).
-get_term_type_1(nil=T, _) -> T;
-get_term_type_1({atom,A}=T, _) when is_atom(A) -> T;
-get_term_type_1({float,F}=T, _) when is_float(F) -> T;
-get_term_type_1({integer,I}=T, _) when is_integer(I) -> T;
-get_term_type_1({literal,[_|_]}, _) -> cons;
-get_term_type_1({literal,Bitstring}, _) when is_bitstring(Bitstring) -> binary;
-get_term_type_1({literal,Map}, _) when is_map(Map) -> map;
-get_term_type_1({literal,Tuple}, _) when is_tuple(Tuple) ->
- {tuple,tuple_size(Tuple)};
-get_term_type_1({literal,_}=T, _) -> T;
get_term_type_1({x,X}=Reg, #vst{current=#st{x=Xs}}) when is_integer(X) ->
case gb_trees:lookup(X, Xs) of
{value,Type} -> Type;
@@ -1764,7 +1832,8 @@ get_term_type_1({y,Y}=Reg, #vst{current=#st{y=Ys}}) when is_integer(Y) ->
{value,uninitialized} -> error({uninitialized_reg,Reg});
{value,Type} -> Type
end;
-get_term_type_1(Src, _) -> error({bad_source,Src}).
+get_term_type_1(Src, _) ->
+ get_literal_type(Src).
get_def(Src, #vst{current=#st{defs=Defs}}) ->
case Defs of
@@ -1772,23 +1841,41 @@ get_def(Src, #vst{current=#st{defs=Defs}}) ->
#{} -> none
end.
-%% get_literal(Src) -> literal_value().
-get_literal(nil) -> [];
-get_literal({atom,A}) when is_atom(A) -> A;
-get_literal({float,F}) when is_float(F) -> F;
-get_literal({integer,I}) when is_integer(I) -> I;
-get_literal({literal,L}) -> L;
-get_literal(T) -> error({not_literal,T}).
-
-branch_arities([Sz,{f,L}|T], Tuple, {tuple,[_]}=Type0, Vst0) when is_integer(Sz) ->
- Vst1 = set_aliased_type({tuple,Sz}, Tuple, Vst0),
+get_literal_type(nil=T) -> T;
+get_literal_type({atom,A}=T) when is_atom(A) -> T;
+get_literal_type({float,F}=T) when is_float(F) -> T;
+get_literal_type({integer,I}=T) when is_integer(I) -> T;
+get_literal_type({literal,[_|_]}) -> cons;
+get_literal_type({literal,Bitstring}) when is_bitstring(Bitstring) -> binary;
+get_literal_type({literal,Map}) when is_map(Map) -> map;
+get_literal_type({literal,Tuple}) when is_tuple(Tuple) -> value_to_type(Tuple);
+get_literal_type({literal,_}) -> term;
+get_literal_type(T) -> error({not_literal,T}).
+
+value_to_type([]) -> nil;
+value_to_type(A) when is_atom(A) -> {atom, A};
+value_to_type(F) when is_float(F) -> {float, F};
+value_to_type(I) when is_integer(I) -> {integer, I};
+value_to_type(T) when is_tuple(T) ->
+ {Es,_} = foldl(fun(Val, {Es0, Index}) ->
+ Type = value_to_type(Val),
+ Es = set_element_type(Index, Type, Es0),
+ {Es, Index + 1}
+ end, {#{}, 1}, tuple_to_list(T)),
+ {tuple, tuple_size(T), Es};
+value_to_type(L) -> {literal, L}.
+
+branch_arities([Sz,{f,L}|T], Tuple, {tuple,[_],Es0}=Type0, Vst0) when is_integer(Sz) ->
+ %% Filter out element types that are no longer valid.
+ Es = maps:filter(fun(Index, _Type) -> Index =< Sz end, Es0),
+ Vst1 = set_aliased_type({tuple,Sz,Es}, Tuple, Vst0),
Vst = branch_state(L, Vst1),
branch_arities(T, Tuple, Type0, Vst);
-branch_arities([Sz,{f,L}|T], Tuple, {tuple,Sz}=Type, Vst0) when is_integer(Sz) ->
+branch_arities([Sz,{f,L}|T], Tuple, {tuple,Sz,_Es}=Type, Vst0) when is_integer(Sz) ->
%% The type is already correct. (This test is redundant.)
Vst = branch_state(L, Vst0),
branch_arities(T, Tuple, Type, Vst);
-branch_arities([Sz0,{f,_}|T], Tuple, {tuple,Sz}=Type, Vst)
+branch_arities([Sz0,{f,_}|T], Tuple, {tuple,Sz,_Es}=Type, Vst)
when is_integer(Sz), Sz0 =/= Sz ->
%% We already have an established different exact size for the tuple.
%% This label can't possibly be reached.
@@ -1902,9 +1989,14 @@ join({catchtag,T0},{catchtag,T1}) ->
{catchtag,ordsets:from_list(T0++T1)};
join({trytag,T0},{trytag,T1}) ->
{trytag,ordsets:from_list(T0++T1)};
-join({tuple,A}, {tuple,B}) ->
- {tuple,[min(tuple_sz(A), tuple_sz(B))]};
-join({Type,A}, {Type,B})
+join({tuple,Size,EsA}, {tuple,Size,EsB}) ->
+ Es = join_tuple_elements(tuple_sz(Size), EsA, EsB),
+ {tuple, Size, Es};
+join({tuple,A,EsA}, {tuple,B,EsB}) ->
+ Size = [min(tuple_sz(A), tuple_sz(B))],
+ Es = join_tuple_elements(Size, EsA, EsB),
+ {tuple, Size, Es};
+join({Type,A}, {Type,B})
when Type =:= atom; Type =:= integer; Type =:= float ->
if A =:= B -> {Type,A};
true -> {Type,[]}
@@ -1916,9 +2008,9 @@ join(number, {Type,_})
when Type =:= integer; Type =:= float ->
number;
join(bool, {atom,A}) ->
- merge_bool(A);
+ join_bool(A);
join({atom,A}, bool) ->
- merge_bool(A);
+ join_bool(A);
join({atom,_}, {atom,_}) ->
{atom,[]};
join(#ms{id=Id1,valid=B1,slots=Slots1},
@@ -1933,19 +2025,35 @@ join(T1, T2) when T1 =/= T2 ->
%% a 'term'.
join_list(T1, T2).
-%% Merges types of literals. Note that the left argument must either be a
+join_tuple_elements(Size, EsA, EsB) ->
+ Es0 = join_elements(EsA, EsB),
+ MinSize = tuple_sz(Size),
+ maps:filter(fun(Index, _Type) -> Index =< MinSize end, Es0).
+
+join_elements(Es1, Es2) ->
+ Keys = if
+ map_size(Es1) =< map_size(Es2) -> maps:keys(Es1);
+ map_size(Es1) > map_size(Es2) -> maps:keys(Es2)
+ end,
+ join_elements_1(Keys, Es1, Es2, #{}).
+
+join_elements_1([Key | Keys], Es1, Es2, Acc0) ->
+ Type = case {Es1, Es2} of
+ {#{ Key := Same }, #{ Key := Same }} -> Same;
+ {#{ Key := Type1 }, #{ Key := Type2 }} -> join(Type1, Type2);
+ {#{}, #{}} -> term
+ end,
+ Acc = set_element_type(Key, Type, Acc0),
+ join_elements_1(Keys, Es1, Es2, Acc);
+join_elements_1([], _Es1, _Es2, Acc) ->
+ Acc.
+
+%% Joins types of literals; note that the left argument must either be a
%% literal or exactly equal to the second argument.
join_literal(Same, Same) ->
Same;
-join_literal({literal,[_|_]}, T) ->
- join_literal(T, cons);
-join_literal({literal,#{}}, T) ->
- join_literal(T, map);
-join_literal({literal,Tuple}, T) when is_tuple(Tuple) ->
- join_literal(T, {tuple, tuple_size(Tuple)});
-join_literal({literal,_}, T) ->
- %% Bitstring, fun, or similar.
- join_literal(T, term);
+join_literal({literal,_}=Lit, T) ->
+ join_literal(T, get_literal_type(Lit));
join_literal(T1, T2) ->
%% We're done extracting the types, try merging them again.
join(T1, T2).
@@ -1959,14 +2067,14 @@ join_list(_, _) ->
%% Not a list, so it must be a term.
term.
+join_bool([]) -> {atom,[]};
+join_bool(true) -> bool;
+join_bool(false) -> bool;
+join_bool(_) -> {atom,[]}.
+
tuple_sz([Sz]) -> Sz;
tuple_sz(Sz) -> Sz.
-merge_bool([]) -> {atom,[]};
-merge_bool(true) -> bool;
-merge_bool(false) -> bool;
-merge_bool(_) -> {atom,[]}.
-
merge_aliases(Al0, Al1) when map_size(Al0) =< map_size(Al1) ->
maps:filter(fun(K, V) ->
case Al1 of
@@ -2169,26 +2277,27 @@ return_type({extfunc,M,F,A}, Vst) -> return_type_1(M, F, A, Vst);
return_type(_, _) -> term.
return_type_1(erlang, setelement, 3, Vst) ->
- Tuple = {x,1},
+ IndexType = get_term_type({x,0}, Vst),
TupleType =
- case get_term_type(Tuple, Vst) of
- {tuple,_}=TT ->
- TT;
- {literal,Lit} when is_tuple(Lit) ->
- {tuple,tuple_size(Lit)};
- _ ->
- {tuple,[0]}
- end,
- case get_term_type({x,0}, Vst) of
- {integer,[]} ->
- TupleType;
- {integer,I} ->
- case meet({tuple,[I]}, TupleType) of
- none -> TupleType;
- T -> T
+ case get_term_type({x,1}, Vst) of
+ {literal,Tuple}=Lit when is_tuple(Tuple) -> get_literal_type(Lit);
+ {tuple,_,_}=TT -> TT;
+ _ -> {tuple,[0],#{}}
+ end,
+ case IndexType of
+ {integer,I} when is_integer(I) ->
+ case meet({tuple,[I],#{}}, TupleType) of
+ {tuple, Sz, Es0} ->
+ ValueType = get_term_type({x,2}, Vst),
+ Es = set_element_type(I, ValueType, Es0),
+ {tuple, Sz, Es};
+ none ->
+ TupleType
end;
_ ->
- TupleType
+ %% The index could point anywhere, so we must discard all element
+ %% information.
+ setelement(3, TupleType, #{})
end;
return_type_1(erlang, '++', 2, Vst) ->
case get_term_type({x,0}, Vst) =:= cons orelse
diff --git a/lib/compiler/src/sys_core_fold_lists.erl b/lib/compiler/src/sys_core_fold_lists.erl
index 9867fab46a..e93b435011 100644
--- a/lib/compiler/src/sys_core_fold_lists.erl
+++ b/lib/compiler/src/sys_core_fold_lists.erl
@@ -37,22 +37,27 @@ call(#c_call{anno=Anno}, lists, all, [Arg1,Arg2]) ->
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]},
- CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true},
+ CC1 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=true}], guard=#c_literal{val=true},
body=#c_apply{anno=Anno, op=Loop, args=[Xs]}},
- CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true},
+ CC2 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=false}], guard=#c_literal{val=true},
body=#c_literal{val=false}},
- CC3 = #c_clause{pats=[X], guard=#c_literal{val=true},
+ CC3 = #c_clause{anno=Anno,
+ pats=[X], guard=#c_literal{val=true},
body=match_fail(Anno, Err1)},
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]},
clauses = [CC1, CC2, CC3]}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=true}},
Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^all',1}}|Anno], Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
@@ -66,16 +71,21 @@ call(#c_call{anno=Anno}, lists, any, [Arg1,Arg2]) ->
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]},
- CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true},
+ CC1 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=true}], guard=#c_literal{val=true},
body=#c_literal{val=true}},
- CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true},
+ CC2 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=false}], guard=#c_literal{val=true},
body=#c_apply{anno=Anno, op=Loop, args=[Xs]}},
- CC3 = #c_clause{pats=[X], guard=#c_literal{val=true},
+ CC3 = #c_clause{anno=Anno,
+ pats=[X], guard=#c_literal{val=true},
body=match_fail(Anno, Err1)},
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]},
clauses = [CC1, CC2, CC3]}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
@@ -94,16 +104,17 @@ call(#c_call{anno=Anno}, lists, foreach, [Arg1,Arg2]) ->
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_seq{arg=#c_apply{anno=Anno, op=F, args=[X]},
body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno, pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=ok}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^foreach',1}}|Anno], Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
@@ -117,7 +128,8 @@ call(#c_call{anno=Anno}, lists, map, [Arg1,Arg2]) ->
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
H = #c_var{name='H'},
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_let{vars=[H], arg=#c_apply{anno=Anno,
op=F,
args=[X]},
@@ -126,7 +138,7 @@ call(#c_call{anno=Anno}, lists, map, [Arg1,Arg2]) ->
tl=#c_apply{anno=Anno,
op=Loop,
args=[Xs]}}}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno, pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
@@ -146,7 +158,8 @@ call(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2]) ->
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
H = #c_var{name='H'},
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_let{vars=[H],
arg=#c_apply{anno=Anno, op=F, args=[X]},
body=#c_call{anno=[compiler_generated|Anno],
@@ -156,13 +169,13 @@ call(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2]) ->
#c_apply{anno=Anno,
op=Loop,
args=[Xs]}]}}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno, pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^flatmap',1}}|Anno], Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
@@ -177,11 +190,13 @@ call(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2]) ->
X = #c_var{name='X'},
B = #c_var{name='B'},
Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]},
- CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true},
+ CC1 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=true}], guard=#c_literal{val=true},
body=#c_cons{anno=[compiler_generated], hd=X, tl=Xs}},
- CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true},
+ CC2 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=false}], guard=#c_literal{val=true},
body=Xs},
- CC3 = #c_clause{pats=[X], guard=#c_literal{val=true},
+ CC3 = #c_clause{anno=Anno, pats=[X], guard=#c_literal{val=true},
body=match_fail(Anno, Err1)},
Case = #c_case{arg=B, clauses = [CC1, CC2, CC3]},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
@@ -192,13 +207,15 @@ call(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2]) ->
op=Loop,
args=[Xs]},
body=Case}}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=1}]},
body=#c_literal{val=[]}},
Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno,
+ pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^filter',1}}|Anno], Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
@@ -212,19 +229,20 @@ call(#c_call{anno=Anno}, lists, foldl, [Arg1,Arg2,Arg3]) ->
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
A = #c_var{name='A'},
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_apply{anno=Anno,
op=Loop,
args=[Xs, #c_apply{anno=Anno,
op=F,
args=[X, A]}]}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno, pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=2}]},
body=A},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^foldl',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
@@ -238,19 +256,20 @@ call(#c_call{anno=Anno}, lists, foldr, [Arg1,Arg2,Arg3]) ->
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
A = #c_var{name='A'},
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_apply{anno=Anno,
op=F,
args=[X, #c_apply{anno=Anno,
op=Loop,
args=[Xs, A]}]}},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno, pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=2}]},
body=A},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^foldr',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
@@ -266,13 +285,14 @@ call(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3]) ->
Avar = #c_var{name='A'},
Match =
fun (A, P, E) ->
- C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E},
+ C1 = #c_clause{anno=Anno, pats=[P], guard=#c_literal{val=true}, body=E},
Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]},
- C2 = #c_clause{pats=[X], guard=#c_literal{val=true},
+ C2 = #c_clause{anno=Anno, pats=[X], guard=#c_literal{val=true},
body=match_fail(Anno, Err)},
#c_case{arg=A, clauses=[C1, C2]}
end,
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno,
+ pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=Match(#c_apply{anno=Anno, op=F, args=[X, Avar]},
#c_tuple{es=[X, Avar]},
%%% Tuple passing version
@@ -292,7 +312,7 @@ call(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3]) ->
%%% body=#c_values{es=[#c_cons{hd=X, tl=Xs},
%%% A]}}
)},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno, pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=2}]},
@@ -302,7 +322,7 @@ call(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3]) ->
%%% Multiple-value version
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^mapfoldl',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
@@ -326,13 +346,13 @@ call(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3]) ->
Avar = #c_var{name='A'},
Match =
fun (A, P, E) ->
- C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E},
+ C1 = #c_clause{anno=Anno, pats=[P], guard=#c_literal{val=true}, body=E},
Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]},
- C2 = #c_clause{pats=[X], guard=#c_literal{val=true},
+ C2 = #c_clause{anno=Anno, pats=[X], guard=#c_literal{val=true},
body=match_fail(Anno, Err)},
#c_case{arg=A, clauses=[C1, C2]}
end,
- C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
+ C1 = #c_clause{anno=Anno, pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
%%% Tuple passing version
body=Match(#c_apply{anno=Anno,
op=Loop,
@@ -352,7 +372,8 @@ call(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3]) ->
%%% #c_values{es=[#c_cons{hd=X, tl=Xs},
%%% A]})}
},
- C2 = #c_clause{pats=[#c_literal{val=[]}],
+ C2 = #c_clause{anno=Anno,
+ pats=[#c_literal{val=[]}],
guard=#c_call{module=#c_literal{val=erlang},
name=#c_literal{val=is_function},
args=[F, #c_literal{val=2}]},
@@ -362,7 +383,7 @@ call(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3]) ->
%%% Multiple-value version
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]},
- C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
+ C3 = #c_clause{anno=Anno, pats=[Xs], guard=#c_literal{val=true},
body=match_fail([{function_name,{'lists^mapfoldr',2}}|Anno], Err)},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 7452466666..dade5d20d5 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -33,7 +33,7 @@
other_output/1, kernel_listing/1, encrypted_abstr/1,
strict_record/1, utf8_atoms/1, utf8_functions/1, extra_chunks/1,
cover/1, env/1, core_pp/1, tuple_calls/1,
- core_roundtrip/1, asm/1, optimized_guards/1,
+ core_roundtrip/1, asm/1,
sys_pre_attributes/1, dialyzer/1,
warnings/1, pre_load_check/1, env_compiler_options/1,
bc_options/1, deterministic_include/1, deterministic_paths/1
@@ -50,7 +50,7 @@ all() ->
binary, makedep, cond_and_ifdef, listings, listings_big,
other_output, kernel_listing, encrypted_abstr, tuple_calls,
strict_record, utf8_atoms, utf8_functions, extra_chunks,
- cover, env, core_pp, core_roundtrip, asm, optimized_guards,
+ cover, env, core_pp, core_roundtrip, asm,
sys_pre_attributes, dialyzer, warnings, pre_load_check,
env_compiler_options, custom_debug_info, bc_options,
custom_compile_info, deterministic_include, deterministic_paths].
@@ -1174,85 +1174,6 @@ do_asm(Beam, Outdir) ->
error
end.
-%% Make sure that guards are fully optimized. Guards should
-%% should use 'test' instructions, not 'bif' instructions.
-
-optimized_guards(_Config) ->
- TestBeams = get_unique_beam_files(),
- test_lib:p_run(fun(F) -> do_opt_guards(F) end, TestBeams).
-
-do_opt_guards(Beam) ->
- {ok,{M,[{abstract_code,{raw_abstract_v1,A}}]}} =
- beam_lib:chunks(Beam, [abstract_code]),
- try
- {ok,M,Asm} = compile:forms(A, ['S']),
- do_opt_guards_mod(Asm)
- catch Class:Error:Stk ->
- io:format("~p: ~p ~p\n~p\n", [M,Class,Error,Stk]),
- error
- end.
-
-do_opt_guards_mod({Mod,_Exp,_Attr,Asm,_NumLabels}) ->
- case do_opt_guards_fs(Mod, Asm) of
- [] ->
- ok;
- [_|_]=Bifs ->
- io:format("ERRORS FOR ~p:\n~p\n", [Mod,Bifs]),
- error
- end.
-
-do_opt_guards_fs(Mod, [{function,Name,Arity,_,Is}|Fs]) ->
- Bifs0 = do_opt_guards_fun(Is),
-
- %% The compiler does not attempt to optimize 'xor'.
- %% Therefore, ignore all functions that use 'xor' in
- %% a guard.
- Bifs = case lists:any(fun({bif,'xor',_,_,_}) -> true;
- (_) -> false
- end, Bifs0) of
- true -> [];
- false -> Bifs0
- end,
-
- %% Filter out the allowed exceptions.
- FA = {Name,Arity},
- case {Bifs,is_exception(Mod, FA)} of
- {[_|_],true} ->
- io:format("~p:~p/~p IGNORED:\n~p\n",
- [Mod,Name,Arity,Bifs]),
- do_opt_guards_fs(Mod, Fs);
- {[_|_],false} ->
- [{FA,Bifs}|do_opt_guards_fs(Mod, Fs)];
- {[],false} ->
- do_opt_guards_fs(Mod, Fs);
- {[],true} ->
- io:format("Redundant exception for ~p:~p/~p\n",
- [Mod,Name,Arity]),
- error(redundant)
- end;
-do_opt_guards_fs(_, []) -> [].
-
-do_opt_guards_fun([{bif,Name,{f,F},As,_}=I|Is]) when F =/= 0 ->
- Arity = length(As),
- case erl_internal:comp_op(Name, Arity) orelse
- erl_internal:bool_op(Name, Arity) orelse
- erl_internal:new_type_test(Name, Arity) of
- true ->
- [I|do_opt_guards_fun(Is)];
- false ->
- do_opt_guards_fun(Is)
- end;
-do_opt_guards_fun([_|Is]) ->
- do_opt_guards_fun(Is);
-do_opt_guards_fun([]) -> [].
-
-is_exception(guard_SUITE, {'-complex_not/1-fun-4-',1}) -> true;
-is_exception(guard_SUITE, {'-complex_not/1-fun-5-',1}) -> true;
-is_exception(guard_SUITE, {bad_guards,1}) -> true;
-is_exception(guard_SUITE, {nested_not_2b,6}) -> true; %% w/o type optimization
-is_exception(guard_SUITE, {nested_not_2b,2}) -> true; %% with type optimization
-is_exception(_, _) -> false.
-
sys_pre_attributes(Config) ->
DataDir = proplists:get_value(data_dir, Config),
File = filename:join(DataDir, "attributes.erl"),
@@ -1469,44 +1390,49 @@ env_compiler_options(_Config) ->
bc_options(Config) ->
DataDir = proplists:get_value(data_dir, Config),
- 101 = highest_opcode(DataDir, small_float, [no_get_hd_tl,no_line_info]),
-
- 103 = highest_opcode(DataDir, big,
- [no_put_tuple2,
- no_get_hd_tl,no_ssa_opt_record,
- no_line_info,no_stack_trimming]),
-
- 125 = highest_opcode(DataDir, small_float,
- [no_get_hd_tl,no_line_info,no_ssa_opt_float]),
-
- 132 = highest_opcode(DataDir, small,
- [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
- no_ssa_opt_float,no_line_info,no_bsm3]),
-
- 153 = highest_opcode(DataDir, small, [r20]),
- 153 = highest_opcode(DataDir, small, [r21]),
-
- 136 = highest_opcode(DataDir, big, [no_put_tuple2,no_get_hd_tl,
- no_ssa_opt_record,no_line_info]),
-
- 153 = highest_opcode(DataDir, big, [no_put_tuple2,no_get_hd_tl,
- no_ssa_opt_record]),
- 153 = highest_opcode(DataDir, big, [r16]),
- 153 = highest_opcode(DataDir, big, [r17]),
- 153 = highest_opcode(DataDir, big, [r18]),
- 153 = highest_opcode(DataDir, big, [r19]),
- 153 = highest_opcode(DataDir, small_float, [r16]),
- 153 = highest_opcode(DataDir, small_float, []),
-
- 158 = highest_opcode(DataDir, small_maps, [r17]),
- 158 = highest_opcode(DataDir, small_maps, [r18]),
- 158 = highest_opcode(DataDir, small_maps, [r19]),
- 158 = highest_opcode(DataDir, small_maps, [r20]),
- 158 = highest_opcode(DataDir, small_maps, [r21]),
-
- 164 = highest_opcode(DataDir, small_maps, []),
- 164 = highest_opcode(DataDir, big, []),
-
+ L = [{101, small_float, [no_get_hd_tl,no_line_info]},
+ {103, big, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
+ no_line_info,no_stack_trimming]},
+ {125, small_float, [no_get_hd_tl,no_line_info,no_ssa_opt_float]},
+
+ {132, small, [no_put_tuple2,no_get_hd_tl,no_ssa_opt_record,
+ no_ssa_opt_float,no_line_info,no_bsm3]},
+
+ {153, small, [r20]},
+ {153, small, [r21]},
+
+ {136, big, [no_put_tuple2,no_get_hd_tl,
+ no_ssa_opt_record,no_line_info]},
+
+ {153, big, [no_put_tuple2,no_get_hd_tl, no_ssa_opt_record]},
+ {153, big, [r16]},
+ {153, big, [r17]},
+ {153, big, [r18]},
+ {153, big, [r19]},
+ {153, small_float, [r16]},
+ {153, small_float, []},
+
+ {158, small_maps, [r17]},
+ {158, small_maps, [r18]},
+ {158, small_maps, [r19]},
+ {158, small_maps, [r20]},
+ {158, small_maps, [r21]},
+
+ {164, small_maps, []},
+ {164, big, []}
+ ],
+
+ Test = fun({Expected,Mod,Options}) ->
+ case highest_opcode(DataDir, Mod, Options) of
+ Expected ->
+ ok;
+ Got ->
+ io:format("*** module ~p, options ~p => got ~p; expected ~p\n",
+ [Mod,Options,Got,Expected]),
+ error
+ end
+ end,
+ test_lib:p_run(Test, L),
ok.
highest_opcode(DataDir, Mod, Opt) ->
diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl
index 69c9dcba69..f700059d20 100644
--- a/lib/compiler/test/inline_SUITE.erl
+++ b/lib/compiler/test/inline_SUITE.erl
@@ -42,13 +42,9 @@ groups() ->
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
- Pa = "-pa " ++ filename:dirname(code:which(?MODULE)),
- {ok,Node} = start_node(compiler, Pa),
- [{testing_node,Node}|Config].
+ Config.
-end_per_suite(Config) ->
- Node = proplists:get_value(testing_node, Config),
- test_server:stop_node(Node),
+end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
@@ -89,7 +85,6 @@ attribute(Config) when is_list(Config) ->
?comp(maps_inline_test).
try_inline(Mod, Config) ->
- Node = proplists:get_value(testing_node, Config),
Src = filename:join(proplists:get_value(data_dir, Config),
atom_to_list(Mod)),
Out = proplists:get_value(priv_dir,Config),
@@ -100,7 +95,7 @@ try_inline(Mod, Config) ->
bin_opt_info,clint,ssalint]),
ct:timetrap({minutes,10}),
- NormalResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]),
+ NormalResult = load_and_call(Out, Mod),
%% Inlining.
io:format("Compiling with old inliner: ~s\n", [Src]),
@@ -109,7 +104,7 @@ try_inline(Mod, Config) ->
%% Run inlined code.
ct:timetrap({minutes,10}),
- OldInlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]),
+ OldInlinedResult = load_and_call(Out, Mod),
%% Compare results.
compare(NormalResult, OldInlinedResult),
@@ -122,7 +117,7 @@ try_inline(Mod, Config) ->
%% Run inlined code.
ct:timetrap({minutes,10}),
- InlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]),
+ InlinedResult = load_and_call(Out, Mod),
%% Compare results.
compare(NormalResult, InlinedResult),
@@ -131,6 +126,11 @@ try_inline(Mod, Config) ->
%% Delete Beam file.
ok = file:delete(filename:join(Out, atom_to_list(Mod)++code:objfile_extension())),
+ %% Delete loaded module.
+ _ = code:purge(Mod),
+ _ = code:delete(Mod),
+ _ = code:purge(Mod),
+
ok.
compare(Same, Same) -> ok;
@@ -144,12 +144,6 @@ compare([H1|_], [H2|_]) ->
ct:fail(different);
compare([], []) -> ok.
-start_node(Name, Args) ->
- case test_server:start_node(Name, slave, [{args,Args}]) of
- {ok,Node} -> {ok, Node};
- Error -> ct:fail(Error)
- end.
-
load_and_call(Out, Module) ->
io:format("Loading...\n",[]),
code:purge(Module),
diff --git a/lib/compiler/test/inline_SUITE_data/barnes2.erl b/lib/compiler/test/inline_SUITE_data/barnes2.erl
index a986331060..49e9bdfb6b 100644
--- a/lib/compiler/test/inline_SUITE_data/barnes2.erl
+++ b/lib/compiler/test/inline_SUITE_data/barnes2.erl
@@ -6,7 +6,7 @@
?MODULE() ->
Stars = create_scenario(1000, 1.0),
R = hd(loop(10,1000.0,Stars,0)),
- Str = lists:flatten(io:lib_format("~s", [R])),
+ Str = lists:flatten(io_lib:format("~p", [R])),
{R,Str =:= {1.00000,-1.92269e+4,-1.92269e+4,2.86459e-2,2.86459e-2}}.
create_scenario(N, M) ->
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 60ab969929..94bfbb0efe 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -25,7 +25,7 @@
match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1,
selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1,
coverage/1,grab_bag/1,literal_binary/1,
- unary_op/1,eq_types/1]).
+ unary_op/1,eq_types/1,match_after_return/1]).
-include_lib("common_test/include/ct.hrl").
@@ -40,7 +40,8 @@ groups() ->
match_in_call,untuplify,
shortcut_boolean,letify_guard,selectify,deselectify,
underscore,match_map,map_vars_used,coverage,
- grab_bag,literal_binary,unary_op,eq_types]}].
+ grab_bag,literal_binary,unary_op,eq_types,
+ match_after_return]}].
init_per_suite(Config) ->
@@ -890,5 +891,15 @@ eq_types(A, B) ->
Ref22.
+match_after_return(Config) when is_list(Config) ->
+ %% The return type of the following call will never match the 'wont_happen'
+ %% clauses below, and the beam_ssa_type was clever enough to see that but
+ %% didn't remove the blocks, so it crashed when trying to extract A.
+ ok = case mar_test_tuple(erlang:unique_integer()) of
+ {gurka, never_matches, A} -> {wont_happen, A};
+ _ -> ok
+ end.
+
+mar_test_tuple(I) -> {gurka, I}.
id(I) -> I.
diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl
index 26149e11e6..7fb4751b42 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -104,11 +104,11 @@ is_cloned_mod(Mod) ->
%% Test whether Mod is a cloned module.
-is_cloned_mod_1("no_opt_SUITE") -> true;
-is_cloned_mod_1("post_opt_SUITE") -> true;
-is_cloned_mod_1("inline_SUITE") -> true;
-is_cloned_mod_1("21_SUITE") -> true;
-is_cloned_mod_1("no_module_opt_SUITE") -> true;
+is_cloned_mod_1("_no_opt_SUITE") -> true;
+is_cloned_mod_1("_post_opt_SUITE") -> true;
+is_cloned_mod_1("_inline_SUITE") -> true;
+is_cloned_mod_1("_21_SUITE") -> true;
+is_cloned_mod_1("_no_module_opt_SUITE") -> true;
is_cloned_mod_1([_|T]) -> is_cloned_mod_1(T);
is_cloned_mod_1([]) -> false.