%%% -*- Erlang -*- %%% -*- erlang-indent-level: 2 -*- %%% %%% 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. %%% %%%------------------------------------------------------------------- %%% File : hipe_ssa_copy_prop.inc %%% Author : Tobias Lindahl %%% Description : Copy propagation on SSA form. %%% %%% Created : 4 Apr 2003 by Tobias Lindahl %%%------------------------------------------------------------------- -export([cfg/1]). %%-------------------------------------------------------------------- %% Two passes through the code visiting the blocks in reverse %% postorder. The first pass binds all destinations of copying moves %% to the sources, and the second propagates the copies and removes %% the copying moves. %% %% Problem: %% Since phi-nodes are implemented as instructions they are not %% atomic. If we are not careful we can get the situation (after propagation): %% %% v0 = phi(v0, v2) %% v1 = phi(v0, v3) %% ^^ %% where the underlined v0 really corresponds to the v0 before the first %% phi-instruction. %% %% Solution: %% * Find all dependencies between the uses of a phi-instruction to %% the destination of any earlier phi-instruction in the same phi-node; %% * Keep the copying move that defines the variable used in the %% latter phi-instruction; and %% * Do not propagate the copy into the phi-instruction %% %%-------------------------------------------------------------------- -spec cfg(#cfg{}) -> #cfg{}. cfg(Cfg) -> Labels = ?cfg:reverse_postorder(Cfg), {Info,PhiDep} = analyse(Labels, Cfg, gb_trees:empty(), gb_trees:empty()), rewrite(Labels, Cfg, Info, PhiDep). analyse([Label|Left], Cfg, Info, PhiDep) -> BB = ?cfg:bb(Cfg, Label), Code = hipe_bb:code(BB), NewPhiDep = get_phi_dep(Code, gb_sets:empty(), PhiDep), NewInfo = analyse_code(Code, Info), analyse(Left, Cfg, NewInfo, NewPhiDep); analyse([], _Cfg, Info, PhiDep) -> {Info,PhiDep}. get_phi_dep([I|Left], Defined, Dep) -> case ?code:is_phi(I) of true -> Use = ?code:uses(I), [Def] = ?code:defines(I), NewDep = add_dep(Use, Defined, Dep), get_phi_dep(Left, gb_sets:insert(Def, Defined), NewDep); false -> Dep end; get_phi_dep([], _Defined, Dep) -> Dep. add_dep([Use|Left], Defined, Dep) -> case gb_trees:lookup(Use, Dep) of none -> add_dep(Left, Defined, gb_trees:insert(Use, Defined, Dep)); {value, Set} -> NewSet = gb_sets:union(Defined, Set), add_dep(Left, Defined, gb_trees:enter(Use, NewSet, Dep)) end; add_dep([], _Defined, Dep) -> Dep. has_dep(Use, Def, Dep) -> case gb_trees:lookup(Use, Dep) of none -> false; {value, Set} -> gb_sets:is_member(Def, Set) end. analyse_code([I|Left], Info) -> case ?code:is_move(I) of true -> NewInfo = get_info_move(I, Info), analyse_code(Left, NewInfo); false -> analyse_code(Left, Info) end; analyse_code([], Info) -> Info. get_info_move(I, Info) -> case ?code:uses(I) of [] -> %% Constant. Info; [Src] -> add_binding(?code:defines(I), Src, Info) end. rewrite([Label|Left], Cfg, Info, PhiDep) -> BB = ?cfg:bb(Cfg, Label), Code = hipe_bb:code(BB), NewCode = rewrite_code(Code, Info, PhiDep, []), NewBB = hipe_bb:code_update(BB, NewCode), rewrite(Left, ?cfg:bb_add(Cfg, Label, NewBB), Info, PhiDep); rewrite([], Cfg, _Info, _PhiDep) -> Cfg. rewrite_code([I|Left], Info, PhiDep, Acc) -> case ?code:is_move(I) of true -> Fun = fun(X, Y) -> ?code:mk_move(X, Y) end, NewI = rewrite_move(I, Fun, Info, PhiDep), rewrite_code(Left, Info, PhiDep, NewI++Acc); false -> NewI = rewrite_instr(I, Info, PhiDep), rewrite_code(Left, Info, PhiDep, [NewI|Acc]) end; rewrite_code([], _Info, _PhiDep, Acc) -> lists:reverse(Acc). rewrite_move(I, Fun, Info, PhiDep) -> case ?code:uses(I) of [] ->%% Constant move. Keep it! [I]; _ -> Dst = hd(?code:defines(I)), case gb_trees:lookup(Dst, Info) of {value, Root} -> case has_dep(Dst, Root, PhiDep) of true -> %% Must keep the copying move! [Fun(Dst, Root)]; false -> [] end; none -> [] end end. rewrite_instr(I, Info, PhiDep) -> rewrite_instr0(I, ?code:uses(I), Info, PhiDep, []). rewrite_instr0(I, [Key|Left], Info, PhiDep, UpdateInfo) -> case gb_trees:lookup(Key, Info) of none -> rewrite_instr0(I, Left, Info, PhiDep, UpdateInfo); {value, Root} -> case gb_trees:lookup(Key, Info) of {value, Root} -> case has_dep(Key, Root, PhiDep) of true -> %% Must keep Key! rewrite_instr0(I, Left, Info, PhiDep, UpdateInfo); false -> rewrite_instr0(I, Left, Info, PhiDep, [{Key, Root}|UpdateInfo]) end; _ -> rewrite_instr0(I, Left, Info, PhiDep, UpdateInfo) end end; rewrite_instr0(I, [], _Info, _PhiDep, UpdateInfo) -> ?code:subst(UpdateInfo, I). add_binding([Key|Left], Val, Info) -> %% Make sure the key is bound to the end of any copy-chains. NewInfo = case gb_trees:lookup(Val, Info) of {value, NewVal} -> gb_trees:insert(Key, NewVal, Info); none -> gb_trees:insert(Key, Val, Info) end, add_binding(Left, Val, NewInfo); add_binding([], _, Info) -> Info.