%% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2004-2016. 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_dominators.erl %% Author : Christoffer Vikström %% Daniel Deogun %% Jesper Bengtsson %% Created : 18 Mar 2002 %% %% @doc %% Contains utilities for creating and manipulating dominator trees %% and dominance frontiers from a CFG. %% @end %%------------------------------------------------------------------------ -module(hipe_dominators). -export([domTree_create/1, domTree_getChildren/2, domTree_dominates/3, domFrontier_create/2, domFrontier_get/2]). -export_type([domTree/0]). -include("cfg.hrl"). %%======================================================================== %% %% CODE FOR CREATING AND MANIPULATING DOMINATOR TREES. %% %%======================================================================== -record(workDataCell, {dfnum = 0 :: non_neg_integer(), dfparent = none :: 'none' | cfg_lbl(), semi = none :: 'none' | cfg_lbl(), ancestor = none :: 'none' | cfg_lbl(), best = none :: 'none' | cfg_lbl(), samedom = none :: 'none' | cfg_lbl(), bucket = [] :: [cfg_lbl()]}). -record(domTree, {root :: cfg_lbl(), size = 0 :: non_neg_integer(), nodes = gb_trees:empty() :: gb_trees:tree()}). -opaque domTree() :: #domTree{}. %%>----------------------------------------------------------------------< %% Procedure : domTree_create/1 %% Purpose : Creates a complete dominator tree given a CFG. %% Arguments : CFG - a Control Flow Graph representation %% Returns : A dominator tree %%>----------------------------------------------------------------------< -spec domTree_create(cfg()) -> domTree(). domTree_create(CFG) -> {WorkData, DFS, N} = dfs(CFG), DomTree = domTree_empty(hipe_gen_cfg:start_label(CFG)), {DomData, WorkData2} = getIdoms(CFG, DomTree, WorkData, N, DFS), finalize(WorkData2, DomData, 1, N, DFS). %%>----------------------------------------------------------------------< %% Procedure : domTree_empty/0 %% Purpose : Creates an empty dominator tree. %% Arguments : The root node %% Returns : A dominator tree %%>----------------------------------------------------------------------< domTree_empty(Node) -> #domTree{root = Node}. %%>----------------------------------------------------------------------< %% Procedure : domTree_createNode/2 %% Purpose : Creates a new node and inserts it into the dominator tree. %% Arguments : Node - The new node %% DomTree - The target dominator tree %% Returns : A dominator tree %%>----------------------------------------------------------------------< domTree_createNode(Node, DomTree) -> DomTree2 = domTree_setNodes(DomTree, gb_trees:enter(Node, {none,[]}, domTree_getNodes(DomTree))), domTree_incSize(DomTree2). %%>----------------------------------------------------------------------< %% Procedure : domTree_getNode/2 %% Purpose : Returns a specific node in the dominator tree. %% Arguments : Node - The new node %% DomTree - The target dominator tree %% Returns : Node %%>----------------------------------------------------------------------< domTree_getNode(Node, DomTree) -> gb_trees:lookup(Node, domTree_getNodes(DomTree)). %%>----------------------------------------------------------------------< %% Procedure : domTree_getNodes/1 %% Purpose : Retrieves the nodes from a dominator tree. %% Arguments : DomTree - The target dominator tree %% Returns : A map containing the nodes of the dominator tree. %%>----------------------------------------------------------------------< domTree_getNodes(#domTree{nodes=Nodes}) -> Nodes. %%>----------------------------------------------------------------------< %% Procedure : domTree_setNodes/2 %% Purpose : Replaces the set of nodes in a dominator tree with a %% new set of nodes. %% Arguments : Nodes - The new set of nodes %% DomTree - The target dominator tree %% Returns : DomTree %%>----------------------------------------------------------------------< domTree_setNodes(DomTree, Nodes) -> DomTree#domTree{nodes = Nodes}. %%>----------------------------------------------------------------------< %% Procedure : domTree_setSize/2 %% Purpose : Sets the size of the dominator tree, i.e. the number of %% nodes in it. %% Arguments : Size - The new size of the target dominator tree %% DomTree - The target dominator tree %% Returns : A dominator tree %%>----------------------------------------------------------------------< domTree_setSize(DomTree, Size) -> DomTree#domTree{size = Size}. %%>----------------------------------------------------------------------< %% Procedure : domTree_incSize/1 %% Purpose : Increases the size of the dominator tree with one. %% Arguments : DomTree - The target dominator tree %% Returns : DomTree %%>----------------------------------------------------------------------< domTree_incSize(DomTree) -> Size = domTree_getSize(DomTree), domTree_setSize(DomTree, Size + 1). %%>----------------------------------------------------------------------< %% Procedure : get IDom/2 %% Purpose : Retrieves the immediate dominators of a node in the %% dominator tree. %% Arguments : Node - The new node %% DomTree - The target dominator tree %% Returns : The immediate dominator %%>----------------------------------------------------------------------< domTree_getIDom(Node, DomTree) -> case domTree_getNode(Node, DomTree) of {value, {IDom, _}} -> IDom; none -> [] end. %%>----------------------------------------------------------------------< %% Procedure : getChildren/2 %% Purpose : Retrieves the children of a node in the dominator tree. %% Arguments : Node - The new node %% DomTree - The target dominator tree %% Returns : [children] %%>----------------------------------------------------------------------< -spec domTree_getChildren(cfg_lbl(), domTree()) -> [cfg_lbl()]. domTree_getChildren(Node, DomTree) -> case domTree_getNode(Node, DomTree) of {value, {_, Children}} -> Children; none -> [] end. %%>----------------------------------------------------------------------< %% Procedure : domTree_getSize/1 %% Purpose : Retrieves the size of a dominator tree. %% Arguments : DomTree - The target dominator tree %% Returns : A number denoting the size of the dominator tree %%>----------------------------------------------------------------------< domTree_getSize(#domTree{size=Size}) -> Size. %%>----------------------------------------------------------------------< %% Procedure : domTree_getRoot/2 %% Purpose : Retrieves the number of the root node in the dominator tree. %% Arguments : DomTree - The target dominator tree %% Returns : Number %%>----------------------------------------------------------------------< domTree_getRoot(#domTree{root=Root}) -> Root. %%>----------------------------------------------------------------------< %% Procedure : domTree_addChild/3 %% Purpose : Inserts a new node as a child to another node in the %% dominator tree. %% Arguments : Node - The old node that should get a new child %% Child - The new child node %% DomTree - The target dominator tree %% Returns : DomTree %%>----------------------------------------------------------------------< domTree_addChild(Node, Child, DomTree) -> {IDom, Children} = case domTree_getNode(Node, DomTree) of {value, Tuple} -> Tuple; none -> {none, []} end, Nodes = case lists:member(Child, Children) of true -> domTree_getNodes(DomTree); false -> gb_trees:enter(Node, {IDom, [Child|Children]}, domTree_getNodes(DomTree)) end, domTree_setNodes(DomTree, Nodes). %%>----------------------------------------------------------------------< %% Procedure : setIDom/3 %% Purpose : Sets the immediate domminator of a node in the domminator tree. %% Arguments : Node - The node whose immediate domminator we are seting %% IDom - The immediate domminator %% DomTree - The target dominator tree %% Returns : DomTree %% Notes : Is used to build the dominator tree. %%>----------------------------------------------------------------------< setIDom(Node, IDom, DomTree) -> DomTree1 = case domTree_getNode(Node, DomTree) of none -> domTree_createNode(Node, DomTree); _ -> DomTree end, DomTree2 = domTree_addChild(IDom, Node, DomTree1), {value, {_, Children}} = domTree_getNode(Node, DomTree2), domTree_setNodes(DomTree2, gb_trees:enter(Node, {IDom, Children}, domTree_getNodes(DomTree2))). %%>----------------------------------------------------------------------< %% Procedure : lookup %% Purpose : This function is used as a wrapper for the lookup function. %% The function retrieves a particular element (defined by %% Field) stored in a workDataCell in the table (defined by %% Table). %% Arguments : Field - Value defined in the workDataCell record %% Key - Value used as a key in the table %% Table - Table storing workDataCells %% Returns : A value defined in the workDataCell record %%>----------------------------------------------------------------------< lookup({Field, Key}, Table) when is_integer(Key) -> WD = lookup_table(Key, Table), case Field of ancestor -> WD#workDataCell.ancestor; best -> WD#workDataCell.best; bucket -> WD#workDataCell.bucket; dfnum -> WD#workDataCell.dfnum; dfparent -> WD#workDataCell.dfparent; samedom -> WD#workDataCell.samedom; semi -> WD#workDataCell.semi end. lookup_table(Key, Table) when is_integer(Key) -> case gb_trees:lookup(Key, Table) of {value, Data} -> Data; none -> #workDataCell{} end. %%>----------------------------------------------------------------------< %% Procedure : update %% Purpose : This function is used as a wrapper for the update function %% The main purpose of the update function is therefore %% change a particular cell in the table (Table) to the %% value given as an argument (Value). %% Arguments : Key - Value used as a key in the table %% Field - Value defined in the workDataCell record. %% Value - The new value that should replace the old in the table %% Table - Table storing workDataCells %% Returns : NewTable %%>----------------------------------------------------------------------< update(Key, {Field, Value}, Table) -> gb_trees:enter(Key, updateCell(Value, Field, lookup_table(Key, Table)), Table); update(Key, List, Table) -> gb_trees:enter(Key, update(List, lookup_table(Key, Table)), Table). update([{Field, Value} | T], WD) -> update(T, updateCell(Value, Field, WD)); update([], WD) -> WD. updateCell(Value, Field, WD) -> case Field of dfnum -> WD#workDataCell{dfnum = Value}; dfparent -> WD#workDataCell{dfparent= Value}; semi -> WD#workDataCell{semi = Value}; ancestor -> WD#workDataCell{ancestor= Value}; best -> WD#workDataCell{best = Value}; samedom -> WD#workDataCell{samedom = Value}; bucket -> WD#workDataCell{bucket = Value} end. %%>----------------------------------------------------------------------< %% Procedure : dfs/1 %% Purpose : The main purpose of this function is to traverse the CFG in %% a depth first order. It is aslo used to initialize certain %% elements defined in a workDataCell. %% Arguments : CFG - a Control Flow Graph representation %% Returns : A table (WorkData) and the total number of elements in %% the CFG. %%>----------------------------------------------------------------------< dfs(CFG) -> {WorkData, DFS, N} = dfs(CFG, hipe_gen_cfg:start_label(CFG), none, 1, gb_trees:empty(), gb_trees:empty()), {WorkData, DFS, N-1}. dfs(CFG, Node, Parent, N, WorkData, DFS) -> case lookup({dfnum, Node}, WorkData) of 0 -> WorkData2 = update(Node, [{dfnum, N}, {dfparent, Parent}, {semi, Node}, {best, Node}], WorkData), DFS2 = gb_trees:enter(N, Node, DFS), dfsTraverse(hipe_gen_cfg:succ(CFG, Node), CFG, Node, N + 1, WorkData2, DFS2); _ -> {WorkData, DFS, N} end. %%>----------------------------------------------------------------------< %% Procedure : dfsTraverse/6 %% Purpose : This function acts as a help function for the dfs algorithm %% in the sence that it traverses a list of nodes given by the %% CFG. %% Arguments : Node - The first element in the node list %% SuccLst - The remainder of the node list %% CFG - Control Flow Graph representation %% Parent - Node representing the parent of the Node defined %% above. %% N - The total number of processed nodes. %% WorkData - Table consisting of workDataCells %% Returns : An updated version of the table (WorkData) and the %% total number of nodes processed. %%>----------------------------------------------------------------------< dfsTraverse([Node|T], CFG, Parent, N, WorkData, DFS) -> {WorkData2, DFS2, N2} = dfs(CFG, Node, Parent, N, WorkData, DFS), dfsTraverse(T, CFG, Parent, N2, WorkData2, DFS2); dfsTraverse([], _, _, N, WorkData, DFS) -> {WorkData, DFS, N}. %%>----------------------------------------------------------------------< %% Procedure : getIdoms/6 %% Purpose : The purpose of this function is to compute the immediate %% dominators. This is accomplished by traversing the CFG nodes %% by their depth first number in a bottom up manner. That is, %% the nodes are processed in a backward order (highest to %% lowest number). %% Arguments : CFG - Control Flow Graph representation %% DomData - Table consisting of domTree cells %% WorkData - Table consisting of workDataCells %% Index - The index used for retrieving the node to be %% processed %% Returns : An updated version of the tables DomData and WorkData %%>----------------------------------------------------------------------< getIdoms(CFG, DomData, WorkData, Index, DFS) when is_integer(Index), Index > 1 -> Node = lookup_table(Index, DFS), PredLst = hipe_gen_cfg:pred(CFG, Node), Par = lookup({dfparent, Node}, WorkData), DfNumN = lookup({dfnum, Node}, WorkData), {S, WorkData2} = getSemiDominator(PredLst, DfNumN, Par, WorkData), WorkData3 = update(Node, {semi, S}, WorkData2), OldBucket = lookup({bucket, S}, WorkData3), WorkData4 = update(S, {bucket, [Node | OldBucket]}, WorkData3), WorkData5 = linkTrees(Par, Node, WorkData4), {WorkData6, DomData2} = filterBucket(lookup({bucket, Par}, WorkData5), Par, WorkData5, DomData), WorkData7 = update(Par, {bucket, []}, WorkData6), getIdoms(CFG, DomData2, WorkData7, Index - 1, DFS); getIdoms(_, DomData, WorkData, 1, _) -> {DomData, WorkData}. %%>----------------------------------------------------------------------< %% Procedure : getSemiDominator/4 %% Purpose : The main purpose of this algorithm is to compute the semi %% dominator of the node Node based on the Semidominator Theorem %% Arguments : Preds - The list of predecessors of the node Node %% Node - Node in the CFG %% S - Parent of node Node (depth first parent) %% WorkData - Table consisting of workDataCells %% Returns : A tuple containing the semidominator and an updated version %% of the table WorkData. %%>----------------------------------------------------------------------< getSemiDominator([Pred|Preds], DfNumChild, S, WorkData) -> {Sp, WorkData3} = case lookup({dfnum, Pred}, WorkData) =< DfNumChild of true -> {Pred, WorkData}; false -> {AncLowSemi, WorkData2} = getAncestorWithLowestSemi(Pred, WorkData), {lookup({semi, AncLowSemi}, WorkData2), WorkData2} end, S2 = case lookup({dfnum, Sp}, WorkData3) < lookup({dfnum, S}, WorkData3) of true -> Sp; false -> S end, getSemiDominator(Preds, DfNumChild, S2, WorkData3); getSemiDominator([], _, S, WorkData) -> {S, WorkData}. %%>----------------------------------------------------------------------< %% Procedure : getAncestorWithLowestSemi/2 %% Purpose : The main purpose of this function is to retrieve the ancestor %% of a node with the lowest depth first number (semi). The %% function is also using path compression, i.e. it remembers the %% best node (the one with the lowest semi number) and hence the %% algorithm is only processing the minimal number of nodes. %% Arguments : Node - Node in the tree %% WorkData - Table consisting of workDataCells %% Returns : A node (the one with the lowest semi) and an updated version %% of the table WorkData. %%>----------------------------------------------------------------------< getAncestorWithLowestSemi(Node, WorkData) -> Best = lookup({best, Node}, WorkData), case lookup({ancestor, Node}, WorkData) of none -> {Best, WorkData}; A -> case lookup({ancestor, A}, WorkData) of none -> {Best, WorkData}; _ -> {B, WorkData2} = getAncestorWithLowestSemi(A, WorkData), AncA = lookup({ancestor, A}, WorkData2), WorkData3 = update(Node, {ancestor, AncA}, WorkData2), DfSemiB = lookup({dfnum, lookup({semi, B}, WorkData3)}, WorkData3), BestN = lookup({best, Node}, WorkData3), SemiB = lookup({semi, BestN}, WorkData3), DfSemiBestN = lookup({dfnum, SemiB}, WorkData3), case DfSemiB < DfSemiBestN of true -> {B, update(Node, {best, B}, WorkData3)}; false -> {BestN, WorkData3} end end end. %%>----------------------------------------------------------------------< %% Procedure : linkTrees/3 %% Purpose : The main purpose of this function is to combine two trees %% into one (accomplished by setting the ancestor for node %% Node to Parent). The algorithm is also updating the best field %% in the workDataCell for node Node to the value of itself. %% Arguments : Parent - The parent of the node Node. %% Node - The node to process %% WorkData - Table consisting of workDataCells %% Returns : An updated version of table WorkData %%>----------------------------------------------------------------------< linkTrees(Parent, Node, WorkData) -> update(Node, [{ancestor, Parent}, {best, Node}], WorkData). %%>----------------------------------------------------------------------< %% Procedure : filterBucket/4 %% Purpose : The purpose of this algorith is to compute the dominator of %% the node Node by utilizing the first clause of the Dominator %% Theorem. If the first clause of the theorem doesn't apply %% then the computation of that particular node is deferred to %% a later stage (see finalize). %% Arguments : Nodes - The list of CFG nodes that need to be computed. %% Parent - The parent of the nodes in the list Nodes %% WorkData - Table consisting of workDataCells %% DomData - Table consisting of domTree cells. %% Returns : An updated version of the tables WorkData and DomData %%>----------------------------------------------------------------------< filterBucket([Node|Nodes], Parent, WorkData, DomData) -> {Y, WorkData2} = getAncestorWithLowestSemi(Node, WorkData), {WorkData3, DomData2} = case lookup({semi, Y}, WorkData2) =:= lookup({semi, Node}, WorkData2) of true -> {WorkData2, setIDom(Node, Parent, DomData)}; false -> {update(Node, {samedom, Y}, WorkData2), DomData} end, filterBucket(Nodes, Parent, WorkData3, DomData2); filterBucket([], _, WorkData, DomData) -> {WorkData, DomData}. %%>----------------------------------------------------------------------< %% Procedure : finalize/5 %% Purpose : This algorithm finishes up the second clause of the Dominator %% Theorem. Hence, the main purpose of this function is therefore %% to update the dominator tree with the nodes that were deferred %% in the filterBucket algorithm. %% Arguments : WorkData - Table consisting of workDataCells %% DomData - Table consisting of domTree cells %% N - The index used for retrieving the node to be %% processed %% Max - Maximum node index %% Returns : An updated version of the table DomData %%>----------------------------------------------------------------------< finalize(WorkData, DomData, N, Max, DFS) when N =< Max -> Node = lookup_table(N, DFS), case lookup({samedom, Node}, WorkData) of none -> finalize(WorkData, DomData, N + 1, Max, DFS); SameDomN -> case domTree_getIDom(SameDomN, DomData) of IdomSameDomN when is_integer(IdomSameDomN) -> DomData2 = setIDom(Node, IdomSameDomN, DomData), finalize(WorkData, DomData2, N + 1, Max, DFS) end end; finalize(_, DomData, _, _, _) -> DomData. %%>----------------------------------------------------------------------< %% Procedure : domTree_dominates/3 %% Purpose : checks wheter Node1 dominates Node2 with respect to the %% dominator tree DomTree %% Arguments : Node1 the possible dominator, Node2 which might be dominated %% and DomTree - the target dominator tree. %% Notes : Relies on lists:any to return false when the a list is empty %%>----------------------------------------------------------------------< -spec domTree_dominates(cfg_lbl(), cfg_lbl(), domTree()) -> boolean(). domTree_dominates(Node1, Node1, _DomTree) -> true; domTree_dominates(Node1, Node2, DomTree) -> Children = domTree_getChildren(Node1, DomTree), lists:any(fun(X) -> domTree_dominates(X, Node2, DomTree) end, Children). %%>----------------------------------------------------------------------< %% Procedure : pp/1 %% Purpose : Pretty Printing a dominator tree. %% Arguments : DomTree - the target dominator tree. %% Notes : Uses pp/2 and pp_children to perform its task. %%>----------------------------------------------------------------------< -ifdef(DEBUG). domTree_pp(DomTree) -> io:format("Domtree:\nRoot: ~w\nSize: ~w\n", [domTree_getRoot(DomTree), domTree_getSize(DomTree)]), domTree_pp(domTree_getRoot(DomTree), DomTree). domTree_pp(N, DomTree) -> case domTree_getNode(N, DomTree) of {value, {IDom, Children}} -> io:format("Node: ~w\n\tIDom: ~w\n\tChildren: ~w\n\n", [N, IDom, Children]), domTree_pp_children(Children, DomTree); none -> failed end. domTree_pp_children([Child|T], DomTree) -> domTree_pp(Child, DomTree), domTree_pp_children(T, DomTree); domTree_pp_children([], _) -> ok. -endif. %% DEBUG %%======================================================================== %% %% CODE FOR CREATING AND MANIPULATING DOMINANCE FRONTIERS. %% %%======================================================================== -type domFrontier() :: gb_trees:tree(). %%>----------------------------------------------------------------------< %% Procedure : domFrontier_create %% Purpose : This function calculates the Dominance Frontiers given %% a CFG and a Dominator Tree. %% Arguments : SuccMap - The successor map of the CFG we are working with. %% DomTree - The dominance tree of the CFG. %% Notes : DomTree must actually be the dominance tree of the CFG. %%>----------------------------------------------------------------------< -spec domFrontier_create(cfg(), domTree()) -> domFrontier(). domFrontier_create(SuccMap, DomTree) -> df_create(domTree_getRoot(DomTree), SuccMap, DomTree, df__empty()). df_create(Node, SuccMap, DomTree, DF) -> Children = domTree_getChildren(Node, DomTree), Succ = hipe_gen_cfg:succ(SuccMap, Node), DF1 = checkIDomList(Succ, Node, DomTree, DF), makeDFChildren(Children, Node, SuccMap, DomTree, DF1). %%>----------------------------------------------------------------------< %% Procedure : domFrontier_get %% Purpose : This function returns the Dominance Frontier for Node. %% Arguments : Node - The node whose Dominance Frontier we request %% DF - The Dominance Frontier structure %% Returns : %%>----------------------------------------------------------------------< -spec domFrontier_get(cfg_lbl(), domFrontier()) -> [cfg_lbl()]. domFrontier_get(Node, DF) -> case gb_trees:lookup(Node, DF) of {value, List} -> List; none -> [] end. %%>----------------------------------------------------------------------< %% Procedure : df__empty %% Purpose : This function creates an empty instance of the Dominance %% Frontiers (DF) structure. %%>----------------------------------------------------------------------< df__empty() -> gb_trees:empty(). %%>----------------------------------------------------------------------< %% Procedure : df__add %% Purpose : This function adds Node to N in DF. %% Arguments : N - The value being inserted %% Node - The node getting the value %% DF - The Dominance Frontiers %% Returns : DF %% Notes : If Node already exists at position N, it is not added again. %%>----------------------------------------------------------------------< df__add_to_node(N, Node, DF) -> case gb_trees:lookup(N, DF) of {value, DFList} -> case lists:member(Node, DFList) of true -> DF; false -> gb_trees:update(N, [Node|DFList], DF) end; none -> gb_trees:insert(N, [Node], DF) end. %%>----------------------------------------------------------------------< %% Procedure : makeDFChildren %% Purpose : This function calculates the dominance frontiers of the %% children of the parent and adds the nodes in these %% dominance frontiers who are not immediate dominantors of %% the parent to parents dominance frontier. %% Arguments : ChildList - The list of children that the function traverses %% Parent - The parent of the children %% SuccMap - The successor map of the CFG %% DomTree - The dominantor tree of the CFG %% DF - The dominance frontiers so far %%>----------------------------------------------------------------------< makeDFChildren([Child|T], Parent, SuccMap, DomTree, DF) -> DF1 = df_create(Child, SuccMap, DomTree, DF), DF2 = checkIDomList(domFrontier_get(Child, DF1), Parent, DomTree, DF1), makeDFChildren(T, Parent, SuccMap, DomTree, DF2); makeDFChildren([], _, _, _, DF) -> DF. %%>----------------------------------------------------------------------< %% Procedure : checIDomList %% Purpose : Adds all the nodes in the list to the parents dominance %% frontier who do not have parent as immediate dominator. %% Arguments : NodeList - The list of nodes that the function traverses %% Parent - The parent of the nodes %% DomTree - Our dominator tree %% DF - The dominance frontiers so far %%>----------------------------------------------------------------------< checkIDomList([Node|T], Parent, DomTree, DF) -> DF1 = checkIDom(Node, Parent, DomTree, DF), checkIDomList(T, Parent, DomTree, DF1); checkIDomList([], _, _, DF) -> DF. %%>----------------------------------------------------------------------< %% Procedure : checkIdom %% Purpose : Adds Node1 to Node2's dominance frontier if Node2 is not %% Node1's immediate dominator. %% Arguments : Node1 - a node %% Node2 - another node %% DomTree - the dominator tree %% DF - the dominance frontier so far %%>----------------------------------------------------------------------< checkIDom(Node1, Node2, DomTree, DF) -> case domTree_getIDom(Node1, DomTree) of Node2 -> DF; none -> DF; _ -> df__add_to_node(Node2, Node1, DF) end.