------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                             E X P _ V F P T                              --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--          Copyright (C) 1997-2008, Free Software Foundation, Inc.         --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with GNAT; see file COPYING3.  If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license.          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with Atree;    use Atree;
with Einfo;    use Einfo;
with Nlists;   use Nlists;
with Nmake;    use Nmake;
with Rtsfind;  use Rtsfind;
with Sem_Res;  use Sem_Res;
with Sinfo;    use Sinfo;
with Stand;    use Stand;
with Tbuild;   use Tbuild;
with Ttypef;   use Ttypef;
with Uintp;    use Uintp;
with Urealp;   use Urealp;

package body Exp_VFpt is

   ----------------------
   -- Expand_Vax_Arith --
   ----------------------

   procedure Expand_Vax_Arith (N : Node_Id) is
      Loc   : constant Source_Ptr := Sloc (N);
      Typ   : constant Entity_Id  := Base_Type (Etype (N));
      Typc  : Character;
      Atyp  : Entity_Id;
      Func  : RE_Id;
      Args  : List_Id;

   begin
      --  Get arithmetic type, note that we do D stuff in G

      if Digits_Value (Typ) = VAXFF_Digits then
         Typc := 'F';
         Atyp := RTE (RE_F);
      else
         Typc := 'G';
         Atyp := RTE (RE_G);
      end if;

      case Nkind (N) is

         when N_Op_Abs =>
            if Typc = 'F' then
               Func := RE_Abs_F;
            else
               Func := RE_Abs_G;
            end if;

         when N_Op_Add =>
            if Typc = 'F' then
               Func := RE_Add_F;
            else
               Func := RE_Add_G;
            end if;

         when N_Op_Divide =>
            if Typc = 'F' then
               Func := RE_Div_F;
            else
               Func := RE_Div_G;
            end if;

         when N_Op_Multiply =>
            if Typc = 'F' then
               Func := RE_Mul_F;
            else
               Func := RE_Mul_G;
            end if;

         when N_Op_Minus =>
            if Typc = 'F' then
               Func := RE_Neg_F;
            else
               Func := RE_Neg_G;
            end if;

         when N_Op_Subtract =>
            if Typc = 'F' then
               Func := RE_Sub_F;
            else
               Func := RE_Sub_G;
            end if;

         when others =>
            Func := RE_Null;
            raise Program_Error;

      end case;

      Args := New_List;

      if Nkind (N) in N_Binary_Op then
         Append_To (Args,
           Convert_To (Atyp, Left_Opnd (N)));
      end if;

      Append_To (Args,
        Convert_To (Atyp, Right_Opnd (N)));

      Rewrite (N,
        Convert_To (Typ,
          Make_Function_Call (Loc,
            Name => New_Occurrence_Of (RTE (Func), Loc),
            Parameter_Associations => Args)));

      Analyze_And_Resolve (N, Typ, Suppress => All_Checks);
   end Expand_Vax_Arith;

   ---------------------------
   -- Expand_Vax_Comparison --
   ---------------------------

   procedure Expand_Vax_Comparison (N : Node_Id) is
      Loc   : constant Source_Ptr := Sloc (N);
      Typ   : constant Entity_Id  := Base_Type (Etype (Left_Opnd (N)));
      Typc  : Character;
      Func  : RE_Id;
      Atyp  : Entity_Id;
      Revrs : Boolean := False;
      Args  : List_Id;

   begin
      --  Get arithmetic type, note that we do D stuff in G

      if Digits_Value (Typ) = VAXFF_Digits then
         Typc := 'F';
         Atyp := RTE (RE_F);
      else
         Typc := 'G';
         Atyp := RTE (RE_G);
      end if;

      case Nkind (N) is

         when N_Op_Eq =>
            if Typc = 'F' then
               Func := RE_Eq_F;
            else
               Func := RE_Eq_G;
            end if;

         when N_Op_Ge =>
            if Typc = 'F' then
               Func := RE_Le_F;
            else
               Func := RE_Le_G;
            end if;

            Revrs := True;

         when N_Op_Gt =>
            if Typc = 'F' then
               Func := RE_Lt_F;
            else
               Func := RE_Lt_G;
            end if;

            Revrs := True;

         when N_Op_Le =>
            if Typc = 'F' then
               Func := RE_Le_F;
            else
               Func := RE_Le_G;
            end if;

         when N_Op_Lt =>
            if Typc = 'F' then
               Func := RE_Lt_F;
            else
               Func := RE_Lt_G;
            end if;

         when N_Op_Ne =>
            if Typc = 'F' then
               Func := RE_Ne_F;
            else
               Func := RE_Ne_G;
            end if;

         when others =>
            Func := RE_Null;
            raise Program_Error;

      end case;

      if not Revrs then
         Args := New_List (
           Convert_To (Atyp, Left_Opnd  (N)),
           Convert_To (Atyp, Right_Opnd (N)));

      else
         Args := New_List (
           Convert_To (Atyp, Right_Opnd (N)),
           Convert_To (Atyp, Left_Opnd  (N)));
      end if;

      Rewrite (N,
        Make_Function_Call (Loc,
          Name => New_Occurrence_Of (RTE (Func), Loc),
          Parameter_Associations => Args));

      Analyze_And_Resolve (N, Standard_Boolean, Suppress => All_Checks);
   end Expand_Vax_Comparison;

   ---------------------------
   -- Expand_Vax_Conversion --
   ---------------------------

   procedure Expand_Vax_Conversion (N : Node_Id) is
      Loc   : constant Source_Ptr := Sloc (N);
      Expr  : constant Node_Id    := Expression (N);
      S_Typ : constant Entity_Id  := Base_Type (Etype (Expr));
      T_Typ : constant Entity_Id  := Base_Type (Etype (N));

      CallS : RE_Id;
      CallT : RE_Id;
      Func  : RE_Id;

      function Call_Type (T : Entity_Id; Otyp : Entity_Id) return RE_Id;
      --  Given one of the two types T, determines the corresponding call
      --  type, i.e. the type to be used for the call (or the result of
      --  the call). The actual operand is converted to (or from) this type.
      --  Otyp is the other type, which is useful in figuring out the result.
      --  The result returned is the RE_Id value for the type entity.

      function Equivalent_Integer_Type (T : Entity_Id) return Entity_Id;
      --  Find the predefined integer type that has the same size as the
      --  fixed-point type T, for use in fixed/float conversions.

      ---------------
      -- Call_Type --
      ---------------

      function Call_Type (T : Entity_Id; Otyp : Entity_Id) return RE_Id is
      begin
         --  Vax float formats

         if Vax_Float (T) then
            if Digits_Value (T) = VAXFF_Digits then
               return RE_F;

            elsif Digits_Value (T) = VAXGF_Digits then
               return RE_G;

            --  For D_Float, leave it as D float if the other operand is
            --  G_Float, since this is the one conversion that is properly
            --  supported for D_Float, but otherwise, use G_Float.

            else pragma Assert (Digits_Value (T) = VAXDF_Digits);

               if Vax_Float (Otyp)
                 and then Digits_Value (Otyp) = VAXGF_Digits
               then
                  return RE_D;
               else
                  return RE_G;
               end if;
            end if;

         --  For all discrete types, use 64-bit integer

         elsif Is_Discrete_Type (T) then
            return RE_Q;

         --  For all real types (other than Vax float format), we use the
         --  IEEE float-type which corresponds in length to the other type
         --  (which is Vax Float).

         else pragma Assert (Is_Real_Type (T));

            if Digits_Value (Otyp) = VAXFF_Digits then
               return RE_S;
            else
               return RE_T;
            end if;
         end if;
      end Call_Type;

      -------------------------------------------------
      -- Expand_Multiply_Fixed_By_Fixed_Giving_Fixed --
      -------------------------------------------------

      function Equivalent_Integer_Type (T : Entity_Id) return Entity_Id is
      begin
         if Esize (T) = Esize (Standard_Long_Long_Integer) then
            return Standard_Long_Long_Integer;
         elsif Esize (T) = Esize (Standard_Long_Integer) then
            return  Standard_Long_Integer;
         else
            return Standard_Integer;
         end if;
      end Equivalent_Integer_Type;

   --  Start of processing for Expand_Vax_Conversion;

   begin
      --  If input and output are the same Vax type, we change the
      --  conversion to be an unchecked conversion and that's it.

      if Vax_Float (S_Typ) and then Vax_Float (T_Typ)
        and then Digits_Value (S_Typ) = Digits_Value (T_Typ)
      then
         Rewrite (N,
           Unchecked_Convert_To (T_Typ, Expr));

      --  Case of conversion of fixed-point type to Vax_Float type

      elsif Is_Fixed_Point_Type (S_Typ) then

         --  If Conversion_OK set, then we introduce an intermediate IEEE
         --  target type since we are expecting the code generator to handle
         --  the case of integer to IEEE float.

         if Conversion_OK (N) then
            Rewrite (N,
              Convert_To (T_Typ, OK_Convert_To (Universal_Real, Expr)));

         --  Otherwise, convert the scaled integer value to the target type,
         --  and multiply by 'Small of type.

         else
            Rewrite (N,
               Make_Op_Multiply (Loc,
                 Left_Opnd =>
                   Make_Type_Conversion (Loc,
                     Subtype_Mark => New_Occurrence_Of (T_Typ, Loc),
                     Expression   =>
                       Unchecked_Convert_To (
                         Equivalent_Integer_Type (S_Typ), Expr)),
                 Right_Opnd =>
                   Make_Real_Literal (Loc, Realval => Small_Value (S_Typ))));
         end if;

      --  Case of conversion of Vax_Float type to fixed-point type

      elsif Is_Fixed_Point_Type (T_Typ) then

         --  If Conversion_OK set, then we introduce an intermediate IEEE
         --  target type, since we are expecting the code generator to handle
         --  the case of IEEE float to integer.

         if Conversion_OK (N) then
            Rewrite (N,
              OK_Convert_To (T_Typ, Convert_To (Universal_Real, Expr)));

         --  Otherwise, multiply value by 'small of type, and convert to the
         --  corresponding integer type.

         else
            Rewrite (N,
              Unchecked_Convert_To (T_Typ,
                Make_Type_Conversion (Loc,
                  Subtype_Mark =>
                    New_Occurrence_Of (Equivalent_Integer_Type (T_Typ), Loc),
                  Expression =>
                    Make_Op_Multiply (Loc,
                      Left_Opnd => Expr,
                      Right_Opnd =>
                        Make_Real_Literal (Loc,
                          Realval => Ureal_1 / Small_Value (T_Typ))))));
         end if;

      --  All other cases

      else
         --  Compute types for call

         CallS := Call_Type (S_Typ, T_Typ);
         CallT := Call_Type (T_Typ, S_Typ);

         --  Get function and its types

         if CallS = RE_D and then CallT = RE_G then
            Func := RE_D_To_G;

         elsif CallS = RE_G and then CallT = RE_D then
            Func := RE_G_To_D;

         elsif CallS = RE_G and then CallT = RE_F then
            Func := RE_G_To_F;

         elsif CallS = RE_F and then CallT = RE_G then
            Func := RE_F_To_G;

         elsif CallS = RE_F and then CallT = RE_S then
            Func := RE_F_To_S;

         elsif CallS = RE_S and then CallT = RE_F then
            Func := RE_S_To_F;

         elsif CallS = RE_G and then CallT = RE_T then
            Func := RE_G_To_T;

         elsif CallS = RE_T and then CallT = RE_G then
            Func := RE_T_To_G;

         elsif CallS = RE_F and then CallT = RE_Q then
            Func := RE_F_To_Q;

         elsif CallS = RE_Q and then CallT = RE_F then
            Func := RE_Q_To_F;

         elsif CallS = RE_G and then CallT = RE_Q then
            Func := RE_G_To_Q;

         else pragma Assert (CallS = RE_Q and then CallT = RE_G);
            Func := RE_Q_To_G;
         end if;

         Rewrite (N,
           Convert_To (T_Typ,
             Make_Function_Call (Loc,
               Name => New_Occurrence_Of (RTE (Func), Loc),
               Parameter_Associations => New_List (
                 Convert_To (RTE (CallS), Expr)))));
      end if;

      Analyze_And_Resolve (N, T_Typ, Suppress => All_Checks);
   end Expand_Vax_Conversion;

   -------------------------------
   -- Expand_Vax_Foreign_Return --
   -------------------------------

   procedure Expand_Vax_Foreign_Return (N : Node_Id) is
      Loc  : constant Source_Ptr := Sloc (N);
      Typ  : constant Entity_Id  := Base_Type (Etype (N));
      Func : RE_Id;
      Args : List_Id;
      Atyp : Entity_Id;
      Rtyp : constant Entity_Id  := Etype (N);

   begin
      if Digits_Value (Typ) = VAXFF_Digits then
         Func := RE_Return_F;
         Atyp := RTE (RE_F);
      elsif Digits_Value (Typ) = VAXDF_Digits then
         Func := RE_Return_D;
         Atyp := RTE (RE_D);
      else pragma Assert (Digits_Value (Typ) = VAXGF_Digits);
         Func := RE_Return_G;
         Atyp := RTE (RE_G);
      end if;

      Args := New_List (Convert_To (Atyp, N));

      Rewrite (N,
        Convert_To (Rtyp,
          Make_Function_Call (Loc,
            Name                   => New_Occurrence_Of (RTE (Func), Loc),
            Parameter_Associations => Args)));

      Analyze_And_Resolve (N, Typ, Suppress => All_Checks);
   end Expand_Vax_Foreign_Return;

   -----------------------------
   -- Expand_Vax_Real_Literal --
   -----------------------------

   procedure Expand_Vax_Real_Literal (N : Node_Id) is
      Loc  : constant Source_Ptr := Sloc (N);
      Typ  : constant Entity_Id  := Etype (N);
      Btyp : constant Entity_Id  := Base_Type (Typ);
      Stat : constant Boolean    := Is_Static_Expression (N);
      Nod  : Node_Id;

      RE_Source : RE_Id;
      RE_Target : RE_Id;
      RE_Fncall : RE_Id;
      --  Entities for source, target and function call in conversion

   begin
      --  We do not know how to convert Vax format real literals, so what
      --  we do is to convert these to be IEEE literals, and introduce the
      --  necessary conversion operation.

      if Vax_Float (Btyp) then
         --  What we want to construct here is

         --    x!(y_to_z (1.0E0))

         --  where

         --    x is the base type of the literal (Btyp)

         --    y_to_z is

         --      s_to_f for F_Float
         --      t_to_g for G_Float
         --      t_to_d for D_Float

         --  The literal is typed as S (for F_Float) or T otherwise

         --  We do all our own construction, analysis, and expansion here,
         --  since things are at too low a level to use Analyze or Expand
         --  to get this built (we get circularities and other strange
         --  problems if we try!)

         if Digits_Value (Btyp) = VAXFF_Digits then
            RE_Source := RE_S;
            RE_Target := RE_F;
            RE_Fncall := RE_S_To_F;

         elsif Digits_Value (Btyp) = VAXDF_Digits then
            RE_Source := RE_T;
            RE_Target := RE_D;
            RE_Fncall := RE_T_To_D;

         else pragma Assert (Digits_Value (Btyp) = VAXGF_Digits);
            RE_Source := RE_T;
            RE_Target := RE_G;
            RE_Fncall := RE_T_To_G;
         end if;

         Nod := Relocate_Node (N);

         Set_Etype (Nod, RTE (RE_Source));
         Set_Analyzed (Nod, True);

         Nod :=
           Make_Function_Call (Loc,
             Name => New_Occurrence_Of (RTE (RE_Fncall), Loc),
             Parameter_Associations => New_List (Nod));

         Set_Etype (Nod, RTE (RE_Target));
         Set_Analyzed (Nod, True);

         Nod :=
           Make_Unchecked_Type_Conversion (Loc,
             Subtype_Mark => New_Occurrence_Of (Typ, Loc),
             Expression   => Nod);

         Set_Etype (Nod, Typ);
         Set_Analyzed (Nod, True);
         Rewrite (N, Nod);

         --  This odd expression is still a static expression. Note that
         --  the routine Sem_Eval.Expr_Value_R understands this.

         Set_Is_Static_Expression (N, Stat);
      end if;
   end Expand_Vax_Real_Literal;

   ----------------------
   -- Expand_Vax_Valid --
   ----------------------

   procedure Expand_Vax_Valid (N : Node_Id) is
      Loc  : constant Source_Ptr := Sloc (N);
      Pref : constant Node_Id    := Prefix (N);
      Ptyp : constant Entity_Id  := Root_Type (Etype (Pref));
      Rtyp : constant Entity_Id  := Etype (N);
      Vtyp : RE_Id;
      Func : RE_Id;

   begin
      if Digits_Value (Ptyp) = VAXFF_Digits then
         Func := RE_Valid_F;
         Vtyp := RE_F;
      elsif Digits_Value (Ptyp) = VAXDF_Digits then
         Func := RE_Valid_D;
         Vtyp := RE_D;
      else pragma Assert (Digits_Value (Ptyp) = VAXGF_Digits);
         Func := RE_Valid_G;
         Vtyp := RE_G;
      end if;

      Rewrite (N,
        Convert_To (Rtyp,
          Make_Function_Call (Loc,
            Name                   => New_Occurrence_Of (RTE (Func), Loc),
            Parameter_Associations => New_List (
              Convert_To (RTE (Vtyp), Pref)))));

      Analyze_And_Resolve (N);
   end Expand_Vax_Valid;

end Exp_VFpt;
