summaryrefslogtreecommitdiff
path: root/lib/compiler/src/v3_kernel.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src/v3_kernel.erl')
-rw-r--r--lib/compiler/src/v3_kernel.erl165
1 files changed, 95 insertions, 70 deletions
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 79352e33ad..febc971427 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2021. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -78,10 +78,9 @@
-export([module/2,format_error/1]).
-import(lists, [all/2,droplast/1,flatten/1,foldl/3,foldr/3,
- map/2,mapfoldl/3,member/2,
- keyfind/3,keyreplace/4,
- last/1,partition/2,reverse/1,
- sort/1,sort/2,splitwith/2]).
+ map/2,mapfoldl/3,member/2,keyfind/3,last/1,
+ partition/2,reverse/1,sort/1,sort/2,
+ splitwith/2]).
-import(ordsets, [add_element/2,intersection/2,
subtract/2,union/2,union/1]).
@@ -234,25 +233,19 @@ gexpr_test_add(Ke, St0) ->
%% expr(Cexpr, Sub, State) -> {Kexpr,[PreKexpr],State}.
%% Convert a Core expression, flattening it at the same time.
-expr(#c_var{anno=A0,name={Name,Arity}}=Fname, Sub, St) ->
- Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
- V <- integers(1, Arity)],
+expr(#c_var{anno=A,name={Name0,Arity}}=Fname, Sub, St) ->
case St#kern.no_shared_fun_wrappers of
false ->
- %% Generate a (possibly shared) wrapper function for calling
- %% this function.
- Wrapper0 = ["-fun.",atom_to_list(Name),"/",integer_to_list(Arity),"-"],
- Wrapper = list_to_atom(flatten(Wrapper0)),
- Id = {id,{0,0,Wrapper}},
- A = keyreplace(id, 1, A0, Id),
- Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{anno=A,op=Fname,args=Vs}},
- expr(Fun, Sub, St);
+ Name = get_fsub(Name0, Arity, Sub),
+ {#k_local{anno=A,name=Name,arity=Arity},[],St};
true ->
%% For backward compatibility with OTP 22 and earlier,
%% use the pre-generated name for the fun wrapper.
%% There will be one wrapper function for each occurrence
%% of `fun F/A`.
- Fun = #c_fun{anno=A0,vars=Vs,body=#c_apply{anno=A0,op=Fname,args=Vs}},
+ Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
+ V <- integers(1, Arity)],
+ Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{anno=A,op=Fname,args=Vs}},
expr(Fun, Sub, St)
end;
expr(#c_var{anno=A,name=V}, Sub, St) ->
@@ -337,7 +330,7 @@ expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
{#k_call{anno=A,op=Remote,args=Kargs},Ap,St1};
error ->
%% Invalid call (e.g. M:42/3). Issue a warning, and let
- %% the generated code use the old explict apply.
+ %% the generated code use the old explicit apply.
St = add_warning(get_location(A), {failed,bad_call}, A, St0),
Call = #c_call{anno=A,
module=#c_literal{val=erlang},
@@ -392,57 +385,74 @@ letrec_local_function(A, Cfs, Cb, Sub0, St0) ->
%% Implement letrec with the single definition as a label and each
%% apply of it as a goto.
-letrec_goto([{#c_var{name={Label,0}},Cfail}], Cb, Sub0,
+letrec_goto([{#c_var{name={Label,_Arity}},Cfail}], Cb, Sub0,
#kern{labels=Labels0}=St0) ->
+ #c_fun{vars=FunVars,body=FunBody} = Cfail,
+ {Kvars,{FunSub,St1}} =
+ mapfoldl(fun(#c_var{anno=A,name=V}, {SubInt,StInt0}) ->
+ {New,StInt1} = new_var_name(StInt0),
+ {#k_var{anno=A,name=New},
+ {set_vsub(V, New, SubInt),
+ StInt1#kern{ds=sets:add_element(New, StInt1#kern.ds)}}}
+ end, {Sub0,St0}, FunVars),
Labels = sets:add_element(Label, Labels0),
- {Kb,Pb,St1} = body(Cb, Sub0, St0#kern{labels=Labels}),
- #c_fun{body=FailBody} = Cfail,
- {Kfail,Fb,St2} = body(FailBody, Sub0, St1),
+ {Kb,Pb,St2} = body(Cb, Sub0, St1#kern{labels=Labels}),
+ {Kfail,Fb,St3} = body(FunBody, FunSub, St2),
case {Kb,Kfail,Fb} of
{#k_goto{label=Label},#k_goto{}=InnerGoto,[]} ->
- {InnerGoto,Pb,St2};
+ {InnerGoto,Pb,St3};
{_,_,_} ->
- St3 = St2#kern{labels=Labels0},
- Alt = #k_letrec_goto{label=Label,first=Kb,then=pre_seq(Fb, Kfail)},
- {Alt,Pb,St3}
+ St4 = St3#kern{labels=Labels0},
+ Alt = #k_letrec_goto{label=Label,vars=Kvars,
+ first=Kb,then=pre_seq(Fb, Kfail)},
+ {Alt,Pb,St4}
end.
%% translate_match_fail(Arg, Sub, Anno, St) -> {Kexpr,[PreKexpr],State}.
-%% Translate a match_fail primop to a call erlang:error/1 or
-%% erlang:error/2.
+%% Translate match_fail primops, paying extra attention to `function_clause`
+%% errors that may have been inlined from other functions.
translate_match_fail(Arg, Sub, Anno, St0) ->
- Cargs = case {cerl:data_type(Arg),cerl:data_es(Arg)} of
- {tuple,[#c_literal{val=function_clause}|As]} ->
- translate_fc_args(As, Sub, St0);
- {_,_} ->
- [Arg]
- end,
- {Kargs,Ap,St} = atomic_list(Cargs, Sub, St0),
+ {Cargs,ExtraAnno,St1} =
+ case {cerl:data_type(Arg),cerl:data_es(Arg)} of
+ {tuple,[#c_literal{val=function_clause} | _]=As} ->
+ translate_fc_args(As, Sub, Anno, St0);
+ {tuple,[#c_literal{} | _]=As} ->
+ {As,[],St0};
+ {{atomic,Reason}, []} ->
+ {[#c_literal{val=Reason}],[],St0}
+ end,
+ {Kargs,Ap,St} = atomic_list(Cargs, Sub, St1),
Ar = length(Cargs),
- Call = #k_call{anno=Anno,
- op=#k_remote{mod=#k_literal{val=erlang},
- name=#k_literal{val=error},
- arity=Ar},args=Kargs},
- {Call,Ap,St}.
-
-translate_fc_args(As, Sub, #kern{fargs=Fargs}) ->
- case same_args(As, Fargs, Sub) of
- true ->
- %% The arguments for the `function_clause` exception are
- %% the arguments for the current function in the correct
- %% order.
- [#c_literal{val=function_clause},cerl:make_list(As)];
- false ->
- %% The arguments in the `function_clause` exception don't
- %% match the arguments for the current function because
- %% of inlining. Keeping the `function_clause`
- %% exception reason would be confusing. Rewrite it to
- %% a `case_clause` exception with the arguments in a
- %% tuple.
- [cerl:c_tuple([#c_literal{val=case_clause},
- cerl:c_tuple(As)])]
- end.
+ Primop = #k_bif{anno=ExtraAnno ++ Anno,
+ op=#k_internal{name=match_fail,arity=Ar},
+ args=Kargs},
+ {Primop,Ap,St}.
+
+translate_fc_args(As, Sub, Anno, #kern{fargs=Fargs}=St0) ->
+ {ExtraAnno, St} =
+ case same_args(As, Fargs, Sub) of
+ true ->
+ %% The arguments for the `function_clause` exception are
+ %% the arguments for the current function in the correct
+ %% order.
+ {[], St0};
+ false ->
+ %% The arguments in the `function_clause` exception don't
+ %% match the arguments for the current function because of
+ %% inlining.
+ case keyfind(function, 1, Anno) of
+ false ->
+ {Name, St1} = new_fun_name("inlined", St0),
+ {[{inlined,{Name,length(As) - 1}}], St1};
+ {_,{Name0,Arity}} ->
+ %% This is function that has been inlined.
+ Name1 = ["-inlined-",Name0,"/",Arity,"-"],
+ Name = list_to_atom(lists:concat(Name1)),
+ {[{inlined,{Name,Arity}}], St0}
+ end
+ end,
+ {As, ExtraAnno, St}.
same_args([#c_var{name=Cv}|Vs], [#k_var{name=Kv}|As], Sub) ->
get_vsub(Cv, Sub) =:= Kv andalso same_args(Vs, As, Sub);
@@ -559,10 +569,11 @@ match_vars(Ka, St0) ->
%% Transform application.
c_apply(A, #c_var{anno=Ra,name={F0,Ar}}, Cargs, Sub, #kern{labels=Labels}=St0) ->
- case Ar =:= 0 andalso sets:is_element(F0, Labels) of
+ case sets:is_element(F0, Labels) of
true ->
%% This is a goto to a label in a letrec_goto construct.
- {#k_goto{label=F0},[],St0};
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ {#k_goto{label=F0,args=Kargs},Ap,St1};
false ->
{Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
F1 = get_fsub(F0, Ar, Sub), %Has it been rewritten
@@ -1216,7 +1227,7 @@ expand_pat_lit(Lit, A) ->
%% comparing with the literal (that is especially true for binaries).
%%
%% It is important not to do this transformation for atomic literals
-%% (such as `[]`), since that would cause the test for an emtpy list
+%% (such as `[]`), since that would cause the test for an empty list
%% to be executed before the test for a nonempty list.
opt_single_valued(Ttcs) ->
@@ -1610,7 +1621,7 @@ new_clauses(Cs0, U, St) ->
%% will be grouped next.
%%
%% We also try to not create too large groups. If we have too many clauses,
-%% it is preferrable to match on 8-bits, select a branch, then match on the
+%% it is preferable to match on 8-bits, select a branch, then match on the
%% next 8-bits, rather than match on 16-bits which would force us to have
%% to select to many values at the same time, which would not be efficient.
%%
@@ -1823,8 +1834,9 @@ ubody(#ivalues{anno=A,args=As}, return, St) ->
ubody(#ivalues{anno=A,args=As}, {break,_Vbs}, St) ->
Au = lit_list_vars(As),
{#k_break{anno=A,args=As},Au,St};
-ubody(#k_goto{}=Goto, _Br, St) ->
- {Goto,[],St};
+ubody(#k_goto{args=As}=Goto, _Br, St) ->
+ Au = lit_list_vars(As),
+ {Goto,Au,St};
ubody(E, return, St0) ->
%% Enterable expressions need no trailing return.
case is_enter_expr(E) of
@@ -1991,11 +2003,21 @@ uexpr(#ifun{anno=A,vars=Vs,body=B0}, {break,Rs}, St0) ->
args=[Local|Fvs],
ret=Rs},
Free,add_local_function(Fun, St)};
-uexpr(#k_letrec_goto{anno=A,first=F0,then=T0}=MatchAlt, Br, St0) ->
+uexpr(#k_local{anno=A,name=Name,arity=Arity}, {break,Rs}, St) ->
+ Free = lit_list_vars(get_free(Name, Arity, St)),
+ Fvs = make_vars(Free),
+ FreeCount = length(Fvs),
+ Bif = #k_bif{anno=A,
+ op=#k_internal{name=make_fun,arity=FreeCount+1},
+ args=[#k_local{name=Name,arity=Arity+FreeCount} | Fvs],
+ ret=Rs},
+ {Bif,Free,St};
+uexpr(#k_letrec_goto{anno=A,vars=Vs,first=F0,then=T0}=MatchAlt, Br, St0) ->
Rs = break_rets(Br),
+ Ns = lit_list_vars(Vs),
{F1,Fu,St1} = ubody(F0, Br, St0),
{T1,Tu,St2} = ubody(T0, Br, St1),
- Used = union(Fu, Tu),
+ Used = subtract(union(Fu, Tu), Ns),
{MatchAlt#k_letrec_goto{anno=A,first=F1,then=T1,ret=Rs},Used,St2};
uexpr(Lit, {break,Rs0}, St0) ->
%% Transform literals to puts here.
@@ -2048,13 +2070,16 @@ break_rets(return) -> [].
%% bif_returns(Op, [Ret], State) -> {[Ret],State}.
-bif_returns(#k_remote{mod=M,name=N,arity=Ar}, Rs, St0) ->
- %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{M,N,Ar,Rs}]),
- {Ns,St1} = new_vars(bif_vals(M, N, Ar) - length(Rs), St0),
- {Rs ++ Ns,St1};
+bif_returns(#k_internal{name=match_fail}, Rs, St) ->
+ %% This is only used for effect, and may have any number of returns.
+ {Rs,St};
bif_returns(#k_internal{name=N,arity=Ar}, Rs, St0) ->
%%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{N,Ar,Rs}]),
{Ns,St1} = new_vars(bif_vals(N, Ar) - length(Rs), St0),
+ {Rs ++ Ns,St1};
+bif_returns(#k_remote{mod=M,name=N,arity=Ar}, Rs, St0) ->
+ %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{M,N,Ar,Rs}]),
+ {Ns,St1} = new_vars(bif_vals(M, N, Ar) - length(Rs), St0),
{Rs ++ Ns,St1}.
%% ensure_return_vars([Ret], State) -> {[Ret],State}.