summaryrefslogtreecommitdiff
path: root/lib/compiler/src/sys_core_fold.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src/sys_core_fold.erl')
-rw-r--r--lib/compiler/src/sys_core_fold.erl202
1 files changed, 58 insertions, 144 deletions
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index fb23fd7d63..024b2d5f2a 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -93,6 +93,9 @@
-define(ASSERT(E), ignore).
-endif.
+-define(MAX_FUNC_ARGS, 255).
+-define(IS_FUNC_ARITY(A), is_integer(A) andalso 0 =< A andalso A =< ?MAX_FUNC_ARGS).
+
%% Variable value info.
-record(sub, {v=[], %Variable substitutions
s=sets:new([{version, 2}]) :: sets:set(), %Variables in scope
@@ -269,17 +272,8 @@ expr(#c_seq{arg=Arg0,body=B0}=Seq0, Ctxt, Sub) ->
end;
expr(#c_let{}=Let0, Ctxt, Sub) ->
Let = opt_case_in_let(Let0),
- case simplify_let(Let, Sub) of
- impossible ->
- %% The argument for the let is "simple", i.e. has no
- %% complex structures such as let or seq that can be entered.
- ?ASSERT(verify_scope(Let, Sub)),
- opt_fun_call(opt_simple_let(Let, Ctxt, Sub));
- Expr ->
- %% The let body was successfully moved into the let argument.
- %% Now recursively re-process the new expression.
- Expr
- end;
+ ?ASSERT(verify_scope(Let, Sub)),
+ opt_fun_call(opt_let(Let, Ctxt, Sub));
expr(#c_letrec{body=#c_var{}}=Letrec, effect, _Sub) ->
%% This is named fun in an 'effect' context. Warn and ignore.
add_warning(Letrec, {ignored,useless_building}),
@@ -409,7 +403,9 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0)
{Evs1,Sub2} = var_list(Evs0, Sub0),
H1 = body(H0, value, Sub2),
Try#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1}
- end.
+ end;
+expr(#c_opaque{}=O, effect, _Sub) ->
+ O.
%% If a fun or its application is used as an argument, then it's unsafe to
%% handle it in effect context as the side-effects may rely on its return
@@ -802,8 +798,6 @@ fold_apply(Apply, _, _) -> Apply.
call(#c_call{args=As0}=Call0, #c_literal{val=M}=M0, #c_literal{val=N}=N0, Sub) ->
As1 = expr_list(As0, value, Sub),
case simplify_call(Call0, M, N, As1) of
- #c_literal{}=Lit ->
- Lit;
#c_call{args=As}=Call ->
case get(no_inline_list_funcs) of
true ->
@@ -813,7 +807,11 @@ call(#c_call{args=As0}=Call0, #c_literal{val=M}=M0, #c_literal{val=N}=N0, Sub) -
none -> fold_call(Call, M0, N0, As, Sub);
Core -> expr(Core, Sub)
end
- end
+ end;
+ #c_let{}=Let ->
+ Let;
+ #c_literal{}=Lit ->
+ Lit
end;
call(#c_call{args=As0}=Call, M, N, Sub) ->
As = expr_list(As0, value, Sub),
@@ -823,6 +821,33 @@ call(#c_call{args=As0}=Call, M, N, Sub) ->
%% slightly at the cost of making tracing and stack traces incorrect.
simplify_call(Call, maps, get, [Key, Map]) ->
rewrite_call(Call, erlang, map_get, [Key, Map]);
+simplify_call(#c_call{anno=Anno0}, maps, get, [Key0, Map, Default]) ->
+ Anno = [compiler_generated | Anno0],
+
+ Key = make_var(Anno),
+ Value = make_var(Anno),
+ Fail = make_var(Anno),
+ Raise = #c_primop{name=#c_literal{val=match_fail},
+ args=[#c_tuple{es=[#c_literal{val=badmap},
+ Fail]}]},
+
+ Cs = [#c_clause{anno=Anno,
+ pats=[#c_map{es=[#c_map_pair{op=#c_literal{val=exact},
+ key=Key,
+ val=Value}],
+ is_pat=true}],
+ guard=#c_literal{val=true},
+ body=Value},
+ #c_clause{anno=Anno,
+ pats=[#c_map{es=[],is_pat=true}],
+ guard=#c_literal{val=true},
+ body=Default},
+ #c_clause{anno=Anno,
+ pats=[Fail],
+ guard=#c_literal{val=true},
+ body=Raise}],
+
+ cerl:ann_c_let(Anno, [Key], Key0, #c_case{anno=Anno,arg=Map,clauses=Cs});
simplify_call(Call, maps, is_key, [Key, Map]) ->
rewrite_call(Call, erlang, is_map_key, [Key, Map]);
simplify_call(_Call, maps, new, []) ->
@@ -1864,6 +1889,7 @@ case_opt_data_2(P, TypeSig, Bs0) ->
{[V|Vs],none} ->
{Type,Arity} = TypeSig,
Ann = [compiler_generated],
+ true = ?IS_FUNC_ARITY(Arity),
Vars = make_vars(Ann, Arity),
Data = cerl:ann_make_data(Ann, Type, Vars),
Bs = [{V,Data} | [{Var,V} || Var <- Vs] ++ Bs0],
@@ -1921,7 +1947,7 @@ pat_to_expr(P) ->
pat_to_expr_list(Ps) -> [pat_to_expr(P) || P <- Ps].
-make_vars(A, Max) ->
+make_vars(A, Max) when ?IS_FUNC_ARITY(Max) ->
make_vars(A, 1, Max).
make_vars(A, I, Max) when I =< Max ->
@@ -2183,112 +2209,6 @@ simplify_fun_call(V, Values, #c_fun{vars=Vars,body=FunBody}, CallArgs) ->
throw(impossible)
end.
-%% simplify_let(Let, Sub) -> Expr | impossible
-%% If the argument part of an let contains a complex expression, such
-%% as a let or a sequence, move the original let body into the complex
-%% expression.
-
-simplify_let(#c_let{arg=Arg}=Let, Sub) ->
- move_let_into_expr(Let, Arg, Sub).
-
-move_let_into_expr(#c_let{vars=InnerVs0,body=InnerBody0}=Inner,
- #c_let{vars=OuterVs0,arg=Arg0,body=OuterBody0}=Outer, Sub0) ->
- %%
- %% let <InnerVars> = let <OuterVars> = <Arg>
- %% in <OuterBody>
- %% in <InnerBody>
- %%
- %% ==>
- %%
- %% let <OuterVars> = <Arg>
- %% in let <InnerVars> = <OuterBody>
- %% in <InnerBody>
- %%
- Arg = body(Arg0, Sub0),
- ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}),
- {OuterVs,ScopeSub} = var_list(OuterVs0, ScopeSub0),
-
- OuterBody = body(OuterBody0, ScopeSub),
-
- {InnerVs,Sub} = var_list(InnerVs0, Sub0),
- InnerBody = body(InnerBody0, Sub),
- Outer#c_let{vars=OuterVs,arg=Arg,
- body=Inner#c_let{vars=InnerVs,arg=OuterBody,body=InnerBody}};
-move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
- #c_case{arg=Cexpr0,clauses=[Ca0|Cs0]}=Case, Sub0) ->
- case not is_failing_clause(Ca0) andalso
- are_all_failing_clauses(Cs0) of
- true ->
- %% let <Lvars> = case <Case-expr> of
- %% <Cpats> -> <Clause-body>;
- %% <OtherCpats> -> erlang:error(...)
- %% end
- %% in <Let-body>
- %%
- %% ==>
- %%
- %% case <Case-expr> of
- %% <Cpats> ->
- %% let <Lvars> = <Clause-body>
- %% in <Let-body>;
- %% <OtherCpats> -> erlang:error(...)
- %% end
-
- Cexpr = body(Cexpr0, Sub0),
- CaPats0 = Ca0#c_clause.pats,
- G0 = Ca0#c_clause.guard,
- B0 = Ca0#c_clause.body,
- ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}),
- try pattern_list(CaPats0, ScopeSub0) of
- {CaPats,ScopeSub} ->
- G = guard(G0, ScopeSub),
-
- B1 = body(B0, ScopeSub),
-
- {Lvs,B2,Sub1} = let_substs(Lvs0, B1, Sub0),
- Sub2 = Sub1#sub{s=sets:union(ScopeSub#sub.s,
- Sub1#sub.s)},
- Lbody = body(Lbody0, Sub2),
- B = Let#c_let{vars=Lvs,
- arg=core_lib:make_values(B2),
- body=Lbody},
-
- Ca = Ca0#c_clause{pats=CaPats,guard=G,body=B},
- Cs = [clause(C, Cexpr, value, Sub0) || C <- Cs0],
- Case#c_case{arg=Cexpr,clauses=[Ca|Cs]}
- catch
- nomatch ->
- %% This is not a defeat. The code will eventually
- %% be optimized to erlang:error(...) by the other
- %% optimizations done in this module.
- impossible
- end;
- false -> impossible
- end;
-move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let,
- #c_seq{arg=Sarg0,body=Sbody0}=Seq, Sub0) ->
- %%
- %% let <Lvars> = do <Seq-arg>
- %% <Seq-body>
- %% in <Let-body>
- %%
- %% ==>
- %%
- %% do <Seq-arg>
- %% let <Lvars> = <Seq-body>
- %% in <Let-body>
- %%
- Sarg = body(Sarg0, Sub0),
- Sbody1 = body(Sbody0, Sub0),
- {Lvs,Sbody,Sub} = let_substs(Lvs0, Sbody1, Sub0),
- Lbody = body(Lbody0, Sub),
- Seq#c_seq{arg=Sarg,body=Let#c_let{vars=Lvs,arg=core_lib:make_values(Sbody),
- body=Lbody}};
-move_let_into_expr(_Let, _Expr, _Sub) -> impossible.
-
-are_all_failing_clauses(Cs) ->
- all(fun is_failing_clause/1, Cs).
-
is_failing_clause(#c_clause{body=B}) ->
will_fail(B).
@@ -2441,6 +2361,7 @@ delay_build_1(Core0, TypeSig) ->
Core ->
{Type,Arity} = TypeSig,
Ann = [compiler_generated],
+ true = ?IS_FUNC_ARITY(Arity),
Vars = make_vars(Ann, Arity),
Data = cerl:ann_make_data(Ann, Type, Vars),
{yes,Vars,Core,Data}
@@ -2483,42 +2404,41 @@ delay_build_expr_1(Core, _TypeSig) ->
false -> throw(impossible)
end.
-%% opt_simple_let(#c_let{}, Context, Sub) -> CoreTerm
-%% Optimize a let construct that does not contain any lets in
-%% in its argument.
+%% opt_let(#c_let{}, Context, Sub) -> CoreTerm
+%% Optimize a let construct.
-opt_simple_let(Let0, Ctxt, Sub) ->
+opt_let(Let0, Ctxt, Sub) ->
case opt_not_in_let(Let0) of
#c_let{}=Let ->
- opt_simple_let_0(Let, Ctxt, Sub);
+ opt_let_0(Let, Ctxt, Sub);
Expr ->
expr(Expr, Ctxt, Sub)
end.
-opt_simple_let_0(#c_let{arg=Arg0}=Let, Ctxt, Sub) ->
+opt_let_0(#c_let{arg=Arg0}=Let, Ctxt, Sub) ->
Arg = body(Arg0, value, Sub), %This is a body
case will_fail(Arg) of
true -> Arg;
- false -> opt_simple_let_1(Let, Arg, Ctxt, Sub)
+ false -> opt_let_1(Let, Arg, Ctxt, Sub)
end.
-opt_simple_let_1(#c_let{vars=Vs0,body=B0}=Let, Arg0, Ctxt, Sub0) ->
+opt_let_1(#c_let{vars=Vs0,body=B0}=Let, Arg0, Ctxt, Sub0) ->
%% Optimise let and add new substitutions.
{Vs,Args,Sub1} = let_substs(Vs0, Arg0, Sub0),
BodySub = update_let_types(Vs, Args, Sub1),
Sub = Sub1#sub{v=[],s=sets:new([{version, 2}])},
B = body(B0, Ctxt, BodySub),
Arg = core_lib:make_values(Args),
- opt_simple_let_2(Let, Vs, Arg, B, B0, Sub).
+ opt_let_2(Let, Vs, Arg, B, B0, Sub).
-%% opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) -> Core.
+%% opt_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) -> Core.
%% Do final simplifications of the let.
%%
%% Note that the substitutions and scope in Sub have been cleared
%% and should not be used.
-opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Sub) ->
+opt_let_2(Let0, Vs0, Arg0, Body, PrevBody, Sub) ->
case {Vs0,Arg0,Body} of
{[#c_var{name=V}],Arg1,#c_var{name=V}} ->
%% let <Var> = Arg in <Var> ==> Arg
@@ -2704,17 +2624,11 @@ update_types(_, _, Sub) -> Sub.
%% Kill any entries that references the variable,
%% either in the key or in the value.
-kill_types(V, Tdb) ->
- maps:from_list(kill_types2(V,maps:to_list(Tdb))).
-
-kill_types2(V, [{V,_}|Tdb]) ->
- kill_types2(V, Tdb);
-kill_types2(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) ->
- case core_lib:is_var_used(V, Tuple) of
- false -> [Entry|kill_types2(V, Tdb)];
- true -> kill_types2(V, Tdb)
- end;
-kill_types2(_, []) -> [].
+kill_types(Var, Tdb) ->
+ #{Key => Value ||
+ Key := Value <- Tdb,
+ Key =/= Var,
+ not core_lib:is_var_used(Var, Value)}.
%% copy_type(DestVar, SrcVar, Tdb) -> Tdb'
%% If the SrcVar has a type, assign it to DestVar.