%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2005-2013. 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% %% %%---------------------------------------------------------------------- %% File : hipe_icode_mulret.erl %% Author : Christoffer Vikström <chvi3471@it.uu.se> %% Purpose : %% Created : 23 Jun 2004 by Christoffer Vikström <chvi3471@it.uu.se> %%---------------------------------------------------------------------- -module(hipe_icode_mulret). -export([mult_ret/4]). -include("../main/hipe.hrl"). -include("hipe_icode.hrl"). -include("hipe_icode_primops.hrl"). %%>----------------------------------------------------------------------< %% Procedure : mult_ret/4 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< -spec mult_ret([_], atom(), comp_options(), _) -> [_]. mult_ret(List, Mod, Opts, Exports) -> case length(List) > 1 of true -> Table = analyse(List, Mod, Exports), %% printTable(Mod, Exports, Table), optimize(List, Mod, Opts, Table); false -> List end. %%>-----------------------< Analysis Steps >-----------------------------< %%>----------------------------------------------------------------------< %% Procedure : analyse/3 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< analyse(List, _Mod, Exports) -> MaxRets = hipe_rtl_arch:nr_of_return_regs(), Table = mkTable(List), %% printTable(Mod, Exports, Table), Table2 = filterTable(Table, MaxRets, Exports), %% printTable(Mod, Exports, Table2), Table2. %%>----------------------------------------------------------------------< %% Procedure : mkTable/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< mkTable(List) -> mkTable(List, {[], []}). mkTable([{MFA, Icode} | List], Table) -> %% New Icode {_LMin,LMax} = hipe_icode:icode_label_range(Icode), hipe_gensym:set_label(icode, LMax+1), {_VMin,VMax} = hipe_icode:icode_var_range(Icode), hipe_gensym:set_var(icode, VMax+1), case isFunDef(MFA) of true -> mkTable(List, Table); false -> CallList = mkCallList(MFA, Icode), Optimizable = isOptimizable(Icode), NewTable = addToTable(MFA, Optimizable, CallList, Table), mkTable(List, NewTable) end; mkTable([_|List], Table) -> mkTable(List, Table); mkTable([], Table) -> Table. %%>----------------------------------------------------------------------< %% Procedure : isFunDef/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< isFunDef({_, F, _}) -> hd(atom_to_list(F)) =:= 45. %% 45 is the character '-' %%>----------------------------------------------------------------------< %% Procedure : mkCallList/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< mkCallList(MFA, Icode) -> Code = hipe_icode:icode_code(Icode), mkCallList(Code, MFA, []). mkCallList([#icode_call{'fun'=F, dstlist=Vars, type=local}|Code], MFA, Res) -> {Size, DstList} = lookForDef(Code, Vars), mkCallList(Code, MFA, [{callPair,MFA,{F,{matchSize,Size,DstList}}}|Res]); mkCallList([_|Code], MFA, Res) -> mkCallList(Code, MFA, Res); mkCallList([], _, Res) -> Res. %%>----------------------------------------------------------------------< %% Procedure : lookForDef/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< lookForDef([#icode_type{test={tuple,Size}, true_label=L}|Code], Vars) -> Code2 = skipToLabel(Code, L), DstLst = lookForUnElems(Code2, Vars), case DstLst of [] -> {1, Vars}; _ -> DstLst2 = fixDstLst(DstLst, Size), {Size, DstLst2} end; lookForDef([#icode_move{src=Var, dst=NewVar}|Code], [Var]) -> lookForDef(Code, [NewVar]); lookForDef([#icode_label{}|_], Vars) -> {1, Vars}; lookForDef([I|Code], [Var] = Vars) -> Defs = hipe_icode:defines(I), case lists:member(Var, Defs) of true -> {1, Vars}; false -> lookForDef(Code, Vars) end; lookForDef([], Vars) -> {1, Vars}. %%>----------------------------------------------------------------------< %% Procedure : skipToLabel/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< skipToLabel(Code, L) -> case skipToLabel2(Code, L) of noLabel -> Code; NewCode -> NewCode end. skipToLabel2([#icode_label{name = L}|Code],L) -> Code; skipToLabel2([_|Code], L) -> skipToLabel2(Code, L); skipToLabel2([], _) -> noLabel. %%>----------------------------------------------------------------------< %% Procedure : lookForUnElems/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< lookForUnElems(Code, Var) -> lookForUnElems(Code, Var, []). lookForUnElems([#icode_call{'fun'=#unsafe_element{index=Nr}, args=Var, dstlist=[Ret]}|Code], Var, Res) -> lookForUnElems(Code, Var, [{Nr, Ret}|Res]); lookForUnElems([#icode_move{dst=Var}|_], [Var], Res) -> lists:flatten(Res); lookForUnElems([#icode_call{dstlist=VarList}|_], VarList, Res) -> lists:flatten(Res); lookForUnElems([_|Code], Var, Res) -> lookForUnElems(Code, Var, Res); lookForUnElems([], _, Res) -> lists:flatten(Res). %%>----------------------------------------------------------------------< %% Procedure : fixDstLst/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< fixDstLst(DstLst, Size) when is_integer(Size) -> fixDstLst(DstLst, Size, 1, []). fixDstLst(DstLst, Size, Cnt, Res) when Cnt =< Size -> case isInLst(Cnt, DstLst) of {true, Var} -> fixDstLst(DstLst, Size, Cnt+1, [Var|Res]); false -> Var = hipe_icode:mk_var(hipe_gensym:new_var(icode)), fixDstLst(DstLst, Size, Cnt+1, [Var|Res]) end; fixDstLst(_, Size, Cnt, Res) when Cnt > Size -> lists:reverse(Res). %%>----------------------------------------------------------------------< %% Procedure : isInLst/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< isInLst(Nr, [{Nr,Var}|_]) -> {true, Var}; isInLst(Cnt, [_|DstLst]) -> isInLst(Cnt, DstLst); isInLst(_, []) -> false. %%>----------------------------------------------------------------------< %% Procedure : isOptimizable/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< isOptimizable(Icode) -> %% Icode2 = hipe_icode:fixup_fallthroughs(Icode), Icode2 = hipe_icode:strip_comments(Icode), Cfg = hipe_icode_cfg:linear_to_cfg(Icode2), %% hipe_icode_cfg:pp(Cfg), case findReturnBlocks(Cfg) of noReturn -> {false, -1}; BlockList -> processReturnBlocks(BlockList, Cfg) end. %%>----------------------------------------------------------------------< %% Procedure : findReturnBlocks/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< findReturnBlocks(IcodeCfg) -> Labels = hipe_icode_cfg:labels(IcodeCfg), case searchBlocks(Labels, IcodeCfg) of [] -> noReturn; BlockList-> BlockList end. %%>----------------------------------------------------------------------< %% Procedure : searchBlocks/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< searchBlocks(Labels, IcodeCfg) -> searchBlocks(Labels, IcodeCfg, []). searchBlocks([Label|Labels], IcodeCfg, Res) -> Block = hipe_icode_cfg:bb(IcodeCfg, Label), Code = hipe_bb:code(Block), case searchBlockCode(Code) of {hasReturn, RetVar} -> searchBlocks(Labels, IcodeCfg, [{Label, RetVar}|Res]); noReturn -> searchBlocks(Labels, IcodeCfg, Res) end; searchBlocks([], _, Res) -> Res. %%>----------------------------------------------------------------------< %% Procedure : searchBlockCode/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< searchBlockCode([#icode_return{vars=Vars}|_]) -> {hasReturn, Vars}; searchBlockCode([_|Icode]) -> searchBlockCode(Icode); searchBlockCode([]) -> noReturn. %%>----------------------------------------------------------------------< %% Procedure : processReturnBlock/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< processReturnBlocks(Blocks, Cfg) -> processReturnBlocks(Blocks, Cfg, {true, -1}, []). processReturnBlocks([{Label, Var}|BlockList], Cfg, {Opts, Size}, TypeLst) -> {Opt, Type, Size2} = traverseCode(Label, Var, Cfg), case (Size =:= -1) orelse (Size =:= Size2) of true -> processReturnBlocks(BlockList, Cfg, {Opt andalso Opts, Size2}, [Type|TypeLst]); false -> {false, -1} end; processReturnBlocks([], _, Res, TypeLst) -> case lists:member(icode_var, TypeLst) of true -> {_, Size} = Res, case Size > 1 of true -> Res; false -> {false, -1} end; false -> {false, -1} end. %%>----------------------------------------------------------------------< %% Procedure : traverseCode/3 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< traverseCode(Label, Var, Cfg) -> traverseCode(Label, Var, Cfg, []). traverseCode(Label, Var, Cfg, LabLst) -> Preds = hipe_icode_cfg:pred(Cfg, Label), Block = hipe_icode_cfg:bb(Cfg, Label), Code = hipe_bb:code(Block), case findDefine(lists:reverse(Code), Var) of {found, Type, NumRets} -> {true, Type, NumRets}; {notFound, SrcVar} -> case Preds of [] -> {false, none, -1}; [Pred] -> case lists:member(Label, LabLst) of false -> traverseCode(Pred, SrcVar, Cfg, [Label|LabLst]); true -> {false, none, -1} end; _ -> {false, none, -1} end end. %%>----------------------------------------------------------------------< %% Procedure : findDefine/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< findDefine([#icode_call{dstlist=Vars,'fun'=mktuple,args=Vs}|_], Vars) -> case length(Vs) of 1 -> [{Type, _}] = Vs, {found, Type, 1}; Len -> case lists:any(fun hipe_icode:is_var/1, Vs) of true -> {found, icode_var, Len}; false -> {found, icode_const, Len} end end; findDefine([#icode_move{dst=Var, src=Src}|Code], [Var]) -> case hipe_icode:is_var(Src) of true -> findDefine(Code, [Src]); false -> case Src of #icode_const{value={flat, Value}} -> case is_tuple(Value) of true -> {found, icode_const, tuple_size(Value)}; false -> {found, icode_const, 1} end; _ -> findDefine(Code, [Var]) end end; findDefine([_|Code], Var) -> findDefine(Code, Var); findDefine([], Var) -> {notFound, Var}. %%>----------------------------------------------------------------------< %% Procedure : addToTable/4 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< addToTable(MFA, Optimizable, CallList, {FunLst, CallLst}) -> NewFunLst = [{MFA, Optimizable}|FunLst], {NewFunLst, CallList ++ CallLst}. %%>----------------------------------------------------------------------< %% Procedure : filterTable/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< filterTable({FunLst, CallLst}, MaxRets, Exports) -> filterTable(FunLst, CallLst, MaxRets, Exports, {[],[]}). filterTable([Fun|FunLst], CallLst, MaxRets, Exports, {Funs, Calls} = FCs) -> {MFA, {ReturnOpt, Rets}} = Fun, {CallOpt, CallsToKeep} = checkCalls(CallLst, MFA, Rets), CallsToKeep2 = removeDuplicateCalls(CallsToKeep), NotExported = checkExported(MFA, Exports), case CallOpt andalso ReturnOpt andalso (Rets =< MaxRets) andalso NotExported andalso (not containRecursiveCalls(CallsToKeep2, MFA)) of true -> filterTable(FunLst, CallLst, MaxRets, Exports, {[Fun|Funs], CallsToKeep2 ++ Calls}); false -> filterTable(FunLst, CallLst, MaxRets, Exports, FCs) end; filterTable([], _, _, _, Res) -> Res. removeDuplicateCalls(Calls) -> removeDuplicateCalls(Calls, []). removeDuplicateCalls([Call|CallsToKeep], Res) -> case lists:member(Call, CallsToKeep) of true -> removeDuplicateCalls(CallsToKeep, Res); false -> removeDuplicateCalls(CallsToKeep, [Call|Res]) end; removeDuplicateCalls([], Res) -> lists:reverse(Res). containRecursiveCalls([Call|Calls], Fun) -> {callPair, Caller, {Callee, _}} = Call, case (Callee =:= Fun) andalso (Caller =:= Fun) of true -> true; false-> containRecursiveCalls(Calls, Fun) end; containRecursiveCalls([], _) -> false. %%>----------------------------------------------------------------------< %% Procedure : checkCalls/3 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< checkCalls(CallLst, MFA, Rets) -> checkCalls(CallLst, MFA, Rets, [], []). checkCalls([C = {callPair, _, {MFA, {matchSize, Rets, _}}}|CallLst], MFA, Rets, Res, Opt) -> checkCalls(CallLst, MFA, Rets, [C|Res], [true|Opt]); checkCalls([{callPair, _, {MFA, {matchSize, _, _}}}|CallLst], MFA, Rets, Res, Opt) -> checkCalls(CallLst, MFA, Rets, Res, [false|Opt]); checkCalls([_|CallLst], MFA, Rets, Res, Opt) -> checkCalls(CallLst, MFA, Rets, Res, Opt); checkCalls([], _, _, Res, Opt) -> {combineOpts(Opt), Res}. %%>----------------------------------------------------------------------< %% Procedure : combineOpts/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< combineOpts([]) -> false; combineOpts([Opt]) -> Opt; combineOpts([Opt|Opts]) -> Opt andalso combineOpts(Opts). %%>----------------------------------------------------------------------< %% Procedure : checkCalls/2 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< checkExported({_,F,A}, [{F,A}|_]) -> false; checkExported(MFA, [_|Exports]) -> checkExported(MFA, Exports); checkExported(_, []) -> true. %%>----------------------< Optimization Steps >--------------------------< %%>----------------------------------------------------------------------< %% Procedure : optimize/4 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< optimize(List, _Mod, Opts, Table) -> {FunLst, CallLst} = Table, List2 = optimizeFuns(FunLst, Opts, List), optimizeCalls(CallLst, Opts, List2). %%>----------------------------------------------------------------------< %% Procedure : optimizeFuns/3 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< optimizeFuns([{Fun, _}|FunList], Opts, List) -> NewList = findFun(List, Fun), optimizeFuns(FunList, Opts, NewList); optimizeFuns([],_,List) -> List. findFun(List, Fun) -> findFun(List, Fun, []). findFun([{Fun, Icode}|List], Fun, Res) -> NewIcode = optimizeFun(Icode), findFun(List, Fun, [{Fun, NewIcode}|Res]); findFun([I|List], Fun, Res) -> findFun(List, Fun, [I|Res]); findFun([], _, Res) -> lists:reverse(Res). optimizeFun(Icode) -> {_LMin,LMax} = hipe_icode:icode_label_range(Icode), hipe_gensym:set_label(icode, LMax+1), {_VMin,VMax} = hipe_icode:icode_var_range(Icode), hipe_gensym:set_var(icode, VMax+1), %% Icode2 = hipe_icode:fixup_fallthroughs(Icode), Icode2 = hipe_icode:strip_comments(Icode), Cfg = hipe_icode_cfg:linear_to_cfg(Icode2), case findReturnBlocks(Cfg) of noReturn -> false; BlockList -> NewCfg = optimizeReturnBlocks(BlockList, Cfg), hipe_icode_cfg:cfg_to_linear(NewCfg) end. optimizeReturnBlocks([Block|BlockList], Cfg) -> {NewCfg, Vars} = optimizeReturnBlock(Block, Cfg), NewCfg2 = case Vars of [_] -> Cfg; _ -> {Label, _} = Block, updateReturnBlock(Label, Vars, NewCfg) end, optimizeReturnBlocks(BlockList, NewCfg2); optimizeReturnBlocks([], Cfg) -> Cfg. optimizeReturnBlock(Block, Cfg) -> optimizeReturnBlock(Block, Cfg, []). optimizeReturnBlock({Label,Var}, Cfg, UpdateMap) -> Preds = hipe_icode_cfg:pred(Cfg, Label), Block = hipe_icode_cfg:bb(Cfg, Label), Code = hipe_bb:code(Block), case optimizeDefine(Code, Var) of {found, NewBlockCode, Vars} -> NewBlock = hipe_bb:code_update(Block, NewBlockCode), NewCfg = resolveUpdateMap(UpdateMap, Cfg), {hipe_icode_cfg:bb_add(NewCfg, Label, NewBlock), Vars}; {none, NewBlockCode, NewVar} -> case Preds of [Pred] -> NewBlock = hipe_bb:code_update(Block, NewBlockCode), optimizeReturnBlock({Pred,NewVar}, Cfg, [{Label, NewBlock}|UpdateMap]); [_|_] -> {Cfg, Var} end; {none, noOpt} -> {Cfg, Var} end. optimizeDefine(Code, Dst) -> optimizeDefine(lists:reverse(Code), Dst, [], []). optimizeDefine([I|Code], Dsts, DstLst, Res) -> [Ds] = Dsts, case isCallPrimop(I, mktuple) andalso DstLst =:= [] of true -> case hipe_icode:call_dstlist(I) =:= Dsts of true -> case length(hipe_icode:call_args(I)) > 1 of true -> optimizeDefine(Code, Dsts, hipe_icode:call_args(I), Res); false -> {none, noOpt} end; false -> optimizeDefine(Code, Dsts, DstLst, [I|Res]) end; false -> case hipe_icode:is_move(I) andalso DstLst =:= [] of true -> case hipe_icode:move_dst(I) =:= Ds of true -> Src = hipe_icode:move_src(I), case hipe_icode:is_var(Src) of true -> NewDst = hipe_icode:move_src(I), optimizeDefine(Code, [NewDst], DstLst, Res); false -> case Src of #icode_const{value={flat, T}} when is_tuple(T) -> NewLst = tuple_to_list(T), optimizeDefine(Code, Dsts, NewLst, Res); _ -> {none, noOpt} end end; false -> optimizeDefine(Code, Dsts, DstLst, [I|Res]) end; false -> case lists:member(Ds, hipe_icode:defines(I)) andalso DstLst =:= [] of true -> {none, noOpt}; false -> optimizeDefine(Code, Dsts, DstLst, [I|Res]) end end end; optimizeDefine([], Dsts, DstLst, Res) -> case DstLst of [] -> {none, Res, Dsts}; _ -> {found, Res, DstLst} end. resolveUpdateMap([{Label, Block}|UpdateMap], Cfg) -> resolveUpdateMap(UpdateMap, hipe_icode_cfg:bb_add(Cfg, Label, Block)); resolveUpdateMap([], Cfg) -> Cfg. %%>----------------------------------------------------------------------< %% Procedure : updateReturnBlock/3 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< updateReturnBlock(Label, Vars, IcodeCfg) -> Block = hipe_icode_cfg:bb(IcodeCfg, Label), Code = hipe_bb:code(Block), NewCode = updateReturnCode(Code, Vars), NewBlock = hipe_bb:code_update(Block, NewCode), hipe_icode_cfg:bb_add(IcodeCfg, Label, NewBlock). updateReturnCode(Code, DstLst) -> updateReturnCode(Code, DstLst, []). updateReturnCode([I| Code], DstLst, Res) -> case hipe_icode:is_return(I) of true -> updateReturnCode(Code, DstLst, [hipe_icode:mk_return(DstLst)|Res]); false -> updateReturnCode(Code, DstLst, [I|Res]) end; updateReturnCode([], _, Res) -> lists:reverse(Res). %%>----------------------------------------------------------------------< %% Procedure : optimizeCalls/3 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< optimizeCalls([Call|CallLst], _Opts, List) -> {callPair, Caller, {Callee, {matchSize, _, DstLst}}} = Call, NewList = optimizeCall(List, Caller, Callee, DstLst), optimizeCalls(CallLst, _Opts, NewList); optimizeCalls([], _Opts, List) -> List. %%>----------------------------------------------------------------------< %% Procedure : optimizeCall/4 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< optimizeCall(List, Caller, Callee, DstLst) -> optimizeCall(List, Caller, Callee, DstLst, []). optimizeCall([{MFA, Icode}|List], MFA, Callee, DstLst, Res) -> {_LMin,LMax} = hipe_icode:icode_label_range(Icode), hipe_gensym:set_label(icode, LMax+1), {_VMin,VMax} = hipe_icode:icode_var_range(Icode), hipe_gensym:set_var(icode, VMax+1), %% Icode2 = hipe_icode:fixup_fallthroughs(Icode), Icode2 = hipe_icode:strip_comments(Icode), Cfg = hipe_icode_cfg:linear_to_cfg(Icode2), NewIcode = findAndUpdateCalls(Cfg, Callee, DstLst), optimizeCall(List, MFA, Callee, DstLst, [{MFA, NewIcode}|Res]); optimizeCall([I|List], Caller, Callee, DstLst, Res) -> optimizeCall(List, Caller, Callee, DstLst, [I|Res]); optimizeCall([], _, _, _, Res) -> lists:reverse(Res). %%>----------------------------------------------------------------------< %% Procedure : findAndUpdateCall/3 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< findAndUpdateCalls(Cfg, Callee, DstLst) -> Labels = hipe_icode_cfg:labels(Cfg), Cfg2 = findAndUpdateCalls(Cfg, Labels, Callee, DstLst, []), hipe_icode_cfg:cfg_to_linear(Cfg2). findAndUpdateCalls(Cfg, [L|Labels], Callee, DstLst, Visited) -> %% Block = hipe_icode_cfg:bb(Cfg, L), %% Code = hipe_bb:code(Block), case containCorrectCall(Cfg, L, Callee, DstLst) of true -> Block = hipe_icode_cfg:bb(Cfg,L), Code = hipe_bb:code(Block), {NewCode, OldVar} = updateCode(Code, Callee, DstLst), NewBlock = hipe_bb:code_update(Block, NewCode), Cfg2 = hipe_icode_cfg:bb_add(Cfg, L, NewBlock), Cfg3 = cleanUpAffectedCode(Cfg2, OldVar, Callee, L, Visited), findAndUpdateCalls(Cfg3, Labels, Callee, DstLst, [L|Visited]); false -> findAndUpdateCalls(Cfg, Labels, Callee, DstLst, [L|Visited]) end; findAndUpdateCalls(Cfg,[], _, _, _) -> Cfg. containCorrectCall(Cfg, Label, Callee, DstLst) -> Block = hipe_icode_cfg:bb(Cfg,Label), Code = hipe_bb:code(Block), case containCallee(Code, Callee) of {true, OldVar} -> Succs = hipe_icode_cfg:succ(Cfg, Label), checkForUnElems(Succs, OldVar, DstLst, Cfg); false -> false end. checkForUnElems([], _, _, _) -> false; checkForUnElems([Succ|Succs], OldVar, DstLst, Cfg) -> Block = hipe_icode_cfg:bb(Cfg,Succ), Code = hipe_bb:code(Block), case checkForUnElems2(Code, OldVar, DstLst, []) of true -> true; false -> checkForUnElems(Succs, OldVar, DstLst, Cfg) end. checkForUnElems2([I|Code], OldVar, DstLst, DstRes) -> case isCallPrimop(I, unsafe_element) of true -> case (hipe_icode:call_args(I) =:= OldVar) of true -> [Dst] = hipe_icode:call_dstlist(I), case lists:member(Dst, DstLst) of true -> checkForUnElems2(Code, OldVar, DstLst, [Dst|DstRes]); false -> checkForUnElems2(Code, OldVar, DstLst, DstRes) end; false -> checkForUnElems2(Code, OldVar, DstLst, DstRes) end; false -> checkForUnElems2(Code, OldVar, DstLst, DstRes) end; checkForUnElems2([], _, DstLst, DstRes) -> DstLst =:= lists:reverse(DstRes). containCallee([I|Code], Callee) -> case isCallLocal(I, Callee) of true -> {true, hipe_icode:call_dstlist(I)}; false -> containCallee(Code, Callee) end; containCallee([], _) -> false. updateCode(Code, Callee, DstLst) -> updateCode(Code, Callee, DstLst, [], []). updateCode([I|Code], Callee, DstLst, Res, OldVars) -> case isCallLocal(I, Callee) of true -> Vars = hipe_icode:call_dstlist(I), I2 = hipe_icode:call_dstlist_update(I, DstLst), updateCode(Code, Callee, DstLst, [I2|Res], Vars); false -> updateCode(Code, Callee, DstLst, [I|Res], OldVars) end; updateCode([], _, _, Res, OldVars) -> {lists:reverse(Res), OldVars}. cleanUpAffectedCode(Cfg, OldVar, Callee, Label, Visited) -> Block = hipe_icode_cfg:bb(Cfg,Label), Code = hipe_bb:code(Block), {CodeBefore, CodeAfter, DstLst} = divideAtCall(Code, Callee), {NewCodeAfter, ContLab, FailLab} = findType(CodeAfter, OldVar), ContBlock = hipe_icode_cfg:bb(Cfg, ContLab), Succs = hipe_icode_cfg:succ(Cfg, ContLab), ContCode = hipe_bb:code(ContBlock), {NewContCode, NewFailLab} = removeUnElems(ContCode, OldVar, DstLst), NewBlock = hipe_bb:code_update(Block, CodeBefore ++ NewCodeAfter ++ NewContCode), Cfg2 = hipe_icode_cfg:bb_add(Cfg, Label, NewBlock), Cfg3 = resolveSuccBlocks(Succs, OldVar, DstLst, [Label|Visited], NewFailLab, Cfg2), insertMiddleFailBlock(Cfg3, NewFailLab, FailLab, OldVar, DstLst). divideAtCall(Code, Caller) -> divideAtCall(Code, Caller, []). divideAtCall([I|Code], Caller, Tail) -> case isCallLocal(I, Caller) of true -> {lists:reverse([I|Tail]), Code, hipe_icode:call_dstlist(I)}; false -> divideAtCall(Code, Caller, [I|Tail]) end; divideAtCall([], _, Tail) -> {Tail, []}. findType(CodeAfter, OldVar) -> findType(CodeAfter, OldVar, [], {none, none}). findType([I|Code], OldVar, Rest, Succs) -> case hipe_icode:is_type(I) of true -> case hipe_icode:type_args(I) =:= OldVar of true -> TrueLab = hipe_icode:type_true_label(I), FalseLab = hipe_icode:type_false_label(I), findType(Code, OldVar, Rest, {TrueLab, FalseLab}); false -> findType(Code, OldVar, [I|Rest], Succs) end; false -> case hipe_icode:is_move(I) of true -> case [hipe_icode:move_src(I)] =:= OldVar of true -> findType(Code, hipe_icode:move_dst(I), [I|Rest], Succs); false -> findType(Code, OldVar, [I|Rest], Succs) end; false -> findType(Code, OldVar, [I|Rest], Succs) end end; findType([],_,Rest, {TrueLab, FalseLab}) -> {lists:reverse(Rest), TrueLab, FalseLab}. %% Nesting hell... check for redundancies. %% --------------------------------------- removeUnElems(Code, OldVars, DstLst) -> removeUnElems(Code, OldVars, DstLst, [], false, none). removeUnElems([I|Code], [OldVar] = OldVars, DstLst, Res, Def, Lab) -> case isCallPrimop(I, unsafe_element) of true -> case (hipe_icode:call_args(I) =:= OldVars) of true -> removeUnElems(Code, OldVars, DstLst, Res, Def, Lab); false -> case lists:member(OldVar, hipe_icode:call_args(I)) of true -> %% XXX: the following test seems redundant, %% hence commented out -- KOSTIS %% case Def of %% true -> removeUnElems(Code, OldVars, DstLst, [I|Res], Def, Lab); %% false -> %% removeUnElems(Code, OldVars, DstLst, %% [I|Res], Def, Lab) %% end; false -> io:format("Borde aldrig kunna hamna här!", []), removeUnElems(Code, OldVars, DstLst, [I|Res], Def, Lab) end end; false -> case hipe_icode:is_move(I) of true -> case hipe_icode:move_src(I) =:= OldVar of true -> NewVar = hipe_icode:move_dst(I), removeUnElems(Code, [NewVar], DstLst, [I|Res], Def, Lab); false -> removeUnElems(Code, OldVars, DstLst, [I|Res], Def, Lab) end; false -> case hipe_icode:is_type(I) andalso not Def of true -> NewFalseLab = case Lab =:= none of true -> hipe_gensym:get_next_label(icode); false -> Lab end, _I2 = updateTypeFalseLabel(I, NewFalseLab), removeUnElems(Code, OldVars, DstLst, [I|Res], Def, NewFalseLab); false -> case lists:member(OldVar, hipe_icode:uses(I)) andalso Def of true -> removeUnElems(Code, OldVars, DstLst, [I|Res], Def, Lab); false -> case lists:member(OldVar, hipe_icode:defines(I)) of true -> removeUnElems(Code, OldVars, DstLst, [I|Res], true, Lab); false -> removeUnElems(Code, OldVars, DstLst, [I|Res], Def, Lab) end end end end end; removeUnElems([], _, _, Res,_, Lab) -> {lists:reverse(Res), Lab}. updateTypeFalseLabel(Instr, NewFalseLabel) -> TrueLabel = hipe_icode:type_true_label(Instr), Args = hipe_icode:type_args(Instr), Type = hipe_icode:type_test(Instr), hipe_icode:mk_type(Args, Type, TrueLabel, NewFalseLabel). resolveSuccBlocks(Succs, OldVar, DstLst, Visited, FailLab, Cfg) -> NewSuccs = [X || X <- Succs, not lists:member(X, Visited)], resolveSuccBlocks2(NewSuccs, OldVar, DstLst, Visited, FailLab, Cfg). resolveSuccBlocks2([Succ|Succs], OldVar, DstLst, Vis, FailLab, Cfg) -> Block = hipe_icode_cfg:bb(Cfg,Succ), Code = hipe_bb:code(Block), {NewCode, ReDefined} = checkUsesDefs(Code, OldVar, DstLst, FailLab), NewBlock = hipe_bb:code_update(Block, NewCode), Cfg2 = hipe_icode_cfg:bb_add(Cfg, Succ, NewBlock), case ReDefined of true -> resolveSuccBlocks2(Succs, OldVar, DstLst, [Succ|Vis], FailLab, Cfg2); false -> NewSuccs = hipe_icode_cfg:succ(Cfg, Succ), NewSuccs2 = [X || X <- NewSuccs, not lists:member(X, Vis++Succs)], resolveSuccBlocks2(NewSuccs2++Succs, OldVar, DstLst, [Succ|Vis], FailLab, Cfg2) end; resolveSuccBlocks2([], _, _, _, _, Cfg) -> Cfg. checkUsesDefs(Code, OldVar, DstLst, FailLab) -> checkUsesDefs(Code, OldVar, DstLst, FailLab, [], false). checkUsesDefs([I|Code], OldVar, DstLst, FailLab, Res, Defined) -> [OVar] = OldVar, case hipe_icode:is_move(I) of true -> case hipe_icode:move_src(I) =:= OVar of true -> NewVar = hipe_icode:move_dst(I), checkUsesDefs(Code, NewVar, DstLst, FailLab, [I|Res], true); false -> case lists:member(OVar, hipe_icode:defines(I)) of true -> checkUsesDefs(Code, OldVar, DstLst, FailLab, [I|Res], true); false -> checkUsesDefs(Code, OldVar, DstLst, FailLab, [I|Res], Defined) end end; false -> case hipe_icode:is_type(I) andalso not Defined of true -> case FailLab =/= none of true -> _I2 = updateTypeFalseLabel(I, FailLab), checkUsesDefs(Code, OldVar, DstLst, FailLab, [I|Res], Defined); false -> checkUsesDefs(Code, OldVar, DstLst, FailLab, [I|Res], Defined) end; false -> case (lists:member(OVar, hipe_icode:uses(I))) andalso (not Defined) andalso (FailLab =/= none) of true -> Tpl = hipe_icode:mk_primop(OldVar, mktuple, DstLst), checkUsesDefs(Code, OldVar, DstLst, FailLab, [I,Tpl|Res], true); false -> case lists:member(OVar, hipe_icode:defines(I)) of true -> checkUsesDefs(Code, OldVar, DstLst, FailLab, [I|Res], true); false -> checkUsesDefs(Code, OldVar, DstLst, FailLab, [I|Res],Defined) end end end end; checkUsesDefs([], _, _, _, Res, Defined) -> {lists:reverse(Res), Defined}. insertMiddleFailBlock(Cfg, NewFailLabel, OldFailLabel, OldVar, DstLst) -> case NewFailLabel =:= none of true -> Cfg; false -> NewCode = [hipe_icode:mk_primop(OldVar, mktuple, DstLst), hipe_icode:mk_goto(OldFailLabel)], NewBlock = hipe_bb:mk_bb(NewCode), hipe_icode_cfg:bb_add(Cfg, NewFailLabel, NewBlock) end. isCallLocal(Instr, Fun) -> hipe_icode:is_call(Instr) andalso (hipe_icode:call_type(Instr) =:= local) andalso (hipe_icode:call_fun(Instr) =:= Fun). isCallPrimop(Instr, Fun) -> case hipe_icode:is_call(Instr) of true -> case is_tuple(hipe_icode:call_fun(Instr)) of true -> ((hipe_icode:call_type(Instr) =:= primop) andalso (element(1,hipe_icode:call_fun(Instr)) =:= Fun)); false -> ((hipe_icode:call_type(Instr) =:= primop) andalso (hipe_icode:call_fun(Instr) =:= Fun)) end; false -> false end. %% >-------------------------< Debug code >------------------------------< -ifdef(DEBUG_MULRET). %%>----------------------------------------------------------------------< %% Procedure : printTable/1 %% Purpose : %% Arguments : %% Return : %% Notes : %%>----------------------------------------------------------------------< printTable(Mod, Exports, {FunLst, CallLst}) -> {Y,Mo,D} = date(), {H,Mi,S} = time(), io:format("Module: ~w - (~w/~w-~w, ~w:~w:~w)~n=======~n", [Mod,D,Mo,Y,H,Mi,S]), io:format("Exports: ~w~n", [Exports]), io:format("FunList: ~n"), printFunList(FunLst), io:format("CallList: ~n"), printCallList(CallLst). printFunList([Fun|FunLst]) -> io:format(" ~w~n", [Fun]), printFunList(FunLst); printFunList([]) -> io:format("~n"). printCallList([Call|CallLst]) -> io:format(" ~w~n", [Call]), printCallList(CallLst); printCallList([]) -> io:format("~n"). -endif. %% >----------------------------< Old code >--------------------------------< %% %%>----------------------------------------------------------------------< %% % Procedure : findCallCode/3 %% % Purpose : %% % Arguments : %% % Return : %% % Notes : %% %%>----------------------------------------------------------------------< %% findCallCode(List, Callee, DstLst) -> findCallCode(List, Callee, DstLst, []). %% findCallCode([I=#icode_call{'fun'=Callee, dstlist=Var, type=local}, I2, I3|List], %% Callee, DstLst, Res) -> %% NewList = removeUnElems(List, Var), %% %% _Uses = checkForUses(NewList, Var, DstLst), %% Size = length(DstLst), %% case I2 of %% #icode_type{test={tuple, Size}, args=Var, true_label=Label} -> %% case I3 of %% #icode_label{name=Label} -> %% findCallCode(NewList, Callee, DstLst, %% [I#icode_call{dstlist=DstLst}|Res]); %% _ -> %% findCallCode(NewList, Callee, DstLst, %% [#goto{label=Label}, %% I#icode_call{dstlist=DstLst}|Res]) %% end; %% _ -> %% findCallCode(NewList, Callee, DstLst, %% [I2,I#icode_call{dstlist=DstLst}|Res]) %% end; %% findCallCode([I|List], Callee, DstLst, Res) -> %% findCallCode(List, Callee, DstLst, [I|Res]); %% findCallCode([], _, _, Res) -> lists:reverse(Res). %% %%>----------------------------------------------------------------------< %% % Procedure : checkForUses %% % Purpose : %% % Arguments : %% % Return : %% % Notes : %% %%>----------------------------------------------------------------------< %% checkForUses(List, Var, Dsts) -> checkForUses(List, Var, Dsts, [], List). %% checkForUses([I|List], Var, Dsts, Rest, Code) -> %% Defs = hipe_icode:defines(I), %% Uses = hipe_icode:uses(I), %% case lists:member(Var, Uses) of %% true -> %% true; %% false -> %% case lists:member(Var, Defs) of %% true -> %% false; %% false -> %% case hipe_icode:is_branch(I) of %% true -> %% Succs = hipe_icode:successors(I), %% checkSuccsForUses(Succs, Var, Dsts, Rest, Code); %% false -> %% checkForUses(List, Var, Dsts, [I|Rest], Code) %% end %% end %% end; %% checkForUses([], _, _, _, _) -> false. %% checkSuccsForUses(Succs, Var, Dsts, Rest, Code) -> %% checkSuccsForUses(Succs, Var, Dsts, Rest, Code, false). %% checkSuccsForUses([S|Succs], Var, Dsts, Rest, Code, Res) -> %% List = gotoLabel(S, Code), %% Used = checkForUses(List, Var, Dsts, Rest, Code), %% checkSuccsForUses(Succs, Var, Code, Dsts, Used andalso Res); %% checkSuccsForUses([], _, _, _, _, Res) -> Res. %% gotoLabel(L, [L|List]) -> List; %% gotoLabel(L, [_|List]) -> gotoLabel(L, List); %% gotoLabel(_, []) -> []. %% %%>----------------------------------------------------------------------< %% % Procedure : removeUnElems/2 %% % Purpose : %% % Arguments : %% % Return : %% % Notes : Fixa så att funktionen använder defines(I) istället och %% % selektorer istället för att matcha på #call{}. Lätt gjort. %% %%>----------------------------------------------------------------------< %% removeUnElems(List, Var) -> removeUnElems(List, Var, []). %% removeUnElems([#icode_call{'fun'={unsafe_element,_}, args=Var}|List], Var, Res) -> %% removeUnElems(List, Var, Res); %% removeUnElems([I=#icode_move{dst=Var}|List], [Var], Res) -> %% lists:reverse(Res, [I|List]); %% removeUnElems([I=#icode_call{dstlist=Var}|List], Var, Res) -> %% lists:reverse(Res, [I|List]); %% removeUnElems([I|List], Var, Res) -> %% removeUnElems(List, Var, [I|Res]); %% removeUnElems([], _, Res) -> lists:reverse(Res). %% removeUnElems(List, Var) -> removeUnElems(List, Var, []). %% removeUnElems([I|List], Var, Res) -> %% Defs = hipe_icode:defines(I), %% case hipe_icode:is_call(I) of %% true -> %% Fn = hipe_icode:call_fun(I), %% case (hipe_icode:call_args(I) =:= Var) andalso is_tuple(Fn) of %% true -> %% case element(1,Fn) =:= unsafe_element of %% true -> %% removeUnElems(List, Var, Res); %% false -> %% case lists:member(Var, Defs) of %% true -> %% lists:reverse(Res, [I|List]); %% false -> %% removeUnElems(List, Var, [I|Res]) %% end %% end; %% false -> %% case lists:member(Var, Defs) of %% true -> %% lists:reverse(Res, [I|List]); %% false -> %% removeUnElems(List, Var, [I|Res]) %% end %% end; %% false -> %% case lists:member(Var, Defs) of %% true -> %% lists:reverse(Res, [I|List]); %% false -> %% removeUnElems(List, Var, [I|Res]) %% end %% end; %% removeUnElems([], _, Res) -> lists:reverse(Res). %% Old findDefine that also could update it. %% ----------------------------------------- %% findDefine(Code, Var) -> findDefine(Code, Var, [], []). %% findDefine([#icode_call{dstlist=Var,'fun'=mktuple,args=Vs}|Code],Var,NewCode,_)-> %% findDefine(Code, Var, NewCode, Vs); %% findDefine([I=#icode_move{dst=Var, src=Src}|Code], [Var], NewCode, _) -> %% case Src of %% #icode_var{} -> %% findDefine(Code, [Src], [I|NewCode], [Src]); %% #icode_const{value={flat, Tuple}} -> %% findDefine(Code, [Var], [I|NewCode], []) %% Check this case! [Var] %% end; %% findDefine([I|Code], Var, NewCode, Vars) -> %% findDefine(Code, Var, [I|NewCode], Vars); %% findDefine([], _, NewCode, Vars) -> %% case Vars of %% [] -> %% notFound; %% [_] -> %% {notFound, Vars}; %% _ -> %% {found, lists:reverse(NewCode), Vars} %% end. %% modifyCode(Code, Var) -> %% [#icode_return{vars=Var}|Code2] = lists:reverse(Code), %% case (length(Var) =< hipe_rtl_arch:nr_of_return_regs()) of %% true -> %% {Arity, Code3} = modifyCode(Code2, Var, []), %% {Arity, Code3}; %% false -> %% {1, Code} %% end. %% modifyCode([I|Code], Var, Res) -> %% case scanInstr(I, Var) of %% {move, Arity, VarLst} -> %% Code2 = [#icode_return{vars=VarLst}, I |lists:reverse(Res, Code)], %% {Arity, lists:reverse(Code2)}; %% {mktuple, Arity, VarLst} -> %% Code2 = [#icode_return{vars=VarLst}|lists:reverse(Res, Code)], %% {Arity, lists:reverse(Code2)}; %% other -> %% modifyCode(Code, Var, [I|Res]) %% end; %% modifyCode([], Var, Res) -> %% {1, lists:reverse(Res, [#icode_return{vars=Var}]}. %% scanInstr(#icode_call{dstlist=Var, 'fun'=mktuple, args=Lst}, Var) -> %% {mktuple, length(Lst), Lst}; %% scanInstr(_, _) -> other. %% printCode(Cfg) -> %% Labels = hipe_icode_cfg:labels(Cfg), %% {_,_,{_,F,_,_,_,_,_,_},_} = Cfg, %% io:format("~nFunction: ~w~n", [F]), %% Print = fun(Label) -> %% Block = hipe_icode_cfg:bb(Cfg, Label), %% Code = hipe_bb:code(Block), %% io:format("Label: ~w~n", [Label]), %% lists:foreach(fun(I) -> io:format("~w~n", [I]) end, Code), %% io:format("~n") %% end, %% lists:foreach(Print, Labels). %% printList(File, [{MFA, #icode{code=Code, params=Parms}}|List]) -> %% io:format(File, "MFA: ~w - Params: ~w~n", [MFA, Parms]), %% printList2(File, Code), %% printList(File, List); %% printList(_, []) -> ok. %% printList2(File, []) -> io:format(File, "~n~n", []); %% printList2(File, IList) when is_list(IList) -> %% [I|List] = IList, %% io:format(File, "~w~n", [I]), %% printList2(File, List); %% printList2(File, SomethingElse) -> %% io:format(File, "Got: ~w~n", [SomethingElse]). %% optimizeDefine([#icode_call{dstlist=Var,'fun'=mktuple,args=Vs}|Code], %% Var, _, Res) -> %% case Vs of %% [_] -> %% {none, noOpt}; %% _ -> %% optimizeDefine(Code, Var, Vs, Res) %% end; %% optimizeDefine([I=#icode_move{dst=Var, src=Src}|Code], [Var], Rets, Res) -> %% case hipe_icode:is_var(Src) of %% true -> %% optimizeDefine(Code, [Src], Rets, Res); %% false -> %% case Src of %% #icode_const{value={flat, Tuple}} when is_tuple(Tuple) -> %% optimizeDefine(Code, [Var], tuple_to_list(Tuple), [I|Res]); %% #icode_const{value={flat, _}} -> %% {none, noOpt}; %% _ -> %% optimizeDefine(Code, [Var], Rets, [I|Res]) %% end %% end; %% optimizeDefine([I|Code], Var, Rets, Res) -> %% optimizeDefine(Code, Var, Rets, [I|Res]); %% optimizeDefine([], Var, Rets, Res) -> %% case Rets of %% [] -> %% {none, Res, Var}; %% _ -> %% {found, Res, Rets} %% end.