%% -*- coding: utf-8; erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%% 
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% %CopyrightEnd%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% File        : hipe_rtl_ssapre.erl
%% Author      : He Bingwen and Frédéric Haziza
%% Description : Performs Partial Redundancy Elimination on SSA form.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc
%%
%% This module implements the <a href="http://cs.wheaton.edu/%7Etvandrun/writings/spessapre.pdf">Anticipation-SSAPRE algorithm</a>,
%% with several modifications for Partial Redundancy Elimination on SSA form.
%% We actually found problems in this algorithm, so
%% we implement another version with several advantages:
%% - No loop for Xsi insertions
%% - No fix point iteration for the downsafety part
%% - Less computations for Will Be Available part
%% - Complexity of the overall algorithm is improved
%%
%% We were supposed to publish these results anyway :D
%%
%% @end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-module(hipe_rtl_ssapre).

-export([rtl_ssapre/2]).

-include("../main/hipe.hrl").
-include("hipe_rtl.hrl").

%%-define(SSAPRE_DEBUG, true        ). %% When uncommented, produces debug printouts
-define(        SETS, ordsets       ). %% Which set implementation module to use
-define(         CFG, hipe_rtl_cfg  ).
-define(         RTL, hipe_rtl      ).
-define(          BB, hipe_bb       ).
-define(        ARCH, hipe_rtl_arch ).
-define(       GRAPH, digraph       ).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Debugging stuff
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-ifndef(SSAPRE_DEBUG).
-define(pp_debug(_Str, _Args), ok).
-else.
-define(pp_debug(Str, Args), io:format(standard_io, Str, Args)).
-endif.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Records / Structures
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-record(xsi_link, {num}). %% Number is the index of the temporary (a Key into the Xsi Tree)
-record(temp, {key, var}).
-record(bottom, {key, var}).
-record(xsi, {inst,   %% Associated instruction
              def,    %% Hypothetical temporary variable
	              %% that stores the result of the computation
              label,  %% Block Label where the xsi is inserted
              opList, %% List of operands
              cba,    %%
              later,  %%
              wba
             }).

-record(pre_candidate, {alu, def}).
-record(xsi_op, {pred, op}).

-record(mp, {xsis, maps, preds, defs, uses, ndsSet}).
-record(block, {type, attributes}).

-record(eop, {expr, var, stopped_by}).
-record(insertion, {code, from}).

-record(const_expr, {var, value}).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Main function
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

rtl_ssapre(RtlSSACfg, Options) ->
  %% io:format("\n################ Original CFG ################\n"),
  %% hipe_rtl_cfg:pp(RtlSSACfg),
  %% io:format("\n\n############ SSA-Form CHECK ==> ~w\n",[hipe_rtl_ssa:check(RtlSSACfg)]),

  {CFG2,XsiGraph,CFGGraph,MPs} = perform_Xsi_insertion(RtlSSACfg,Options),
  %%?pp_debug("~n~n################ Xsi CFG ################\n",[]),pp_cfg(CFG2,XsiGraph),
  XsiList = ?GRAPH:vertices(XsiGraph),
  case XsiList of
    [] ->
      %% No Xsi
      ?option_time(?pp_debug("~n~n################ No Xsi Inserted ################~n",[]),"RTL A-SSAPRE No Xsi inserted (skip Downsafety and Will Be Available)",Options),
      ok;
    _ ->
      ?pp_debug("~n############ Downsafety ##########~n",[]),
      ?option_time(perform_downsafety(MPs,CFGGraph,XsiGraph),"RTL A-SSAPRE Downsafety",Options),
      ?pp_debug("~n~n################ CFG Graph ################~n",[]),pp_cfggraph(CFGGraph),
      ?pp_debug("~n############ Will Be Available ##########~n",[]),
      ?option_time(perform_will_be_available(XsiGraph,CFGGraph,Options),"RTL A-SSAPRE WillBeAvailable",Options)
  end,
  
  ?pp_debug("~n############ No more need for the CFG Graph....Deleting...",[]),?GRAPH:delete(CFGGraph),
  ?pp_debug("~n~n################ Xsi Graph ################~n",[]),pp_xsigraph(XsiGraph),
  
  ?pp_debug("~n############ Code Motion ##########~n",[]),
  Labels = ?CFG:preorder(CFG2),

  ?pp_debug("~n~n################ Xsi CFG ################~n",[]),pp_cfg(CFG2,XsiGraph),

  init_redundancy_count(),
  ?option_time(FinalCFG=perform_code_motion(Labels,CFG2,XsiGraph),"RTL A-SSAPRE Code Motion",Options),
  
  ?pp_debug("\n############ No more need for the Xsi Graph....Deleting...",[]),?GRAPH:delete(XsiGraph),

  %% io:format("\n################ Final CFG ################\n"),
  %% hipe_rtl_cfg:pp(FinalCFG),
  %% io:format("\n\n############ SSA-Form CHECK ==> ~w\n",
  %%           [hipe_rtl_ssa:check(FinalCFG)]),
  ?pp_debug("\nSSAPRE : ~w redundancies were found\n",[get_redundancy_count()]),

  FinalCFG.

%% ##########################################################################
%% ######################## XSI INSERTION ###################################
%% ##########################################################################

perform_Xsi_insertion(Cfg, Options) ->
  init_counters(), %% Init counters for Bottoms and Temps
  DigraphOpts = [cyclic, private],
  XsiGraph = digraph:new(DigraphOpts),
  %% Be carefull, the digraph component is NOT garbage collected,
  %% so don't create 20 millions of instances!
  %% finds the longest depth
  %% Depth-first, preorder traversal over Basic Blocks.
  %%Labels = ?CFG:reverse_postorder(Cfg), 
  Labels = ?CFG:preorder(Cfg), 

  ?pp_debug("~n~n############# Finding definitions for computation~n~n",[]),
  ?option_time({Cfg2,XsiGraph} = find_definition_for_computations(Labels,Cfg,XsiGraph),"RTL A-SSAPRE Xsi Insertion, searching from instructions",Options),

  %% Active List creation
  GeneratorXsiList = lists:sort(?GRAPH:vertices(XsiGraph)),
  ?pp_debug("~n~n############# Inserted Xsis ~w",[GeneratorXsiList]),
  ?pp_debug("~n~n############# Finding operands~n",[]),
  ?option_time({Cfg3,XsiGraph} = find_operands(Cfg2,XsiGraph,GeneratorXsiList,0),"RTL A-SSAPRE Xsi Insertion, finding operands",Options),

  %% Creating the CFGGraph
  ?pp_debug("~n~n############# Creating CFG Graph",[]),
  ?pp_debug("~n############# Labels = ~w",[Labels]),
  CFGGraph = digraph:new(DigraphOpts),
  [StartLabel|Others] = Labels,   % adding the start label as a leaf
  ?pp_debug("~nAdding a vertex for the start label: ~w",[StartLabel]),
  ?GRAPH:add_vertex(CFGGraph, StartLabel, #block{type = top}),
                                                % Doing the others
  ?option_time(MPs=create_cfggraph(Others,Cfg3,CFGGraph,[],[],[],XsiGraph),"RTL A-SSAPRE Xsi Insertion, creating intermediate 'SSAPRE Graph'",Options),

  %% Return the bloody collected information
  {Cfg3,XsiGraph,CFGGraph,MPs}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

find_definition_for_computations([], Cfg, XsiGraph) ->
  {Cfg,XsiGraph}; %% No more block to inspect in the depth-first order
find_definition_for_computations([Label|Rest], Cfg, XsiGraph) ->
  Code = ?BB:code(?CFG:bb(Cfg,Label)),
  {NewCfg,XsiGraph} = find_definition_for_computations_in_block(Label,Code,Cfg,[],XsiGraph),
  find_definition_for_computations(Rest, NewCfg, XsiGraph).

%%===========================================================================
%% Searches from instruction for one block BlockLabel.
%% We process forward over instructions.

find_definition_for_computations_in_block(BlockLabel,[],Cfg,
					  VisitedInstructions,XsiGraph)->
  Code = lists:reverse(VisitedInstructions),
  NewBB = ?BB:mk_bb(Code),
  NewCfg = ?CFG:bb_add(Cfg,BlockLabel,NewBB),
  {NewCfg,XsiGraph}; %% No more instructions to inspect in this block
find_definition_for_computations_in_block(BlockLabel,[Inst|Rest],Cfg,
					  VisitedInstructions,XsiGraph) ->
  %%  ?pp_debug(" Inspecting instruction: ",[]),pp_instr(Inst,nil),
  case Inst of
    #alu{} ->
      %% Is Inst interesting for SSAPRE?
      %% i.e., is Inst an arithmetic operation which doesn't deal with precoloured?
      %% Note that since we parse forward, we have no 'pre_candidate'-type so far.
      case check_definition(Inst,VisitedInstructions,BlockLabel,Cfg,XsiGraph) of
 	{def_found,Def} ->
 	  %% Replacing Inst in Cfg
 	  NewInst = #pre_candidate{alu=Inst,def=Def},
 	  NewVisited = [NewInst|VisitedInstructions],
 	  %% Recurse forward over instructions, same CFG, same XsiGraph
 	  find_definition_for_computations_in_block(BlockLabel,Rest,Cfg,
						    NewVisited,XsiGraph);
 	{merge_point,Xsi} ->
          Def = Xsi#xsi.def,
    	  Key = Def#temp.key,
 	  NewInst = #pre_candidate{alu=Inst,def=Def},
          XsiLink = #xsi_link{num=Key},

          %% Add a vertex to the Xsi Graph
          ?GRAPH:add_vertex(XsiGraph,Key,Xsi),
 	  ?pp_debug(" Inserting Xsi: ",[]),pp_xsi(Xsi),

          Label = Xsi#xsi.label,
          case BlockLabel =:= Label of
            false ->
              %% Insert the Xsi in the appropriate block
              Code = hipe_bb:code(?CFG:bb(Cfg,Label)),
              {BeforeCode,AfterCode} = split_for_xsi(lists:reverse(Code),[]),
              NewCode = BeforeCode++[XsiLink|AfterCode],
              NewBB = hipe_bb:mk_bb(NewCode),
              NewCfg = ?CFG:bb_add(Cfg,Label,NewBB),
              NewVisited = [NewInst|VisitedInstructions];
            _->
              {BeforeCode,AfterCode} = split_for_xsi(VisitedInstructions,[]),
              TempVisited = BeforeCode++[XsiLink|AfterCode],
              TempVisited2 = lists:reverse(TempVisited),
              NewVisited = [NewInst|TempVisited2],
              NewCfg = Cfg
          end,
          find_definition_for_computations_in_block(BlockLabel, Rest, NewCfg,
						    NewVisited, XsiGraph)
      end;
    _ ->
      %%?pp_debug("~n [L~w] Not concerned with: ~w",[BlockLabel,Inst]),
      %% If the instruction is not a SSAPRE candidate, we skip it and keep on
      %% processing instructions
      %% Prepend Inst, so that we have all in reverse order.
      %% Easy to parse backwards
      find_definition_for_computations_in_block(BlockLabel, Rest, Cfg,
						[Inst|VisitedInstructions], XsiGraph)
  end.

%% ############################################################################
%% We have E as an expression, I has an alu (arithmetic operation), and
%% we inspect backwards the previous instructions to find a definition for E.
%% Since we parse in forward order, we know that the previous SSAPRE 
%% instruction will have a definition.

check_definition(E,[],BlockLabel,Cfg,XsiGraph)->
  %% No more instructions in that block
  %% No definition found in that block
  %% Search is previous blocks
  Preds = ?CFG:pred(Cfg, BlockLabel),
  %%  ?pp_debug("~n CHECKING DEFINITION  ####### Is L~w a merge block? It has ~w preds. So far E=",[BlockLabel,length(Preds)]),pp_expr(E),
  case Preds of
    [] ->
      %% Entry Point
      {def_found,bottom};
    [P] ->
      %% One predecessor only, we just keep looking for a definition in that block
      VisitedInstructions = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,P))),
      check_definition(E,VisitedInstructions,P,Cfg,XsiGraph);
    _ ->
      Temp = new_temp(),
      %% It's a merge point
      OpList = [#xsi_op{pred=X} || X<-Preds],
      Xsi = #xsi{inst=E,def=Temp,label=BlockLabel,opList=OpList},
      {merge_point,Xsi} 
  end;
check_definition(E,[CC|Rest],BlockLabel,Cfg,XsiGraph) ->
  SRC1 = ?RTL:alu_src1(E),
  SRC2 = ?RTL:alu_src2(E),
  case CC of
    #alu{} ->
      exit({?MODULE,should_not_be_an_alu,
	    {"Why the hell do we still have an alu???",CC}});
    #pre_candidate{} ->
      %% C is the previous instruction
      C = CC#pre_candidate.alu,
      DST = ?RTL:alu_dst(C),
      case DST =:= SRC1 orelse DST =:= SRC2 of
 	false ->
 	  case check_match(E,C) of
 	    true -> %% It's a computation of E!
 	      %% Get the dst of the alu
 	      {def_found,DST};
 	    _->
 	      check_definition(E,Rest,BlockLabel,Cfg,XsiGraph)
 	  end;
 	true ->
 	  %% Get the definition of C, since C is PRE-candidate AND has been processed before
 	  DEF = CC#pre_candidate.def,
 	  case DEF of
 	    bottom -> 
 	      %% Def(E)=bottom, STOP
 	      {def_found,bottom};
 	    _ -> 
 	      %% Emend E with this def(C)
 	      %%?pp_debug("Parameters are E=~w, DST=~w, DEF=~w",[E,DST,DEF]),
 	      F = emend(E,DST,DEF),
 	      check_definition(F,Rest,BlockLabel,Cfg,XsiGraph) %% Continue the search
 	  end
      end;
    #move{} ->
      %% It's a move, we emend E, and continue the definition search
      DST = ?RTL:move_dst(CC),
      F = case SRC1 =:= DST orelse SRC2 =:= DST of
	    true ->
	      SRC = ?RTL:move_src(CC),
	      emend(E,DST,SRC);
	    _ ->
	      E
	  end,
      check_definition(F,Rest,BlockLabel,Cfg,XsiGraph); %% Continue the search
    #xsi_link{} ->
      {_K,Xsi} = ?GRAPH:vertex(XsiGraph,CC#xsi_link.num),
      C = Xsi#xsi.inst,
      case check_match(C,E) of
 	true -> %% There is a Xsi already with a computation of E!
 	  %% fetch definition of C, and give it to E
 	  {def_found,Xsi#xsi.def};
 	_->
 	  check_definition(E,Rest,BlockLabel,Cfg,XsiGraph)
      end;
    #phi{} -> 
      %% skip them. NOTE: Important to separate this case from the next one
      check_definition(E,Rest,BlockLabel,Cfg,XsiGraph);
    _ ->
      %% Note: the function calls or some other instructions can change the pre-coloured registers
      %% which are able to be redefined. This breaks of course the SSA form.
      %% If there is a redefinition we can give bottom to the computation, and no xsi will be inserted.
      %% (In some sens, the result of the computation is new at that point.)
      PreColouredTest = ?ARCH:is_precoloured(SRC1) orelse ?ARCH:is_precoloured(SRC2),

      %%RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)) orelse ?RTL:is_reg(SRC1) orelse ?RTL:is_reg(SRC2),
      RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)), %% That means we cannot reuse the result held in this register...

      case PreColouredTest orelse RegisterTest of
 	true ->
 	  {def_found,bottom};
 	false ->
          DC = ?RTL:defines(CC),
          case lists:member(SRC1,DC) orelse lists:member(SRC2,DC) of
            true ->
              {def_found,bottom};
            false ->
              %% Orthogonal to E, we continue the search
              check_definition(E,Rest,BlockLabel,Cfg,XsiGraph)
          end
      end
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

check_match(E, C) ->
  OpE = ?RTL:alu_op(E),
  OpC = ?RTL:alu_op(C),
  case OpE =:= OpC of
    false ->
      false;
    true ->
      Src1E = ?RTL:alu_src1(E),
      Src2E = ?RTL:alu_src2(E),
      Src1C = ?RTL:alu_src1(C),
      Src2C = ?RTL:alu_src2(C),
      case Src1E =:= Src1C of
 	true ->
 	  Src2E =:= Src2C;
 	false ->
 	  Src1E =:= Src2C andalso Src2E =:= Src1C
      end
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

expr_is_const(E) ->
  ?RTL:is_imm(?RTL:alu_src1(E)) andalso ?RTL:is_imm(?RTL:alu_src2(E)).
%%  is_number(?RTL:alu_src1(E)) andalso is_number(?RTL:alu_src2(E)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Must be an arithmetic operation, i.e. #alu{}
emend(Expr, S, Var) ->
  SRC1 = ?RTL:alu_src1(Expr),
  NewExpr = case SRC1 =:= S of
	      true  -> ?RTL:alu_src1_update(Expr,Var);
	      false -> Expr
	    end,
  SRC2 = ?RTL:alu_src2(NewExpr),
  case SRC2 =:= S of
    true  -> ?RTL:alu_src2_update(NewExpr,Var);
    false -> NewExpr
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

split_for_xsi([], Acc) ->
  {[], Acc};  %  no_xsi_no_phi_found;
split_for_xsi([I|Is] = Code, Acc) -> %% [I|Is] in backward order, Acc in order
  case I of
    #xsi_link{} ->
      {lists:reverse(Code), Acc};
    #phi{} ->
      {lists:reverse(Code), Acc};
    _ ->
      split_for_xsi(Is, [I|Acc])
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Phase 1.B : Search for operands
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

find_operands(Cfg,XsiGraph,[],_Count) ->
  {Cfg,XsiGraph};
find_operands(Cfg,XsiGraph,ActiveList,Count) ->
  {NewCfg,TempActiveList} = find_operands_for_active_list(Cfg,XsiGraph,ActiveList,[]),
  NewActiveList = lists:reverse(TempActiveList),
  ?pp_debug("~n################ Finding operands (iteration ~w): ~w have been introduced. Now ~w in total~n",
 	   [Count+1, length(NewActiveList), length(?GRAPH:vertices(XsiGraph))]),
  find_operands(NewCfg,XsiGraph,NewActiveList,Count+1).

find_operands_for_active_list(Cfg,_XsiGraph,[],ActiveListAcc) ->
  {Cfg,ActiveListAcc};
find_operands_for_active_list(Cfg,XsiGraph,[K|Ks],ActiveListAcc) ->
  {_Key,Xsi} = ?GRAPH:vertex(XsiGraph,K),
  ?pp_debug("~n Inspecting operands of : ~n",[]),pp_xsi(Xsi),
  Preds = ?CFG:pred(Cfg, Xsi#xsi.label),
  {NewCfg,NewActiveListAcc}=determine_operands(Xsi,Preds,Cfg,K,XsiGraph,ActiveListAcc),
  {_Key2,Xsi2} = ?GRAPH:vertex(XsiGraph,K),
  ?pp_debug("~n ** Final Xsi: ~n",[]),pp_xsi(Xsi2),
  ?pp_debug("~n #####################################################~n",[]),
  find_operands_for_active_list(NewCfg,XsiGraph,Ks,NewActiveListAcc).

determine_operands(_Xsi,[],Cfg,_K,_XsiGraph,ActiveAcc) ->
  %% All operands have been determined.
  %% The CFG is not updated, only the XsiGraph
  {Cfg,ActiveAcc};
determine_operands(Xsi,[P|Ps],Cfg,K,XsiGraph,ActiveAcc) ->
  Label = Xsi#xsi.label,
  ReverseCode = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,Label))),
  VisitedInstructions = get_visited_instructions(Xsi,ReverseCode),
  Res = determine_e_prime(Xsi#xsi.inst,VisitedInstructions,P,XsiGraph),
  case Res of
    operand_is_bottom ->
      NewXsi = xsi_arg_update(Xsi,P,new_bottom()),
      ?GRAPH:add_vertex(XsiGraph,K,NewXsi),
      determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc);
    operand_is_const_expr ->
      NewXsi = xsi_arg_update(Xsi,P,new_bottom()),
      ?GRAPH:add_vertex(XsiGraph,K,NewXsi),
      determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc);
    {sharing_operand,Op} ->
      NewXsi = xsi_arg_update(Xsi,P,Op),
      ?GRAPH:add_vertex(XsiGraph,K,NewXsi),
      determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc);  
    {revised_expression,E_prime} ->
      ?pp_debug(" E' is determined : ",[]),pp_expr(E_prime),
      ?pp_debug(" and going along the edge L~w~n",[P]),
      %% Go along the edge P
      RevCode = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,P))),
      case check_one_operand(E_prime,RevCode,P,Cfg,K,XsiGraph) of
 	{def_found,Def} ->
 	  NewXsi = xsi_arg_update(Xsi,P,Def),
          ?GRAPH:add_vertex(XsiGraph,K,NewXsi),
          determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc);

 	{expr_found,ChildExpr} ->
 	  NewXsi = xsi_arg_update(Xsi,P,ChildExpr),
          ?GRAPH:add_vertex(XsiGraph,K,NewXsi),
          determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc);

 	{expr_is_const, Op} ->
          %% We detected that the expression is of the form: 'N op M'
          %% where N and M are constant.
 	  NewXsi = xsi_arg_update(Xsi,P,Op),
          ?GRAPH:add_vertex(XsiGraph,K,NewXsi),
          determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc);

 	{merge_point,XsiChild} ->
 	  %% Update that Xsi, give its definition as Operand for the
 	  %% search, and go on
          XsiChildDef = XsiChild#xsi.def,
 	  NewXsi = xsi_arg_update(Xsi,P,XsiChildDef),
          ?GRAPH:add_vertex(XsiGraph,K,NewXsi),

 	  KeyChild = XsiChildDef#temp.key,
 	  XsiChildLink = #xsi_link{num=KeyChild},
          ?GRAPH:add_vertex(XsiGraph,KeyChild,XsiChild),

 	  %% Should not be the same block !!!!!!!
 	  RCode = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,XsiChild#xsi.label))),
 	  {BCode,ACode} = split_code_for_xsi(RCode,[]),

 	  NewCode = BCode++[XsiChildLink|ACode],
 	  NewBB = hipe_bb:mk_bb(NewCode),
 	  NewCfg = ?CFG:bb_add(Cfg, XsiChild#xsi.label, NewBB),

 	  ?pp_debug(" -- ",[]),pp_arg(Xsi#xsi.def),?pp_debug(" causes insertion of: ~n",[]),pp_xsi(XsiChild),
          ?pp_debug(" -- Adding an edge ",[]),pp_arg(Xsi#xsi.def),?pp_debug(" -> ",[]),pp_arg(XsiChild#xsi.def),

          %% Adding an edge...
 	  %%?GRAPH:add_edge(XsiGraph,K,KeyChild,"family"),
          ?GRAPH:add_edge(XsiGraph,K,KeyChild),
 	  determine_operands(NewXsi,Ps,NewCfg,K,XsiGraph,[KeyChild|ActiveAcc])
      end
  end.

determine_e_prime(Expr,VisitedInstructions,Pred,XsiGraph) ->
  %% MUST FETCH FROM THE XSI TREE, since Xsis are not updated yet in the CFG
  NewExpr = emend_with_phis(Expr,VisitedInstructions,Pred),
  emend_with_processed_xsis(NewExpr,VisitedInstructions,Pred,XsiGraph).

emend_with_phis(EmendedE, [], _) ->
  EmendedE;
emend_with_phis(E, [I|Rest], Pred) ->
  case I of
    #phi{} ->
      Dst = ?RTL:phi_dst(I),
      UE = ?RTL:uses(E), %% Should we get SRC1 and SRC2 instead?
      case lists:member(Dst, UE) of
  	false ->
  	  emend_with_phis(E, Rest, Pred);
  	true ->
  	  NewE = emend(E, Dst, ?RTL:phi_arg(I,Pred)),
  	  emend_with_phis(NewE, Rest, Pred)
      end;
    _ ->
      emend_with_phis(E, Rest, Pred)
  end.

emend_with_processed_xsis(EmendedE, [], _, _) ->
  {revised_expression,EmendedE};  
emend_with_processed_xsis(E, [I|Rest], Pred, XsiGraph) ->
  case I of
    #xsi_link{} ->
      Key = I#xsi_link.num,
      {_KK,Xsi} = ?GRAPH:vertex(XsiGraph,Key),
      Def = Xsi#xsi.def,
      UE = ?RTL:uses(E), %% Should we get SRC1 and SRC2 instead?
      case lists:member(Def,UE) of
  	false ->
 	  CE = Xsi#xsi.inst,
 	  case check_match(E,CE) of
 	    true -> %% It's a computation of E!
 	      case xsi_arg(Xsi,Pred) of
 		undetermined_operand ->
                  exit({?MODULE,check_operand_sharing,"######## Ôh Dear, we trusted Kostis !!!!!!!!! #############"});
 		XsiOp ->
 		  {sharing_operand,XsiOp} %% They share operands
 	      end;
 	    _->
 	      emend_with_processed_xsis(E,Rest,Pred,XsiGraph)
 	  end;
  	true ->
 	  A = xsi_arg(Xsi,Pred),
 	  %% ?pp_debug(" ######### xsi_arg(I:~w,Pred:~w) = ~w~n",[I,Pred,A]),
 	  case A of
 	    #bottom{} ->
 	      operand_is_bottom;
 	    #const_expr{} ->
 	      operand_is_const_expr;
            #eop{} ->
              NewE = emend(E,Def,A#eop.var),
  	      emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph);
            undetermined_operand ->
              exit({?MODULE,emend_with_processed_xsis,"######## Ôh Dear, we trusted Kostis, again !!!!!!!!! #############"});
 	    XsiOp ->
 	      NewE = emend(E,Def,XsiOp),
 	      emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph)
 	  end
      end;
    _ ->
      emend_with_processed_xsis(E,Rest,Pred,XsiGraph)
  end.

%% get_visited_instructions(Xsi,[]) ->
%%   ?pp_debug("~nWe don't find this xsi with def ",[]),pp_arg(Xsi#xsi.def),?pp_debug(" in L~w : ",[Xsi#xsi.label]),
%%   exit({?MODULE,no_such_xsi_in_block,"We didn't find that Xsi in the block"});
get_visited_instructions(Xsi, [I|Is]) ->
  case I of
    #xsi_link{} ->
      XsiDef = Xsi#xsi.def,
      Key = XsiDef#temp.key,
      case I#xsi_link.num =:= Key of
 	true ->
 	  Is;
 	false ->
 	  get_visited_instructions(Xsi, Is)
      end;
    _ ->
      get_visited_instructions(Xsi, Is)
  end.

split_code_for_xsi([], Acc) ->
  {[],Acc};
split_code_for_xsi([I|Is] = Code, Acc) ->
  case I of
    #xsi_link{} ->
      {lists:reverse(Code), Acc};
    #phi{} ->
      {lists:reverse(Code), Acc};
    _ ->
      split_code_for_xsi(Is, [I|Acc])
  end.

check_one_operand(E, [], BlockLabel, Cfg, XsiKey, XsiGraph) ->
  %% No more instructions in that block
  %% No definition found in that block
  %% Search is previous blocks
  Preds = ?CFG:pred(Cfg, BlockLabel),
  case Preds of
    [] ->
      %% Entry Point
      {def_found,new_bottom()};
    [P] ->
      %% One predecessor only, we just keep looking for a definition in that block
      case expr_is_const(E) of
        true ->
          ?pp_debug("\n\n############## Wow expr is constant: ~w",[E]),
          Var = ?RTL:mk_new_var(),
          Value = eval_expr(E),
          Op = #const_expr{var = Var, value = Value},
          {expr_is_const, Op};
        false ->
          VisitedInstructions = lists:reverse(?BB:code(?CFG:bb(Cfg,P))),
          check_one_operand(E, VisitedInstructions, P, Cfg, XsiKey, XsiGraph)
      end;
    _ ->
      %% It's a merge point
      case expr_is_const(E) of
        true ->
          ?pp_debug("\n\n############## Wow expr is constant at merge point: ~w",[E]),
          Var = ?RTL:mk_new_var(),
          Value = eval_expr(E),
          Op = #const_expr{var = Var, value = Value},
          {expr_is_const, Op};
        false ->
          Temp = new_temp(),
          OpList = [#xsi_op{pred = X} || X <- Preds],
          Xsi = #xsi{inst = E, def = Temp, label = BlockLabel, opList = OpList},
          {merge_point, Xsi}
      end
  end;
check_one_operand(E, [CC|Rest], BlockLabel, Cfg, XsiKey, XsiGraph) ->
  SRC1 = ?RTL:alu_src1(E),
  SRC2 = ?RTL:alu_src2(E),
  %% C is the previous instruction
  case CC of
    #alu{} ->
      exit({?MODULE,should_not_be_an_alu,
	    {"Why the hell do we still have an alu???",CC}});
    #xsi{} ->
      exit({?MODULE,should_not_be_a_xsi,
	    {"Why the hell do we still have a xsi???",CC}});
    #pre_candidate{} ->
      C = CC#pre_candidate.alu,
      DST = ?RTL:alu_dst(C),
      case DST =:= SRC1 orelse DST =:= SRC2 of
 	true ->
 	  %% Get the definition of C, since C is PRE-candidate AND has
 	  %% been processed before
 	  DEF = CC#pre_candidate.def,
 	  case DEF of
 	    bottom -> 
 	      %% Def(E)=bottom, STOP
              %% No update of the XsiGraph
 	      {def_found,new_bottom()};
 	    _->
 	      %% Simply emend
 	      F = emend(E,DST,DEF),
 	      ?pp_debug("~nEmendation : E= ",[]),pp_expr(E),?pp_debug(" ==> E'= ",[]),pp_expr(F),?pp_debug("~n",[]),
 	      check_one_operand(F,Rest,BlockLabel,Cfg,XsiKey,XsiGraph)
 	  end;
 	false ->
 	  case check_match(C,E) of
 	    true -> %% It's a computation of E!
 	      %% It should give DST and not Def
              %% No update of the XsiGraph, cuz we use DST and not Def
              %% The operand is therefore gonna be a real variable
 	      {def_found,DST};
 	    _->
 	      %% Nothing to do with E
 	      check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph)
 	  end
      end;
    #move{} ->
      %% It's a move, we emend E, and continue the definition search
      DST = ?RTL:move_dst(CC),
      case SRC1 =:= DST orelse SRC2 =:= DST of
 	true ->
 	  SRC = ?RTL:move_src(CC),
 	  F = emend(E,DST,SRC),
  	  check_one_operand(F,Rest,BlockLabel,Cfg,XsiKey,XsiGraph); %% Continue the search
  	_ ->
 	  check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph) %% Continue the search
      end;
    #xsi_link{} ->
      Key = CC#xsi_link.num,
      %% Is Key a family member of XsiDef ?
      {_KK,Xsi} = ?GRAPH:vertex(XsiGraph,Key),
      C = Xsi#xsi.inst,
      case check_match(E,C) of
        true -> %% There is a Xsi already with a computation of E!
          %% fetch definition of C, and give it to E
          %% Must update an edge in the XsiGraph, and here, we know it's a Temp
          %% Note: this can create a loop (= a cycle of length 1)
          ?pp_debug(" -- Found a cycle with match: Adding an edge t~w -> t~w",[XsiKey,Key]),
          ?GRAPH:add_edge(XsiGraph,XsiKey,Key),
          {def_found,Xsi#xsi.def};
        _ ->
          case ?GRAPH:get_path(XsiGraph,Key,XsiKey) of 
            false ->
              %% Is it a loop back to itself???
              case Key =:= XsiKey of 
                false ->
                  check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph);
                _ ->
                  {expr_found,#eop{expr=E,var=?RTL:mk_new_var(),stopped_by=Key}}
              end;
            _ ->
              %% Returning the expression instead of looping
              %% And in case of no match
              ExprOp = #eop{expr=E,var=?RTL:mk_new_var(),stopped_by=Key},
              {expr_found,ExprOp}
          end
      end;
    #phi{} -> %% skip them
      check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph);
    _ ->
      PreColouredTest = ?ARCH:is_precoloured(SRC1) orelse ?ARCH:is_precoloured(SRC2),

      %%RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)) orelse ?RTL:is_reg(SRC1) orelse ?RTL:is_reg(SRC2),
      RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)),
      case PreColouredTest orelse RegisterTest of
 	true ->
 	  {def_found,new_bottom()};
 	_->
 	  DC = ?RTL:defines(CC),
 	  case lists:member(SRC1,DC) orelse lists:member(SRC2,DC) of
 	    true ->
 	      {def_found,new_bottom()};
 	    _ ->
 	      %% Orthogonal to E, we continue the search
 	      check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph)
 	  end
      end
  end.

eval_expr(E) ->
  ?pp_debug("~n Evaluating the result of ~w~n", [E]),
  Op1 = ?RTL:alu_src1(E),
  Op2 = ?RTL:alu_src2(E),
  true = ?RTL:is_imm(Op1),
  Val1 = ?RTL:imm_value(Op1),
  true = ?RTL:is_imm(Op2),
  Val2 = ?RTL:imm_value(Op2),    
  {Result, _Sign, _Zero, _Overflow, _Carry} = ?ARCH:eval_alu(?RTL:alu_op(E), Val1, Val2),
  ?pp_debug("~n Result is then ~w~n", [Result]),
  ?RTL:mk_imm(Result).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%  CREATTING CFGGRAPH  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

create_cfggraph([],_Cfg,CFGGraph,ToBeFactorizedAcc,MPAcc,LateEdges,_XsiGraph) ->
  ?pp_debug("~n~n ############# PostProcessing ~n~w~n",[LateEdges]),
  post_process(LateEdges,CFGGraph),
  ?pp_debug("~n~n ############# Factorizing ~n~w~n",[ToBeFactorizedAcc]),
  factorize(ToBeFactorizedAcc,CFGGraph),
  MPAcc;
create_cfggraph([Label|Ls],Cfg,CFGGraph,ToBeFactorizedAcc,MPAcc,LateEdges,XsiGraph) ->
  Preds = ?CFG:pred(Cfg, Label),
  case Preds of
    [] ->
      exit({?MODULE,do_not_call_on_top,{"Why the hell do we call that function on the start label???",Label}});
    [P] ->
      Code = ?BB:code(?CFG:bb(Cfg, Label)),
      Defs = get_defs_in_non_merge_block(Code, []),
      ?pp_debug("~nAdding a vertex for ~w", [Label]),
      Succs = ?CFG:succ(Cfg, Label),
      case Succs of
        [] -> %% Exit point
          ?GRAPH:add_vertex(CFGGraph, Label, #block{type = exit}),
          NewToBeFactorizedAcc = ToBeFactorizedAcc;
        _ -> %% Split point
          ?GRAPH:add_vertex(CFGGraph,Label,#block{type=not_mp,attributes={P,Succs}}),
          NewToBeFactorizedAcc = [Label|ToBeFactorizedAcc]
      end,
      ?pp_debug("~nAdding an edge ~w -> ~w (~w)",[P,Label,Defs]),
      case ?GRAPH:add_edge(CFGGraph,P,Label,Defs) of
        {error,Reason} ->
          exit({?MODULE,forget_that_for_christs_sake_bingwen_please,{"Bad edge",Reason}});
        _ ->
          ok
      end,
      create_cfggraph(Ls,Cfg,CFGGraph,NewToBeFactorizedAcc,MPAcc,LateEdges,XsiGraph);
    _ -> %% Merge point
      Code = ?BB:code(?CFG:bb(Cfg,Label)),
      {Defs,Xsis,Maps,Uses} = get_info_in_merge_block(Code,XsiGraph,[],[],gb_trees:empty(),gb_trees:empty()),
      Attributes = #mp{preds=Preds,xsis=Xsis,defs=Defs,maps=Maps,uses=Uses},
      MergeBlock = #block{type=mp,attributes=Attributes},
      ?pp_debug("~nAdding a vertex for ~w with Defs= ~w",[Label,Defs]),
      ?GRAPH:add_vertex(CFGGraph,Label,MergeBlock),
      %% Add edges
      NewLateEdges = add_edges_for_mp(Preds,Label,LateEdges),
      create_cfggraph(Ls,Cfg,CFGGraph,ToBeFactorizedAcc,[Label|MPAcc],NewLateEdges,XsiGraph)
  end.

get_defs_in_non_merge_block([], Acc) ->
  ?SETS:from_list(Acc);
get_defs_in_non_merge_block([Inst|Rest], Acc) ->
  case Inst of
    #pre_candidate{} ->
      Def = Inst#pre_candidate.def,
      case Def of
        #temp{} ->
          %%        {temp,Key,_Var} ->
          %%          get_defs_in_non_merge_block(Rest,[Key|Acc]);
          get_defs_in_non_merge_block(Rest, [Def#temp.key|Acc]);
 	_-> %% Real variables or bottom
 	  get_defs_in_non_merge_block(Rest, Acc)
      end;
    _  ->
      get_defs_in_non_merge_block(Rest, Acc)
  end.

get_info_in_merge_block([],_XsiGraph,Defs,Xsis,Maps,Uses) ->
  {?SETS:from_list(Defs),Xsis,Maps,Uses}; %% Xsis are in backward order
get_info_in_merge_block([Inst|Rest],XsiGraph,Defs,Xsis,Maps,Uses) ->
  case Inst of
    #pre_candidate{} ->
      Def = Inst#pre_candidate.def,
      case Def of
        #temp{} ->
          get_info_in_merge_block(Rest,XsiGraph,[Def#temp.key|Defs],Xsis,Maps,Uses);
 	_ ->
          get_info_in_merge_block(Rest,XsiGraph,Defs,Xsis,Maps,Uses)
      end;
    #xsi_link{} ->
      Key = Inst#xsi_link.num,
      {_Key,Xsi} = ?GRAPH:vertex(XsiGraph,Key),
      OpList = xsi_oplist(Xsi),
      {NewMaps,NewUses} = add_map_and_uses(OpList,Key,Maps,Uses),
      get_info_in_merge_block(Rest,XsiGraph,Defs,[Key|Xsis],NewMaps,NewUses);
    _  ->
      get_info_in_merge_block(Rest,XsiGraph,Defs,Xsis,Maps,Uses)
  end.

add_edges_for_mp([], _Label, LateEdges) ->
  LateEdges;
add_edges_for_mp([P|Ps], Label, LateEdges) ->
  add_edges_for_mp(Ps,Label,[{P,Label}|LateEdges]).

%% Doesn't do anything so far
add_map_and_uses([], _Key, Maps, Uses) ->
  {Maps,Uses};
add_map_and_uses([XsiOp|Ops], Key, Maps, Uses) ->
  case XsiOp#xsi_op.op of
    #bottom{} ->
      Set = case gb_trees:lookup(XsiOp,Maps) of
              {value, V} ->
                ?SETS:add_element(Key,V);
              none ->
                ?SETS:from_list([Key])
            end,
      NewMaps = gb_trees:enter(XsiOp,Set,Maps),
      NewUses = Uses;
    #temp{} ->
      Set = case gb_trees:lookup(XsiOp,Maps) of
              {value, V} ->
                ?SETS:add_element(Key,V);
              none ->
                ?SETS:from_list([Key])
            end,
      NewMaps = gb_trees:enter(XsiOp,Set,Maps),
      Pred = XsiOp#xsi_op.pred,
      OOP = XsiOp#xsi_op.op,
      SSet = case gb_trees:lookup(Pred,Uses) of
	       {value, VV} ->
		 ?SETS:add_element(OOP#temp.key,VV);
	       none ->
		 ?SETS:from_list([OOP#temp.key])
	     end,
      NewUses = gb_trees:enter(Pred,SSet,Uses);
    #eop{} ->
      Set = case gb_trees:lookup(XsiOp,Maps) of
              {value, V} ->
                ?SETS:add_element(Key,V);
              none ->
                ?SETS:from_list([Key])
            end,
      NewMaps = gb_trees:enter(XsiOp,Set,Maps),
      Pred = XsiOp#xsi_op.pred,
      Op = XsiOp#xsi_op.op,
      SSet = case gb_trees:lookup(Pred,Uses) of
	       {value, VV} ->
		 ?SETS:add_element(Op#eop.stopped_by,VV);
	       none ->
		 ?SETS:from_list([Op#eop.stopped_by])
	     end,
      NewUses = gb_trees:enter(Pred,SSet,Uses);
    _->
      NewMaps = Maps,
      NewUses = Uses
  end,
  add_map_and_uses(Ops, Key, NewMaps, NewUses).

post_process([], _CFGGraph) -> ok;
post_process([E|Es], CFGGraph) ->
  {Pred,Label} = E,
  {_PP,Block} = ?GRAPH:vertex(CFGGraph,Label),
  Att = Block#block.attributes,
  Uses = Att#mp.uses,
  SetToAdd = case gb_trees:lookup(Pred,Uses) of
               {value, Set} ->
                 Set;
               none ->
                 ?SETS:new()
             end,
  %% ?pp_debug("~nAdding an edge ~w -> ~w (~w)",[Pred,Label,SetToAdd]),
  ?GRAPH:add_edge(CFGGraph, Pred, Label, SetToAdd),
  post_process(Es, CFGGraph).

factorize([], _CFGGraph) -> ok;
factorize([P|Ps], CFGGraph) ->
  [OE|OEs] = ?GRAPH:out_edges(CFGGraph,P),
  %% ?pp_debug("~nIn_degrees ~w : ~w",[P,?GRAPH:in_degree(CFGGraph,P)]),
  [InEdge] = ?GRAPH:in_edges(CFGGraph,P),
  {E,V1,V2,Label} = ?GRAPH:edge(CFGGraph,InEdge),
  {_OEE,_OEV1,_OEV2,LOE} = ?GRAPH:edge(CFGGraph,OE),
  List = shoot_info_upwards(OEs,LOE,CFGGraph),
  NewLabel = ?SETS:union(Label,List),
  ?GRAPH:add_edge(CFGGraph,E,V1,V2,NewLabel),
  factorize(Ps, CFGGraph).

shoot_info_upwards([], Acc, _CFGGraph) -> Acc;
shoot_info_upwards([E|Es], Acc, CFGGraph) ->
  {_E,_V1,_V2,Set} = ?GRAPH:edge(CFGGraph,E),
  NewAcc = ?SETS:intersection(Acc, Set),
  case ?SETS:size(NewAcc) of
    0 -> NewAcc;
    _ -> shoot_info_upwards(Es,NewAcc,CFGGraph)
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  DOWNSAFETY   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

perform_downsafety([], _G, _XsiG) ->
  ok;
perform_downsafety([MP|MPs], G, XG) ->
  {V,Block} = ?GRAPH:vertex(G, MP),
  NDS = ?SETS:new(),
  Att = Block#block.attributes,
  Maps = Att#mp.maps,
  Defs = Att#mp.defs,
  OutEdges = ?GRAPH:out_edges(G, MP),
  %% ?pp_debug("~n Inspection Maps : ~w",[Maps]),
  NewNDS = parse_keys(gb_trees:keys(Maps),Maps,OutEdges,G,Defs,NDS,XG),
  NewAtt = Att#mp{ndsSet = NewNDS},
  ?GRAPH:add_vertex(G, V, Block#block{attributes = NewAtt}),
  ?pp_debug("~n Not Downsafe at L~w: ~w", [V, NewNDS]),
  %%io:format(standard_io,"~n Not Downsafe at L~w: ~w",[V,NewNDS]),
  perform_downsafety(MPs, G, XG).

parse_keys([], _Maps, _OutEdges, _G, _Defs, NDS, _XsiG) ->
  NDS;
parse_keys([M|Ms], Maps, OutEdges, G, Defs, NDS, XsiG) ->
  KillerSet = gb_trees:get(M,Maps),
  %% ?pp_debug("~n Inspection ~w -> ~w",[M,KillerSet]),
  TempSet = ?SETS:intersection(KillerSet,Defs),
  NewNDS = case ?SETS:size(TempSet) of
	     0 -> getNDS(M,KillerSet,NDS,OutEdges,G,XsiG);
	     _ ->
	       %% One Xsi which has M as operand has killed it
	       %% M is then Downsafe
	       %% and is not added to the NotDownsafeSet (NDS)
	       NDS
	   end,
  parse_keys(Ms, Maps, OutEdges, G, Defs, NewNDS, XsiG).

getNDS(_M, _KillerSet, NDS, [], _G, _XsiG) ->
  NDS;
getNDS(M, KillerSet, NDS, [E|Es], G, XsiG) ->
  {_EE,_V1,_V2,Label} = ?GRAPH:edge(G, E),
  Set = ?SETS:intersection(KillerSet, Label),
  %% ?pp_debug("~n ######## Intersection between KillerSet: ~w and Label: ~w",[KillerSet,Label]),
  %% ?pp_debug("~n ######## ~w",[Set]),
  case ?SETS:size(Set) of
    0 ->
      %% M is not downsafe
      ?SETS:add_element(M, NDS);
    _ ->
      %% Try the other edges
      getNDS(M, KillerSet, NDS, Es, G, XsiG)
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  WILL BE AVAILABLE  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

perform_will_be_available(XsiGraph,CFGGraph,Options) ->
  Keys = ?GRAPH:vertices(XsiGraph),
  ?pp_debug("~n############ Can Be Available ##########~n",[]),
  ?option_time(perform_can_be_available(Keys,XsiGraph,CFGGraph),"RTL A-SSAPRE WillBeAvailable - Compute CanBeAvailable",Options),
  ?pp_debug("~n############ Later ##########~n",[]),
  ?option_time(perform_later(Keys,XsiGraph),"RTL A-SSAPRE WillBeAvailable - Compute Later",Options).

perform_can_be_available([],_XsiGraph,_CFGGraph) -> ok;
perform_can_be_available([Key|Keys],XsiGraph,CFGGraph) ->
  {V,Xsi} = ?GRAPH:vertex(XsiGraph,Key),
  case Xsi#xsi.cba of
    undefined ->
      {_VV,Block} = ?GRAPH:vertex(CFGGraph,Xsi#xsi.label),
      Att = Block#block.attributes,
      NDS = Att#mp.ndsSet,
      OpList = ?SETS:from_list(xsi_oplist(Xsi)),
      Set = ?SETS:intersection(NDS,OpList),
      case ?SETS:size(Set) of
        0 ->
          ?GRAPH:add_vertex(XsiGraph, V, Xsi#xsi{cba = true}),
          perform_can_be_available(Keys, XsiGraph, CFGGraph);
        _ ->
          LIST = [X || #temp{key=X} <- ?SETS:to_list(Set)],
          case LIST of
            [] ->
              ?GRAPH:add_vertex(XsiGraph, V, Xsi#xsi{cba = false}),
              ImmediateParents = ?GRAPH:in_neighbours(XsiGraph, Key),
              propagate_cba(ImmediateParents,XsiGraph,Xsi#xsi.def,CFGGraph);
            _ ->
              ok
          end,
          perform_can_be_available(Keys, XsiGraph, CFGGraph)
      end;
    _ -> %% True or False => recurse
      perform_can_be_available(Keys, XsiGraph, CFGGraph)
  end.

propagate_cba([],_XG,_Def,_CFGG) -> ok;
propagate_cba([IPX|IPXs],XsiGraph,XsiDef,CFGGraph) ->
  {V,IPXsi} = ?GRAPH:vertex(XsiGraph,IPX),
  {_VV,Block} = ?GRAPH:vertex(CFGGraph,IPXsi#xsi.label),
  Att = Block#block.attributes,
  NDS = Att#mp.ndsSet,
  List = ?SETS:to_list(?SETS:intersection(NDS,?SETS:from_list(xsi_oplist(IPXsi)))),
  case IPXsi#xsi.cba of
    false -> ok;
    _ ->
      case lists:keymember(XsiDef, #xsi_op.op, List) of
        true ->
          ?GRAPH:add_vertex(XsiGraph, V, IPXsi#xsi{cba = false}),
          ImmediateParents = ?GRAPH:in_neighbours(XsiGraph, IPX),
          propagate_cba(ImmediateParents,XsiGraph,IPXsi#xsi.def,CFGGraph);
        _ ->
          ok
      end
  end,
  propagate_cba(IPXs,XsiGraph,XsiDef,CFGGraph).

perform_later([], _XsiGraph) -> ok;
perform_later([Key|Keys], XsiGraph) ->
  {V, Xsi} = ?GRAPH:vertex(XsiGraph, Key),
  %% ?pp_debug("~n DEBUG : inspecting later of ~w (~w)~n",[Key,Xsi#xsi.later]),
  case Xsi#xsi.later of
    undefined ->
      OpList = xsi_oplist(Xsi),
      case parse_ops(OpList,fangpi) of  %% It means "fart" in chinese :D
        has_temp ->
          perform_later(Keys,XsiGraph);
        has_real ->
          case Xsi#xsi.cba of
            true ->
              ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=false,wba=true});
            undefined ->
              ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=false,wba=true});
            _ ->
              ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=false,wba=false})
          end,
          AllParents = digraph_utils:reaching([Key], XsiGraph),
          ?pp_debug("~nPropagating to all parents of t~w: ~w",[Key,AllParents]),
          propagate_later(AllParents,XsiGraph),
          perform_later(Keys,XsiGraph);
        _ -> %% Just contains bottoms and/or expressions
          ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=true}),
          perform_later(Keys,XsiGraph)
      end;
    _ -> %% True or False => recurse
      perform_later(Keys,XsiGraph)
  end.

propagate_later([], _XG) -> ok;
propagate_later([IPX|IPXs], XsiGraph) ->
  {V,IPXsi} = ?GRAPH:vertex(XsiGraph,IPX),
  case IPXsi#xsi.later of
    false ->
      ?pp_debug("~nThrough propagation, later of t~w is already reset",[IPX]),
      propagate_later(IPXs,XsiGraph);
    _ ->
      ?pp_debug("~nThrough propagation, resetting later of t~w",[IPX]),
      case IPXsi#xsi.cba of
        true ->
          ?GRAPH:add_vertex(XsiGraph,V,IPXsi#xsi{later=false,wba=true});
        undefined ->
          ?GRAPH:add_vertex(XsiGraph,V,IPXsi#xsi{later=false,wba=true});
        _ ->
          ?GRAPH:add_vertex(XsiGraph,V,IPXsi#xsi{later=false,wba=false})
      end,
      propagate_later(IPXs,XsiGraph)
  end.

parse_ops([], Res) ->
  Res;
parse_ops([Op|Ops], Res) ->
  case Op#xsi_op.op of
    #temp{} ->
      NewRes = has_temp,
      parse_ops(Ops,NewRes);
    #bottom{} ->
      parse_ops(Ops,Res);
    #eop{} ->
      parse_ops(Ops,Res);
    _ ->
      has_real
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  CODE MOTION  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

perform_code_motion([], Cfg, _XsiG) ->
  Cfg;
perform_code_motion([L|Labels], Cfg, XsiG) ->
  Code=?BB:code(?CFG:bb(Cfg,L)),
  ?pp_debug("~n################  Code Motion in L~w~n",[L]),
  ?pp_debug("~nCode to move ~n",[]),
  pp_instrs(Code,XsiG),
  NewCfg = code_motion_in_block(L,Code,Cfg,XsiG,[],gb_trees:empty()),
  ?pp_debug("~n################  Code Motion successful in L~w~n",[L]),
  perform_code_motion(Labels,NewCfg,XsiG).

code_motion_in_block(Label,[],Cfg,_XsiG,Visited,InsertionsAcc) ->
  InsertionsAlong = gb_trees:keys(InsertionsAcc),
  Code = lists:reverse(Visited),
  NewBB = ?BB:mk_bb(Code),
  Cfg2 = ?CFG:bb_add(Cfg,Label,NewBB),
  %% Must come after the bb_add, since redirect will update the Phis too...
  Cfg3 = make_insertions(Label,InsertionsAlong,InsertionsAcc,Cfg2),
  %% ?pp_debug("~nChecking the Code at L~w:~n~p",[Label,?BB:code(?CFG:bb(Cfg3,Label))]),
  Cfg3;
code_motion_in_block(L,[Inst|Insts],Cfg,XsiG,Visited,InsertionsAcc) ->
  ?pp_debug("~nInspecting Inst : ~n",[]),pp_instr(Inst,XsiG),
  case Inst of
    #pre_candidate{} ->
      Def = Inst#pre_candidate.def,
      Alu = Inst#pre_candidate.alu,
      case Def of
        bottom ->
          InstToAdd = Alu;
        #temp{} ->
          Key = Def#temp.key,
          {_V,Xsi} = ?GRAPH:vertex(XsiG,Key),
          case Xsi#xsi.wba of
            true ->
              %% Turn into a move
              Dst = ?RTL:alu_dst(Alu),
              Move = ?RTL:mk_move(Dst,Def#temp.var),
              pp_instr(Inst#pre_candidate.alu,nil), ?pp_debug(" ==> ",[]), pp_instr(Move,nil),
              %% Counting redundancies
              redundancy_add(),
              InstToAdd = Move;
            _ ->
              InstToAdd = Alu
          end;
        _ -> %% Def is a real variable
          %% Turn into a move
          Dst = ?RTL:alu_dst(Alu),
          Move = ?RTL:mk_move(Dst,Def),
          pp_instr(Alu,nil), ?pp_debug(" ==> ",[]), pp_instr(Move,nil),
          %% Counting redundancies
          redundancy_add(),
          InstToAdd = Move
      end,
      code_motion_in_block(L,Insts,Cfg,XsiG,[InstToAdd|Visited],InsertionsAcc);
    #xsi_link{} ->
      Key = Inst#xsi_link.num,
      {_V,Xsi} = ?GRAPH:vertex(XsiG,Key),      
      case Xsi#xsi.wba of
        true ->
          %% Xsi is a WBA, it might trigger insertions
          OpList = xsi_oplist(Xsi),
          ?pp_debug(" This Xsi is a 'Will be available'",[]),
          %% Cleaning the instruction
          Expr = prepare_inst(Xsi#xsi.inst),
          {NewOpList,NewInsertionsAcc} = get_insertions(OpList,[],InsertionsAcc,Visited,Expr,XsiG),
          %% Making Xsi a Phi with Oplist
          PhiOpList = [{Pred,Var} || #xsi_op{pred=Pred,op=Var} <- NewOpList],
          Def = Xsi#xsi.def,
          Phi = ?RTL:phi_arglist_update(?RTL:mk_phi(Def#temp.var),PhiOpList),
          ?pp_debug("~n Xsi is turned into Phi : ~w",[Phi]),
          code_motion_in_block(L,Insts,Cfg,XsiG,[Phi|Visited],NewInsertionsAcc);
        _ ->
          ?pp_debug(" This Xsi is not a 'Will be available'",[]),
          code_motion_in_block(L,Insts,Cfg,XsiG,Visited,InsertionsAcc)
      end;
%%     phi ->
%%       code_motion_in_block(L,Insts,Cfg,XsiG,[Inst|Visited],InsertionsAcc);
    _ ->
      %% Other instructions.... Phis too
      code_motion_in_block(L,Insts,Cfg,XsiG,[Inst|Visited],InsertionsAcc)
  end.

prepare_inst(Expr) ->
  S1 = ?RTL:alu_src1(Expr),
  S2 = ?RTL:alu_src2(Expr),
  NewInst = case S1 of
	      #temp{} -> ?RTL:alu_src1_update(Expr,S1#temp.var);
	      _ -> Expr
	    end,
  case S2 of
    #temp{} -> ?RTL:alu_src2_update(NewInst,S2#temp.var);
    _ -> NewInst
  end.

get_insertions([],OpAcc,InsertionsAcc,_Visited,_Expr,_XsiG) ->
  {OpAcc,InsertionsAcc};
get_insertions([XsiOp|Ops],OpAcc,InsertionsAcc,Visited,Expr,XsiG) ->
  Pred = XsiOp#xsi_op.pred,
  Op = XsiOp#xsi_op.op,
  case Op of
    #bottom{} ->
      case gb_trees:lookup(Pred,InsertionsAcc) of
        {value,Insertion} ->
          From = Insertion#insertion.from,
          case lists:keyfind(Op, 1, From) of
            false ->
              ?pp_debug("~nThere has been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op),
              Dst = Op#bottom.var,
              Expr2 = ?RTL:alu_dst_update(Expr,Dst),
              Inst = manufacture_computation(Pred,Expr2,Visited),
              Code = Insertion#insertion.code,
              NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]},
              NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc);
            {_, Val} ->
              ?pp_debug("~nThere has been insertions along the edge L~w already, and for that operand too | Op=",[Pred]),pp_arg(Op),
              Dst = Val,
              NewInsertionsAcc = InsertionsAcc
          end;
        none ->
          ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course)| Op=",[Pred]),pp_arg(Op),
          Dst = Op#bottom.var,
          Expr2 = ?RTL:alu_dst_update(Expr,Dst),
          Inst = manufacture_computation(Pred,Expr2,Visited),
          NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]},
          NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc)
      end;
    #const_expr{} ->
      case gb_trees:lookup(Pred,InsertionsAcc) of
        {value,Insertion} ->
          From = Insertion#insertion.from,
          case lists:keyfind(Op, 1, From) of
            false ->
              ?pp_debug("~nThere have been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op),
              Dst = Op#const_expr.var,
              Val = Op#const_expr.value,
              Inst = ?RTL:mk_move(Dst,Val),
              Code = Insertion#insertion.code,
              NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]},
              NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc);
            {_, Val} ->
              ?pp_debug("~nThere have been insertions along the edge L~w already, and for that operand too | Op=",[Pred]),pp_arg(Op),
              Dst = Val,
              NewInsertionsAcc = InsertionsAcc
          end;
        none ->
          ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course)| Op=",[Pred]),pp_arg(Op),
          Dst = Op#const_expr.var,
          Val = Op#const_expr.value,
          Inst = ?RTL:mk_move(Dst,Val),
          NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]},
          NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc)
      end;
    #eop{} ->
      %% We treat expressions like bottoms
      %% The value must be recomputed, and therefore not available...
      case gb_trees:lookup(Pred,InsertionsAcc) of
        {value,Insertion} ->
          From = Insertion#insertion.from,
          case lists:keyfind(Op, 1, From) of
            false ->
              ?pp_debug("~nThere has been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op),
              Dst = Op#eop.var,
              Expr2 = ?RTL:alu_dst_update(Expr,Dst),
              Inst = manufacture_computation(Pred,Expr2,Visited),
              Code = Insertion#insertion.code,
              NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]},
              NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc);
            {_, Val} ->
              ?pp_debug("~nThere has been insertions along the edge L~w already, and for that operand too | Op=",[Pred]),pp_arg(Op),
              Dst = Val,
              NewInsertionsAcc = InsertionsAcc
          end;
        none ->
          ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course)| Op=",[Pred]),pp_arg(Op),
          Dst = Op#eop.var,
          Expr2 = ?RTL:alu_dst_update(Expr,Dst),
          Inst = manufacture_computation(Pred,Expr2,Visited),
          NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]},
          NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc)
      end;
    #temp{} ->
      case gb_trees:lookup(Pred,InsertionsAcc) of
        {value,Insertion} ->
          From = Insertion#insertion.from,
          case lists:keyfind(Op, 1, From) of
            false ->
              ?pp_debug("~nThere has been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op),
              Key = Op#temp.key,
              {_V,Xsi} = ?GRAPH:vertex(XsiG,Key),      
              case Xsi#xsi.wba of
                true ->
                  ?pp_debug("~nBut the operand is a WBA Xsi: no need for insertion",[]),
                  Dst = Op#temp.var,
                  NewInsertionsAcc = InsertionsAcc;
                _ ->
                  ?pp_debug("~nBut the operand is a NOT WBA Xsi: we must make an insertion",[]),
                  Dst = ?RTL:mk_new_var(),
                  Expr2 = ?RTL:alu_dst_update(Expr,Dst),
                  Inst = manufacture_computation(Pred,Expr2,Visited),
                  Code = Insertion#insertion.code,
                  NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]},
                  NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc)
              end;
            {_, Val} ->
              ?pp_debug("~nThere has been insertions along the edge L~w already, and for that operand too (Op=~w)",[Pred,Op]),
              ?pp_debug("~nThis means, this temp is a WBA Xsi's definition",[]),
              Dst = Val,
              NewInsertionsAcc = InsertionsAcc
          end;
        none ->
          ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course | Op=",[Pred]),pp_arg(Op),
          Key = Op#temp.key,
          {_V,Xsi} = ?GRAPH:vertex(XsiG,Key),
          case Xsi#xsi.wba of
            true ->
              ?pp_debug("~nBut the operand is a WBA Xsi: no need for insertion",[]),
              Dst = Op#temp.var,
              NewInsertionsAcc = InsertionsAcc;
            _ ->
              ?pp_debug("~nBut the operand is a NOT WBA Xsi: we must make an insertion",[]),
              Dst = ?RTL:mk_new_var(),
              Expr2 = ?RTL:alu_dst_update(Expr,Dst),
              Inst = manufacture_computation(Pred,Expr2,Visited),
              NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]},
              NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc)
          end
      end;
    _ ->
      ?pp_debug("~nThe operand (Op=",[]),pp_arg(Op),?pp_debug(") is a real variable, no need for insertion along L~w",[Pred]),
      Dst = Op,
      NewInsertionsAcc = InsertionsAcc
  end,
  NewXsiOp = XsiOp#xsi_op{op=Dst},
  get_insertions(Ops, [NewXsiOp|OpAcc], NewInsertionsAcc, Visited, Expr, XsiG).

manufacture_computation(_Pred, Expr, []) ->
  ?pp_debug("~n Manufactured computation : ~w", [Expr]),
  Expr;
manufacture_computation(Pred, Expr, [I|Rest]) ->
  %% ?pp_debug("~n Expr = ~w",[Expr]),
  SRC1 = ?RTL:alu_src1(Expr),
  SRC2 = ?RTL:alu_src2(Expr),
  case I of
    #xsi_link{} ->
      exit({?MODULE,should_not_be_a_xsi_link,{"Why the hell do we still have a xsi link???",I}});
    #xsi{} ->
      exit({?MODULE,should_not_be_a_xsi,{"Why the hell do we still have a xsi ???",I}});
    #phi{} ->
      DST = ?RTL:phi_dst(I),
      Arg = ?RTL:phi_arg(I,Pred),
      NewInst = case DST =:= SRC1 of
  	          true -> ?RTL:alu_src1_update(Expr,Arg);
  	          false -> Expr
                end,
      NewExpr = case DST =:= SRC2 of
                  true -> ?RTL:alu_src2_update(NewInst,Arg);
                  false -> NewInst
                end,
      manufacture_computation(Pred,NewExpr,Rest)
  end.

make_insertions(_L, [], _ITree, Cfg) ->
  Cfg; 
make_insertions(L, [OldPred|Is], ITree, Cfg) ->
  NewPred      = ?RTL:label_name(?RTL:mk_new_label()),
  I            = gb_trees:get(OldPred, ITree),
  CodeToInsert = lists:reverse([?RTL:mk_goto(L)|I#insertion.code]),
  BBToInsert   = ?BB:mk_bb(CodeToInsert),
  NewCfg       = ?CFG:bb_insert_between(Cfg, NewPred, BBToInsert, OldPred, L),
  make_insertions(L, Is, ITree, NewCfg).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  XSI INTERFACE  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

xsi_oplist(#xsi{opList=OpList}) ->
  case OpList of undefined -> [] ; _ -> OpList end.
xsi_arg(Xsi, Pred) ->
  case lists:keyfind(Pred, #xsi_op.pred, xsi_oplist(Xsi)) of
    false ->
      undetermined_operand;
    R ->
      R#xsi_op.op
  end.
xsi_arg_update(Xsi, Pred, Op) ->
  NewOpList = lists:keyreplace(Pred, #xsi_op.pred, xsi_oplist(Xsi), 
			       #xsi_op{pred=Pred,op=Op}),
  Xsi#xsi{opList=NewOpList}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PRETTY-PRINTING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-ifndef(SSAPRE_DEBUG).

%%pp_cfg(Cfg,_) -> ?CFG:pp(Cfg).
pp_cfg(_,_) -> ok.
pp_instr(_,_) -> ok.
pp_instrs(_,_) -> ok.
pp_expr(_) -> ok.
pp_xsi(_) -> ok.
pp_arg(_) -> ok.
pp_xsigraph(_) -> ok.
pp_cfggraph(_) -> ok.
%% pp_xsigraph(G) ->
%%   Vertices = lists:sort(?GRAPH:vertices(G)),
%%   io:format(standard_io, "Size of the Xsi Graph: ~w", [length(Vertices)]).
%% pp_cfggraph(G) ->
%%   Vertices = lists:sort(?GRAPH:vertices(G)),
%%   io:format(standard_io, "Size of the CFG Graph: ~w", [length(Vertices)]).

-else.

pp_cfg(Cfg, Graph) ->
  Labels = ?CFG:preorder(Cfg),
  pp_blocks(Labels, Cfg, Graph).

pp_blocks([], _, _) ->
  ok;
pp_blocks([L|Ls], Cfg, Graph) ->
  Code = hipe_bb:code(?CFG:bb(Cfg,L)),
  io:format(standard_io,"~n########## Label L~w~n", [L]),
  pp_instrs(Code, Graph),
  pp_blocks(Ls, Cfg, Graph).

pp_instrs([], _) ->
  ok;
pp_instrs([I|Is], Graph) ->
  pp_instr(I, Graph),
  pp_instrs(Is, Graph).

pp_xsi_link(Key, Graph) ->
  {_Key,Xsi} = ?GRAPH:vertex(Graph, Key),
  pp_xsi(Xsi).

pp_xsi(Xsi) ->
  io:format(standard_io, "    [L~w] ", [Xsi#xsi.label]),
  io:format(standard_io, "[", []), pp_expr(Xsi#xsi.inst),
  io:format(standard_io, "] Xsi(", []), pp_xsi_args(xsi_oplist(Xsi)),
  io:format(standard_io, ") (", []), pp_xsi_def(Xsi#xsi.def),
  io:format(standard_io, ") cba=~w, later=~w | wba=~w~n", [Xsi#xsi.cba,Xsi#xsi.later,Xsi#xsi.wba]).

pp_instr(I, Graph) ->
  case I of
    #alu{} ->
      io:format(standard_io, "    ", []),
      pp_arg(?RTL:alu_dst(I)),
      io:format(standard_io, " <- ", []),
      pp_expr(I),
      io:format(standard_io, "~n", []);
    _ ->
      try ?RTL:pp_instr(standard_io, I)
      catch _:_ ->
 	case I of
 	  #pre_candidate{} ->
 	    pp_pre(I);
 	  #xsi{} ->
 	    pp_xsi(I);
 	  #xsi_link{} ->
 	    pp_xsi_link(I#xsi_link.num, Graph);
 	  _->
 	    io:format(standard_io,"*** ~w ***~n", [I])
 	end
      end
  end.

pp_pre(I) ->
  A = I#pre_candidate.alu,
  io:format(standard_io, "    ", []),
  pp_arg(?RTL:alu_dst(A)),
  io:format(standard_io, " <- ", []),pp_expr(A),
  io:format(standard_io, " [ ", []),pp_arg(I#pre_candidate.def),
  %%io:format(standard_io, "~w", [I#pre_candidate.def]),
  io:format(standard_io, " ]~n",[]).

pp_expr(I) ->
  pp_arg(?RTL:alu_dst(I)),
  io:format(standard_io, " <- ", []),
  pp_arg(?RTL:alu_src1(I)),
  io:format(standard_io, " ~w ", [?RTL:alu_op(I)]),
  pp_arg(?RTL:alu_src2(I)).

pp_arg(Arg) ->
  case Arg of
    bottom ->
      io:format(standard_io, "_|_", []);
    #bottom{} ->
      io:format(standard_io, "_|_:~w (", [Arg#bottom.key]),pp_arg(Arg#bottom.var),io:format(standard_io,")",[]);
    #temp{} ->
      pp_xsi_def(Arg);
    #eop{} ->
      io:format(standard_io,"#",[]),pp_expr(Arg#eop.expr),io:format(standard_io,"(",[]),pp_arg(Arg#eop.var),io:format(standard_io,")#",[]);
    #const_expr{} ->
      io:format(standard_io,"*",[]),pp_arg(Arg#const_expr.var),io:format(standard_io," -> ",[]),pp_arg(Arg#const_expr.value),io:format(standard_io,"*",[]);
    undefined ->
      io:format(standard_io, "...", []); %%"undefined", []);
    _->
      case Arg of
        #alu{} ->
          pp_expr(Arg);      
        _->
          ?RTL:pp_arg(standard_io, Arg)
      end
  end.

pp_args([]) ->
  ok;
pp_args(undefined) ->
  io:format(standard_io, "...,...,...", []);
pp_args([A]) ->
  pp_arg(A);
pp_args([A|As]) ->
  pp_arg(A),
  io:format(standard_io, ", ", []),
  pp_args(As).

pp_xsi_args([]) -> ok;
pp_xsi_args([XsiOp]) ->
  io:format(standard_io, "{~w| ", [XsiOp#xsi_op.pred]),
  pp_arg(XsiOp#xsi_op.op),
  io:format(standard_io, "}", []);
pp_xsi_args([XsiOp|Args]) ->
  io:format(standard_io, "{~w| ", [XsiOp#xsi_op.pred]),
  pp_arg(XsiOp#xsi_op.op),
  io:format(standard_io, "}, ", []),
  pp_xsi_args(Args);
pp_xsi_args(Args) ->
  pp_args(Args).

pp_xsi_def(Arg) ->
  D = Arg#temp.key,
  V = Arg#temp.var,
  io:format(standard_io, "t~w (", [D]),pp_arg(V),io:format(standard_io,")",[]).

pp_cfggraph(G) ->
  Vertices = lists:sort(?GRAPH:vertices(G)),
  io:format(standard_io, "Size of the CFG Graph: ~w ~n", [length(Vertices)]),
  pp_cfgvertex(Vertices, G).

pp_xsigraph(G) ->
  Vertices = lists:sort(?GRAPH:vertices(G)),
  io:format(standard_io, "Size of the Xsi Graph: ~w ~n", [length(Vertices)]),
  pp_xsivertex(Vertices,G).

pp_xsivertex([], _G) ->
  ok;
pp_xsivertex([Key|Keys], G) ->
  {V,Xsi} = ?GRAPH:vertex(G, Key),
  OutNeighbours = ?GRAPH:out_neighbours(G, V),
  ?pp_debug(" ~w -> ~w", [V,OutNeighbours]), pp_xsi(Xsi),
  pp_xsivertex(Keys, G).

pp_cfgvertex([], _G) ->
  ok;
pp_cfgvertex([Key|Keys], G) ->
  {V,Block} = ?GRAPH:vertex(G,Key),
  case Block#block.type of
    mp ->
      ?pp_debug("~n Block ~w's attributes: ~n", [V]),
      pp_attributes(Block),
      ?pp_debug("~n Block ~w's edges: ~n", [V]),
      pp_edges(G, ?GRAPH:in_edges(G,Key), ?GRAPH:out_edges(G,Key));
    _->
      ok
  end,
  pp_cfgvertex(Keys, G).

pp_attributes(Block) ->
  Att = Block#block.attributes,
  case Att of
    undefined ->
      ok;
    _ ->
      ?pp_debug("        Maps: ~n",[]),pp_maps(gb_trees:keys(Att#mp.maps),Att#mp.maps),
      ?pp_debug("        Uses: ~n",[]),pp_uses(gb_trees:keys(Att#mp.uses),Att#mp.uses),
      ?pp_debug("        Defs: ~w~n",[Att#mp.defs]),
      ?pp_debug("        Xsis: ~w~n",[Att#mp.xsis]),
      ?pp_debug("        NDS : ",[]),pp_nds(?SETS:to_list(Att#mp.ndsSet))
  end.

pp_maps([], _Maps) -> ok;
pp_maps([K|Ks], Maps) ->
  ?pp_debug("              ",[]),pp_arg(K#xsi_op.op),?pp_debug("-> ~w~n",[?SETS:to_list(gb_trees:get(K,Maps))]),
  pp_maps(Ks, Maps).

pp_uses([], _Maps) -> ok;
pp_uses([K|Ks], Maps) ->
  ?pp_debug("              ~w -> ~w~n",[K,?SETS:to_list(gb_trees:get(K,Maps))]),
  pp_uses(Ks, Maps).

pp_nds([]) -> ?pp_debug("~n",[]);
pp_nds(undefined) -> ?pp_debug("None",[]);
pp_nds([K]) ->
  pp_arg(K#xsi_op.op), ?pp_debug("~n",[]);
pp_nds([K|Ks]) ->
  pp_arg(K#xsi_op.op), ?pp_debug(", ",[]),
  pp_nds(Ks).

pp_edges(_G, [], []) -> ok;
pp_edges(G, [], [OUT|OUTs]) ->
  {_E,V1,V2,Label} = ?GRAPH:edge(G,OUT),
  ?pp_debug(" Out edge ~w -> ~w (~w)~n", [V1,V2,?SETS:to_list(Label)]),
  pp_edges(G, [], OUTs);
pp_edges(G, [IN|INs], Outs) ->
  {_E,V1,V2,Label} = ?GRAPH:edge(G,IN),
  ?pp_debug("  In edge ~w -> ~w (~w)~n", [V1,V2,?SETS:to_list(Label)]),
  pp_edges(G, INs, Outs).

-endif.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% COUNTERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

init_counters() ->
  put({ssapre_temp,temp_count}, 0),
  put({ssapre_index,index_count}, 0).

new_bottom() ->
  IndxCountPair = {ssapre_index, index_count},
  V = get(IndxCountPair),
  put(IndxCountPair, V+1),
  #bottom{key = V, var = ?RTL:mk_new_var()}.

new_temp() ->
  TmpCountPair = {ssapre_temp, temp_count},
  V = get(TmpCountPair),
  put(TmpCountPair, V+1),
  #temp{key = V, var = ?RTL:mk_new_var()}.

init_redundancy_count() ->
  put({ssapre_redundancy,redundancy_count}, 0).

redundancy_add() ->
  RedCountPair = {ssapre_redundancy, redundancy_count},
  V = get(RedCountPair),
  put(RedCountPair, V+1).

-ifdef(SSAPRE_DEBUG).
get_redundancy_count() ->
  get({ssapre_redundancy,redundancy_count}).
-endif.