%% -*- 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. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%@doc %% GRAPH COLORING REGISTER ALLOCATOR %% %% A simple graph coloring register allocator: %% %% - build interference graph + estimate spill costs %% - simplify graph (push on stack + spill) %% - select colors %% %% Emits a coloring: a list of {TempName,Location} %% where Location is {reg,N} or {spill,M} %% and {reg,N} denotes some register N %% and {spill,M} denotes the Mth spilled node %% You have to figure out how to rewrite the code yourself. %% %% This version uses vectors rather than hash tables, and uses %% faster algorithms since all vars are known at the start. %% The result should be considerably quicker than earlier versions. %% %% Deficiencies: %% - no renaming (to reduce unnecessary register pressure) %% - spill costs are naive (should use better; e.g., exec.estimates) %% - no biased coloring (which coalesces moves) %% - no live range splitting (possibly not critical) %% %% *** NOTE *** %% Uses apply for target specific functions, takes the module name as %% argument. This target specific module should implement all target %% specific functions, see the end of the file. %% -module(hipe_graph_coloring_regalloc). -export([regalloc/7]). %%-ifndef(DO_ASSERT). %%-define(DO_ASSERT, true). %%-endif. %%-ifndef(DEBUG). %%-define(DEBUG,0). %%-endif. -include("../main/hipe.hrl"). %% Define these as 'ok' or 'report(X,Y)' depending on how much output you want. -define(report0(X,Y), ?IF_DEBUG_LEVEL(0,?msg(X, Y),ok)). -define(report(X,Y), ?IF_DEBUG_LEVEL(1,?msg(X, Y),ok)). -define(report2(X,Y), ?IF_DEBUG_LEVEL(2,?msg(X, Y),ok)). -define(report3(X,Y), ?IF_DEBUG_LEVEL(3,?msg(X, Y),ok)). %% Given CFG and number of colors K, produce a coloring list %% of items {reg,N} (0 =< N =< K) and {spill,M}, where M is %% an index denoting 'a location'. %% (You might use it as a stack index, perhaps.) %% %% You can in principle delete check_coloring/2; it merely checks %% that the coloring agrees with the interference graph (that is, that %% no neighbors have the same register or spill location). %% @spec regalloc(#cfg{}, liveness(), non_neg_fixnum(), non_neg_fixnum(), %% module(), tgt_ctx(), list()) -> {, non_neg_fixnum()} regalloc(CFG, Live, SpillIndex, SpillLimit, TargetMod, TargetContext, _Options) -> Target = {TargetMod, TargetContext}, PhysRegs = allocatable(Target), ?report2("building IG~n", []), {IG, Spill} = build_ig(CFG, Live, Target), %% check_ig(IG), ?report3("graph: ~p~nphysical regs: ~p~n", [list_ig(IG), PhysRegs]), %% These nodes *can't* be allocated to registers. NotAllocatable = non_alloc(CFG, Target), %% i.e. Arguments on x86 ?report2("Nonalloc ~w~n", [NotAllocatable]), {Cols, NewSpillIndex} = color(IG, Spill, ordsets:from_list(PhysRegs), SpillIndex, SpillLimit, number_of_temporaries(CFG, Target), Target, NotAllocatable), Coloring = [{X, {reg, X}} || X <- NotAllocatable] ++ Cols, ?ASSERT(check_coloring(Coloring, IG, Target)), {Coloring, NewSpillIndex}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% *** BUILD THE INTERFERENCE GRAPH *** %% %% Returns {Interference_graph, Spill_cost_dictionary} %% build_ig(CFG, Live, Target) -> NumN = number_of_temporaries(CFG, Target), % poss. N-1? {IG, Spill} = build_ig_bbs(labels(CFG, Target), CFG, Live, empty_ig(NumN), empty_spill(NumN), Target), {normalize_ig(IG), Spill}. build_ig_bbs([], _CFG, _Live, IG, Spill, _Target) -> {IG, Spill}; build_ig_bbs([L|Ls], CFG, Live, IG, Spill, Target) -> Xs = bb(CFG, L, Target), {_, NewIG, NewSpill} = build_ig_bb(Xs, liveout(Live, L, Target), IG, Spill, Target), build_ig_bbs(Ls, CFG, Live, NewIG, NewSpill, Target). build_ig_bb([], LiveOut, IG, Spill, _Target) -> {LiveOut, IG, Spill}; build_ig_bb([X|Xs], LiveOut, IG, Spill, Target) -> {Live,NewIG,NewSpill} = build_ig_bb(Xs, LiveOut, IG, Spill, Target), build_ig_instr(X, Live, NewIG, NewSpill, Target). %% Note: We could add move-related arcs here as well. %% %% Note: Ideally, we would like to add all registers to the IG %% at once rather than doing 'add_nodes' for each instruction. %% (This is costly, since nodes that already are present are checked!) build_ig_instr(X, Live, IG, Spill, Target) -> {Def, Use} = def_use(X, Target), ?report3("Live ~w\n~w : Def: ~w Use ~w\n", [Live, X, Def,Use]), DefList = ordsets:to_list(Def), NewSpill = inc_spill_costs(DefList, inc_spill_costs(ordsets:to_list(Use), Spill)), NewIG = interference_arcs(DefList, ordsets:to_list(Live), IG), NewLive = ordsets:union(Use, ordsets:subtract(Live, Def)), {NewLive, NewIG, NewSpill}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% interference_arcs([], _Live, IG) -> IG; interference_arcs([X|Xs], Live, IG) -> interference_arcs(Xs, Live, i_arcs(X, Live, IG)). i_arcs(_X, [], IG) -> IG; i_arcs(X, [Y|Ys], IG) -> i_arcs(X, Ys, add_edge(X,Y, IG)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% inc_spill_costs([], Spill) -> Spill; inc_spill_costs([X|Xs], Spill) -> inc_spill_costs(Xs, inc_spill_cost(X, Spill)). inc_spill_cost(X, Spill) -> set_spill_cost(X, get_spill_cost(X, Spill)+1, Spill). get_spill_cost(X, Spill) -> spill_cost_lookup(X, Spill). set_spill_cost(X, N, Spill) -> spill_cost_update(X, N, Spill). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% *** COLORING *** %% %% Coloring is done straightforwardly: %% - find the low-degree nodes, put them in low %% - while low non-empty: %% * remove x from low %% * push x on stack %% * decrement degree of neighbors of x %% * for each neighbor y of low degree, put y on low %% - when low empty: %% - if graph empty, return stack %% - otherwise %% * select a node z to spill %% * push z on stack %% * decrement degree of neighbors of z %% * add low-degree neighbors of z to low %% * restart the while-loop above color(IG, Spill, PhysRegs, SpillIx, SpillLimit, NumNodes, Target, NotAllocatable) -> ?report("simplification of IG~n", []), K = ordsets:size(PhysRegs), Nodes = list_ig(IG), Low = low_degree_nodes(Nodes, K, NotAllocatable), %% Any nodes above the spillimit must be colored first... MustNotSpill = if NumNodes > SpillLimit -> sort_on_degree(lists:seq(SpillLimit,NumNodes-1) -- Low,IG); true -> [] end, ?report(" starting with low degree nodes ~p~n",[Low]), EmptyStk = [], Precolored = all_precoloured(Target), {Stk, NewSpillIx} = simplify(Low, NumNodes, Precolored, IG, Spill, K, SpillIx, EmptyStk, SpillLimit, Target, NotAllocatable, MustNotSpill), ?report("selecting colors~n",[]), {select(Stk, Precolored, IG, K, PhysRegs, NumNodes, Target), NewSpillIx}. sort_on_degree(Nodes, IG) -> [ Node3 || {_,Node3} <- lists:sort([{degree(Info),Node2} || {Info,Node2} <- [{hipe_vectors:get(IG, Node), Node} || Node <- Nodes]])]. %%%%%%%%%%%%%%%%%%%% %% %% Simplification: push all easily colored nodes on a stack; %% when the list of easy nodes becomes empty, see if graph is %% empty as well. If it is not, spill a node and continue. %% If it is empty, return the stack. %% %% Notes: %% - We keep the set of visited nodes around for spill purposes %% (visited nodes are not considered for spilling) %% %% - At present, nodes can be pushed onto the stack even if they %% already are on the stack. This can be fixed by another 'Vis' %% dictionary that keeps track of what is on the stack. %% Currently, we just skip already colored nodes. %% %% - Arguments: %% Low: low-degree nodes (ready to color) %% NumNodes: number of remaining nodes in graph %% IG: interference graph %% Spill: spill costs of nodes %% K: number of colors %% Ix: next spill index %% Stk: stack of already simplified nodes %% %% Physical registers are marked as 'visited' prior to simplify. %% This has the following effect: %% - they are not considered for spilling %% - they are not pushed on the stack %% - since we do NOT decrement degrees of surrounding vars, the %% non-physreg variables must still take them into account. simplify(Low, NumNodes, PreC, IG, Spill, K, Ix, Stk, SpillLimit, Target, NotAllocatable, MustNotSpill) -> Vis = visit_all(PreC, none_visited(NumNodes)), Vis1 = visit_all(NotAllocatable, Vis), ActualNumNodes = (NumNodes-length(PreC))-length(NotAllocatable), %% Make sure that the registers that must not be spilled %% get a degree less than K by spilling other regs. {Stk2, Ix2, Vis2, Low2} = handle_non_spill(MustNotSpill, IG, Spill, K, Ix, Stk, Vis1, Low, SpillLimit, Target), simplify_ig(Low2, ActualNumNodes-length(Stk2), IG, Spill, K, Ix2, Stk2, Vis2, SpillLimit, Target). handle_non_spill([], _IG, _Spill, _K, Ix, Stk, Vis, Low, _SpillLimit, _Target) -> {Stk, Ix, Vis, Low}; handle_non_spill([X|Xs] = L, IG, Spill, K, Ix, Stk, Vis, Low, SpillLimit, Target) -> Info = hipe_vectors:get(IG, X), Degree = degree(Info), ?report("Can't Spill ~w with degree ~w\n", [X,Degree]), if Degree > K -> ?report(" *** spill required (N<~w)***~n", [SpillLimit]), {Y, NewLow, NewIG} = spill(IG, Vis, Spill, K, SpillLimit, Target), NewVis = visit(Y,Vis), {NewStk, NewIx} = push_spill_node(Y, Ix, Stk), ?report(" node ~w spilled~n", [Y]), handle_non_spill(L, NewIG, Spill, K, NewIx, NewStk, NewVis, Low ++ NewLow, SpillLimit, Target); true -> {NewLow, NewIG} = decrement_neighbors(X, Low, IG, Vis, K), ?report(" node ~w pushed\n(~w now ready)~n", [X,NewLow]), NewStk = push_colored(X, Stk), handle_non_spill(Xs, NewIG, Spill, K, Ix, NewStk, visit(X,Vis), NewLow, SpillLimit, Target) end. simplify_ig([], 0, _IG, _Spill, _K, Ix, Stk, _Vis, _SpillLimit, _Target) -> {Stk, Ix}; simplify_ig([], N, IG, Spill, K, Ix, Stk, Vis, SpillLimit, Target) when N > 0 -> ?report3("N: ~w Stk: ~w N+Stk ~w\n", [N,length(Stk),N+length(Stk)]), ?report(" *** spill required (N<~w)***~n", [SpillLimit]), {X, Low, NewIG} = spill(IG, Vis, Spill, K, SpillLimit, Target), NewVis = visit(X,Vis), {NewStk, NewIx} = push_spill_node(X, Ix, Stk), ?report(" node ~w spilled\n(~w now ready)~n", [X, Low]), simplify_ig(Low, N-1, NewIG, Spill, K, NewIx, NewStk, NewVis, SpillLimit, Target); simplify_ig([X|Xs], N, IG, Spill, K, Ix, Stk, Vis, SpillLimit, Target) -> ?report3("N: ~w Stk: ~w N+Stk ~w\n", [N,length(Stk),N+length(Stk)]), case is_visited(X,Vis) of true -> ?report(" node ~p already visited~n",[X]), simplify_ig(Xs, N, IG, Spill, K, Ix, Stk, Vis, SpillLimit, Target); false -> ?report("Stack ~w\n", [Stk]), {NewLow, NewIG} = decrement_neighbors(X, Xs, IG, Vis, K), ?report(" node ~w pushed\n(~w now ready)~n", [X,NewLow]), NewStk = push_colored(X, Stk), simplify_ig(NewLow, N-1, NewIG, Spill, K, Ix, NewStk, visit(X,Vis), SpillLimit, Target) end. %% Returns { NowLowDegreeNeighbors, NewIG } decrement_neighbors(X, Xs, IG, Vis, K) -> Ns = unvisited_neighbors(X, Vis, IG), ?report(" node ~p has neighbors ~w\n(unvisited ~p)~n", [X, neighbors(X, IG), Ns]), decrement_each(Ns, Xs, IG, Vis, K). %% For each node, decrement its degree and check if it is now %% a low-degree node. In that case, add it to the 'low list'. decrement_each([], Low, IG, _Vis, _K) -> {Low, IG}; decrement_each([N|Ns], OldLow, IG, Vis, K) -> {Low, CurrIG} = Res = decrement_each(Ns, OldLow, IG, Vis, K), case is_visited(N, Vis) of true -> Res; false -> {D, NewIG} = decrement_degree(N, CurrIG), if D =:= K-1 -> {[N|Low], NewIG}; true -> {Low, NewIG} end end. %%%%%%%%%%%%%%%%%%%% %% %% The spill cost of a node is: %% est_spill_cost / current_degree %% %% For all unvisited nodes, compute spill cost and select the minimum. %% This node is chosen to be spilled. Then decrement the degree of its %% neighbors, and return those of low degree. %% %% Notes: %% - A better method for computing spill costs is to just keep the %% minimum cost node. But for debugging purposes, we compute a list %% of {node,spillcost} pairs and select the minimum. %% %% Returns: %% {Spilled_node, Low_degree_neighbors, New_interference_graph} spill(IG, Vis, Spill, K, SpillLimit, Target) -> Ns = list_ig(IG), Costs = spill_costs(Ns, IG, Vis, Spill, SpillLimit, Target), ?report3("spill costs are ~p~n", [Costs]), ActualCosts = lists:sort(Costs), ?report3("actual costs are ~p~n", [ActualCosts]), case ActualCosts of [] -> ?error_msg("There is no node to spill", []), ?EXIT('no node to spill'); [{_Cost,N}|_] -> {Low, NewIG} = decrement_neighbors(N, [], IG, Vis, K), %% ?report("spilled node ~p at cost ~p (~p now ready)~n", [N,Cost,Low]), {N, Low, NewIG} end. spill_costs([], _IG, _Vis, _Spill, _SpillLimit, _Target) -> []; spill_costs([{N,Info}|Ns], IG, Vis, Spill, SpillLimit, Target) -> case degree(Info) of 0 -> spill_costs(Ns,IG,Vis,Spill, SpillLimit, Target); Deg -> case is_visited(N,Vis) of true -> spill_costs(Ns,IG,Vis,Spill, SpillLimit, Target); _ -> case is_fixed(N, Target) of true -> spill_costs(Ns, IG, Vis, Spill, SpillLimit, Target); false -> if N >= SpillLimit -> spill_costs(Ns, IG, Vis, Spill, SpillLimit, Target); true -> [{spill_cost_of(N,Spill)/Deg,N} | spill_costs(Ns,IG, Vis, Spill, SpillLimit, Target)] end end end end. %%%%%%%%%%%%%%%%%%%% %% %% Returns a list of {Name,Location}, where Location is %% either {spill,M} or {reg,R} %% %% Note: we use pessimistic coloring here. %% - we could use optimistic coloring: for spilled node, check if there is %% an unused color among the neighbors and choose that. select(Stk, PreC, IG, K, PhysRegs, NumNodes, Target) -> %% NumNodes = length(Stk)+length(PreC), {PhysColors, Cols} = precolor(PreC, none_colored(NumNodes), Target), ?report("precoloring has yielded ~p~n",[list_coloring(Cols)]), PhysColors ++ select_colors(Stk, IG, Cols, PhysRegs, K). select_colors([], _IG, _Cols, _PhysRegs, _K) -> ?report("all nodes colored~n",[]), []; select_colors([{X,colorable}|Xs], IG, Cols, PhysRegs, K) -> ?report("color of ~p\n",[X]), {Reg,NewCols} = select_color(X, IG, Cols, PhysRegs), ?report("~p~n",[Reg]), [{X,{reg,Reg}} | select_colors(Xs, IG, NewCols, PhysRegs, K)]; %%select_colors([{X,{spill,M}}|Xs], IG, Cols, PhysRegs, K) -> %% ?report('spilled: ~p~n',[X]), %% %% Check if optimistic coloring could have found a color %% case catch select_color(X,IG,Cols,K) of %% {'EXIT',_} -> % no color possible %% ?report('(no optimistic color)~n',[]), %% [{X,{spill,M}}|select_colors(Xs, IG, Cols, PhysRegs, K)]; %% {Reg,NewCols} -> %% ?report('(optimistic color: ~p)~n',[Reg]), %% [{X,{reg,Reg}}|select_colors(Xs, IG, Cols, PhysRegs, K)] %% end. %% Old code / pessimistic coloring: select_colors([{X,{spill,M}}|Xs], IG, Cols, PhysRegs, K) -> ?report("spilled: ~p~n",[X]), %% Check if optimistic coloring could have found a color %% case catch select_color(X,IG,Cols,K) of %% {'EXIT',_} -> % no color possible %% ?report('(no optimistic color)~n',[]); %% {Reg,NewCols} -> %% ?report('(optimistic color: ~p)~n',[Reg]) %% end, [{X,{spill,M}} | select_colors(Xs, IG, Cols, PhysRegs, K)]. select_color(X, IG, Cols, PhysRegs) -> UsedColors = get_colors(neighbors(X, IG), Cols), Reg = select_unused_color(UsedColors, PhysRegs), {Reg, set_color(X, Reg, Cols)}. %%%%%%%%%%%%%%%%%%%% get_colors([], _Cols) -> []; get_colors([X|Xs], Cols) -> case color_of(X, Cols) of uncolored -> get_colors(Xs, Cols); {color,R} -> [R|get_colors(Xs, Cols)] end. select_unused_color(UsedColors, PhysRegs) -> Summary = ordsets:from_list(UsedColors), AvailRegs = ordsets:to_list(ordsets:subtract(PhysRegs, Summary)), hd(AvailRegs). %% select_avail_reg(AvailRegs). %% We choose the register to use randomly from the set of available %% registers. %% %% Note: Another way of doing it is LRU-order: %% - Have an LRU-queue of register names; when coloring, try the colors in that %% order (some may be occupied). %% - When a color has been selected, put it at the end of the LRU. %% select_avail_reg(Regs) -> %% case get(seeded) of %% undefined -> %% random:seed(), %% put(seeded,true); %% true -> %% ok %% end, %% NReg = length(Regs), %% RegNo = random:uniform(NReg), %% lists:nth(RegNo, Regs). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% push_spill_node(X, M, Stk) -> {[{X,{spill,M}}|Stk], M+1}. push_colored(X, Stk) -> [{X, colorable} | Stk]. %%%%%%%%%%%%%%%%%%%% low_degree_nodes([], _K, _NotAllocatable) -> []; low_degree_nodes([{N,Info}|Xs], K, NotAllocatable) -> case lists:member(N, NotAllocatable) of true -> low_degree_nodes(Xs,K, NotAllocatable); false -> ?report0("node ~p has degree ~p: ~w~n",[N,degree(Info),neighbors(Info)]), Deg = degree(Info), if Deg < K -> [N|low_degree_nodes(Xs, K, NotAllocatable)]; true -> low_degree_nodes(Xs, K, NotAllocatable) end end. %%%%%%%%%%%%%%%%%%%% unvisited_neighbors(X, Vis, IG) -> ordsets:from_list(unvisited(neighbors(X,IG), Vis)). unvisited([], _Vis) -> []; unvisited([X|Xs], Vis) -> case is_visited(X, Vis) of true -> unvisited(Xs, Vis); false -> [X|unvisited(Xs, Vis)] end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% *** ABSTRACT DATATYPES *** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% The ig datatype: %% %% Note: if we know the number of temps used, we can use a VECTOR %% instead, which will speed up things. %% %% Note: later on, we may wish to add 'move-related' support. -record(ig_info, {neighbors=[], degree=0 :: integer()}). empty_ig(NumNodes) -> hipe_vectors:new(NumNodes, #ig_info{neighbors=[], degree=0}). degree(Info) -> Info#ig_info.degree. neighbors(Info) -> Info#ig_info.neighbors. add_edge(X, X, IG) -> IG; add_edge(X, Y, IG) -> add_arc(X, Y, add_arc(Y, X, IG)). add_arc(X, Y, IG) -> Info = hipe_vectors:get(IG, X), Old = neighbors(Info), New = Info#ig_info{neighbors=[Y|Old]}, hipe_vectors:set(IG, X, New). normalize_ig(IG) -> Size = hipe_vectors:size(IG), normalize_ig(Size-1, IG). normalize_ig(-1, IG) -> IG; normalize_ig(I, IG) -> Info = hipe_vectors:get(IG, I), N = ordsets:from_list(neighbors(Info)), NewIG = hipe_vectors:set(IG, I, Info#ig_info{neighbors=N, degree=length(N)}), normalize_ig(I-1, NewIG). %%degree(X, IG) -> %% Info = hipe_vectors:get(IG, X), %% Info#ig_info.degree. neighbors(X, IG) -> Info = hipe_vectors:get(IG, X), Info#ig_info.neighbors. decrement_degree(X, IG) -> Info = hipe_vectors:get(IG, X), Degree = degree(Info), NewDegree = Degree-1, NewInfo = Info#ig_info{degree=NewDegree}, {NewDegree, hipe_vectors:set(IG,X,NewInfo)}. list_ig(IG) -> hipe_vectors:list(IG). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% The spill cost datatype: %% %% Note: if we know the number of temps used, we can use a VECTOR %% instead, which will speed up things. empty_spill(NumNodes) -> hipe_vectors:new(NumNodes, 0). spill_cost_of(X, Spill) -> hipe_vectors:get(Spill, X). spill_cost_lookup(X, Spill) -> spill_cost_of(X, Spill). spill_cost_update(X, N, Spill) -> hipe_vectors:set(Spill, X, N). %%list_spill_costs(Spill) -> %% hipe_vectors:list(Spill). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% The coloring datatype: none_colored(NumNodes) -> hipe_vectors:new(NumNodes,uncolored). color_of(X,Cols) -> hipe_vectors:get(Cols,X). set_color(X,R,Cols) -> hipe_vectors:set(Cols,X,{color,R}). -ifdef(DEBUG). list_coloring(Cols) -> hipe_vectors:list(Cols). -endif. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Note: there might be a slight gain in separating the two versions %% of visit/2 and visited/2. (So that {var,X} selects X and calls the %% integer version. none_visited(NumNodes) -> hipe_vectors:new(NumNodes, false). visit(X,Vis) -> hipe_vectors:set(Vis, X, true). is_visited(X,Vis) -> hipe_vectors:get(Vis, X). visit_all([], Vis) -> Vis; visit_all([X|Xs], Vis) -> visit_all(Xs, visit(X, Vis)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Check that all arcs in IG are bidirectional + degree is correct %% check_ig(IG) -> %% check_ig(list_ig(IG),IG). %% check_ig([],IG) -> %% ok; %% check_ig([{N,Info}|Xs],IG) -> %% Ns = neighbors(Info), %% NumNs = length(Ns), %% D = degree(Info), %% if %% D =:= NumNs -> %% ok; %% true -> %% ?WARNING_MSG('node ~p has degree ~p but ~p neighbors~n',[N,D,NumNs]) %% end, %% check_neighbors(N,Ns,IG), %% check_ig(Xs,IG). %% check_neighbors(N,[],IG) -> %% ok; %% check_neighbors(N,[M|Ms],IG) -> %% Ns = neighbors(M,IG), %% case member(N,Ns) of %% true -> %% ok; %% true -> %% ?WARNING_MSG('node ~p should have ~p as neighbor (has ~p)~n',[M,N,Ns]) %% end, %% check_neighbors(N,Ms,IG). -ifdef(DO_ASSERT). %%%%%%%%%%%%%%%%%%%% %% Check that the coloring is correct (if the IG is correct): %% check_coloring(Coloring, IG, Target) -> ?report0("checking coloring ~p~n",[Coloring]), check_cols(list_ig(IG),init_coloring(Coloring, Target)). init_coloring(Xs, Target) -> hipe_temp_map:cols2tuple(Xs, Target). check_color_of(X, Cols) -> %% if %% is_precoloured(X) -> %% phys_reg_color(X,Cols); %% true -> case hipe_temp_map:find(X, Cols) of unknown -> ?WARNING_MSG("node ~p: color not found~n", [X]), uncolored; C -> C end. check_cols([], Cols) -> ?report("coloring valid~n",[]), true; check_cols([{X,Info}|Xs], Cols) -> Cs = [{N, check_color_of(N, Cols)} || N <- neighbors(Info)], C = check_color_of(X, Cols), case valid_coloring(X, C, Cs) of yes -> check_cols(Xs, Cols); {no,Invalids} -> ?WARNING_MSG("node ~p has same color (~p) as ~p~n", [X,C,Invalids]), check_cols(Xs, Cols) end. valid_coloring(X, C, []) -> yes; valid_coloring(X, C, [{Y,C}|Ys]) -> case valid_coloring(X, C, Ys) of yes -> {no, [Y]}; {no,Zs} -> {no, [Y|Zs]} end; valid_coloring(X, C, [_|Ys]) -> valid_coloring(X, C, Ys). -endif. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% *** INTERFACES TO OTHER MODULES *** %% all_precoloured({TgtMod,TgtCtx}) -> TgtMod:all_precoloured(TgtCtx). allocatable({TgtMod,TgtCtx}) -> TgtMod:allocatable(TgtCtx). is_fixed(Reg, {TgtMod,TgtCtx}) -> TgtMod:is_fixed(Reg, TgtCtx). labels(CFG, {TgtMod,TgtCtx}) -> TgtMod:labels(CFG, TgtCtx). liveout(CFG, L, Target={TgtMod,TgtCtx}) -> ordsets:from_list(reg_names(TgtMod:liveout(CFG, L, TgtCtx), Target)). bb(CFG, L, {TgtMod,TgtCtx}) -> hipe_bb:code(TgtMod:bb(CFG, L, TgtCtx)). def_use(X, Target={TgtMod,TgtCtx}) -> {ordsets:from_list(reg_names(TgtMod:defines(X,TgtCtx), Target)), ordsets:from_list(reg_names(TgtMod:uses(X,TgtCtx), Target))}. non_alloc(CFG, Target={TgtMod,TgtCtx}) -> reg_names(TgtMod:non_alloc(CFG, TgtCtx), Target). number_of_temporaries(CFG, {TgtMod,TgtCtx}) -> TgtMod:number_of_temporaries(CFG, TgtCtx). reg_names(Regs, {TgtMod,TgtCtx}) -> [TgtMod:reg_nr(X,TgtCtx) || X <- Regs]. %% %% Precoloring: use this version when a proper implementation of %% physical_name(X) is available! %% precolor(Xs, Cols, Target) -> ?report("precoloring ~p~n", [Xs]), {_Cs, _NewCol} = Res = precolor0(Xs, Cols, Target), ?report(" yielded ~p~n", [_Cs]), Res. precolor0([], Cols, _Target) -> {[], Cols}; precolor0([R|Rs], Cols, Target) -> {Cs, Cols1} = precolor0(Rs, Cols, Target), {[{R, {reg, physical_name(R, Target)}}|Cs], set_color(R, physical_name(R, Target), Cols1)}. physical_name(X, {TgtMod,TgtCtx}) -> TgtMod:physical_name(X, TgtCtx).