aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/opt/hipe_schedule.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/opt/hipe_schedule.erl')
-rw-r--r--lib/hipe/opt/hipe_schedule.erl1489
1 files changed, 1489 insertions, 0 deletions
diff --git a/lib/hipe/opt/hipe_schedule.erl b/lib/hipe/opt/hipe_schedule.erl
new file mode 100644
index 0000000000..4925b2927b
--- /dev/null
+++ b/lib/hipe/opt/hipe_schedule.erl
@@ -0,0 +1,1489 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-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%
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% INSTRUCTION SCHEDULER
+%%
+%% This is a basic ILP cycle scheduler:
+%% * set cycle = 0
+%% * while ready[cycle] nonempty do
+%% - take x with greatest priority from ready[cycle]
+%% - try to schedule x;
+%% * if scheduling x was possible,
+%% - reserve resources
+%% - add x to schedule and delete x from dag
+%% - update earliest-time for all successor nodes
+%% as max[earliest[y],cycle+latency[x]]
+%% - if some node y now has no predecessors,
+%% add y to ready[earliest[y]]
+%% * if it was impossible, put x in ready[cycle+1]
+%% (= try again)
+%%
+%% We use the following data structures:
+%% 1. all nodes are numbered and indices used as array keys
+%% 2. priority per node can be computed statically or dynamically
+%% * statically: before scheduling, each node gets a priority value
+%% * dynamically: at each cycle, compute priorities for all ready nodes
+%% 3. earliest: earliest cycle of issue, starts at 0
+%% and is updated as predecessors issue
+%% 4. predecessors: number of predecessors (0 = ready to issue)
+%% 5. successors: list of {Latency,NodeID}
+%% 6. ready: an array indexed by cycle-time (integer), where
+%% ready nodes are kept.
+%% 7. resources: a resource representation (ADT) that answers
+%% certain queries, e.g., "can x be scheduled this cycle"
+%% and "reserve resources for x".
+%% 8. schedule: list of scheduled instructions {Instr,Cycle}
+%% in the order of issue
+%% 9. instructions: maps IDs back to instructions
+%%
+%% Inputs:
+%% - a list of {ID,Node} pairs (where ID is a unique key)
+%% - a dependence list {ID0,Latency,ID1}, which is used to
+%% build the DAG.
+%%
+%% Note that there is some leeway in how things are represented
+%% from here.
+%%
+%% MODIFICATIONS:
+%% - Some basic blocks are not worth scheduling (e.g., GC save/restore code)
+%% yet are pretty voluminous. How do we skip them?
+%% - Scheduling should be done at finalization time: when basic block is
+%% linearized and is definitely at Sparc assembly level, THEN reorder
+%% stuff.
+
+-module(hipe_schedule).
+-export([cfg/1, est_cfg/1, delete_node/5]).
+
+-include("../sparc/hipe_sparc.hrl").
+
+%%-define(debug1,true).
+
+-define(debug2(Str,Args),ok).
+%%-define(debug2(Str,Args),io:format(Str,Args)).
+
+-define(debug3(Str,Args),ok).
+%%-define(debug3(Str,Args),io:format(Str,Args)).
+
+-define(debug4(Str,Args),ok).
+%%-define(debug4(Str,Args),io:format(Str,Args)).
+
+-define(debug5(Str,Args),ok).
+%%-define(debug5(Str,Args),io:format(Str,Args)).
+
+-define(debug(Str,Args),ok).
+%%-define(debug(Str,Args),io:format(Str,Args)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cfg
+%% Argument : CFG - the control flow graph
+%% Returns : CFG - A new cfg with scheduled blocks
+%% Description : Takes each basic block and schedules them one by one.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cfg(CFG) ->
+ ?debug3("CFG: ~n~p", [CFG]),
+ update_all( [ {L,
+ hipe_bb:mk_bb(
+ block(L,hipe_bb:code(hipe_sparc_cfg:bb(CFG,L))) )}
+ || L <- hipe_sparc_cfg:labels(CFG) ], CFG).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : update_all
+%% Argument : Blocks - [{Label, Block}] , a list with labels and new code
+%% used for updating the old CFG.
+%% CFG - The old controlflow graph
+%% Returns : An updated controlflow graph.
+%% Description : Just swappes the basic blocks in the CFG to the scheduled one.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+update_all([],CFG) -> CFG;
+update_all([{L,NewB}|Ls],CFG) ->
+ update_all(Ls,hipe_sparc_cfg:bb_add(CFG,L,NewB)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+est_cfg(CFG) ->
+ update_all([ {L, hipe_bb:mk_bb(est_block(hipe_bb:code(hipe_sparc_cfg:bb(CFG,L))))}
+ || L <- hipe_sparc_cfg:labels(CFG) ], CFG).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Provides an estimation of how quickly a block will execute.
+%% This is done by chaining all instructions in sequential order
+%% by 0-cycle dependences (which means they will never be reordered),
+%% then scheduling the mess.
+
+est_block([]) -> [];
+est_block([I]) -> [I];
+est_block(Blk) ->
+ {IxBlk,DAG} = est_deps(Blk),
+ Sch = bb(IxBlk,DAG),
+ separate_block(Sch,IxBlk).
+
+est_deps(Blk) ->
+ IxBlk = indexed_bb(Blk),
+ DAG = deps(IxBlk),
+ {IxBlk, chain_instrs(IxBlk,DAG)}.
+
+chain_instrs([{N,_}|Xs],DAG) ->
+ chain_i(N,Xs,DAG).
+
+chain_i(_,[],DAG) -> DAG;
+chain_i(N,[{M,_}|Xs],DAG) ->
+ NewDAG = dep_arc(N,zero_latency(),M,DAG),
+ chain_i(M,Xs,NewDAG).
+
+zero_latency() -> 0.
+
+lookup_instr([{N,I}|_], N) -> I;
+lookup_instr([_|Xs], N) -> lookup_instr(Xs, N).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : block
+%% Argument : Instrs - [Instr], list of all the instructions in a basic
+%% block.
+%% Returns : A new scheduled block
+%% Description : Schedule a basic block
+%%
+%% Note: does not consider delay slots!
+%% (another argument for using only annulled delay slots?)
+%% * how do we add delay slots? somewhat tricky to
+%% reconcile with the sort of scheduling we consider.
+%% (as-early-as-possible)
+%% => rewrite scheduler into as-late-as-possible?
+%% (=> just reverse the dependence arcs??)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Don't fire up the scheduler if there's no work to do.
+block(_, []) ->
+ [];
+block(_L, [I]) ->
+ case hipe_sparc:is_any_branch(I) of
+ true -> [hipe_sparc:nop_create(), I];
+ false -> [I]
+ end;
+block(_L, Blk) ->
+ IxBlk = indexed_bb(Blk),
+ case IxBlk of
+ [{_N, I}] -> % comments and nops may have been removed.
+ case hipe_sparc:is_any_branch(I) of
+ true -> [hipe_sparc:nop_create(), I];
+ false -> [I]
+ end;
+ _ ->
+ Sch = bb(IxBlk, {DAG, _Preds} = deps(IxBlk)),
+ {NewSch, NewIxBlk} = fill_delays(Sch, IxBlk, DAG),
+ X = finalize_block(NewSch, NewIxBlk),
+ debug1_stuff(Blk, DAG, IxBlk, Sch, X),
+ X
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : fill_delays
+%% Argument : Sch - List of {{cycle, C}, {node, N}} : C = current cycle
+%% N = node index
+%% IxBlk - Indexed block [{N, Instr}]
+%% DAG - Dependence graph
+%% Returns : {NewSch, NewIxBlk} - vector with new schedule and vector
+%% with {N, Instr}
+%% Description : Goes through the schedule from back to front looking for
+%% branches/jumps. If one is found fill_del tries to find
+%% an instr to fill the delayslot.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+fill_delays(Sch, IxBlk, DAG) ->
+ NewIxBlk = hipe_vectors:list_to_vector(IxBlk),
+ %% NewSch = hipe_vectors:list_to_vector(Sch),
+ NewSch = fill_del(length(Sch), hipe_vectors:list_to_vector(Sch),
+ NewIxBlk, DAG),
+ {NewSch, NewIxBlk}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : fill_del
+%% Argument : N - current index in the schedule
+%% Sch - schedule
+%% IxBlk - indexed block
+%% DAG - dependence graph
+%% Returns : Sch - New schedule with possibly a delay instr in the last
+%% position.
+%% Description : If a call/jump is found fill_branch_delay/fill_call_delay
+%% is called to find a delay-filler.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+fill_del(N, Sch, _IxBlk, _DAG) when N < 1 -> Sch;
+fill_del(N, Sch, IxBlk, DAG) ->
+ Index = get_index(Sch, N),
+ ?debug2("Index for ~p: ~p~nInstr: ~p~n",
+ [N, Index, get_instr(IxBlk, Index)]),
+ NewSch =
+ case get_instr(IxBlk, Index) of
+ #call_link{} ->
+ fill_branch_delay(N - 1, N, Sch, IxBlk, DAG);
+ #jmp_link{} ->
+ fill_call_delay(N - 1, N, Sch, IxBlk, DAG);
+ #jmp{} ->
+ fill_call_delay(N - 1, N, Sch, IxBlk, DAG);
+ #b{} ->
+ fill_branch_delay(N - 1, N, Sch, IxBlk, DAG);
+ #br{} ->
+ fill_branch_delay(N - 1, N, Sch, IxBlk, DAG);
+ #goto{} ->
+ fill_branch_delay(N - 1, N, Sch, IxBlk, DAG);
+ _Other ->
+ Sch
+ end,
+ NewSch.
+ %% fill_del(N - 1, NewSch, IxBlk, DAG).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : fill_call_delay
+%% Argument : Cand - index in schedule of delay-candidate
+%% Call - index in schedule of call
+%% Sch - schedule vector: < {{cycle,Ci},{node,Nj}}, ... >
+%% IxBlk - block vector: < {N, Instr1}, {N+1, Instr2} ... >
+%% DAG - dependence graph
+%% Returns : Sch - new updated schedule.
+%% Description : Searches backwards through the schedule trying to find an
+%% instr without conflicts with the Call-instr.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+fill_call_delay(Cand, _Call, Sch, _IxBlk, _DAG) when Cand < 1 -> Sch;
+fill_call_delay(Cand, Call, Sch, IxBlk, DAG) ->
+ CandIndex = get_index(Sch, Cand),
+ CallIndex = get_index(Sch, Call),
+ CandI = get_instr(IxBlk, CandIndex),
+ case move_or_alu(CandI) of
+ true ->
+ case single_depend(CandIndex, CallIndex, DAG) of
+ false -> % Other instrs depends on Cand ...
+ fill_call_delay(Cand - 1, Call, Sch, IxBlk, DAG);
+
+ true ->
+ CallI = get_instr(IxBlk, CallIndex),
+
+ CandDefs = ordsets:from_list(hipe_sparc:defines(CandI)),
+ %% CandUses = ordsets:from_list(hipe_sparc:uses(CandI)),
+ %% CallDefs = ordsets:from_list(hipe_sparc:defines(CallI)),
+ CallUses = ordsets:from_list(hipe_sparc:uses(CallI)),
+
+ Args = case CallI of
+ #jmp_link{} ->
+ ordsets:from_list(
+ hipe_sparc:jmp_link_args(CallI));
+ #jmp{} ->
+ ordsets:from_list(hipe_sparc:jmp_args(CallI));
+ #call_link{} ->
+ ordsets:from_list(
+ hipe_sparc:call_link_args(CallI))
+ end,
+ CallUses2 = ordsets:subtract(CallUses, Args),
+ Conflict = ordsets:intersection(CandDefs, CallUses2),
+ %% io:format("single_depend -> true:~n ~p~n, ~p~n,~p~n",[CandI,CallI,DAG]),
+ %% io:format("Cand = ~p~nCall = ~p~n",[CandI,CallI]),
+ %% io:format("CandDefs = ~p~nCallDefs = ~p~n",[CandDefs,CallDefs]),
+ %% io:format("CandUses = ~p~nCallUses = ~p~n",[CandUses,CallUses]),
+ %% io:format("Args = ~p~nCallUses2 = ~p~n",[Args,CallUses2]),
+ %% io:format("Conflict = ~p~n",[Conflict]),
+
+ case Conflict of
+ [] -> % No conflicts ==> Cand can fill delayslot after Call
+ update_schedule(Cand, Call, Sch);
+ _ -> % Conflict: try with preceeding instrs
+ fill_call_delay(Cand - 1, Call, Sch, IxBlk, DAG)
+ end
+ end;
+ false ->
+ fill_call_delay(Cand - 1, Call, Sch, IxBlk, DAG)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : fill_branch_delay
+%% Argument : Cand - index in schedule of delay-candidate
+%% Branch - index in schedule of branch
+%% Sch - schedule
+%% IxBlk - indexed block
+%% DAG - dependence graph
+%% Returns : Sch - new updated schedule.
+%% Description : Searches backwards through the schedule trying to find an
+%% instr without conflicts with the Branch-instr.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+fill_branch_delay(Cand, _Br, Sch, _IxBlk, _DAG) when Cand < 1 -> Sch;
+fill_branch_delay(Cand, Br, Sch, IxBlk, DAG) ->
+ CandIndex = get_index(Sch, Cand),
+ BrIndex = get_index(Sch, Br),
+ CandI = get_instr(IxBlk, CandIndex),
+ case move_or_alu(CandI) of
+ true ->
+ case single_depend(CandIndex, BrIndex, DAG) of
+ false -> % Other instrs depends on Cand ...
+ fill_branch_delay(Cand - 1, Br, Sch, IxBlk, DAG);
+
+ true ->
+ BrI = get_instr(IxBlk, BrIndex),
+ CandDefs = ordsets:from_list(hipe_sparc:defines(CandI)),
+ %% CandUses = ordsets:from_list(hipe_sparc:uses(CandI)),
+ %% BrDefs = ordsets:from_list(hipe_sparc:defines(BrI)),
+ BrUses = ordsets:from_list(hipe_sparc:uses(BrI)),
+
+ Conflict = ordsets:intersection(CandDefs, BrUses),
+ %% io:format("single_depend -> true: ~p~n, ~p~n,~p~n", [CandI, BrI, DAG]),
+ %% io:format("Cand = ~p~nBr = ~p~n",[CandI,BrI]),
+ %% io:format("CandDefs = ~p~nBrDefs = ~p~n",[CandDefs,BrDefs]),
+ %% io:format("CandUses = ~p~nBrUses = ~p~n",[CandUses,BrUses]),
+ %% io:format("Conflict = ~p~n",[Conflict]);
+
+ case Conflict of
+ [] -> % No conflicts ==>
+ % Cand can fill delayslot after Branch
+ update_schedule(Cand, Br, Sch);
+ _ -> % Conflict: try with preceeding instrs
+ fill_branch_delay(Cand - 1, Br, Sch, IxBlk, DAG)
+ end
+ end;
+ false ->
+ fill_branch_delay(Cand - 1, Br, Sch, IxBlk, DAG)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : update_schedule
+%% Argument : From - the position from where to switch indexes in Sch
+%% To - the position to where to switch indexes in Sch
+%% Sch - schedule
+%% Returns : Sch - an updated schedule
+%% Description : If From is the delay-filler and To is the Call/jump, the
+%% schedule is updated so From gets index To, To gets index
+%% To - 1, and the nodes between From and To gets old_index - 1.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+update_schedule(To, To, Sch) ->
+ {{cycle, C}, {node, _N} = Node} = hipe_vectors:get(Sch, To-1),
+ hipe_vectors:set(Sch, To-1, {{cycle, C+1}, Node});
+update_schedule(From, To, Sch) ->
+ Temp = hipe_vectors:get(Sch, From-1),
+ Sch1 = hipe_vectors:set(Sch, From-1, hipe_vectors:get(Sch, From)),
+ update_schedule(From + 1, To, hipe_vectors:set(Sch1, From, Temp)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : single_depend
+%% Argument : N - Index of the delayslot candidate
+%% M - Index of the node that N possibly has a single
+%% depend to.
+%% DAG - The dependence graph
+%% Returns : true if no other nodes than N os depending on N
+%% Description : Checks that no other nodes than M depends on N and that the
+%% latency between them is zero or 1.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+single_depend(N, M, DAG) ->
+ Deps = hipe_vectors:get(DAG, N-1),
+ single_depend(M, Deps).
+
+single_depend(_N, []) -> true;
+single_depend(N, [{0, N}]) -> true;
+single_depend(N, [{1, N}]) -> true;
+single_depend(_N, [{_Lat, _}|_]) -> false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : get_index
+%% Argument : Sch - schedule
+%% N - index in schedule
+%% Returns : Index - index of the node
+%% Description : Returns the index of the node on position N in the schedule.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+get_index(Sch, N) ->
+ {{cycle, _C}, {node, Index}} = hipe_vectors:get(Sch,N-1),
+ Index.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : get_instr
+%% Argument : IxBlk - indexed block
+%% N - index in block
+%% Returns : Instr
+%% Description : Returns the instr on position N in the indexed block.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+get_instr(IxBlk, N) ->
+ {_, Instr} = hipe_vectors:get(IxBlk, N-1),
+ Instr.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : get_instr
+%% Argument : Sch - schedule
+%% IxBlk - indexed block
+%% N - index in schedule
+%% Returns : Instr
+%% Description : Returns the instr on position N in the schedule.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+get_instr(Sch, IxBlk, N) ->
+ {{cycle, _C}, {node, Index}} = hipe_vectors:get(Sch, N-1),
+ {_, Instr} = hipe_vectors:get(IxBlk, Index-1),
+ Instr.
+
+separate_block(Sch,IxBlk) ->
+ sep_comments([{C,lookup_instr(IxBlk,N)} || {{cycle,C},{node,N}} <- Sch]).
+
+sep_comments([]) -> [];
+sep_comments([{C,I}|Xs]) ->
+ [hipe_sparc:comment_create({cycle,C}), I | sep_comments(Xs,C)].
+
+sep_comments([], _) -> [];
+sep_comments([{C1,I}|Xs], C0) ->
+ if
+ C1 > C0 ->
+ [hipe_sparc:comment_create({cycle,C1}),I|sep_comments(Xs,C1)];
+ true ->
+ [I|sep_comments(Xs, C0)]
+ end.
+
+finalize_block(Sch, IxBlk) ->
+ ?debug5("Sch: ~p~nIxBlk: ~p~n",[Sch,IxBlk]),
+ finalize_block(1, hipe_vectors:size(Sch), 1, Sch, IxBlk, []).
+
+finalize_block(N, End, _C, Sch, IxBlk, _Instrs) when N =:= End - 1 ->
+ NextLast = get_instr(Sch, IxBlk, N),
+ Last = get_instr(Sch, IxBlk, End),
+ ?debug5("NextLast: ~p~nLast: ~p~n",[NextLast,Last]),
+ case hipe_sparc:is_any_branch(Last) of
+ true -> % Couldn't fill delayslot ==> add NOP
+ [NextLast , hipe_sparc:nop_create(), Last];
+ false -> % Last is a delayslot-filler ==> change order...
+ [Last, NextLast]
+ end;
+finalize_block(N, End, C0, Sch, IxBlk, Instrs) ->
+ {{cycle, _C1}, {node, _M}} = hipe_vectors:get(Sch, N-1),
+ Instr = get_instr(Sch, IxBlk, N),
+ ?debug5("Instr: ~p~n~n",[Instr]),
+ [Instr | finalize_block(N + 1, End, C0, Sch, IxBlk, Instrs)].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : bb
+%% Argument : IxBlk - indexed block
+%% DAG - {Dag, Preds} where Dag is dependence graph and
+%% Preds is number of predecessors for each node.
+%% Returns : Sch
+%% Description : Initializes earliest-list, ready-list, priorities, resources
+%% and so on, and calls the cycle_sched which does the scheduling
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+bb(IxBlk,DAG) ->
+ bb(length(IxBlk), IxBlk, DAG).
+
+bb(N,IxBlk,{DAG, Preds}) ->
+ Earliest = init_earliest(N),
+ BigArray = N*10, % "nothing" is this big :-)
+ Ready = hipe_schedule_prio:init_ready(BigArray,Preds),
+ I_res = init_instr_resources(N, IxBlk),
+
+ Prio = hipe_schedule_prio:init_instr_prio(N,DAG),
+ Rsrc = init_resources(BigArray),
+ ?debug4("I_res: ~n~p~nPrio: ~n~p~nRsrc: ~n~p~n", [I_res,Prio,Rsrc]),
+ ?debug('cycle 1~n',[]),
+ Sch = empty_schedule(),
+ cycle_sched(1,Ready,DAG,Preds,Earliest,Rsrc,I_res,Prio,Sch,N,IxBlk).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cycle_sched
+%% Argument : - C is current cycle, 1 or more.
+%% - Ready is an array (Cycle -> [Node])
+%% yielding the collection of nodes ready to be
+%% scheduled in a cycle.
+%% - DAG is an array (Instr -> [{Latency,Instr}])
+%% represents the dependence DAG.
+%% - Preds is an array (Instr -> NumPreds)
+%% counts the number of predecessors
+%% (0 preds = ready to be scheduled).
+%% - Earl is an array (Instr -> EarliestCycle)
+%% holds the earliest cycle an instruction can be scheduled.
+%% - Rsrc is a 'resource ADT' that handles scheduler resource
+%% management checks whether instruction can be scheduled
+%% this cycle without a stall.
+%% - I_res is an array (Instr -> Required_resources)
+%% holds the resources required to schedule an instruction.
+%% - Sch is the representation of the schedule current schedule.
+%% - N is the number of nodes remaining to be scheduled
+%% tells us when to stop the scheduler.
+%% - IxBlk is the indexed block with instrs
+%% Returns : present schedule
+%% Description : Scheduler main loop.
+%% Pick next ready node in priority order for cycle C until
+%% none remain.
+%% * check each node if it can be scheduled w/o stalling
+%% * if so, schedule it
+%% * otherwise, bump the node to the next cycle
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cycle_sched(C,Ready,DAG,Preds,Earl,Rsrc,I_res,Prio,Sch,N,IxBlk) ->
+ case hipe_schedule_prio:next_ready(C,Ready,Prio,IxBlk,DAG,Preds,Earl) of
+% case hipe_schedule_prio:next_ready(C,Ready,Prio,IxBlk) of
+ {next,I,Ready1} ->
+ ?debug('try ~p~n==> ready = ~p~n',[I, Ready1]),
+ case resources_available(C,I,Rsrc,I_res) of
+ {yes,NewRsrc} ->
+ ?debug(' scheduled~n==> Rscrs = ~p~n',[NewRsrc]),
+ NewSch = add_to_schedule(I,C,Sch),
+ {ReadyNs,NewDAG,NewPreds,NewEarl} =
+ delete_node(C,I,DAG,Preds,Earl),
+ ?debug("NewPreds : ~p~n",[Preds]),
+ ?debug(' ReadyNs: ~p~n',[ReadyNs]),
+ NewReady = hipe_schedule_prio:add_ready_nodes(ReadyNs,
+ Ready1),
+ ?debug(' New ready: ~p~n',[NewReady]),
+ cycle_sched(C,NewReady,NewDAG,NewPreds,NewEarl,
+ NewRsrc,I_res,Prio,NewSch,N-1, IxBlk);
+ no ->
+ ?debug(' resource conflict~n',[]),
+ NewReady = hipe_schedule_prio:insert_node(C+1,I,Ready1),
+ cycle_sched(C,NewReady,DAG,Preds,Earl,Rsrc,
+ I_res,Prio,Sch,N,IxBlk)
+ end;
+ none -> % schedule next cycle if some node remains
+ if
+ N > 0 ->
+ ?debug('cycle ~p~n',[C+1]),
+ cycle_sched(C+1,Ready,DAG,Preds,Earl,
+ advance_cycle(Rsrc),
+ I_res,Prio,Sch,N, IxBlk);
+ true ->
+ present_schedule(Sch)
+ end
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : init_earliest
+%% Argument : N - number of instrs
+%% Returns :
+%% Description :
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+init_earliest(N) ->
+ hipe_vectors:new(N,1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Schedule is kept reversed until the end.
+
+-define(present_node(I,Cycle),{{cycle,Cycle},{node,I}}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : empty_schedule
+%% Description : Returns an empty schedule.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+empty_schedule() -> [].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : add_to_schedule
+%% Argument : I - instr
+%% Cycle - cycle when I was placed
+%% Sch - schedule
+%% Description : Adds instr to schedule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+add_to_schedule(I,Cycle,Sch) ->
+ [?present_node(I,Cycle)|Sch].
+
+present_schedule(Sch) -> lists:reverse(Sch).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Interface to resource manager:
+%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : init_resources
+%% Description : Yields a 'big enough' array mapping (Cycle -> Resources);
+%% this array is called Rsrc below.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+init_resources(S) ->
+ hipe_target_machine:init_resources(S).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : init_instr_resources
+%% Argument : Nodes - a list of the instructions
+%% N - is the number of nodes
+%% Description : return a vector (NodeID -> Resource_requirements)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+init_instr_resources(N,Nodes) ->
+ hipe_target_machine:init_instr_resources(N,Nodes).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : resources_available
+%% Argument : Cycle - the current cycle
+%% I - the current instruction (index = NodeID)
+%% Rsrc - a map (Cycle -> Resources)
+%% I_res - maps (NodeID -> Resource_requirements)
+%% Description : returns {yes,NewResTab} | no
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+resources_available(Cycle,I,Rsrc,I_res) ->
+ hipe_target_machine:resources_available(Cycle,I,Rsrc,I_res).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : advance_cycle
+%% Argument : Rsrc - resources
+%% Description : Returns an empty resources-state
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+advance_cycle(Rsrc) ->
+ hipe_target_machine:advance_cycle(Rsrc).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : delete_node
+%% Argument : Cycle - current cycle
+%% I - index of instr
+%% DAG - dependence dag
+%% Preds - array with number of predecessors for nodes
+%% Earl - array with earliest-times for nodes
+%% Returns : {ReadyNs,NewDAG,NewPreds,NewEarl}
+%% Description : Deletes node I and updates earliest times for the rest.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+delete_node(Cycle,I,DAG,Preds,Earl) ->
+ Succ = hipe_vectors:get(DAG,I-1),
+ NewDAG = hipe_vectors:set(DAG,I-1,scheduled), % provides debug 'support'
+ {ReadyNs,NewPreds,NewEarl} = update_earliest(Succ,Cycle,Preds,Earl,[]),
+ ?debug('earliest after ~p: ~p~n',[I,[{Ix+1,V} || {Ix,V} <- hipe_vectors:list(NewEarl)]]),
+ {ReadyNs,NewDAG,NewPreds,NewEarl}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : update_earliest
+%% Argument : Succ - successor list
+%% Cycle - current cycle
+%% Preds - predecessors
+%% Earl - earliest times for nodes
+%% Ready - array with readynodes for cycles
+%% Returns : {Ready,Preds,Earl}
+%% Description : Updates the earliest times for nodes and updates number of
+%% predecessors for nodes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+update_earliest([],_Cycle,Preds,Earl,Ready) ->
+ {Ready,Preds,Earl};
+update_earliest([{Lat,N}|Xs],Cycle,Preds,Earl,Ready) ->
+ Old_earl = hipe_vectors:get(Earl,N-1),
+ New_earl = erlang:max(Old_earl,Cycle+Lat),
+ NewEarl = hipe_vectors:set(Earl,N-1,New_earl),
+ Num_preds = hipe_vectors:get(Preds,N-1),
+ NewPreds = hipe_vectors:set(Preds,N-1,Num_preds-1),
+ if
+ Num_preds =:= 0 ->
+ ?debug('inconsistent DAG~n',[]),
+ exit({update_earliest,N});
+ Num_preds =:= 1 ->
+ NewReady = [{New_earl,N}|Ready],
+ NewPreds2 = hipe_vectors:set(NewPreds,N-1,0),
+ update_earliest(Xs,Cycle,NewPreds2,NewEarl,NewReady);
+ is_integer(Num_preds), Num_preds > 1 ->
+ update_earliest(Xs,Cycle,NewPreds,NewEarl,Ready)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Collect instruction dependences.
+%%
+%% Three forms:
+%% - data/register
+%% * insert RAW, WAR, WAW dependences
+%% - memory
+%% * stores serialize memory references
+%% * alias analysis may allow loads to bypass stores
+%% - control
+%% * unsafe operations are 'trapped' between branches
+%% * branches are ordered
+%%
+%% returns { [{Index,Instr}], DepDAG }
+%% DepDAG is defined below.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : deps
+%% Argument : BB - Basic block
+%% Returns : {IxBB,DAG} - indexed block and dependence graph. DAG consists
+%% of both Dag and Preds, where Preds is number
+%% of predecessors for nodes.
+%% Description : Collect instruction dependences.
+%%
+%% Three forms:
+%% - data/register
+%% * insert RAW, WAR, WAW dependences
+%% - memory
+%% * stores serialize memory references
+%% * alias analysis may allow loads to bypass stores
+%% - control
+%% * unsafe operations are 'trapped' between branches
+%% * branches are ordered
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+deps(IxBB) ->
+ N = length(IxBB),
+ DAG = empty_dag(N), % The DAG contains both dependence-arcs and
+ % number of predeccessors...
+ {_DepTab,DAG1} = dd(IxBB, DAG),
+ DAG2 = md(IxBB, DAG1),
+ cd(IxBB, DAG2).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : empty_dag
+%% Argument : N - number of nodes
+%% Returns : empty DAG
+%% Description : DAG consists of dependence graph and predeccessors
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+empty_dag(N) ->
+ {hipe_vectors:new(N, []), hipe_vectors:new(N, 0)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : indexed_bb
+%% Argument : BB - basic block
+%% Returns : [{N, Instr}]
+%% Description : Puts indexes to all instrs of a block, removes comments.
+%% NOP's are also removed because if both sparc_schedule and
+%% sparc_post_schedule options are used, the first pass will
+%% add nop's before the branch if necessary, and these are
+%% removed before scheduling the second pass.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+indexed_bb(BB) ->
+ indexed_bb(BB,1).
+
+indexed_bb([],_N) -> [];
+indexed_bb([X|Xs],N) ->
+ case X of
+ #comment{} ->
+ indexed_bb(Xs,N);
+ #nop{} ->
+ indexed_bb(Xs,N);
+ _Other ->
+ [{N,X}|indexed_bb(Xs,N+1)]
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : dep_arc
+%% Argument : N - Current node
+%% Lat - Latency from current node to M
+%% M - The dependent node
+%% DAG - The dependence graph. Consists of both DAG and
+%% predeccessors
+%% Returns : A new DAG with the arc added and number of predeccessors for
+%% M increased.
+%% Description : Adds a new arc to the graph, if an older arc goes from N to M
+%% it will be replaced with a new arc {max(OldLat, NewLat), M}.
+%% Number of predeccessors for node M is increased.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dep_arc(N, Lat, M, {Dag,Preds}) ->
+ OldDeps = hipe_vectors:get(Dag, N-1),
+ %% io:format("{OldDeps} = {~p}~n",[OldDeps]),
+ {NewDeps, Status} = add_arc(Lat, M, OldDeps),
+ %% io:format("{NewDeps, Status} = {~p, ~p}~n",[NewDeps, Status]),
+ NewDag = hipe_vectors:set(Dag, N-1, NewDeps),
+ NewPreds = case Status of
+ added -> % just increase preds if new arc was added
+ OldPreds = hipe_vectors:get(Preds, M-1),
+ hipe_vectors:set(Preds, M-1, OldPreds + 1);
+ non_added ->
+ Preds
+ end,
+ {NewDag, NewPreds}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : add_arc
+%% Argument : Lat - The latency from current node to To.
+%% To - The instr-id of the node which the dependence goes to
+%% Arcs - The dependecies that are already in the dep-graph
+%% Returns : A dependence graph sorted by To.
+%% Description : A new arc that is added is sorted in the right place, and if
+%% there is already an arc between nodes A and B, the one with
+%% the greatest latency is choosen.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+add_arc(Lat,To, []) -> {[{Lat, To}], added};
+add_arc(Lat1, To, [{Lat2, To} | Arcs]) ->
+ {[{erlang:max(Lat1, Lat2), To} | Arcs], non_added};
+add_arc(Lat1,To1, [{Lat2, To2} | Arcs]) when To1 < To2 ->
+ {[{Lat1, To1}, {Lat2, To2} | Arcs], added};
+add_arc(Lat1 ,To1, [{Lat2, To2} | Arcs]) ->
+ {Arcs1, Status} = add_arc(Lat1, To1, Arcs),
+ {[{Lat2, To2} | Arcs1], Status}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% The register/data dependence DAG of a block is represented
+%% as a mapping (Variable -> {NextWriter,NextReaders})
+%% where NextWriter is a pair {Ix,Type}
+%% and NextReaders is a list of pairs {Ix,Type}.
+%%
+%% Type is used to determine latencies of operations; on the UltraSparc,
+%% latencies of arcs (n -> m) are determined by both n and m. (E.g., if
+%% n is an integer op and m is a store, then latency is 0; if m is an
+%% integer op, it's 1.)
+
+dd([],DAG) -> { empty_deptab(), DAG };
+dd([{N,I}|Is],DAG0) ->
+ {DepTab,DAG1} = dd(Is,DAG0),
+ add_deps(N,I,DepTab,DAG1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : add_deps
+%% Argument : N - current node
+%% Instr - current instr
+%% DepTab - hashtable with {next-writer, next-readers} for reg
+%% DAG - dependence graph
+%% Returns : {DepTab, BlockInfo, DAG} - with new values
+%% Description : Adds dependencies for node N to the graph. The registers that
+%% node N defines and uses are used for computing the
+%% dependencies to the following nodes.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+add_deps(N,Instr,DepTab,DAG) ->
+ {Ds,Us} = def_use(Instr),
+ Type = dd_type(Instr),
+ {DepTab1,DAG1} = add_write_deps(Ds,N,Type,DepTab,DAG),
+ add_read_deps(Us,N,Type,DepTab1,DAG1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Instructions are classified into symbolic categories,
+%% which are subsequently used to determine operation latencies
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dd_type(Instr) ->
+ case Instr of
+ #b{} -> branch;
+ %% #br{} -> branch;
+ #call_link{} -> branch;
+ #jmp_link{} -> branch;
+ #jmp{} -> branch;
+ #goto{} -> branch;
+ #load{} -> load;
+ #store{} -> store;
+ #alu{} -> alu;
+ #move{} -> alu;
+ #multimove{} ->
+ Src = hipe_sparc:multimove_src(Instr),
+ Lat = round(length(Src)/2),
+ {mmove,Lat};
+ #sethi{} -> alu;
+ #alu_cc{} -> alu_cc;
+ %% #cmov_cc{} -> cmov_cc;
+ %% #cmov_r{} -> alu;
+ #load_atom{} -> alu;
+ #load_address{} -> alu;
+ #pseudo_enter{} -> pseudo;
+ #pseudo_pop{} -> pseudo;
+ #pseudo_return{} -> pseudo;
+ #pseudo_spill{} -> pseudo;
+ #pseudo_unspill{} -> pseudo
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : add_write_deps
+%% Argument : Defs - registers that node N defines.
+%% N - current node
+%% Ty - the type of current instr
+%% DepTab - Dependence-table
+%% DAG - The dependence graph.
+%% Returns : {DepTab,DAG} - with new values
+%% Description : Adds dependencies to the graph for nodes that depends on the
+%% registers that N defines.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+add_write_deps([],_N,_Ty,DepTab,DAG) -> {DepTab,DAG};
+add_write_deps([D|Ds],N,Ty,DepTab,DAG) ->
+ {NewDepTab,NewDAG} = add_write_dep(D,N,Ty,DepTab,DAG),
+ add_write_deps(Ds,N,Ty,NewDepTab,NewDAG).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : add_write_dep
+%% Description : Updates the dependence table with N as next writer, and
+%% updates the DAG with the dependencies from N to subsequent
+%% nodes.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+add_write_dep(X,N,Ty,DepTab,DAG) ->
+ {NxtWriter,NxtReaders} = lookup(X,DepTab),
+ NewDepTab = writer(X,N,Ty,DepTab),
+ NewDAG = write_deps(N,Ty,NxtWriter,NxtReaders,DAG),
+ {NewDepTab, NewDAG}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : write_deps
+%% Argument : Instr - Current instr
+%% Ty - Type of current instr
+%% NxtWriter - The node that is the next writer of the ragister
+%% that Instr defines.
+%% NxtReaders - The nodes that are subsequent readers of the
+%% register that N defines.
+%% DAG - The dependence graph
+%% Returns : Calls raw_deps that finally returns a new DAG with the new
+%% dependence arcs added.
+%% Description : If a next writer exists a dependence arc for this node is
+%% added, and after this raw_deps is called to compute the
+%% arcs for read-after-write dependencies.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+write_deps(Instr,Ty,NxtWriter,NxtReaders,DAG) ->
+ DAG1 = case NxtWriter of
+ none ->
+ DAG;
+ {Instr,_} ->
+ DAG;
+ {Wr,WrTy} ->
+ dep_arc(Instr,
+ hipe_target_machine:waw_latency(Ty,WrTy),
+ Wr, DAG)
+ end,
+ raw_deps(Instr,Ty,NxtReaders,DAG1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : raw_deps
+%% Argument : Instr - current instr
+%% Type - type of instr
+%% Readers - subsequent readers
+%% DAG - dependence graph
+%% Returns : DAG - A new DAG with read-after-write dependencies added
+%% Description : Updates the DAG with the dependence-arcs from Instr to the
+%% subsequent readers, with the appropriate latencies.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+raw_deps(_Instr,_Type,[],DAG) -> DAG;
+raw_deps(Instr,Ty,[{Rd,RdTy}|Xs],DAG) ->
+ raw_deps(Instr,Ty,Xs,
+ dep_arc(Instr,hipe_target_machine:raw_latency(Ty,RdTy),
+ Rd,DAG)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : add_read_deps
+%% Argument : Uses - The registers that node N uses.
+%% N - Index of the current node.
+%% Ty - Type of current node.
+%% DepTab - Dependence table
+%% DAG - Dependence graph
+%% Returns : {DepTab, DAG} - with updated values.
+%% Description : Adds the read dependencies from node N to subsequent ones,
+%% according to the registers that N uses.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+add_read_deps([],_N,_Ty,DepTab,DAG) -> {DepTab,DAG};
+add_read_deps([U|Us],N,Ty,DepTab,DAG) ->
+ {NewDepTab,NewDAG} = add_read_dep(U,N,Ty,DepTab,DAG),
+ add_read_deps(Us,N,Ty,NewDepTab,NewDAG).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : add_read_dep
+%% Argument : X - Used register
+%% N - Index of checked instr
+%% Ty - Type of checked instr
+%% DepTab - Hashtable with {next-writer, next-readers}
+%% DAG - Dependence graph
+%% Returns : {DepTab, DAG} - with updated values
+%% Description : Looks up what the next-writer/next-readers are, and adjusts
+%% the table with current node as new reader. Finally
+%% read-dependencies are added to the DAG.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+add_read_dep(X,N,Ty,DepTab,DAG) ->
+ {NxtWriter,_NxtReaders} = lookup(X,DepTab),
+ NewDepTab = reader(X,N,Ty,DepTab),
+ NewDAG = read_deps(N,Ty,NxtWriter,DAG),
+ {NewDepTab, NewDAG}.
+
+% If NxtWriter is 'none', then this var is not written subsequently
+% Add WAR from Instr to NxtWriter (if it exists)
+% *** UNFINISHED ***
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : read_deps
+%% Argument : N - Index of current node
+%% Ty - Type of current node
+%% Writer - tuple {NextWriter, WrType} where NextWriter is the
+%% subsequent instr that writes this register next time,
+%% and WrType is the type of that instr.
+%% DAG - The dependence graph
+%% Returns : DAG
+%% Description : Returns a new DAG if a next-writer exists, otherwise the old
+%% DAG is returned.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+read_deps(_Instr,_Ty,none,DAG) ->
+ DAG;
+read_deps(_Instr,_Ty,{_Instr,_},DAG) ->
+ DAG;
+read_deps(Instr,Ty,{NxtWr,NxtWrTy},DAG) ->
+ dep_arc(Instr,hipe_target_machine:war_latency(Ty,NxtWrTy),NxtWr,
+ DAG).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : empty_deptab
+%% Description : Creates an empty dependence table (hash-table)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+empty_deptab() ->
+ gb_trees:empty().
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : lookup
+%% Argument : X - key (register)
+%% DepTab - dependence table
+%% Returns : {NextWriter, NextReaders}
+%% Description : Returns next writer and a list of following readers on
+%% register X.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+lookup(X, DepTab) ->
+ case gb_trees:lookup(X, DepTab) of
+ none ->
+ {none, []};
+ {value, {W, Rs} = Val} ->
+ Val
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : writer
+%% Argument : X - key (register)
+%% N - index of writer
+%% Ty - type of writer
+%% DepTab - dependence table to be updated
+%% Returns : DepTab - new dependence table
+%% Description : Sets N tobe next writer on X
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+writer(X, N, Ty, DepTab) ->
+ gb_trees:enter(X, {{N, Ty}, []}, DepTab).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : reader
+%% Argument : X - key (register)
+%% N - index of reader
+%% Ty - type of reader
+%% DepTab - dependence table to be updated
+%% Returns : DepTab - new dependence table
+%% Description : Adds N to the dependence table as a reader.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+reader(X,N,Ty,DepTab) ->
+ {W,Rs} = lookup(X,DepTab),
+ gb_trees:enter(X,{W,[{N,Ty}|Rs]},DepTab).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% The following version of md/2 separates heap- and stack operations,
+%% which allows for greater reordering.
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : md
+%% Argument : IxBB - indexed block
+%% DAG - dependence graph
+%% Returns : DAG - new dependence graph
+%% Description : Adds arcs for load/store dependencies to the DAG.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+md(IxBB, DAG) ->
+ md(IxBB,empty_md_state(),DAG).
+
+md([],_,DAG) -> DAG;
+md([{N,I}|Is],St,DAG) ->
+ case md_type(I) of
+ other ->
+ md(Is,St,DAG);
+ {st,T} ->
+ { WAW_nodes, WAR_nodes, NewSt } = st_overlap(N,T,St),
+ md(Is,NewSt,
+ md_war_deps(WAR_nodes,N,md_waw_deps(WAW_nodes,N,DAG)));
+ {ld,T} ->
+ { RAW_nodes, NewSt } = ld_overlap(N,T,St),
+ md(Is,NewSt,
+ md_raw_deps(RAW_nodes,N,DAG))
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : md_war_deps
+%% Argument : WAR_nodes - write-after-read nodes depending on N
+%% N - index of current instr
+%% DAG - dependence graph
+%% Returns : DAG - updated DAG
+%% Description : Adds arcs for write-after-read dependencies for N
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+md_war_deps([],_,DAG) -> DAG;
+md_war_deps([M|Ms],N,DAG) ->
+ md_war_deps(Ms,N,dep_arc(M,hipe_target_machine:m_war_latency(),N,DAG)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : md_waw_deps
+%% Argument : WAW_nodes - write-after-write nodes depending on N
+%% N - index of current instr
+%% DAG - dependence graph
+%% Returns : DAG - updated DAG
+%% Description : Adds arcs for write-after-write dependencies for N
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+md_waw_deps([],_,DAG) -> DAG;
+md_waw_deps([M|Ms],N,DAG) ->
+ md_waw_deps(Ms,N,dep_arc(M,hipe_target_machine:m_waw_latency(),N,DAG)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : md_raw_deps
+%% Argument : RAW_nodes - read-after-write nodes depending on N
+%% N - index of current instr
+%% DAG - dependence graph
+%% Returns : DAG - updated DAG
+%% Description : Adds arcs for read-after-write dependencies for N
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+md_raw_deps([],_,DAG) -> DAG;
+md_raw_deps([M|Ms],N,DAG) ->
+ md_raw_deps(Ms,N,dep_arc(M,hipe_target_machine:m_raw_latency(),N,DAG)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : empty_md_state
+%% Description : Returns an empty memorydependence state, eg. 4 lists
+%% representing {StackStores, HeapStores, StackLoads, HeapLoads}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+empty_md_state() -> {[], [], [], []}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : md_type
+%% Argument : I - instr
+%% Description : Maps the instr-type to a simplified type, telling if it's
+%% store/load resp. heap or stack.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+md_type(I) ->
+ case I of
+ #load{} ->
+ Sp = hipe_sparc_registers:stack_pointer(),
+ Src = hipe_sparc:load_src(I),
+ N = hipe_sparc:reg_nr(Src),
+ Off = hipe_sparc:load_off(I),
+ if
+ N =:= Sp -> % operation on stack
+ {ld,{sp,Off}};
+ true ->
+ {ld,{hp,Src,Off}}
+ end;
+ #store{} ->
+ Sp = hipe_sparc_registers:stack_pointer(),
+ Dst = hipe_sparc:store_dest(I),
+ N = hipe_sparc:reg_nr(Dst),
+ Off = hipe_sparc:store_off(I),
+ if
+ N =:= Sp ->
+ {st,{sp,Off}};
+ true ->
+ {st,{hp,Dst,Off}}
+ end;
+ _ ->
+ other
+ end.
+
+%% Given a memory operation and a 'memory op state',
+%% overlap(N,MemOp,State) returns { Preceding_Dependent_Ops, NewState }.
+%% which are either a tuple { WAW_deps, WAR_deps } or a list RAW_deps.
+%%
+%% NOTES:
+%% Note that Erlang's semantics ("heap stores never overwrite existing data")
+%% means we can be quite free in reordering stores to the heap.
+%% Ld/St to the stack are simply handled by their offsets; since we do not
+%% rename the stack pointer, this is sufficient.
+%% *** We assume all memory ops have uniform size = 4 ***
+%%
+%% NOTES:
+%% The method mentioned above has now been changed because the assumption that
+%% "heap stores never overwrite existing data" caused a bug when the
+%% process-pointer was treated the same way as the heap. We were also told
+%% that the semantics can possibly change in the future, so it would be more
+%% safe to treat the heap store/loads as the stack.
+%% A future improvement can be to do an alias analysis to give more freedom
+%% in reordering stuff...
+%%
+%% Alias state:
+%% { [StackOp], [HeapOp], [StackOp], [HeapOp] }
+%% where StackOp = {InstrID, Offset}
+%% HeapOp = {InstrID, Reg, Offset}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : st_overlap
+%% Argument : N - Index of current node
+%% Type - {sp,Off} or {hp,Dst,Off}, store on stack or heap
+%% State - { [StackStrs], [HeapStrs], [StackLds], [HeapLds] }
+%% where StackStrs/StackLds = {InstrID, Offset}
+%% and HeapStrs/HeapLds = {InstrID, Reg, Offset}
+%% Returns : { DepStrs, DepLds, State } -
+%% where DepStrs/DepLds = [NodeId]
+%% and State is the new state
+%% Description : Adds dependencies for overlapping stores.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+st_overlap(N, {sp, Off}, {St_Sp, St_Hp, Ld_Sp, Ld_Hp}) ->
+ {DepSt, IndepSt_Sp} = st_sp_dep(St_Sp, Off),
+ {DepLd, IndepLd_Sp} = ld_sp_dep(Ld_Sp, Off),
+ {DepSt, DepLd, {[{N, Off}|IndepSt_Sp], St_Hp, IndepLd_Sp, Ld_Hp}};
+st_overlap(N, {hp, Dst, Off}, {St_Sp, St_Hp, Ld_Sp, Ld_Hp}) ->
+ DstOff = {Dst, Off},
+ {DepSt,_IndepSt_Hp} = st_hp_dep(St_Hp, DstOff),
+ {DepLd, IndepLd_Hp} = ld_hp_dep(Ld_Hp, DstOff),
+ {DepSt, DepLd, {St_Sp, [{N, Dst, Off}|St_Hp], Ld_Sp, IndepLd_Hp}}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : ld_overlap
+%% Argument : N - Index of current node
+%% Type - {sp,Off} or {hp,Dst,Off}, store on stack or heap
+%% State - { [StackStrs], [HeapStrs], [StackLds], [HeapLds] }
+%% where StackStrs/StackLds = {InstrID, Offset}
+%% and HeapStrs/HeapLds = {InstrID, Reg, Offset}
+%% Returns : { DepStrs, State }
+%% Description : Adds dependencies for overlapping laods
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ld_overlap(N, {sp, Off}, {St_Sp, St_Hp, Ld_Sp, Ld_Hp}) ->
+ DepSt = sp_dep_only(St_Sp, Off),
+ {DepSt, {St_Sp, St_Hp, [{N, Off}|Ld_Sp], Ld_Hp}};
+ld_overlap(N, {hp, Src, Off}, {St_Sp, St_Hp, Ld_Sp, Ld_Hp}) ->
+ DepSt = hp_dep_only(St_Hp, Src, Off),
+ {DepSt, {St_Sp, St_Hp, Ld_Sp, [{N, Src, Off}|Ld_Hp]}}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : st_sp_dep
+%% Description : Adds dependencies that are depending on a stack store
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+st_sp_dep(Stores, Off) ->
+ sp_dep(Stores, Off, [], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : ld_sp_dep
+%% Description : Adds dependencies that are depending on a stack load
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ld_sp_dep(Loads, Off) ->
+ sp_dep(Loads, Off, [], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : st_hp_dep
+%% Description : Adds dependencies that are depending on a heap store
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+st_hp_dep(Stores, {_Reg, _Off} = RegOff) ->
+ hp_dep(Stores, RegOff, [], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : ld_hp_dep
+%% Description : Adds dependencies that are depending on a heap load
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ld_hp_dep(Loads, {_Reg, _Off} = RegOff) ->
+ hp_dep(Loads, RegOff, [], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : sp_dep
+%% Description : Returns {Dependent, Independent} which are lists of nodes
+%% that depends or not on a stack load/store
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+sp_dep([], _Off, Dep, Indep) -> {Dep, Indep};
+sp_dep([{N,Off}|Xs], Off, Dep, Indep) ->
+ sp_dep(Xs, Off, [N|Dep], Indep);
+sp_dep([X|Xs], Off, Dep, Indep) ->
+ sp_dep(Xs, Off, Dep, [X|Indep]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : hp_dep
+%% Description : Returns {Dependent, Independent} which are lists of nodes
+%% that depends or not on a heap load/store
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+hp_dep([], {_Reg,_Off}, Dep, Indep) -> {Dep,Indep};
+hp_dep([{N,Reg,Off1}|Xs], {Reg,Off}, Dep, Indep) when Off1 =/= Off ->
+ hp_dep(Xs, {Reg,Off}, Dep, [{N,Reg,Off1}|Indep]);
+hp_dep([{N,_,_}|Xs], {Reg,Off}, Dep, Indep) ->
+ hp_dep(Xs, {Reg,Off}, [N|Dep], Indep).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : sp_dep_only
+%% Description : Returns a list of nodes that are depending on a stack store
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+sp_dep_only(Stores, Off) ->
+ [N || {N,Off0} <- Stores, Off =:= Off0].
+
+%% Dependences from heap stores to heap loads.
+%% *** UNFINISHED ***
+%% - but works
+%% This is somewhat subtle:
+%% - a heap load can only bypass a heap store if we KNOW it won't
+%% load the stored value
+%% - unfortunately, we do not know the relationships between registers
+%% at this point, so we can't say that store(p+4) is independent of
+%% load(q+0).
+%% (OR CAN WE? A bit closer reasoning might show that it's possible?)
+%% - We can ONLY say that st(p+c) and ld(p+c') are independent when c /= c'
+%%
+%% (As said before, it might be possible to lighten this restriction?)
+
+hp_dep_only([], _Reg, _Off) -> [];
+hp_dep_only([{_N,Reg,Off_1}|Xs], Reg, Off) when Off_1 =/= Off ->
+ hp_dep_only(Xs, Reg, Off);
+hp_dep_only([{N,_,_}|Xs], Reg, Off) ->
+ [N|hp_dep_only(Xs, Reg, Off)].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Control dependences:
+%% - add dependences so that
+%% * branches are performed in order
+%% * unsafe operations are 'fenced in' by surrounding branches
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cd
+%% Argument : IxBB - indexed block
+%% DAG - dependence graph
+%% Returns : DAG - new dependence graph
+%% Description : Adds conditional dependencies to the DAG
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cd(IxBB,DAG) ->
+ cd(IxBB, DAG, none, [], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cd
+%% Argument : IxBB - indexed block
+%% DAG - dependence graph
+%% PrevBr - previous branch
+%% PrevUnsafe - previous unsafe instr (mem-op)
+%% PrevOthers - previous other instrs, used to "fix" preceeding
+%% instrs so they don't bypass a branch.
+%% Returns : DAG - new dependence graph
+%% Description : Adds conditional dependencies to the graph.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cd([], DAG, _PrevBr, _PrevUnsafe, _PrevOthers) ->
+ DAG;
+cd([{N,I}|Xs], DAG, PrevBr, PrevUnsafe, PrevOthers) ->
+ case cd_type(I) of
+ {branch,Ty} ->
+ DAG1 = cd_branch_to_other_deps(N, PrevOthers, DAG),
+ NewDAG = cd_branch_deps(PrevBr, PrevUnsafe, N, Ty, DAG1),
+ cd(Xs,NewDAG,{N,Ty},[],[]);
+ {unsafe,Ty} ->
+ NewDAG = cd_unsafe_deps(PrevBr,N,Ty,DAG),
+ cd(Xs, NewDAG, PrevBr, [{N,Ty}|PrevUnsafe], PrevOthers);
+ {other,_Ty} ->
+ cd(Xs, DAG, PrevBr, PrevUnsafe, [N|PrevOthers])
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cd_branch_to_other_deps
+%% Argument : N - index of branch
+%% Ms - list of indexes of "others" preceeding instrs
+%% DAG - dependence graph
+%% Returns : DAG - new graph
+%% Description : Makes preceeding instrs fixed so they don't bypass a branch
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cd_branch_to_other_deps(_, [], DAG) ->
+ DAG;
+cd_branch_to_other_deps(N, [M | Ms], DAG) ->
+ cd_branch_to_other_deps(N, Ms, dep_arc(M, zero_latency(), N, DAG)).
+
+%% Is the operation a branch, an unspeculable op or something else?
+
+%% Returns
+%% {branch,BranchType}
+%% {unsafe,OpType}
+%% {other,OpType}
+
+%% *** UNFINISHED ***
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cd_type
+%% Argument : I - instr
+%% Description : Maps instrs to a simpler type.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cd_type(I) ->
+ case I of
+ #goto{} ->
+ {branch,uncond};
+ #br{} ->
+ {branch,'cond'};
+ #b{} ->
+ {branch,'cond'};
+ #call_link{} ->
+ {branch,call};
+ #jmp_link{} ->
+ {branch,call};
+ #jmp{} ->
+ {branch,call};
+ #load{} ->
+ {unsafe,load};
+ #store{} ->
+ {unsafe,load};
+ T ->
+ {other,T}
+ end.
+
+%% add dependences to keep order of branches + unspeculable ops:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cd_branch_deps
+%% Argument : PrevBr - preceeding branch
+%% PrevUnsafe - preceeding unsafe ops, eg, mem-ops
+%% N - current id.
+%% Ty - type of current instr
+%% DAG - dependence graph
+%% Returns : DAG - new DAG
+%% Description : Adds arcs between branches and calls deps_to_unsafe that adds
+%% arcs between branches and unsafe ops.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cd_branch_deps(PrevBr, PrevUnsafe, N, Ty, DAG) ->
+ DAG1 = case PrevBr of
+ none ->
+ DAG;
+ {Br,BrTy} ->
+ dep_arc(Br,
+ hipe_target_machine:br_br_latency(BrTy,Ty),
+ N, DAG)
+ end,
+ deps_to_unsafe(PrevUnsafe, N, Ty, DAG1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : deps_to_unsafe
+%% Description : Adds dependencies between unsafe's and branches
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+deps_to_unsafe([], _, _, DAG) -> DAG;
+deps_to_unsafe([{M,UTy}|Us], N, Ty, DAG) ->
+ deps_to_unsafe(Us,N,Ty,
+ dep_arc(M, hipe_target_machine:unsafe_to_br_latency(UTy,Ty),
+ N, DAG)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : cd_unsafe_deps
+%% Description : Adds dependencies between branches and unsafe's
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+cd_unsafe_deps(none, _, _, DAG) ->
+ DAG;
+cd_unsafe_deps({Br,BrTy}, N, Ty, DAG) ->
+ dep_arc(Br, hipe_target_machine:br_to_unsafe_latency(BrTy, Ty), N, DAG).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : def_use
+%% Argument : Instr
+%% Description : Returns the registers that Instr defines resp. uses as 2 lists
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+def_use(Instr) ->
+ {hipe_sparc:defines(Instr), hipe_sparc:uses(Instr)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Function : move_or_alu
+%% Description : True if the instruction is a move or an alu; false otherwise
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+move_or_alu(#move{}) -> true;
+move_or_alu(#alu{}) -> true;
+move_or_alu(_) -> false.
+
+%% Debugging stuff below %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-ifdef(debug1).
+debug1_stuff(Blk, DAG, IxBlk, Sch, X) ->
+ io:format("Blk: ~p~n",[Blk]),
+ io:format("DAG: ~n~p~n~p",[DAG,IxBlk]),
+ io:format("~n"),
+ print_instrs(IxBlk),
+ print_sch(Sch, IxBlk),
+ print_instrs2(X).
+
+print_instrs([]) ->
+ io:format("~n");
+print_instrs([{N,Instr} | Instrs]) ->
+ io:format("(~p): ",[N]),
+ hipe_sparc_pp:pp_instr(Instr),
+ io:format("~p~n",[element(1,Instr)]),
+ print_instrs(Instrs).
+
+print_instrs2([]) ->
+ io:format("~n");
+print_instrs2([Instr | Instrs]) ->
+ hipe_sparc_pp:pp_instr(Instr),
+ print_instrs2(Instrs).
+
+print_sch([],_) -> io:format("~n");
+print_sch([{{cycle,Cycle},{node,I}} | Rest], IxBlk) ->
+ io:format("{C~p, N~p} ",[Cycle,I]),
+ print_node(I, IxBlk),
+ print_sch(Rest, IxBlk).
+
+print_node(_, []) ->
+ io:format("~n");
+print_node(I, [{I, Instr} | _]) ->
+ hipe_sparc_pp:pp_instr(Instr);
+print_node(I, [_ | IxBlk]) ->
+ print_node(I, IxBlk).
+-else.
+debug1_stuff(_Blk, _DAG, _IxBlk, _Sch, _X) ->
+ ok.
+-endif.