%% -*- Erlang -*-
%% -*- erlang-indent-level: 2 -*-
%%----------------------------------------------------------------------
%% File    : hipe_rtl_arith.inc
%% Created : Feb 2004
%% Purpose : Implements arithmetic which is parameterized by the size
%%           of the word of the target architecture (given as defines).
%%----------------------------------------------------------------------


%% Returns a tuple
%%  {Res, Sign, Zero, Overflow, Carry}
%%  Res will be a number in the range 
%%   MAX_SIGNED_INT >= Res >= MIN_SIGNED_INT
%% The other four values are flags that are either true or false
%% 
eval_alu(Op, Arg1, Arg2) 
  when Arg1 =< ?MAX_SIGNED_INT, 
       Arg1 >= ?MIN_SIGNED_INT,
       Arg2 =< ?MAX_SIGNED_INT, 
       Arg2 >= ?MIN_SIGNED_INT ->

  Sign1 = sign_bit(Arg1),
  Sign2 = sign_bit(Arg2),

  case Op of
    'sub' ->
      Res = (Arg1 - Arg2) band ?WORDMASK,
      N = sign_bit(Res),
      Z = zero(Res),
      V = (Sign1 and (not Sign2) and (not N)) 
	or
          ((not Sign1) and Sign2 and N),
      C = ((not Sign1) and Sign2) 
	or 
	  (N and ((not Sign1) or Sign2));
    'add' ->
      Res = (Arg1 + Arg2) band ?WORDMASK,
      N = sign_bit(Res),
      Z = zero(Res),
      V = (Sign1 and Sign2 and (not N)) 
	or
          ((not Sign1) and (not Sign2) and N),
      C = (Sign1 and Sign2)
	or 
	  ((not N) and (Sign1 or Sign2));
    'sra' ->
      Res = (Arg1 bsr Arg2) band ?WORDMASK,
      N = sign_bit(Res),
      Z = zero(Res),    
      V = 0,
      C = 0;
    'srl' ->
      Res = (Arg1 bsr Arg2) band shiftmask(Arg2),
      N = sign_bit(Res),
      Z = zero(Res),     
      V = 0,
      C = 0;
    'sll' ->
      Res = (Arg1 bsl Arg2) band ?WORDMASK, 
      N = sign_bit(Res),
      Z = zero(Res),     
      V = 0,
      C = 0;
    'or' ->
      Res = (Arg1 bor Arg2) band ?WORDMASK,
      N = sign_bit(Res),
      Z = zero(Res),     
      V = 0,
      C = 0;
    'and' ->
      Res = (Arg1 band Arg2) band ?WORDMASK,
      N = sign_bit(Res),
      Z = zero(Res),     
      V = 0,
      C = 0;
    'xor' ->
      Res = (Arg1 bxor Arg2) band ?WORDMASK,
      N = sign_bit(Res),
      Z = zero(Res),     
      V = 0,
      C = 0;
    Op ->
      Res = N = Z = V = C = 0,
      ?EXIT({"unknown alu op", Op})
  end,
  {two_comp_to_erl(Res),N,Z,V,C};
 
eval_alu(Op,Arg1,Arg2) ->
  ?EXIT({argument_overflow,Op,Arg1,Arg2}).

%% Björn & Bjarni: 
%% We need to be able to do evaluations based only on the bits, since
%% there are cases where we can evaluate a subset of the bits, but can
%% not do a full eval-alub call (eg. a + 0 gives no carry)
eval_cond_bits(Cond, N, Z, V, C) ->
  case Cond of
    'eq' ->
      Z;
    'ne' -> 
      not Z;
    'gt'	-> 
      not (Z or (N xor V));
    'gtu' -> 
      not (C or Z);
    'ge' -> 
      not (N xor V);
    'geu'-> 
      not C;
    'lt'	->
      N xor V;
    'ltu'-> 
      C;
    'le'	->
      Z or (N xor V);
    'leu'-> 
      C or Z;
    'overflow' ->
      V;
    'not_overflow' ->
      not V;
    _ ->
      ?EXIT({'condition code not handled',Cond})
  end.

eval_alub(Op, Cond, Arg1, Arg2) ->
  {Res,N,Z,V,C} = eval_alu(Op,Arg1,Arg2),
  {Res, eval_cond_bits(Cond, N, Z, V, C) }.

eval_cond(Cond, Arg1, Arg2) ->
  {_,Bool} = eval_alub('sub', Cond, Arg1, Arg2),
  Bool.

sign_bit(Val) ->
  ((Val bsr ?SIGN_BIT) band 1) =:= 1.

two_comp_to_erl(V) ->
  if V > ?MAX_SIGNED_INT ->
      - ((?MAX_UNSIGNED_INT + 1) - V);
     true -> V
  end.

shiftmask(Arg) ->
  Setbits = ?BITS-Arg,
  (1 bsl Setbits)-1.

zero(Val) ->
  Val =:= 0.

