%% -*- Erlang -*-
%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2004-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%
%%
%%----------------------------------------------------------------------
%% 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));
    'mul' ->
      FullRes = Arg1 * Arg2,
      Res = FullRes band ?WORDMASK,
      ResHi = FullRes bsr ?BITS,
      N = sign_bit(Res),
      Z = zero(Res),
      V = (N and (ResHi =/= -1)) or ((not N) and (ResHi =/= 0)),
      C = V;
    '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)
%%
-spec eval_cond_bits(atom(), boolean(), boolean(), boolean(), boolean()) -> boolean().

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.

