File 2452-compiler-Remove-eta-conversion.patch of Package erlang
From 36656399c65d7b493238860b243c2dde6397ddc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20H=C3=B6gberg?= <john@erlang.org>
Date: Tue, 21 Dec 2021 22:07:09 +0100
Subject: [PATCH 2/4] compiler: Remove eta conversion
This resurrects 750ecdea08fa5fa7e32b7f3019eed96c1699427e, which
was removed because it made HiPE unhappy.
Co-authored-by: Anthony Ramine <n.oxyde@gmail.com>
---
erts/emulator/test/trace_local_SUITE.erl | 16 +--
lib/compiler/src/Makefile | 2 -
lib/compiler/src/beam_dict.erl | 8 +-
lib/compiler/src/beam_ssa_funs.erl | 152 -----------------------
lib/compiler/src/compile.erl | 5 -
lib/compiler/src/compiler.app.src | 1 -
lib/compiler/src/v3_core.erl | 2 +
lib/compiler/src/v3_kernel.erl | 34 ++---
lib/compiler/test/compile_SUITE.erl | 4 +-
lib/tools/test/cprof_SUITE.erl | 10 +-
10 files changed, 39 insertions(+), 195 deletions(-)
delete mode 100644 lib/compiler/src/beam_ssa_funs.erl
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
index 8cf8c81b7a..5913da3381 100644
--- a/erts/emulator/test/trace_local_SUITE.erl
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -803,16 +803,16 @@ exception_test(Opts, Func0, Args0) ->
%% Func0 and Args0 are for the innermost call, now we will
%% wrap them in wrappers...
{Func1,Args1} =
- case Function of
- true -> {fun exc/2,[Func0,Args0]};
- false -> {Func0,Args0}
- end,
+ case Function of
+ true -> {fun(F, As) -> exc(F, As) end, [Func0,Args0]};
+ false -> {Func0,Args0}
+ end,
{Func,Args} =
- case Apply of
- true -> {{erlang,apply},[Func1,Args1]};
- false -> {Func1,Args1}
- end,
+ case Apply of
+ true -> {{erlang,apply},[Func1,Args1]};
+ false -> {Func1,Args1}
+ end,
R1 = exc_slave(ExcOpts, Func, Args),
Stack2 = [{?MODULE,exc_top,3,[]},{?MODULE,slave,2,[]}],
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 54bf9bcb1f..b7dcb275ea 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -64,7 +64,6 @@ MODULES = \
beam_ssa_bsm \
beam_ssa_codegen \
beam_ssa_dead \
- beam_ssa_funs \
beam_ssa_lint \
beam_ssa_opt \
beam_ssa_pp \
@@ -205,7 +204,6 @@ $(EBIN)/beam_ssa_bsm.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_bool.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_codegen.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_dead.beam: beam_ssa.hrl
-$(EBIN)/beam_ssa_funs.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_lint.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_opt.beam: beam_ssa.hrl
$(EBIN)/beam_ssa_pp.beam: beam_ssa.hrl
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index f6651ae119..36bdb01df5 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -285,15 +285,17 @@ string_table(#asm{strings=Strings,string_offset=Size}) ->
-spec lambda_table(bdict()) -> {non_neg_integer(), [<<_:192>>]}.
-lambda_table(#asm{locals=Loc0,lambdas={NumLambdas,Lambdas0}}) ->
+lambda_table(#asm{locals=Loc0,exports=Ext0,lambdas={NumLambdas,Lambdas0}}) ->
Lambdas1 = sofs:relation(Lambdas0),
Loc = sofs:relation([{Lbl,{F,A}} || {F,A,Lbl} <- Loc0]),
- Lambdas2 = sofs:relative_product1(Lambdas1, Loc),
+ Ext = sofs:relation([{Lbl,{F,A}} || {F,A,Lbl} <- Ext0]),
+ All = sofs:union(Loc, Ext),
+ Lambdas2 = sofs:relative_product1(Lambdas1, All),
%% Initialize OldUniq to 0. It will be set to an unique value
%% based on the MD5 checksum of the BEAM code for the module.
OldUniq = 0,
Lambdas = [<<F:32,A:32,Lbl:32,Index:32,NumFree:32,OldUniq:32>> ||
- {{Index,Lbl,NumFree},{F,A}} <- sofs:to_external(Lambdas2)],
+ {{Index,Lbl,NumFree},{F,A}} <- sofs:to_external(Lambdas2)],
{NumLambdas,Lambdas}.
%% Returns the literal table.
diff --git a/lib/compiler/src/beam_ssa_funs.erl b/lib/compiler/src/beam_ssa_funs.erl
deleted file mode 100644
index a7b648bc47..0000000000
--- a/lib/compiler/src/beam_ssa_funs.erl
+++ /dev/null
@@ -1,152 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2018-2021. 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.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%%
-%%% If a fun is defined locally and only used for calls, it can be replaced
-%%% with direct calls to the relevant function. This greatly speeds up "named
-%%% functions" (which rely on make_fun to recreate themselves) and macros that
-%%% wrap their body in a fun.
-%%%
-
--module(beam_ssa_funs).
-
--export([module/2]).
-
--include("beam_ssa.hrl").
-
--import(lists, [foldl/3]).
-
--spec module(Module, Options) -> Result when
- Module :: beam_ssa:b_module(),
- Options :: [compile:option()],
- Result :: {ok, beam_ssa:b_module()}.
-
-module(#b_module{body=Fs0}=Module, _Opts) ->
- Trampolines = foldl(fun find_trampolines/2, #{}, Fs0),
- Fs = [lfo(F, Trampolines) || F <- Fs0],
- {ok, Module#b_module{body=Fs}}.
-
-%% If a function does absolutely nothing beyond calling another function with
-%% 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 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},
- Trampolines#{Trampoline => Actual};
- _ ->
- Trampolines
- end.
-
-lfo(#b_function{bs=Blocks0}=F, Trampolines) ->
- Linear0 = beam_ssa:linearize(Blocks0),
- Linear = lfo_optimize(Linear0, lfo_analyze(Linear0, #{}), Trampolines),
- F#b_function{bs=maps:from_list(Linear)}.
-
-%% Gather a map of the locally defined funs that are only used for calls.
-lfo_analyze([{_L,#b_blk{is=Is,last=Last}}|Bs], LFuns0) ->
- LFuns = lfo_analyze_last(Last, lfo_analyze_is(Is, LFuns0)),
- lfo_analyze(Bs, LFuns);
-lfo_analyze([], LFuns) ->
- LFuns.
-
-lfo_analyze_is([#b_set{op=make_fun,
- dst=Dst,
- args=[#b_local{} | FreeVars]}=Def | Is],
- LFuns0) ->
- LFuns = maps:put(Dst, Def, maps:without(FreeVars, LFuns0)),
- lfo_analyze_is(Is, LFuns);
-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]} = map_get(Fun, LFuns),
- case length(CallArgs) + length(FreeVars) of
- Arity ->
- lfo_analyze_is(Is, maps:without(CallArgs, LFuns));
- _ ->
- %% This will `badarity` at runtime, and it's easier to disable the
- %% optimization than to simulate it.
- lfo_analyze_is(Is, maps:without([Fun | CallArgs], LFuns))
- end;
-lfo_analyze_is([#b_set{args=Args} | Is], LFuns) when map_size(LFuns) =/= 0 ->
- %% We disqualify funs that are used outside calls because this forces them
- %% to be created anyway, and the slight performance gain from direct calls
- %% is not enough to offset the potential increase in stack frame size (the
- %% free variables need to be kept alive until the call).
- lfo_analyze_is(Is, maps:without(Args, LFuns));
-lfo_analyze_is([_ | Is], LFuns) ->
- lfo_analyze_is(Is, LFuns);
-lfo_analyze_is([], LFuns) ->
- LFuns.
-
-lfo_analyze_last(#b_switch{arg=Arg}, LFuns) ->
- maps:remove(Arg, LFuns);
-lfo_analyze_last(#b_ret{arg=Arg}, LFuns) ->
- maps:remove(Arg, LFuns);
-lfo_analyze_last(_, LFuns) ->
- LFuns.
-
-%% Replace all calls of suitable funs with a direct call to their
-%% implementation. Liveness optimization will get rid of the make_fun
-%% instruction.
-lfo_optimize(Linear, LFuns, _Trampolines) when map_size(LFuns) =:= 0 ->
- Linear;
-lfo_optimize(Linear, LFuns, Trampolines) ->
- lfo_optimize_1(Linear, LFuns, Trampolines).
-
-lfo_optimize_1([{L,#b_blk{is=Is0}=Blk}|Bs], LFuns, Trampolines) ->
- Is = lfo_optimize_is(Is0, LFuns, Trampolines),
- [{L,Blk#b_blk{is=Is}} | lfo_optimize_1(Bs, LFuns, Trampolines)];
-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]} = 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)];
-lfo_optimize_is([I | Is], LFuns, Trampolines) ->
- [I | lfo_optimize_is(Is, LFuns, Trampolines)];
-lfo_optimize_is([], _LFuns, _Trampolines) ->
- [].
-
-lfo_short_circuit(Call, Trampolines) ->
- lfo_short_circuit(Call, Trampolines, sets:new([{version, 2}])).
-
-lfo_short_circuit(Call, Trampolines, Seen0) ->
- %% Beware of infinite loops! Get out if this call has been seen before.
- case sets:is_element(Call, Seen0) of
- true ->
- Call;
- false ->
- case Trampolines of
- #{Call := Other} ->
- Seen = sets:add_element(Call, Seen0),
- lfo_short_circuit(Other, Trampolines, Seen);
- #{} ->
- Call
- end
- end.
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index b65750d153..f364b56645 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -876,10 +876,6 @@ kernel_passes() ->
{iff,dssabsm,{listing,"ssabsm"}},
{unless,no_bsm_opt,{iff,ssalint,{pass,beam_ssa_lint}}},
- {unless,no_fun_opt,{pass,beam_ssa_funs}},
- {iff,dssafuns,{listing,"ssafuns"}},
- {unless,no_fun_opt,{iff,ssalint,{pass,beam_ssa_lint}}},
-
{unless,no_ssa_opt,{pass,beam_ssa_opt}},
{iff,dssaopt,{listing,"ssaopt"}},
{unless,no_ssa_opt,{iff,ssalint,{pass,beam_ssa_lint}}},
@@ -2054,7 +2050,6 @@ pre_load() ->
beam_ssa_bsm,
beam_ssa_codegen,
beam_ssa_dead,
- beam_ssa_funs,
beam_ssa_opt,
beam_ssa_pre_codegen,
beam_ssa_recv,
diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src
index e257c8fc3a..5e8135bd1e 100644
--- a/lib/compiler/src/compiler.app.src
+++ b/lib/compiler/src/compiler.app.src
@@ -40,7 +40,6 @@
beam_ssa_bsm,
beam_ssa_codegen,
beam_ssa_dead,
- beam_ssa_funs,
beam_ssa_lint,
beam_ssa_opt,
beam_ssa_pp,
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 2223db6608..6bf87e131c 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -723,6 +723,8 @@ expr({'catch',L,E0}, St0) ->
Lanno = lineno_anno(L, St1),
{#icatch{anno=#a{anno=Lanno},body=Eps ++ [E1]},[],St1};
expr({'fun',L,{function,F,A}}, St0) ->
+ %% Generate a new name for eta conversion of local funs (`fun local/123`)
+ %% in case `no_shared_fun_wrappers` is given.
{Fname,St1} = new_fun_name(St0),
Lanno = full_anno(L, St1),
Id = {0,0,Fname},
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index cf6df77d68..aa3fc033eb 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -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) ->
@@ -1991,6 +1984,15 @@ uexpr(#ifun{anno=A,vars=Vs,body=B0}, {break,Rs}, St0) ->
args=[Local|Fvs],
ret=Rs},
Free,add_local_function(Fun, St)};
+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,first=F0,then=T0}=MatchAlt, Br, St0) ->
Rs = break_rets(Br),
{F1,Fu,St1} = ubody(F0, Br, St0),
diff --git a/lib/tools/test/cprof_SUITE.erl b/lib/tools/test/cprof_SUITE.erl
index 8eb7476f57..d27371414a 100644
--- a/lib/tools/test/cprof_SUITE.erl
+++ b/lib/tools/test/cprof_SUITE.erl
@@ -203,9 +203,8 @@ on_load_test(Config) ->
N2 = cprof:pause(),
{Module,0,[]} = cprof:analyse(Module),
M4__4 = M*4 - 4,
- M10_7 = M*10 - 7,
- {?MODULE,M10_7,[{{?MODULE,succ,1},M4__4},
- {{?MODULE,'-fun.succ/1-',1},M4__4},
+ M6_3 = M*6 - 3,
+ {?MODULE,M6_3,[{{?MODULE,succ,1},M4__4},
{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M},
{{?MODULE,seq_r,3},1}]}
@@ -234,11 +233,10 @@ modules_test(Config) ->
N = cprof:pause(),
Lr = lists:reverse(L),
M4_4 = M*4 - 4,
- M10_7 = M*10 - 7,
+ M6_3 = M*6 - 3,
M2__1 = M*2 + 1,
{Tot,ModList} = cprof:analyse(),
- {value,{?MODULE,M10_7,[{{?MODULE,succ,1},M4_4},
- {{?MODULE,'-fun.succ/1-',1},M4_4},
+ {value,{?MODULE,M6_3,[{{?MODULE,succ,1},M4_4},
{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M},
{{?MODULE,seq_r,3},1}]}} =
--
2.31.1