%%% -*- erlang-indent-level: 2 -*-
%%% $Id$
%%% ===========================================================================
%%%  Filename :  hipe_amd64_registers.erl
%%%  Author   :  Daniel Luna (luna@update.uu.se)
%%%  Purpose  :  
%%%  Notes    :  
%%% ===========================================================================

-module(hipe_amd64_registers).

-export([
 	 all_precoloured/0,
 	 allocatable/0,
	 allocatable_sse2/0,
	 allocatable_x87/0,
 	 arg/1,
         args/1,
         call_clobbered/0,
	 fcalls/0,
	 float_size/0,
	 first_virtual/0,
 	 heap_limit/0,
 	 is_arg/1,
 	 is_fixed/1,
 	 is_precoloured/1,
 	 is_precoloured_sse2/1,
	 is_precoloured_x87/1,
 	 live_at_return/0,
	 nr_args/0,
 	 proc_offset/1,
 	 proc_pointer/0,
 	 rax/0,
         rcx/0,
	 ret/1,
         sp/0,
         sp_limit_offset/0,
	 reg_name/1,
         alignment/0,
 	 tailcall_clobbered/0,
 	 temp0/0,
	 temp1/0,
% 	 %% fixed/0,
	 wordsize/0
	]).

-include("../rtl/hipe_literals.hrl").

-ifdef(AMD64_HP_IN_REGISTER).
-export([heap_pointer/0]).
-endif.

-ifdef(AMD64_FCALLS_IN_REGISTER).
fcalls_offset() -> false.
-else.
fcalls_offset() -> ?P_FCALLS.
-define(AMD64_FCALLS_REGISTER,16).
-endif.

-ifdef(AMD64_HEAP_LIMIT_IN_REGISTER).
heap_limit_offset() -> false.
-else.
-define(AMD64_HEAP_LIMIT_REGISTER, 17).
heap_limit_offset() -> ?P_HP_LIMIT.
-endif.


-define(RAX,  0).
-define(RCX,  1).
-define(RDX,  2).
-define(RBX,  3).
-define(RSP,  4).
-define(RBP,  5).
-define(RSI,  6).
-define(RDI,  7).
-define(R8 ,  8).
-define(R9 ,  9).
-define(R10, 10).
-define(R11, 11).
-define(R12, 12).
-define(R13, 13).
-define(R14, 14).
-define(R15, 15).
-define(FCALLS,           ?AMD64_FCALLS_REGISTER).
-define(HEAP_LIMIT,       ?AMD64_HEAP_LIMIT_REGISTER).
-define(LAST_PRECOLOURED, 17).

-define(ARG0, ?RSI).
-define(ARG1, ?RDX).
-define(ARG2, ?RCX).
-define(ARG3, ?R8).
-define(ARG4, ?R9).
-define(ARG5, ?RDI).

-define(TEMP0, ?R14).
-define(TEMP1, ?R13).

-define(PROC_POINTER, ?RBP).

reg_name(R) ->
  case R of
    ?RAX -> "%rax";
    ?RCX -> "%rcx";
    ?RDX -> "%rdx";
    ?RBX -> "%rbx";
    ?RSP -> "%rsp";
    ?RBP -> "%rbp";
    ?RSI -> "%rsi";
    ?RDI -> "%rdi";
    ?FCALLS -> "%fcalls";
    ?HEAP_LIMIT -> "%hplim";
    Other -> "%r" ++ integer_to_list(Other)
  end.

alignment() -> 8.  

float_size() -> 8.  

first_virtual() -> ?LAST_PRECOLOURED + 1.

is_precoloured(X) -> X =< ?LAST_PRECOLOURED.

is_precoloured_sse2(X) -> X =< 15.

is_precoloured_x87(X) -> X =< 6.

all_precoloured() ->
  [?RAX,
   ?RCX,
   ?RDX,
   ?RBX,
   ?RSP,
   ?RBP,
   ?RSI,
   ?RDI,
   ?R8 ,
   ?R9 ,
   ?R10,
   ?R11,
   ?R12,
   ?R13,
   ?R14,
   ?R15,
   ?FCALLS,
   ?HEAP_LIMIT].

rax() -> ?RAX.
rcx() -> ?RCX.
temp0() -> ?TEMP0.
temp1() -> ?TEMP1.
sp() -> ?RSP.
proc_pointer() -> ?PROC_POINTER.
fcalls() -> ?FCALLS.
heap_limit() -> ?HEAP_LIMIT.


-ifdef(AMD64_HP_IN_REGISTER).
-define(HEAP_POINTER, ?AMD64_HEAP_POINTER).
heap_pointer() -> ?HEAP_POINTER.
-define(LIST_HP_LIVE_AT_RETURN,[{?HEAP_POINTER,untagged}]).
is_heap_pointer(?HEAP_POINTER) -> true;
is_heap_pointer(_) -> false.
%% -define(LIST_HP_FIXED,[?HEAP_POINTER]).

-else.
-define(HEAP_POINTER, -1).
is_heap_pointer(_) -> false.
%% -define(LIST_HP_FIXED,[]).
-define(LIST_HP_LIVE_AT_RETURN,[]).
-endif.

proc_offset(?FCALLS) -> fcalls_offset();
proc_offset(?HEAP_LIMIT) -> heap_limit_offset();
proc_offset(_) -> false.

sp_limit_offset() -> ?P_NSP_LIMIT.

is_fixed(?RSP) -> true;
is_fixed(?PROC_POINTER) -> true;
is_fixed(?FCALLS) -> true;
is_fixed(?HEAP_LIMIT) -> true;
is_fixed(R) -> is_heap_pointer(R).

% %% fixed() ->
% %%     [?ESP, ?PROC_POINTER, ?FCALLS, ?HEAP_LIMIT | ?LIST_HP_FIXED].

allocatable() ->
  [?RDX, ?RCX, ?RBX, ?RAX, ?RSI, ?RDI,
   ?R8 , ?R9 , ?R10, ?R11, ?R12, ?R13, ?R14, ?R15]
    -- [?FCALLS, ?HEAP_POINTER, ?HEAP_LIMIT].

allocatable_sse2() ->
  [00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15]. %% xmm0 - xmm15

allocatable_x87() ->
  [0,1,2,3,4,5,6].

nr_args() -> ?AMD64_NR_ARG_REGS.

arg(N) ->
  if N < ?AMD64_NR_ARG_REGS ->
      case N of
	0 -> ?ARG0;
	1 -> ?ARG1;
	2 -> ?ARG2;
	3 -> ?ARG3;
	4 -> ?ARG4;
	5 -> ?ARG5;
	_ -> exit({?MODULE, arg, N})
      end;
     true ->
      exit({?MODULE, arg, N})
  end.

is_arg(R) ->
  case R of
    ?ARG0 -> ?AMD64_NR_ARG_REGS > 0;
    ?ARG1 -> ?AMD64_NR_ARG_REGS > 1;
    ?ARG2 -> ?AMD64_NR_ARG_REGS > 2;
    ?ARG3 -> ?AMD64_NR_ARG_REGS > 3;
    ?ARG4 -> ?AMD64_NR_ARG_REGS > 4;
    ?ARG5 -> ?AMD64_NR_ARG_REGS > 5;
    _ -> false
  end.

args(Arity) ->
  Max = ?AMD64_NR_ARG_REGS,
  N = if Arity > Max -> Max; true -> Arity end,
  args(N-1, []).

args(I, Rest) when I < 0 -> Rest;
args(I, Rest) -> args(I-1, [arg(I) | Rest]).

ret(N) ->
  case N of
    0 -> ?RAX;
    _ -> exit({?MODULE, ret, N})
  end.

call_clobbered() ->
  [{?RAX,tagged},{?RAX,untagged},	% does the RA strip the type or not?
   {?RDX,tagged},{?RDX,untagged},
   {?RCX,tagged},{?RCX,untagged},
   {?RBX,tagged},{?RBX,untagged},
   {?RDI,tagged},{?RDI,untagged},
   {?RSI,tagged},{?RSI,untagged},
   {?R8 ,tagged},{?R8 ,untagged},
   {?R9 ,tagged},{?R9 ,untagged},
   {?R10,tagged},{?R10,untagged},
   {?R11,tagged},{?R11,untagged},
   {?R12,tagged},{?R12,untagged},
   {?R13,tagged},{?R13,untagged},
   {?R14,tagged},{?R14,untagged},
   {?R15,tagged},{?R15,untagged}
   | fp_call_clobbered()]
    --
    [{?FCALLS,tagged},{?FCALLS,untagged},
     {?HEAP_POINTER,tagged},{?HEAP_POINTER,untagged},
     {?HEAP_LIMIT,tagged},{?HEAP_LIMIT,untagged}
    ].

fp_call_clobbered() -> %% sse2 since it has more registers than x87
  [{Reg,double} || Reg <- allocatable_sse2()].

tailcall_clobbered() ->		% tailcall crapola needs two temps
  [{?TEMP0,tagged},{?TEMP0,untagged},
   {?TEMP1,tagged},{?TEMP1,untagged}
  | fp_call_clobbered()].

live_at_return() ->
    [{?RAX,tagged}
     ,{?RSP,untagged}
     ,{?PROC_POINTER,untagged}
     ,{?FCALLS,untagged}
     ,{?HEAP_LIMIT,untagged}
     | ?LIST_HP_LIVE_AT_RETURN
    ].

wordsize() -> 8.
