%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Copyright (c) 2001 by Erik Johansson.  All Rights Reserved 
%% Time-stamp: <2008-04-20 14:55:35 richard>
%% ====================================================================
%%  Module   :	hipe_rtl_varmap
%%  Purpose  :  
%%  Notes    : 
%%  History  :	* 2001-04-10 Erik Johansson (happi@it.uu.se): Created.
%% ====================================================================
%%  Exports  :
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-module(hipe_rtl_varmap).

-export([init/1,
	 ivs2rvs/2,
	 icode_var2rtl_var/2,
	 icode_label2rtl_label/2]).

%-------------------------------------------------------------------------

-include("../main/hipe.hrl").
-include("../icode/hipe_icode.hrl").

%-------------------------------------------------------------------------

%% @spec init(IcodeRecord::#icode{}) -> {Args, VarMap}
%%
%% @doc Initializes gensym for RTL.

-spec init(#icode{}) -> {[_], _}.  % XXX: fix me please

init(IcodeRecord) ->
  hipe_gensym:init(rtl),
  hipe_gensym:set_var(rtl, hipe_rtl_arch:first_virtual_reg()),
  hipe_gensym:set_label(rtl, 0),
  VarMap = new_var_map(),
  {_Args, _VarMap1} = ivs2rvs(hipe_icode:icode_params(IcodeRecord), VarMap).


%%------------------------------------------------------------------------
%%
%% Mapping of labels and variables from Icode to RTL.
%%
%%------------------------------------------------------------------------


%% @spec icode_label2rtl_label(Icode_Label::term(), LabelMap::term()) ->
%%           {RTL_Label, NewLabelMap}
%%
%% @doc Converts an Icode label to an RTL label.

icode_label2rtl_label(LabelName, Map) ->
  case lookup(LabelName, Map) of
    {value, NewLabel} ->
      {NewLabel, Map};
    none ->
      NewLabel = hipe_rtl:mk_new_label(),
      {NewLabel, insert(LabelName, NewLabel, Map)}
  end.


%% @spec ivs2rvs(Icode_Vars::[term()], VarMap::term()) -> {[RTL_Var],NewVarMap}
%%
%% @doc Converts a list of Icode variables to a list of RTL variables.

ivs2rvs([], VarMap) ->
  {[], VarMap};
ivs2rvs([V|Vs], VarMap) ->
  {NewV, VarMap0} = icode_var2rtl_var(V, VarMap),
  {NewVs, VarMap1} = ivs2rvs(Vs, VarMap0),
  {[NewV|NewVs], VarMap1}.


%% @spec icode_var2rtl_var(Icode_Var::term(), VarMap::term()) ->
%%           {RTL_Var, NewVarMap}
%%
%% @doc Converts an Icode variable to an RTL variable.

icode_var2rtl_var(Var, Map) ->
  Value = lookup(Var, Map),
  case Value of
    none ->
      case type_of_var(Var) of
	fvar ->
	  NewVar = hipe_rtl:mk_new_fpreg(),
	  {NewVar, insert(Var, NewVar, Map)};
	var ->
	  NewVar = hipe_rtl:mk_new_var(),
	  {NewVar, insert(Var, NewVar, Map)};
	{reg, IsGcSafe} ->
	  NewVar =
	    case IsGcSafe of
	      %% true -> hipe_rtl:mk_new_reg_gcsafe();
	      false -> hipe_rtl:mk_new_reg()
	    end,
	  {NewVar, insert(Var, NewVar, Map)}
      end;
    {value, NewVar} ->
      {NewVar, Map}
  end.

%%
%% Simple type test
%%

type_of_var(X) ->
  case hipe_icode:is_fvar(X) of
    true ->
      fvar;
    false ->
      case hipe_icode:is_var(X) of
	true ->
	  var;
	false ->
	  case hipe_icode:is_reg(X) of
	    true ->
	      {reg, hipe_icode:reg_is_gcsafe(X)};
	    false ->
	      %% Sanity check
	      case hipe_icode:is_const(X) of
		true -> const;
		false ->
		  exit({"Unknown Icode variable", X})
	      end
	  end
      end
  end.

%%
%% Helping utilities
%% 

new_var_map() ->
  gb_trees:empty().

lookup(V, Map) ->
  gb_trees:lookup(V, Map).

insert(Key, Val, Map) ->
  gb_trees:insert(Key, Val, Map).
