%% -*- erlang-indent-level: 2 -*-
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%%%=======================================================================
%% File : hipe_icode_ssa_struct_reuse.erl
%% Author : Ragnar Osterlund <[email protected]>
%% student at the compiler techniques 2 course at UU 2007
%% Description : HiPE module that removes redundant or partially redundant
%% structure creations from Icode.
%% It does so by inserting redundant expressions as late
%% as possible in the CFG, with the exception of loops where
%% expressions are moved to just before the loop head.
%% Current Icode instructions that can be moved are mktuple()
%% and cons() primop calls. It also handles cases like
%% f({Z}) -> {Z}. It does so by looking at the structure of
%% the match, and recognizes tuples and conses.
%%=======================================================================
-module(hipe_icode_ssa_struct_reuse).
-export([struct_reuse/1]).
-include("../main/hipe.hrl").
-include("hipe_icode.hrl").
-include("hipe_icode_primops.hrl").
-include("../flow/cfg.hrl").
-define(SET, ordset).
-define(SETS, ordsets).
%%-define(DEBUG, true).
-define(MKTUPLE, mktuple).
-define(CONS, cons).
-define(SR_INSTR_TYPE, sr_instr_type).
-define(SR_STRUCT_INSTR_TYPE, sr_struct_instr_type).
-type struct_type() :: {?CONS | ?MKTUPLE, icode_term_arg(), any()}.
-type struct_elems() :: {icode_var(), non_neg_integer(), icode_term_arg()}.
%% DATATYPE AREA
%%-----------------------------------------------------------------------------
%% maps
%% The maps are used to identify variables and expressions.
%% The maps are:
%%
%% expr - a map that contains value numbered structure expressions, ie
%% mktuple and cons expression. The key is the value number and the value
%% is an expr record.
%%
%% instr - maps the semantic instruction to an expression value number,
%% that is, a key in the expr map above.
%%
%% var - maps variables to expression value numbers. These variables are
%% defined or used by the structure expressions.
-record(maps, {var = gb_trees:empty() :: gb_trees:tree(),
instr = gb_trees:empty() :: gb_trees:tree(),
expr = gb_trees:empty() :: gb_trees:tree()}).
maps_var(#maps{var = Out}) -> Out.
maps_instr(#maps{instr = Out}) -> Out.
maps_expr(#maps{expr = Out}) -> Out.
maps_expr_keys(Maps) -> gb_trees:keys(maps_expr(Maps)).
maps_expr_values(Maps) -> gb_trees:values(maps_expr(Maps)).
maps_instr_lookup(Instr, Maps) -> gb_trees:lookup(Instr, maps_instr(Maps)).
maps_instr_enter(Instr, ExprId, Maps) ->
NewInstr = gb_trees:enter(Instr, ExprId, maps_instr(Maps)),
Maps#maps{instr = NewInstr}.
maps_expr_get(Id, Maps) -> gb_trees:get(Id, maps_expr(Maps)).
maps_expr_enter(Expr, Maps) ->
NewExprMap = gb_trees:enter(expr_id(Expr), Expr, maps_expr(Maps)),
Maps#maps{expr = NewExprMap}.
maps_var_get(Var, Maps) -> gb_trees:get(Var, maps_var(Maps)).
maps_var_lookup(Var, #maps{var = VarMap}) -> gb_trees:lookup(Var, VarMap).
maps_var_enter(Var, Info, Maps = #maps{var = VarMap}) ->
NewMap = gb_trees:enter(Var, Info, VarMap),
Maps#maps{var = NewMap}.
maps_var_insert(Var, Info, Maps = #maps{var = VarMap}) ->
NewMap = gb_trees:insert(Var, Info, VarMap),
Maps#maps{var = NewMap}.
maps_balance(Maps) ->
Maps#maps{instr = gb_trees:balance(maps_instr(Maps)),
expr = gb_trees:balance(maps_expr(Maps)),
var = gb_trees:balance(maps_var(Maps))}.
maps_expr_key_enter(Expr, Maps) ->
NewMaps = maps_instr_enter(expr_key(Expr), expr_id(Expr), Maps),
maps_expr_enter(Expr, NewMaps).
%%-----------------------------------------------------------------------------
%% expr
%% An expression record. Contains information about a structure expression.
%% The fields are:
%%
%% id - the value number of the expression
%% key - the semantic instruction, as defined in icode, with destination
%% removed and arguments rewritten.
%% defs - destination variable to hold the value of the expression.
%% direct_replace - indicates whether the expression shall be replaced wherever
%% it occurs, although it might not have been inserted. This is used for
%% the expressions that are detected by the icode type constructs.
%% inserts - a list of node labels that will insert this expression
%% use - a list of expression value numbers that use the value of this
%% expression
-record(expr, {id = none :: 'none' | non_neg_integer(),
key = none :: 'none' | tuple(), % illegal_icode_instr()
defs = none :: 'none' | [icode_var()],
direct_replace = false :: boolean(),
inserts = ?SETS:new() :: ?SETS:?SET(_),
use = ?SETS:new() :: ?SETS:?SET(_)}).
expr_id(#expr{id = Out}) -> Out.
expr_defs(#expr{defs = Out}) -> Out.
expr_key(#expr{key = Out}) -> Out.
expr_inserts(#expr{inserts = Out}) -> Out.
expr_use(#expr{use = Out}) -> Out.
expr_direct_replace(#expr{direct_replace = Out}) -> Out.
expr_use_add(Expr = #expr{use = UseSet}, Use) ->
Expr#expr{use = ?SETS:add_element(Use, UseSet)}.
%% expr_key_set(Expr, In) -> Expr#expr{key = In}.
expr_direct_replace_set(Expr, In) -> Expr#expr{direct_replace = In}.
expr_inserts_set(Expr, In) -> Expr#expr{inserts = In}.
expr_create(Key, Defs) ->
NewExprId = new_expr_id(),
#expr{id = NewExprId, key = Key, defs = Defs}.
%%-----------------------------------------------------------------------------
%% varinfo
%% A variable mapping info. Contains info about variable references.
%% The fields are:
%%
%% use - a set of expression value numbers that use this variable
%% ref - the variable which value this variable will be assigned
%% when expression is replaced. This is encoded as {N, M} where
%% N is the expression value number and M is the nth destination
%% variable defined by the expression N.
%% elem - indicates that this variable has been detected to be a part of
%% a tuple. The field contains a {V, N} tuple where V is the variable
%% that refers to the structure that this variable is an element in
%% and N is the position that the element occurs on in the tuple. Eg.
%% {{var, 3}, 2} means that the variable {var, 3} refers to a tuple
%% in which this variable is on second place.
%% exprid - a expression value number which is the expression that
%% the variable is defined by.
-record(varinfo, {use = ?SETS:new() :: ?SETS:?SET(_),
ref = none :: 'none' | {non_neg_integer(), non_neg_integer()},
elem = none :: 'none' | {icode_var(), non_neg_integer()},
exprid = none :: 'none' | non_neg_integer()}).
varinfo_exprid(#varinfo{exprid = Out}) -> Out.
varinfo_use_add(#varinfo{use = UseSet} = I, Use) ->
I#varinfo{use = ?SETS:add_element(Use, UseSet)}.
%%-----------------------------------------------------------------------------
%% node - a node in the temp CFG.
%%
%% label - the label of the node in the original CFG
%% pred - a list of predecessors to this node
%% succ - a list of successors to this node
%% code - code from CFG filtered to only contain structure instructions
%% non_struct_defs - a list of variable definitions that are not defined
%% by structures
%% up_expr - upwards exposed expression value numbers
%% killed_expr - killed expressions value numbers
%% sub_inserts - a set of labels of nodes that defines one or more
%% expressions and that are in a subtree of this node
%% inserts - a set of expression value numbers to be inserted into the node
%% antic_in - a set of expression value numbers that are anticipated into
%% the node
%% antic_out - a set of expression value numbers that are anticipated out of
%% the node
%% phi - a tree of node labels which is defined in phi functions in the node
%% varmap - a list of variable tuples {V1, V2} that maps a variable that are
%% the output of phi functions in sub blocks, V1, into a variable
%% flowing from the block of this node, V2.
%% struct_type - a list of {V, N} tuples that indicates that V is a tuple
%% with N elements. These are added from the icode primop type().
%% struct_elems - a list of {VD, N, VS} tuples where VD is a variable in the N'th position
%% in VS. These are added from the icode primop unsafe_element()
-record(node, {
label = none :: 'none' | icode_lbl(),
pred = none :: 'none' | [icode_lbl()],
succ = none :: 'none' | [icode_lbl()],
code = [] :: [tuple()], % [illegal_icode_instr()]
phi = gb_trees:empty() :: gb_trees:tree(),
varmap = [] :: [{icode_var(), icode_var()}],
pre_loop = false :: boolean(),
non_struct_defs = gb_sets:new() :: gb_sets:set(),
up_expr = none :: 'none' | ?SETS:?SET(_),
killed_expr = none :: 'none' | ?SETS:?SET(_),
sub_inserts = ?SETS:new() :: ?SETS:?SET(_),
inserts = ?SETS:new() :: ?SETS:?SET(_),
antic_in = none :: 'none' | ?SETS:?SET(_),
antic_out = none :: 'none' | ?SETS:?SET(_),
struct_type = [] :: [struct_type()],
struct_elems = [] :: [struct_elems()]}).
node_sub_inserts(#node{sub_inserts = Out}) -> Out.
node_inserts(#node{inserts = Out}) -> Out.
node_antic_out(#node{antic_out = Out}) -> Out.
node_antic_in(#node{antic_in = Out}) -> Out.
node_killed_expr(#node{killed_expr = Out}) -> Out.
node_pred(#node{pred = Out}) -> Out.
node_succ(#node{succ = Out}) -> Out.
node_label(#node{label = Out}) -> Out.
node_code(#node{code = Out}) -> Out.
node_non_struct_defs(#node{non_struct_defs = Out}) -> Out.
node_up_expr(#node{up_expr = Out}) -> Out.
node_pre_loop(#node{pre_loop = Out}) -> Out.
node_struct_type(#node{struct_type = Out}) -> Out.
%% node_atom_type(#node{atom_type = Out}) -> Out.
node_struct_elems(#node{struct_elems = Out}) -> Out.
node_pre_loop_set(Node) -> Node#node{pre_loop = true}.
node_phi_add(Node = #node{phi = Phi}, Pred, Value) ->
NewList =
case gb_trees:lookup(Pred, Phi) of
{value, List} -> [Value | List];
none -> [Value]
end,
Node#node{phi = gb_trees:enter(Pred, NewList, Phi)}.
node_phi_get(#node{phi = Phi}, Pred) ->
case gb_trees:lookup(Pred, Phi) of
{value, List} -> List;
none -> []
end.
node_code_add(Node = #node{code = Code}, Instr) ->
Node#node{code = [Instr | Code]}.
node_code_rev(Node = #node{code = Code}) ->
Node#node{code = lists:reverse(Code)}.
node_struct_type_add(Node = #node{struct_type = T}, Value) ->
Node#node{struct_type = [Value | T]}.
%% node_atom_type_add(Node = #node{atom_type = T}, Value) ->
%% Node#node{atom_type = [Value | T]}.
node_struct_elems_add(Node = #node{struct_elems = T}, Value) ->
Node#node{struct_elems = [Value | T]}.
node_non_struct_defs_list(Node) ->
gb_sets:to_list(node_non_struct_defs(Node)).
node_non_struct_instr_add(Node, Instr) ->
DefList = hipe_icode:defines(Instr),
Tmp = gb_sets:union(node_non_struct_defs(Node), gb_sets:from_list(DefList)),
Node#node{non_struct_defs = Tmp}.
node_set_sub_inserts(Node, In) -> Node#node{sub_inserts = In}.
node_add_insert(Node, In) ->
NewIns = ?SETS:add_element(In, node_inserts(Node)),
Node#node{inserts = NewIns}.
node_union_sub_inserts(Node, SubIns) ->
NewSubIns = ?SETS:union(SubIns, node_sub_inserts(Node)),
node_set_sub_inserts(Node, NewSubIns).
node_varmap_set(Node, Vars) ->
Node#node{varmap = Vars}.
node_varmap_lookup(#node{varmap = Varmap}, Var) ->
case lists:keyfind(Var, 1, Varmap) of
{_, NewVar} -> NewVar;
false -> Var
end.
node_create(Label, Pred, Succ) ->
#node{label = Label, pred = Pred, succ = Succ}.
%%-----------------------------------------------------------------------------
%% nodes - describes the new temporary CFG
%%
%% domtree - the dominator tree of the original CFG
%% labels - the labels of the original CFG, filtered to only contain non fail trace paths
%% postorder - the postorder walk of labels of the original CFG, filtered to only contain non fail trace paths
%% rev_postorder - reverse of postorder.
%% start_label - the start basic block label.
%% all_expr - all expression value numbers that the CFG defines
%% tree - the tree of nodes, with labels as keys and node records as values
-record(nodes, {
domtree = none :: 'none' | hipe_dominators:domTree(),
labels = none :: 'none' | [icode_lbl()],
postorder = none :: 'none' | [icode_lbl()],
start_label = none :: 'none' | icode_lbl(),
rev_postorder = none :: 'none' | [icode_lbl()],
all_expr = none :: 'none' | [non_neg_integer()],
tree = gb_trees:empty() :: gb_trees:tree()}).
nodes_postorder(#nodes{postorder = Out}) -> Out.
nodes_rev_postorder(#nodes{rev_postorder = Out}) -> Out.
nodes_tree(#nodes{tree = Out}) -> Out.
nodes_domtree(#nodes{domtree = Out}) -> Out.
nodes_start_label(#nodes{start_label = Out}) -> Out.
nodes_tree_is_empty(#nodes{tree = Tree}) ->
gb_trees:is_empty(Tree).
nodes_tree_set(Tree, Nodes) -> Nodes#nodes{tree = Tree}.
nodes_all_expr_set(AllExpr, Nodes) -> Nodes#nodes{all_expr = AllExpr}.
nodes_tree_values(Nodes) ->
gb_trees:values(nodes_tree(Nodes)).
get_node(Label, Nodes) ->
gb_trees:get(Label, nodes_tree(Nodes)).
enter_node(Node, Nodes) ->
nodes_tree_set(gb_trees:enter(node_label(Node), Node, nodes_tree(Nodes)), Nodes).
remove_node(Node, Nodes) ->
nodes_tree_set(gb_trees:delete(node_label(Node), nodes_tree(Nodes)), Nodes).
nodes_create() -> #nodes{}.
%%-----------------------------------------------------------------------------
%% update
%% record used when updating the CFG, keeping track of which expressions
%% have been inserted and their mappings to variable names.
%%
%% inserted - maps an expression to a list of variables
%% del_red_test - flag that is set to true when the reduction test
%% has been inserted is used to move the reduction test.
-record(update, {inserted = gb_trees:empty() :: gb_trees:tree(),
del_red_test = false :: boolean()}).
update_inserted_lookup(#update{inserted = Inserted}, ExprId) ->
gb_trees:lookup(ExprId, Inserted).
update_inserted_add_new(Update = #update{inserted = Inserted}, ExprId, Defs) ->
VarList = [case hipe_icode:is_var(Def) of
true -> hipe_icode:mk_new_var();
false ->
case hipe_icode:is_reg(Def) of
true -> hipe_icode:mk_new_reg();
false ->
true = hipe_icode:is_fvar(Def),
hipe_icode:mk_new_fvar()
end
end || Def <- Defs],
NewInserted = gb_trees:enter(ExprId, VarList, Inserted),
{Update#update{inserted = NewInserted}, VarList}.
update_inserted_add(Update = #update{inserted = Inserted}, ExprId, Defs) ->
Update#update{inserted = gb_trees:enter(ExprId, Defs, Inserted)}.
update_del_red_test(#update{del_red_test = DelRed}) -> DelRed.
update_del_red_test_set(Update) ->
Update#update{del_red_test = true}.
%%-----------------------------------------------------------------------------
%% CODE AREA
%%-----------------------------------------------------------------------------
%% Main function called from the hipe_main module
-spec struct_reuse(cfg()) -> cfg().
struct_reuse(CFG) ->
%% debug_init_case_count(?SR_INSTR_TYPE),
%% debug_init_case_count(?SR_STRUCT_INSTR_TYPE),
%% debug_function({wings_ask,ask_unzip,3}, CFG),
%% debug_function(nil, CFG),
%% set_debug_flag(true),
%% debug_struct("CFG In: ", CFG),
%% debug_cfg_pp(CFG),
init_expr_id(),
Nodes = construct_nodes(CFG),
case nodes_tree_is_empty(Nodes) of
false ->
Maps = create_maps(Nodes),
Nodes3 = init_nodes(Nodes, Maps),
Nodes4 = calc_anticipated(Nodes3),
{Nodes5, Maps3} = calc_inserts(Nodes4, Maps),
Nodes6 = update_nodes_inserts(Nodes5, Maps3),
%% debug_list("ExprMap: ", gb_trees:to_list(Maps3#maps.expr)),
%% debug_list("VarMap: ", gb_trees:to_list(maps_var(Maps3))),
%% debug_nodes(Nodes6),
%% update the cfg
CFG1 = rewrite_cfg(CFG, Nodes6, Maps3),
CFG2 = hipe_icode_ssa:remove_dead_code(CFG1),
CFGOut = hipe_icode_ssa_copy_prop:cfg(CFG2),
%% CFGOut = CFG1,
%% print_struct("CFG: ", CFG),
%% debug_cfg_pp(CFG),
%% debug_cfg_pp(CFGOut),
%% debug_print_case_count(?SR_STRUCT_INSTR_TYPE),
%% debug_print_case_count(?SR_INSTR_TYPE),
%% debug("Done~n"),
%% debug_struct("CFG Out: ", CFGOut),
CFGOut;
true ->
CFG
end.
%%-----------------------------------------------------------------------------
%% Calculate simplified CFG with all fail paths removed
construct_nodes(CFG) ->
%% all important dominator tree
DomTree = hipe_dominators:domTree_create(CFG),
%% construct initial nodes
{Nodes, NonFailSet} = nodes_from_cfg(CFG, DomTree),
%% remove nodes on fail paths
NewNodes = prune_nodes(Nodes, NonFailSet),
%% fill in misc node tree info
Postorder = [Label || Label <- hipe_icode_cfg:postorder(CFG),
gb_sets:is_member(Label, NonFailSet)],
%% check postorder is valid
PostOrderTmp = hipe_icode_cfg:postorder(CFG),
LabelsTmp = hipe_icode_cfg:labels(CFG),
case length(PostOrderTmp) =/= length(LabelsTmp) of
true ->
print("Warning, Postorder and Labels differ!~n"),
print_struct("Postorder: ", PostOrderTmp),
print_struct("Labels: ", LabelsTmp);
false ->
done
end,
RevPostorder = lists:reverse(Postorder),
StartLabel = hipe_icode_cfg:start_label(CFG),
NewTree = gb_trees:balance(nodes_tree(NewNodes)),
NewNodes#nodes{postorder = Postorder,
rev_postorder = RevPostorder,
start_label = StartLabel,
tree = NewTree,
domtree = DomTree}.
%%-----------------------------------------------------------------------------
%% Constructs a tree of nodes, one node for each basic block in CFG
nodes_from_cfg(CFG, DomTree) ->
lists:foldl(fun(Label, {NodesAcc, NonFailAcc}) ->
Code = hipe_bb:code(hipe_icode_cfg:bb(CFG, Label)),
Pred = hipe_icode_cfg:pred(CFG, Label),
Succ = hipe_icode_cfg:succ(CFG, Label),
%% debug_struct("Label: ", Label),
%% debug_struct("Code: ", Code),
%% Find all structures and phi functions.
%% Find all defines in this bb that are not from structures
%% and add them to NonStructDefs, later to be used for calculating upwards
%% exposed expressions, and killed expressions.
%% Also find all non fail blocks, ie backtrace from return blocks,
%% and add them to NewNonFailAcc
Node = node_create(Label, Pred, Succ),
{NewNode, NewNonFailAcc, PreLoopPreds} =
lists:foldl(fun(Instr, {NodeAcc, NFAcc, PLPAcc}) ->
case instr_type(Instr) of
struct ->
{node_code_add(NodeAcc, Instr), NFAcc, PLPAcc};
return ->
{NodeAcc, get_back_trace_rec(CFG, Label, NFAcc), PLPAcc};
{struct_elems, NumElem, DstVar, SrcVar} ->
NewNodeAcc = node_struct_elems_add(NodeAcc, {DstVar, NumElem, SrcVar}),
{node_non_struct_instr_add(NewNodeAcc, Instr), NFAcc, PLPAcc};
{struct_type, NumElems, Var, Type} ->
{node_struct_type_add(NodeAcc, {Type, Var, NumElems}), NFAcc, PLPAcc};
{tuple_arity, Var, Cases} ->
NewNodeAcc =
lists:foldl(fun(Case, NAcc) ->
case Case of
{{const, {flat, Arity}}, _} ->
Tuple = {?MKTUPLE, Var, Arity},
node_struct_type_add(NAcc, Tuple);
_ -> NAcc
end
end, NodeAcc, Cases),
{NewNodeAcc, NFAcc, PLPAcc};
%% {atom_type, Atom, Var} ->
%% {node_atom_type_add(NodeAcc, {Var, Atom}), NFAcc, PLPAcc};
phi ->
Def = hipe_icode:phi_dst(Instr),
Part = lists:foldl(fun(P = {Pr, PredVar}, {IsDef, NotDom}) ->
case hipe_dominators:domTree_dominates(Label, Pr, DomTree) of
false ->
{IsDef, [P | NotDom]};
true ->
{IsDef andalso PredVar =:= Def, NotDom}
end
end, {true, []}, hipe_icode:phi_arglist(Instr)),
case Part of
{true, [{P, V}]} ->
%% This is the only case recognized so far. All phi
%% sub block references a static variable that is
%% assigned the same value again in the phi function.
{node_phi_add(NodeAcc, P, {Def, V}),
NFAcc, ?SETS:add_element(P, PLPAcc)};
{false, [{P, _}]} ->
{node_non_struct_instr_add(NodeAcc, Instr),
NFAcc, ?SETS:add_element(P, PLPAcc)};
_ ->
{node_non_struct_instr_add(NodeAcc, Instr), NFAcc, PLPAcc}
end;
_ ->
{node_non_struct_instr_add(NodeAcc, Instr), NFAcc, PLPAcc}
end
end, {Node, NonFailAcc, ?SETS:new()}, Code),
%% insert the new node
NewNodesAcc = enter_node(node_code_rev(NewNode), NodesAcc),
%% Set the pre loop flag of all nodes that are predecessor to this node
%% and that are the first nodes prior to a loop.
NewNodesAcc2 =
lists:foldl(fun(Lbl, NsAcc) ->
PredNode = get_node(Lbl, NsAcc),
NewPredNode = node_pre_loop_set(PredNode),
NewPredNode2 = node_varmap_set(NewPredNode, node_phi_get(NewNode, Lbl)),
enter_node(NewPredNode2, NsAcc)
end, NewNodesAcc, PreLoopPreds),
{NewNodesAcc2, NewNonFailAcc}
end, {nodes_create(), gb_sets:new()}, hipe_icode_cfg:reverse_postorder(CFG)).
%%-----------------------------------------------------------------------------
%% Get all labels from Label to root of CFG, ie backtraces from Label.
get_back_trace_rec(CFG, Label, LabelSet) ->
%% debug_struct("Label :", Label),
%% debug_struct("Set :", gb_sets:to_list(LabelSet)),
case gb_sets:is_member(Label, LabelSet) of
false ->
Preds = hipe_icode_cfg:pred(CFG, Label),
lists:foldl(fun(Lbl, SetAcc) ->
get_back_trace_rec(CFG, Lbl, SetAcc)
end, gb_sets:add(Label, LabelSet), Preds);
true -> LabelSet
end.
%%-----------------------------------------------------------------------------
%% Remove all fail block paths and successors and predecessors
%% That are on fail paths
prune_nodes(Nodes, NonFailSet) ->
lists:foldl(fun(Node, NodesAcc) ->
case gb_sets:is_member(node_label(Node), NonFailSet) of
true ->
NewSucc = [L || L <- node_succ(Node), gb_sets:is_member(L, NonFailSet)],
NewPred = [L || L <- node_pred(Node), gb_sets:is_member(L, NonFailSet)],
enter_node(Node#node{succ = NewSucc, pred = NewPred}, NodesAcc);
false ->
remove_node(Node, NodesAcc)
end
end, Nodes, nodes_tree_values(Nodes)).
%%-----------------------------------------------------------------------------
%% Map calculations.
%%-----------------------------------------------------------------------------
%% Create a maps structure from the Nodes record
create_maps(Nodes) ->
Maps = lists:foldl(fun(Label, MapsAcc) ->
Node = get_node(Label, Nodes),
NewMapsAcc = maps_from_node_struct_type(MapsAcc, Node),
NewMapsAcc2 = maps_from_node_struct_elems(NewMapsAcc, Node),
%% NewMapsAcc3 = maps_from_node_atom_type(NewMapsAcc2, Node),
maps_from_node_code(NewMapsAcc2, Node)
end, #maps{}, nodes_rev_postorder(Nodes)),
maps_balance(Maps).
%%-----------------------------------------------------------------------------
%% Add all elements in the struct_type list of Node to Maps as expressions
maps_from_node_struct_type(Maps, Node) ->
%% debug_struct("Node Label: ", node_label(Node)),
%% debug_struct("Node Tuple Type: ", node_struct_type(Node)),
lists:foldl(fun({Type, Var, Size}, MapsAcc) ->
Key = create_elem_expr_key(Size, Var, []),
InstrKey = hipe_icode:mk_primop([], Type, Key),
NewExpr2 = expr_create(InstrKey, [Var]),
NewExpr3 = expr_direct_replace_set(NewExpr2, true),
maps_expr_key_enter(NewExpr3, MapsAcc)
end, Maps, node_struct_type(Node)).
create_elem_expr_key(0, _, Key) -> Key;
create_elem_expr_key(N, Var, Key) ->
create_elem_expr_key(N - 1, Var, [{Var, N} | Key]).
%%-----------------------------------------------------------------------------
%%maps_from_node_atom_type(Maps, Node) ->
%% lists:foldl(fun({Var, Atom}, MapsAcc) ->
%% case maps_var_lookup(Var, MapsAcc) of
%% none ->
%% MapsAcc;
%% {value, #varinfo{elem = none}} ->
%% MapsAcc;
%% {value, #varinfo{elem = {Src, Num, ExprId}}} ->
%% Expr = maps_expr_get(ExprId, MapsAcc),
%% Key = expr_key(Expr),
%%
%% Filter = fun(Arg) ->
%% case Arg of
%% {Src, Num, ExprId} ->
%% hipe_icode:mk_const(Atom);
%% _ ->
%% Arg
%% end end,
%%
%% NewKey = replace_call_variables(Filter, Key),
%% NewExpr = expr_create(NewKey, expr_defs(Expr)),
%% maps_expr_key_enter(NewExpr, MapsAcc)
%% end
%% end, Maps, node_atom_type(Node)).
%%-----------------------------------------------------------------------------
%% Add all struct_elemns in Node to Maps as variables
maps_from_node_struct_elems(Maps, Node) ->
lists:foldl(fun({Dst, Num, Src}, MapsAcc) ->
VarInfo = #varinfo{elem = {Src, Num}},
maps_var_insert(Dst, VarInfo, MapsAcc)
end, Maps, node_struct_elems(Node)).
%%-----------------------------------------------------------------------------
%% Get all expressions defined by the Node and insert them into Maps.
%% Also insert information about all affected variables into Maps.
maps_from_node_code(Maps, Node) ->
%% debug_struct("Node Label: ", Label),
%% debug_struct("Node Code: ", Code),
%% Label = node_label(Node),
lists:foldl(fun(Instr, MapsAcc) ->
%% create two keys that are used to reference this structure creation
%% instruction, so that we can lookup its expression value number
%% later.
InstrKey = hipe_icode:call_dstlist_update(Instr, []),
%% Fetch the two keys from the instruction
{HasElems, RefKey, ElemKey} =
replace_call_vars_elems(MapsAcc, InstrKey),
%% create a new expr record or lookup an existing one.
case HasElems of
true ->
%% The instruction contains uses of variables that are
%% part of another structure.
case maps_instr_lookup(ElemKey, MapsAcc) of
{value, ExprId} ->
%% The instruction is equal to a structure that has
%% already been created. This is the f({Z}) -> {Z}
%% optimization. I.e. there is no need to create {Z} again.
%% Also lookup if ExprId is defining a variable that is
%% already an element in another structure. If so,
%% use that element. This takes care of nested structures
%% such as f({X, {Y, Z}}) -> {X, {Y, Z}}.
#expr{defs = [Var]} = maps_expr_get(ExprId, MapsAcc),
StructElem =
case maps_var_lookup(Var, MapsAcc) of
{value, #varinfo{elem = Elem, exprid = none}} when Elem =/= none ->
Elem;
_ -> none
end,
Defines = hipe_icode:defines(Instr),
maps_varinfos_create(Defines, ExprId, StructElem, MapsAcc);
none ->
%% create a new expression
maps_expr_varinfos_create(Instr, RefKey, MapsAcc)
end;
false ->
%% create a new expression
maps_expr_varinfos_create(Instr, RefKey, MapsAcc)
end
end, Maps, node_code(Node)).
%%-----------------------------------------------------------------------------
%% Creates varinfo structures with exprid set to ExprId for all
%% variables contained in Defines. These are put into MapsIn.
maps_varinfos_create(Defines, ExprId, Elem, MapsIn) ->
VarInfo = #varinfo{exprid = ExprId, elem = Elem},
{MapsOut, _} =
lists:foldl(fun (Def, {Maps, NumAcc}) ->
NewVarInfo = VarInfo#varinfo{ref = {ExprId, NumAcc}},
{maps_var_insert(Def, NewVarInfo, Maps), NumAcc + 1}
end, {MapsIn, 1}, Defines),
MapsOut.
%%-----------------------------------------------------------------------------
%% Creates a new expression from RefKey if RefKey is not already reffering
%% to an expression. Also creates varinfo structures for all variables defined
%% and used by Instr. Result is put in Maps.
maps_expr_varinfos_create(Instr, RefKey, Maps) ->
Defines = hipe_icode:defines(Instr),
{ExprId, Maps2} =
case maps_instr_lookup(RefKey, Maps) of
{value, EId} ->
{EId, Maps};
none ->
NewExpr = expr_create(RefKey, Defines),
{expr_id(NewExpr), maps_expr_key_enter(NewExpr, Maps)}
end,
Maps3 = maps_varinfos_create(Defines, ExprId, none, Maps2),
update_maps_var_use(Instr, ExprId, Maps3).
%%-----------------------------------------------------------------------------
%% A variable replacement function that returns a tuple of three elements
%% {T, K1, K2}, where T indicates if Instr contained variables that where
%% elements of other structures, K1 is the Instr with all variables that
%% references another structure replaced, and K2 is K1 but also with all
%% variables that are elements of other structures replaced.
replace_call_vars_elems(Maps, Instr) ->
VarMap = maps_var(Maps),
{HasElems, Vars, Elems} =
lists:foldr(fun(Arg, {HasElems, Vars, Elems}) ->
case hipe_icode:is_const(Arg) of
false ->
case gb_trees:lookup(Arg, VarMap) of
none ->
{HasElems, [Arg | Vars], [Arg | Elems]};
{value, #varinfo{ref = none, elem = none}} ->
{HasElems, [Arg | Vars], [Arg | Elems]};
{value, #varinfo{ref = Ref, elem = none}} ->
{HasElems, [Ref | Vars], [Ref | Elems]};
{value, #varinfo{ref = none, elem = Elem}} ->
{true, [Arg | Vars], [Elem | Elems]};
{value, #varinfo{ref = Ref, elem = Elem}} ->
{true, [Ref | Vars], [Elem | Elems]}
end;
true ->
{HasElems, [Arg | Vars], [Arg | Elems]}
end end, {false, [], []}, hipe_icode:args(Instr)),
{HasElems, hipe_icode:call_args_update(Instr, Vars),
hipe_icode:call_args_update(Instr, Elems)}.
%%-----------------------------------------------------------------------------
%% Updates the usage information of all variables used by Instr to also
%% contain Id and updates Maps to contain the new variable information.
%% Also updates the expressions where the updated variables are used to
%% contain the use information.
update_maps_var_use(Instr, Id, Maps) ->
lists:foldl(fun(Use, MapsAcc) ->
VarInfo = get_varinfo(Use, MapsAcc),
NewVarInfo = varinfo_use_add(VarInfo, Id),
MapsAcc2 = maps_var_enter(Use, NewVarInfo, MapsAcc),
case varinfo_exprid(VarInfo) of
none ->
MapsAcc2;
VarExprId ->
Expr = maps_expr_get(VarExprId, MapsAcc2),
NewExpr = expr_use_add(Expr, Id),
maps_expr_enter(NewExpr, MapsAcc2)
end
end, Maps, hipe_icode:uses(Instr)).
%%-----------------------------------------------------------------------------
%% Looks up an old variable info or creates a new one if none is found.
get_varinfo(Var, Maps) ->
case maps_var_lookup(Var, Maps) of
{value, Info} ->
Info;
none ->
#varinfo{}
end.
%%-----------------------------------------------------------------------------
%% filters all arguments to a function call Instr that are not constants
%% through the Filter function, and replaces the arguments in Instr with
%% the result.
replace_call_variables(Filter, Instr) ->
NewArgs = [case hipe_icode:is_const(Arg) of
false -> Filter(Arg);
true -> Arg
end || Arg <- hipe_icode:args(Instr)],
hipe_icode:call_args_update(Instr, NewArgs).
%%-----------------------------------------------------------------------------
%% Init nodes from node local expression information
init_nodes(Nodes, Maps) ->
AllExpr = maps_expr_keys(Maps),
lists:foldl(fun(Node, NodesAcc) ->
UEExpr = calc_up_exposed_expr(maps_var(Maps), Node),
%% print_list("Up ExprSet: ", ?SETS:to_list(UEExpr)),
KilledExpr = calc_killed_expr(Node, Maps),
%% print_list("Killed: ", ?SETS:to_list(KilledExpr)),
%% End nodes have no anticipated out
AnticOut =
case node_succ(Node) of
[] ->
?SETS:new();
_ ->
AllExpr
end,
enter_node(Node#node{up_expr = UEExpr,
killed_expr = KilledExpr,
antic_out = AnticOut}, NodesAcc)
end, nodes_all_expr_set(AllExpr, Nodes), nodes_tree_values(Nodes)).
%%-----------------------------------------------------------------------------
%% Calculate the upwards exposed expressions for a node.
calc_up_exposed_expr(VarMap, Node) ->
%% debug_struct("UpExpr label: ", node_label(Node)),
NonStructDefs = node_non_struct_defs(Node),
{_, ExprIdSet} =
lists:foldl(fun(Instr, {NotToUseAcc, ExprIdAcc}) ->
Defs = hipe_icode:defines(Instr),
Uses = hipe_icode:uses(Instr),
IsNotToUse =
lists:any(fun(Use) -> gb_sets:is_member(Use, NotToUseAcc) end, Uses),
case IsNotToUse of
false ->
NewExprIdAcc =
lists:foldl(fun(Def, Acc) ->
#varinfo{exprid = Id} = gb_trees:get(Def, VarMap),
?SETS:add_element(Id, Acc) end, ExprIdAcc, Defs),
{NotToUseAcc, NewExprIdAcc};
true ->
NewNotToUse =
gb_sets:union(gb_sets:from_list(Defs), NotToUseAcc),
{NewNotToUse, ExprIdAcc}
end
end, {NonStructDefs, ?SETS:new()}, node_code(Node)),
ExprIdSet.
%%-----------------------------------------------------------------------------
%% Calculate killed expression for node
calc_killed_expr(Node, Maps) ->
calc_killed_expr_defs(node_non_struct_defs_list(Node), ?SETS:new(), Maps).
calc_killed_expr_defs(Defs, UseSet, Maps) ->
lists:foldl(fun(Def, Acc) ->
case maps_var_lookup(Def, Maps) of
none ->
Acc;
{value, #varinfo{use = Use}} ->
?SETS:union(Acc, calc_killed_expr_use(Use, Maps))
end
end, UseSet, Defs).
calc_killed_expr_use(ExprIds, Maps) ->
?SETS:fold(fun(Id, Acc) ->
Expr = maps_expr_get(Id, Maps),
?SETS:union(Acc, calc_killed_expr_use(expr_use(Expr), Maps))
end, ExprIds, ExprIds).
%%-----------------------------------------------------------------------------
%% Calculate the anticipated in and anticipated out sets for each node
calc_anticipated(NodesIn) ->
calc_anticipated_rec(NodesIn, nodes_postorder(NodesIn)).
calc_anticipated_rec(NodesIn, []) -> NodesIn;
calc_anticipated_rec(NodesIn, WorkIn) ->
{NodesOut, WorkOut} =
lists:foldl(fun(Label, {NodesAcc, WorkAcc}) ->
Node = get_node(Label, NodesAcc),
%debug_struct("~nNode Label: ", Label),
AnticIn = ?SETS:union(node_up_expr(Node),
?SETS:subtract(node_antic_out(Node), node_killed_expr(Node))),
%debug_struct("AnticIn: ", AnticIn),
case (node_antic_in(Node) =:= AnticIn) of
false ->
NewNodes1 = enter_node(Node#node{antic_in = AnticIn}, NodesAcc),
Preds = node_pred(Node),
%debug_struct("Preds: ", Preds),
NewNodes2 =
lists:foldl(fun(Label2, NodesAcc2) ->
PredNode = get_node(Label2, NodesAcc2),
AnticOut = ?SETS:intersection(AnticIn, node_antic_out(PredNode)),
%debug_struct("Pred Node Label: ", Label2),
%debug_struct("Pred AnticOut: ", AnticOut),
enter_node(PredNode#node{antic_out = AnticOut}, NodesAcc2)
end, NewNodes1, Preds),
NewWork = add_work_list(Preds, WorkAcc),
%debug_struct("New Work: ", NewWork),
{NewNodes2, NewWork};
true ->
{NodesAcc, WorkAcc}
end
end, {NodesIn, new_work()}, WorkIn),
calc_anticipated_rec(NodesOut, get_work_list(WorkOut)).
%%-----------------------------------------------------------------------------
%% Function that adds inserts to expressions from nodes which either
%% have an upwards exposed expression or dominate more than one node
%% that inserts the same expression or the node is a prior to loop
%% node. The inserted info is stored in the #expr records in the expr
%% map of the #maps structure.
calc_inserts(NodesIn, MapsIn) ->
DomTree = nodes_domtree(NodesIn),
lists:foldl(fun(Label, {NodesAcc, MapsAcc}) ->
Node = get_node(Label, NodesAcc),
%% get some basic properties.
UpExpr = node_up_expr(Node),
AnticOut = node_antic_out(Node),
SubIns = node_sub_inserts(Node),
%% debug_struct("Label: ", Label),
{HasIns, NewMapsAcc} =
?SETS:fold(fun(ExprId, {HasInsAcc, MapsAcc2}) ->
Expr = maps_expr_get(ExprId, MapsAcc2),
ExprIns = expr_inserts(Expr),
ExprSubIns = ?SETS:intersection(ExprIns, SubIns),
%% There are three cases when to insert an expression
%% 1. The expression is defined at least twice in the subtree of this
%% node, that is length(ExprSubIns) > 1.
%% 2. It is defined in the node and is upwards exposed.
%% 3. The node is a block just above a loop, so we should move
%% all anticipated expressions to the node.
case length(ExprSubIns) > 1 orelse ?SETS:is_element(ExprId, UpExpr)
orelse node_pre_loop(Node) of
true ->
%% get labels of all sub blocks that inserts the expression and
%% that are dominated by the current node.
Dominates =
?SETS:filter(fun(SubLabel) ->
hipe_dominators:domTree_dominates(Label, SubLabel, DomTree)
end, ExprSubIns),
%% remove inserts labels from insert labelset.
NewIns = ?SETS:subtract(ExprIns, Dominates),
NewIns2 = ?SETS:add_element(Label, NewIns),
%% update the node.
NewMaps =
maps_expr_enter(expr_inserts_set(Expr, NewIns2), MapsAcc2),
{true, NewMaps};
false ->
{HasInsAcc, MapsAcc2}
end
end, {false, MapsAcc}, ?SETS:union(AnticOut, UpExpr)),
%% Check if there was an insert into this node,
%% and if so add to the sub inserts set.
NewSubIns =
case HasIns of
true ->
?SETS:add_element(Label, SubIns);
false ->
SubIns
end,
%% update sub inserts for all predecessors to the node.
NewNodes2 =
lists:foldl(fun(PredLabel, NodesAcc2) ->
PredNode = get_node(PredLabel, NodesAcc2),
enter_node(node_union_sub_inserts(PredNode, NewSubIns), NodesAcc2)
end, NodesAcc, node_pred(Node)),
{NewNodes2, NewMapsAcc}
end, {NodesIn, MapsIn}, nodes_postorder(NodesIn)).
%%-----------------------------------------------------------------------------
%% Update the insert sets of each node in the node tree.
%% That is, move the insert information from the expressions to
%% the actual nodes that perform the inserts.
update_nodes_inserts(Nodes, Maps) ->
lists:foldl(fun(Expr, NodesAcc) ->
ExprId = expr_id(Expr),
?SETS:fold(fun(Label, NsAcc) ->
Nd = get_node(Label, NsAcc),
enter_node(node_add_insert(Nd, ExprId), NsAcc)
end, NodesAcc, expr_inserts(Expr))
end, Nodes, maps_expr_values(Maps)).
%%-----------------------------------------------------------------------------
%% Rewrite CFG functions
%%-----------------------------------------------------------------------------
%% Do the code updating from the info in the nodes and maps structures. This
%% is a proxy function for rewrite_cfg/6
rewrite_cfg(CFG, Nodes, Maps) ->
{NewCFG, _Visited} =
rewrite_cfg(CFG, ?SETS:new(), #update{}, Nodes, Maps, [nodes_start_label(Nodes)]),
%% debug_struct("Visited: ", _Visited),
NewCFG.
%%-----------------------------------------------------------------------------
%% rewrite_cfg
%% traverse the CFG in reverse postorder and rewrite each basic block before
%% rewriteing its children. Pass along to each BB update the mappings of
%% inserted expressions in the Update record.
rewrite_cfg(CFG, Visited, Update, Nodes, Maps, Labels) ->
lists:foldl(fun(Label, {CFGAcc, VisitedAcc}) ->
case ?SETS:is_element(Label, VisitedAcc) of
false ->
%% debug_struct("Visit: ", Label),
Node = get_node(Label, Nodes),
NewVisitedAcc = ?SETS:add_element(Label, VisitedAcc),
{NewCFGAcc, NewUpdate} = rewrite_bb(CFGAcc, Update, Maps, Node),
%% debug_struct("Update inserted: ", update_inserted_list(NewUpdate)),
rewrite_cfg(NewCFGAcc, NewVisitedAcc, NewUpdate, Nodes, Maps, node_succ(Node));
true ->
{CFGAcc, VisitedAcc}
end
end, {CFG, Visited}, Labels).
%%-----------------------------------------------------------------------------
%% rewrite one single basic block in the CFG as described by the properties
%% in the Node for that block. Uses the Maps and Update info to lookup
%% the instructions and expressions to insert or delete.
rewrite_bb(CFG, Update, Maps, Node) ->
#node{pre_loop = PreLoop, label = Label, up_expr = UpExpr, inserts = Inserts} = Node,
Code = hipe_bb:code(hipe_icode_cfg:bb(CFG, Label)),
%debug_struct("RW Label: ", Label),
%debug_struct("Inserts", Inserts),
DelRed = update_del_red_test(Update),
Delete = ?SETS:subtract(UpExpr, Inserts),
%% local function that gets the instruction and defines list of an
%% expression id in the current node and and returns them.
GetInstrFunc = fun(Expr) ->
Instr = expr_key(Expr),
Defs = expr_defs(Expr),
NewInstr =
if
PreLoop ->
replace_call_variables(fun(Var) ->
node_varmap_lookup(Node,
Var)
end,
Instr);
true ->
Instr
end,
{NewInstr, Defs}
end,
%% go through all expressions defined by the node and replace
%% or remove them as indicated by the delete set. Also perform
%% reduction test replacement if neccessary.
{[CodeLast | CodeRest], NewUpdate, LocalAcc} =
lists:foldl(fun(Instr, {CodeAcc, UpdateAcc, LocalAcc}) ->
case struct_instr_type(Instr) of
struct ->
Defs = hipe_icode:defines(Instr),
#varinfo{exprid = ExprId} = maps_var_get(hd(Defs), Maps),
Expr = maps_expr_get(ExprId, Maps),
DirectReplace = expr_direct_replace(Expr),
%% Creates move intstructions from Vars to Defs
RemoveFuncVars = fun(Vars) ->
CodeAcc2 = mk_defs_moves(CodeAcc, Defs, Vars),
{CodeAcc2, UpdateAcc, LocalAcc} end,
%% Looks up an already inserted ExprId and makes moves
%% of variables from that expression to this expression.
RemoveFunc = fun() ->
{value, Vars} = update_inserted_lookup(UpdateAcc, ExprId),
RemoveFuncVars(Vars) end,
%% Is ExprId already inserted?
IsLocal = ?SETS:is_element(ExprId, LocalAcc),
case DirectReplace of
true ->
%% The Instr is reffering to an expression that is
%% defined as an identical already present instruction,
%% and can be removed directly.
RemoveFuncVars(expr_defs(Expr));
false when IsLocal ->
%% The instruction has already been inserted.
RemoveFunc();
_ ->
case ?SETS:is_element(ExprId, Delete) of
true ->
%% should not be inserted
RemoveFunc();
_ ->
%% Should remain
UpdateAcc2 = update_inserted_add(UpdateAcc, ExprId, Defs),
LocalAcc2 = ?SETS:add_element(ExprId, LocalAcc),
{[Instr | CodeAcc], UpdateAcc2, LocalAcc2}
end
end;
redtest when DelRed ->
%% delete reduction test
{CodeAcc, UpdateAcc, LocalAcc};
_ ->
{[Instr | CodeAcc], UpdateAcc, LocalAcc}
end
end, {[], Update, ?SETS:new()}, Code),
%debug_struct("RW Label 2: ", Label),
%% calculate the inserts that are new to this node, that is
%% the expressions that are in Inserts but not in UpExpr,
%% and that have not been added already,
%% that is not present in LocalAcc
NewInserts = ?SETS:subtract(?SETS:subtract(Inserts, UpExpr), LocalAcc),
{NewCodeRest, NewUpdate2} =
?SETS:fold(fun(ExprId, {CodeAcc, UpdateAcc}) ->
Expr = maps_expr_get(ExprId, Maps),
{ExprInstr, Defs} = GetInstrFunc(Expr),
{UpdateAcc2, NewDefs} = update_inserted_add_new(UpdateAcc, ExprId, Defs),
%% check if there exists an identical expression, so that
%% this expression can be replaced directly.
CodeAcc2 =
case expr_direct_replace(Expr) of
false ->
NewInstr = rewrite_expr(UpdateAcc2, ExprInstr, NewDefs),
[NewInstr | CodeAcc];
true ->
mk_defs_moves(CodeAcc, NewDefs, Defs)
end,
{CodeAcc2, UpdateAcc2}
end, {CodeRest, NewUpdate}, NewInserts),
NewCode = lists:reverse([CodeLast | NewCodeRest]),
%% Check if we are to insert new reduction test here...
{NewCode2, NewUpdate3} =
case PreLoop andalso ?SETS:size(Inserts) > 0 andalso not DelRed of
true ->
{[hipe_icode:mk_primop([], redtest, []) | NewCode], update_del_red_test_set(NewUpdate2)};
false ->
{NewCode, NewUpdate2}
end,
NewBB = hipe_bb:mk_bb(NewCode2),
NewCFG = hipe_icode_cfg:bb_add(CFG, Label, NewBB),
{NewCFG, NewUpdate3}.
%%-----------------------------------------------------------------------------
%% Create a new structure instruction from Instr with destination Defs
%% from the insert mapping in Update.
rewrite_expr(Update, Instr, Defs) ->
NewInstr =
replace_call_variables(fun(Ref) ->
case Ref of
{ExprId, Num} when is_integer(ExprId) ->
{value, DefList} = update_inserted_lookup(Update, ExprId),
lists:nth(Num, DefList);
_ -> Ref
end end, Instr),
hipe_icode:call_dstlist_update(NewInstr, Defs).
%%-----------------------------------------------------------------------------
%% Make move instructions from Defs list to all variables in
%% the Refs list and insert into Code.
mk_defs_moves(Code, [], []) -> Code;
mk_defs_moves(Code, [Ref | Refs], [Def | Defs]) ->
mk_defs_moves([hipe_icode:mk_move(Ref, Def) | Code], Refs, Defs).
%%-----------------------------------------------------------------------------
%% Utilities
new_work() ->
{[], gb_sets:new()}.
add_work_list(List, Work) ->
lists:foldl(fun(Label, WorkAcc) ->
add_work_label(Label, WorkAcc) end, Work, List).
add_work_label(Label, {List, Set}) ->
case gb_sets:is_member(Label, Set) of
false ->
{[Label | List], gb_sets:add(Label, Set)};
true ->
{List, Set}
end.
get_work_list({List, _}) ->
lists:reverse(List).
%%-----------------------------------------------------------------------------
%% instr_type
%% gets a tag for the type of instruction that is passed in I
struct_instr_type(I) ->
case I of
#icode_call{type = primop, 'fun' = mktuple} ->
%%debug_count_case(?SR_STRUCT_INSTR_TYPE, #call{type = primop, 'fun' = mktuple}),
struct;
#icode_call{type = primop, 'fun' = cons} ->
%%debug_count_case(?SR_STRUCT_INSTR_TYPE, #call{type = primop, 'fun' = cons}),
struct;
#icode_call{type = primop, 'fun' = redtest} ->
%%debug_count_case(?SR_STRUCT_INSTR_TYPE, #call{type = primop, 'fun' = redtest}),
redtest;
_ ->
%%debug_count_case(?SR_STRUCT_INSTR_TYPE, other),
other
end.
instr_type(I) ->
case I of
%#call{type = primop, dstlist = List} when length(List) >= 1 -> struct;
#icode_call{type = primop, 'fun' = {unsafe_element, Elem}, dstlist = [DstVar], args = [SrcVar]} ->
%%debug_count_case(?SR_INSTR_TYPE, #call{type = primop, 'fun' = {unsafe_element, num}}),
{struct_elems, Elem, DstVar, SrcVar};
#icode_phi{} ->
%%debug_count_case(?SR_INSTR_TYPE,#phi{}),
phi;
#icode_enter{} ->
%%debug_count_case(?SR_INSTR_TYPE,#enter{}),
return;
#icode_return{} ->
%%debug_count_case(?SR_INSTR_TYPE,#return{}),
return;
#icode_call{type = primop, 'fun' = mktuple} ->
%%debug_count_case(?SR_INSTR_TYPE, #call{type = primop, 'fun' = mktuple}),
struct;
#icode_call{type = primop, 'fun' = cons} ->
%%debug_count_case(?SR_INSTR_TYPE, #call{type = primop, 'fun' = cons}),
struct;
#icode_call{type = primop, 'fun' = redtest} ->
%%debug_count_case(?SR_INSTR_TYPE, #call{type = primop, 'fun' = redtest}),
redtest;
#icode_type{test = {tuple, Size}, args = [Var]} ->
%%debug_count_case(?SR_INSTR_TYPE, #type{type = {tuple, size}}),
{struct_type, Size, Var, ?MKTUPLE};
#icode_type{test = cons, args = [Var]} ->
%%debug_count_case(?SR_INSTR_TYPE,#type{type = cons}),
{struct_type, 2, Var, ?CONS};
%#type{type = {atom, Atom}, args = [Var]} -> {atom_type, Atom, Var};
#icode_call{type = primop, 'fun' = unsafe_hd,
dstlist = [DstVar], args = [SrcVar]} ->
%%debug_count_case(?SR_INSTR_TYPE,#call{type = primop, 'fun' = unsafe_hd}),
{struct_elems, 1, DstVar, SrcVar};
#icode_call{type = primop, 'fun' = unsafe_tl,
dstlist = [DstVar], args = [SrcVar]} ->
%%debug_count_case(?SR_INSTR_TYPE, #call{type = primop, 'fun' = unsafe_tl}),
{struct_elems, 2, DstVar, SrcVar};
#icode_switch_tuple_arity{term = Var, cases = Cases} ->
%%debug_count_case(?SR_INSTR_TYPE,#switch_tuple_arity{}),
{tuple_arity, Var, Cases};
_ -> other
end.
%%-----------------------------------------------------------------------------
%% Expression ID counter
init_expr_id() ->
put({struct_reuse, expr_id_count}, 0).
-spec new_expr_id() -> non_neg_integer().
new_expr_id() ->
V = get({struct_reuse, expr_id_count}),
put({struct_reuse, expr_id_count}, V+1),
V.
%%-----------------------------------------------------------------------------
%% Debug and print functions
print_struct(String, Struct) ->
io:format(String),
erlang:display(Struct).
print(String) ->
io:format(String).
-ifdef(DEBUG).
debug_count_case(Type, Case) ->
Cases = get(Type),
NewCases =
case gb_trees:lookup(Case, Cases) of
{value, Value} -> gb_trees:enter(Case, Value + 1, Cases);
none -> gb_trees:insert(Case, 1, Cases)
end,
put(Type, NewCases).
debug_init_case_count(Type) ->
case get(Type) of
undefined -> put(Type, gb_trees:empty());
_ -> ok
end.
debug_print_case_count(Type) ->
Cases = get(Type),
debug_struct("Case type: ", Type),
debug_list("Cases: ", gb_trees:to_list(Cases)).
set_debug_flag(Value) ->
put({struct_reuse, debug}, Value).
get_debug_flag() -> get({struct_reuse, debug}).
debug_function(FuncName, CFG) ->
Linear = hipe_icode_cfg:cfg_to_linear(CFG),
Func = hipe_icode:icode_fun(Linear),
case Func =:= FuncName orelse FuncName =:= nil of
true ->
set_debug_flag(true),
%% debug_struct("Code: ", hipe_icode_cfg:bb(CFG, 15)),
debug_struct("~nFunction name :", Func);
false ->
set_debug_flag(undefined)
end.
debug_cfg_pp(CFG) ->
case get_debug_flag() of
true -> hipe_icode_cfg:pp(CFG);
_ -> none
end.
debug_struct(String, Struct) ->
case get_debug_flag() of
true ->
io:format(String),
erlang:display(Struct);
_ -> none
end.
debug(String) ->
case get_debug_flag() of
true -> io:format(String);
_ -> none
end.
debug_list(String, List) ->
case get_debug_flag() of
true -> print_list(String, List);
_ -> none
end.
print_list(String, List) ->
io:format(String),
io:format("~n"),
print_list_rec(List),
io:format("~n").
print_list_rec([]) -> ok;
print_list_rec([Struct | List]) ->
erlang:display(Struct),
print_list_rec(List).
debug_nodes(Nodes) ->
lists:foreach(fun(Node) -> debug_node(Node) end, nodes_tree_values(Nodes)).
debug_node(Node) ->
case get_debug_flag() of
true ->
print_struct("Node Label: ", Node#node.label),
print_struct("Code: ", Node#node.code),
print_struct("Phi: ", Node#node.phi),
print_struct("PreLoop: ", Node#node.pre_loop),
print_struct("Preds: ", Node#node.pred),
print_struct("Succ: ", Node#node.succ),
print_struct("Up Expr: ", Node#node.up_expr),
print_struct("Kill : ", Node#node.killed_expr),
print_struct("AnticIn: ", Node#node.antic_in),
print_struct("AnticOut: ", Node#node.antic_out),
print_struct("SubInserts: ", Node#node.sub_inserts),
print_struct("Inserts: ", Node#node.inserts),
print_struct("NonStructDefs: ", Node#node.non_struct_defs),
print_struct("Params: ", Node#node.struct_type),
print_struct("Elems: ", Node#node.struct_elems),
io:format("~n");
_ -> none
end.
-endif.