diff options
Diffstat (limited to 'lib/compiler/src/beam_ssa_funs.erl')
-rw-r--r-- | lib/compiler/src/beam_ssa_funs.erl | 152 |
1 files changed, 0 insertions, 152 deletions
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. |