aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/regalloc/hipe_optimistic_regalloc.erl')
-rw-r--r--lib/hipe/regalloc/hipe_optimistic_regalloc.erl2043
1 files changed, 2043 insertions, 0 deletions
diff --git a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
new file mode 100644
index 0000000000..183ec1994c
--- /dev/null
+++ b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl
@@ -0,0 +1,2043 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%-----------------------------------------------------------------------
+%% File : hipe_optimistic_regalloc.erl
+%% Authors : NilsOla Linnermark <[email protected]>
+%% Petter Holmberg <[email protected]>
+%% Purpose : Play paintball with registers on a target machine. We win
+%% if they are all colored. This is an optimistic coalescing
+%% register allocator.
+%% Created : Spring 2005
+%%-----------------------------------------------------------------------
+
+-module(hipe_optimistic_regalloc).
+-export([regalloc/5]).
+
+-ifndef(DEBUG).
+%%-define(DEBUG,true).
+-else.
+-ifndef(COMPARE_ITERATED_OPTIMISTIC).
+%% If this macro is turned on you can easily compare
+%% each intermediate step in the iterated coalescing
+%% register allocator and the optimitsitc coalescing
+%% register allocator. This is useful for debugging -
+%% many small erlang functions should render the same
+%% register allocaton for both allocators.
+-define(COMPARE_ITERATED_OPTIMISTIC, true).
+-endif.
+-endif.
+-include("../main/hipe.hrl").
+-ifdef(DEBUG_PRINTOUTS).
+-define(print_adjacent(IG), hipe_ig:print_adjacent(IG)).
+-define(print_degrees(IG), hipe_ig:print_degrees(IG)).
+-define(print_spill_costs(IG), hipe_ig:print_spill_costs(IG)).
+-define(mov_print_memberships(MV), hipe_moves:print_memberships(MV)).
+-define(reg_print_memberships(WL), hipe_reg_worklists:print_memberships(WL)).
+-define(print_alias(A), printAlias(A)).
+-define(print_colors(T,C), printColors(T,C)).
+-else.
+-define(print_adjacent(IG), no_print).
+-define(print_degrees(IG), no_print).
+-define(print_spill_costs(IG), no_print).
+-define(mov_print_memberships(MV), no_print).
+-define(reg_print_memberships(WL), no_print).
+-define(print_alias(A), no_print).
+-define(print_colors(T,C), no_print).
+-endif.
+
+
+%%-----------------------------------------------------------------------
+%% Function: regalloc
+%%
+%% Description: Creates a K coloring for a function.
+%% Parameters:
+%% CFG -- A control flow graph
+%% SpillIndex -- Last index of spill variable
+%% SpillLimit -- Temporaris with numbers higher than this have
+%% infinit spill cost.
+%% Consider changing this to a set.
+%% Target -- The module containing the target-specific functions.
+%%
+%% Returns:
+%% Coloring -- A coloring for specified CFG
+%% SpillIndex0 -- A new spill index
+%%-----------------------------------------------------------------------
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+regalloc(CFG, SpillIndex, SpillLimit, Target, _Options) ->
+ ?debug_msg("optimistic ~w\n",[Target]),
+ ?debug_msg("CFG: ~p\n",[CFG]),
+ %% Build interference graph
+ ?debug_msg("Build IG\n",[]),
+ IG_O = hipe_ig:build(CFG, Target),
+ IG = hipe_ig:build(CFG, Target),
+ ?debug_msg("adjlist: ~p\n",[hipe_ig:adj_list(IG)]),
+ ?debug_msg("IG:\n",[]),
+ ?print_adjacent(IG),
+ ?print_degrees(IG),
+ ?print_spill_costs(IG),
+
+ SavedSpillCosts = hipe_ig:spill_costs(IG),
+ SavedAdjList = hipe_ig:adj_list(IG),
+
+ ?debug_msg("Init\n",[]),
+ No_temporaries = Target:number_of_temporaries(CFG),
+ ?debug_msg("Coalescing RA: num_temps = ~p~n", [No_temporaries]),
+ Allocatable = Target:allocatable(),
+ K = length(Allocatable),
+ All_colors = colset_from_list(Allocatable),
+ ?debug_msg("K: ~w~nAll_colors: ~p\n",[K, All_colors]),
+
+ %% Add registers with their own coloring
+ ?debug_msg("Moves\n",[]),
+ Move_sets_O = hipe_moves:new(IG_O),
+ Move_sets = hipe_moves:new(IG),
+ ?debug_msg("Move_sets:\n ~p\n",[Move_sets]),
+ ?mov_print_memberships(Move_sets),
+
+ ?debug_msg("Build Worklist\n",[]),
+ Worklists_O = hipe_reg_worklists:new(IG_O, Target, CFG, Move_sets_O, K, No_temporaries),
+ ?debug_msg("Worklists:\n ~p\n", [Worklists_O]),
+ ?reg_print_memberships(Worklists_O),
+
+ Worklists = hipe_reg_worklists:new(IG, Target, CFG, K, No_temporaries),
+ ?debug_msg("New Worklists:\n ~p\n", [Worklists]),
+ ?reg_print_memberships(Worklists),
+
+ Alias_O = initAlias(No_temporaries),
+ Alias = initAlias(No_temporaries),
+ ?print_alias(Alias),
+
+ ?debug_msg("Do coloring\n~p~n",[Worklists_O]),
+ {IG0_O, Worklists0_O, Moves0_O, Alias0_O} =
+ do_coloring(IG_O, Worklists_O, Move_sets_O, Alias_O,
+ K, SpillLimit, Target),
+ ?debug_msg("IG_O after color:\n ~p\n",[IG0_O]),
+ ?print_adjacent(IG0_O),
+ ?print_degrees(IG0_O),
+ ?print_spill_costs(IG0_O),
+ ?debug_msg("Move_sets after color:\n ~p\n",[Moves0_O]),
+ ?mov_print_memberships(Moves0_O),
+ ?debug_msg("Worklists after color:\n ~p\n", [Worklists0_O]),
+ ?reg_print_memberships(Worklists0_O),
+
+ {IG0, Moves0, Alias0, Worklists0} =
+ do_coalescing(IG, Worklists, Move_sets, Alias, K, Target),
+ ?debug_msg("IG after coalescing:\n",[]),
+ ?print_adjacent(IG0),
+ ?print_degrees(IG0),
+ ?print_spill_costs(IG0),
+ ?debug_msg("Move_sets after coalescing:\n ~p\n",[Moves0]),
+ ?mov_print_memberships(Moves0),
+ ?debug_msg("New Worklists after coalescing:\n ~p\n",
+ [Worklists0]),
+ ?reg_print_memberships(Worklists0),
+
+ {IG1, Worklists1, Moves1, Alias1} =
+ do_simplify_or_spill(IG0, Worklists0, Moves0, Alias0,
+ K, SpillLimit, Target),
+ ?debug_msg("IG after simplify_or_spill:\n",[]),
+ ?print_adjacent(IG1),
+ ?print_degrees(IG1),
+ ?print_spill_costs(IG1),
+ ?debug_msg("Saved spill costs ~p~n", [SavedSpillCosts]),
+ ?debug_msg("Move_sets after simplify_or_spill:\n ~p\n",[Moves1]),
+ ?mov_print_memberships(Moves1),
+ ?debug_msg("New Worklists after simplify_or_spill:\n ~p\n",
+ [Worklists1]),
+ ?reg_print_memberships(Worklists1),
+ ?print_alias(Alias1),
+
+ %% only for testing undoCoalescing and member_coalesced_to
+ %test_undoCoalescing(No_temporaries, Alias1, Worklists1),
+
+ %% only for testing fixAdj
+ %?debug_msg("adj_lists_before_fixAdj ~n~p~n", [hipe_ig:adj_list(IG1)]),
+ %IG2 = test_fixAdj(No_temporaries, SavedAdjList, IG1, Target),
+ %?debug_msg("adj_lists__after_fixAdj ~n~p~n", [hipe_ig:adj_list(IG2)]),
+
+ ?debug_msg("Init node sets\n",[]),
+ Node_sets = hipe_node_sets:new(),
+ %% ?debug_msg("NodeSet: ~w\n NonAlloc ~w\n",[Node_sets,Target:non_alloc(CFG)]),
+ ?debug_msg("Default coloring\n",[]),
+ {Color0,Node_sets1} =
+ defaultColoring(Target:all_precoloured(),
+ initColor(No_temporaries), Node_sets, Target),
+ ?debug_msg("Color0\n",[]),
+ ?print_colors(No_temporaries, Color0),
+
+ ?debug_msg("----------------------Assign colors _N\n",[]),
+
+ Stack = hipe_reg_worklists:stack(Worklists1),
+ ?debug_msg("The stack _N ~p~n", [Stack]),
+ %SortedStack = sort_stack(Stack),
+ %?debug_msg("The stack _N ~p~n", [SortedStack]),
+
+ %?debug_msg("Nodes _N ~w~n", [Node_sets1]),
+
+ {Color1,Node_sets2,Alias2} =
+ assignColors(Worklists1, Stack, Node_sets1, Color0,
+ No_temporaries, SavedAdjList, SavedSpillCosts, IG1, Alias1, All_colors, Target),
+ ?print_colors(No_temporaries, Color1),
+ ?debug_msg("Nodes:~w\nNodes2:~w\nNo_temporaries:~w\n",[Node_sets,Node_sets2,No_temporaries]),
+
+ ?debug_msg("Build mapping _N ~w\n",[Node_sets2]),
+ Coloring = build_namelist(Node_sets2,SpillIndex,Alias2,Color1),
+ ?debug_msg("Coloring ~p\n",[Coloring]),
+ SortedColoring = { sort_stack(element(1, Coloring)), element(2, Coloring)},
+ ?debug_msg("SortedColoring ~p\n",[SortedColoring]),
+ %%Coloring.
+ ?debug_msg("----------------------Assign colors _O\n",[]),
+ {Color1_O,Node_sets2_O} =
+ assignColors_O(hipe_reg_worklists:stack(Worklists0_O), Node_sets1, Color0,
+ Alias0_O, All_colors, Target),
+ ?print_colors(No_temporaries, Color1_O),
+ ?debug_msg("Nodes:~w\nNodes2:~w\nNo_temporaries:~w\n",[Node_sets,Node_sets2_O,No_temporaries]),
+
+ ?debug_msg("Build mapping ~w\n",[Node_sets2_O]),
+ Coloring_O = build_namelist_O(Node_sets2_O,SpillIndex,Alias0_O,Color1_O),
+ ?debug_msg("Coloring_O ~p\n",[Coloring_O]),
+ SortedColoring_O = {sort_stack(element(1, Coloring_O)), element(2, Coloring_O)},
+ ?debug_msg("SortedColoring_O ~p\n",[SortedColoring_O]),
+ sanity_compare(SortedColoring_O, SortedColoring),
+ Coloring.
+-else.
+regalloc(CFG, SpillIndex, SpillLimit, Target, _Options) ->
+ ?debug_msg("optimistic ~w\n",[Target]),
+ ?debug_msg("CFG: ~p\n",[CFG]),
+ %% Build interference graph
+ ?debug_msg("Build IG\n",[]),
+ IG = hipe_ig:build(CFG, Target),
+ ?debug_msg("adjlist: ~p\n",[hipe_ig:adj_list(IG)]),
+ ?debug_msg("IG:\n",[]),
+ ?print_adjacent(IG),
+ ?print_degrees(IG),
+ ?print_spill_costs(IG),
+
+ SavedSpillCosts = hipe_ig:spill_costs(IG),
+ SavedAdjList = hipe_ig:adj_list(IG),
+
+ ?debug_msg("Init\n",[]),
+ No_temporaries = Target:number_of_temporaries(CFG),
+ ?debug_msg("Coalescing RA: num_temps = ~p~n", [No_temporaries]),
+ Allocatable = Target:allocatable(),
+ K = length(Allocatable),
+ All_colors = colset_from_list(Allocatable),
+ ?debug_msg("K: ~w~nAll_colors: ~p\n",[K, All_colors]),
+
+ %% Add registers with their own coloring
+ ?debug_msg("Moves\n",[]),
+ Move_sets = hipe_moves:new(IG),
+ ?debug_msg("Move_sets:\n ~p\n",[Move_sets]),
+ ?mov_print_memberships(Move_sets),
+
+ ?debug_msg("Build Worklist\n",[]),
+
+ Worklists = hipe_reg_worklists:new(IG, Target, CFG, K, No_temporaries),
+ ?debug_msg("New Worklists:\n ~p\n", [Worklists]),
+ ?reg_print_memberships(Worklists),
+
+ Alias = initAlias(No_temporaries),
+ ?print_alias(Alias),
+
+ {IG0, Moves0, Alias0, Worklists0} =
+ do_coalescing(IG, Worklists, Move_sets, Alias, K, Target),
+ ?debug_msg("IG after coalescing:\n",[]),
+ ?print_adjacent(IG0),
+ ?print_degrees(IG0),
+ ?print_spill_costs(IG0),
+ ?debug_msg("Move_sets after coalescing:\n ~p\n",[Moves0]),
+ ?mov_print_memberships(Moves0),
+ ?debug_msg("New Worklists after coalescing:\n ~p\n",
+ [Worklists0]),
+ ?reg_print_memberships(Worklists0),
+
+ {IG1, Worklists1, _Moves1, Alias1} =
+ do_simplify_or_spill(IG0, Worklists0, Moves0, Alias0,
+ K, SpillLimit, Target),
+ ?debug_msg("IG after simplify_or_spill:\n",[]),
+ ?print_adjacent(IG1),
+ ?print_degrees(IG1),
+ ?print_spill_costs(IG1),
+ ?debug_msg("Saved spill costs ~p~n", [SavedSpillCosts]),
+ ?debug_msg("New Worklists after simplify_or_spill:\n ~p\n",
+ [Worklists1]),
+ ?reg_print_memberships(Worklists1),
+ ?print_alias(Alias1),
+
+ %% only for testing undoCoalescing and member_coalesced_to
+ %test_undoCoalescing(No_temporaries, Alias1, Worklists1),
+
+ %% only for testing fixAdj
+ %?debug_msg("adj_lists_before_fixAdj ~n~p~n", [hipe_ig:adj_list(IG1)]),
+ %IG2 = test_fixAdj(No_temporaries, SavedAdjList, IG1, Target),
+ %?debug_msg("adj_lists__after_fixAdj ~n~p~n", [hipe_ig:adj_list(IG2)]),
+
+ ?debug_msg("Init node sets\n",[]),
+ Node_sets = hipe_node_sets:new(),
+ %% ?debug_msg("NodeSet: ~w\n NonAlloc ~w\n",[Node_sets,Target:non_alloc(CFG)]),
+ ?debug_msg("Default coloring\n",[]),
+ {Color0,Node_sets1} =
+ defaultColoring(Target:all_precoloured(),
+ initColor(No_temporaries), Node_sets, Target),
+ ?debug_msg("Color0\n",[]),
+ ?print_colors(No_temporaries, Color0),
+
+ ?debug_msg("----------------------Assign colors _N\n",[]),
+
+ Stack = hipe_reg_worklists:stack(Worklists1),
+ ?debug_msg("The stack _N ~p~n", [Stack]),
+ %SortedStack = sort_stack(Stack),
+ %?debug_msg("The stack _N ~p~n", [SortedStack]),
+
+ %?debug_msg("Nodes _N ~w~n", [Node_sets1]),
+
+ {Color1,Node_sets2,Alias2} =
+ assignColors(Worklists1, Stack, Node_sets1, Color0,
+ No_temporaries, SavedAdjList, SavedSpillCosts, IG1, Alias1, All_colors, Target),
+ ?print_colors(No_temporaries, Color1),
+ ?debug_msg("Nodes:~w\nNodes2:~w\nNo_temporaries:~w\n",[Node_sets,Node_sets2,No_temporaries]),
+
+ ?debug_msg("Build mapping _N ~w\n",[Node_sets2]),
+ Coloring = build_namelist(Node_sets2,SpillIndex,Alias2,Color1),
+ ?debug_msg("Coloring ~p\n",[Coloring]),
+ Coloring.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: do_coloring
+%%
+%% Description: Create a coloring. That is, play paintball.
+%% Parameters:
+%% IG -- An interference graph
+%% Worklists -- Worklists, that is simplify, spill and freeze
+%% Moves -- Moves sets, that is coalesced, constrained
+%% and so on.
+%% Alias -- Tells if two temporaries can have their value
+%% in the same register.
+%% K -- Want to create a K coloring.
+%% SpillLimit -- Try not to spill nodes that are above the spill limit.
+%%
+%% Returns:
+%% IG -- Updated interference graph
+%% Worklists -- Updated Worklists structure
+%% Moves -- Updated Moves structure
+%% Alias -- Updates Alias structure
+%%
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+do_coloring(IG, Worklists, Moves, Alias, K, SpillLimit, Target) ->
+ Simplify = not(hipe_reg_worklists:is_empty_simplify(Worklists)),
+ Coalesce = not(hipe_moves:is_empty_worklist(Moves)),
+ Freeze = not(hipe_reg_worklists:is_empty_freeze(Worklists)),
+ Spill = not(hipe_reg_worklists:is_empty_spill(Worklists)),
+ if Simplify =:= true ->
+ {IG0, Worklists0, Moves0} =
+ simplify_O(hipe_reg_worklists:simplify(Worklists),
+ IG,
+ Worklists,
+ Moves,
+ K),
+ do_coloring(IG0, Worklists0, Moves0, Alias, K, SpillLimit, Target);
+ Coalesce =:= true ->
+ {Moves0, IG0, Worklists0, Alias0} =
+ coalesce_O(Moves, IG, Worklists, Alias, K, Target),
+ do_coloring(IG0, Worklists0, Moves0, Alias0, K, SpillLimit, Target);
+ Freeze =:= true ->
+ {Worklists0, Moves0} =
+ freeze(K, Worklists, Moves, IG, Alias),
+ do_coloring(IG, Worklists0, Moves0, Alias, K, SpillLimit, Target);
+ Spill =:= true ->
+ {Worklists0, Moves0} =
+ selectSpill_O(Worklists, Moves, IG, K, Alias, SpillLimit),
+ do_coloring(IG, Worklists0, Moves0, Alias, K, SpillLimit, Target);
+ true -> % Catchall case
+ {IG, Worklists, Moves, Alias}
+ end.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: do_coalescing
+%%
+%% Description: Try to coalesce everything (find out later if it was
+%% possible).
+%% Parameters:
+%% IG -- An interference graph
+%% Moves -- Moves sets, that is coalesced, constrained
+%% and so on.
+%% Alias -- Tells if two temporaries can have their value
+%% in the same register.
+%%
+%% Returns:
+%% IG -- Updated interference graph
+%% Moves -- Updated Moves structure
+%% Alias -- Updates Alias structure
+%%
+%%----------------------------------------------------------------------
+
+do_coalescing(IG, Worklists, Moves, Alias, K, Target) ->
+ case hipe_moves:is_empty_worklist(Moves) of
+ true ->
+ {IG, Moves, Alias, Worklists};
+ _ ->
+ {Moves0, IG0, Alias0, Worklists0} =
+ coalesce(Moves, IG, Worklists, Alias, K, Target),
+ do_coalescing(IG0, Worklists0, Moves0, Alias0, K, Target)
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: do_simplify_or_spill
+%%
+%% Parameters:
+%% IG -- An interference graph
+%% Worklists -- Worklists, that is simplify, spill and freeze
+%% Moves -- Moves sets, that is coalesced, constrained
+%% and so on.
+%% Alias -- Tells if two temporaries can have their value
+%% in the same register.
+%% K -- Want to create a K coloring.
+%% SpillLimit -- Try not to spill nodes that are above the spill limit.
+%%
+%% Returns:
+%% IG -- Updated interference graph
+%% Worklists -- Updated Worklists structure
+%% Moves -- Updated Moves structure
+%% Alias -- Updates Alias structure
+%%
+%%----------------------------------------------------------------------
+
+do_simplify_or_spill(IG, Worklists, Moves, Alias, K, SpillLimit, Target) ->
+ Simplify = not(hipe_reg_worklists:is_empty_simplify(Worklists)),
+ Spill = not(hipe_reg_worklists:is_empty_spill(Worklists)),
+ if Simplify =:= true ->
+ {IG0, Worklists0, Moves0} =
+ simplify(hipe_reg_worklists:simplify(Worklists),
+ IG,
+ Worklists,
+ Moves,
+ K),
+ do_simplify_or_spill(IG0, Worklists0, Moves0, Alias,
+ K, SpillLimit, Target);
+ Spill =:= true ->
+ Worklists0 =
+ selectSpill(Worklists, IG, SpillLimit),
+ do_simplify_or_spill(IG, Worklists0, Moves, Alias,
+ K, SpillLimit, Target);
+ true -> % Catchall case
+ {IG, Worklists, Moves, Alias}
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: adjacent
+%%
+%% Description: Adjacent nodes that's not coalesced, on the stack or
+%% precoloured.
+%% Parameters:
+%% Node -- Node that you want to adjacents of
+%% IG -- The interference graph
+%%
+%% Returns:
+%% A set with nodes/temporaries that are not coalesced, on the
+%% stack or precoloured.
+%%----------------------------------------------------------------------
+
+adjacent(Node, IG, Worklists) ->
+ Adjacent_edges = hipe_ig:node_adj_list(Node, IG),
+ hipe_reg_worklists:non_stacked_or_coalesced_nodes(Adjacent_edges, Worklists).
+
+%%----------------------------------------------------------------------
+%% Function: simplify
+%%
+%% Description: Simplify graph by removing nodes of low degree. This
+%% function simplify all nodes it can at once.
+%% Parameters:
+%% [Node|Nodes] -- The simplify worklist
+%% IG -- The interference graph
+%% Worklists -- The worklists data-structure
+%% Moves -- The moves data-structure
+%% K -- Produce a K coloring
+%%
+%% Returns:
+%% IG -- An updated interference graph
+%% Worklists -- An updated worklists data-structure
+%% Moves -- An updated moves data-structure
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+simplify_O([], IG, Worklists, Moves, _K) ->
+ {IG, Worklists, Moves};
+simplify_O([Node|Nodes], IG, Worklists, Moves, K) ->
+ Worklists0 = hipe_reg_worklists:remove_simplify(Node, Worklists),
+ ?debug_msg("putting ~w on stack~n",[Node]),
+ Adjacent = adjacent(Node, IG, Worklists0),
+ Worklists01 = hipe_reg_worklists:push_stack(Node, Adjacent, Worklists0),
+ {New_ig, Worklists1, New_moves} =
+ decrement_degree_O(Adjacent, IG, Worklists01, Moves, K),
+ simplify_O(Nodes, New_ig, Worklists1, New_moves, K).
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: simplify
+%%
+%% Description: Simplify graph by removing nodes of low degree. This
+%% function simplify all nodes it can at once.
+%% Parameters:
+%% [Node|Nodes] -- The simplify worklist
+%% IG -- The interference graph
+%% Worklists -- The worklists data-structure
+%% Moves -- The moves data-structure
+%% K -- Produce a K coloring
+%%
+%% Returns:
+%% IG -- An updated interference graph
+%% Worklists -- An updated worklists data-structure
+%% Moves -- An updated moves data-structure
+%%----------------------------------------------------------------------
+
+simplify([], IG, Worklists, Moves, _K) ->
+ {IG, Worklists, Moves};
+simplify([Node|Nodes], IG, Worklists, Moves, K) ->
+ Worklists0 = hipe_reg_worklists:remove_simplify(Node, Worklists),
+ ?debug_msg("putting ~w on stack~n",[Node]),
+ Adjacent = adjacent(Node, IG, Worklists0),
+ Worklists01 = hipe_reg_worklists:push_stack(Node, Adjacent, Worklists0),
+ {New_ig, Worklists1} = decrement_degree(Adjacent, IG, Worklists01, K),
+ simplify(Nodes, New_ig, Worklists1, Moves, K).
+
+%%----------------------------------------------------------------------
+%% Function: decrement_degree
+%%
+%% Description: Decrement the degree on a number of nodes/temporaries.
+%% Parameters:
+%% [Node|Nodes] -- Decrement degree on these nodes
+%% IG -- The interference graph
+%% Worklists -- The Worklists data structure
+%% Moves -- The Moves data structure.
+%% K -- We want to create a coloring with K colors
+%%
+%% Returns:
+%% IG -- An updated interference graph (the degrees)
+%% Worklists -- Updated Worklists. Changed if one degree goes
+%% down to K.
+%% Moves -- Updated Moves. Changed if a move related temporary
+%% gets degree K.
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+decrement_degree_O([], IG, Worklists, Moves, _K) ->
+ {IG, Worklists, Moves};
+decrement_degree_O([Node|Nodes], IG, Worklists, Moves, K) ->
+ PrevDegree = hipe_ig:get_node_degree(Node, IG),
+ IG0 = hipe_ig:dec_node_degree(Node, IG),
+ case PrevDegree =:= K of
+ true ->
+ AdjList = hipe_ig:node_adj_list(Node, IG0),
+ %% OK since Node (a) is still in IG, and (b) cannot be adjacent to itself.
+ Moves00 = enable_moves_active_to_worklist(hipe_moves:node_movelist(Node, Moves),
+ Moves),
+ Moves0 = enable_moves(AdjList, Worklists, Moves00),
+ Worklists0 = hipe_reg_worklists:remove_spill(Node, Worklists),
+ case hipe_moves:move_related(Node, Moves0) of
+ true ->
+ Worklists1 = hipe_reg_worklists:add_freeze(Node, Worklists0),
+ decrement_degree_O(Nodes, IG0, Worklists1, Moves0, K);
+ _ ->
+ Worklists1 = hipe_reg_worklists:add_simplify(Node, Worklists0),
+ decrement_degree_O(Nodes, IG0, Worklists1, Moves0, K)
+ end;
+ _ ->
+ decrement_degree_O(Nodes, IG0, Worklists, Moves, K)
+ end.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: decrement_degree
+%%
+%% Description: Decrement the degree on a number of nodes/temporaries.
+%% Parameters:
+%% [Node|Nodes] -- Decrement degree on these nodes
+%% IG -- The interference graph
+%% Worklists -- The Worklists data structure
+%% Moves -- The Moves data structure.
+%% K -- We want to create a coloring with K colors
+%%
+%% Returns:
+%% IG -- An updated interference graph (the degrees)
+%% Worklists -- Updated Worklists. Changed if one degree goes
+%% down to K.
+%% Moves -- Updated Moves. Changed if a move related temporary
+%% gets degree K.
+%%----------------------------------------------------------------------
+
+decrement_degree([], IG, Worklists, _K) ->
+ {IG, Worklists};
+decrement_degree([Node|Nodes], IG, Worklists, K) ->
+ PrevDegree = hipe_ig:get_node_degree(Node, IG),
+ IG0 = hipe_ig:dec_node_degree(Node, IG),
+ case PrevDegree =:= K of
+ true ->
+ Worklists0 = hipe_reg_worklists:remove_spill(Node, Worklists),
+ Worklists1 = hipe_reg_worklists:add_simplify(Node, Worklists0),
+ decrement_degree(Nodes, IG0, Worklists1, K);
+ _ ->
+ decrement_degree(Nodes, IG0, Worklists, K)
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: enable_moves
+%%
+%% Description: Make (move-related) nodes that are not yet considered for
+%% coalescing, ready for possible coalescing.
+%%
+%% Parameters:
+%% [Node|Nodes] -- A list of move nodes
+%% Moves -- The moves data-structure
+%%
+%% Returns:
+%% An updated moves data-structure
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+enable_moves([], _Worklists, Moves) -> Moves;
+enable_moves([Node|Nodes], Worklists, Moves) ->
+ case hipe_reg_worklists:member_stack_or_coalesced(Node, Worklists) of
+ true -> enable_moves(Nodes, Worklists, Moves);
+ _ ->
+ %% moveList[n] suffices since we're checking for activeMoves membership
+ Node_moves = hipe_moves:node_movelist(Node, Moves),
+ New_moves = enable_moves_active_to_worklist(Node_moves, Moves),
+ enable_moves(Nodes, Worklists, New_moves)
+ end.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: enable_moves_active_to_worklist
+%%
+%% Description: Make (move-related) nodes that are not yeat considered for
+%% coalescing, ready for possible coalescing.
+%%
+%% Parameters:
+%% [Node|Nodes] -- A list of move nodes
+%% Moves -- The moves data-structure
+%%
+%% Returns:
+%% An updated moves data-structure
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+enable_moves_active_to_worklist([], Moves) -> Moves;
+enable_moves_active_to_worklist([Node|Nodes], Moves) ->
+ case hipe_moves:member_active(Node, Moves) of
+ true ->
+ New_moves =
+ hipe_moves:add_worklist(Node, hipe_moves:remove_active(Node, Moves)),
+ enable_moves_active_to_worklist(Nodes, New_moves);
+ _ ->
+ enable_moves_active_to_worklist(Nodes, Moves)
+ end.
+-endif.
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+sanity_compare(Coloring, Coloring_N) ->
+ case compare_sanity(Coloring, Coloring_N) of
+ false ->
+ ?debug_msg("mismatch for coloring: ~n~p~n~p", [Coloring, Coloring_N]);
+ _ -> true
+ end.
+compare_sanity({[], _C}, {[], _C_N}) ->
+ ?debug_msg("Sanity - OK!~n", []),
+ true;
+compare_sanity({_Coloring_list, _C}, {[], _C_N}) ->
+ ?debug_msg("Sanity - unequal numbers~n", []),
+ false;
+compare_sanity({[], _C}, {_Coloring_list_N, _C_N}) ->
+ ?debug_msg("Sanity - unequal numbers~n", []),
+ false;
+compare_sanity({[Color|Coloring_list], C}, {[Color_N|Coloring_list_N], C_N}) ->
+ case element(1, Color) =:= element(1, Color_N) of
+ false ->
+ ?debug_msg("Sanity - unequal measure~n", []),
+ false;
+ _ ->
+ case element(2, Color) =:= element(2, Color_N) of
+ false ->
+ ?debug_msg("Sanity - unequal color~n", []),
+ false;
+ _ ->
+ case C =:= C_N of
+ false ->
+ ?debug_msg("Sanity - unequal last element~n", []),
+ false;
+ _ ->
+ compare_sanity({Coloring_list, C}, {Coloring_list_N, C_N})
+ end
+ end
+ end.
+-endif.
+
+
+%% Build the namelists, these functions are fast hacks, they use knowledge
+%% about data representation that they shouldn't know, bad abstraction.
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+build_namelist_O(NodeSets,Index,Alias,Color) ->
+ ?debug_msg("NodeSets ~w~n", [NodeSets]),
+ ?debug_msg("Building mapping\n",[]),
+ ?debug_msg("Vector to list\n",[]),
+ AliasList =
+ build_alias_list(aliasToList(Alias),
+ 0, %% The first temporary has index 0
+ []), %% Accumulator
+ ?debug_msg("Alias list:~p\n",[AliasList]),
+ ?debug_msg("Coalesced\n",[]),
+ NL1 = build_coalescedlist(AliasList,Color,Alias,[]),
+ ?debug_msg("Coalesced list:~p\n",[NL1]),
+ ?debug_msg("Regs\n",[]),
+ NL2 = build_reglist_O(hipe_node_sets:colored(NodeSets),Color,NL1),
+ ?debug_msg("Regs list:~p\n",[NL2]),
+ ?debug_msg("Spills\n",[]),
+ build_spillist(hipe_node_sets:spilled(NodeSets),Index,NL2).
+-endif.
+
+build_namelist(NodeSets,Index,Alias,Color) ->
+ ?debug_msg("NodeSets _N ~w~n", [NodeSets]),
+ ?debug_msg("Building mapping _N\n",[]),
+ ?debug_msg("Vector to list _N\n",[]),
+ AliasList =
+ build_alias_list(aliasToList(Alias),
+ 0, %% The first temporary has index 0
+ []), %% Accumulator
+ ?debug_msg("Alias list _N:~p\n",[AliasList]),
+ ?debug_msg("Coalesced\n",[]),
+ NL1 = build_coalescedlist(AliasList,Color,Alias,[]),
+ ?debug_msg("Coalesced list:~p\n",[NL1]),
+ ?debug_msg("Regs _N\n",[]),
+ ColoredNodes = hipe_node_sets:colored(NodeSets),
+ ?debug_msg("ColoredNodes ~p~n", [ColoredNodes]),
+ NL2 = build_reglist_N(ColoredNodes,Color,NL1,NL1),
+ ?debug_msg("Regs list _N:~p\n",[NL2]),
+ ?debug_msg("Spills _N\n",[]),
+ build_spillist(hipe_node_sets:spilled(NodeSets),Index,NL2).
+
+build_spillist([],Index,List) ->
+ {List,Index};
+build_spillist([Node|Nodes],Index,List) ->
+ ?debug_msg("[~p]: Spill ~p to ~p\n", [?MODULE,Node,Index]),
+ build_spillist(Nodes,Index+1,[{Node,{spill,Index}}|List]).
+
+build_coalescedlist([],_Color,_Alias,List) ->
+ List;
+build_coalescedlist([Node|Ns],Color,Alias,List) when is_integer(Node) ->
+ ?debug_msg("Alias of ~p is ~p~n",[Node,getAlias(Node,Alias)]),
+ AC = getColor(getAlias(Node,Alias),Color),
+ build_coalescedlist(Ns,Color,Alias,[{Node,{reg,AC}}|List]).
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+build_reglist_O([],_Color,List) ->
+ List;
+build_reglist_O([Node|Ns],Color,List) ->
+ build_reglist_O(Ns,Color,[{Node,{reg,getColor(Node,Color)}}|List]).
+-endif.
+
+build_reglist_N([],_Color,List,_OrgList) ->
+ List;
+build_reglist_N([Node|Ns],Color,List,OrgList) ->
+ %% XXX this could be done more efficiently if both lists were sorted
+ case is_already_in_list(Node, OrgList) of
+ true -> build_reglist_N(Ns, Color, List, OrgList);
+ _ -> build_reglist_N(Ns,Color,[{Node,{reg,getColor(Node,Color)}}|List], OrgList)
+ end.
+
+is_already_in_list(_Node, []) ->
+ false;
+is_already_in_list(Node, [L|List]) ->
+ ?debug_msg("---test--- Node ~w element ~w~n", [Node, element(1, L)]),
+ case Node =:= element(1, L) of
+ true -> true;
+ _ -> is_already_in_list(Node, List)
+ end.
+
+build_alias_list([], _I, List) ->
+ List;
+build_alias_list([Alias|Aliases], I, List) when is_integer(Alias) ->
+ build_alias_list(Aliases, I+1, [I|List]);
+build_alias_list([_Alias|Aliases], I, List) ->
+ build_alias_list(Aliases, I+1, List).
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+sort_stack([]) -> [];
+sort_stack([Pivot|Rest]) ->
+ {Smaller, Bigger} = sort_stack_split(Pivot, Rest),
+ lists:append(sort_stack(Smaller), [Pivot|sort_stack(Bigger)]).
+
+sort_stack_split(Pivot, L) ->
+ sort_stack_split(Pivot, L, [], []).
+
+sort_stack_split(_Pivot, [], Smaller, Bigger) ->
+ {Smaller, Bigger};
+sort_stack_split(Pivot, [H|T], Smaller, Bigger) when element(1, H) > element(1, Pivot) ->
+ sort_stack_split(Pivot, T, [H|Smaller], Bigger);
+sort_stack_split(Pivot, [H|T], Smaller, Bigger) ->
+ sort_stack_split(Pivot, T, Smaller, [H|Bigger]).
+-endif.
+
+%sort([]) -> [];
+%sort([Pivot|Rest]) ->
+% {Smaller, Bigger} = sort_split(Pivot, Rest),
+% lists:append(sort(Smaller), [Pivot|sort(Bigger)]).
+%
+%sort_split(Pivot, L) ->
+% sort_split(Pivot, L, [], []).
+%
+%sort_split(_Pivot, [], Smaller, Bigger) -> {Smaller, Bigger};
+%sort_split(Pivot, [H|T], Smaller, Bigger) when H > Pivot ->
+% sort_split(Pivot, T, [H|Smaller], Bigger);
+%sort_split(Pivot, [H|T], Smaller, Bigger) ->
+% sort_split(Pivot, T, Smaller, [H|Bigger]).
+
+%%----------------------------------------------------------------------
+%% Function: assignColors
+%%
+%% Description: Tries to assign colors to nodes in a stack.
+%% Parameters:
+%% Worklists -- The Worklists data structure.
+%% Stack -- The SelectStack built by the Select function,
+%% this stack contains tuples in the form {Node,Edges}
+%% where Node is the Node number and Edges is an ordset
+%% containing the numbers of all the adjacent nodes.
+%% NodeSets -- This is a record containing all the different node
+%% sets that are used in the register allocator.
+%% Color -- A mapping from nodes to their respective color.
+%% No_temporaries -- Number of temporaries.
+%% SavedAdjList -- Saved adjacency list (from before coalescing).
+%% SavedSpillCosts -- Saved spill costs (from before coalescing).
+%% IG -- The interference graph.
+%% Alias -- This is a mapping from nodes to nodes. If a node has
+%% been coalesced, this mapping shows the alias for that
+%% node.
+%% AllColors -- This is an ordset containing all the available colors
+%% Target -- The module containing the target-specific functions.
+%%
+%% Returns:
+%% Color -- A mapping from nodes to their respective color.
+%% NodeSets -- The updated node sets.
+%% Alias -- The updated aliases.
+%%----------------------------------------------------------------------
+
+assignColors(Worklists, Stack, NodeSets, Color, No_Temporaries,
+ SavedAdjList, SavedSpillCosts, IG, Alias, AllColors, Target) ->
+ case Stack of
+ [] ->
+ {Color,NodeSets,Alias};
+ [{Node,Edges}|Stack1] ->
+ ?debug_msg("Coloring Node: ~p~n",[Node]),
+ ?IF_DEBUG(lists:foreach(fun (_E) ->
+ ?msg(" Edge ~w-><~w>->~w~n",
+ begin A = getAlias(_E,Alias),
+ [_E,A,getColor(A,Color)]
+ end)
+ end, Edges),
+ []),
+ %% When debugging, check that Node isn't precoloured.
+ OkColors = findOkColors(Edges, AllColors, Color, Alias),
+ case colset_is_empty(OkColors) of
+ true -> % Spill case
+ case hipe_reg_worklists:member_coalesced_to(Node, Worklists) of
+ true ->
+ ?debug_msg("Alias case. Undoing coalescing.~n", []),
+ {Alias1, IG1, NodeSets1, Color1, Stack2} = tryPrimitiveNodes(Node, Stack1, NodeSets, AllColors, Color, No_Temporaries, SavedAdjList, SavedSpillCosts, IG, Alias, Target),
+ %{Alias1, IG1, NodeSets1, Color1, Stack2} = {Alias, IG, NodeSets, Color, Stack1},
+ assignColors(Worklists, Stack2, NodeSets1, Color1, No_Temporaries, SavedAdjList, SavedSpillCosts, IG1, Alias1, AllColors, Target);
+ false ->
+ ?debug_msg("Spill case. Spilling node.~n", []),
+ NodeSets1 = hipe_node_sets:add_spilled(Node, NodeSets),
+ assignColors(Worklists, Stack1, NodeSets1, Color, No_Temporaries, SavedAdjList, SavedSpillCosts, IG, Alias, AllColors, Target)
+ end;
+ false -> % Color case
+ Col = colset_smallest(OkColors),
+ NodeSets1 = hipe_node_sets:add_colored(Node, NodeSets),
+ Color1 = setColor(Node, Target:physical_name(Col), Color),
+ ?debug_msg("Color case. Assigning color ~p to node.~n", [Col]),
+ assignColors(Worklists, Stack1, NodeSets1, Color1, No_Temporaries, SavedAdjList, SavedSpillCosts, IG, Alias, AllColors, Target)
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: tryPrimitiveNodes
+%%
+%% Description: Undoes coalescing of a non-colorable coalesced node and tries
+%% to assign colors to its primitives, such that the cheapest
+%% potential spill cost is achieved.
+%% Parameters:
+%% Node -- The representative node to undo coalescing for.
+%% Stack -- The SelectStack built by the Select function,
+%% this stack contains tuples in the form {Node,Edges}
+%% where Node is the Node number and Edges is an ordset
+%% containing the numbers of all the adjacent nodes.
+%% NodeSets -- This is a record containing all the different node
+%% sets that are used in the register allocator.
+%% AllColors -- This is an ordset containing all the available colors.
+%% No_temporaries -- Number of temporaries.
+%% SavedAdjList -- Saved adjacency list (from before coalescing).
+%% SavedSpillCosts -- Saved spill costs (from before coalescing).
+%% IG -- The interference graph.
+%% Alias -- This is a mapping from nodes to nodes. If a node has
+%% been coalesced, this mapping shows the alias for that
+%% node.
+%% Target -- The module containing the target-specific functions.
+%%
+%% Returns:
+%% Alias -- The restored aliases after the uncoalescing.
+%% IG -- An updated interference graph after the uncoalescing.
+%% NodeSets -- The updated node sets.
+%% Color -- A mapping from nodes to their respective color.
+%% Stack -- The updated SelectStack with non-colored primitives
+%% placed at the bottom.
+%%----------------------------------------------------------------------
+
+tryPrimitiveNodes(Node, Stack, NodeSets, AllColors, Color, No_temporaries, SavedAdjList, SavedSpillCosts, IG, Alias, Target) ->
+ ?debug_msg("Undoing coalescing of node ~p.~n", [Node]),
+ {PrimitiveNodes, Alias1, IG1} = undoCoalescing(Node, No_temporaries, Alias, SavedAdjList, IG, Target),
+ ?debug_msg("Spilling non-colorable primitives.~n", []),
+ {ColorableNodes, NodeSets1} = spillNonColorablePrimitives([], PrimitiveNodes, NodeSets, AllColors, Color, SavedAdjList, Alias1),
+ ?debug_msg("Generating splits of colorable nodes.~n", []),
+ Splits = splits(ColorableNodes, SavedSpillCosts),
+ {NodeSets2, Color1, Stack1} = processSplits(Splits, AllColors, IG1, Color, NodeSets1, Alias1, Target, Stack),
+ {Alias1, IG1, NodeSets2, Color1, Stack1}.
+
+%% Spill all non-colorable primitives and return the remaining set of nodes.
+
+spillNonColorablePrimitives(ColorableNodes, [], NodeSets, _AllColors, _Color, _SavedAdjList, _Alias) ->
+ {ColorableNodes, NodeSets};
+spillNonColorablePrimitives(ColorableNodes, [Primitive|Primitives], NodeSets, AllColors, Color, SavedAdjList, Alias) ->
+ OkColors = findOkColors(hipe_adj_list:edges(Primitive, SavedAdjList), AllColors, Color, Alias),
+ case colset_is_empty(OkColors) of
+ true -> % Spill case
+ ?debug_msg(" Spilling primitive node ~p.~n", [Primitive]),
+ NodeSets1 = hipe_node_sets:add_spilled(Primitive, NodeSets),
+ spillNonColorablePrimitives(ColorableNodes, Primitives, NodeSets1, AllColors, Color, SavedAdjList, Alias);
+ false -> % Colorable case
+ ?debug_msg(" Primitive node ~p is colorable.~n", [Primitive]),
+ spillNonColorablePrimitives([Primitive|ColorableNodes], Primitives, NodeSets, AllColors, Color, SavedAdjList, Alias)
+ end.
+
+%% Generate all splits of colorable primitives, sorted in spill cost order.
+
+splits([], _SavedSpillCosts) ->
+ [{[], [], 0}];
+splits([L|Ls], SavedSpillCosts) ->
+ Spl = splits(Ls, SavedSpillCosts),
+ SpillCost = hipe_spillcost:spill_cost(L, SavedSpillCosts),
+ Spl1 = [splits_1(S, L) || S <- Spl],
+ Spl2 = [splits_2(S, L, SpillCost) || S <- Spl],
+ spillCostOrderedMerge(Spl1, Spl2, []).
+
+splits_1({Cols, NonCols, OldSpillCost}, L) ->
+ {[L|Cols], NonCols, OldSpillCost}.
+
+splits_2({Cols, NonCols, OldSpillCost}, L, SpillCost) ->
+ {Cols, [L|NonCols], OldSpillCost + SpillCost}.
+
+%% Merge two ordered sub-splits into one.
+
+spillCostOrderedMerge(Spl1, [], Spl) ->
+ lists:reverse(Spl) ++ Spl1;
+spillCostOrderedMerge([], Spl2, Spl) ->
+ lists:reverse(Spl) ++ Spl2;
+spillCostOrderedMerge(Spl1, Spl2, Spl) ->
+ {_, _, SpillCost1} = hd(Spl1),
+ {_, _, SpillCost2} = hd(Spl2),
+ case SpillCost1 =< SpillCost2 of
+ true ->
+ spillCostOrderedMerge(tl(Spl1), Spl2, [hd(Spl1)|Spl]);
+ false ->
+ spillCostOrderedMerge(Spl1, tl(Spl2), [hd(Spl2)|Spl])
+ end.
+
+%% Process splits, finding the one with the smallest spill cost that
+%% can be assigned one color.
+
+processSplits([], _AllColors, _IG, Color, NodeSets, _Alias, _Target, Stack) ->
+ {NodeSets, Color, Stack};
+processSplits([{Cols, NonCols, _SpillCost}|Splits], AllColors, IG, Color, NodeSets, Alias, Target, Stack) ->
+ OkColors = findCommonColors(Cols, IG, Color, Alias, AllColors),
+ case colset_is_empty(OkColors) of
+ false -> % This split can be colored with one color - use it
+ ?debug_msg("Found a colorable split.~n", []),
+ Col = colset_smallest(OkColors),
+ {NodeSets1, Color1} = colorSplit(Cols, Col, NodeSets, Color, Target),
+ Stack1 = enqueueSplit(NonCols, IG, Stack),
+ {NodeSets1, Color1, Stack1};
+ true -> % This split cannot be colored with one color - try another
+ ?debug_msg("Unable to color split.~n", []),
+ processSplits(Splits, AllColors, IG, Color, NodeSets, Alias, Target, Stack)
+ end.
+
+%% Find the set of colors that can be assigned to one split.
+
+findCommonColors([], _IG, _Color, _Alias, OkColors) ->
+ OkColors;
+findCommonColors([Primitive|Primitives], IG, Color, Alias, OkColors) ->
+ OkColors1 = findOkColors(hipe_ig:node_adj_list(Primitive, IG), OkColors, Color, Alias),
+ findCommonColors(Primitives, IG, Color, Alias, OkColors1).
+
+%% Color nodes in a split.
+
+colorSplit([], _Col, NodeSets, Color, _Target) ->
+ {NodeSets, Color};
+colorSplit([Node|Nodes], Col, NodeSets, Color, Target) ->
+ ?debug_msg(" Coloring node ~p with color ~p.~n", [Node, Col]),
+ NodeSets1 = hipe_node_sets:add_colored(Node, NodeSets),
+ Color1 = setColor(Node, Target:physical_name(Col), Color),
+ colorSplit(Nodes, Col, NodeSets1, Color1, Target).
+
+%% Place non-colorable nodes in a split at the bottom of the SelectStack.
+
+enqueueSplit([], _IG, Stack) ->
+ Stack;
+enqueueSplit([Node|Nodes], IG, Stack) ->
+ ?debug_msg(" Placing node ~p at the bottom of the stack.~n", [Node]),
+ Edges = hipe_ig:node_adj_list(Node, IG),
+ Stack1 = Stack ++ [{Node, Edges}],
+ enqueueSplit(Nodes, IG, Stack1).
+
+%%----------------------------------------------------------------------
+%% Function: assignColors
+%%
+%% Description: Tries to assign colors to nodes in a stack.
+%% Parameters:
+%% Stack -- The SelectStack built by the Select function,
+%% this stack contains tuples in the form {Node,Edges}
+%% where Node is the Node number and Edges is an ordset
+%% containing the numbers of all the adjacent nodes.
+%% NodeSets -- This is a record containing all the different node
+%% sets that are used in the register allocator.
+%% Alias -- This is a mapping from nodes to nodes, if a node has
+%% been coalesced this mapping shows the alias for that
+%% node.
+%% AllColors -- This is an ordset containing all the available colors
+%%
+%% Target -- The module containing the target-specific functions.
+%%
+%% Returns:
+%% Color -- A mapping from nodes to their respective color.
+%% NodeSets -- The updated node sets.
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+assignColors_O(Stack,NodeSets,Color,Alias,AllColors,Target) ->
+ case Stack of
+ [] ->
+ {Color,NodeSets};
+ [{Node,Edges}|Stack1] ->
+ ?debug_msg("Coloring Node: ~p~n",[Node]),
+ ?IF_DEBUG(lists:foreach(fun (_E) ->
+ ?msg(" Edge ~w-><~w>->~w~n",
+ begin A = getAlias(_E,Alias),
+ [_E,A,getColor(A,Color)]
+ end)
+ end, Edges),
+ []),
+ %% When debugging, check that Node isn't precoloured.
+ OkColors = findOkColors(Edges, AllColors, Color, Alias),
+ case colset_is_empty(OkColors) of
+ true -> % Spill case
+ NodeSets1 = hipe_node_sets:add_spilled(Node, NodeSets),
+ assignColors_O(Stack1, NodeSets1, Color, Alias, AllColors, Target);
+ false -> % Colour case
+ Col = colset_smallest(OkColors),
+ NodeSets1 = hipe_node_sets:add_colored(Node, NodeSets),
+ Color1 = setColor(Node, Target:physical_name(Col), Color),
+ assignColors_O(Stack1, NodeSets1, Color1, Alias, AllColors, Target)
+ end
+ end.
+-endif.
+
+%%---------------------------------------------------------------------
+%% Function: defaultColoring
+%%
+%% Description: Make the default coloring
+%% Parameters:
+%% Regs -- The list of registers to be default colored
+%% Color -- The color mapping that shall be changed
+%% NodeSets -- The node sets that shall be updated
+%% Target -- The module containing the target-specific functions.
+%%
+%% Returns:
+%% NewColor -- The updated color mapping
+%% NewNodeSets -- The updated node sets
+%%---------------------------------------------------------------------
+
+defaultColoring([], Color, NodeSets, _Target) ->
+ {Color,NodeSets};
+defaultColoring([Reg|Regs], Color, NodeSets, Target) ->
+ Color1 = setColor(Reg,Target:physical_name(Reg), Color),
+ NodeSets1 = hipe_node_sets:add_colored(Reg, NodeSets),
+ defaultColoring(Regs, Color1, NodeSets1, Target).
+
+%% Find the colors that are OK for a node with certain edges.
+
+findOkColors(Edges, AllColors, Color, Alias) ->
+ find(Edges, AllColors, Color, Alias).
+
+%% Find all the colors of the nodes in the list [Node|Nodes] and remove them
+%% from the set OkColors, when the list is empty, return OkColors.
+
+find([], OkColors, _Color, _Alias) ->
+ OkColors;
+find([Node0|Nodes], OkColors, Color, Alias) ->
+ Node = getAlias(Node0, Alias),
+ case getColor(Node, Color) of
+ [] ->
+ find(Nodes, OkColors, Color, Alias);
+ Col ->
+ OkColors1 = colset_del_element(Col, OkColors),
+ find(Nodes, OkColors1, Color, Alias)
+ end.
+
+%%%
+%%% ColSet -- ADT for the set of available colours while
+%%% assigning colours.
+%%%
+-ifdef(notdef). % old ordsets-based implementation
+colset_from_list(Allocatable) ->
+ ordsets:from_list(Allocatable).
+
+colset_del_element(Colour, ColSet) ->
+ ordsets:del_element(Colour, ColSet).
+
+colset_is_empty(ColSet) ->
+ case ColSet of
+ [] -> true;
+ [_|_] -> false
+ end.
+
+colset_smallest([Colour|_]) ->
+ Colour.
+-endif.
+
+-ifdef(notdef). % new gb_sets-based implementation
+colset_from_list(Allocatable) ->
+ gb_sets:from_list(Allocatable).
+
+colset_del_element(Colour, ColSet) ->
+ %% Must use gb_sets:delete_any/2 since gb_sets:del_element/2
+ %% fails if the element isn't present. Bummer.
+ gb_sets:delete_any(Colour, ColSet).
+
+colset_is_empty(ColSet) ->
+ gb_sets:is_empty(ColSet).
+
+colset_smallest(ColSet) ->
+ gb_sets:smallest(ColSet).
+-endif.
+
+%%-ifdef(notdef). % new bitmask-based implementation
+colset_from_list(Allocatable) ->
+ colset_from_list(Allocatable, 0).
+
+colset_from_list([], ColSet) ->
+ ColSet;
+colset_from_list([Colour|Allocatable], ColSet) ->
+ colset_from_list(Allocatable, ColSet bor (1 bsl Colour)).
+
+colset_del_element(Colour, ColSet) ->
+ ColSet band bnot(1 bsl Colour).
+
+colset_is_empty(0) -> true;
+colset_is_empty(_) -> false.
+
+colset_smallest(ColSet) ->
+ bitN_log2(ColSet band -ColSet, 0).
+
+bitN_log2(BitN, ShiftN) ->
+ case BitN > 16#ffff of
+ true ->
+ bitN_log2(BitN bsr 16, ShiftN + 16);
+ _ ->
+ ShiftN + hweight16(BitN - 1)
+ end.
+
+hweight16(W) ->
+ Res1 = ( W band 16#5555) + (( W bsr 1) band 16#5555),
+ Res2 = (Res1 band 16#3333) + ((Res1 bsr 2) band 16#3333),
+ Res3 = (Res2 band 16#0F0F) + ((Res2 bsr 4) band 16#0F0F),
+ (Res3 band 16#00FF) + ((Res3 bsr 8) band 16#00FF).
+%%-endif.
+
+%%%
+%%% Colour ADT providing a partial mapping from nodes to colours.
+%%%
+
+initColor(NrNodes) ->
+ {colmap, hipe_bifs:array(NrNodes, [])}.
+
+getColor(Node, {colmap, ColMap}) ->
+ hipe_bifs:array_sub(ColMap, Node).
+
+setColor(Node, Color, {colmap, ColMap} = C) ->
+ hipe_bifs:array_update(ColMap, Node, Color),
+ C.
+
+-ifdef(DEBUG_PRINTOUTS).
+printColors(0, _) ->
+ true;
+printColors(Node, {colmap, ColMap} = C) ->
+ NextNode = Node - 1,
+ ?debug_msg("node ~w color ~w~n", [NextNode, hipe_bifs:array_sub(ColMap, NextNode)]),
+ printColors(NextNode, C).
+-endif.
+
+%%%
+%%% Alias ADT providing a partial mapping from nodes to nodes.
+%%%
+
+initAlias(NrNodes) ->
+ {alias, hipe_bifs:array(NrNodes, [])}.
+
+%% Get alias for a node.
+%% Note that non-aliased nodes could be represented in
+%% two ways, either not aliased or aliased to itself.
+%% Including the latter case prevents looping bugs.
+getAlias(Node, {alias, AliasMap} = Alias) ->
+ case hipe_bifs:array_sub(AliasMap, Node) of
+ [] ->
+ Node;
+ Node ->
+ Node;
+ AliasNode ->
+ getAlias(AliasNode, Alias)
+ end.
+
+-ifdef(DEBUG_PRINTOUTS).
+printAlias({alias, AliasMap} = Alias) ->
+ ?debug_msg("Aliases:\n",[]),
+ printAlias(hipe_bifs:array_length(AliasMap), Alias).
+
+printAlias(0, {alias, _}) ->
+ true ;
+printAlias(Node, {alias, _AliasMap} = Alias) ->
+ ?debug_msg("alias ~p ~p\n", [Node - 1, getAlias(Node - 1, Alias)]),
+ printAlias(Node - 1, Alias).
+-endif.
+
+setAlias(Node, AliasNode, {alias, AliasMap} = Alias) ->
+ hipe_bifs:array_update(AliasMap, Node, AliasNode),
+ Alias.
+
+aliasToList({alias, AliasMap}) ->
+ aliasToList(AliasMap, hipe_bifs:array_length(AliasMap), []).
+
+aliasToList(AliasMap, I1, Tail) ->
+ I0 = I1 - 1,
+ case I0 >= 0 of
+ true ->
+ aliasToList(AliasMap, I0, [hipe_bifs:array_sub(AliasMap, I0)|Tail]);
+ _ ->
+ Tail
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: coalesce
+%%
+%% Description: Coalesces nodes in worklist
+%% Parameters:
+%% Moves -- Current move information
+%% IG -- Interference graph
+%% Worklists -- Current worklists
+%% Alias -- Current aliases for temporaries
+%% K -- Number of registers
+%%
+%% Returns:
+%% {Moves, IG, Worklists, Alias}
+%% (Updated versions of above structures, after coalescing)
+%%----------------------------------------------------------------------
+
+coalesce(Moves, IG, Worklists, Alias, K, Target) ->
+ case hipe_moves:worklist_get_and_remove(Moves) of
+ {[],Moves0} ->
+ %% Moves marked for removal from worklistMoves by FreezeMoves()
+ %% are removed by worklist_get_and_remove(). This case is unlikely,
+ %% but can occur if only stale moves remain in worklistMoves.
+ {Moves0, IG, Alias};
+ {Move,Moves0} ->
+ {Dest,Source} = hipe_moves:get_move(Move, Moves0),
+ ?debug_msg("Testing nodes ~p and ~p for coalescing~n",[Dest,Source]),
+ Alias_src = getAlias(Source, Alias),
+ Alias_dst = getAlias(Dest, Alias),
+ {U,V} = case Target:is_precoloured(Alias_dst) of
+ true -> {Alias_dst, Alias_src};
+ false -> {Alias_src, Alias_dst}
+ end,
+ %% When debugging, check that neither V nor U is on the stack.
+ case U =:= V of
+ true ->
+ %% drop coalesced move Move
+ {Moves0, IG, Alias, Worklists};
+ _ ->
+ case (Target:is_precoloured(V) orelse
+ hipe_ig:nodes_are_adjacent(U, V, IG)) of
+ true ->
+ %% drop constrained move Move
+ {Moves0, IG, Alias, Worklists};
+ false ->
+ case (case Target:is_precoloured(U) of
+ true ->
+ AdjV = hipe_ig:node_adj_list(V, IG),
+ all_adjacent_ok(AdjV, U, Worklists, IG, K, Target);
+ false ->
+ AdjV = hipe_ig:node_adj_list(V, IG),
+ AdjU = hipe_ig:node_adj_list(U, IG),
+ conservative(AdjU, AdjV, U, Worklists, IG, K)
+ end) of
+ true ->
+ %% drop coalesced move Move
+ {IG1, Alias1, Worklists1} =
+ combine(U, V, IG, Alias, Worklists, K, Target),
+ {Moves0, IG1, Alias1, Worklists1};
+ false ->
+ Moves1 = hipe_moves:add_active(Move, Moves0),
+ {Moves1, IG, Alias, Worklists}
+ end
+ end
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: coalesce_O
+%%
+%% Description: Coalesces nodes in worklist
+%% Parameters:
+%% Moves -- Current move information
+%% IG -- Interference graph
+%% Worklists -- Current worklists
+%% Alias -- Current aliases for temporaries
+%% K -- Number of registers
+%%
+%% Returns:
+%% {Moves, IG, Worklists, Alias}
+%% (Updated versions of above structures, after coalescing)
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+coalesce_O(Moves, IG, Worklists, Alias, K, Target) ->
+ case hipe_moves:worklist_get_and_remove(Moves) of
+ {[],Moves0} ->
+ %% Moves marked for removal from worklistMoves by FreezeMoves()
+ %% are removed by worklist_get_and_remove(). This case is unlikely,
+ %% but can occur if only stale moves remain in worklistMoves.
+ {Moves0,IG,Worklists,Alias};
+ {Move,Moves0} ->
+ {Dest,Source} = hipe_moves:get_move(Move, Moves0),
+ ?debug_msg("Testing nodes ~p and ~p for coalescing~n",[Dest,Source]),
+ Alias_src = getAlias(Source, Alias),
+ Alias_dst = getAlias(Dest, Alias),
+ {U,V} = case Target:is_precoloured(Alias_dst) of
+ true -> {Alias_dst, Alias_src};
+ false -> {Alias_src, Alias_dst}
+ end,
+ %% When debugging, check that neither V nor U is on the stack.
+ case U =:= V of
+ true ->
+ Moves1 = Moves0, % drop coalesced move Move
+ Worklists1 = add_worklist(Worklists, U, K, Moves1, IG, Target),
+ {Moves1, IG, Worklists1, Alias};
+ _ ->
+ case (Target:is_precoloured(V) orelse
+ hipe_ig:nodes_are_adjacent(U, V, IG)) of
+ true ->
+ Moves1 = Moves0, % drop constrained move Move
+ Worklists1 = add_worklist(Worklists, U, K, Moves1, IG, Target),
+ Worklists2 = add_worklist(Worklists1, V, K, Moves1, IG, Target),
+ {Moves1, IG, Worklists2, Alias};
+ false ->
+ case (case Target:is_precoloured(U) of
+ true ->
+ AdjV = hipe_ig:node_adj_list(V, IG),
+ all_adjacent_ok(AdjV, U, Worklists, IG, K, Target);
+ false ->
+ AdjV = hipe_ig:node_adj_list(V, IG),
+ AdjU = hipe_ig:node_adj_list(U, IG),
+ conservative(AdjU, AdjV, U, Worklists, IG, K)
+ end) of
+ true ->
+ Moves1 = Moves0, % drop coalesced move Move
+ {IG1,Worklists1,Moves2,Alias1} =
+ combine_O(U, V, IG, Worklists, Moves1, Alias, K, Target),
+ Worklists2 = add_worklist(Worklists1, U, K, Moves2, IG1, Target),
+ {Moves2, IG1, Worklists2, Alias1};
+ false ->
+ Moves1 = hipe_moves:add_active(Move, Moves0),
+ {Moves1, IG, Worklists, Alias}
+ end
+ end
+ end
+ end.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: add_worklist
+%%
+%% Description: Builds new worklists where U is transferred from freeze
+%% to simplify, if possible
+%%
+%% Parameters:
+%% Worklists -- Current worklists
+%% U -- Node to operate on
+%% K -- Number of registers
+%% Moves -- Current move information
+%% IG -- Interference graph
+%% Target -- The containing the target-specific functions
+%%
+%% Returns:
+%% Worklists (updated)
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+add_worklist(Worklists, U, K, Moves, IG, Target) ->
+ case (not(Target:is_precoloured(U))
+ andalso not(hipe_moves:move_related(U, Moves))
+ andalso (hipe_ig:is_trivially_colourable(U, K, IG))) of
+ true ->
+ hipe_reg_worklists:transfer_freeze_simplify(U, Worklists);
+ false ->
+ Worklists
+ end.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: combine
+%%
+%% Description: Combines two nodes into one (used when coalescing)
+%%
+%% Parameters:
+%% U -- First node to operate on
+%% V -- Second node to operate on
+%% IG -- Interference graph
+%% Worklists -- Current worklists
+%% Moves -- Current move information
+%% Alias -- Current aliases for temporaries
+%% K -- Number of registers
+%%
+%% Returns:
+%% {IG, Worklists, Moves, Alias} (updated)
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+combine_O(U, V, IG, Worklists, Moves, Alias, K, Target) ->
+ Worklists1 = case hipe_reg_worklists:member_freeze(V, Worklists) of
+ true -> hipe_reg_worklists:remove_freeze(V, Worklists);
+ false -> hipe_reg_worklists:remove_spill(V, Worklists)
+ end,
+ Worklists11 = hipe_reg_worklists:add_coalesced(V, Worklists1),
+
+ ?debug_msg("Coalescing ~p and ~p to ~p~n",[V,U,U]),
+
+ Alias1 = setAlias(V, U, Alias),
+
+ %% Typo in published algorithm: s/nodeMoves/moveList/g to fix.
+ %% XXX: moveList[u] \union moveList[v] OR NodeMoves(u) \union NodeMoves(v) ???
+ %% XXX: NodeMoves() is correct, but unnecessarily strict. The ordsets:union
+ %% constrains NodeMoves() to return an ordset.
+ Moves1 = hipe_moves:update_movelist(U,
+ ordsets:union(hipe_moves:node_moves(U, Moves),
+ hipe_moves:node_moves(V, Moves)),
+ Moves),
+ %% Missing in published algorithm. From Tiger book Errata.
+ Moves2 = enable_moves_active_to_worklist(hipe_moves:node_movelist(V, Moves1), Moves1),
+ AdjV = hipe_ig:node_adj_list(V, IG),
+
+ {IG1, Worklists2, Moves3} =
+ combine_edges_O(AdjV, U, IG, Worklists11, Moves2, K, Target),
+
+ New_worklists = case (not(hipe_ig:is_trivially_colourable(U, K, IG1))
+ andalso hipe_reg_worklists:member_freeze(U, Worklists2)) of
+ true -> hipe_reg_worklists:transfer_freeze_spill(U, Worklists2);
+ false -> Worklists2
+ end,
+ {IG1, New_worklists, Moves3, Alias1}.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: combine
+%%
+%% Description: Combines two nodes into one (used when coalescing)
+%%
+%% Parameters:
+%% U -- First node to operate on
+%% V -- Second node to operate on
+%% IG -- Interference graph
+%% Worklists -- Current worklists
+%% Moves -- Current move information
+%% Alias -- Current aliases for temporaries
+%% K -- Number of registers
+%%
+%% Returns:
+%% {IG, Worklists, Moves, Alias} (updated)
+%%----------------------------------------------------------------------
+
+combine(U, V, IG, Alias, Worklists, K, Target) ->
+ ?debug_msg("N_Coalescing ~p and ~p to ~p~n",[V,U,U]),
+ Worklists1 = hipe_reg_worklists:add_coalesced(V, U, Worklists),
+ Alias1 = setAlias(V, U, Alias),
+ AdjV = hipe_ig:node_adj_list(V, IG),
+ IG1 = combine_edges(AdjV, U, IG, Worklists1, K, Target),
+ {IG1, Alias1, Worklists1}.
+
+%%----------------------------------------------------------------------
+%% Function: combine_edges
+%%
+%% Description: For each node in a list, make an edge between that node
+%% and node U, and decrement its degree by 1
+%% (Used when two nodes are coalesced, to connect all nodes
+%% adjacent to one node to the other node)
+%%
+%% Parameters:
+%% [T|Ts] -- List of nodes to make edges to
+%% U -- Node to make edges from
+%% IG -- Interference graph
+%% Worklists -- Current worklists
+%% Moves -- Current move information
+%% K -- Number of registers
+%%
+%% Returns:
+%% {IG, Worklists, Moves} (updated)
+%%----------------------------------------------------------------------
+
+combine_edges([], _U, IG, _Worklists, _K, _Target) ->
+ IG;
+combine_edges([T|Ts], U, IG, Worklists, K, Target) ->
+ case hipe_reg_worklists:member_stack_or_coalesced(T, Worklists) of
+ true -> combine_edges(Ts, U, IG, Worklists, K, Target);
+ _ ->
+ IG1 = hipe_ig:add_edge(T, U, IG, Target),
+ IG2 = case Target:is_precoloured(T) of
+ true -> IG1;
+ false -> hipe_ig:dec_node_degree(T, IG1)
+ end,
+ combine_edges(Ts, U, IG2, Worklists, K, Target)
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: combine_edges
+%%
+%% Description: For each node in a list, make an edge between that node
+%% and node U, and decrement its degree by 1
+%% (Used when two nodes are coalesced, to connect all nodes
+%% adjacent to one node to the other node)
+%%
+%% Parameters:
+%% [T|Ts] -- List of nodes to make edges to
+%% U -- Node to make edges from
+%% IG -- Interference graph
+%% Worklists -- Current worklists
+%% Moves -- Current move information
+%% K -- Number of registers
+%%
+%% Returns:
+%% {IG, Worklists, Moves} (updated)
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+combine_edges_O([], _U, IG, Worklists, Moves, _K, _Target) ->
+ {IG, Worklists, Moves};
+combine_edges_O([T|Ts], U, IG, Worklists, Moves, K, Target) ->
+ case hipe_reg_worklists:member_stack_or_coalesced(T, Worklists) of
+ true -> combine_edges_O(Ts, U, IG, Worklists, Moves, K, Target);
+ _ ->
+ %% XXX: The issue below occurs because the T->V edge isn't removed.
+ %% This causes adjList[T] to contain stale entries, to possibly grow
+ %% (if T isn't already adjacent to U), and degree[T] to possibly
+ %% increase (again, if T isn't already adjacent to U).
+ %% The decrement_degree() call repairs degree[T] but not adjList[T].
+ %% It would be better to physically replace T->V with T->U, and only
+ %% decrement_degree(T) if T->U already existed.
+ %%
+ %% add_edge() may change a low-degree move-related node to be of
+ %% significant degree. In this case the node belongs in the spill
+ %% worklist, and that's where decrement_degree() expects to find it.
+ %% This issue is not covered in the published algorithm.
+ OldDegree = hipe_ig:get_node_degree(T, IG),
+ IG1 = hipe_ig:add_edge(T, U, IG, Target),
+ NewDegree = hipe_ig:get_node_degree(T, IG1),
+ Worklists0 =
+ if NewDegree =:= K, OldDegree =:= K-1 ->
+ %% ?debug_msg("~w:combine_edges_O(): repairing worklist membership for node ~w\n", [?MODULE,T]),
+ %% The node T must be on the freeze worklist:
+ %% 1. Since we're coalescing, the simplify worklist must have been
+ %% empty when combine_edges_O() started.
+ %% 2. decrement_degree() may put the node T back on the simplify
+ %% worklist, but that occurs after the worklists repair step.
+ %% 3. There are no duplicates among the edges.
+ Worklists00 = hipe_reg_worklists:remove_freeze(T, Worklists),
+ hipe_reg_worklists:add_spill(T, Worklists00);
+ true ->
+ Worklists
+ end,
+ {IG2, Worklists1, Moves1} =
+ decrement_degree_O([T], IG1, Worklists0, Moves, K),
+ combine_edges_O(Ts, U, IG2, Worklists1, Moves1, K, Target)
+ end.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: undoCoalescing
+%%
+%% Description: Returns necessary information for a coalesced node
+%%
+%% Parameters:
+%% N -- The node to uncoalesce
+%% No_temporaries -- Number of temporaries
+%% Alias -- The Alias vector before undoing
+%% SavedAdj -- Saved adjacency list
+%% IG -- Interference graph
+%% Target -- The module containing the target-specific functions.
+%%
+%% Returns:
+%% list of primitive nodes, that is all nodes that were previously
+%% coalesced to N
+%% updated alias vector
+%% updated Interferece graph
+%%----------------------------------------------------------------------
+undoCoalescing(N, No_temporaries, Alias, SavedAdj, IG, Target) ->
+ Primitives = findPrimitiveNodes(No_temporaries, N, Alias),
+ Alias1 = restoreAliases(Primitives, Alias),
+ IG1 = fixAdj(N, SavedAdj, IG, Target),
+ {Primitives, Alias1, IG1}.
+
+%% Restore aliasinfo for primitive nodes, that is
+%% unalias the node sthat were aliased to the primitive
+%% nodes. Note that an unaliased node could be
+%% represented in two ways, either not aliased or aliased
+%% to itself. See also getAlias
+restoreAliases([], Alias) ->
+ Alias;
+restoreAliases([Primitive|Primitives], Alias) ->
+ Alias1 = setAlias(Primitive, Primitive, Alias),
+ restoreAliases(Primitives, Alias1).
+
+%% find the primitive nodes to N, that is find all
+%% nodes that are aliased to N
+findPrimitiveNodes(No_temporaries, N, Alias) ->
+ findPrimitiveNodes(No_temporaries, N, Alias, []).
+
+findPrimitiveNodes(0, _N, _Alias, PrimitiveNodes) ->
+ PrimitiveNodes;
+findPrimitiveNodes(Node, N, Alias, PrimitiveNodes) ->
+ NextNode = Node - 1,
+ case (getAlias(NextNode, Alias) =:= N) of
+ true -> findPrimitiveNodes(NextNode, N, Alias, [NextNode | PrimitiveNodes]);
+ _ -> findPrimitiveNodes(NextNode, N, Alias, PrimitiveNodes)
+ end.
+
+%test_undoCoalescing(No_temporaries, Alias, Worklists) ->
+% test_undoCoalescing(No_temporaries, No_temporaries, Alias, Worklists).
+%
+%test_undoCoalescing(0, _No_temporaries, _Alias, _Worklists) ->
+% true;
+%test_undoCoalescing(Node, No_temporaries, Alias, Worklists) ->
+% %?debug_msg("++ the adj list: ~p~n", [SavedAdj]),
+% %?debug_msg("Node ~p~n", [Node]),
+% NextNode = Node - 1,
+% Coalesced_to = hipe_reg_worklists:member_coalesced_to(NextNode, Worklists),
+% ?debug_msg("��-- member coalesced: ~p~n", [Coalesced_to]),
+% {Primitives, Alias1} = undoCoalescing(NextNode, No_temporaries, Alias),
+% ?debug_msg("��-- primitivenodes ~w\n", [Primitives]),
+% case (Coalesced_to) of
+% true -> printAlias(Alias1);
+% _ -> true
+% end,
+% test_undoCoalescing(NextNode, No_temporaries, Alias, Worklists).
+
+%%----------------------------------------------------------------------
+%% Function: fixAdj
+%%
+%% Description: Fixes adajency set and adjacency list when undoing coalescing
+%%
+%% Parameters:
+%% N -- Node that should be uncoalesced
+%% SavedAdj -- Saved adjacency list
+%% IG -- Interference graph
+%% Target -- The module containing the target-specific functions.
+%%
+%% Returns:
+%% updated Interferece graph
+%%----------------------------------------------------------------------
+fixAdj(N, SavedAdj, IG, Target) ->
+ %Saved = hipe_vectors:get(SavedAdj, N),
+ Saved = hipe_adj_list:edges(N, SavedAdj),
+ ?debug_msg("��--adj to ~p: ~p~n", [N, Saved]),
+ Adj = hipe_ig:node_adj_list(N, IG),
+ ?debug_msg("��--adj to ~p: ~p~n", [N, Adj]),
+ New = findNew(Adj, Saved),
+ ?debug_msg("++--new adj to ~p: ~p~n", [N, New]),
+ removeAdj(New, N, IG, Target),
+ %% XXX the following lines seems to make double nodes in
+ %% some adj_lists, which is a bug, apart from that they
+ %% don't seem to make any difference at all (even though
+ %% they are in the pseudocode of "optimistic coalescing")
+ %% addedge for all in the restored adj_list
+ %%RestoredAdj = hipe_ig:node_adj_list(N, IG),
+ %%?debug_msg("adj_lists_before_restore_o ~n~p~n", [hipe_ig:adj_list(IG)]),
+ %%restoreAdj(RestoredAdj, N, IG, Alias, Target).
+ IG.
+
+removeAdj([], _N, _IG, _Target) ->
+ true;
+removeAdj([V| New], N, IG, Target) ->
+ hipe_ig:remove_edge(V, N, IG, Target),
+ removeAdj(New, N, IG, Target).
+
+%%restoreAdj([], _N, IG, _Alias, _Target) ->
+%% %%?debug_msg("adj_lists__after_restore_o ~n~p~n", [hipe_ig:adj_list(IG)]),
+%% IG;
+%%restoreAdj([V| AdjToN], N, IG, Alias, Target) ->
+%% AliasToV = getAlias(V, Alias),
+%% IG1 = hipe_ig:add_edge(N, AliasToV, IG, Target),
+%% restoreAdj(AdjToN, N, IG1, Alias, Target).
+
+%% XXX This is probably a clumsy way of doing it
+%% better to assure the lists are sorted from the beginning
+%% also coalesce findNew and removeAdj should improve performance
+findNew(Adj, Saved) ->
+ findNew(Adj, Saved, []).
+
+findNew([], _Saved, New) ->
+ New;
+findNew([A| Adj], Saved, New) ->
+ case lists:member(A, Saved) of
+ true -> findNew(Adj, Saved, New);
+ _ -> findNew(Adj, Saved, [A| New])
+ end.
+
+%test_fixAdj(0, _SavedAdj, IG, _Target) ->
+% IG;
+%test_fixAdj(Node, SavedAdj, IG, Target) ->
+% NextNode = Node - 1,
+% IG1 = fixAdj(NextNode, SavedAdj, IG, Target),
+% test_fixAdj(NextNode, SavedAdj, IG1, Target).
+%%----------------------------------------------------------------------
+%% Function: ok
+%%
+%% Description: Checks if a node T is suitable to coalesce with R
+%%
+%% Parameters:
+%% T -- Node to test
+%% R -- Other node to test
+%% IG -- Interference graph
+%% K -- Number of registers
+%% Target -- The module containing the target-specific functions
+%%
+%% Returns:
+%% true iff coalescing is OK
+%%----------------------------------------------------------------------
+
+ok(T, R, IG, K, Target) ->
+ ((hipe_ig:is_trivially_colourable(T, K, IG))
+ orelse Target:is_precoloured(T)
+ orelse hipe_ig:nodes_are_adjacent(T, R, IG)).
+
+%%----------------------------------------------------------------------
+%% Function: all_ok
+%%
+%% Description: True iff, for every T in the list, OK(T,U)
+%%
+%% Parameters:
+%% [T|Ts] -- Nodes to test
+%% U -- Node to test for coalescing
+%% IG -- Interference graph
+%% K -- Number of registers
+%% Target -- The module containing the target-specific functions
+%%
+%% Returns:
+%% true iff coalescing is OK for all nodes in the list
+%%----------------------------------------------------------------------
+
+all_adjacent_ok([], _U, _Worklists, _IG, _K, _Target) -> true;
+all_adjacent_ok([T|Ts], U, Worklists, IG, K, Target) ->
+ case hipe_reg_worklists:member_stack_or_coalesced(T, Worklists) of
+ true -> all_adjacent_ok(Ts, U, Worklists, IG, K, Target);
+ _ ->
+ %% 'andalso' does not preserve tail-recursion
+ case ok(T, U, IG, K, Target) of
+ true -> all_adjacent_ok(Ts, U, Worklists, IG, K, Target);
+ false -> false
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: conservative
+%%
+%% Description: Checks if nodes can be safely coalesced according to
+%% the Briggs' conservative coalescing heuristic
+%%
+%% Parameters:
+%% Nodes -- Adjacent nodes
+%% IG -- Interference graph
+%% K -- Number of registers
+%%
+%% Returns:
+%% true iff coalescing is safe
+%%----------------------------------------------------------------------
+
+conservative(AdjU, AdjV, U, Worklists, IG, K) ->
+ conservative_countU(AdjU, AdjV, U, Worklists, IG, K, 0).
+
+%%----------------------------------------------------------------------
+%% Function: conservative_count
+%%
+%% Description: Counts degrees for conservative (Briggs' heuristics)
+%%
+%% Parameters:
+%% Nodes -- (Remaining) adjacent nodes
+%% IG -- Interference graph
+%% K -- Number of registers
+%% Cnt -- Accumulator for counting
+%%
+%% Returns:
+%% Final value of accumulator
+%%----------------------------------------------------------------------
+
+conservative_countU([], AdjV, U, Worklists, IG, K, Cnt) ->
+ conservative_countV(AdjV, U, Worklists, IG, K, Cnt);
+conservative_countU([Node|AdjU], AdjV, U, Worklists, IG, K, Cnt) ->
+ case hipe_reg_worklists:member_stack_or_coalesced(Node, Worklists) of
+ true -> conservative_countU(AdjU, AdjV, U, Worklists, IG, K, Cnt);
+ _ ->
+ case hipe_ig:is_trivially_colourable(Node, K, IG) of
+ true -> conservative_countU(AdjU, AdjV, U, Worklists, IG, K, Cnt);
+ _ ->
+ Cnt1 = Cnt + 1,
+ if Cnt1 < K -> conservative_countU(AdjU, AdjV, U, Worklists, IG, K, Cnt1);
+ true -> false
+ end
+ end
+ end.
+
+conservative_countV([], _U, _Worklists, _IG, _K, _Cnt) -> true;
+conservative_countV([Node|AdjV], U, Worklists, IG, K, Cnt) ->
+ case hipe_reg_worklists:member_stack_or_coalesced(Node, Worklists) of
+ true -> conservative_countV(AdjV, U, Worklists, IG, K, Cnt);
+ _ ->
+ case hipe_ig:nodes_are_adjacent(Node, U, IG) of
+ true -> conservative_countV(AdjV, U, Worklists, IG, K, Cnt);
+ _ ->
+ case hipe_ig:is_trivially_colourable(Node, K, IG) of
+ true -> conservative_countV(AdjV, U, Worklists, IG, K, Cnt);
+ _ ->
+ Cnt1 = Cnt + 1,
+ if Cnt1 < K -> conservative_countV(AdjV, U, Worklists, IG, K, Cnt1);
+ true -> false
+ end
+ end
+ end
+ end.
+
+%%---------------------------------------------------------------------
+%% Function: selectSpill
+%%
+%% Description: Select the node to spill and spill it
+%% Parameters:
+%% WorkLists -- A datatype containing the different worklists
+%% IG -- The interference graph
+%% K -- The number of available registers
+%% Alias -- The alias mapping
+%% SpillLimit -- Try not to spill any nodes above the spill limit
+%%
+%% Returns:
+%% WorkLists -- The updated worklists
+%%---------------------------------------------------------------------
+
+selectSpill(WorkLists, IG, SpillLimit) ->
+ [CAR|CDR] = hipe_reg_worklists:spill(WorkLists),
+ SpillCost = getCost(CAR, IG, SpillLimit),
+ M = findCheapest(CDR, IG, SpillCost, CAR, SpillLimit),
+ WorkLists1 = hipe_reg_worklists:remove_spill(M, WorkLists),
+ hipe_reg_worklists:add_simplify(M, WorkLists1).
+
+%%---------------------------------------------------------------------
+%% Function: selectSpill
+%%
+%% Description: Select the node to spill and spill it
+%% Parameters:
+%% WorkLists -- A datatype containing the different worklists
+%% Moves -- A datatype containing the move sets
+%% IG -- The interference graph
+%% K -- The number of available registers
+%% Alias -- The alias mapping
+%% SpillLimit -- Try not to spill any nodes above the spill limit
+%%
+%% Returns:
+%% WorkLists -- The updated worklists
+%% Moves -- The updated moves
+%%---------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+selectSpill_O(WorkLists, Moves, IG, K, Alias, SpillLimit) ->
+ [CAR|CDR] = hipe_reg_worklists:spill(WorkLists),
+
+ SpillCost = getCost(CAR, IG, SpillLimit),
+ M = findCheapest(CDR, IG, SpillCost, CAR, SpillLimit),
+
+ WorkLists1 = hipe_reg_worklists:remove_spill(M, WorkLists),
+ %% The published algorithm adds M to the simplify worklist
+ %% before the freezeMoves() call. That breaks the worklist
+ %% invariants, which is why the order is switched here.
+ {WorkLists2,Moves1} = freezeMoves(M, K, WorkLists1, Moves, IG, Alias),
+ WorkLists3 = hipe_reg_worklists:add_simplify(M, WorkLists2),
+ {WorkLists3,Moves1}.
+-endif.
+
+%% Find the node that is cheapest to spill
+
+findCheapest([], _IG, _Cost, Cheapest, _SpillLimit) ->
+ Cheapest;
+findCheapest([Node|Nodes], IG, Cost, Cheapest, SpillLimit) ->
+ ThisCost = getCost(Node, IG, SpillLimit),
+ case ThisCost < Cost of
+ true ->
+ findCheapest(Nodes, IG, ThisCost, Node, SpillLimit);
+ false ->
+ findCheapest(Nodes, IG, Cost, Cheapest, SpillLimit)
+ end.
+
+%% Get the cost for spilling a certain node, node numbers above the spill
+%% limit are extremely expensive.
+
+getCost(Node, IG, SpillLimit) ->
+ case Node > SpillLimit of
+ true -> inf;
+ false ->
+ SpillCost = hipe_ig:node_spill_cost(Node, IG),
+ ?debug_msg("Actual spillcost f node ~w is ~w~n", [Node, SpillCost]),
+ SpillCost
+ end.
+
+%%----------------------------------------------------------------------
+%% Function: freeze
+%%
+%% Description: When both simplifying and coalescing is impossible we
+%% rather freezes a node in stead of spilling, this function
+%% selects a node for freezing (it just picks the first one in
+%% the list)
+%%
+%% Parameters:
+%% K -- The number of available registers
+%% WorkLists -- A datatype containing the different worklists
+%% Moves -- A datatype containing the different movelists
+%% IG -- Interference graph
+%% Alias -- An alias mapping, shows the alias of all coalesced
+%% nodes
+%%
+%% Returns:
+%% WorkLists -- The updated worklists
+%% Moves -- The updated movelists
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+freeze(K, WorkLists, Moves, IG, Alias) ->
+ [U|_] = hipe_reg_worklists:freeze(WorkLists), % Smarter routine?
+ ?debug_msg("freezing node ~p~n", [U]),
+ WorkLists0 = hipe_reg_worklists:remove_freeze(U, WorkLists),
+ %% The published algorithm adds U to the simplify worklist
+ %% before the freezeMoves() call. That breaks the worklist
+ %% invariants, which is why the order is switched here.
+ {WorkLists1, Moves1} = freezeMoves(U, K, WorkLists0, Moves, IG, Alias),
+ WorkLists2 = hipe_reg_worklists:add_simplify(U, WorkLists1),
+ {WorkLists2, Moves1}.
+-endif.
+
+%%----------------------------------------------------------------------
+%% Function: freezeMoves
+%%
+%% Description: Make all move related interferences for a certain node
+%% into ordinary interference arcs.
+%%
+%% Parameters:
+%% U -- The node we want to freeze
+%% K -- The number of available registers
+%% WorkLists -- A datatype containing the different worklists
+%% Moves -- A datatype containing the different movelists
+%% IG -- Interference graph
+%% Alias -- An alias mapping, shows the alias of all coalesced
+%% nodes
+%%
+%% Returns:
+%% WorkLists -- The updated worklists
+%% Moves -- The updated movelists
+%%----------------------------------------------------------------------
+
+-ifdef(COMPARE_ITERATED_OPTIMISTIC).
+freezeMoves(U, K, WorkLists, Moves, IG, Alias) ->
+ Nodes = hipe_moves:node_moves(U, Moves),
+ freezeEm(U, Nodes, K, WorkLists, Moves, IG, Alias).
+
+%% Find what the other value in a copy instruction is, return false if
+%% the instruction isn't a move with the first argument in it.
+
+moves(U, Move, Alias, Moves) ->
+ {X,Y} = hipe_moves:get_move(Move, Moves),
+ %% The old code (which followed the published algorithm) did
+ %% not follow aliases before looking for "the other" node.
+ %% This caused moves() to skip some moves, making some nodes
+ %% still move-related after freezeMoves(). These move-related
+ %% nodes were then added to the simplify worklist (by freeze()
+ %% or selectSpill()), breaking the worklist invariants. Nodes
+ %% already simplified appeared in coalesce_O(), were re-added to
+ %% the simplify worklist by add_worklist(), simplified again,
+ %% and coloured multiple times by assignColors(). Ouch!
+ X1 = getAlias(X, Alias),
+ Y1 = getAlias(Y, Alias),
+ if U =:= X1 -> Y1;
+ U =:= Y1 -> X1;
+ true -> exit({?MODULE,moves}) % XXX: shouldn't happen
+ end.
+
+freezeEm(_U, [], _K, WorkLists, Moves, _IG, _Alias) ->
+ {WorkLists,Moves};
+freezeEm(U, [M|Ms], K, WorkLists, Moves, IG, Alias) ->
+ V = moves(U, M, Alias, Moves),
+ {WorkLists2,Moves2} = freezeEm2(U, V, M, K, WorkLists, Moves, IG, Alias),
+ freezeEm(U, Ms, K, WorkLists2, Moves2, IG, Alias).
+
+freezeEm2(U, V, M, K, WorkLists, Moves, IG, Alias) ->
+ case hipe_moves:member_active(M, Moves) of
+ true ->
+ Moves1 = hipe_moves:remove_active(M, Moves),
+ freezeEm3(U, V, M, K, WorkLists, Moves1, IG, Alias);
+ false ->
+ Moves1 = hipe_moves:remove_worklist(M, Moves),
+ freezeEm3(U, V, M, K, WorkLists, Moves1, IG, Alias)
+ end.
+
+freezeEm3(_U,V,_M,K,WorkLists,Moves,IG,_Alias) ->
+ Moves1 = Moves, % drop frozen move M
+ V1 = V, % getAlias(V,Alias),
+ %% "not MoveRelated(v)" is cheaper than "NodeMoves(v) = {}"
+ case ((not hipe_moves:move_related(V1,Moves1)) andalso
+ hipe_ig:is_trivially_colourable(V1,K,IG)) of
+ true ->
+ ?debug_msg("freezing move to ~p~n", [V]),
+ Worklists1 = hipe_reg_worklists:transfer_freeze_simplify(V1, WorkLists),
+ {Worklists1,Moves1};
+ false ->
+ {WorkLists,Moves1}
+ end.
+-endif.