From 415dddc81c99f37554902cbe0d838060b62a2548 Mon Sep 17 00:00:00 2001 From: Richard Kenner Date: Tue, 2 Oct 2001 10:57:59 -0400 Subject: [PATCH] New Language: Ada From-SVN: r45960 --- gcc/ada/table.adb | 345 +++ gcc/ada/table.ads | 225 ++ gcc/ada/targparm.adb | 228 ++ gcc/ada/targparm.ads | 288 +++ gcc/ada/targtyps.c | 226 ++ gcc/ada/tbuild.adb | 522 ++++ gcc/ada/tbuild.ads | 241 ++ gcc/ada/text_io.ads | 21 + gcc/ada/tracebak.c | 1177 +++++++++ gcc/ada/trans.c | 5428 ++++++++++++++++++++++++++++++++++++++++++ gcc/ada/tree_gen.adb | 63 + gcc/ada/tree_gen.ads | 31 + gcc/ada/tree_in.adb | 69 + gcc/ada/tree_in.ads | 46 + gcc/ada/tree_io.adb | 661 +++++ gcc/ada/tree_io.ads | 107 + gcc/ada/treepr.adb | 1873 +++++++++++++++ gcc/ada/treepr.ads | 79 + gcc/ada/treeprs.ads | 795 +++++++ gcc/ada/treeprs.adt | 108 + gcc/ada/ttypef.ads | 207 ++ gcc/ada/ttypes.ads | 211 ++ gcc/ada/types.adb | 235 ++ gcc/ada/types.ads | 720 ++++++ gcc/ada/types.h | 335 +++ gcc/ada/uintp.adb | 2472 +++++++++++++++++++ gcc/ada/uintp.ads | 505 ++++ gcc/ada/uintp.h | 75 + gcc/ada/uname.adb | 653 +++++ gcc/ada/uname.ads | 176 ++ gcc/ada/unchconv.ads | 24 + gcc/ada/unchdeal.ads | 23 + gcc/ada/urealp.adb | 1472 ++++++++++++ gcc/ada/urealp.ads | 355 +++ gcc/ada/urealp.h | 50 + gcc/ada/usage.adb | 390 +++ gcc/ada/usage.ads | 31 + gcc/ada/utils.c | 3350 ++++++++++++++++++++++++++ gcc/ada/utils2.c | 2049 ++++++++++++++++ gcc/ada/validsw.adb | 222 ++ gcc/ada/validsw.ads | 146 ++ gcc/ada/widechar.adb | 163 ++ gcc/ada/widechar.ads | 87 + gcc/ada/xeinfo.adb | 539 +++++ gcc/ada/xnmake.adb | 485 ++++ gcc/ada/xr_tabls.adb | 1376 +++++++++++ gcc/ada/xr_tabls.ads | 384 +++ gcc/ada/xref_lib.adb | 1676 +++++++++++++ gcc/ada/xref_lib.ads | 205 ++ gcc/ada/xsinfo.adb | 261 ++ gcc/ada/xtreeprs.adb | 383 +++ 51 files changed, 31793 insertions(+) create mode 100644 gcc/ada/table.adb create mode 100644 gcc/ada/table.ads create mode 100644 gcc/ada/targparm.adb create mode 100644 gcc/ada/targparm.ads create mode 100644 gcc/ada/targtyps.c create mode 100644 gcc/ada/tbuild.adb create mode 100644 gcc/ada/tbuild.ads create mode 100644 gcc/ada/text_io.ads create mode 100644 gcc/ada/tracebak.c create mode 100644 gcc/ada/trans.c create mode 100644 gcc/ada/tree_gen.adb create mode 100644 gcc/ada/tree_gen.ads create mode 100644 gcc/ada/tree_in.adb create mode 100644 gcc/ada/tree_in.ads create mode 100644 gcc/ada/tree_io.adb create mode 100644 gcc/ada/tree_io.ads create mode 100644 gcc/ada/treepr.adb create mode 100644 gcc/ada/treepr.ads create mode 100644 gcc/ada/treeprs.ads create mode 100644 gcc/ada/treeprs.adt create mode 100644 gcc/ada/ttypef.ads create mode 100644 gcc/ada/ttypes.ads create mode 100644 gcc/ada/types.adb create mode 100644 gcc/ada/types.ads create mode 100644 gcc/ada/types.h create mode 100644 gcc/ada/uintp.adb create mode 100644 gcc/ada/uintp.ads create mode 100644 gcc/ada/uintp.h create mode 100644 gcc/ada/uname.adb create mode 100644 gcc/ada/uname.ads create mode 100644 gcc/ada/unchconv.ads create mode 100644 gcc/ada/unchdeal.ads create mode 100644 gcc/ada/urealp.adb create mode 100644 gcc/ada/urealp.ads create mode 100644 gcc/ada/urealp.h create mode 100644 gcc/ada/usage.adb create mode 100644 gcc/ada/usage.ads create mode 100644 gcc/ada/utils.c create mode 100644 gcc/ada/utils2.c create mode 100644 gcc/ada/validsw.adb create mode 100644 gcc/ada/validsw.ads create mode 100644 gcc/ada/widechar.adb create mode 100644 gcc/ada/widechar.ads create mode 100644 gcc/ada/xeinfo.adb create mode 100644 gcc/ada/xnmake.adb create mode 100644 gcc/ada/xr_tabls.adb create mode 100644 gcc/ada/xr_tabls.ads create mode 100644 gcc/ada/xref_lib.adb create mode 100644 gcc/ada/xref_lib.ads create mode 100644 gcc/ada/xsinfo.adb create mode 100644 gcc/ada/xtreeprs.adb diff --git a/gcc/ada/table.adb b/gcc/ada/table.adb new file mode 100644 index 00000000000..95da3a7e355 --- /dev/null +++ b/gcc/ada/table.adb @@ -0,0 +1,345 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T A B L E -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.44 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Debug; use Debug; +with Opt; +with Output; use Output; +with System; use System; +with Tree_IO; use Tree_IO; + +package body Table is + package body Table is + + Min : constant Int := Int (Table_Low_Bound); + -- Subscript of the minimum entry in the currently allocated table + + Length : Int := 0; + -- Number of entries in currently allocated table. The value of zero + -- ensures that we initially allocate the table. + + procedure free (T : Table_Ptr); + pragma Import (C, free); + + ----------------------- + -- Local Subprograms -- + ----------------------- + + procedure Reallocate; + -- Reallocate the existing table according to the current value stored + -- in Max. Works correctly to do an initial allocation if the table + -- is currently null. + + function Tree_Get_Table_Address return Address; + -- Return Null_Address if the table length is zero, + -- Table (First)'Address if not. + + ------------ + -- Append -- + ------------ + + procedure Append (New_Val : Table_Component_Type) is + begin + Increment_Last; + Table (Table_Index_Type (Last_Val)) := New_Val; + end Append; + + -------------------- + -- Decrement_Last -- + -------------------- + + procedure Decrement_Last is + begin + Last_Val := Last_Val - 1; + end Decrement_Last; + + ---------- + -- Free -- + ---------- + + procedure Free is + begin + free (Table); + Table := null; + Length := 0; + end Free; + + -------------------- + -- Increment_Last -- + -------------------- + + procedure Increment_Last is + begin + Last_Val := Last_Val + 1; + + if Last_Val > Max then + Reallocate; + end if; + end Increment_Last; + + ---------- + -- Init -- + ---------- + + procedure Init is + Old_Length : Int := Length; + + begin + Last_Val := Min - 1; + Max := Min + (Table_Initial * Opt.Table_Factor) - 1; + Length := Max - Min + 1; + + -- If table is same size as before (happens when table is never + -- expanded which is a common case), then simply reuse it. Note + -- that this also means that an explicit Init call right after + -- the implicit one in the package body is harmless. + + if Old_Length = Length then + return; + + -- Otherwise we can use Reallocate to get a table of the right size. + -- Note that Reallocate works fine to allocate a table of the right + -- initial size when it is first allocated. + + else + Reallocate; + end if; + end Init; + + ---------- + -- Last -- + ---------- + + function Last return Table_Index_Type is + begin + return Table_Index_Type (Last_Val); + end Last; + + ---------------- + -- Reallocate -- + ---------------- + + procedure Reallocate is + + function realloc + (memblock : Table_Ptr; + size : size_t) + return Table_Ptr; + pragma Import (C, realloc); + + function malloc + (size : size_t) + return Table_Ptr; + pragma Import (C, malloc); + + New_Size : size_t; + + begin + if Max < Last_Val then + pragma Assert (not Locked); + + -- Make sure that we have at least the initial allocation. This + -- is needed in cases where a zero length table is written out. + + Length := Int'Max (Length, Table_Initial); + + -- Now increment table length until it is sufficiently large + + while Max < Last_Val loop + Length := Length * (100 + Table_Increment) / 100; + Max := Min + Length - 1; + end loop; + + if Debug_Flag_D then + Write_Str ("--> Allocating new "); + Write_Str (Table_Name); + Write_Str (" table, size = "); + Write_Int (Max - Min + 1); + Write_Eol; + end if; + end if; + + New_Size := + size_t ((Max - Min + 1) * + (Table_Type'Component_Size / Storage_Unit)); + + if Table = null then + Table := malloc (New_Size); + + elsif New_Size > 0 then + Table := + realloc + (memblock => Table, + size => New_Size); + end if; + + if Length /= 0 and then Table = null then + Set_Standard_Error; + Write_Str ("available memory exhausted"); + Write_Eol; + Set_Standard_Output; + raise Unrecoverable_Error; + end if; + + end Reallocate; + + ------------- + -- Release -- + ------------- + + procedure Release is + begin + Length := Last_Val - Int (Table_Low_Bound) + 1; + Max := Last_Val; + Reallocate; + end Release; + + ------------- + -- Restore -- + ------------- + + procedure Restore (T : Saved_Table) is + begin + free (Table); + Last_Val := T.Last_Val; + Max := T.Max; + Table := T.Table; + Length := Max - Min + 1; + end Restore; + + ---------- + -- Save -- + ---------- + + function Save return Saved_Table is + Res : Saved_Table; + + begin + Res.Last_Val := Last_Val; + Res.Max := Max; + Res.Table := Table; + + Table := null; + Length := 0; + Init; + return Res; + end Save; + + -------------- + -- Set_Item -- + -------------- + + procedure Set_Item + (Index : Table_Index_Type; + Item : Table_Component_Type) + is + begin + if Int (Index) > Max then + Set_Last (Index); + end if; + + Table (Index) := Item; + end Set_Item; + + -------------- + -- Set_Last -- + -------------- + + procedure Set_Last (New_Val : Table_Index_Type) is + begin + if Int (New_Val) < Last_Val then + Last_Val := Int (New_Val); + else + Last_Val := Int (New_Val); + + if Last_Val > Max then + Reallocate; + end if; + end if; + end Set_Last; + + ---------------------------- + -- Tree_Get_Table_Address -- + ---------------------------- + + function Tree_Get_Table_Address return Address is + begin + if Length = 0 then + return Null_Address; + else + return Table (First)'Address; + end if; + end Tree_Get_Table_Address; + + --------------- + -- Tree_Read -- + --------------- + + -- Note: we allocate only the space required to accomodate the data + -- actually written, which means that a Tree_Write/Tree_Read sequence + -- does an implicit Release. + + procedure Tree_Read is + begin + Tree_Read_Int (Max); + Last_Val := Max; + Length := Max - Min + 1; + Reallocate; + + Tree_Read_Data + (Tree_Get_Table_Address, + (Last_Val - Int (First) + 1) * + Table_Type'Component_Size / Storage_Unit); + end Tree_Read; + + ---------------- + -- Tree_Write -- + ---------------- + + -- Note: we write out only the currently valid data, not the entire + -- contents of the allocated array. See note above on Tree_Read. + + procedure Tree_Write is + begin + Tree_Write_Int (Int (Last)); + Tree_Write_Data + (Tree_Get_Table_Address, + (Last_Val - Int (First) + 1) * + Table_Type'Component_Size / Storage_Unit); + end Tree_Write; + + begin + Init; + end Table; +end Table; diff --git a/gcc/ada/table.ads b/gcc/ada/table.ads new file mode 100644 index 00000000000..4588e4d0e89 --- /dev/null +++ b/gcc/ada/table.ads @@ -0,0 +1,225 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T A B L E -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.38 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This package provides an implementation of dynamically resizable one +-- dimensional arrays. The idea is to mimic the normal Ada semantics for +-- arrays as closely as possible with the one additional capability of +-- dynamically modifying the value of the Last attribute. + +-- Note that this interface should remain synchronized with those in +-- GNAT.Table and GNAT.Dynamic_Tables to keep coherency between these +-- three related units. + +with Types; use Types; + +package Table is +pragma Elaborate_Body (Table); + + generic + type Table_Component_Type is private; + type Table_Index_Type is range <>; + + Table_Low_Bound : Table_Index_Type; + Table_Initial : Pos; + Table_Increment : Nat; + Table_Name : String; + + package Table is + + -- Table_Component_Type and Table_Index_Type specify the type of the + -- array, Table_Low_Bound is the lower bound. Index_type must be an + -- integer type. The effect is roughly to declare: + + -- Table : array (Table_Index_Type range Table_Low_Bound .. <>) + -- of Table_Component_Type; + + -- Note: since the upper bound can be one less than the lower + -- bound for an empty array, the table index type must be able + -- to cover this range, e.g. if the lower bound is 1, then the + -- Table_Index_Type should be Natural rather than Positive. + + -- Table_Component_Type may be any Ada type, except that controlled + -- types are not supported. Note however that default initialization + -- will NOT occur for array components. + + -- The Table_Initial values controls the allocation of the table when + -- it is first allocated, either by default, or by an explicit Init + -- call. The value used is Opt.Table_Factor * Table_Initial. + + -- The Table_Increment value controls the amount of increase, if the + -- table has to be increased in size. The value given is a percentage + -- value (e.g. 100 = increase table size by 100%, i.e. double it). + + -- The Table_Name parameter is simply use in debug output messages it + -- has no other usage, and is not referenced in non-debugging mode. + + -- The Last and Set_Last subprograms provide control over the current + -- logical allocation. They are quite efficient, so they can be used + -- freely (expensive reallocation occurs only at major granularity + -- chunks controlled by the allocation parameters). + + -- Note: we do not make the table components aliased, since this would + -- restict the use of table for discriminated types. If it is necessary + -- to take the access of a table element, use Unrestricted_Access. + + type Table_Type is + array (Table_Index_Type range <>) of Table_Component_Type; + + subtype Big_Table_Type is + Table_Type (Table_Low_Bound .. Table_Index_Type'Last); + -- We work with pointers to a bogus array type that is constrained + -- with the maximum possible range bound. This means that the pointer + -- is a thin pointer, which is more efficient. Since subscript checks + -- in any case must be on the logical, rather than physical bounds, + -- safety is not compromised by this approach. + + type Table_Ptr is access all Big_Table_Type; + -- The table is actually represented as a pointer to allow reallocation + + Table : aliased Table_Ptr := null; + -- The table itself. The lower bound is the value of Low_Bound. + -- Logically the upper bound is the current value of Last (although + -- the actual size of the allocated table may be larger than this). + -- The program may only access and modify Table entries in the range + -- First .. Last. + + Locked : Boolean := False; + -- Table expansion is permitted only if this switch is set to False. A + -- client may set Locked to True, in which case any attempt to expand + -- the table will cause an assertion failure. Note that while a table + -- is locked, its address in memory remains fixed and unchanging. This + -- feature is used to control table expansion during Gigi processing. + -- Gigi assumes that tables other than the Uint and Ureal tables do + -- not move during processing, which means that they cannot be expanded. + -- The Locked flag is used to enforce this restriction. + + procedure Init; + -- This procedure allocates a new table of size Initial (freeing any + -- previously allocated larger table). It is not necessary to call + -- Init when a table is first instantiated (since the instantiation does + -- the same initialization steps). However, it is harmless to do so, and + -- Init is convenient in reestablishing a table for new use. + + function Last return Table_Index_Type; + pragma Inline (Last); + -- Returns the current value of the last used entry in the table, which + -- can then be used as a subscript for Table. Note that the only way to + -- modify Last is to call the Set_Last procedure. Last must always be + -- used to determine the logically last entry. + + procedure Release; + -- Storage is allocated in chunks according to the values given in the + -- Initial and Increment parameters. A call to Release releases all + -- storage that is allocated, but is not logically part of the current + -- array value. Current array values are not affected by this call. + + procedure Free; + -- Free all allocated memory for the table. A call to init is required + -- before any use of this table after calling Free. + + First : constant Table_Index_Type := Table_Low_Bound; + -- Export First as synonym for Low_Bound (parallel with use of Last) + + procedure Set_Last (New_Val : Table_Index_Type); + pragma Inline (Set_Last); + -- This procedure sets Last to the indicated value. If necessary the + -- table is reallocated to accomodate the new value (i.e. on return + -- the allocated table has an upper bound of at least Last). If Set_Last + -- reduces the size of the table, then logically entries are removed + -- from the table. If Set_Last increases the size of the table, then + -- new entries are logically added to the table. + + procedure Increment_Last; + pragma Inline (Increment_Last); + -- Adds 1 to Last (same as Set_Last (Last + 1). + + procedure Decrement_Last; + pragma Inline (Decrement_Last); + -- Subtracts 1 from Last (same as Set_Last (Last - 1). + + procedure Append (New_Val : Table_Component_Type); + pragma Inline (Append); + -- Equivalent to: + -- x.Increment_Last; + -- x.Table (x.Last) := New_Val; + -- i.e. the table size is increased by one, and the given new item + -- stored in the newly created table element. + + procedure Set_Item + (Index : Table_Index_Type; + Item : Table_Component_Type); + pragma Inline (Set_Item); + -- Put Item in the table at position Index. The table is expanded if + -- current table length is less than Index and in that case Last is set + -- to Index. Item will replace any value already present in the table + -- at this position. + + type Saved_Table is private; + -- Type used for Save/Restore subprograms + + function Save return Saved_Table; + -- Resets table to empty, but saves old contents of table in returned + -- value, for possible later restoration by a call to Restore. + + procedure Restore (T : Saved_Table); + -- Given a Saved_Table value returned by a prior call to Save, restores + -- the table to the state it was in at the time of the Save call. + + procedure Tree_Write; + -- Writes out contents of table using Tree_IO + + procedure Tree_Read; + -- Initializes table by reading contents previously written + -- with the Tree_Write call (also using Tree_IO) + + private + + Last_Val : Int; + -- Current value of Last. Note that we declare this in the private part + -- because we don't want the client to modify Last except through one of + -- the official interfaces (since a modification to Last may require a + -- reallocation of the table). + + Max : Int; + -- Subscript of the maximum entry in the currently allocated table + + type Saved_Table is record + Last_Val : Int; + Max : Int; + Table : Table_Ptr; + end record; + + end Table; +end Table; diff --git a/gcc/ada/targparm.adb b/gcc/ada/targparm.adb new file mode 100644 index 00000000000..9e823d89971 --- /dev/null +++ b/gcc/ada/targparm.adb @@ -0,0 +1,228 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT RUN-TIME COMPONENTS -- +-- -- +-- T A R G P A R M -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.15 $ +-- -- +-- Copyright (C) 1999-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Namet; use Namet; +with Output; use Output; +with Sinput; use Sinput; +with Sinput.L; use Sinput.L; +with Fname.UF; use Fname.UF; +with Types; use Types; + +package body Targparm is + + type Targparm_Tags is + (AAM, CLA, DEN, DSP, FEL, HIM, LSI, MOV, + MRN, SCD, SCP, SNZ, UAM, VMS, ZCD, ZCG, ZCF); + + Targparm_Flags : array (Targparm_Tags) of Boolean := (others => False); + -- Flag is set True if corresponding parameter is scanned + + AAM_Str : aliased constant Source_Buffer := "AAMP"; + CLA_Str : aliased constant Source_Buffer := "Command_Line_Args"; + DEN_Str : aliased constant Source_Buffer := "Denorm"; + DSP_Str : aliased constant Source_Buffer := "Functions_Return_By_DSP"; + FEL_Str : aliased constant Source_Buffer := "Frontend_Layout"; + HIM_Str : aliased constant Source_Buffer := "High_Integrity_Mode"; + LSI_Str : aliased constant Source_Buffer := "Long_Shifts_Inlined"; + MOV_Str : aliased constant Source_Buffer := "Machine_Overflows"; + MRN_Str : aliased constant Source_Buffer := "Machine_Rounds"; + SCD_Str : aliased constant Source_Buffer := "Stack_Check_Default"; + SCP_Str : aliased constant Source_Buffer := "Stack_Check_Probes"; + SNZ_Str : aliased constant Source_Buffer := "Signed_Zeros"; + UAM_Str : aliased constant Source_Buffer := "Use_Ada_Main_Program_Name"; + VMS_Str : aliased constant Source_Buffer := "OpenVMS"; + ZCD_Str : aliased constant Source_Buffer := "ZCX_By_Default"; + ZCG_Str : aliased constant Source_Buffer := "GCC_ZCX_Support"; + ZCF_Str : aliased constant Source_Buffer := "Front_End_ZCX_Support"; + + type Buffer_Ptr is access constant Source_Buffer; + Targparm_Str : array (Targparm_Tags) of Buffer_Ptr := + (AAM_Str'Access, + CLA_Str'Access, + DEN_Str'Access, + DSP_Str'Access, + FEL_Str'Access, + HIM_Str'Access, + LSI_Str'Access, + MOV_Str'Access, + MRN_Str'Access, + SCD_Str'Access, + SCP_Str'Access, + SNZ_Str'Access, + UAM_Str'Access, + VMS_Str'Access, + ZCD_Str'Access, + ZCG_Str'Access, + ZCF_Str'Access); + + --------------------------- + -- Get_Target_Parameters -- + --------------------------- + + procedure Get_Target_Parameters is + use ASCII; + + S : Source_File_Index; + N : Name_Id; + T : Source_Buffer_Ptr; + P : Source_Ptr; + Z : Source_Ptr; + + Fatal : Boolean := False; + -- Set True if a fatal error is detected + + Result : Boolean; + -- Records boolean from system line + + begin + Name_Buffer (1 .. 6) := "system"; + Name_Len := 6; + N := File_Name_Of_Spec (Name_Find); + S := Load_Source_File (N); + + if S = No_Source_File then + Write_Line ("fatal error, run-time library not installed correctly"); + Write_Str ("cannot locate file "); + Write_Line (Name_Buffer (1 .. Name_Len)); + raise Unrecoverable_Error; + + -- This must always be the first source file read, and we have defined + -- a constant Types.System_Source_File_Index as 1 to reflect this. + + else + pragma Assert (S = System_Source_File_Index); + null; + end if; + + P := Source_First (S); + Z := Source_Last (S); + T := Source_Text (S); + + while T (P .. P + 10) /= "end System;" loop + + for K in Targparm_Tags loop + if T (P + 3 .. P + 2 + Targparm_Str (K)'Length) = + Targparm_Str (K).all + then + P := P + 3 + Targparm_Str (K)'Length; + + if Targparm_Flags (K) then + Set_Standard_Error; + Write_Line + ("fatal error: system.ads is incorrectly formatted"); + Write_Str ("duplicate line for parameter: "); + + for J in Targparm_Str (K)'Range loop + Write_Char (Targparm_Str (K).all (J)); + end loop; + + Write_Eol; + Set_Standard_Output; + Fatal := True; + + else + Targparm_Flags (K) := True; + end if; + + while T (P) /= ':' or else T (P + 1) /= '=' loop + P := P + 1; + end loop; + + P := P + 2; + + while T (P) = ' ' loop + P := P + 1; + end loop; + + Result := (T (P) = 'T'); + + case K is + when AAM => AAMP_On_Target := Result; + when CLA => Command_Line_Args_On_Target := Result; + when DEN => Denorm_On_Target := Result; + when DSP => Functions_Return_By_DSP_On_Target := Result; + when FEL => Frontend_Layout_On_Target := Result; + when HIM => High_Integrity_Mode_On_Target := Result; + when LSI => Long_Shifts_Inlined_On_Target := Result; + when MOV => Machine_Overflows_On_Target := Result; + when MRN => Machine_Rounds_On_Target := Result; + when SCD => Stack_Check_Default_On_Target := Result; + when SCP => Stack_Check_Probes_On_Target := Result; + when SNZ => Signed_Zeros_On_Target := Result; + when UAM => Use_Ada_Main_Program_Name_On_Target := Result; + when VMS => OpenVMS_On_Target := Result; + when ZCD => ZCX_By_Default_On_Target := Result; + when ZCG => GCC_ZCX_Support_On_Target := Result; + when ZCF => Front_End_ZCX_Support_On_Target := Result; + end case; + + exit; + end if; + end loop; + + while T (P) /= CR and then T (P) /= LF loop + P := P + 1; + exit when P >= Z; + end loop; + + while T (P) = CR or else T (P) = LF loop + P := P + 1; + exit when P >= Z; + end loop; + + if P >= Z then + Set_Standard_Error; + Write_Line ("fatal error, system.ads not formatted correctly"); + Set_Standard_Output; + raise Unrecoverable_Error; + end if; + end loop; + + for K in Targparm_Tags loop + if not Targparm_Flags (K) then + Set_Standard_Error; + Write_Line + ("fatal error: system.ads is incorrectly formatted"); + Write_Str ("missing line for parameter: "); + + for J in Targparm_Str (K)'Range loop + Write_Char (Targparm_Str (K).all (J)); + end loop; + + Write_Eol; + Set_Standard_Output; + Fatal := True; + end if; + end loop; + + if Fatal then + raise Unrecoverable_Error; + end if; + end Get_Target_Parameters; + +end Targparm; diff --git a/gcc/ada/targparm.ads b/gcc/ada/targparm.ads new file mode 100644 index 00000000000..2346fd209e7 --- /dev/null +++ b/gcc/ada/targparm.ads @@ -0,0 +1,288 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT RUN-TIME COMPONENTS -- +-- -- +-- T A R G P A R M -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.13 $ +-- -- +-- Copyright (C) 1999-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This package obtains parameters from the target runtime version of +-- System, to indicate parameters relevant to the target environment. + +-- Conceptually, these parameters could be obtained using rtsfind, but +-- we do not do this for three reasons: + +-- 1. Compiling System for every compilation wastes time +-- 2. This compilation impedes debugging by adding extra compile steps +-- 3. There are recursion problems coming from compiling System itself +-- or any of its children. + +-- For all these reasons, we read in the source of System, and then scan +-- it at the text level to extract the parameter values. + +-- Note however, that later on, when the ali file is written, we make sure +-- that the System file is at least parsed, so that the checksum is properly +-- computed and set in the ali file. This partially negates points 1 and 2 +-- above although just parsing is quick and does not impact debugging much. + +package Targparm is + + -- The following parameters correspond to the variables defined in the + -- private part of System (without the terminating _On_Target). Note + -- that it is required that all parameters be specified in system.ads. + + ----------------------------------- + -- Control of Exception Handling -- + ----------------------------------- + + -- GNAT provides two methods of implementing exceptions: + + -- Longjmp/Setjmp (-gnatL) + + -- This approach uses longjmp/setjmp to handle exceptions. It + -- uses less storage, and can often propagate exceptions faster, + -- at the expense of (sometimes considerable) overhead in setting + -- up an exception handler. This approach is available on all + -- targets, and is the default where it is the only approach. + + -- Zero Cost (-gnatZ) + + -- This approach uses separate exception tables. These use extra + -- storage, and exception propagation can be quite slow, but there + -- is no overhead in setting up an exception handler (it is to this + -- latter operation that the phrase zero-cost refers). This approach + -- is only available on some targets, and is the default where it is + -- available. + + ZCX_By_Default_On_Target : Boolean; + -- Indicates if zero cost exceptions are active by default. Can be modified + -- by the use of -gnatZ and -gnatL switches. + + GCC_ZCX_Support_On_Target : Boolean; + -- Indicates that when ZCX is active the mechanism to be used is the + -- standard GCC ZCX mechanism (introduced in GCC 3.1) + + Front_End_ZCX_Support_On_Target : Boolean; + -- Indicates that when ZCX is active (and GCC_ZCX_Support is not set) + -- the mechanism to be used is the GNAT front end specific ZCX mechanism + + --------------------------------------- + -- High_Integrity (No Run Time) Mode -- + --------------------------------------- + + -- In High_Integrity mode, there is no system run-time, and the flag + -- Opt.No_Run_Time is set so that the language is appropriately + -- restricted to forbid construct that would generate run-time calls. + + High_Integrity_Mode_On_Target : Boolean; + -- Indicates that this build is for a high integrity mode version of + -- GNAT, so that no run time is permitted. + + ------------------------------- + -- Control of Stack Checking -- + ------------------------------- + + -- GNAT provides two methods of implementing exceptions: + + -- GCC Probing Mechanism + + -- This approach uses the standard GCC mechanism for + -- stack checking. The method assumes that accessing + -- storage immediately beyond the end of the stack + -- will result in a trap that is converted to a storage + -- error by the runtime system. This mechanism has + -- minimal overhead, but requires complex hardware, + -- operating system and run-time support. Probing is + -- the default method where it is available. The stack + -- size for the environment task depends on the operating + -- system and cannot be set in a system-independent way. + + -- GNAT Stack-limit Checking + + -- This method relies on comparing the stack pointer + -- with per-task stack limits. If the check fails, an + -- exception is explicitly raised. The advantage is + -- that the method requires no extra system dependent + -- runtime support and can be used on systems without + -- memory protection as well, but at the cost of more + -- overhead for doing the check. This method is the + -- default on systems that lack complete support for + -- probing. + + Stack_Check_Probes_On_Target : Boolean; + -- Indicates if stack check probes are used, as opposed to the standard + -- target independent comparison method. + + Stack_Check_Default_On_Target : Boolean; + -- Indicates if stack checking is on by default + + ---------------------------- + -- Command Line Arguments -- + ---------------------------- + + -- For most ports of GNAT, command line arguments are supported. The + -- following flag is set to False for targets that do not support + -- command line arguments (notably VxWorks). + + Command_Line_Args_On_Target : Boolean; + -- Set False if no command line arguments on target + + -- Note: this is prepared for future use, but not yet used, since we + -- do not yet have a way of propagating Targparm params to the binder + + ----------------------- + -- Main Program Name -- + ----------------------- + + -- When the binder generates the main program to be used to create the + -- executable, the main program name is main by default (to match the + -- usual Unix practice). If this parameter is set to True, then the + -- name is instead by default taken from the actual Ada main program + -- name (just the name of the child if the main program is a child unit). + -- In either case, this value can be overridden using -M name. + + Use_Ada_Main_Program_Name_On_Target : Boolean; + -- Set True to use the Ada main program name as the main name + + -- Note: this is prepared for future use, but not yet used, since we + -- do not yet have a way of propagating Targparm params to the binder + + ---------------------------- + -- Support of Long Shifts -- + ---------------------------- + + -- In GNORT mode, we cannot call library routines, and in particular + -- we cannot call routines for long (64-bit) shifts if such routines + -- are required on the target. This comes up in the context of support + -- of packed arrays. We can only represent packed arrays whose length + -- is in the range 33- to 64-bits as modular types if long shifts are + -- done with inline code. + + -- For the default version, for now we set long shifts inlined as True + -- This may not be quite accurate, but until we get proper separate + -- System's for each target, it is a safer choice. + + Long_Shifts_Inlined_On_Target : Boolean; + -- Indicates if long (double word) shifts are generated using inlined + -- code (and thus are permissible in No_Run_Time mode). + + ---------------------------------------------- + -- Boolean-Valued Floating-Point Attributes -- + ---------------------------------------------- + + -- The constants below give the values for representation oriented + -- floating-point attributes that are the same for all float types + -- on the target. These are all boolean values. + + -- A value is only True if the target reliably supports the corresponding + -- feature. Reliably here means that support is guaranteed for all + -- possible settings of the relevant compiler switches (like -mieee), + -- since we cannot control the user setting of those switches. + + -- The attributes cannot dependent on the current setting of compiler + -- switches, since the values must be static and consistent throughout + -- the partition. We probably should add such consistency checks in future, + -- but for now we don't do this. + + AAMP_On_Target : Boolean; + -- Set to True if target is AAMP. + + Denorm_On_Target : Boolean; + -- Set to False on targets that do not reliably support denormals. + -- Reliably here means for all settings of the relevant -m flag, so + -- for example, this is False on the Alpha where denormals are not + -- supported unless -mieee is used. + + Machine_Rounds_On_Target : Boolean; + -- Set to False for targets where S'Machine_Rounds is False + + Machine_Overflows_On_Target : Boolean; + -- Set to True for targets where S'Machine_Overflows is True + + Signed_Zeros_On_Target : Boolean; + -- Set to False on targets that do not reliably support signed zeros. + + OpenVMS_On_Target : Boolean; + -- Set to True if target is OpenVMS. + + -------------------------------------------------------------- + -- Handling of Unconstrained Values Returned from Functions -- + -------------------------------------------------------------- + + -- Functions that return variable length objects, notably unconstrained + -- arrays are a special case, because there is no simple obvious way of + -- implementing this feature. Furthermore, this capability is not present + -- in C++ or C, so typically the system ABI does not handle this case. + + -- GNAT uses two different approaches + + -- The Secondary Stack + + -- The secondary stack is a special storage pool that is used for + -- this purpose. The called function places the result on the + -- secondary stack, and the caller uses or copies the value from + -- the secondary stack, and pops the secondary stack after the + -- value is consumed. The secondary stack is outside the system + -- ABI, and the important point is that although generally it is + -- handled in a stack like manner corresponding to the subprogram + -- call structure, a return from a function does NOT pop the stack. + + -- DSP (Depressed Stack Pointer) + + -- Some targets permit the implementation of a function call/return + -- protocol in which the function does not pop the main stack pointer + -- on return, but rather returns with the stack pointer depressed. + -- This is not generally permitted by any ABI, but for at least some + -- targets, the implementation of alloca provides a model for this + -- approach. If return-with-DSP is implemented, then functions that + -- return variable length objects do it by returning with the stack + -- pointer depressed, and the returned object is a pointer to the + -- area within the stack frame of the called procedure that contains + -- the returned value. The caller must then pop the main stack when + -- this value is consumed. + + Functions_Return_By_DSP_On_Target : Boolean; + -- Set to True if target permits functions to return with using the + -- DSP (depressed stack pointer) approach. + + ----------------- + -- Data Layout -- + ----------------- + + -- Normally when using the GCC backend, Gigi and GCC perform much of the + -- data layout using the standard layout capabilities of GCC. If the + -- parameter Backend_Layout is set to False, then the front end must + -- perform all data layout. For further details see the package Layout. + + Frontend_Layout_On_Target : Boolean; + -- Set True if front end does layout + + ----------------- + -- Subprograms -- + ----------------- + + procedure Get_Target_Parameters; + -- Called at the start of execution to read the source of System and + -- obtain and set the values of the above parameters. + +end Targparm; diff --git a/gcc/ada/targtyps.c b/gcc/ada/targtyps.c new file mode 100644 index 00000000000..900762b2e1e --- /dev/null +++ b/gcc/ada/targtyps.c @@ -0,0 +1,226 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * T A R G T Y P S * + * * + * Body * + * * + * $Revision: 1.1 $ + * * + * Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * As a special exception, if you link this file with other files to * + * produce an executable, this file does not by itself cause the resulting * + * executable to be covered by the GNU General Public License. This except- * + * ion does not however invalidate any other reasons why the executable * + * file might be covered by the GNU Public License. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +/* Functions for retrieving target types. See Ada package Get_Targ */ + +#include "config.h" +#include "system.h" +#include "tree.h" +#include "real.h" +#include "rtl.h" +#include "ada.h" +#include "types.h" +#include "atree.h" +#include "elists.h" +#include "namet.h" +#include "nlists.h" +#include "snames.h" +#include "stringt.h" +#include "uintp.h" +#include "urealp.h" +#include "fe.h" +#include "sinfo.h" +#include "einfo.h" +#include "ada-tree.h" +#include "gigi.h" + +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/* Standard data type sizes. Most of these are not used. */ + +#ifndef CHAR_TYPE_SIZE +#define CHAR_TYPE_SIZE BITS_PER_UNIT +#endif + +#ifndef SHORT_TYPE_SIZE +#define SHORT_TYPE_SIZE (BITS_PER_UNIT * MIN ((UNITS_PER_WORD + 1) / 2, 2)) +#endif + +#ifndef INT_TYPE_SIZE +#define INT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifdef OPEN_VMS /* A target macro defined in vms.h */ +#define LONG_TYPE_SIZE 64 +#else +#ifndef LONG_TYPE_SIZE +#define LONG_TYPE_SIZE BITS_PER_WORD +#endif +#endif + +#ifndef LONG_LONG_TYPE_SIZE +#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef FLOAT_TYPE_SIZE +#define FLOAT_TYPE_SIZE BITS_PER_WORD +#endif + +#ifndef DOUBLE_TYPE_SIZE +#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef LONG_DOUBLE_TYPE_SIZE +#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2) +#endif + +#ifndef WIDEST_HARDWARE_FP_SIZE +#define WIDEST_HARDWARE_FP_SIZE LONG_DOUBLE_TYPE_SIZE +#endif + +/* The following provide a functional interface for the front end Ada code + to determine the sizes that are used for various C types. */ + +Pos +get_target_bits_per_unit () +{ + return BITS_PER_UNIT; +} + +Pos +get_target_bits_per_word () +{ + return BITS_PER_WORD; +} + +Pos +get_target_char_size () +{ + return CHAR_TYPE_SIZE; +} + +Pos +get_target_wchar_t_size () +{ + /* We never want wide chacters less than "short" in Ada. */ + return MAX (SHORT_TYPE_SIZE, WCHAR_TYPE_SIZE); +} + +Pos +get_target_short_size () +{ + return SHORT_TYPE_SIZE; +} + +Pos +get_target_int_size () +{ + return INT_TYPE_SIZE; +} + +Pos +get_target_long_size () +{ + return LONG_TYPE_SIZE; +} + +Pos +get_target_long_long_size () +{ + return LONG_LONG_TYPE_SIZE; +} + +Pos +get_target_float_size () +{ + return FLOAT_TYPE_SIZE; +} + +Pos +get_target_double_size () +{ + return DOUBLE_TYPE_SIZE; +} + +Pos +get_target_long_double_size () +{ + return WIDEST_HARDWARE_FP_SIZE; +} + +Pos +get_target_pointer_size () +{ + return POINTER_SIZE; +} + +Pos +get_target_maximum_alignment () +{ + return BIGGEST_ALIGNMENT / BITS_PER_UNIT; +} + +Boolean +get_target_no_dollar_in_label () +{ +#ifdef NO_DOLLAR_IN_LABEL + return 1; +#else + return 0; +#endif +} + +#ifndef FLOAT_WORDS_BIG_ENDIAN +#define FLOAT_WORDS_BIG_ENDIAN WORDS_BIG_ENDIAN +#endif + +Nat +get_float_words_be () +{ + return FLOAT_WORDS_BIG_ENDIAN; +} + +Nat +get_words_be () +{ + return WORDS_BIG_ENDIAN; +} + +Nat +get_bytes_be () +{ + return BYTES_BIG_ENDIAN; +} + +Nat +get_bits_be () +{ + return BITS_BIG_ENDIAN; +} + +Nat +get_strict_alignment () +{ + return STRICT_ALIGNMENT; +} diff --git a/gcc/ada/tbuild.adb b/gcc/ada/tbuild.adb new file mode 100644 index 00000000000..3ccd7a7472e --- /dev/null +++ b/gcc/ada/tbuild.adb @@ -0,0 +1,522 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T B U I L D -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.98 $ +-- -- +-- Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Atree; use Atree; +with Einfo; use Einfo; +with Lib; use Lib; +with Namet; use Namet; +with Nlists; use Nlists; +with Nmake; use Nmake; +with Restrict; use Restrict; +with Sinfo; use Sinfo; +with Snames; use Snames; +with Stand; use Stand; +with Uintp; use Uintp; + +package body Tbuild is + + ----------------------- + -- Local Subprograms -- + ----------------------- + + procedure Add_Unique_Serial_Number; + -- Add a unique serialization to the string in the Name_Buffer. This + -- consists of a unit specific serial number, and b/s for body/spec. + + ------------------------------ + -- Add_Unique_Serial_Number -- + ------------------------------ + + procedure Add_Unique_Serial_Number is + Unit_Node : constant Node_Id := Unit (Cunit (Current_Sem_Unit)); + + begin + Add_Nat_To_Name_Buffer (Increment_Serial_Number); + + -- Add either b or s, depending on whether current unit is a spec + -- or a body. This is needed because we may generate the same name + -- in a spec and a body otherwise. + + Name_Len := Name_Len + 1; + + if Nkind (Unit_Node) = N_Package_Declaration + or else Nkind (Unit_Node) = N_Subprogram_Declaration + or else Nkind (Unit_Node) in N_Generic_Declaration + then + Name_Buffer (Name_Len) := 's'; + else + Name_Buffer (Name_Len) := 'b'; + end if; + end Add_Unique_Serial_Number; + + ---------------- + -- Checks_Off -- + ---------------- + + function Checks_Off (N : Node_Id) return Node_Id is + begin + return + Make_Unchecked_Expression (Sloc (N), + Expression => N); + end Checks_Off; + + ---------------- + -- Convert_To -- + ---------------- + + function Convert_To (Typ : Entity_Id; Expr : Node_Id) return Node_Id is + Result : Node_Id; + + begin + if Present (Etype (Expr)) + and then (Etype (Expr)) = Typ + then + return Relocate_Node (Expr); + else + Result := + Make_Type_Conversion (Sloc (Expr), + Subtype_Mark => New_Occurrence_Of (Typ, Sloc (Expr)), + Expression => Relocate_Node (Expr)); + + Set_Etype (Result, Typ); + return Result; + end if; + end Convert_To; + + -------------------- + -- Make_DT_Access -- + -------------------- + + function Make_DT_Access + (Loc : Source_Ptr; + Rec : Node_Id; + Typ : Entity_Id) + return Node_Id + is + Full_Type : Entity_Id := Typ; + + begin + if Is_Private_Type (Typ) then + Full_Type := Underlying_Type (Typ); + end if; + + return + Unchecked_Convert_To ( + New_Occurrence_Of (Etype (Access_Disp_Table (Full_Type)), Loc), + Make_Selected_Component (Loc, + Prefix => New_Copy (Rec), + Selector_Name => + New_Reference_To (Tag_Component (Full_Type), Loc))); + end Make_DT_Access; + + ----------------------- + -- Make_DT_Component -- + ----------------------- + + function Make_DT_Component + (Loc : Source_Ptr; + Typ : Entity_Id; + I : Positive) + return Node_Id + is + X : Node_Id; + Full_Type : Entity_Id := Typ; + + begin + if Is_Private_Type (Typ) then + Full_Type := Underlying_Type (Typ); + end if; + + X := First_Component ( + Designated_Type (Etype (Access_Disp_Table (Full_Type)))); + + for J in 2 .. I loop + X := Next_Component (X); + end loop; + + return New_Reference_To (X, Loc); + end Make_DT_Component; + + -------------------------------- + -- Make_Implicit_If_Statement -- + -------------------------------- + + function Make_Implicit_If_Statement + (Node : Node_Id; + Condition : Node_Id; + Then_Statements : List_Id; + Elsif_Parts : List_Id := No_List; + Else_Statements : List_Id := No_List) + return Node_Id + is + begin + Check_Restriction (No_Implicit_Conditionals, Node); + return Make_If_Statement (Sloc (Node), + Condition, + Then_Statements, + Elsif_Parts, + Else_Statements); + end Make_Implicit_If_Statement; + + ------------------------------------- + -- Make_Implicit_Label_Declaration -- + ------------------------------------- + + function Make_Implicit_Label_Declaration + (Loc : Source_Ptr; + Defining_Identifier : Node_Id; + Label_Construct : Node_Id) + return Node_Id + is + N : constant Node_Id := + Make_Implicit_Label_Declaration (Loc, Defining_Identifier); + + begin + Set_Label_Construct (N, Label_Construct); + return N; + end Make_Implicit_Label_Declaration; + + ---------------------------------- + -- Make_Implicit_Loop_Statement -- + ---------------------------------- + + function Make_Implicit_Loop_Statement + (Node : Node_Id; + Statements : List_Id; + Identifier : Node_Id := Empty; + Iteration_Scheme : Node_Id := Empty; + Has_Created_Identifier : Boolean := False; + End_Label : Node_Id := Empty) + return Node_Id + is + begin + Check_Restriction (No_Implicit_Loops, Node); + + if Present (Iteration_Scheme) + and then Present (Condition (Iteration_Scheme)) + then + Check_Restriction (No_Implicit_Conditionals, Node); + end if; + + return Make_Loop_Statement (Sloc (Node), + Identifier => Identifier, + Iteration_Scheme => Iteration_Scheme, + Statements => Statements, + Has_Created_Identifier => Has_Created_Identifier, + End_Label => End_Label); + end Make_Implicit_Loop_Statement; + + -------------------------- + -- Make_Integer_Literal -- + --------------------------- + + function Make_Integer_Literal + (Loc : Source_Ptr; + Intval : Int) + return Node_Id + is + begin + return Make_Integer_Literal (Loc, UI_From_Int (Intval)); + end Make_Integer_Literal; + + --------------------------- + -- Make_Unsuppress_Block -- + --------------------------- + + -- Generates the following expansion: + + -- declare + -- pragma Suppress (); + -- begin + -- + -- end; + + function Make_Unsuppress_Block + (Loc : Source_Ptr; + Check : Name_Id; + Stmts : List_Id) + return Node_Id + is + begin + return + Make_Block_Statement (Loc, + Declarations => New_List ( + Make_Pragma (Loc, + Chars => Name_Suppress, + Pragma_Argument_Associations => New_List ( + Make_Pragma_Argument_Association (Loc, + Expression => Make_Identifier (Loc, Check))))), + + Handled_Statement_Sequence => + Make_Handled_Sequence_Of_Statements (Loc, + Statements => Stmts)); + end Make_Unsuppress_Block; + + -------------------------- + -- New_Constraint_Error -- + -------------------------- + + function New_Constraint_Error (Loc : Source_Ptr) return Node_Id is + Ident_Node : Node_Id; + Raise_Node : Node_Id; + + begin + Ident_Node := New_Node (N_Identifier, Loc); + Set_Chars (Ident_Node, Chars (Standard_Entity (S_Constraint_Error))); + Set_Entity (Ident_Node, Standard_Entity (S_Constraint_Error)); + Raise_Node := New_Node (N_Raise_Statement, Loc); + Set_Name (Raise_Node, Ident_Node); + return Raise_Node; + end New_Constraint_Error; + + ----------------------- + -- New_External_Name -- + ----------------------- + + function New_External_Name + (Related_Id : Name_Id; + Suffix : Character := ' '; + Suffix_Index : Int := 0; + Prefix : Character := ' ') + return Name_Id + is + begin + Get_Name_String (Related_Id); + + if Prefix /= ' ' then + pragma Assert (Is_OK_Internal_Letter (Prefix)); + + for J in reverse 1 .. Name_Len loop + Name_Buffer (J + 1) := Name_Buffer (J); + end loop; + + Name_Len := Name_Len + 1; + Name_Buffer (1) := Prefix; + end if; + + if Suffix /= ' ' then + pragma Assert (Is_OK_Internal_Letter (Suffix)); + Name_Len := Name_Len + 1; + Name_Buffer (Name_Len) := Suffix; + end if; + + if Suffix_Index /= 0 then + if Suffix_Index < 0 then + Add_Unique_Serial_Number; + else + Add_Nat_To_Name_Buffer (Suffix_Index); + end if; + end if; + + return Name_Find; + end New_External_Name; + + function New_External_Name + (Related_Id : Name_Id; + Suffix : String; + Suffix_Index : Int := 0; + Prefix : Character := ' ') + return Name_Id + is + begin + Get_Name_String (Related_Id); + + if Prefix /= ' ' then + pragma Assert (Is_OK_Internal_Letter (Prefix)); + + for J in reverse 1 .. Name_Len loop + Name_Buffer (J + 1) := Name_Buffer (J); + end loop; + + Name_Len := Name_Len + 1; + Name_Buffer (1) := Prefix; + end if; + + if Suffix /= "" then + Name_Buffer (Name_Len + 1 .. Name_Len + Suffix'Length) := Suffix; + Name_Len := Name_Len + Suffix'Length; + end if; + + if Suffix_Index /= 0 then + if Suffix_Index < 0 then + Add_Unique_Serial_Number; + else + Add_Nat_To_Name_Buffer (Suffix_Index); + end if; + end if; + + return Name_Find; + end New_External_Name; + + function New_External_Name + (Suffix : Character; + Suffix_Index : Nat) + return Name_Id + is + begin + Name_Buffer (1) := Suffix; + Name_Len := 1; + Add_Nat_To_Name_Buffer (Suffix_Index); + return Name_Find; + end New_External_Name; + + ----------------------- + -- New_Internal_Name -- + ----------------------- + + function New_Internal_Name (Id_Char : Character) return Name_Id is + begin + pragma Assert (Is_OK_Internal_Letter (Id_Char)); + Name_Buffer (1) := Id_Char; + Name_Len := 1; + Add_Unique_Serial_Number; + return Name_Enter; + end New_Internal_Name; + + ----------------------- + -- New_Occurrence_Of -- + ----------------------- + + function New_Occurrence_Of + (Def_Id : Entity_Id; + Loc : Source_Ptr) + return Node_Id + is + Occurrence : Node_Id; + + begin + Occurrence := New_Node (N_Identifier, Loc); + Set_Chars (Occurrence, Chars (Def_Id)); + Set_Entity (Occurrence, Def_Id); + + if Is_Type (Def_Id) then + Set_Etype (Occurrence, Def_Id); + else + Set_Etype (Occurrence, Etype (Def_Id)); + end if; + + return Occurrence; + end New_Occurrence_Of; + + ---------------------- + -- New_Reference_To -- + ---------------------- + + function New_Reference_To + (Def_Id : Entity_Id; + Loc : Source_Ptr) + return Node_Id + is + Occurrence : Node_Id; + + begin + Occurrence := New_Node (N_Identifier, Loc); + Set_Chars (Occurrence, Chars (Def_Id)); + Set_Entity (Occurrence, Def_Id); + return Occurrence; + end New_Reference_To; + + ----------------------- + -- New_Suffixed_Name -- + ----------------------- + + function New_Suffixed_Name + (Related_Id : Name_Id; + Suffix : String) + return Name_Id + is + begin + Get_Name_String (Related_Id); + Name_Len := Name_Len + 1; + Name_Buffer (Name_Len) := '_'; + Name_Buffer (Name_Len + 1 .. Name_Len + Suffix'Length) := Suffix; + Name_Len := Name_Len + Suffix'Length; + return Name_Find; + end New_Suffixed_Name; + + ------------------- + -- OK_Convert_To -- + ------------------- + + function OK_Convert_To (Typ : Entity_Id; Expr : Node_Id) return Node_Id is + Result : Node_Id; + + begin + Result := + Make_Type_Conversion (Sloc (Expr), + Subtype_Mark => New_Occurrence_Of (Typ, Sloc (Expr)), + Expression => Relocate_Node (Expr)); + Set_Conversion_OK (Result, True); + Set_Etype (Result, Typ); + return Result; + end OK_Convert_To; + + -------------------------- + -- Unchecked_Convert_To -- + -------------------------- + + function Unchecked_Convert_To + (Typ : Entity_Id; + Expr : Node_Id) + return Node_Id + is + Loc : constant Source_Ptr := Sloc (Expr); + Result : Node_Id; + + begin + -- If the expression is already of the correct type, then nothing + -- to do, except for relocating the node in case this is required. + + if Present (Etype (Expr)) + and then (Base_Type (Etype (Expr)) = Typ + or else Etype (Expr) = Typ) + then + return Relocate_Node (Expr); + + -- Cases where the inner expression is itself an unchecked conversion + -- to the same type, and we can thus eliminate the outer conversion. + + elsif Nkind (Expr) = N_Unchecked_Type_Conversion + and then Entity (Subtype_Mark (Expr)) = Typ + then + Result := Relocate_Node (Expr); + + -- All other cases + + else + Result := + Make_Unchecked_Type_Conversion (Loc, + Subtype_Mark => New_Occurrence_Of (Typ, Loc), + Expression => Relocate_Node (Expr)); + end if; + + Set_Etype (Result, Typ); + return Result; + end Unchecked_Convert_To; + +end Tbuild; diff --git a/gcc/ada/tbuild.ads b/gcc/ada/tbuild.ads new file mode 100644 index 00000000000..51d539b523b --- /dev/null +++ b/gcc/ada/tbuild.ads @@ -0,0 +1,241 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T B U I L D -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.69 $ +-- -- +-- Copyright (C) 1992-2000, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This package contains various utility procedures to assist in +-- building specific types of tree nodes. + +with Types; use Types; + +package Tbuild is + + function Make_DT_Component + (Loc : Source_Ptr; + Typ : Entity_Id; + I : Positive) + return Node_Id; + -- Gives a reference to the Ith component of the Dispatch Table of + -- a given Tagged Type. + -- + -- I = 1 --> Inheritance_Depth + -- I = 2 --> Tags (array of ancestors) + -- I = 3, 4 --> predefined primitive + -- function _Size (X : Typ) return Long_Long_Integer; + -- function _Equality (X : Typ; Y : Typ'Class) return Boolean; + -- I >= 5 --> User-Defined Primitive Operations + + function Make_DT_Access + (Loc : Source_Ptr; Rec : Node_Id; Typ : Entity_Id) return Node_Id; + -- Create an access to the Dispatch Table by using the Tag field + -- of a tagged record : Acc_Dt (Rec.tag).all + + function Make_Implicit_If_Statement + (Node : Node_Id; + Condition : Node_Id; + Then_Statements : List_Id; + Elsif_Parts : List_Id := No_List; + Else_Statements : List_Id := No_List) + return Node_Id; + pragma Inline (Make_Implicit_If_Statement); + -- This function makes an N_If_Statement node whose fields are filled + -- in with the indicated values (see Sinfo), and whose Sloc field is + -- is set to Sloc (Node). The effect is identical to calling function + -- Nmake.Make_If_Statement except that there is a check for restriction + -- No_Implicit_Conditionals, and if this restriction is being violated, + -- an error message is posted on Node. + + function Make_Implicit_Label_Declaration + (Loc : Source_Ptr; + Defining_Identifier : Node_Id; + Label_Construct : Node_Id) + return Node_Id; + -- Used to contruct an implicit label declaration node, including setting + -- the proper Label_Construct field (since Label_Construct is a semantic + -- field, the normal call to Make_Implicit_Label_Declaration does not + -- set this field). + + function Make_Implicit_Loop_Statement + (Node : Node_Id; + Statements : List_Id; + Identifier : Node_Id := Empty; + Iteration_Scheme : Node_Id := Empty; + Has_Created_Identifier : Boolean := False; + End_Label : Node_Id := Empty) + return Node_Id; + -- This function makes an N_Loop_Statement node whose fields are filled + -- in with the indicated values (see Sinfo), and whose Sloc field is + -- is set to Sloc (Node). The effect is identical to calling function + -- Nmake.Make_Loop_Statement except that there is a check for restrictions + -- No_Implicit_Loops and No_Implicit_Conditionals (the first applying in + -- all cases, and the second only for while loops), and if one of these + -- restrictions is being violated, an error message is posted on Node. + + function Make_Integer_Literal + (Loc : Source_Ptr; + Intval : Int) + return Node_Id; + pragma Inline (Make_Integer_Literal); + -- A convenient form of Make_Integer_Literal taking Int instead of Uint + + function Make_Unsuppress_Block + (Loc : Source_Ptr; + Check : Name_Id; + Stmts : List_Id) + return Node_Id; + -- Build a block with a pragma Suppress on 'Check'. Stmts is the + -- statements list that needs protection against the check + + function New_Constraint_Error (Loc : Source_Ptr) return Node_Id; + -- This function builds a tree corresponding to the Ada statement + -- "raise Constraint_Error" and returns the root of this tree, + -- the N_Raise_Statement node. + + function New_External_Name + (Related_Id : Name_Id; + Suffix : Character := ' '; + Suffix_Index : Int := 0; + Prefix : Character := ' ') + return Name_Id; + function New_External_Name + (Related_Id : Name_Id; + Suffix : String; + Suffix_Index : Int := 0; + Prefix : Character := ' ') + return Name_Id; + -- Builds a new entry in the names table of the form: + -- + -- [Prefix &] Related_Id [& Suffix] [& Suffix_Index] + -- + -- Prefix is prepended only if Prefix is non-blank (in which case it + -- must be an upper case letter other than O,Q,U,W (which are used for + -- identifier encoding, see Namet), and T is reserved for use by implicit + -- types. and X is reserved for use by debug type encoding (see package + -- Exp_Dbug). Note: the reason that Prefix is last is that it is almost + -- always omitted. The notable case of Prefix being non-null is when + -- it is 'T' for an implicit type. + + -- Suffix_Index'Image is appended only if the value of Suffix_Index is + -- positive, or if Suffix_Index is negative 1, then a unique serialized + -- suffix is added. If Suffix_Index is zero, then no index is appended. + + -- Suffix is also a single upper case letter other than O,Q,U,W,X and is a + -- required parameter (T is permitted). The constructed name is stored + -- using Find_Name so that it can be located using a subsequent Find_Name + -- operation (i.e. it is properly hashed into the names table). The upper + -- case letter given as the Suffix argument ensures that the name does + -- not clash with any Ada identifier name. These generated names are + -- permitted, but not required, to be made public by setting the flag + -- Is_Public in the associated entity. + + function New_External_Name + (Suffix : Character; + Suffix_Index : Nat) + return Name_Id; + -- Builds a new entry in the names table of the form + -- Suffix & Suffix_Index'Image + -- where Suffix is a single upper case letter other than O,Q,U,W,X and is + -- a required parameter (T is permitted). The constructed name is stored + -- using Find_Name so that it can be located using a subsequent Find_Name + -- operation (i.e. it is properly hashed into the names table). The upper + -- case letter given as the Suffix argument ensures that the name does + -- not clash with any Ada identifier name. These generated names are + -- permitted, but not required, to be made public by setting the flag + -- Is_Public in the associated entity. + + function New_Internal_Name (Id_Char : Character) return Name_Id; + -- Id_Char is an upper case letter other than O,Q,U,W (which are reserved + -- for identifier encoding (see Namet package for details) and X which is + -- used for debug encoding (see Exp_Dbug). The letter T is permitted, but + -- is reserved by convention for the case of internally generated types. + -- The result of the call is a new generated unique name of the form XyyyU + -- where X is Id_Char, yyy is a unique serial number, and U is either a + -- lower case s or b indicating if the current unit is a spec or a body. + -- + -- The name is entered into the names table using Name_Enter rather than + -- Name_Find, because there can never be a need to locate the entry using + -- the Name_Find procedure later on. Names created by New_Internal_Name + -- are guaranteed to be consistent from one compilation to another (i.e. + -- if the identical unit is compiled with a semantically consistent set + -- of sources, the numbers will be consistent. This means that it is fine + -- to use these as public symbols. + + function New_Suffixed_Name + (Related_Id : Name_Id; + Suffix : String) + return Name_Id; + -- This function is used to create special suffixed names used by the + -- debugger. Suffix is a string of upper case letters, used to construct + -- the required name. For instance, the special type used to record the + -- fixed-point small is called typ_SMALL where typ is the name of the + -- fixed-point type (as passed in Related_Id), and Suffix is "SMALL". + + function New_Occurrence_Of + (Def_Id : Entity_Id; + Loc : Source_Ptr) + return Node_Id; + -- New_Occurrence_Of creates an N_Identifier node which is an + -- occurrence of the defining identifier which is passed as its + -- argument. The Entity and Etype of the result are set from + -- the given defining identifier as follows: Entity is simply + -- a copy of Def_Id. Etype is a copy of Def_Id for types, and + -- a copy of the Etype of Def_Id for other entities. + + function New_Reference_To + (Def_Id : Entity_Id; + Loc : Source_Ptr) + return Node_Id; + -- This is like New_Occurrence_Of, but it does not set the Etype field. + -- It is used from the expander, where Etype fields are generally not set, + -- since they are set when the expanded tree is reanalyzed. + + function Checks_Off (N : Node_Id) return Node_Id; + pragma Inline (Checks_Off); + -- Returns an N_Unchecked_Expression node whose expression is the given + -- argument. The results is a subexpression identical to the argument, + -- except that it will be analyzed and resolved with checks off. + + function Convert_To (Typ : Entity_Id; Expr : Node_Id) return Node_Id; + -- Returns an expression that represents the result of a checked convert + -- of expression Exp to type T. If the base type of Exp is T, then no + -- conversion is required, and Exp is returned unchanged. Otherwise an + -- N_Type_Conversion node is constructed to convert the expression. + -- If an N_Type_Conversion node is required, Relocate_Node is used on + -- Exp. This means that it is safe to replace a node by a Convert_To + -- of itself to some other type. + + function OK_Convert_To (Typ : Entity_Id; Expr : Node_Id) return Node_Id; + -- Like Convert_To, except that a conversion node is always generated, + -- and the Conversion_OK flag is set on this conversion node. + + function Unchecked_Convert_To + (Typ : Entity_Id; + Expr : Node_Id) + return Node_Id; + -- Like Convert_To, but if a conversion is actually needed, constructs + -- an N_Unchecked_Type_Conversion node to do the required conversion. + +end Tbuild; diff --git a/gcc/ada/text_io.ads b/gcc/ada/text_io.ads new file mode 100644 index 00000000000..7715464f826 --- /dev/null +++ b/gcc/ada/text_io.ads @@ -0,0 +1,21 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT RUNTIME COMPONENTS -- +-- -- +-- T E X T _ I O -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.8 $ -- +-- -- +-- This specification is adapted from the Ada Reference Manual for use with -- +-- GNAT. In accordance with the copyright of that document, you can freely -- +-- copy and modify this specification, provided that if you redistribute a -- +-- modified version, any changes that you have made are clearly indicated. -- +-- -- +------------------------------------------------------------------------------ + +pragma Ada_95; +with Ada.Text_IO; + +package Text_IO renames Ada.Text_IO; diff --git a/gcc/ada/tracebak.c b/gcc/ada/tracebak.c new file mode 100644 index 00000000000..1ea4b8025ea --- /dev/null +++ b/gcc/ada/tracebak.c @@ -0,0 +1,1177 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * T R A C E B A C K * + * * + * C Implementation File * + * * + * $Revision: 1.1 $ + * * + * Copyright (C) 2000-2001 Ada Core Technologies, 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * As a special exception, if you link this file with other files to * + * produce an executable, this file does not by itself cause the resulting * + * executable to be covered by the GNU General Public License. This except- * + * ion does not however invalidate any other reasons why the executable * + * file might be covered by the GNU Public License. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +/* This file contains low level support for stack unwinding using GCC intrinsic + functions. + It has been tested on the following configurations: + HPPA/HP-UX + PowerPC/AiX + PowerPC/VxWorks + Sparc/Solaris + i386/Linux + i386/Solaris + i386/NT + i386/OS2 + i386/LynxOS + Alpha/VxWorks +*/ + +#ifdef __alpha_vxworks +#include "vxWorks.h" +#endif + +#ifdef IN_RTS +#define POSIX +#include "tconfig.h" +#include "tsystem.h" +#else +#include "config.h" +#include "system.h" +#endif + +#define Lock_Task system__soft_links__lock_task +extern void (*Lock_Task) (void); + +#define Unlock_Task system__soft_links__unlock_task +extern void (*Unlock_Task) (void); + +#ifndef CURRENT_STACK_FRAME +# define CURRENT_STACK_FRAME ({ char __csf; &__csf; }) +#endif + +extern int __gnat_backtrace PARAMS ((void **, int, void *, void *)); + +#if defined (__hppa) +struct layout +{ + void *return_address; + void *pad[4]; + struct layout *next; +}; + +#define FRAME_LEVEL 1 +#define FRAME_OFFSET -20 +#define SKIP_FRAME 1 +#define PC_ADJUST -4 + +/* If CURRENT is unaligned, it means that CURRENT is not a valid frame + pointer and we should stop popping frames. */ + +#define STOP_FRAME(CURRENT, TOP_STACK) \ + (((int) (CURRENT) & 0x3) != 0 && (CURRENT)->return_address == 0) + +/* Current implementation need to be protected against invalid memory + accesses */ +#define PROTECT_SEGV + +#elif defined (_AIX) +struct layout +{ + struct layout *next; + void *pad; + void *return_address; +}; + +#define FRAME_LEVEL 1 +#define FRAME_OFFSET 0 +#define SKIP_FRAME 2 +#define PC_ADJUST -4 +#define STOP_FRAME(CURRENT, TOP_STACK) ((void *) (CURRENT) < (TOP_STACK)) + +#elif defined (_ARCH_PPC) && defined (__vxworks) +struct layout +{ + struct layout *next; + void *return_address; +}; + +#define FRAME_LEVEL 1 +#define FRAME_OFFSET 0 +#define SKIP_FRAME 2 +#define PC_ADJUST 0 +#define STOP_FRAME(CURRENT, TOP_STACK) ((CURRENT)->return_address == 0) + +#elif defined (sun) && defined (sparc) +struct layout +{ + struct layout *next; + void *return_address; +}; + +#define FRAME_LEVEL 1 +#define FRAME_OFFSET (14*4) +#define SKIP_FRAME 1 +#define PC_ADJUST 0 +#define STOP_FRAME(CURRENT, TOP_STACK) \ + ((CURRENT)->return_address == 0|| (CURRENT)->next == 0 \ + || (void *) (CURRENT) < (TOP_STACK)) + +#elif defined (i386) +struct layout +{ + struct layout *next; + void *return_address; +}; + +#define FRAME_LEVEL 0 +#define FRAME_OFFSET 0 +#define SKIP_FRAME 1 +#define PC_ADJUST -2 +#define STOP_FRAME(CURRENT, TOP_STACK) \ + ((CURRENT)->return_address == 0|| (CURRENT)->next == 0 \ + || (void *) (CURRENT) < (TOP_STACK)) + +#elif defined (__alpha_vxworks) + +#define SKIP_FRAME 1 +#define PC_ADJUST -4 + +extern void kerTaskEntry(); + +#define STOP_FRAME \ + (current == NULL \ + || ((CORE_ADDR) &kerTaskEntry >= PROC_LOW_ADDR (current->proc_desc) \ + && current->pc >= (CORE_ADDR) &kerTaskEntry)) +#endif + +#if !defined (PC_ADJUST) +int +__gnat_backtrace (array, size, exclude_min, exclude_max) + void **array ATTRIBUTE_UNUSED; + int size ATTRIBUTE_UNUSED; + void *exclude_min ATTRIBUTE_UNUSED; + void *exclude_max ATTRIBUTE_UNUSED; +{ + return 0; +} + +#elif !defined (__alpha_vxworks) + +#ifdef PROTECT_SEGV +#include +#include + +static jmp_buf sigsegv_excp; + +static void +segv_handler (ignored) + int ignored; +{ + longjmp (sigsegv_excp, 1); +} +#endif + +int +__gnat_backtrace (array, size, exclude_min, exclude_max) + void **array; + int size; + void *exclude_min; + void *exclude_max; +{ + struct layout *current; + void *top_frame; + void *top_stack; + void *ret; + int cnt = 0; + +#ifdef PROTECT_SEGV + struct sigaction this_act, old_act; + + /* This function is not thread safe if PROTECT_SEGV is defined, so + protect it */ + (*Lock_Task) (); +#endif + + top_frame = __builtin_frame_address (FRAME_LEVEL); + top_stack = CURRENT_STACK_FRAME; + current = (struct layout *) ((size_t) top_frame + FRAME_OFFSET); + +#ifdef PROTECT_SEGV + this_act.sa_handler = segv_handler; + sigemptyset (&this_act.sa_mask); + this_act.sa_flags = 0; + sigaction (SIGSEGV, &this_act, &old_act); + + if (setjmp (sigsegv_excp)) + goto Done; +#endif + + /* We skip the call to this function, it makes no sense to record it. */ + while (cnt < SKIP_FRAME) + { + current = (struct layout *) ((size_t) current->next + FRAME_OFFSET); + cnt++; + } + + cnt = 0; + while (cnt < size) + { + if (STOP_FRAME (current, top_stack)) + break; + + if (current->return_address < exclude_min + || current->return_address > exclude_max) + array[cnt++] = current->return_address + PC_ADJUST; + + current = (struct layout *) ((size_t) current->next + FRAME_OFFSET); + } + +#ifdef PROTECT_SEGV + Done: + sigaction (SIGSEGV, &old_act, NULL); + (*Unlock_Task) (); +#endif + return cnt; +} + +#else +/* Alpha vxWorks requires a special, complex treatment that is extracted + from GDB */ + +#include + +/* Register numbers of various important registers. + Note that most of these values are "real" register numbers, + and correspond to the general registers of the machine, + and FP_REGNUM is a "phony" register number which is too large + to be an actual register number as far as the user is concerned + but serves to get the desired value when passed to read_register. */ + +#define T7_REGNUM 8 /* Return address register for OSF/1 __add* */ +#define GCC_FP_REGNUM 15 /* Used by gcc as frame register */ +#define T9_REGNUM 23 /* Return address register for OSF/1 __div* */ +#define SP_REGNUM 30 /* Contains address of top of stack */ +#define RA_REGNUM 26 /* Contains return address value */ +#define FP0_REGNUM 32 /* Floating point register 0 */ +#define PC_REGNUM 64 /* Contains program counter */ +#define NUM_REGS 66 + +#define VM_MIN_ADDRESS (CORE_ADDR)0x120000000 + +#define SIZEOF_FRAME_SAVED_REGS (sizeof (CORE_ADDR) * (NUM_REGS)) +#define INIT_EXTRA_FRAME_INFO(fromleaf, fci) init_extra_frame_info(fci) + +#define FRAME_CHAIN(thisframe) (CORE_ADDR) alpha_frame_chain (thisframe) + +#define FRAME_CHAIN_VALID(CHAIN, THISFRAME) \ + ((CHAIN) != 0 \ + && !inside_entry_file (FRAME_SAVED_PC (THISFRAME))) + +#define FRAME_SAVED_PC(FRAME) (alpha_frame_saved_pc (FRAME)) + +#define FRAME_CHAIN_COMBINE(CHAIN, THISFRAME) (CHAIN) + +#define INIT_FRAME_PC(FROMLEAF, PREV) + +#define INIT_FRAME_PC_FIRST(FROMLEAF, PREV) \ + (PREV)->pc = ((FROMLEAF) ? SAVED_PC_AFTER_CALL ((PREV)->next) \ + : (PREV)->next ? FRAME_SAVED_PC ((prev)->NEXT) : read_pc ()); + +#define SAVED_PC_AFTER_CALL(FRAME) alpha_saved_pc_after_call (FRAME) + +typedef unsigned long long int bfd_vma; + +typedef bfd_vma CORE_ADDR; + +typedef struct pdr +{ + bfd_vma adr; /* memory address of start of procedure */ + long isym; /* start of local symbol entries */ + long iline; /* start of line number entries*/ + long regmask; /* save register mask */ + long regoffset; /* save register offset */ + long iopt; /* start of optimization symbol entries*/ + long fregmask; /* save floating point register mask */ + long fregoffset; /* save floating point register offset */ + long frameoffset; /* frame size */ + short framereg; /* frame pointer register */ + short pcreg; /* offset or reg of return pc */ + long lnLow; /* lowest line in the procedure */ + long lnHigh; /* highest line in the procedure */ + bfd_vma cbLineOffset; /* byte offset for this procedure from the fd base */ + /* These fields are new for 64 bit ECOFF. */ + unsigned gp_prologue : 8; /* byte size of GP prologue */ + unsigned gp_used : 1; /* true if the procedure uses GP */ + unsigned reg_frame : 1; /* true if register frame procedure */ + unsigned prof : 1; /* true if compiled with -pg */ + unsigned reserved : 13; /* reserved: must be zero */ + unsigned localoff : 8; /* offset of local variables from vfp */ +} PDR; + +typedef struct alpha_extra_func_info +{ + long numargs; /* number of args to procedure (was iopt) */ + PDR pdr; /* Procedure descriptor record */ +} +*alpha_extra_func_info_t; + +struct frame_info +{ + /* Nominal address of the frame described. See comments at FRAME_FP + about what this means outside the *FRAME* macros; in the *FRAME* + macros, it can mean whatever makes most sense for this machine. */ + CORE_ADDR frame; + + /* Address at which execution is occurring in this frame. For the + innermost frame, it's the current pc. For other frames, it is a + pc saved in the next frame. */ + CORE_ADDR pc; + + /* For each register, address of where it was saved on entry to the + frame, or zero if it was not saved on entry to this frame. This + includes special registers such as pc and fp saved in special + ways in the stack frame. The SP_REGNUM is even more special, the + address here is the sp for the next frame, not the address where + the sp was saved. Allocated by frame_saved_regs_zalloc () which + is called and initialized by FRAME_INIT_SAVED_REGS. */ + CORE_ADDR *saved_regs; /*NUM_REGS */ + + int localoff; + int pc_reg; + alpha_extra_func_info_t proc_desc; + + /* Pointers to the next and previous frame_info's in the frame cache. */ + struct frame_info *next, *prev; +}; + +struct frame_saved_regs +{ + /* For each register R (except the SP), regs[R] is the address at + which it was saved on entry to the frame, or zero if it was not + saved on entry to this frame. This includes special registers + such as pc and fp saved in special ways in the stack frame. + + regs[SP_REGNUM] is different. It holds the actual SP, not the + address at which it was saved. */ + + CORE_ADDR regs[NUM_REGS]; +}; + +static CORE_ADDR theRegisters[32]; + +/* Prototypes for local functions. */ + +static CORE_ADDR read_next_frame_reg PARAMS ((struct frame_info *, int)); +static CORE_ADDR heuristic_proc_start PARAMS ((CORE_ADDR)); +static int alpha_about_to_return PARAMS ((CORE_ADDR pc)); +static void init_extra_frame_info PARAMS ((struct frame_info *)); +static CORE_ADDR alpha_frame_chain PARAMS ((struct frame_info *)); +static CORE_ADDR alpha_frame_saved_pc PARAMS ((struct frame_info *frame)) +static void *trace_alloc PARAMS ((unsigned int)); +static struct frame_info *create_new_frame PARAMS ((CORE_ADDR, CORE_ADDR)); + +static alpha_extra_func_info_t +heuristic_proc_desc PARAMS ((CORE_ADDR, CORE_ADDR, struct frame_info *, + struct frame_saved_regs *)); + +static alpha_extra_func_info_t +find_proc_desc PARAMS ((CORE_ADDR, struct frame_info *, + struct frame_saved_regs *)); + +/* Heuristic_proc_start may hunt through the text section for a long + time across a 2400 baud serial line. Allows the user to limit this + search. */ +static unsigned int heuristic_fence_post = 1<<16; + +/* Layout of a stack frame on the alpha: + + | | + pdr members: | 7th ... nth arg, | + | `pushed' by caller. | + | | +----------------|-------------------------------|<-- old_sp == vfp + ^ ^ ^ ^ | | + | | | | | | + | |localoff | Copies of 1st .. 6th | + | | | | | argument if necessary. | + | | | v | | + | | | --- |-------------------------------|<-- FRAME_LOCALS_ADDRESS + | | | | | + | | | | Locals and temporaries. | + | | | | | + | | | |-------------------------------| + | | | | | + |-fregoffset | Saved float registers. | + | | | | F9 | + | | | | . | + | | | | . | + | | | | F2 | + | | v | | + | | -------|-------------------------------| + | | | | + | | | Saved registers. | + | | | S6 | + |-regoffset | . | + | | | . | + | | | S0 | + | | | pdr.pcreg | + | v | | + | ----------|-------------------------------| + | | | + frameoffset | Argument build area, gets | + | | 7th ... nth arg for any | + | | called procedure. | + v | | + -------------|-------------------------------|<-- sp + | | */ + +#define PROC_LOW_ADDR(PROC) ((PROC)->pdr.adr) /* least address */ +#define PROC_HIGH_ADDR(PROC) ((PROC)->pdr.iline) /* upper address bound */ +#define PROC_DUMMY_FRAME(PROC) ((PROC)->pdr.cbLineOffset) /*CALL_DUMMY frame */ +#define PROC_FRAME_OFFSET(PROC) ((PROC)->pdr.frameoffset) +#define PROC_FRAME_REG(PROC) ((PROC)->pdr.framereg) +#define PROC_REG_MASK(PROC) ((PROC)->pdr.regmask) +#define PROC_FREG_MASK(PROC) ((PROC)->pdr.fregmask) +#define PROC_REG_OFFSET(PROC) ((PROC)->pdr.regoffset) +#define PROC_FREG_OFFSET(PROC) ((PROC)->pdr.fregoffset) +#define PROC_PC_REG(PROC) ((PROC)->pdr.pcreg) +#define PROC_LOCALOFF(PROC) ((PROC)->pdr.localoff) + +/* Local storage allocation/deallocation functions. trace_alloc does + a malloc, but also chains allocated blocks on trace_alloc_chain, so + they may all be freed on exit from __gnat_backtrace. */ + +struct alloc_chain +{ + struct alloc_chain *next; + double x[0]; +}; +struct alloc_chain *trace_alloc_chain; + +static void * +trace_alloc (n) + unsigned int n; +{ + struct alloc_chain * result = malloc (n + sizeof(struct alloc_chain)); + + result->next = trace_alloc_chain; + trace_alloc_chain = result; + return (void*) result->x; +} + +static void +free_trace_alloc () +{ + while (trace_alloc_chain != 0) + { + struct alloc_chain *old = trace_alloc_chain; + + trace_alloc_chain = trace_alloc_chain->next; + free (old); + } +} + +/* Read value at ADDR into *DEST, returning 0 if this is valid, != 0 + otherwise. */ + +static int +read_memory_safe4 (addr, dest) + CORE_ADDR addr; + unsigned int *dest; +{ + *dest = *((unsigned int*) addr); + return 0; +} + +/* Read value at ADDR into *DEST, returning 0 if this is valid, != 0 + otherwise. */ + +static int +read_memory_safe8 (addr, dest) + CORE_ADDR addr; + CORE_ADDR *dest; +{ + *dest = *((CORE_ADDR*) addr); + return 0; +} + +static CORE_ADDR +read_register (regno) + int regno; +{ + if (regno >= 0 && regno < 31) + return theRegisters[regno]; + + return (CORE_ADDR) 0; +} + +static void +frame_saved_regs_zalloc (fi) + struct frame_info *fi; +{ + fi->saved_regs = (CORE_ADDR *) trace_alloc (SIZEOF_FRAME_SAVED_REGS); + memset (fi->saved_regs, 0, SIZEOF_FRAME_SAVED_REGS); +} + +static void * +frame_obstack_alloc (size) + unsigned long size; +{ + return (void *) trace_alloc (size); +} + +static int +inside_entry_file (addr) + CORE_ADDR addr; +{ + if (addr == 0) + return 1; + else + return 0; +} + +static CORE_ADDR +alpha_saved_pc_after_call (frame) + struct frame_info *frame; +{ + CORE_ADDR pc = frame->pc; + alpha_extra_func_info_t proc_desc; + int pcreg; + + proc_desc = find_proc_desc (pc, frame->next, NULL); + pcreg = proc_desc ? PROC_PC_REG (proc_desc) : RA_REGNUM; + + return read_register (pcreg); +} + +/* Guaranteed to set frame->saved_regs to some values (it never leaves it + NULL). */ + +static void +alpha_find_saved_regs (frame) + struct frame_info *frame; +{ + int ireg; + CORE_ADDR reg_position; + unsigned long mask; + alpha_extra_func_info_t proc_desc; + int returnreg; + + frame_saved_regs_zalloc (frame); + + /* If it is the frame for __sigtramp, the saved registers are located in a + sigcontext structure somewhere on the stack. __sigtramp passes a pointer + to the sigcontext structure on the stack. If the stack layout for + __sigtramp changes, or if sigcontext offsets change, we might have to + update this code. */ + +#ifndef SIGFRAME_PC_OFF +#define SIGFRAME_PC_OFF (2 * 8) +#define SIGFRAME_REGSAVE_OFF (4 * 8) +#define SIGFRAME_FPREGSAVE_OFF (SIGFRAME_REGSAVE_OFF + 32 * 8 + 8) +#endif + + proc_desc = frame->proc_desc; + if (proc_desc == NULL) + /* I'm not sure how/whether this can happen. Normally when we can't + find a proc_desc, we "synthesize" one using heuristic_proc_desc + and set the saved_regs right away. */ + return; + + /* Fill in the offsets for the registers which gen_mask says + were saved. */ + + reg_position = frame->frame + PROC_REG_OFFSET (proc_desc); + mask = PROC_REG_MASK (proc_desc); + + returnreg = PROC_PC_REG (proc_desc); + + /* Note that RA is always saved first, regardless of its actual + register number. */ + if (mask & (1 << returnreg)) + { + frame->saved_regs[returnreg] = reg_position; + reg_position += 8; + mask &= ~(1 << returnreg); /* Clear bit for RA so we + don't save again later. */ + } + + for (ireg = 0; ireg <= 31; ireg++) + if (mask & (1 << ireg)) + { + frame->saved_regs[ireg] = reg_position; + reg_position += 8; + } + + /* Fill in the offsets for the registers which float_mask says + were saved. */ + + reg_position = frame->frame + PROC_FREG_OFFSET (proc_desc); + mask = PROC_FREG_MASK (proc_desc); + + for (ireg = 0; ireg <= 31; ireg++) + if (mask & (1 << ireg)) + { + frame->saved_regs[FP0_REGNUM + ireg] = reg_position; + reg_position += 8; + } + + frame->saved_regs[PC_REGNUM] = frame->saved_regs[returnreg]; +} + +static CORE_ADDR +read_next_frame_reg (fi, regno) + struct frame_info *fi; + int regno; +{ + CORE_ADDR result; + for (; fi; fi = fi->next) + { + /* We have to get the saved sp from the sigcontext + if it is a signal handler frame. */ + if (regno == SP_REGNUM) + return fi->frame; + else + { + if (fi->saved_regs == 0) + alpha_find_saved_regs (fi); + + if (fi->saved_regs[regno]) + { + if (read_memory_safe8 (fi->saved_regs[regno], &result) == 0) + return result; + else + return 0; + } + } + } + + return read_register (regno); +} + +static CORE_ADDR +alpha_frame_saved_pc (frame) + struct frame_info *frame; +{ + return read_next_frame_reg (frame, frame->pc_reg); +} + +static struct alpha_extra_func_info temp_proc_desc; + +/* Nonzero if instruction at PC is a return instruction. "ret + $zero,($ra),1" on alpha. */ + +static int +alpha_about_to_return (pc) + CORE_ADDR pc; +{ + int inst; + + read_memory_safe4 (pc, &inst); + return inst == 0x6bfa8001; +} + +/* A heuristically computed start address for the subprogram + containing address PC. Returns 0 if none detected. */ + +static CORE_ADDR +heuristic_proc_start (pc) + CORE_ADDR pc; +{ + CORE_ADDR start_pc = pc; + CORE_ADDR fence = start_pc - heuristic_fence_post; + + if (start_pc == 0) + return 0; + + if (heuristic_fence_post == UINT_MAX + || fence < VM_MIN_ADDRESS) + fence = VM_MIN_ADDRESS; + + /* search back for previous return */ + for (start_pc -= 4; ; start_pc -= 4) + { + if (start_pc < fence) + return 0; + else if (alpha_about_to_return (start_pc)) + break; + } + + start_pc += 4; /* skip return */ + return start_pc; +} + +static alpha_extra_func_info_t +heuristic_proc_desc (start_pc, limit_pc, next_frame, saved_regs_p) + CORE_ADDR start_pc; + CORE_ADDR limit_pc; + struct frame_info *next_frame; + struct frame_saved_regs *saved_regs_p; +{ + CORE_ADDR sp = read_next_frame_reg (next_frame, SP_REGNUM); + CORE_ADDR cur_pc; + int frame_size; + int has_frame_reg = 0; + unsigned long reg_mask = 0; + int pcreg = -1; + + if (start_pc == 0) + return 0; + + memset (&temp_proc_desc, '\0', sizeof (temp_proc_desc)); + if (saved_regs_p != 0) + memset (saved_regs_p, '\0', sizeof (struct frame_saved_regs)); + + PROC_LOW_ADDR (&temp_proc_desc) = start_pc; + + if (start_pc + 200 < limit_pc) + limit_pc = start_pc + 200; + + frame_size = 0; + for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += 4) + { + unsigned int word; + int status; + + status = read_memory_safe4 (cur_pc, &word); + if (status) + return 0; + + if ((word & 0xffff0000) == 0x23de0000) /* lda $sp,n($sp) */ + { + if (word & 0x8000) + frame_size += (-word) & 0xffff; + else + /* Exit loop if a positive stack adjustment is found, which + usually means that the stack cleanup code in the function + epilogue is reached. */ + break; + } + else if ((word & 0xfc1f0000) == 0xb41e0000 /* stq reg,n($sp) */ + && (word & 0xffff0000) != 0xb7fe0000) /* reg != $zero */ + { + int reg = (word & 0x03e00000) >> 21; + + reg_mask |= 1 << reg; + if (saved_regs_p != 0) + saved_regs_p->regs[reg] = sp + (short) word; + + /* Starting with OSF/1-3.2C, the system libraries are shipped + without local symbols, but they still contain procedure + descriptors without a symbol reference. GDB is currently + unable to find these procedure descriptors and uses + heuristic_proc_desc instead. + As some low level compiler support routines (__div*, __add*) + use a non-standard return address register, we have to + add some heuristics to determine the return address register, + or stepping over these routines will fail. + Usually the return address register is the first register + saved on the stack, but assembler optimization might + rearrange the register saves. + So we recognize only a few registers (t7, t9, ra) within + the procedure prologue as valid return address registers. + If we encounter a return instruction, we extract the + the return address register from it. + + FIXME: Rewriting GDB to access the procedure descriptors, + e.g. via the minimal symbol table, might obviate this hack. */ + if (pcreg == -1 + && cur_pc < (start_pc + 80) + && (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM)) + pcreg = reg; + } + else if ((word & 0xffe0ffff) == 0x6be08001) /* ret zero,reg,1 */ + pcreg = (word >> 16) & 0x1f; + else if (word == 0x47de040f) /* bis sp,sp fp */ + has_frame_reg = 1; + } + + if (pcreg == -1) + { + /* If we haven't found a valid return address register yet, + keep searching in the procedure prologue. */ + while (cur_pc < (limit_pc + 80) && cur_pc < (start_pc + 80)) + { + unsigned int word; + + if (read_memory_safe4 (cur_pc, &word)) + break; + cur_pc += 4; + + if ((word & 0xfc1f0000) == 0xb41e0000 /* stq reg,n($sp) */ + && (word & 0xffff0000) != 0xb7fe0000) /* reg != $zero */ + { + int reg = (word & 0x03e00000) >> 21; + + if (reg == T7_REGNUM || reg == T9_REGNUM || reg == RA_REGNUM) + { + pcreg = reg; + break; + } + } + else if ((word & 0xffe0ffff) == 0x6be08001) /* ret zero,reg,1 */ + { + pcreg = (word >> 16) & 0x1f; + break; + } + } + } + + if (has_frame_reg) + PROC_FRAME_REG (&temp_proc_desc) = GCC_FP_REGNUM; + else + PROC_FRAME_REG (&temp_proc_desc) = SP_REGNUM; + + PROC_FRAME_OFFSET (&temp_proc_desc) = frame_size; + PROC_REG_MASK (&temp_proc_desc) = reg_mask; + PROC_PC_REG (&temp_proc_desc) = (pcreg == -1) ? RA_REGNUM : pcreg; + PROC_LOCALOFF (&temp_proc_desc) = 0; /* XXX - bogus */ + + return &temp_proc_desc; +} + +static alpha_extra_func_info_t +find_proc_desc (pc, next_frame, saved_regs) + CORE_ADDR pc; + struct frame_info *next_frame; + struct frame_saved_regs *saved_regs; +{ + CORE_ADDR startaddr; + + /* If heuristic_fence_post is non-zero, determine the procedure + start address by examining the instructions. + This allows us to find the start address of static functions which + have no symbolic information, as startaddr would have been set to + the preceding global function start address by the + find_pc_partial_function call above. */ + startaddr = heuristic_proc_start (pc); + + return heuristic_proc_desc (startaddr, pc, next_frame, saved_regs); +} + +static CORE_ADDR +alpha_frame_chain (frame) + struct frame_info *frame; +{ + alpha_extra_func_info_t proc_desc; + CORE_ADDR saved_pc = FRAME_SAVED_PC (frame); + + if (saved_pc == 0 || inside_entry_file (saved_pc)) + return 0; + + proc_desc = find_proc_desc (saved_pc, frame, NULL); + if (!proc_desc) + return 0; + + /* If no frame pointer and frame size is zero, we must be at end + of stack (or otherwise hosed). If we don't check frame size, + we loop forever if we see a zero size frame. */ + if (PROC_FRAME_REG (proc_desc) == SP_REGNUM + && PROC_FRAME_OFFSET (proc_desc) == 0) + return 0; + else + return read_next_frame_reg (frame, PROC_FRAME_REG (proc_desc)) + + PROC_FRAME_OFFSET (proc_desc); +} + +static void +init_extra_frame_info (frame) + struct frame_info *frame; +{ + struct frame_saved_regs temp_saved_regs; + alpha_extra_func_info_t proc_desc = + find_proc_desc (frame->pc, frame->next, &temp_saved_regs); + + frame->saved_regs = NULL; + frame->localoff = 0; + frame->pc_reg = RA_REGNUM; + frame->proc_desc = proc_desc; + + if (proc_desc) + { + /* Get the locals offset and the saved pc register from the + procedure descriptor, they are valid even if we are in the + middle of the prologue. */ + frame->localoff = PROC_LOCALOFF (proc_desc); + frame->pc_reg = PROC_PC_REG (proc_desc); + + /* Fixup frame-pointer - only needed for top frame */ + + /* This may not be quite right, if proc has a real frame register. + Get the value of the frame relative sp, procedure might have been + interrupted by a signal at it's very start. */ + if (frame->pc == PROC_LOW_ADDR (proc_desc)) + frame->frame = read_next_frame_reg (frame->next, SP_REGNUM); + else + frame->frame + = (read_next_frame_reg (frame->next, PROC_FRAME_REG (proc_desc)) + + PROC_FRAME_OFFSET (proc_desc)); + + frame->saved_regs + = (CORE_ADDR *) frame_obstack_alloc (SIZEOF_FRAME_SAVED_REGS); + memcpy + (frame->saved_regs, temp_saved_regs.regs, SIZEOF_FRAME_SAVED_REGS); + frame->saved_regs[PC_REGNUM] = frame->saved_regs[RA_REGNUM]; + } +} + +/* Create an arbitrary (i.e. address specified by user) or innermost frame. + Always returns a non-NULL value. */ + +static struct frame_info * +create_new_frame (addr, pc) + CORE_ADDR addr; + CORE_ADDR pc; +{ + struct frame_info *fi; + + fi = (struct frame_info *) + trace_alloc (sizeof (struct frame_info)); + + /* Arbitrary frame */ + fi->next = NULL; + fi->prev = NULL; + fi->frame = addr; + fi->pc = pc; + +#ifdef INIT_EXTRA_FRAME_INFO + INIT_EXTRA_FRAME_INFO (0, fi); +#endif + + return fi; +} + +static CORE_ADDR current_pc; + +static void +set_current_pc () +{ + current_pc = (CORE_ADDR) __builtin_return_address (0); +} + +static CORE_ADDR +read_pc () +{ + return current_pc; +} + +static struct frame_info * +get_current_frame () +{ + return create_new_frame (0, read_pc ()); +} + +/* Return the frame that called FI. + If FI is the original frame (it has no caller), return 0. */ + +static struct frame_info * +get_prev_frame (next_frame) + struct frame_info *next_frame; +{ + CORE_ADDR address = 0; + struct frame_info *prev; + int fromleaf = 0; + + /* If we have the prev one, return it */ + if (next_frame->prev) + return next_frame->prev; + + /* On some machines it is possible to call a function without + setting up a stack frame for it. On these machines, we + define this macro to take two args; a frameinfo pointer + identifying a frame and a variable to set or clear if it is + or isn't leafless. */ + + /* Two macros defined in tm.h specify the machine-dependent + actions to be performed here. + + First, get the frame's chain-pointer. If that is zero, the frame + is the outermost frame or a leaf called by the outermost frame. + This means that if start calls main without a frame, we'll return + 0 (which is fine anyway). + + Nope; there's a problem. This also returns when the current + routine is a leaf of main. This is unacceptable. We move + this to after the ffi test; I'd rather have backtraces from + start go curfluy than have an abort called from main not show + main. */ + + address = FRAME_CHAIN (next_frame); + if (!FRAME_CHAIN_VALID (address, next_frame)) + return 0; + address = FRAME_CHAIN_COMBINE (address, next_frame); + + if (address == 0) + return 0; + + prev = (struct frame_info *) trace_alloc (sizeof (struct frame_info)); + + prev->saved_regs = NULL; + if (next_frame) + next_frame->prev = prev; + + prev->next = next_frame; + prev->prev = (struct frame_info *) 0; + prev->frame = address; + + /* This change should not be needed, FIXME! We should + determine whether any targets *need* INIT_FRAME_PC to happen + after INIT_EXTRA_FRAME_INFO and come up with a simple way to + express what goes on here. + + INIT_EXTRA_FRAME_INFO is called from two places: create_new_frame + (where the PC is already set up) and here (where it isn't). + INIT_FRAME_PC is only called from here, always after + INIT_EXTRA_FRAME_INFO. + + The catch is the MIPS, where INIT_EXTRA_FRAME_INFO requires the PC + value (which hasn't been set yet). Some other machines appear to + require INIT_EXTRA_FRAME_INFO before they can do INIT_FRAME_PC. Phoo. + + We shouldn't need INIT_FRAME_PC_FIRST to add more complication to + an already overcomplicated part of GDB. gnu@cygnus.com, 15Sep92. + + Assuming that some machines need INIT_FRAME_PC after + INIT_EXTRA_FRAME_INFO, one possible scheme: + + SETUP_INNERMOST_FRAME() + Default version is just create_new_frame (read_fp ()), + read_pc ()). Machines with extra frame info would do that (or the + local equivalent) and then set the extra fields. + INIT_PREV_FRAME(fromleaf, prev) + Replace INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC. This should + also return a flag saying whether to keep the new frame, or + whether to discard it, because on some machines (e.g. mips) it + is really awkward to have FRAME_CHAIN_VALID called *before* + INIT_EXTRA_FRAME_INFO (there is no good way to get information + deduced in FRAME_CHAIN_VALID into the extra fields of the new frame). + std_frame_pc(fromleaf, prev) + This is the default setting for INIT_PREV_FRAME. It just does what + the default INIT_FRAME_PC does. Some machines will call it from + INIT_PREV_FRAME (either at the beginning, the end, or in the middle). + Some machines won't use it. + kingdon@cygnus.com, 13Apr93, 31Jan94, 14Dec94. */ + +#ifdef INIT_FRAME_PC_FIRST + INIT_FRAME_PC_FIRST (fromleaf, prev); +#endif + +#ifdef INIT_EXTRA_FRAME_INFO + INIT_EXTRA_FRAME_INFO (fromleaf, prev); +#endif + + /* This entry is in the frame queue now, which is good since + FRAME_SAVED_PC may use that queue to figure out its value + (see tm-sparc.h). We want the pc saved in the inferior frame. */ + INIT_FRAME_PC (fromleaf, prev); + + /* If ->frame and ->pc are unchanged, we are in the process of getting + ourselves into an infinite backtrace. Some architectures check this + in FRAME_CHAIN or thereabouts, but it seems like there is no reason + this can't be an architecture-independent check. */ + if (next_frame != NULL) + { + if (prev->frame == next_frame->frame + && prev->pc == next_frame->pc) + { + next_frame->prev = NULL; + free (prev); + return NULL; + } + } + + return prev; +} + +#define SAVE(regno,disp) \ + "stq $" #regno ", " #disp "(%0)\n" + +int +__gnat_backtrace (array, size, exclude_min, exclude_max) + void **array; + int size; + void *exclude_min; + void *exclude_max; +{ + struct frame_info* top; + struct frame_info* current; + int cnt; + + /* This function is not thread safe, protect it */ + (*Lock_Task) (); + asm volatile ( + SAVE (9,72) + SAVE (10,80) + SAVE (11,88) + SAVE (12,96) + SAVE (13,104) + SAVE (14,112) + SAVE (15,120) + SAVE (16,128) + SAVE (17,136) + SAVE (18,144) + SAVE (19,152) + SAVE (20,160) + SAVE (21,168) + SAVE (22,176) + SAVE (23,184) + SAVE (24,192) + SAVE (25,200) + SAVE (26,208) + SAVE (27,216) + SAVE (28,224) + SAVE (29,232) + SAVE (30,240) + : : "r" (&theRegisters)); + + trace_alloc_chain = NULL; + set_current_pc (); + + top = current = get_current_frame (); + cnt = 0; + + /* We skip the call to this function, it makes no sense to record it. */ + for (cnt = 0; cnt < SKIP_FRAME; cnt += 1) { + current = get_prev_frame (current); + } + + cnt = 0; + while (cnt < size) + { + if (STOP_FRAME) + break; + + if (current->pc < (CORE_ADDR) exclude_min + || current->pc > (CORE_ADDR) exclude_max) + array[cnt++] = (void*) (current->pc + PC_ADJUST); + + current = get_prev_frame (current); + } + + free_trace_alloc (); + (*Unlock_Task) (); + + return cnt; +} +#endif diff --git a/gcc/ada/trans.c b/gcc/ada/trans.c new file mode 100644 index 00000000000..572dff2645c --- /dev/null +++ b/gcc/ada/trans.c @@ -0,0 +1,5428 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * T R A N S * + * * + * C Implementation File * + * * + * $Revision: 1.2 $ + * * + * Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "tree.h" +#include "real.h" +#include "flags.h" +#include "rtl.h" +#include "expr.h" +#include "ggc.h" +#include "function.h" +#include "debug.h" +#include "output.h" +#include "ada.h" +#include "types.h" +#include "atree.h" +#include "elists.h" +#include "namet.h" +#include "nlists.h" +#include "snames.h" +#include "stringt.h" +#include "uintp.h" +#include "urealp.h" +#include "fe.h" +#include "sinfo.h" +#include "einfo.h" +#include "ada-tree.h" +#include "gigi.h" + +int max_gnat_nodes; +int number_names; +struct Node *Nodes_Ptr; +Node_Id *Next_Node_Ptr; +Node_Id *Prev_Node_Ptr; +struct Elist_Header *Elists_Ptr; +struct Elmt_Item *Elmts_Ptr; +struct String_Entry *Strings_Ptr; +Char_Code *String_Chars_Ptr; +struct List_Header *List_Headers_Ptr; + +/* Current filename without path. */ +const char *ref_filename; + +/* Flag indicating whether file names are discarded in exception messages */ +int discard_file_names; + +/* If true, then gigi is being called on an analyzed but unexpanded + tree, and the only purpose of the call is to properly annotate + types with representation information. */ +int type_annotate_only; + +/* List of TREE_LIST nodes representing a block stack. TREE_VALUE + of each gives the variable used for the setjmp buffer in the current + block, if any. TREE_PURPOSE gives the bottom condition for a loop, + if this block is for a loop. The latter is only used to save the tree + over GC. */ +tree gnu_block_stack; + +/* List of TREE_LIST nodes representing a stack of exception pointer + variables. TREE_VALUE is the VAR_DECL that stores the address of + the raised exception. Nonzero means we are in an exception + handler. Set to error_mark_node in the zero-cost case. */ +static tree gnu_except_ptr_stack; + +/* Map GNAT tree codes to GCC tree codes for simple expressions. */ +static enum tree_code gnu_codes[Number_Node_Kinds]; + +/* Current node being treated, in case gigi_abort called. */ +Node_Id error_gnat_node; + +/* Variable that stores a list of labels to be used as a goto target instead of + a return in some functions. See processing for N_Subprogram_Body. */ +static tree gnu_return_label_stack; + +static tree tree_transform PARAMS((Node_Id)); +static void elaborate_all_entities PARAMS((Node_Id)); +static void process_freeze_entity PARAMS((Node_Id)); +static void process_inlined_subprograms PARAMS((Node_Id)); +static void process_decls PARAMS((List_Id, List_Id, Node_Id, + int, int)); +static tree emit_access_check PARAMS((tree)); +static tree emit_discriminant_check PARAMS((tree, Node_Id)); +static tree emit_range_check PARAMS((tree, Node_Id)); +static tree emit_index_check PARAMS((tree, tree, tree, tree)); +static tree emit_check PARAMS((tree, tree)); +static tree convert_with_check PARAMS((Entity_Id, tree, + int, int, int)); +static int addressable_p PARAMS((tree)); +static tree assoc_to_constructor PARAMS((Node_Id, tree)); +static tree extract_values PARAMS((tree, tree)); +static tree pos_to_constructor PARAMS((Node_Id, tree, Entity_Id)); +static tree maybe_implicit_deref PARAMS((tree)); +static tree gnat_stabilize_reference_1 PARAMS((tree, int)); +static int build_unit_elab PARAMS((Entity_Id, int, tree)); + +/* Constants for +0.5 and -0.5 for float-to-integer rounding. */ +static REAL_VALUE_TYPE dconstp5; +static REAL_VALUE_TYPE dconstmp5; + +/* This is the main program of the back-end. It sets up all the table + structures and then generates code. */ + +void +gigi (gnat_root, max_gnat_node, number_name, + nodes_ptr, next_node_ptr, prev_node_ptr, elists_ptr, elmts_ptr, + strings_ptr, string_chars_ptr, list_headers_ptr, + number_units, file_info_ptr, + standard_integer, standard_long_long_float, standard_exception_type, + gigi_operating_mode) + + Node_Id gnat_root; + int max_gnat_node; + int number_name; + + struct Node *nodes_ptr; + Node_Id *next_node_ptr; + Node_Id *prev_node_ptr; + struct Elist_Header *elists_ptr; + struct Elmt_Item *elmts_ptr; + struct String_Entry *strings_ptr; + Char_Code *string_chars_ptr; + struct List_Header *list_headers_ptr; + Int number_units ATTRIBUTE_UNUSED; + char *file_info_ptr ATTRIBUTE_UNUSED; + + Entity_Id standard_integer; + Entity_Id standard_long_long_float; + Entity_Id standard_exception_type; + + Int gigi_operating_mode; +{ + max_gnat_nodes = max_gnat_node; + number_names = number_name; + Nodes_Ptr = nodes_ptr - First_Node_Id; + Next_Node_Ptr = next_node_ptr - First_Node_Id; + Prev_Node_Ptr = prev_node_ptr - First_Node_Id; + Elists_Ptr = elists_ptr - First_Elist_Id; + Elmts_Ptr = elmts_ptr - First_Elmt_Id; + Strings_Ptr = strings_ptr - First_String_Id; + String_Chars_Ptr = string_chars_ptr; + List_Headers_Ptr = list_headers_ptr - First_List_Id; + + type_annotate_only = (gigi_operating_mode == 1); + + /* See if we should discard file names in exception messages. */ + discard_file_names = (Global_Discard_Names || Debug_Flag_NN); + + if (Nkind (gnat_root) != N_Compilation_Unit) + gigi_abort (301); + + set_lineno (gnat_root, 0); + + /* Initialize ourselves. */ + init_gnat_to_gnu (); + init_dummy_type (); + init_code_table (); + + /* Enable GNAT stack checking method if needed */ + if (!Stack_Check_Probes_On_Target) + set_stack_check_libfunc (gen_rtx (SYMBOL_REF, Pmode, "_gnat_stack_check")); + + /* Save the type we made for integer as the type for Standard.Integer. + Then make the rest of the standard types. Note that some of these + may be subtypes. */ + save_gnu_tree (Base_Type (standard_integer), + TYPE_NAME (integer_type_node), 0); + + ggc_add_tree_root (&gnu_block_stack, 1); + ggc_add_tree_root (&gnu_except_ptr_stack, 1); + ggc_add_tree_root (&gnu_return_label_stack, 1); + gnu_except_ptr_stack = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); + + dconstp5 = REAL_VALUE_ATOF ("0.5", DFmode); + dconstmp5 = REAL_VALUE_ATOF ("-0.5", DFmode); + + init_gigi_decls (gnat_to_gnu_entity (Base_Type (standard_long_long_float), + NULL_TREE, 0), + gnat_to_gnu_entity (Base_Type (standard_exception_type), + NULL_TREE, 0)); + + /* Emit global symbols containing context list info for the SGI Workshop + debugger */ + +#ifdef MIPS_DEBUGGING_INFO + if (Spec_Context_List != 0) + emit_unit_label (Spec_Context_List, Spec_Filename); + + if (Body_Context_List != 0) + emit_unit_label (Body_Context_List, Body_Filename); +#endif + +#ifdef ASM_OUTPUT_IDENT + if (Present (Ident_String (Main_Unit))) + ASM_OUTPUT_IDENT + (asm_out_file, + TREE_STRING_POINTER (gnat_to_gnu (Ident_String (Main_Unit)))); +#endif + + gnat_to_code (gnat_root); +} + + +/* This function is the driver of the GNAT to GCC tree transformation process. + GNAT_NODE is the root of some gnat tree. It generates code for that + part of the tree. */ + +void +gnat_to_code (gnat_node) + Node_Id gnat_node; +{ + tree gnu_root; + + /* Save node number in case error */ + error_gnat_node = gnat_node; + + gnu_root = tree_transform (gnat_node); + + /* This should just generate code, not return a value. If it returns + a value, something is wrong. */ + if (gnu_root != error_mark_node) + gigi_abort (302); +} + +/* GNAT_NODE is the root of some GNAT tree. Return the root of the GCC + tree corresponding to that GNAT tree. Normally, no code is generated. + We just return an equivalent tree which is used elsewhere to generate + code. */ + +tree +gnat_to_gnu (gnat_node) + Node_Id gnat_node; +{ + tree gnu_root; + + /* Save node number in case error */ + error_gnat_node = gnat_node; + + gnu_root = tree_transform (gnat_node); + + /* If we got no code as a result, something is wrong. */ + if (gnu_root == error_mark_node && ! type_annotate_only) + gigi_abort (303); + + return gnu_root; +} + +/* This function is the driver of the GNAT to GCC tree transformation process. + It is the entry point of the tree transformer. GNAT_NODE is the root of + some GNAT tree. Return the root of the corresponding GCC tree or + error_mark_node to signal that there is no GCC tree to return. + + The latter is the case if only code generation actions have to be performed + like in the case of if statements, loops, etc. This routine is wrapped + in the above two routines for most purposes. */ + +static tree +tree_transform (gnat_node) + Node_Id gnat_node; +{ + tree gnu_result = error_mark_node; /* Default to no value. */ + tree gnu_result_type = void_type_node; + tree gnu_expr; + tree gnu_lhs, gnu_rhs; + Node_Id gnat_temp; + Entity_Id gnat_temp_type; + + /* Set input_file_name and lineno from the Sloc in the GNAT tree. */ + set_lineno (gnat_node, 0); + + /* If this is a Statement and we are at top level, we add the statement + as an elaboration for a null tree. That will cause it to be placed + in the elaboration procedure. */ + if (global_bindings_p () + && ((IN (Nkind (gnat_node), N_Statement_Other_Than_Procedure_Call) + && Nkind (gnat_node) != N_Null_Statement) + || Nkind (gnat_node) == N_Procedure_Call_Statement + || Nkind (gnat_node) == N_Label + || (Nkind (gnat_node) == N_Handled_Sequence_Of_Statements + && (Present (Exception_Handlers (gnat_node)) + || Present (At_End_Proc (gnat_node)))) + || ((Nkind (gnat_node) == N_Raise_Constraint_Error + || Nkind (gnat_node) == N_Raise_Storage_Error + || Nkind (gnat_node) == N_Raise_Program_Error) + && (Ekind (Etype (gnat_node)) == E_Void)))) + { + add_pending_elaborations (NULL_TREE, make_transform_expr (gnat_node)); + + return error_mark_node; + } + + /* If this node is a non-static subexpression and we are only + annotating types, make this into a NULL_EXPR for non-VOID types + and error_mark_node for void return types. But allow + N_Identifier since we use it for lots of things, including + getting trees for discriminants. */ + + if (type_annotate_only + && IN (Nkind (gnat_node), N_Subexpr) + && Nkind (gnat_node) != N_Identifier + && ! Compile_Time_Known_Value (gnat_node)) + { + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + if (TREE_CODE (gnu_result_type) == VOID_TYPE) + return error_mark_node; + else + return build1 (NULL_EXPR, gnu_result_type, + build_call_raise (raise_constraint_error_decl)); + } + + switch (Nkind (gnat_node)) + { + /********************************/ + /* Chapter 2: Lexical Elements: */ + /********************************/ + + case N_Identifier: + case N_Expanded_Name: + case N_Operator_Symbol: + case N_Defining_Identifier: + + /* If the Etype of this node does not equal the Etype of the + Entity, something is wrong with the entity map, probably in + generic instantiation. However, this does not apply to + types. Since we sometime have strange Ekind's, just do + this test for objects. Also, if the Etype of the Entity + is private, the Etype of the N_Identifier is allowed to be the + full type and also we consider a packed array type to be the + same as the original type. Finally, if the types are Itypes, + one may be a copy of the other, which is also legal. */ + + gnat_temp = (Nkind (gnat_node) == N_Defining_Identifier + ? gnat_node : Entity (gnat_node)); + gnat_temp_type = Etype (gnat_temp); + + if (Etype (gnat_node) != gnat_temp_type + && ! (Is_Packed (gnat_temp_type) + && Etype (gnat_node) == Packed_Array_Type (gnat_temp_type)) + && ! (IN (Ekind (gnat_temp_type), Private_Kind) + && Present (Full_View (gnat_temp_type)) + && ((Etype (gnat_node) == Full_View (gnat_temp_type)) + || (Is_Packed (Full_View (gnat_temp_type)) + && Etype (gnat_node) == + Packed_Array_Type (Full_View (gnat_temp_type))))) + && (!Is_Itype (Etype (gnat_node)) || !Is_Itype (gnat_temp_type)) + && (Ekind (gnat_temp) == E_Variable + || Ekind (gnat_temp) == E_Component + || Ekind (gnat_temp) == E_Constant + || Ekind (gnat_temp) == E_Loop_Parameter + || IN (Ekind (gnat_temp), Formal_Kind))) + gigi_abort (304); + + /* If this is a reference to a deferred constant whose partial view + is an unconstrained private type, the proper type is on the full + view of the constant, not on the full view of the type, which may + be unconstrained. + + This may be a reference to a type, for example in the prefix of the + attribute Position, generated for dispatching code (see Make_DT in + exp_disp,adb). In that case we need the type itself, not is parent, + in particular if it is a derived type */ + + if (Is_Private_Type (gnat_temp_type) + && Has_Unknown_Discriminants (gnat_temp_type) + && Present (Full_View (gnat_temp)) + && ! Is_Type (gnat_temp)) + { + gnat_temp = Full_View (gnat_temp); + gnat_temp_type = Etype (gnat_temp); + gnu_result_type = get_unpadded_type (gnat_temp_type); + } + else + { + /* Expand the type of this identitier first, in case it is + an enumeral literal, which only get made when the type + is expanded. There is no order-of-elaboration issue here. + We want to use the Actual_Subtype if it has already been + elaborated, otherwise the Etype. Avoid using Actual_Subtype + for packed arrays to simplify things. */ + if ((Ekind (gnat_temp) == E_Constant + || Ekind (gnat_temp) == E_Variable || Is_Formal (gnat_temp)) + && ! (Is_Array_Type (Etype (gnat_temp)) + && Present (Packed_Array_Type (Etype (gnat_temp)))) + && Present (Actual_Subtype (gnat_temp)) + && present_gnu_tree (Actual_Subtype (gnat_temp))) + gnat_temp_type = Actual_Subtype (gnat_temp); + else + gnat_temp_type = Etype (gnat_node); + + gnu_result_type = get_unpadded_type (gnat_temp_type); + } + + gnu_result = gnat_to_gnu_entity (gnat_temp, NULL_TREE, 0); + + /* If we are in an exception handler, force this variable into memory + to ensure optimization does not remove stores that appear + redundant but are actually needed in case an exception occurs. + + ??? Note that we need not do this if the variable is declared within + the handler, only if it is referenced in the handler and declared + in an enclosing block, but we have no way of testing that + right now. */ + if (TREE_VALUE (gnu_except_ptr_stack) != 0) + { + mark_addressable (gnu_result); + flush_addressof (gnu_result); + } + + /* Some objects (such as parameters passed by reference, globals of + variable size, and renamed objects) actually represent the address + of the object. In that case, we must do the dereference. Likewise, + deal with parameters to foreign convention subprograms. Call fold + here since GNU_RESULT may be a CONST_DECL. */ + if (DECL_P (gnu_result) + && (DECL_BY_REF_P (gnu_result) + || DECL_BY_COMPONENT_PTR_P (gnu_result))) + { + int ro = DECL_POINTS_TO_READONLY_P (gnu_result); + + if (DECL_BY_COMPONENT_PTR_P (gnu_result)) + gnu_result = convert (build_pointer_type (gnu_result_type), + gnu_result); + + gnu_result = build_unary_op (INDIRECT_REF, NULL_TREE, + fold (gnu_result)); + TREE_READONLY (gnu_result) = TREE_STATIC (gnu_result) = ro; + } + + /* The GNAT tree has the type of a function as the type of its result. + Also use the type of the result if the Etype is a subtype which + is nominally unconstrained. But remove any padding from the + resulting type. */ + if (TREE_CODE (TREE_TYPE (gnu_result)) == FUNCTION_TYPE + || Is_Constr_Subt_For_UN_Aliased (gnat_temp_type)) + { + gnu_result_type = TREE_TYPE (gnu_result); + if (TREE_CODE (gnu_result_type) == RECORD_TYPE + && TYPE_IS_PADDING_P (gnu_result_type)) + gnu_result_type = TREE_TYPE (TYPE_FIELDS (gnu_result_type)); + } + + /* We always want to return the underlying INTEGER_CST for an + enumeration literal to avoid the need to call fold in lots + of places. But don't do this is the parent will be taking + the address of this object. */ + if (TREE_CODE (gnu_result) == CONST_DECL) + { + gnat_temp = Parent (gnat_node); + if (DECL_CONST_CORRESPONDING_VAR (gnu_result) == 0 + || (Nkind (gnat_temp) != N_Reference + && ! (Nkind (gnat_temp) == N_Attribute_Reference + && ((Get_Attribute_Id (Attribute_Name (gnat_temp)) + == Attr_Address) + || (Get_Attribute_Id (Attribute_Name (gnat_temp)) + == Attr_Access) + || (Get_Attribute_Id (Attribute_Name (gnat_temp)) + == Attr_Unchecked_Access) + || (Get_Attribute_Id (Attribute_Name (gnat_temp)) + == Attr_Unrestricted_Access))))) + gnu_result = DECL_INITIAL (gnu_result); + } + break; + + case N_Integer_Literal: + { + tree gnu_type; + + /* Get the type of the result, looking inside any padding and + left-justified modular types. Then get the value in that type. */ + gnu_type = gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + if (TREE_CODE (gnu_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (gnu_type)) + gnu_type = TREE_TYPE (TYPE_FIELDS (gnu_type)); + + gnu_result = UI_To_gnu (Intval (gnat_node), gnu_type); + /* Get the type of the result, looking inside any padding and + left-justified modular types. Then get the value in that type. */ + gnu_type = gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + if (TREE_CODE (gnu_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (gnu_type)) + gnu_type = TREE_TYPE (TYPE_FIELDS (gnu_type)); + + gnu_result = UI_To_gnu (Intval (gnat_node), gnu_type); + + /* If the result overflows (meaning it doesn't fit in its base type) + or is outside of the range of the subtype, we have an illegal tree + entry, so abort. Note that the test for of types with biased + representation is harder, so we don't test in that case. */ + if (TREE_CONSTANT_OVERFLOW (gnu_result) + || (TREE_CODE (TYPE_MIN_VALUE (gnu_result_type)) == INTEGER_CST + && ! TYPE_BIASED_REPRESENTATION_P (gnu_result_type) + && tree_int_cst_lt (gnu_result, + TYPE_MIN_VALUE (gnu_result_type))) + || (TREE_CODE (TYPE_MAX_VALUE (gnu_result_type)) == INTEGER_CST + && ! TYPE_BIASED_REPRESENTATION_P (gnu_result_type) + && tree_int_cst_lt (TYPE_MAX_VALUE (gnu_result_type), + gnu_result))) + gigi_abort (305); + } + break; + + case N_Character_Literal: + /* If a Entity is present, it means that this was one of the + literals in a user-defined character type. In that case, + just return the value in the CONST_DECL. Otherwise, use the + character code. In that case, the base type should be an + INTEGER_TYPE, but we won't bother checking for that. */ + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + if (Present (Entity (gnat_node))) + gnu_result = DECL_INITIAL (get_gnu_tree (Entity (gnat_node))); + else + gnu_result = convert (gnu_result_type, + build_int_2 (Char_Literal_Value (gnat_node), 0)); + break; + + case N_Real_Literal: + /* If this is of a fixed-point type, the value we want is the + value of the corresponding integer. */ + if (IN (Ekind (Underlying_Type (Etype (gnat_node))), Fixed_Point_Kind)) + { + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = UI_To_gnu (Corresponding_Integer_Value (gnat_node), + gnu_result_type); + if (TREE_CONSTANT_OVERFLOW (gnu_result) +#if 0 + || (TREE_CODE (TYPE_MIN_VALUE (gnu_result_type)) == INTEGER_CST + && tree_int_cst_lt (gnu_result, + TYPE_MIN_VALUE (gnu_result_type))) + || (TREE_CODE (TYPE_MAX_VALUE (gnu_result_type)) == INTEGER_CST + && tree_int_cst_lt (TYPE_MAX_VALUE (gnu_result_type), + gnu_result)) +#endif + ) + gigi_abort (305); + } + /* We should never see a Vax_Float type literal, since the front end + is supposed to transform these using appropriate conversions */ + else if (Vax_Float (Underlying_Type (Etype (gnat_node)))) + gigi_abort (334); + + else + { + Ureal ur_realval = Realval (gnat_node); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + /* If the real value is zero, so is the result. Otherwise, + convert it to a machine number if it isn't already. That + forces BASE to 0 or 2 and simplifies the rest of our logic. */ + if (UR_Is_Zero (ur_realval)) + gnu_result = convert (gnu_result_type, integer_zero_node); + else + { + if (! Is_Machine_Number (gnat_node)) + ur_realval = + Machine (Base_Type (Underlying_Type (Etype (gnat_node))), + ur_realval); + + gnu_result + = UI_To_gnu (Numerator (ur_realval), gnu_result_type); + + /* If we have a base of zero, divide by the denominator. + Otherwise, the base must be 2 and we scale the value, which + we know can fit in the mantissa of the type (hence the use + of that type above). */ + if (Rbase (ur_realval) == 0) + gnu_result + = build_binary_op (RDIV_EXPR, + get_base_type (gnu_result_type), + gnu_result, + UI_To_gnu (Denominator (ur_realval), + gnu_result_type)); + else if (Rbase (ur_realval) != 2) + gigi_abort (336); + + else + gnu_result + = build_real (gnu_result_type, + REAL_VALUE_LDEXP + (TREE_REAL_CST (gnu_result), + - UI_To_Int (Denominator (ur_realval)))); + } + + /* Now see if we need to negate the result. Do it this way to + properly handle -0. */ + if (UR_Is_Negative (Realval (gnat_node))) + gnu_result + = build_unary_op (NEGATE_EXPR, get_base_type (gnu_result_type), + gnu_result); + } + + break; + + case N_String_Literal: + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + if (TYPE_PRECISION (TREE_TYPE (gnu_result_type)) == HOST_BITS_PER_CHAR) + { + /* We assume here that all strings are of type standard.string. + "Weird" types of string have been converted to an aggregate + by the expander. */ + String_Id gnat_string = Strval (gnat_node); + int length = String_Length (gnat_string); + char *string = (char *) alloca (length + 1); + int i; + + /* Build the string with the characters in the literal. Note + that Ada strings are 1-origin. */ + for (i = 0; i < length; i++) + string[i] = Get_String_Char (gnat_string, i + 1); + + /* Put a null at the end of the string in case it's in a context + where GCC will want to treat it as a C string. */ + string[i] = 0; + + gnu_result = build_string (length, string); + + /* Strings in GCC don't normally have types, but we want + this to not be converted to the array type. */ + TREE_TYPE (gnu_result) = gnu_result_type; + } + else + { + /* Build a list consisting of each character, then make + the aggregate. */ + String_Id gnat_string = Strval (gnat_node); + int length = String_Length (gnat_string); + int i; + tree gnu_list = NULL_TREE; + + for (i = 0; i < length; i++) + gnu_list + = tree_cons (NULL_TREE, + convert (TREE_TYPE (gnu_result_type), + build_int_2 (Get_String_Char (gnat_string, + i + 1), + 0)), + gnu_list); + + gnu_result + = build_constructor (gnu_result_type, nreverse (gnu_list)); + } + break; + + case N_Pragma: + if (type_annotate_only) + break; + + /* Check for (and ignore) unrecognized pragma */ + if (! Is_Pragma_Name (Chars (gnat_node))) + break; + + switch (Get_Pragma_Id (Chars (gnat_node))) + { + case Pragma_Inspection_Point: + /* Do nothing at top level: all such variables are already + viewable. */ + if (global_bindings_p ()) + break; + + set_lineno (gnat_node, 1); + for (gnat_temp = First (Pragma_Argument_Associations (gnat_node)); + Present (gnat_temp); + gnat_temp = Next (gnat_temp)) + { + gnu_expr = gnat_to_gnu (Expression (gnat_temp)); + if (TREE_CODE (gnu_expr) == UNCONSTRAINED_ARRAY_REF) + gnu_expr = TREE_OPERAND (gnu_expr, 0); + + gnu_expr = build1 (USE_EXPR, void_type_node, gnu_expr); + TREE_SIDE_EFFECTS (gnu_expr) = 1; + expand_expr_stmt (gnu_expr); + } + break; + + case Pragma_Optimize: + switch (Chars (Expression + (First (Pragma_Argument_Associations (gnat_node))))) + { + case Name_Time: case Name_Space: + if (optimize == 0) + post_error ("insufficient -O value?", gnat_node); + break; + + case Name_Off: + if (optimize != 0) + post_error ("must specify -O0?", gnat_node); + break; + + default: + gigi_abort (331); + break; + } + break; + + case Pragma_Reviewable: + if (write_symbols == NO_DEBUG) + post_error ("must specify -g?", gnat_node); + break; + } + break; + + /**************************************/ + /* Chapter 3: Declarations and Types: */ + /**************************************/ + + case N_Subtype_Declaration: + case N_Full_Type_Declaration: + case N_Incomplete_Type_Declaration: + case N_Private_Type_Declaration: + case N_Private_Extension_Declaration: + case N_Task_Type_Declaration: + process_type (Defining_Entity (gnat_node)); + break; + + case N_Object_Declaration: + case N_Exception_Declaration: + gnat_temp = Defining_Entity (gnat_node); + + /* If we are just annotating types and this object has an unconstrained + or task type, don't elaborate it. */ + if (type_annotate_only + && (((Is_Array_Type (Etype (gnat_temp)) + || Is_Record_Type (Etype (gnat_temp))) + && ! Is_Constrained (Etype (gnat_temp))) + || Is_Concurrent_Type (Etype (gnat_temp)))) + break; + + if (Present (Expression (gnat_node)) + && ! (Nkind (gnat_node) == N_Object_Declaration + && No_Initialization (gnat_node)) + && (! type_annotate_only + || Compile_Time_Known_Value (Expression (gnat_node)))) + { + gnu_expr = gnat_to_gnu (Expression (gnat_node)); + if (Do_Range_Check (Expression (gnat_node))) + gnu_expr = emit_range_check (gnu_expr, Etype (gnat_temp)); + + /* If this object has its elaboration delayed, we must force + evaluation of GNU_EXPR right now and save it for when the object + is frozen. */ + if (Present (Freeze_Node (gnat_temp))) + { + if ((Is_Public (gnat_temp) || global_bindings_p ()) + && ! TREE_CONSTANT (gnu_expr)) + gnu_expr + = create_var_decl (create_concat_name (gnat_temp, "init"), + NULL_TREE, TREE_TYPE (gnu_expr), gnu_expr, + 0, Is_Public (gnat_temp), 0, 0, 0); + else + gnu_expr = maybe_variable (gnu_expr, Expression (gnat_node)); + + save_gnu_tree (gnat_node, gnu_expr, 1); + } + } + else + gnu_expr = 0; + + if (type_annotate_only && gnu_expr != 0 + && TREE_CODE (gnu_expr) == ERROR_MARK) + gnu_expr = 0; + + if (No (Freeze_Node (gnat_temp))) + gnat_to_gnu_entity (gnat_temp, gnu_expr, 1); + break; + + case N_Object_Renaming_Declaration: + + gnat_temp = Defining_Entity (gnat_node); + + /* Don't do anything if this renaming handled by the front end. + or if we are just annotating types and this object has an + unconstrained or task type, don't elaborate it. */ + if (! Is_Renaming_Of_Object (gnat_temp) + && ! (type_annotate_only + && (((Is_Array_Type (Etype (gnat_temp)) + || Is_Record_Type (Etype (gnat_temp))) + && ! Is_Constrained (Etype (gnat_temp))) + || Is_Concurrent_Type (Etype (gnat_temp))))) + { + gnu_expr = gnat_to_gnu (Renamed_Object (gnat_temp)); + gnat_to_gnu_entity (gnat_temp, gnu_expr, 1); + } + break; + + case N_Implicit_Label_Declaration: + gnat_to_gnu_entity (Defining_Entity (gnat_node), NULL_TREE, 1); + break; + + case N_Subprogram_Renaming_Declaration: + case N_Package_Renaming_Declaration: + case N_Exception_Renaming_Declaration: + case N_Number_Declaration: + /* These are fully handled in the front end. */ + break; + + /*************************************/ + /* Chapter 4: Names and Expressions: */ + /*************************************/ + + case N_Explicit_Dereference: + gnu_result = gnat_to_gnu (Prefix (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + /* Emit access check if necessary */ + if (Do_Access_Check (gnat_node)) + gnu_result = emit_access_check (gnu_result); + + gnu_result = build_unary_op (INDIRECT_REF, NULL_TREE, gnu_result); + break; + + case N_Indexed_Component: + { + tree gnu_array_object = gnat_to_gnu (Prefix (gnat_node)); + tree gnu_type; + int ndim; + int i; + Node_Id *gnat_expr_array; + + /* Emit access check if necessary */ + if (Do_Access_Check (gnat_node)) + gnu_array_object = emit_access_check (gnu_array_object); + + gnu_array_object = maybe_implicit_deref (gnu_array_object); + gnu_array_object = maybe_unconstrained_array (gnu_array_object); + + /* If we got a padded type, remove it too. */ + if (TREE_CODE (TREE_TYPE (gnu_array_object)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (gnu_array_object))) + gnu_array_object + = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (gnu_array_object))), + gnu_array_object); + + gnu_result = gnu_array_object; + + /* First compute the number of dimensions of the array, then + fill the expression array, the order depending on whether + this is a Convention_Fortran array or not. */ + for (ndim = 1, gnu_type = TREE_TYPE (gnu_array_object); + TREE_CODE (TREE_TYPE (gnu_type)) == ARRAY_TYPE + && TYPE_MULTI_ARRAY_P (TREE_TYPE (gnu_type)); + ndim++, gnu_type = TREE_TYPE (gnu_type)) + ; + + gnat_expr_array = (Node_Id *) alloca (ndim * sizeof (Node_Id)); + + if (TYPE_CONVENTION_FORTRAN_P (TREE_TYPE (gnu_array_object))) + for (i = ndim - 1, gnat_temp = First (Expressions (gnat_node)); + i >= 0; + i--, gnat_temp = Next (gnat_temp)) + gnat_expr_array[i] = gnat_temp; + else + for (i = 0, gnat_temp = First (Expressions (gnat_node)); + i < ndim; + i++, gnat_temp = Next (gnat_temp)) + gnat_expr_array[i] = gnat_temp; + + for (i = 0, gnu_type = TREE_TYPE (gnu_array_object); + i < ndim; i++, gnu_type = TREE_TYPE (gnu_type)) + { + if (TREE_CODE (gnu_type) != ARRAY_TYPE) + gigi_abort (307); + + gnat_temp = gnat_expr_array[i]; + gnu_expr = gnat_to_gnu (gnat_temp); + + if (Do_Range_Check (gnat_temp)) + gnu_expr + = emit_index_check + (gnu_array_object, gnu_expr, + TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type))), + TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type)))); + + gnu_result = build_binary_op (ARRAY_REF, NULL_TREE, + gnu_result, gnu_expr); + } + } + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + break; + + case N_Slice: + { + tree gnu_type; + Node_Id gnat_range_node = Discrete_Range (gnat_node); + + gnu_result = gnat_to_gnu (Prefix (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + /* Emit access check if necessary */ + if (Do_Access_Check (gnat_node)) + gnu_result = emit_access_check (gnu_result); + + /* Do any implicit dereferences of the prefix and do any needed + range check. */ + gnu_result = maybe_implicit_deref (gnu_result); + gnu_result = maybe_unconstrained_array (gnu_result); + gnu_type = TREE_TYPE (gnu_result); + if (Do_Range_Check (gnat_range_node)) + { + /* Get the bounds of the slice. */ + tree gnu_index_type + = TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_result_type)); + tree gnu_min_expr = TYPE_MIN_VALUE (gnu_index_type); + tree gnu_max_expr = TYPE_MAX_VALUE (gnu_index_type); + tree gnu_expr_l, gnu_expr_h, gnu_expr_type; + + /* Check to see that the minimum slice value is in range */ + gnu_expr_l + = emit_index_check + (gnu_result, gnu_min_expr, + TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type))), + TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type)))); + + /* Check to see that the maximum slice value is in range */ + gnu_expr_h + = emit_index_check + (gnu_result, gnu_max_expr, + TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type))), + TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type)))); + + /* Derive a good type to convert everything too */ + gnu_expr_type = get_base_type (TREE_TYPE (gnu_expr_l)); + + /* Build a compound expression that does the range checks */ + gnu_expr + = build_binary_op (COMPOUND_EXPR, gnu_expr_type, + convert (gnu_expr_type, gnu_expr_h), + convert (gnu_expr_type, gnu_expr_l)); + + /* Build a conditional expression that returns the range checks + expression if the slice range is not null (max >= min) or + returns the min if the slice range is null */ + gnu_expr + = fold (build (COND_EXPR, gnu_expr_type, + build_binary_op (GE_EXPR, gnu_expr_type, + convert (gnu_expr_type, + gnu_max_expr), + convert (gnu_expr_type, + gnu_min_expr)), + gnu_expr, gnu_min_expr)); + } + else + gnu_expr = TYPE_MIN_VALUE (TYPE_DOMAIN (gnu_result_type)); + + gnu_result = build_binary_op (ARRAY_RANGE_REF, gnu_result_type, + gnu_result, gnu_expr); + } + break; + + case N_Selected_Component: + { + tree gnu_prefix = gnat_to_gnu (Prefix (gnat_node)); + Entity_Id gnat_field = Entity (Selector_Name (gnat_node)); + Entity_Id gnat_pref_type = Etype (Prefix (gnat_node)); + tree gnu_field; + + while (IN (Ekind (gnat_pref_type), Incomplete_Or_Private_Kind) + || IN (Ekind (gnat_pref_type), Access_Kind)) + { + if (IN (Ekind (gnat_pref_type), Incomplete_Or_Private_Kind)) + gnat_pref_type = Underlying_Type (gnat_pref_type); + else if (IN (Ekind (gnat_pref_type), Access_Kind)) + gnat_pref_type = Designated_Type (gnat_pref_type); + } + + if (Do_Access_Check (gnat_node)) + gnu_prefix = emit_access_check (gnu_prefix); + + gnu_prefix = maybe_implicit_deref (gnu_prefix); + + /* For discriminant references in tagged types always substitute the + corresponding discriminant as the actual selected component. */ + + if (Is_Tagged_Type (gnat_pref_type)) + while (Present (Corresponding_Discriminant (gnat_field))) + gnat_field = Corresponding_Discriminant (gnat_field); + + /* For discriminant references of untagged types always substitute the + corresponding girder discriminant. */ + + else if (Present (Corresponding_Discriminant (gnat_field))) + gnat_field = Original_Record_Component (gnat_field); + + /* Handle extracting the real or imaginary part of a complex. + The real part is the first field and the imaginary the last. */ + + if (TREE_CODE (TREE_TYPE (gnu_prefix)) == COMPLEX_TYPE) + gnu_result = build_unary_op (Present (Next_Entity (gnat_field)) + ? REALPART_EXPR : IMAGPART_EXPR, + NULL_TREE, gnu_prefix); + else + { + gnu_field = gnat_to_gnu_entity (gnat_field, NULL_TREE, 0); + + /* If there are discriminants, the prefix might be + evaluated more than once, which is a problem if it has + side-effects. */ + + if (Has_Discriminants (Is_Access_Type (Etype (Prefix (gnat_node))) + ? Designated_Type (Etype + (Prefix (gnat_node))) + : Etype (Prefix (gnat_node))) + && TREE_SIDE_EFFECTS (gnu_prefix)) + gnu_prefix = make_save_expr (gnu_prefix); + + /* Emit discriminant check if necessary. */ + if (Do_Discriminant_Check (gnat_node)) + gnu_prefix = emit_discriminant_check (gnu_prefix, gnat_node); + gnu_result + = build_component_ref (gnu_prefix, NULL_TREE, gnu_field); + } + + if (gnu_result == 0) + gigi_abort (308); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + } + break; + + case N_Attribute_Reference: + { + /* The attribute designator (like an enumeration value). */ + int attribute = Get_Attribute_Id (Attribute_Name (gnat_node)); + int prefix_unused = 0; + tree gnu_prefix; + tree gnu_type; + + /* The Elab_Spec and Elab_Body attributes are special in that + Prefix is a unit, not an object with a GCC equivalent. Similarly + for Elaborated, since that variable isn't otherwise known. */ + if (attribute == Attr_Elab_Body || attribute == Attr_Elab_Spec) + { + gnu_prefix + = create_subprog_decl + (create_concat_name (Entity (Prefix (gnat_node)), + attribute == Attr_Elab_Body + ? "elabb" : "elabs"), + NULL_TREE, void_ftype, NULL_TREE, 0, 1, 1, 0); + return gnu_prefix; + } + + gnu_prefix = gnat_to_gnu (Prefix (gnat_node)); + gnu_type = TREE_TYPE (gnu_prefix); + + /* If the input is a NULL_EXPR, make a new one. */ + if (TREE_CODE (gnu_prefix) == NULL_EXPR) + { + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = build1 (NULL_EXPR, gnu_result_type, + TREE_OPERAND (gnu_prefix, 0)); + break; + } + + switch (attribute) + { + case Attr_Pos: + case Attr_Val: + /* These are just conversions until since representation + clauses for enumerations are handled in the front end. */ + { + int check_p = Do_Range_Check (First (Expressions (gnat_node))); + + gnu_result = gnat_to_gnu (First (Expressions (gnat_node))); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = convert_with_check (Etype (gnat_node), gnu_result, + check_p, check_p, 1); + } + break; + + case Attr_Pred: + case Attr_Succ: + /* These just add or subject the constant 1. Representation + clauses for enumerations are handled in the front-end. */ + gnu_expr = gnat_to_gnu (First (Expressions (gnat_node))); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + if (Do_Range_Check (First (Expressions (gnat_node)))) + { + gnu_expr = make_save_expr (gnu_expr); + gnu_expr + = emit_check + (build_binary_op (EQ_EXPR, integer_type_node, + gnu_expr, + attribute == Attr_Pred + ? TYPE_MIN_VALUE (gnu_result_type) + : TYPE_MAX_VALUE (gnu_result_type)), + gnu_expr); + } + + gnu_result + = build_binary_op (attribute == Attr_Pred + ? MINUS_EXPR : PLUS_EXPR, + gnu_result_type, gnu_expr, + convert (gnu_result_type, integer_one_node)); + break; + + case Attr_Address: + case Attr_Unrestricted_Access: + + /* Conversions don't change something's address but can cause + us to miss the COMPONENT_REF case below, so strip them off. */ + gnu_prefix = remove_conversions (gnu_prefix); + + /* If we are taking 'Address of an unconstrained object, + this is the pointer to the underlying array. */ + gnu_prefix = maybe_unconstrained_array (gnu_prefix); + + /* ... fall through ... */ + + case Attr_Access: + case Attr_Unchecked_Access: + case Attr_Code_Address: + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result + = build_unary_op (attribute == Attr_Address + || attribute == Attr_Unrestricted_Access + ? ATTR_ADDR_EXPR : ADDR_EXPR, + gnu_result_type, gnu_prefix); + + /* For 'Code_Address, find an inner ADDR_EXPR and mark it + so that we don't try to build a trampoline. */ + if (attribute == Attr_Code_Address) + { + for (gnu_expr = gnu_result; + TREE_CODE (gnu_expr) == NOP_EXPR + || TREE_CODE (gnu_expr) == CONVERT_EXPR; + gnu_expr = TREE_OPERAND (gnu_expr, 0)) + TREE_CONSTANT (gnu_expr) = 1; + ; + + if (TREE_CODE (gnu_expr) == ADDR_EXPR) + TREE_STATIC (gnu_expr) = TREE_CONSTANT (gnu_expr) = 1; + } + + break; + + case Attr_Size: + case Attr_Object_Size: + case Attr_Value_Size: + case Attr_Max_Size_In_Storage_Elements: + + gnu_expr = gnu_prefix; + + /* Remove NOPS from gnu_expr and conversions from gnu_prefix. + We only use GNU_EXPR to see if a COMPONENT_REF was involved. */ + while (TREE_CODE (gnu_expr) == NOP_EXPR) + gnu_expr = TREE_OPERAND (gnu_expr, 0); + + gnu_prefix = remove_conversions (gnu_prefix); + prefix_unused = 1; + gnu_type = TREE_TYPE (gnu_prefix); + + /* Replace an unconstrained array type with the type of the + underlying array. We can't do this with a call to + maybe_unconstrained_array since we may have a TYPE_DECL. + For 'Max_Size_In_Storage_Elements, use the record type + that will be used to allocate the object and its template. */ + + if (TREE_CODE (gnu_type) == UNCONSTRAINED_ARRAY_TYPE) + { + gnu_type = TYPE_OBJECT_RECORD_TYPE (gnu_type); + if (attribute != Attr_Max_Size_In_Storage_Elements) + gnu_type = TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (gnu_type))); + } + + /* If we are looking for the size of a field, return the + field size. Otherwise, if the prefix is an object, + or if 'Object_Size or 'Max_Size_In_Storage_Elements has + been specified, the result is the GCC size of the type. + Otherwise, the result is the RM_Size of the type. */ + if (TREE_CODE (gnu_prefix) == COMPONENT_REF) + gnu_result = DECL_SIZE (TREE_OPERAND (gnu_prefix, 1)); + else if (TREE_CODE (gnu_prefix) != TYPE_DECL + || attribute == Attr_Object_Size + || attribute == Attr_Max_Size_In_Storage_Elements) + { + /* If this is a padded type, the GCC size isn't relevant + to the programmer. Normally, what we want is the RM_Size, + which was set from the specified size, but if it was not + set, we want the size of the relevant field. Using the MAX + of those two produces the right result in all case. Don't + use the size of the field if it's a self-referential type, + since that's never what's wanted. */ + if (TREE_CODE (gnu_type) == RECORD_TYPE + && TYPE_IS_PADDING_P (gnu_type) + && TREE_CODE (gnu_expr) == COMPONENT_REF) + { + gnu_result = rm_size (gnu_type); + if (! (contains_placeholder_p + (DECL_SIZE (TREE_OPERAND (gnu_expr, 1))))) + gnu_result + = size_binop (MAX_EXPR, gnu_result, + DECL_SIZE (TREE_OPERAND (gnu_expr, 1))); + } + else + gnu_result = TYPE_SIZE (gnu_type); + } + else + gnu_result = rm_size (gnu_type); + + if (gnu_result == 0) + gigi_abort (325); + + /* Deal with a self-referential size by returning the maximum + size for a type and by qualifying the size with + the object for 'Size of an object. */ + + if (TREE_CODE (gnu_result) != INTEGER_CST + && contains_placeholder_p (gnu_result)) + { + if (TREE_CODE (gnu_prefix) != TYPE_DECL) + gnu_result = build (WITH_RECORD_EXPR, TREE_TYPE (gnu_result), + gnu_result, gnu_prefix); + else + gnu_result = max_size (gnu_result, 1); + } + + /* If the type contains a template, subtract the size of the + template. */ + if (TREE_CODE (gnu_type) == RECORD_TYPE + && TYPE_CONTAINS_TEMPLATE_P (gnu_type)) + gnu_result = size_binop (MINUS_EXPR, gnu_result, + DECL_SIZE (TYPE_FIELDS (gnu_type))); + + /* If the type contains a template, subtract the size of the + template. */ + if (TREE_CODE (gnu_type) == RECORD_TYPE + && TYPE_CONTAINS_TEMPLATE_P (gnu_type)) + gnu_result = size_binop (MINUS_EXPR, gnu_result, + DECL_SIZE (TYPE_FIELDS (gnu_type))); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + /* Always perform division using unsigned arithmetic as the + size cannot be negative, but may be an overflowed positive + value. This provides correct results for sizes up to 512 MB. + ??? Size should be calculated in storage elements directly. */ + + if (attribute == Attr_Max_Size_In_Storage_Elements) + gnu_result = convert (sizetype, + fold (build (CEIL_DIV_EXPR, bitsizetype, + gnu_result, + bitsize_unit_node))); + break; + + case Attr_Alignment: + if (TREE_CODE (gnu_prefix) == COMPONENT_REF + && (TREE_CODE (TREE_TYPE (TREE_OPERAND (gnu_prefix, 0))) + == RECORD_TYPE) + && (TYPE_IS_PADDING_P + (TREE_TYPE (TREE_OPERAND (gnu_prefix, 0))))) + gnu_prefix = TREE_OPERAND (gnu_prefix, 0); + + gnu_type = TREE_TYPE (gnu_prefix); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + prefix_unused = 1; + + if (TREE_CODE (gnu_prefix) == COMPONENT_REF) + gnu_result + = size_int (DECL_ALIGN (TREE_OPERAND (gnu_prefix, 1))); + else + gnu_result = size_int (TYPE_ALIGN (gnu_type) / BITS_PER_UNIT); + break; + + case Attr_First: + case Attr_Last: + case Attr_Range_Length: + prefix_unused = 1; + + if (INTEGRAL_TYPE_P (gnu_type) + || TREE_CODE (gnu_type) == REAL_TYPE) + { + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + if (attribute == Attr_First) + gnu_result = TYPE_MIN_VALUE (gnu_type); + else if (attribute == Attr_Last) + gnu_result = TYPE_MAX_VALUE (gnu_type); + else + gnu_result + = build_binary_op + (MAX_EXPR, get_base_type (gnu_result_type), + build_binary_op + (PLUS_EXPR, get_base_type (gnu_result_type), + build_binary_op (MINUS_EXPR, + get_base_type (gnu_result_type), + convert (gnu_result_type, + TYPE_MAX_VALUE (gnu_type)), + convert (gnu_result_type, + TYPE_MIN_VALUE (gnu_type))), + convert (gnu_result_type, integer_one_node)), + convert (gnu_result_type, integer_zero_node)); + + break; + } + /* ... fall through ... */ + case Attr_Length: + { + int Dimension + = (Present (Expressions (gnat_node)) + ? UI_To_Int (Intval (First (Expressions (gnat_node)))) + : 1); + + /* Emit access check if necessary */ + if (Do_Access_Check (gnat_node)) + gnu_prefix = emit_access_check (gnu_prefix); + + /* Make sure any implicit dereference gets done. */ + gnu_prefix = maybe_implicit_deref (gnu_prefix); + gnu_prefix = maybe_unconstrained_array (gnu_prefix); + gnu_type = TREE_TYPE (gnu_prefix); + prefix_unused = 1; + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + if (TYPE_CONVENTION_FORTRAN_P (gnu_type)) + { + int ndim; + tree gnu_type_temp; + + for (ndim = 1, gnu_type_temp = gnu_type; + TREE_CODE (TREE_TYPE (gnu_type_temp)) == ARRAY_TYPE + && TYPE_MULTI_ARRAY_P (TREE_TYPE (gnu_type_temp)); + ndim++, gnu_type_temp = TREE_TYPE (gnu_type_temp)) + ; + + Dimension = ndim + 1 - Dimension; + } + + for (; Dimension > 1; Dimension--) + gnu_type = TREE_TYPE (gnu_type); + + if (TREE_CODE (gnu_type) != ARRAY_TYPE) + gigi_abort (309); + + if (attribute == Attr_First) + gnu_result + = TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type))); + else if (attribute == Attr_Last) + gnu_result + = TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type))); + else + /* 'Length or 'Range_Length. */ + { + tree gnu_compute_type + = signed_or_unsigned_type + (0, get_base_type (gnu_result_type)); + + gnu_result + = build_binary_op + (MAX_EXPR, gnu_compute_type, + build_binary_op + (PLUS_EXPR, gnu_compute_type, + build_binary_op + (MINUS_EXPR, gnu_compute_type, + convert (gnu_compute_type, + TYPE_MAX_VALUE + (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type)))), + convert (gnu_compute_type, + TYPE_MIN_VALUE + (TYPE_INDEX_TYPE (TYPE_DOMAIN (gnu_type))))), + convert (gnu_compute_type, integer_one_node)), + convert (gnu_compute_type, integer_zero_node)); + } + + /* If this has a PLACEHOLDER_EXPR, qualify it by the object + we are handling. Note that these attributes could not + have been used on an unconstrained array type. */ + if (TREE_CODE (gnu_result) != INTEGER_CST + && contains_placeholder_p (gnu_result)) + gnu_result = build (WITH_RECORD_EXPR, TREE_TYPE (gnu_result), + gnu_result, gnu_prefix); + + break; + } + + case Attr_Bit_Position: + case Attr_Position: + case Attr_First_Bit: + case Attr_Last_Bit: + case Attr_Bit: + { + HOST_WIDE_INT bitsize; + HOST_WIDE_INT bitpos; + tree gnu_offset; + tree gnu_field_bitpos; + tree gnu_field_offset; + tree gnu_inner; + enum machine_mode mode; + int unsignedp, volatilep; + unsigned int alignment; + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_prefix = remove_conversions (gnu_prefix); + prefix_unused = 1; + + /* We can have 'Bit on any object, but if it isn't a + COMPONENT_REF, the result is zero. Do not allow + 'Bit on a bare component, though. */ + if (attribute == Attr_Bit + && TREE_CODE (gnu_prefix) != COMPONENT_REF + && TREE_CODE (gnu_prefix) != FIELD_DECL) + { + gnu_result = integer_zero_node; + break; + } + + else if (TREE_CODE (gnu_prefix) != COMPONENT_REF + && ! (attribute == Attr_Bit_Position + && TREE_CODE (gnu_prefix) == FIELD_DECL)) + gigi_abort (310); + + get_inner_reference (gnu_prefix, &bitsize, &bitpos, &gnu_offset, + &mode, &unsignedp, &volatilep, &alignment); + + + if (TREE_CODE (gnu_prefix) == COMPONENT_REF) + { + gnu_field_bitpos + = bit_position (TREE_OPERAND (gnu_prefix, 1)); + gnu_field_offset + = byte_position (TREE_OPERAND (gnu_prefix, 1)); + + for (gnu_inner = TREE_OPERAND (gnu_prefix, 0); + TREE_CODE (gnu_inner) == COMPONENT_REF + && DECL_INTERNAL_P (TREE_OPERAND (gnu_inner, 1)); + gnu_inner = TREE_OPERAND (gnu_inner, 0)) + { + gnu_field_bitpos + = size_binop (PLUS_EXPR, gnu_field_bitpos, + bit_position (TREE_OPERAND (gnu_inner, + 1))); + gnu_field_offset + = size_binop (PLUS_EXPR, gnu_field_offset, + byte_position (TREE_OPERAND (gnu_inner, + 1))); + } + } + else if (TREE_CODE (gnu_prefix) == FIELD_DECL) + { + gnu_field_bitpos = bit_position (gnu_prefix); + gnu_field_offset = byte_position (gnu_prefix); + } + else + { + gnu_field_bitpos = bitsize_zero_node; + gnu_field_offset = size_zero_node; + } + + switch (attribute) + { + case Attr_Position: + gnu_result = gnu_field_offset; + break; + + + case Attr_First_Bit: + case Attr_Bit: + gnu_result = size_int (bitpos % BITS_PER_UNIT); + break; + + + case Attr_Last_Bit: + gnu_result = bitsize_int (bitpos % BITS_PER_UNIT); + gnu_result + = size_binop (PLUS_EXPR, gnu_result, + TYPE_SIZE (TREE_TYPE (gnu_prefix))); + gnu_result = size_binop (MINUS_EXPR, gnu_result, + bitsize_one_node); + break; + + case Attr_Bit_Position: + gnu_result = gnu_field_bitpos; + break; + } + + /* If this has a PLACEHOLDER_EXPR, qualify it by the object + we are handling. */ + if (TREE_CODE (gnu_result) != INTEGER_CST + && contains_placeholder_p (gnu_result)) + gnu_result = build (WITH_RECORD_EXPR, TREE_TYPE (gnu_result), + gnu_result, gnu_prefix); + + break; + } + + case Attr_Min: + case Attr_Max: + gnu_lhs = gnat_to_gnu (First (Expressions (gnat_node))); + gnu_rhs = gnat_to_gnu (Next (First (Expressions (gnat_node)))); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = build_binary_op (attribute == Attr_Min + ? MIN_EXPR : MAX_EXPR, + gnu_result_type, gnu_lhs, gnu_rhs); + break; + + case Attr_Passed_By_Reference: + gnu_result = size_int (default_pass_by_ref (gnu_type) + || must_pass_by_ref (gnu_type)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + break; + + case Attr_Component_Size: + if (TREE_CODE (gnu_prefix) == COMPONENT_REF + && (TREE_CODE (TREE_TYPE (TREE_OPERAND (gnu_prefix, 0))) + == RECORD_TYPE) + && (TYPE_IS_PADDING_P + (TREE_TYPE (TREE_OPERAND (gnu_prefix, 0))))) + gnu_prefix = TREE_OPERAND (gnu_prefix, 0); + + gnu_prefix = maybe_implicit_deref (gnu_prefix); + gnu_type = TREE_TYPE (gnu_prefix); + + if (TREE_CODE (gnu_type) == UNCONSTRAINED_ARRAY_TYPE) + gnu_type + = TREE_TYPE (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (gnu_type)))); + + while (TREE_CODE (TREE_TYPE (gnu_type)) == ARRAY_TYPE + && TYPE_MULTI_ARRAY_P (TREE_TYPE (gnu_type))) + gnu_type = TREE_TYPE (gnu_type); + + if (TREE_CODE (gnu_type) != ARRAY_TYPE) + gigi_abort (330); + + /* Note this size cannot be self-referential. */ + gnu_result = TYPE_SIZE (TREE_TYPE (gnu_type)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + prefix_unused = 1; + break; + + case Attr_Null_Parameter: + /* This is just a zero cast to the pointer type for + our prefix and dereferenced. */ + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result + = build_unary_op (INDIRECT_REF, NULL_TREE, + convert (build_pointer_type (gnu_result_type), + integer_zero_node)); + TREE_PRIVATE (gnu_result) = 1; + break; + + case Attr_Mechanism_Code: + { + int code; + Entity_Id gnat_obj = Entity (Prefix (gnat_node)); + + prefix_unused = 1; + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + if (Present (Expressions (gnat_node))) + { + int i = UI_To_Int (Intval (First (Expressions (gnat_node)))); + + for (gnat_obj = First_Formal (gnat_obj); i > 1; + i--, gnat_obj = Next_Formal (gnat_obj)) + ; + } + + code = Mechanism (gnat_obj); + if (code == Default) + code = ((present_gnu_tree (gnat_obj) + && (DECL_BY_REF_P (get_gnu_tree (gnat_obj)) + || (DECL_BY_COMPONENT_PTR_P + (get_gnu_tree (gnat_obj))))) + ? By_Reference : By_Copy); + gnu_result = convert (gnu_result_type, size_int (- code)); + } + break; + + default: + /* Say we have an unimplemented attribute. Then set the + value to be returned to be a zero and hope that's something + we can convert to the type of this attribute. */ + + post_error ("unimplemented attribute", gnat_node); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = integer_zero_node; + break; + } + + /* If this is an attribute where the prefix was unused, + force a use of it if it has a side-effect. */ + if (prefix_unused && TREE_SIDE_EFFECTS (gnu_prefix)) + gnu_result = fold (build (COMPOUND_EXPR, TREE_TYPE (gnu_result), + gnu_prefix, gnu_result)); + } + break; + + case N_Reference: + /* Like 'Access as far as we are concerned. */ + gnu_result = gnat_to_gnu (Prefix (gnat_node)); + gnu_result = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_result); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + break; + + case N_Aggregate: + case N_Extension_Aggregate: + { + tree gnu_aggr_type; + + /* ??? It is wrong to evaluate the type now, but there doesn't + seem to be any other practical way of doing it. */ + + gnu_aggr_type = gnu_result_type + = get_unpadded_type (Etype (gnat_node)); + + if (TREE_CODE (gnu_result_type) == RECORD_TYPE + && TYPE_CONTAINS_TEMPLATE_P (gnu_result_type)) + gnu_aggr_type + = TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (gnu_result_type))); + + if (Null_Record_Present (gnat_node)) + gnu_result = build_constructor (gnu_aggr_type, NULL_TREE); + + else if (TREE_CODE (gnu_aggr_type) == RECORD_TYPE) + gnu_result + = assoc_to_constructor (First (Component_Associations (gnat_node)), + gnu_aggr_type); + else if (TREE_CODE (gnu_aggr_type) == UNION_TYPE) + { + /* The first element is the discrimant, which we ignore. The + next is the field we're building. Convert the expression + to the type of the field and then to the union type. */ + Node_Id gnat_assoc + = Next (First (Component_Associations (gnat_node))); + Entity_Id gnat_field = Entity (First (Choices (gnat_assoc))); + tree gnu_field_type + = TREE_TYPE (gnat_to_gnu_entity (gnat_field, NULL_TREE, 0)); + + gnu_result = convert (gnu_field_type, + gnat_to_gnu (Expression (gnat_assoc))); + } + else if (TREE_CODE (gnu_aggr_type) == ARRAY_TYPE) + gnu_result = pos_to_constructor (First (Expressions (gnat_node)), + gnu_aggr_type, + Component_Type (Etype (gnat_node))); + else if (TREE_CODE (gnu_aggr_type) == COMPLEX_TYPE) + gnu_result + = build_binary_op + (COMPLEX_EXPR, gnu_aggr_type, + gnat_to_gnu (Expression (First + (Component_Associations (gnat_node)))), + gnat_to_gnu (Expression + (Next + (First (Component_Associations (gnat_node)))))); + else + gigi_abort (312); + + gnu_result = convert (gnu_result_type, gnu_result); + } + break; + + case N_Null: + gnu_result = null_pointer_node; + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + break; + + case N_Type_Conversion: + case N_Qualified_Expression: + /* Get the operand expression. */ + gnu_result = gnat_to_gnu (Expression (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + gnu_result + = convert_with_check (Etype (gnat_node), gnu_result, + Do_Overflow_Check (gnat_node), + Do_Range_Check (Expression (gnat_node)), + Nkind (gnat_node) == N_Type_Conversion + && Float_Truncate (gnat_node)); + break; + + case N_Unchecked_Type_Conversion: + gnu_result = gnat_to_gnu (Expression (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + /* If the result is a pointer type, see if we are improperly + converting to a stricter alignment. */ + + if (STRICT_ALIGNMENT && POINTER_TYPE_P (gnu_result_type) + && IN (Ekind (Etype (gnat_node)), Access_Kind)) + { + unsigned int align = known_alignment (gnu_result); + tree gnu_obj_type = TREE_TYPE (gnu_result_type); + unsigned int oalign + = TREE_CODE (gnu_obj_type) == FUNCTION_TYPE + ? FUNCTION_BOUNDARY : TYPE_ALIGN (gnu_obj_type); + + if (align != 0 && align < oalign && ! TYPE_ALIGN_OK_P (gnu_obj_type)) + post_error_ne_tree_2 + ("?source alignment (^) < alignment of & (^)", + gnat_node, Designated_Type (Etype (gnat_node)), + size_int (align / BITS_PER_UNIT), oalign / BITS_PER_UNIT); + } + + gnu_result = unchecked_convert (gnu_result_type, gnu_result); + break; + + case N_In: + case N_Not_In: + { + tree gnu_object = gnat_to_gnu (Left_Opnd (gnat_node)); + Node_Id gnat_range = Right_Opnd (gnat_node); + tree gnu_low; + tree gnu_high; + + /* GNAT_RANGE is either an N_Range node or an identifier + denoting a subtype. */ + if (Nkind (gnat_range) == N_Range) + { + gnu_low = gnat_to_gnu (Low_Bound (gnat_range)); + gnu_high = gnat_to_gnu (High_Bound (gnat_range)); + } + else if (Nkind (gnat_range) == N_Identifier + || Nkind (gnat_range) == N_Expanded_Name) + { + tree gnu_range_type = get_unpadded_type (Entity (gnat_range)); + + gnu_low = TYPE_MIN_VALUE (gnu_range_type); + gnu_high = TYPE_MAX_VALUE (gnu_range_type); + } + else + gigi_abort (313); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + /* If LOW and HIGH are identical, perform an equality test. + Otherwise, ensure that GNU_OBJECT is only evaluated once + and perform a full range test. */ + if (operand_equal_p (gnu_low, gnu_high, 0)) + gnu_result = build_binary_op (EQ_EXPR, gnu_result_type, + gnu_object, gnu_low); + else + { + gnu_object = make_save_expr (gnu_object); + gnu_result + = build_binary_op (TRUTH_ANDIF_EXPR, gnu_result_type, + build_binary_op (GE_EXPR, gnu_result_type, + gnu_object, gnu_low), + build_binary_op (LE_EXPR, gnu_result_type, + gnu_object, gnu_high)); + } + + if (Nkind (gnat_node) == N_Not_In) + gnu_result = invert_truthvalue (gnu_result); + } + break; + + case N_Op_Divide: + gnu_lhs = gnat_to_gnu (Left_Opnd (gnat_node)); + gnu_rhs = gnat_to_gnu (Right_Opnd (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = build_binary_op (FLOAT_TYPE_P (gnu_result_type) + ? RDIV_EXPR + : (Rounded_Result (gnat_node) + ? ROUND_DIV_EXPR : TRUNC_DIV_EXPR), + gnu_result_type, gnu_lhs, gnu_rhs); + break; + + case N_And_Then: case N_Or_Else: + { + enum tree_code code = gnu_codes[Nkind (gnat_node)]; + tree gnu_rhs_side; + + /* The elaboration of the RHS may generate code. If so, + we need to make sure it gets executed after the LHS. */ + gnu_lhs = gnat_to_gnu (Left_Opnd (gnat_node)); + clear_last_expr (); + gnu_rhs_side = expand_start_stmt_expr (); + gnu_rhs = gnat_to_gnu (Right_Opnd (gnat_node)); + expand_end_stmt_expr (gnu_rhs_side); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + if (RTL_EXPR_SEQUENCE (gnu_rhs_side) != 0) + gnu_rhs = build (COMPOUND_EXPR, gnu_result_type, gnu_rhs_side, + gnu_rhs); + + gnu_result = build_binary_op (code, gnu_result_type, gnu_lhs, gnu_rhs); + } + break; + + case N_Op_Or: case N_Op_And: case N_Op_Xor: + /* These can either be operations on booleans or on modular types. + Fall through for boolean types since that's the way GNU_CODES is + set up. */ + if (IN (Ekind (Underlying_Type (Etype (gnat_node))), + Modular_Integer_Kind)) + { + enum tree_code code + = (Nkind (gnat_node) == N_Op_Or ? BIT_IOR_EXPR + : Nkind (gnat_node) == N_Op_And ? BIT_AND_EXPR + : BIT_XOR_EXPR); + + gnu_lhs = gnat_to_gnu (Left_Opnd (gnat_node)); + gnu_rhs = gnat_to_gnu (Right_Opnd (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = build_binary_op (code, gnu_result_type, + gnu_lhs, gnu_rhs); + break; + } + + /* ... fall through ... */ + + case N_Op_Eq: case N_Op_Ne: case N_Op_Lt: + case N_Op_Le: case N_Op_Gt: case N_Op_Ge: + case N_Op_Add: case N_Op_Subtract: case N_Op_Multiply: + case N_Op_Mod: case N_Op_Rem: + case N_Op_Rotate_Left: + case N_Op_Rotate_Right: + case N_Op_Shift_Left: + case N_Op_Shift_Right: + case N_Op_Shift_Right_Arithmetic: + { + enum tree_code code = gnu_codes[Nkind (gnat_node)]; + tree gnu_type; + + gnu_lhs = gnat_to_gnu (Left_Opnd (gnat_node)); + gnu_rhs = gnat_to_gnu (Right_Opnd (gnat_node)); + gnu_type = gnu_result_type = get_unpadded_type (Etype (gnat_node)); + + /* If this is a comparison operator, convert any references to + an unconstrained array value into a reference to the + actual array. */ + if (TREE_CODE_CLASS (code) == '<') + { + gnu_lhs = maybe_unconstrained_array (gnu_lhs); + gnu_rhs = maybe_unconstrained_array (gnu_rhs); + } + + /* If this is a shift whose count is not guaranteed to be correct, + we need to adjust the shift count. */ + if (IN (Nkind (gnat_node), N_Op_Shift) + && ! Shift_Count_OK (gnat_node)) + { + tree gnu_count_type = get_base_type (TREE_TYPE (gnu_rhs)); + tree gnu_max_shift + = convert (gnu_count_type, TYPE_SIZE (gnu_type)); + + if (Nkind (gnat_node) == N_Op_Rotate_Left + || Nkind (gnat_node) == N_Op_Rotate_Right) + gnu_rhs = build_binary_op (TRUNC_MOD_EXPR, gnu_count_type, + gnu_rhs, gnu_max_shift); + else if (Nkind (gnat_node) == N_Op_Shift_Right_Arithmetic) + gnu_rhs + = build_binary_op + (MIN_EXPR, gnu_count_type, + build_binary_op (MINUS_EXPR, + gnu_count_type, + gnu_max_shift, + convert (gnu_count_type, + integer_one_node)), + gnu_rhs); + } + + /* For right shifts, the type says what kind of shift to do, + so we may need to choose a different type. */ + if (Nkind (gnat_node) == N_Op_Shift_Right + && ! TREE_UNSIGNED (gnu_type)) + gnu_type = unsigned_type (gnu_type); + else if (Nkind (gnat_node) == N_Op_Shift_Right_Arithmetic + && TREE_UNSIGNED (gnu_type)) + gnu_type = signed_type (gnu_type); + + if (gnu_type != gnu_result_type) + { + gnu_lhs = convert (gnu_type, gnu_lhs); + gnu_rhs = convert (gnu_type, gnu_rhs); + } + + gnu_result = build_binary_op (code, gnu_type, gnu_lhs, gnu_rhs); + + /* If this is a logical shift with the shift count not verified, + we must return zero if it is too large. We cannot compensate + above in this case. */ + if ((Nkind (gnat_node) == N_Op_Shift_Left + || Nkind (gnat_node) == N_Op_Shift_Right) + && ! Shift_Count_OK (gnat_node)) + gnu_result + = build_cond_expr + (gnu_type, + build_binary_op (GE_EXPR, integer_type_node, + gnu_rhs, + convert (TREE_TYPE (gnu_rhs), + TYPE_SIZE (gnu_type))), + convert (gnu_type, integer_zero_node), + gnu_result); + } + break; + + case N_Conditional_Expression: + { + tree gnu_cond = gnat_to_gnu (First (Expressions (gnat_node))); + tree gnu_true = gnat_to_gnu (Next (First (Expressions (gnat_node)))); + tree gnu_false + = gnat_to_gnu (Next (Next (First (Expressions (gnat_node))))); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = build_cond_expr (gnu_result_type, + truthvalue_conversion (gnu_cond), + gnu_true, gnu_false); + } + break; + + case N_Op_Plus: + gnu_result = gnat_to_gnu (Right_Opnd (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + break; + + case N_Op_Not: + /* This case can apply to a boolean or a modular type. + Fall through for a boolean operand since GNU_CODES is set + up to handle this. */ + if (IN (Ekind (Etype (gnat_node)), Modular_Integer_Kind)) + { + gnu_expr = gnat_to_gnu (Right_Opnd (gnat_node)); + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result = build_unary_op (BIT_NOT_EXPR, gnu_result_type, + gnu_expr); + break; + } + + /* ... fall through ... */ + + case N_Op_Minus: case N_Op_Abs: + gnu_expr = gnat_to_gnu (Right_Opnd (gnat_node)); + + if (Ekind (Etype (gnat_node)) != E_Private_Type) + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + else + gnu_result_type = get_unpadded_type (Base_Type + (Full_View (Etype (gnat_node)))); + + gnu_result = build_unary_op (gnu_codes[Nkind (gnat_node)], + gnu_result_type, gnu_expr); + break; + + case N_Allocator: + { + tree gnu_init = 0; + tree gnu_type; + + gnat_temp = Expression (gnat_node); + + /* The Expression operand can either be an N_Identifier or + Expanded_Name, which must represent a type, or a + N_Qualified_Expression, which contains both the object type and an + initial value for the object. */ + if (Nkind (gnat_temp) == N_Identifier + || Nkind (gnat_temp) == N_Expanded_Name) + gnu_type = gnat_to_gnu_type (Entity (gnat_temp)); + else if (Nkind (gnat_temp) == N_Qualified_Expression) + { + Entity_Id gnat_desig_type + = Designated_Type (Underlying_Type (Etype (gnat_node))); + + gnu_init = gnat_to_gnu (Expression (gnat_temp)); + + gnu_init = maybe_unconstrained_array (gnu_init); + if (Do_Range_Check (Expression (gnat_temp))) + gnu_init = emit_range_check (gnu_init, gnat_desig_type); + + if (Is_Elementary_Type (gnat_desig_type) + || Is_Constrained (gnat_desig_type)) + { + gnu_type = gnat_to_gnu_type (gnat_desig_type); + gnu_init = convert (gnu_type, gnu_init); + } + else + { + gnu_type = gnat_to_gnu_type (Etype (Expression (gnat_temp))); + if (TREE_CODE (gnu_type) == UNCONSTRAINED_ARRAY_TYPE) + gnu_type = TREE_TYPE (gnu_init); + + gnu_init = convert (gnu_type, gnu_init); + } + } + else + gigi_abort (315); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + return build_allocator (gnu_type, gnu_init, gnu_result_type, + Procedure_To_Call (gnat_node), + Storage_Pool (gnat_node)); + } + break; + + /***************************/ + /* Chapter 5: Statements: */ + /***************************/ + + case N_Label: + if (! type_annotate_only) + { + tree gnu_label = gnat_to_gnu (Identifier (gnat_node)); + Node_Id gnat_parent = Parent (gnat_node); + + expand_label (gnu_label); + + /* If this is the first label of an exception handler, we must + mark that any CALL_INSN can jump to it. */ + if (Present (gnat_parent) + && Nkind (gnat_parent) == N_Exception_Handler + && First (Statements (gnat_parent)) == gnat_node) + nonlocal_goto_handler_labels + = gen_rtx_EXPR_LIST (VOIDmode, label_rtx (gnu_label), + nonlocal_goto_handler_labels); + } + break; + + case N_Null_Statement: + break; + + case N_Assignment_Statement: + if (type_annotate_only) + break; + + /* Get the LHS and RHS of the statement and convert any reference to an + unconstrained array into a reference to the underlying array. */ + gnu_lhs = maybe_unconstrained_array (gnat_to_gnu (Name (gnat_node))); + gnu_rhs + = maybe_unconstrained_array (gnat_to_gnu (Expression (gnat_node))); + + /* If range check is needed, emit code to generate it */ + if (Do_Range_Check (Expression (gnat_node))) + gnu_rhs = emit_range_check (gnu_rhs, Etype (Name (gnat_node))); + + set_lineno (gnat_node, 1); + + /* If either side's type has a size that overflows, convert this + into raise of Storage_Error: execution shouldn't have gotten + here anyway. */ + if ((TREE_CODE (TYPE_SIZE (TREE_TYPE (gnu_lhs))) == INTEGER_CST + && TREE_OVERFLOW (TYPE_SIZE (TREE_TYPE (gnu_lhs)))) + || (TREE_CODE (TYPE_SIZE (TREE_TYPE (gnu_rhs))) == INTEGER_CST + && TREE_OVERFLOW (TYPE_SIZE (TREE_TYPE (gnu_rhs))))) + expand_expr_stmt (build_call_raise (raise_storage_error_decl)); + else + expand_expr_stmt (build_binary_op (MODIFY_EXPR, NULL_TREE, + gnu_lhs, gnu_rhs)); + break; + + case N_If_Statement: + /* Start an IF statement giving the condition. */ + gnu_expr = gnat_to_gnu (Condition (gnat_node)); + set_lineno (gnat_node, 1); + expand_start_cond (gnu_expr, 0); + + /* Generate code for the statements to be executed if the condition + is true. */ + + for (gnat_temp = First (Then_Statements (gnat_node)); + Present (gnat_temp); + gnat_temp = Next (gnat_temp)) + gnat_to_code (gnat_temp); + + /* Generate each of the "else if" parts. */ + if (Present (Elsif_Parts (gnat_node))) + { + for (gnat_temp = First (Elsif_Parts (gnat_node)); + Present (gnat_temp); + gnat_temp = Next (gnat_temp)) + { + Node_Id gnat_statement; + + expand_start_else (); + + /* Set up the line numbers for each condition we test. */ + set_lineno (Condition (gnat_temp), 1); + expand_elseif (gnat_to_gnu (Condition (gnat_temp))); + + for (gnat_statement = First (Then_Statements (gnat_temp)); + Present (gnat_statement); + gnat_statement = Next (gnat_statement)) + gnat_to_code (gnat_statement); + } + } + + /* Finally, handle any statements in the "else" part. */ + if (Present (Else_Statements (gnat_node))) + { + expand_start_else (); + + for (gnat_temp = First (Else_Statements (gnat_node)); + Present (gnat_temp); + gnat_temp = Next (gnat_temp)) + gnat_to_code (gnat_temp); + } + + expand_end_cond (); + break; + + case N_Case_Statement: + { + Node_Id gnat_when; + Node_Id gnat_choice; + tree gnu_label; + Node_Id gnat_statement; + + gnu_expr = gnat_to_gnu (Expression (gnat_node)); + gnu_expr = convert (get_base_type (TREE_TYPE (gnu_expr)), gnu_expr); + + set_lineno (gnat_node, 1); + expand_start_case (1, gnu_expr, TREE_TYPE (gnu_expr), "case"); + + for (gnat_when = First_Non_Pragma (Alternatives (gnat_node)); + Present (gnat_when); + gnat_when = Next_Non_Pragma (gnat_when)) + { + /* First compile all the different case choices for the current + WHEN alternative. */ + + for (gnat_choice = First (Discrete_Choices (gnat_when)); + Present (gnat_choice); gnat_choice = Next (gnat_choice)) + { + int error_code; + + gnu_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + set_lineno (gnat_choice, 1); + switch (Nkind (gnat_choice)) + { + case N_Range: + /* Abort on all errors except range empty, which + means we ignore this alternative. */ + error_code + = pushcase_range (gnat_to_gnu (Low_Bound (gnat_choice)), + gnat_to_gnu (High_Bound (gnat_choice)), + convert, gnu_label, 0); + + if (error_code != 0 && error_code != 4) + gigi_abort (332); + break; + + case N_Subtype_Indication: + error_code + = pushcase_range + (gnat_to_gnu (Low_Bound (Range_Expression + (Constraint (gnat_choice)))), + gnat_to_gnu (High_Bound (Range_Expression + (Constraint (gnat_choice)))), + convert, gnu_label, 0); + + if (error_code != 0 && error_code != 4) + gigi_abort (332); + break; + + case N_Identifier: + case N_Expanded_Name: + /* This represents either a subtype range or a static value + of some kind; Ekind says which. If a static value, + fall through to the next case. */ + if (IN (Ekind (Entity (gnat_choice)), Type_Kind)) + { + tree type = get_unpadded_type (Entity (gnat_choice)); + + error_code + = pushcase_range (fold (TYPE_MIN_VALUE (type)), + fold (TYPE_MAX_VALUE (type)), + convert, gnu_label, 0); + + if (error_code != 0 && error_code != 4) + gigi_abort (332); + break; + } + /* ... fall through ... */ + case N_Character_Literal: + case N_Integer_Literal: + if (pushcase (gnat_to_gnu (gnat_choice), convert, + gnu_label, 0)) + gigi_abort (332); + break; + + case N_Others_Choice: + if (pushcase (NULL_TREE, convert, gnu_label, 0)) + gigi_abort (332); + break; + + default: + gigi_abort (316); + } + } + + /* After compiling the choices attached to the WHEN compile the + body of statements that have to be executed, should the + "WHEN ... =>" be taken. */ + for (gnat_statement = First (Statements (gnat_when)); + Present (gnat_statement); + gnat_statement = Next (gnat_statement)) + gnat_to_code (gnat_statement); + + /* Communicate to GCC that we are done with the current WHEN, + i.e. insert a "break" statement. */ + expand_exit_something (); + } + + expand_end_case (gnu_expr); + } + break; + + case N_Loop_Statement: + { + /* The loop variable in GCC form, if any. */ + tree gnu_loop_var = NULL_TREE; + /* PREINCREMENT_EXPR or PREDECREMENT_EXPR. */ + enum tree_code gnu_update = ERROR_MARK; + /* Used if this is a named loop for so EXIT can work. */ + struct nesting *loop_id; + /* Condition to continue loop tested at top of loop. */ + tree gnu_top_condition = integer_one_node; + /* Similar, but tested at bottom of loop. */ + tree gnu_bottom_condition = integer_one_node; + Node_Id gnat_statement; + Node_Id gnat_iter_scheme = Iteration_Scheme (gnat_node); + Node_Id gnat_top_condition = Empty; + int enclosing_if_p = 0; + + /* Set the condition that under which the loop should continue. + For "LOOP .... END LOOP;" the condition is always true. */ + if (No (gnat_iter_scheme)) + ; + /* The case "WHILE condition LOOP ..... END LOOP;" */ + else if (Present (Condition (gnat_iter_scheme))) + gnat_top_condition = Condition (gnat_iter_scheme); + else + { + /* We have an iteration scheme. */ + Node_Id gnat_loop_spec + = Loop_Parameter_Specification (gnat_iter_scheme); + Entity_Id gnat_loop_var = Defining_Entity (gnat_loop_spec); + Entity_Id gnat_type = Etype (gnat_loop_var); + tree gnu_type = get_unpadded_type (gnat_type); + tree gnu_low = TYPE_MIN_VALUE (gnu_type); + tree gnu_high = TYPE_MAX_VALUE (gnu_type); + int reversep = Reverse_Present (gnat_loop_spec); + tree gnu_first = reversep ? gnu_high : gnu_low; + tree gnu_last = reversep ? gnu_low : gnu_high; + enum tree_code end_code = reversep ? GE_EXPR : LE_EXPR; + tree gnu_base_type = get_base_type (gnu_type); + tree gnu_limit + = (reversep ? TYPE_MIN_VALUE (gnu_base_type) + : TYPE_MAX_VALUE (gnu_base_type)); + + /* We know the loop variable will not overflow if GNU_LAST is + a constant and is not equal to GNU_LIMIT. If it might + overflow, we have to move the limit test to the end of + the loop. In that case, we have to test for an + empty loop outside the loop. */ + if (TREE_CODE (gnu_last) != INTEGER_CST + || TREE_CODE (gnu_limit) != INTEGER_CST + || tree_int_cst_equal (gnu_last, gnu_limit)) + { + gnu_expr = build_binary_op (LE_EXPR, integer_type_node, + gnu_low, gnu_high); + set_lineno (gnat_loop_spec, 1); + expand_start_cond (gnu_expr, 0); + enclosing_if_p = 1; + } + + /* Open a new nesting level that will surround the loop to declare + the loop index variable. */ + pushlevel (0); + expand_start_bindings (0); + + /* Declare the loop index and set it to its initial value. */ + gnu_loop_var = gnat_to_gnu_entity (gnat_loop_var, gnu_first, 1); + if (DECL_BY_REF_P (gnu_loop_var)) + gnu_loop_var = build_unary_op (INDIRECT_REF, NULL_TREE, + gnu_loop_var); + + /* The loop variable might be a padded type, so use `convert' to + get a reference to the inner variable if so. */ + gnu_loop_var = convert (get_base_type (gnu_type), gnu_loop_var); + + /* Set either the top or bottom exit condition as + appropriate depending on whether we know an overflow + cannot occur or not. */ + if (enclosing_if_p) + gnu_bottom_condition + = build_binary_op (NE_EXPR, integer_type_node, + gnu_loop_var, gnu_last); + else + gnu_top_condition + = build_binary_op (end_code, integer_type_node, + gnu_loop_var, gnu_last); + + gnu_update = reversep ? PREDECREMENT_EXPR : PREINCREMENT_EXPR; + } + + set_lineno (gnat_node, 1); + if (gnu_loop_var) + loop_id = expand_start_loop_continue_elsewhere (1); + else + loop_id = expand_start_loop (1); + + /* If the loop was named, have the name point to this loop. In this + case, the association is not a ..._DECL node; in fact, it isn't + a GCC tree node at all. Since this name is referenced inside + the loop, do it before we process the statements of the loop. */ + if (Present (Identifier (gnat_node))) + { + tree gnu_loop_id = make_node (GNAT_LOOP_ID); + + TREE_LOOP_ID (gnu_loop_id) = (rtx) loop_id; + save_gnu_tree (Entity (Identifier (gnat_node)), gnu_loop_id, 1); + } + + set_lineno (gnat_node, 1); + + /* We must evaluate the condition after we've entered the + loop so that any expression actions get done in the right + place. */ + if (Present (gnat_top_condition)) + gnu_top_condition = gnat_to_gnu (gnat_top_condition); + + expand_exit_loop_if_false (0, gnu_top_condition); + + /* Make the loop body into its own block, so any allocated + storage will be released every iteration. This is needed + for stack allocation. */ + + pushlevel (0); + gnu_block_stack + = tree_cons (gnu_bottom_condition, NULL_TREE, gnu_block_stack); + expand_start_bindings (0); + + for (gnat_statement = First (Statements (gnat_node)); + Present (gnat_statement); + gnat_statement = Next (gnat_statement)) + gnat_to_code (gnat_statement); + + expand_end_bindings (getdecls (), kept_level_p (), 0); + poplevel (kept_level_p (), 1, 0); + gnu_block_stack = TREE_CHAIN (gnu_block_stack); + + set_lineno (gnat_node, 1); + expand_exit_loop_if_false (0, gnu_bottom_condition); + + if (gnu_loop_var) + { + expand_loop_continue_here (); + gnu_expr = build_binary_op (gnu_update, TREE_TYPE (gnu_loop_var), + gnu_loop_var, + convert (TREE_TYPE (gnu_loop_var), + integer_one_node)); + set_lineno (gnat_iter_scheme, 1); + expand_expr_stmt (gnu_expr); + } + + set_lineno (gnat_node, 1); + expand_end_loop (); + + if (gnu_loop_var) + { + /* Close the nesting level that sourround the loop that was used to + declare the loop index variable. */ + set_lineno (gnat_node, 1); + expand_end_bindings (getdecls (), 1, 0); + poplevel (1, 1, 0); + } + + if (enclosing_if_p) + { + set_lineno (gnat_node, 1); + expand_end_cond (); + } + } + break; + + case N_Block_Statement: + pushlevel (0); + gnu_block_stack = tree_cons (NULL_TREE, NULL_TREE, gnu_block_stack); + expand_start_bindings (0); + process_decls (Declarations (gnat_node), Empty, Empty, 1, 1); + gnat_to_code (Handled_Statement_Sequence (gnat_node)); + expand_end_bindings (getdecls (), kept_level_p (), 0); + poplevel (kept_level_p (), 1, 0); + gnu_block_stack = TREE_CHAIN (gnu_block_stack); + if (Present (Identifier (gnat_node))) + mark_out_of_scope (Entity (Identifier (gnat_node))); + break; + + case N_Exit_Statement: + { + /* Which loop to exit, NULL if the current loop. */ + struct nesting *loop_id = 0; + /* The GCC version of the optional GNAT condition node attached to the + exit statement. Exit the loop if this is false. */ + tree gnu_cond = integer_zero_node; + + if (Present (Name (gnat_node))) + loop_id + = (struct nesting *) + TREE_LOOP_ID (get_gnu_tree (Entity (Name (gnat_node)))); + + if (Present (Condition (gnat_node))) + gnu_cond + = invert_truthvalue + (truthvalue_conversion (gnat_to_gnu (Condition (gnat_node)))); + + set_lineno (gnat_node, 1); + expand_exit_loop_if_false (loop_id, gnu_cond); + } + break; + + case N_Return_Statement: + if (type_annotate_only) + break; + + { + /* The gnu function type of the subprogram currently processed. */ + tree gnu_subprog_type = TREE_TYPE (current_function_decl); + /* The return value from the subprogram. */ + tree gnu_ret_val = 0; + + /* If we are dealing with a "return;" from an Ada procedure with + parameters passed by copy in copy out, we need to return a record + containing the final values of these parameters. If the list + contains only one entry, return just that entry. + + For a full description of the copy in copy out parameter mechanism, + see the part of the gnat_to_gnu_entity routine dealing with the + translation of subprograms. + + But if we have a return label defined, convert this into + a branch to that label. */ + + if (TREE_VALUE (gnu_return_label_stack) != 0) + expand_goto (TREE_VALUE (gnu_return_label_stack)); + + else if (TYPE_CI_CO_LIST (gnu_subprog_type) != NULL_TREE) + { + if (list_length (TYPE_CI_CO_LIST (gnu_subprog_type)) == 1) + gnu_ret_val = TREE_VALUE (TYPE_CI_CO_LIST (gnu_subprog_type)); + else + gnu_ret_val + = build_constructor (TREE_TYPE (gnu_subprog_type), + TYPE_CI_CO_LIST (gnu_subprog_type)); + } + + /* If the Ada subprogram is a function, we just need to return the + expression. If the subprogram returns an unconstrained + array, we have to allocate a new version of the result and + return it. If we return by reference, return a pointer. */ + + else if (Present (Expression (gnat_node))) + { + gnu_ret_val = gnat_to_gnu (Expression (gnat_node)); + + /* Do not remove the padding from GNU_RET_VAL if the inner + type is self-referential since we want to allocate the fixed + size in that case. */ + if (TREE_CODE (gnu_ret_val) == COMPONENT_REF + && (TYPE_IS_PADDING_P + (TREE_TYPE (TREE_OPERAND (gnu_ret_val, 0)))) + && contains_placeholder_p + (TYPE_SIZE (TREE_TYPE (gnu_ret_val)))) + gnu_ret_val = TREE_OPERAND (gnu_ret_val, 0); + + if (TYPE_RETURNS_BY_REF_P (gnu_subprog_type) + || By_Ref (gnat_node)) + gnu_ret_val = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_ret_val); + + else if (TYPE_RETURNS_UNCONSTRAINED_P (gnu_subprog_type)) + { + gnu_ret_val = maybe_unconstrained_array (gnu_ret_val); + + /* We have two cases: either the function returns with + depressed stack or not. If not, we allocate on the + secondary stack. If so, we allocate in the stack frame. + if no copy is needed, the front end will set By_Ref, + which we handle in the case above. */ + if (TYPE_RETURNS_STACK_DEPRESSED (gnu_subprog_type)) + gnu_ret_val + = build_allocator (TREE_TYPE (gnu_ret_val), gnu_ret_val, + TREE_TYPE (gnu_subprog_type), 0, -1); + else + gnu_ret_val + = build_allocator (TREE_TYPE (gnu_ret_val), gnu_ret_val, + TREE_TYPE (gnu_subprog_type), + Procedure_To_Call (gnat_node), + Storage_Pool (gnat_node)); + } + } + + set_lineno (gnat_node, 1); + if (gnu_ret_val) + expand_return (build_binary_op (MODIFY_EXPR, NULL_TREE, + DECL_RESULT (current_function_decl), + gnu_ret_val)); + else + expand_null_return (); + + } + break; + + case N_Goto_Statement: + if (type_annotate_only) + break; + + gnu_expr = gnat_to_gnu (Name (gnat_node)); + TREE_USED (gnu_expr) = 1; + set_lineno (gnat_node, 1); + expand_goto (gnu_expr); + break; + + /****************************/ + /* Chapter 6: Subprograms: */ + /****************************/ + + case N_Subprogram_Declaration: + /* Unless there is a freeze node, declare the subprogram. We consider + this a "definition" even though we're not generating code for + the subprogram because we will be making the corresponding GCC + node here. */ + + if (No (Freeze_Node (Defining_Entity (Specification (gnat_node))))) + gnat_to_gnu_entity (Defining_Entity (Specification (gnat_node)), + NULL_TREE, 1); + + break; + + case N_Abstract_Subprogram_Declaration: + /* This subprogram doesn't exist for code generation purposes, but we + have to elaborate the types of any parameters, unless they are + imported types (nothing to generate in this case). */ + for (gnat_temp + = First_Formal (Defining_Entity (Specification (gnat_node))); + Present (gnat_temp); + gnat_temp = Next_Formal_With_Extras (gnat_temp)) + if (Is_Itype (Etype (gnat_temp)) + && !From_With_Type (Etype (gnat_temp))) + gnat_to_gnu_entity (Etype (gnat_temp), NULL_TREE, 0); + + break; + + case N_Defining_Program_Unit_Name: + /* For a child unit identifier go up a level to get the + specificaton. We get this when we try to find the spec of + a child unit package that is the compilation unit being compiled. */ + gnat_to_code (Parent (gnat_node)); + break; + + case N_Subprogram_Body: + { + /* Save debug output mode in case it is reset. */ + enum debug_info_type save_write_symbols = write_symbols; + struct gcc_debug_hooks *save_debug_hooks = debug_hooks; + /* Definining identifier of a parameter to the subprogram. */ + Entity_Id gnat_param; + /* The defining identifier for the subprogram body. Note that if a + specification has appeared before for this body, then the identifier + occurring in that specification will also be a defining identifier + and all the calls to this subprogram will point to that + specification. */ + Entity_Id gnat_subprog_id + = (Present (Corresponding_Spec (gnat_node)) + ? Corresponding_Spec (gnat_node) : Defining_Entity (gnat_node)); + + /* The FUNCTION_DECL node corresponding to the subprogram spec. */ + tree gnu_subprog_decl; + /* The FUNCTION_TYPE node corresponding to the subprogram spec. */ + tree gnu_subprog_type; + tree gnu_cico_list; + + /* If this is a generic object or if it has been eliminated, + ignore it. */ + + if (Ekind (gnat_subprog_id) == E_Generic_Procedure + || Ekind (gnat_subprog_id) == E_Generic_Function + || Is_Eliminated (gnat_subprog_id)) + break; + + /* If debug information is suppressed for the subprogram, + turn debug mode off for the duration of processing. */ + if (Debug_Info_Off (gnat_subprog_id)) + { + write_symbols = NO_DEBUG; + debug_hooks = &do_nothing_debug_hooks; + } + + /* If this subprogram acts as its own spec, define it. Otherwise, + just get the already-elaborated tree node. However, if this + subprogram had its elaboration deferred, we will already have + made a tree node for it. So treat it as not being defined in + that case. Such a subprogram cannot have an address clause or + a freeze node, so this test is safe, though it does disable + some otherwise-useful error checking. */ + gnu_subprog_decl + = gnat_to_gnu_entity (gnat_subprog_id, NULL_TREE, + Acts_As_Spec (gnat_node) + && ! present_gnu_tree (gnat_subprog_id)); + + gnu_subprog_type = TREE_TYPE (gnu_subprog_decl); + + /* Set the line number in the decl to correspond to that of + the body so that the line number notes are written + correctly. */ + set_lineno (gnat_node, 0); + DECL_SOURCE_FILE (gnu_subprog_decl) = input_filename; + DECL_SOURCE_LINE (gnu_subprog_decl) = lineno; + + begin_subprog_body (gnu_subprog_decl); + set_lineno (gnat_node, 1); + + pushlevel (0); + gnu_block_stack = tree_cons (NULL_TREE, NULL_TREE, gnu_block_stack); + expand_start_bindings (0); + + gnu_cico_list = TYPE_CI_CO_LIST (gnu_subprog_type); + + /* If there are OUT parameters, we need to ensure that the + return statement properly copies them out. We do this by + making a new block and converting any inner return into a goto + to a label at the end of the block. */ + + if (gnu_cico_list != 0) + { + gnu_return_label_stack + = tree_cons (NULL_TREE, + build_decl (LABEL_DECL, NULL_TREE, NULL_TREE), + gnu_return_label_stack); + pushlevel (0); + expand_start_bindings (0); + } + else + gnu_return_label_stack + = tree_cons (NULL_TREE, NULL_TREE, gnu_return_label_stack); + + /* See if there are any parameters for which we don't yet have + GCC entities. These must be for OUT parameters for which we + will be making VAR_DECL nodes here. Fill them in to + TYPE_CI_CO_LIST, which must contain the empty entry as well. + We can match up the entries because TYPE_CI_CO_LIST is in the + order of the parameters. */ + + for (gnat_param = First_Formal (gnat_subprog_id); + Present (gnat_param); + gnat_param = Next_Formal_With_Extras (gnat_param)) + if (present_gnu_tree (gnat_param)) + adjust_decl_rtl (get_gnu_tree (gnat_param)); + else + { + /* Skip any entries that have been already filled in; they + must correspond to IN OUT parameters. */ + for (; gnu_cico_list != 0 && TREE_VALUE (gnu_cico_list) != 0; + gnu_cico_list = TREE_CHAIN (gnu_cico_list)) + ; + + /* Do any needed references for padded types. */ + TREE_VALUE (gnu_cico_list) + = convert (TREE_TYPE (TREE_PURPOSE (gnu_cico_list)), + gnat_to_gnu_entity (gnat_param, NULL_TREE, 1)); + } + + process_decls (Declarations (gnat_node), Empty, Empty, 1, 1); + + /* Generate the code of the subprogram itself. A return statement + will be present and any OUT parameters will be handled there. */ + gnat_to_code (Handled_Statement_Sequence (gnat_node)); + + expand_end_bindings (getdecls (), kept_level_p (), 0); + poplevel (kept_level_p (), 1, 0); + gnu_block_stack = TREE_CHAIN (gnu_block_stack); + + if (TREE_VALUE (gnu_return_label_stack) != 0) + { + tree gnu_retval; + + expand_end_bindings (NULL_TREE, kept_level_p (), 0); + poplevel (kept_level_p (), 1, 0); + expand_label (TREE_VALUE (gnu_return_label_stack)); + + gnu_cico_list = TYPE_CI_CO_LIST (gnu_subprog_type); + set_lineno (gnat_node, 1); + if (list_length (gnu_cico_list) == 1) + gnu_retval = TREE_VALUE (gnu_cico_list); + else + gnu_retval = build_constructor (TREE_TYPE (gnu_subprog_type), + gnu_cico_list); + + if (DECL_P (gnu_retval) && DECL_BY_REF_P (gnu_retval)) + gnu_retval + = build_unary_op (INDIRECT_REF, NULL_TREE, gnu_retval); + + expand_return + (build_binary_op (MODIFY_EXPR, NULL_TREE, + DECL_RESULT (current_function_decl), + gnu_retval)); + + } + + gnu_return_label_stack = TREE_CHAIN (gnu_return_label_stack); + + /* Disconnect the trees for parameters that we made variables for + from the GNAT entities since these will become unusable after + we end the function. */ + for (gnat_param = First_Formal (gnat_subprog_id); + Present (gnat_param); + gnat_param = Next_Formal_With_Extras (gnat_param)) + if (TREE_CODE (get_gnu_tree (gnat_param)) == VAR_DECL) + save_gnu_tree (gnat_param, NULL_TREE, 0); + + end_subprog_body (); + mark_out_of_scope (Defining_Unit_Name (Specification (gnat_node))); + write_symbols = save_write_symbols; + debug_hooks = save_debug_hooks; + } + break; + + case N_Function_Call: + case N_Procedure_Call_Statement: + + if (type_annotate_only) + break; + + { + /* The GCC node corresponding to the GNAT subprogram name. This can + either be a FUNCTION_DECL node if we are dealing with a standard + subprogram call, or an indirect reference expression (an + INDIRECT_REF node) pointing to a subprogram. */ + tree gnu_subprog_node = gnat_to_gnu (Name (gnat_node)); + /* The FUNCTION_TYPE node giving the GCC type of the subprogram. */ + tree gnu_subprog_type = TREE_TYPE (gnu_subprog_node); + tree gnu_subprog_addr + = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_subprog_node); + Entity_Id gnat_formal; + Node_Id gnat_actual; + tree gnu_actual_list = NULL_TREE; + tree gnu_name_list = NULL_TREE; + tree gnu_after_list = NULL_TREE; + tree gnu_subprog_call; + + switch (Nkind (Name (gnat_node))) + { + case N_Identifier: + case N_Operator_Symbol: + case N_Expanded_Name: + case N_Attribute_Reference: + if (Is_Eliminated (Entity (Name (gnat_node)))) + post_error_ne ("cannot call eliminated subprogram &!", + gnat_node, Entity (Name (gnat_node))); + } + + if (TREE_CODE (gnu_subprog_type) != FUNCTION_TYPE) + gigi_abort (317); + + /* If we are calling a stubbed function, make this into a + raise of Program_Error. Elaborate all our args first. */ + + if (TREE_CODE (gnu_subprog_node) == FUNCTION_DECL + && DECL_STUBBED_P (gnu_subprog_node)) + { + for (gnat_actual = First_Actual (gnat_node); + Present (gnat_actual); + gnat_actual = Next_Actual (gnat_actual)) + expand_expr_stmt (gnat_to_gnu (gnat_actual)); + + if (Nkind (gnat_node) == N_Function_Call) + { + gnu_result_type = TREE_TYPE (gnu_subprog_type); + gnu_result + = build1 (NULL_EXPR, gnu_result_type, + build_call_raise (raise_program_error_decl)); + } + else + expand_expr_stmt (build_call_raise (raise_program_error_decl)); + break; + } + + /* The only way we can be making a call via an access type is + if Name is an explicit dereference. In that case, get the + list of formal args from the type the access type is pointing + to. Otherwise, get the formals from entity being called. */ + if (Nkind (Name (gnat_node)) == N_Explicit_Dereference) + gnat_formal = First_Formal (Etype (Name (gnat_node))); + else if (Nkind (Name (gnat_node)) == N_Attribute_Reference) + /* Assume here that this must be 'Elab_Body or 'Elab_Spec. */ + gnat_formal = 0; + else + gnat_formal = First_Formal (Entity (Name (gnat_node))); + + /* Create the list of the actual parameters as GCC expects it, namely + a chain of TREE_LIST nodes in which the TREE_VALUE field of each + node is a parameter-expression and the TREE_PURPOSE field is + null. Skip OUT parameters that are not passed by reference. */ + + for (gnat_actual = First_Actual (gnat_node); + Present (gnat_actual); + gnat_formal = Next_Formal_With_Extras (gnat_formal), + gnat_actual = Next_Actual (gnat_actual)) + { + tree gnu_formal_type = gnat_to_gnu_type (Etype (gnat_formal)); + Node_Id gnat_name + = ((Nkind (gnat_actual) == N_Unchecked_Type_Conversion) + ? Expression (gnat_actual) : gnat_actual); + tree gnu_name = gnat_to_gnu (gnat_name); + tree gnu_name_type = gnat_to_gnu_type (Etype (gnat_name)); + tree gnu_actual; + + /* If it's possible we may need to use this expression twice, + make sure than any side-effects are handled via SAVE_EXPRs. + Likewise if we need to force side-effects before the call. + ??? This is more conservative than we need since we don't + need to do this for pass-by-ref with no conversion. + If we are passing a non-addressable Out or In Out parameter by + reference, pass the address of a copy and set up to copy back + out after the call. */ + + if (Ekind (gnat_formal) != E_In_Parameter) + { + gnu_name = gnat_stabilize_reference (gnu_name, 1); + if (! addressable_p (gnu_name) + && present_gnu_tree (gnat_formal) + && (DECL_BY_REF_P (get_gnu_tree (gnat_formal)) + || DECL_BY_COMPONENT_PTR_P (get_gnu_tree (gnat_formal)) + || DECL_BY_DESCRIPTOR_P (get_gnu_tree (gnat_formal)))) + { + tree gnu_copy = gnu_name; + + /* Remove any unpadding on the actual and make a copy. + But if the actual is a left-justified modular type, + first convert to it. */ + if (TREE_CODE (gnu_name) == COMPONENT_REF + && (TYPE_IS_PADDING_P + (TREE_TYPE (TREE_OPERAND (gnu_name, 0))))) + gnu_name = gnu_copy = TREE_OPERAND (gnu_name, 0); + else if (TREE_CODE (gnu_name_type) == RECORD_TYPE + && (TYPE_LEFT_JUSTIFIED_MODULAR_P + (gnu_name_type))) + gnu_name = convert (gnu_name_type, gnu_name); + + gnu_actual = save_expr (gnu_name); + + /* Set up to move the copy back to the original. */ + gnu_after_list = tree_cons (gnu_copy, gnu_actual, + gnu_after_list); + + gnu_name = gnu_actual; + } + } + + /* If this was a procedure call, we may not have removed any + padding. So do it here for the part we will use as an + input, if any. */ + gnu_actual = gnu_name; + if (Ekind (gnat_formal) != E_Out_Parameter + && TREE_CODE (TREE_TYPE (gnu_actual)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (gnu_actual))) + gnu_actual = convert (get_unpadded_type (Etype (gnat_actual)), + gnu_actual); + + if (Ekind (gnat_formal) != E_Out_Parameter + && Nkind (gnat_actual) != N_Unchecked_Type_Conversion + && Do_Range_Check (gnat_actual)) + gnu_actual = emit_range_check (gnu_actual, Etype (gnat_formal)); + + /* Do any needed conversions. We need only check for + unchecked conversion since normal conversions will be handled + by just converting to the formal type. */ + if (Nkind (gnat_actual) == N_Unchecked_Type_Conversion) + { + gnu_actual + = unchecked_convert (gnat_to_gnu_type (Etype (gnat_actual)), + gnu_actual); + + /* One we've done the unchecked conversion, we still + must ensure that the object is in range of the formal's + type. */ + if (Ekind (gnat_formal) != E_Out_Parameter + && Do_Range_Check (gnat_actual)) + gnu_actual = emit_range_check (gnu_actual, + Etype (gnat_formal)); + } + else + /* We may have suppressed a conversion to the Etype of the + actual since the parent is a procedure call. So add the + conversion here. */ + gnu_actual = convert (gnat_to_gnu_type (Etype (gnat_actual)), + gnu_actual); + + gnu_actual = convert (gnu_formal_type, gnu_actual); + + /* If we have not saved a GCC object for the formal, it means + it is an OUT parameter not passed by reference. Otherwise, + look at the PARM_DECL to see if it is passed by reference. */ + if (present_gnu_tree (gnat_formal) + && TREE_CODE (get_gnu_tree (gnat_formal)) == PARM_DECL + && DECL_BY_REF_P (get_gnu_tree (gnat_formal))) + { + if (Ekind (gnat_formal) != E_In_Parameter) + { + gnu_actual = gnu_name; + + /* If we have a padded type, be sure we've removed the + padding. */ + if (TREE_CODE (TREE_TYPE (gnu_actual)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (gnu_actual))) + gnu_actual + = convert (get_unpadded_type (Etype (gnat_actual)), + gnu_actual); + } + + /* The symmetry of the paths to the type of an entity is + broken here since arguments don't know that they will + be passed by ref. */ + gnu_formal_type = TREE_TYPE (get_gnu_tree (gnat_formal)); + gnu_actual = build_unary_op (ADDR_EXPR, gnu_formal_type, + gnu_actual); + } + else if (present_gnu_tree (gnat_formal) + && TREE_CODE (get_gnu_tree (gnat_formal)) == PARM_DECL + && DECL_BY_COMPONENT_PTR_P (get_gnu_tree (gnat_formal))) + { + gnu_formal_type = TREE_TYPE (get_gnu_tree (gnat_formal)); + gnu_actual = maybe_implicit_deref (gnu_actual); + gnu_actual = maybe_unconstrained_array (gnu_actual); + + if (TREE_CODE (gnu_formal_type) == RECORD_TYPE + && TYPE_IS_PADDING_P (gnu_formal_type)) + { + gnu_formal_type + = TREE_TYPE (TYPE_FIELDS (gnu_formal_type)); + gnu_actual = convert (gnu_formal_type, gnu_actual); + } + + /* Take the address of the object and convert to the + proper pointer type. We'd like to actually compute + the address of the beginning of the array using + an ADDR_EXPR of an ARRAY_REF, but there's a possibility + that the ARRAY_REF might return a constant and we'd + be getting the wrong address. Neither approach is + exactly correct, but this is the most likely to work + in all cases. */ + gnu_actual = convert (gnu_formal_type, + build_unary_op (ADDR_EXPR, NULL_TREE, + gnu_actual)); + } + else if (present_gnu_tree (gnat_formal) + && TREE_CODE (get_gnu_tree (gnat_formal)) == PARM_DECL + && DECL_BY_DESCRIPTOR_P (get_gnu_tree (gnat_formal))) + { + /* If arg is 'Null_Parameter, pass zero descriptor. */ + if ((TREE_CODE (gnu_actual) == INDIRECT_REF + || TREE_CODE (gnu_actual) == UNCONSTRAINED_ARRAY_REF) + && TREE_PRIVATE (gnu_actual)) + gnu_actual + = convert (DECL_ARG_TYPE (get_gnu_tree (gnat_formal)), + integer_zero_node); + else + gnu_actual + = build_unary_op (ADDR_EXPR, NULL_TREE, + fill_vms_descriptor (gnu_actual, + gnat_formal)); + } + else + { + tree gnu_actual_size = TYPE_SIZE (TREE_TYPE (gnu_actual)); + + if (Ekind (gnat_formal) != E_In_Parameter) + gnu_name_list + = chainon (gnu_name_list, + build_tree_list (NULL_TREE, gnu_name)); + + if (! present_gnu_tree (gnat_formal) + || TREE_CODE (get_gnu_tree (gnat_formal)) != PARM_DECL) + continue; + + /* If this is 'Null_Parameter, pass a zero even though we are + dereferencing it. */ + else if (TREE_CODE (gnu_actual) == INDIRECT_REF + && TREE_PRIVATE (gnu_actual) + && host_integerp (gnu_actual_size, 1) + && 0 >= compare_tree_int (gnu_actual_size, + BITS_PER_WORD)) + gnu_actual + = unchecked_convert + (DECL_ARG_TYPE (get_gnu_tree (gnat_formal)), + convert (type_for_size + (tree_low_cst (gnu_actual_size, 1), 1), + integer_zero_node)); + else + gnu_actual + = convert (TYPE_MAIN_VARIANT + (DECL_ARG_TYPE (get_gnu_tree (gnat_formal))), + gnu_actual); + } + + gnu_actual_list + = chainon (gnu_actual_list, + build_tree_list (NULL_TREE, gnu_actual)); + } + + gnu_subprog_call = build (CALL_EXPR, TREE_TYPE (gnu_subprog_type), + gnu_subprog_addr, gnu_actual_list, + NULL_TREE); + TREE_SIDE_EFFECTS (gnu_subprog_call) = 1; + + /* If it is a function call, the result is the call expression. */ + if (Nkind (gnat_node) == N_Function_Call) + { + gnu_result = gnu_subprog_call; + + /* If the function returns an unconstrained array or by reference, + we have to de-dereference the pointer. */ + if (TYPE_RETURNS_UNCONSTRAINED_P (gnu_subprog_type) + || TYPE_RETURNS_BY_REF_P (gnu_subprog_type)) + gnu_result = build_unary_op (INDIRECT_REF, NULL_TREE, + gnu_result); + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + } + + /* If this is the case where the GNAT tree contains a procedure call + but the Ada procedure has copy in copy out parameters, the special + parameter passing mechanism must be used. */ + else if (TYPE_CI_CO_LIST (gnu_subprog_type) != NULL_TREE) + { + /* List of FIELD_DECLs associated with the PARM_DECLs of the copy + in copy out parameters. */ + tree scalar_return_list = TYPE_CI_CO_LIST (gnu_subprog_type); + int length = list_length (scalar_return_list); + + if (length > 1) + { + tree gnu_name; + + gnu_subprog_call = make_save_expr (gnu_subprog_call); + + /* If any of the names had side-effects, ensure they are + all evaluated before the call. */ + for (gnu_name = gnu_name_list; gnu_name; + gnu_name = TREE_CHAIN (gnu_name)) + if (TREE_SIDE_EFFECTS (TREE_VALUE (gnu_name))) + gnu_subprog_call + = build (COMPOUND_EXPR, TREE_TYPE (gnu_subprog_call), + TREE_VALUE (gnu_name), gnu_subprog_call); + } + + if (Nkind (Name (gnat_node)) == N_Explicit_Dereference) + gnat_formal = First_Formal (Etype (Name (gnat_node))); + else + gnat_formal = First_Formal (Entity (Name (gnat_node))); + + for (gnat_actual = First_Actual (gnat_node); + Present (gnat_actual); + gnat_formal = Next_Formal_With_Extras (gnat_formal), + gnat_actual = Next_Actual (gnat_actual)) + /* If we are dealing with a copy in copy out parameter, we must + retrieve its value from the record returned in the function + call. */ + if (! (present_gnu_tree (gnat_formal) + && TREE_CODE (get_gnu_tree (gnat_formal)) == PARM_DECL + && (DECL_BY_REF_P (get_gnu_tree (gnat_formal)) + || (DECL_BY_COMPONENT_PTR_P + (get_gnu_tree (gnat_formal))) + || DECL_BY_DESCRIPTOR_P (get_gnu_tree (gnat_formal)))) + && Ekind (gnat_formal) != E_In_Parameter) + { + /* Get the value to assign to this OUT or IN OUT + parameter. It is either the result of the function if + there is only a single such parameter or the appropriate + field from the record returned. */ + tree gnu_result + = length == 1 ? gnu_subprog_call + : build_component_ref + (gnu_subprog_call, NULL_TREE, + TREE_PURPOSE (scalar_return_list)); + int unchecked_conversion + = Nkind (gnat_actual) == N_Unchecked_Type_Conversion; + /* If the actual is a conversion, get the inner expression, + which will be the real destination, and convert the + result to the type of the actual parameter. */ + tree gnu_actual + = maybe_unconstrained_array (TREE_VALUE (gnu_name_list)); + + /* If the result is a padded type, remove the padding. */ + if (TREE_CODE (TREE_TYPE (gnu_result)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (gnu_result))) + gnu_result + = convert (TREE_TYPE (TYPE_FIELDS + (TREE_TYPE (gnu_result))), + gnu_result); + + /* If the result is a type conversion, do it. */ + if (Nkind (gnat_actual) == N_Type_Conversion) + gnu_result + = convert_with_check + (Etype (Expression (gnat_actual)), gnu_result, + Do_Overflow_Check (gnat_actual), + Do_Range_Check (Expression (gnat_actual)), + Float_Truncate (gnat_actual)); + + else if (unchecked_conversion) + gnu_result + = unchecked_convert (TREE_TYPE (gnu_actual), gnu_result); + else + { + if (Do_Range_Check (gnat_actual)) + gnu_result = emit_range_check (gnu_result, + Etype (gnat_actual)); + + if (! (! TREE_CONSTANT (TYPE_SIZE + (TREE_TYPE (gnu_actual))) + && TREE_CONSTANT (TYPE_SIZE + (TREE_TYPE (gnu_result))))) + gnu_result = convert (TREE_TYPE (gnu_actual), + gnu_result); + } + + set_lineno (gnat_node, 1); + expand_expr_stmt (build_binary_op (MODIFY_EXPR, NULL_TREE, + gnu_actual, gnu_result)); + scalar_return_list = TREE_CHAIN (scalar_return_list); + gnu_name_list = TREE_CHAIN (gnu_name_list); + } + } + else + { + set_lineno (gnat_node, 1); + expand_expr_stmt (gnu_subprog_call); + } + + /* Handle anything we need to assign back. */ + for (gnu_expr = gnu_after_list; + gnu_expr; + gnu_expr = TREE_CHAIN (gnu_expr)) + expand_expr_stmt (build_binary_op (MODIFY_EXPR, NULL_TREE, + TREE_PURPOSE (gnu_expr), + TREE_VALUE (gnu_expr))); + } + break; + + /*************************/ + /* Chapter 7: Packages: */ + /*************************/ + + case N_Package_Declaration: + gnat_to_code (Specification (gnat_node)); + break; + + case N_Package_Specification: + + process_decls (Visible_Declarations (gnat_node), + Private_Declarations (gnat_node), Empty, 1, 1); + break; + + case N_Package_Body: + + /* If this is the body of a generic package - do nothing */ + if (Ekind (Corresponding_Spec (gnat_node)) == E_Generic_Package) + break; + + process_decls (Declarations (gnat_node), Empty, Empty, 1, 1); + + if (Present (Handled_Statement_Sequence (gnat_node))) + { + gnu_block_stack = tree_cons (NULL_TREE, NULL_TREE, gnu_block_stack); + gnat_to_code (Handled_Statement_Sequence (gnat_node)); + gnu_block_stack = TREE_CHAIN (gnu_block_stack); + } + break; + + /*********************************/ + /* Chapter 8: Visibility Rules: */ + /*********************************/ + + case N_Use_Package_Clause: + case N_Use_Type_Clause: + /* Nothing to do here - but these may appear in list of declarations */ + break; + + /***********************/ + /* Chapter 9: Tasks: */ + /***********************/ + + case N_Protected_Type_Declaration: + break; + + case N_Single_Task_Declaration: + gnat_to_gnu_entity (Defining_Entity (gnat_node), NULL_TREE, 1); + break; + + /***********************************************************/ + /* Chapter 10: Program Structure and Compilation Issues: */ + /***********************************************************/ + + case N_Compilation_Unit: + + /* For a body, first process the spec if there is one. */ + if (Nkind (Unit (gnat_node)) == N_Package_Body + || (Nkind (Unit (gnat_node)) == N_Subprogram_Body + && ! Acts_As_Spec (gnat_node))) + gnat_to_code (Library_Unit (gnat_node)); + + process_inlined_subprograms (gnat_node); + + if (type_annotate_only && gnat_node == Cunit (Main_Unit)) + { + elaborate_all_entities (gnat_node); + + if (Nkind (Unit (gnat_node)) == N_Subprogram_Declaration + || Nkind (Unit (gnat_node)) == N_Generic_Package_Declaration + || Nkind (Unit (gnat_node)) == N_Generic_Subprogram_Declaration) + break; + }; + + process_decls (Declarations (Aux_Decls_Node (gnat_node)), + Empty, Empty, 1, 1); + + gnat_to_code (Unit (gnat_node)); + + /* Process any pragmas following the unit. */ + if (Present (Pragmas_After (Aux_Decls_Node (gnat_node)))) + for (gnat_temp = First (Pragmas_After (Aux_Decls_Node (gnat_node))); + gnat_temp; gnat_temp = Next (gnat_temp)) + gnat_to_code (gnat_temp); + + /* Put all the Actions into the elaboration routine if we already had + elaborations. This will happen anyway if they are statements, but we + want to force declarations there too due to order-of-elaboration + issues. Most should have Is_Statically_Allocated set. If we + have had no elaborations, we have no order-of-elaboration issue and + don't want to create elaborations here. */ + if (Is_Non_Empty_List (Actions (Aux_Decls_Node (gnat_node)))) + for (gnat_temp = First (Actions (Aux_Decls_Node (gnat_node))); + Present (gnat_temp); gnat_temp = Next (gnat_temp)) + { + if (pending_elaborations_p ()) + add_pending_elaborations (NULL_TREE, + make_transform_expr (gnat_temp)); + else + gnat_to_code (gnat_temp); + } + + /* Generate elaboration code for this unit, if necessary, and + say whether we did or not. */ + Set_Has_No_Elaboration_Code + (gnat_node, + build_unit_elab + (Defining_Entity (Unit (gnat_node)), + Nkind (Unit (gnat_node)) == N_Package_Body + || Nkind (Unit (gnat_node)) == N_Subprogram_Body, + get_pending_elaborations ())); + + break; + + case N_Subprogram_Body_Stub: + case N_Package_Body_Stub: + case N_Protected_Body_Stub: + case N_Task_Body_Stub: + /* Simply process whatever unit is being inserted. */ + gnat_to_code (Unit (Library_Unit (gnat_node))); + break; + + case N_Subunit: + gnat_to_code (Proper_Body (gnat_node)); + break; + + /***************************/ + /* Chapter 11: Exceptions: */ + /***************************/ + + case N_Handled_Sequence_Of_Statements: + /* If there are exception handlers, start a new binding level that + we can exit (since each exception handler will do so). Then + declare a variable to save the old __gnat_jmpbuf value and a + variable for our jmpbuf. Call setjmp and handle each of the + possible exceptions if it returns one. */ + + if (! type_annotate_only && Present (Exception_Handlers (gnat_node))) + { + tree gnu_jmpsave_decl = 0; + tree gnu_jmpbuf_decl = 0; + tree gnu_cleanup_call = 0; + tree gnu_cleanup_decl; + + pushlevel (0); + expand_start_bindings (1); + + if (! Zero_Cost_Handling (gnat_node)) + { + gnu_jmpsave_decl + = create_var_decl (get_identifier ("JMPBUF_SAVE"), NULL_TREE, + jmpbuf_ptr_type, + build_call_0_expr (get_jmpbuf_decl), + 0, 0, 0, 0, 0); + + gnu_jmpbuf_decl = create_var_decl (get_identifier ("JMP_BUF"), + NULL_TREE, jmpbuf_type, + NULL_TREE, 0, 0, 0, 0, + 0); + TREE_VALUE (gnu_block_stack) = gnu_jmpbuf_decl; + } + + /* See if we are to call a function when exiting this block. */ + if (Present (At_End_Proc (gnat_node))) + { + gnu_cleanup_call + = build_call_0_expr (gnat_to_gnu (At_End_Proc (gnat_node))); + + gnu_cleanup_decl + = create_var_decl (get_identifier ("CLEANUP"), NULL_TREE, + integer_type_node, NULL_TREE, 0, 0, 0, 0, + 0); + + expand_decl_cleanup (gnu_cleanup_decl, gnu_cleanup_call); + } + + if (! Zero_Cost_Handling (gnat_node)) + { + /* When we exit this block, restore the saved value. */ + expand_decl_cleanup (gnu_jmpsave_decl, + build_call_1_expr (set_jmpbuf_decl, + gnu_jmpsave_decl)); + + /* Call setjmp and handle exceptions if it returns one. */ + set_lineno (gnat_node, 1); + expand_start_cond + (build_call_1_expr (setjmp_decl, + build_unary_op (ADDR_EXPR, NULL_TREE, + gnu_jmpbuf_decl)), + 0); + + /* Restore our incoming longjmp value before we do anything. */ + expand_expr_stmt (build_call_1_expr (set_jmpbuf_decl, + gnu_jmpsave_decl)); + + pushlevel (0); + expand_start_bindings (0); + + gnu_except_ptr_stack + = tree_cons (NULL_TREE, + create_var_decl + (get_identifier ("EXCEPT_PTR"), NULL_TREE, + build_pointer_type (except_type_node), + build_call_0_expr (get_excptr_decl), + 0, 0, 0, 0, 0), + gnu_except_ptr_stack); + + /* Generate code for each exception handler. The code at + N_Exception_Handler below does the real work. Note that + we ignore the dummy exception handler for the identifier + case, this is used only by the front end */ + if (Present (Exception_Handlers (gnat_node))) + for (gnat_temp + = First_Non_Pragma (Exception_Handlers (gnat_node)); + Present (gnat_temp); + gnat_temp = Next_Non_Pragma (gnat_temp)) + gnat_to_code (gnat_temp); + + /* If none of the exception handlers did anything, re-raise + but do not defer abortion. */ + set_lineno (gnat_node, 1); + expand_expr_stmt + (build_call_1_expr (raise_nodefer_decl, + TREE_VALUE (gnu_except_ptr_stack))); + + gnu_except_ptr_stack = TREE_CHAIN (gnu_except_ptr_stack); + expand_end_bindings (getdecls (), kept_level_p (), 0); + poplevel (kept_level_p (), 1, 0); + + /* End the "if" on setjmp. Note that we have arranged things so + control never returns here. */ + expand_end_cond (); + + /* This is now immediately before the body proper. Set + our jmp_buf as the current buffer. */ + expand_expr_stmt + (build_call_1_expr (set_jmpbuf_decl, + build_unary_op (ADDR_EXPR, NULL_TREE, + gnu_jmpbuf_decl))); + } + } + + /* If there are no exception handlers, we must not have an at end + cleanup identifier, since the cleanup identifier should always + generate a corresponding exception handler. */ + else if (! type_annotate_only && Present (At_End_Proc (gnat_node))) + gigi_abort (335); + + /* Generate code and declarations for the prefix of this block, + if any. */ + if (Present (First_Real_Statement (gnat_node))) + process_decls (Statements (gnat_node), Empty, + First_Real_Statement (gnat_node), 1, 1); + + /* Generate code for each statement in the block. */ + for (gnat_temp = (Present (First_Real_Statement (gnat_node)) + ? First_Real_Statement (gnat_node) + : First (Statements (gnat_node))); + Present (gnat_temp); gnat_temp = Next (gnat_temp)) + gnat_to_code (gnat_temp); + + /* For zero-cost exceptions, exit the block and then compile + the handlers. */ + if (! type_annotate_only && Zero_Cost_Handling (gnat_node) + && Present (Exception_Handlers (gnat_node))) + { + expand_exit_something (); + gnu_except_ptr_stack + = tree_cons (NULL_TREE, error_mark_node, gnu_except_ptr_stack); + + for (gnat_temp = First_Non_Pragma (Exception_Handlers (gnat_node)); + Present (gnat_temp); + gnat_temp = Next_Non_Pragma (gnat_temp)) + gnat_to_code (gnat_temp); + + gnu_except_ptr_stack = TREE_CHAIN (gnu_except_ptr_stack); + } + + /* If we have handlers, close the block we made. */ + if (! type_annotate_only && Present (Exception_Handlers (gnat_node))) + { + expand_end_bindings (getdecls (), kept_level_p (), 0); + poplevel (kept_level_p (), 1, 0); + } + + break; + + case N_Exception_Handler: + if (! Zero_Cost_Handling (gnat_node)) + { + /* Unless this is "Others" or the special "Non-Ada" exception + for Ada, make an "if" statement to select the proper + exceptions. For "Others", exclude exceptions where + Handled_By_Others is nonzero unless the All_Others flag is set. + For "Non-ada", accept an exception if "Lang" is 'V'. */ + tree gnu_choice = integer_zero_node; + + for (gnat_temp = First (Exception_Choices (gnat_node)); + gnat_temp; gnat_temp = Next (gnat_temp)) + { + tree this_choice; + + if (Nkind (gnat_temp) == N_Others_Choice) + { + if (All_Others (gnat_temp)) + this_choice = integer_one_node; + else + this_choice + = build_binary_op + (EQ_EXPR, integer_type_node, + convert + (integer_type_node, + build_component_ref + (build_unary_op + (INDIRECT_REF, NULL_TREE, + TREE_VALUE (gnu_except_ptr_stack)), + get_identifier ("not_handled_by_others"), NULL_TREE)), + integer_zero_node); + } + + else if (Nkind (gnat_temp) == N_Identifier + || Nkind (gnat_temp) == N_Expanded_Name) + { + /* ??? Note that we have to use gnat_to_gnu_entity here + since the type of the exception will be wrong in the + VMS case and that's exactly what this test is for. */ + gnu_expr + = gnat_to_gnu_entity (Entity (gnat_temp), NULL_TREE, 0); + + /* If this was a VMS exception, check import_code + against the value of the exception. */ + if (TREE_CODE (TREE_TYPE (gnu_expr)) == INTEGER_TYPE) + this_choice + = build_binary_op + (EQ_EXPR, integer_type_node, + build_component_ref + (build_unary_op + (INDIRECT_REF, NULL_TREE, + TREE_VALUE (gnu_except_ptr_stack)), + get_identifier ("import_code"), NULL_TREE), + gnu_expr); + else + this_choice + = build_binary_op + (EQ_EXPR, integer_type_node, + TREE_VALUE (gnu_except_ptr_stack), + convert + (TREE_TYPE (TREE_VALUE (gnu_except_ptr_stack)), + build_unary_op (ADDR_EXPR, NULL_TREE, gnu_expr))); + + /* If this is the distinguished exception "Non_Ada_Error" + (and we are in VMS mode), also allow a non-Ada + exception (a VMS condition) to match. */ + if (Is_Non_Ada_Error (Entity (gnat_temp))) + { + tree gnu_comp + = build_component_ref + (build_unary_op + (INDIRECT_REF, NULL_TREE, + TREE_VALUE (gnu_except_ptr_stack)), + get_identifier ("lang"), NULL_TREE); + + this_choice + = build_binary_op + (TRUTH_ORIF_EXPR, integer_type_node, + build_binary_op + (EQ_EXPR, integer_type_node, gnu_comp, + convert (TREE_TYPE (gnu_comp), + build_int_2 ('V', 0))), + this_choice); + } + } + else + gigi_abort (318); + + gnu_choice = build_binary_op (TRUTH_ORIF_EXPR, integer_type_node, + gnu_choice, this_choice); + } + + set_lineno (gnat_node, 1); + + expand_start_cond (gnu_choice, 0); + } + + for (gnat_temp = First (Statements (gnat_node)); + gnat_temp; gnat_temp = Next (gnat_temp)) + gnat_to_code (gnat_temp); + + /* At the end of the handler, exit the block. We made this block + in N_Handled_Sequence_Of_Statements. */ + expand_exit_something (); + + if (! Zero_Cost_Handling (gnat_node)) + expand_end_cond (); + + break; + + /*******************************/ + /* Chapter 12: Generic Units: */ + /*******************************/ + + case N_Generic_Function_Renaming_Declaration: + case N_Generic_Package_Renaming_Declaration: + case N_Generic_Procedure_Renaming_Declaration: + case N_Generic_Package_Declaration: + case N_Generic_Subprogram_Declaration: + case N_Package_Instantiation: + case N_Procedure_Instantiation: + case N_Function_Instantiation: + /* These nodes can appear on a declaration list but there is nothing to + to be done with them. */ + break; + + + /***************************************************/ + /* Chapter 13: Representation Clauses and */ + /* Implementation-Dependent Features: */ + /***************************************************/ + + case N_Attribute_Definition_Clause: + + /* The only one we need deal with is for 'Address. For the others, SEM + puts the information elsewhere. We need only deal with 'Address + if the object has a Freeze_Node (which it never will currently). */ + if (Get_Attribute_Id (Chars (gnat_node)) != Attr_Address + || No (Freeze_Node (Entity (Name (gnat_node))))) + break; + + /* Get the value to use as the address and save it as the + equivalent for GNAT_TEMP. When the object is frozen, + gnat_to_gnu_entity will do the right thing. */ + gnu_expr = gnat_to_gnu (Expression (gnat_node)); + save_gnu_tree (Entity (Name (gnat_node)), gnu_expr, 1); + break; + + case N_Enumeration_Representation_Clause: + case N_Record_Representation_Clause: + case N_At_Clause: + /* We do nothing with these. SEM puts the information elsewhere. */ + break; + + case N_Code_Statement: + if (! type_annotate_only) + { + tree gnu_template = gnat_to_gnu (Asm_Template (gnat_node)); + tree gnu_input_list = 0, gnu_output_list = 0, gnu_orig_out_list = 0; + tree gnu_clobber_list = 0; + char *clobber; + + /* First process inputs, then outputs, then clobbers. */ + Setup_Asm_Inputs (gnat_node); + while (Present (gnat_temp = Asm_Input_Value ())) + { + gnu_input_list = tree_cons (gnat_to_gnu + (Asm_Input_Constraint ()), + gnat_to_gnu (gnat_temp), + gnu_input_list); + Next_Asm_Input (); + } + + Setup_Asm_Outputs (gnat_node); + while (Present (gnat_temp = Asm_Output_Variable ())) + { + tree gnu_value = gnat_to_gnu (gnat_temp); + tree gnu_constr = gnat_to_gnu (Asm_Output_Constraint ()); + + gnu_orig_out_list + = tree_cons (gnu_constr, gnu_value, gnu_orig_out_list); + gnu_output_list + = tree_cons (gnu_constr, gnu_value, gnu_output_list); + Next_Asm_Output (); + } + + Clobber_Setup (gnat_node); + while ((clobber = Clobber_Get_Next ()) != 0) + gnu_clobber_list + = tree_cons (NULL_TREE, + build_string (strlen (clobber) + 1, clobber), + gnu_clobber_list); + + expand_asm_operands (gnu_template, nreverse (gnu_output_list), + nreverse (gnu_input_list), gnu_clobber_list, + Is_Asm_Volatile (gnat_node), + input_filename, lineno); + + /* Copy all the intermediate outputs into the specified outputs. */ + for (; gnu_output_list; + (gnu_output_list = TREE_CHAIN (gnu_output_list), + gnu_orig_out_list = TREE_CHAIN (gnu_orig_out_list))) + if (TREE_VALUE (gnu_orig_out_list) != TREE_VALUE (gnu_output_list)) + { + expand_expr_stmt + (build_binary_op (MODIFY_EXPR, NULL_TREE, + TREE_VALUE (gnu_orig_out_list), + TREE_VALUE (gnu_output_list))); + free_temp_slots (); + } + } + break; + + /***************************************************/ + /* Added Nodes */ + /***************************************************/ + + case N_Freeze_Entity: + process_freeze_entity (gnat_node); + process_decls (Actions (gnat_node), Empty, Empty, 1, 1); + break; + + case N_Itype_Reference: + if (! present_gnu_tree (Itype (gnat_node))) + process_type (Itype (gnat_node)); + break; + + case N_Free_Statement: + if (! type_annotate_only) + { + tree gnu_ptr = gnat_to_gnu (Expression (gnat_node)); + tree gnu_obj_type; + tree gnu_obj_size; + int align; + + /* If this is an unconstrained array, we know the object must + have been allocated with the template in front of the object. + So pass the template address, but get the total size. Do this + by converting to a thin pointer. */ + if (TYPE_FAT_POINTER_P (TREE_TYPE (gnu_ptr))) + gnu_ptr + = convert (build_pointer_type + (TYPE_OBJECT_RECORD_TYPE + (TYPE_UNCONSTRAINED_ARRAY (TREE_TYPE (gnu_ptr)))), + gnu_ptr); + + gnu_obj_type = TREE_TYPE (TREE_TYPE (gnu_ptr)); + gnu_obj_size = TYPE_SIZE_UNIT (gnu_obj_type); + align = TYPE_ALIGN (gnu_obj_type); + + if (TREE_CODE (gnu_obj_type) == RECORD_TYPE + && TYPE_CONTAINS_TEMPLATE_P (gnu_obj_type)) + { + tree gnu_char_ptr_type = build_pointer_type (char_type_node); + tree gnu_pos = byte_position (TYPE_FIELDS (gnu_obj_type)); + tree gnu_byte_offset + = convert (gnu_char_ptr_type, + size_diffop (size_zero_node, gnu_pos)); + + gnu_ptr = convert (gnu_char_ptr_type, gnu_ptr); + gnu_ptr = build_binary_op (MINUS_EXPR, gnu_char_ptr_type, + gnu_ptr, gnu_byte_offset); + } + + set_lineno (gnat_node, 1); + expand_expr_stmt + (build_call_alloc_dealloc (gnu_ptr, gnu_obj_size, align, + Procedure_To_Call (gnat_node), + Storage_Pool (gnat_node))); + } + break; + + case N_Raise_Constraint_Error: + case N_Raise_Program_Error: + case N_Raise_Storage_Error: + + if (type_annotate_only) + break; + + gnu_result_type = get_unpadded_type (Etype (gnat_node)); + gnu_result + = build_call_raise + (Nkind (gnat_node) == N_Raise_Constraint_Error + ? raise_constraint_error_decl + : Nkind (gnat_node) == N_Raise_Program_Error + ? raise_program_error_decl : raise_storage_error_decl); + + /* If the type is VOID, this is a statement, so we need to + generate the code for the call. Handle a Condition, if there + is one. */ + if (TREE_CODE (gnu_result_type) == VOID_TYPE) + { + set_lineno (gnat_node, 1); + + if (Present (Condition (gnat_node))) + expand_start_cond (gnat_to_gnu (Condition (gnat_node)), 0); + + expand_expr_stmt (gnu_result); + if (Present (Condition (gnat_node))) + expand_end_cond (); + gnu_result = error_mark_node; + } + else + gnu_result = build1 (NULL_EXPR, gnu_result_type, gnu_result); + break; + + /* Nothing to do, since front end does all validation using the + values that Gigi back-annotates. */ + case N_Validate_Unchecked_Conversion: + break; + + case N_Raise_Statement: + case N_Function_Specification: + case N_Procedure_Specification: + case N_Op_Concat: + case N_Component_Association: + case N_Task_Body: + default: + if (! type_annotate_only) + gigi_abort (321); + } + + /* If the result is a constant that overflows, raise constraint error. */ + if (TREE_CODE (gnu_result) == INTEGER_CST + && TREE_CONSTANT_OVERFLOW (gnu_result)) + { + post_error ("Constraint_Error will be raised at run-time?", gnat_node); + + gnu_result + = build1 (NULL_EXPR, gnu_result_type, + build_call_raise (raise_constraint_error_decl)); + } + + /* If our result has side-effects and is of an unconstrained type, + make a SAVE_EXPR so that we can be sure it will only be referenced + once. Note we must do this before any conversions. */ + if (TREE_SIDE_EFFECTS (gnu_result) + && (TREE_CODE (gnu_result_type) == UNCONSTRAINED_ARRAY_TYPE + || (TREE_CODE (TYPE_SIZE (gnu_result_type)) != INTEGER_CST + && contains_placeholder_p (TYPE_SIZE (gnu_result_type))))) + gnu_result = gnat_stabilize_reference (gnu_result, 0); + + /* Now convert the result to the proper type. If the type is void or if + we have no result, return error_mark_node to show we have no result. + If the type of the result is correct or if we have a label (which doesn't + have any well-defined type), return our result. Also don't do the + conversion if the "desired" type involves a PLACEHOLDER_EXPR in its size + since those are the cases where the front end may have the type wrong due + to "instantiating" the unconstrained record with discriminant values + or if this is a FIELD_DECL. If this is the Name of an assignment + statement or a parameter of a procedure call, return what we have since + the RHS has to be converted to our type there in that case, unless + GNU_RESULT_TYPE has a simpler size. Similarly, if the two types are + record types with the same name, the expression type has integral mode, + and GNU_RESULT_TYPE BLKmode, don't convert. This will be the case when + we are converting from a packable type to its actual type and we need + those conversions to be NOPs in order for assignments into these types to + work properly if the inner object is a bitfield and hence can't have + its address taken. Finally, don't convert integral types that are the + operand of an unchecked conversion since we need to ignore those + conversions (for 'Valid). Otherwise, convert the result to the proper + type. */ + + if (Present (Parent (gnat_node)) + && ((Nkind (Parent (gnat_node)) == N_Assignment_Statement + && Name (Parent (gnat_node)) == gnat_node) + || (Nkind (Parent (gnat_node)) == N_Procedure_Call_Statement + && Name (Parent (gnat_node)) != gnat_node) + || (Nkind (Parent (gnat_node)) == N_Unchecked_Type_Conversion + && ! AGGREGATE_TYPE_P (gnu_result_type) + && ! AGGREGATE_TYPE_P (TREE_TYPE (gnu_result))) + || Nkind (Parent (gnat_node)) == N_Parameter_Association) + && ! (TYPE_SIZE (gnu_result_type) != 0 + && TYPE_SIZE (TREE_TYPE (gnu_result)) != 0 + && (AGGREGATE_TYPE_P (gnu_result_type) + == AGGREGATE_TYPE_P (TREE_TYPE (gnu_result))) + && ((TREE_CODE (TYPE_SIZE (gnu_result_type)) == INTEGER_CST + && (TREE_CODE (TYPE_SIZE (TREE_TYPE (gnu_result))) + != INTEGER_CST)) + || (TREE_CODE (TYPE_SIZE (gnu_result_type)) != INTEGER_CST + && (TREE_CODE (TYPE_SIZE (TREE_TYPE (gnu_result))) + != INTEGER_CST) + && ! (contains_placeholder_p (TYPE_SIZE (gnu_result_type))) + && (contains_placeholder_p + (TYPE_SIZE (TREE_TYPE (gnu_result)))))) + && ! (TREE_CODE (gnu_result_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (gnu_result_type)))) + { + /* In this case remove padding only if the inner object is of + self-referential size: in that case it must be an object of + unconstrained type with a default discriminant. In other cases, + we want to avoid copying too much data. */ + if (TREE_CODE (TREE_TYPE (gnu_result)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (gnu_result)) + && contains_placeholder_p (TYPE_SIZE + (TREE_TYPE (TYPE_FIELDS + (TREE_TYPE (gnu_result)))))) + gnu_result = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (gnu_result))), + gnu_result); + } + + else if (TREE_CODE (gnu_result) == LABEL_DECL + || TREE_CODE (gnu_result) == FIELD_DECL + || TREE_CODE (gnu_result) == ERROR_MARK + || (TYPE_SIZE (gnu_result_type) != 0 + && TREE_CODE (TYPE_SIZE (gnu_result_type)) != INTEGER_CST + && TREE_CODE (gnu_result) != INDIRECT_REF + && contains_placeholder_p (TYPE_SIZE (gnu_result_type))) + || ((TYPE_NAME (gnu_result_type) + == TYPE_NAME (TREE_TYPE (gnu_result))) + && TREE_CODE (gnu_result_type) == RECORD_TYPE + && TREE_CODE (TREE_TYPE (gnu_result)) == RECORD_TYPE + && TYPE_MODE (gnu_result_type) == BLKmode + && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (gnu_result))) + == MODE_INT))) + { + /* Remove any padding record, but do nothing more in this case. */ + if (TREE_CODE (TREE_TYPE (gnu_result)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (gnu_result))) + gnu_result = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (gnu_result))), + gnu_result); + } + + else if (gnu_result == error_mark_node + || gnu_result_type == void_type_node) + gnu_result = error_mark_node; + else if (gnu_result_type != TREE_TYPE (gnu_result)) + gnu_result = convert (gnu_result_type, gnu_result); + + /* We don't need any NOP_EXPR or NON_LVALUE_EXPR on GNU_RESULT. */ + while ((TREE_CODE (gnu_result) == NOP_EXPR + || TREE_CODE (gnu_result) == NON_LVALUE_EXPR) + && TREE_TYPE (TREE_OPERAND (gnu_result, 0)) == TREE_TYPE (gnu_result)) + gnu_result = TREE_OPERAND (gnu_result, 0); + + return gnu_result; +} + +/* Force references to each of the entities in packages GNAT_NODE with's + so that the debugging information for all of them are identical + in all clients. Operate recursively on anything it with's, but check + that we aren't elaborating something more than once. */ + +/* The reason for this routine's existence is two-fold. + First, with some debugging formats, notably MDEBUG on SGI + IRIX, the linker will remove duplicate debugging information if two + clients have identical debugguing information. With the normal scheme + of elaboration, this does not usually occur, since entities in with'ed + packages are elaborated on demand, and if clients have different usage + patterns, the normal case, then the order and selection of entities + will differ. In most cases however, it seems that linkers do not know + how to eliminate duplicate debugging information, even if it is + identical, so the use of this routine would increase the total amount + of debugging information in the final executable. + + Second, this routine is called in type_annotate mode, to compute DDA + information for types in withed units, for ASIS use */ + +static void +elaborate_all_entities (gnat_node) + Node_Id gnat_node; +{ + Entity_Id gnat_with_clause, gnat_entity; + + save_gnu_tree (gnat_node, integer_zero_node, 1); + + /* Save entities in all context units. A body may have an implicit_with + on its own spec, if the context includes a child unit, so don't save + the spec twice. */ + + for (gnat_with_clause = First (Context_Items (gnat_node)); + Present (gnat_with_clause); + gnat_with_clause = Next (gnat_with_clause)) + if (Nkind (gnat_with_clause) == N_With_Clause + && ! present_gnu_tree (Library_Unit (gnat_with_clause)) + && Library_Unit (gnat_with_clause) != Library_Unit (Cunit (Main_Unit))) + { + elaborate_all_entities (Library_Unit (gnat_with_clause)); + + if (Ekind (Entity (Name (gnat_with_clause))) == E_Package) + for (gnat_entity = First_Entity (Entity (Name (gnat_with_clause))); + Present (gnat_entity); + gnat_entity = Next_Entity (gnat_entity)) + if (Is_Public (gnat_entity) + && Convention (gnat_entity) != Convention_Intrinsic + && Ekind (gnat_entity) != E_Package + && Ekind (gnat_entity) != E_Package_Body + && Ekind (gnat_entity) != E_Operator + && ! (IN (Ekind (gnat_entity), Type_Kind) + && ! Is_Frozen (gnat_entity)) + && ! ((Ekind (gnat_entity) == E_Procedure + || Ekind (gnat_entity) == E_Function) + && Is_Intrinsic_Subprogram (gnat_entity)) + && ! IN (Ekind (gnat_entity), Named_Kind) + && ! IN (Ekind (gnat_entity), Generic_Unit_Kind)) + gnat_to_gnu_entity (gnat_entity, NULL_TREE, 0); + } + + if (Nkind (Unit (gnat_node)) == N_Package_Body && type_annotate_only) + elaborate_all_entities (Library_Unit (gnat_node)); +} + +/* Do the processing of N_Freeze_Entity, GNAT_NODE. */ + +static void +process_freeze_entity (gnat_node) + Node_Id gnat_node; +{ + Entity_Id gnat_entity = Entity (gnat_node); + tree gnu_old; + tree gnu_new; + tree gnu_init + = (Nkind (Declaration_Node (gnat_entity)) == N_Object_Declaration + && present_gnu_tree (Declaration_Node (gnat_entity))) + ? get_gnu_tree (Declaration_Node (gnat_entity)) : NULL_TREE; + + /* If this is a package, need to generate code for the package. */ + if (Ekind (gnat_entity) == E_Package) + { + insert_code_for + (Parent (Corresponding_Body + (Parent (Declaration_Node (gnat_entity))))); + return; + } + + /* Check for old definition after the above call. This Freeze_Node + might be for one its Itypes. */ + gnu_old + = present_gnu_tree (gnat_entity) ? get_gnu_tree (gnat_entity) : 0; + + /* If this entity has an Address representation clause, GNU_OLD is the + address, so discard it here. */ + if (Present (Address_Clause (gnat_entity))) + gnu_old = 0; + + /* Don't do anything for class-wide types they are always + transformed into their root type. */ + if (Ekind (gnat_entity) == E_Class_Wide_Type + || (Ekind (gnat_entity) == E_Class_Wide_Subtype + && Present (Equivalent_Type (gnat_entity)))) + return; + + /* If we have a non-dummy type old tree, we have nothing to do. Unless + this is the public view of a private type whose full view was not + delayed, this node was never delayed as it should have been. + Also allow this to happen for concurrent types since we may have + frozen both the Corresponding_Record_Type and this type. */ + if (gnu_old != 0 + && ! (TREE_CODE (gnu_old) == TYPE_DECL + && TYPE_IS_DUMMY_P (TREE_TYPE (gnu_old)))) + { + if (IN (Ekind (gnat_entity), Incomplete_Or_Private_Kind) + && Present (Full_View (gnat_entity)) + && No (Freeze_Node (Full_View (gnat_entity)))) + return; + else if (Is_Concurrent_Type (gnat_entity)) + return; + else + gigi_abort (320); + } + + /* Reset the saved tree, if any, and elaborate the object or type for real. + If there is a full declaration, elaborate it and copy the type to + GNAT_ENTITY. Likewise if this is the record subtype corresponding to + a class wide type or subtype. */ + if (gnu_old != 0) + { + save_gnu_tree (gnat_entity, NULL_TREE, 0); + if (IN (Ekind (gnat_entity), Incomplete_Or_Private_Kind) + && Present (Full_View (gnat_entity)) + && present_gnu_tree (Full_View (gnat_entity))) + save_gnu_tree (Full_View (gnat_entity), NULL_TREE, 0); + if (Present (Class_Wide_Type (gnat_entity)) + && Class_Wide_Type (gnat_entity) != gnat_entity) + save_gnu_tree (Class_Wide_Type (gnat_entity), NULL_TREE, 0); + } + + if (IN (Ekind (gnat_entity), Incomplete_Or_Private_Kind) + && Present (Full_View (gnat_entity))) + { + gnu_new = gnat_to_gnu_entity (Full_View (gnat_entity), NULL_TREE, 1); + + /* The above call may have defined this entity (the simplest example + of this is when we have a private enumeral type since the bounds + will have the public view. */ + if (! present_gnu_tree (gnat_entity)) + save_gnu_tree (gnat_entity, gnu_new, 0); + if (Present (Class_Wide_Type (gnat_entity)) + && Class_Wide_Type (gnat_entity) != gnat_entity) + save_gnu_tree (Class_Wide_Type (gnat_entity), gnu_new, 0); + } + else + gnu_new = gnat_to_gnu_entity (gnat_entity, gnu_init, 1); + + /* If we've made any pointers to the old version of this type, we + have to update them. Also copy the name of the old object to + the new one. */ + + if (gnu_old != 0) + { + DECL_NAME (gnu_new) = DECL_NAME (gnu_old); + update_pointer_to (TREE_TYPE (gnu_old), TREE_TYPE (gnu_new)); + } +} + +/* Process the list of inlined subprograms of GNAT_NODE, which is an + N_Compilation_Unit. */ + +static void +process_inlined_subprograms (gnat_node) + Node_Id gnat_node; +{ + Entity_Id gnat_entity; + Node_Id gnat_body; + + /* If we can inline, generate RTL for all the inlined subprograms. + Define the entity first so we set DECL_EXTERNAL. */ + if (optimize > 0 && ! flag_no_inline) + for (gnat_entity = First_Inlined_Subprogram (gnat_node); + Present (gnat_entity); + gnat_entity = Next_Inlined_Subprogram (gnat_entity)) + { + gnat_body = Parent (Declaration_Node (gnat_entity)); + + if (Nkind (gnat_body) != N_Subprogram_Body) + { + /* ??? This really should always be Present. */ + if (No (Corresponding_Body (gnat_body))) + continue; + + gnat_body + = Parent (Declaration_Node (Corresponding_Body (gnat_body))); + } + + if (Present (gnat_body)) + { + gnat_to_gnu_entity (gnat_entity, NULL_TREE, 0); + gnat_to_code (gnat_body); + } + } +} + +/* Elaborate decls in the lists GNAT_DECLS and GNAT_DECLS2, if present. + We make two passes, one to elaborate anything other than bodies (but + we declare a function if there was no spec). The second pass + elaborates the bodies. + + GNAT_END_LIST gives the element in the list past the end. Normally, + this is Empty, but can be First_Real_Statement for a + Handled_Sequence_Of_Statements. + + We make a complete pass through both lists if PASS1P is true, then make + the second pass over both lists if PASS2P is true. The lists usually + correspond to the public and private parts of a package. */ + +static void +process_decls (gnat_decls, gnat_decls2, gnat_end_list, pass1p, pass2p) + List_Id gnat_decls, gnat_decls2; + Node_Id gnat_end_list; + int pass1p, pass2p; +{ + List_Id gnat_decl_array[2]; + Node_Id gnat_decl; + int i; + + gnat_decl_array[0] = gnat_decls, gnat_decl_array[1] = gnat_decls2; + + if (pass1p) + for (i = 0; i <= 1; i++) + if (Present (gnat_decl_array[i])) + for (gnat_decl = First (gnat_decl_array[i]); + gnat_decl != gnat_end_list; gnat_decl = Next (gnat_decl)) + { + set_lineno (gnat_decl, 0); + + /* For package specs, we recurse inside the declarations, + thus taking the two pass approach inside the boundary. */ + if (Nkind (gnat_decl) == N_Package_Declaration + && (Nkind (Specification (gnat_decl) + == N_Package_Specification))) + process_decls (Visible_Declarations (Specification (gnat_decl)), + Private_Declarations (Specification (gnat_decl)), + Empty, 1, 0); + + /* Similarly for any declarations in the actions of a + freeze node. */ + else if (Nkind (gnat_decl) == N_Freeze_Entity) + { + process_freeze_entity (gnat_decl); + process_decls (Actions (gnat_decl), Empty, Empty, 1, 0); + } + + /* Package bodies with freeze nodes get their elaboration deferred + until the freeze node, but the code must be placed in the right + place, so record the code position now. */ + else if (Nkind (gnat_decl) == N_Package_Body + && Present (Freeze_Node (Corresponding_Spec (gnat_decl)))) + record_code_position (gnat_decl); + + else if (Nkind (gnat_decl) == N_Package_Body_Stub + && Present (Library_Unit (gnat_decl)) + && Present (Freeze_Node + (Corresponding_Spec + (Proper_Body (Unit + (Library_Unit (gnat_decl))))))) + record_code_position + (Proper_Body (Unit (Library_Unit (gnat_decl)))); + + /* We defer most subprogram bodies to the second pass. + However, Init_Proc subprograms cannot be defered, but luckily + don't need to be. */ + else if ((Nkind (gnat_decl) == N_Subprogram_Body + && (Chars (Defining_Entity (gnat_decl)) + != Name_uInit_Proc))) + { + if (Acts_As_Spec (gnat_decl)) + { + Node_Id gnat_subprog_id = Defining_Entity (gnat_decl); + + if (Ekind (gnat_subprog_id) != E_Generic_Procedure + && Ekind (gnat_subprog_id) != E_Generic_Function) + gnat_to_gnu_entity (gnat_subprog_id, NULL_TREE, 1); + } + } + /* For bodies and stubs that act as their own specs, the entity + itself must be elaborated in the first pass, because it may + be used in other declarations. */ + else if (Nkind (gnat_decl) == N_Subprogram_Body_Stub) + { + Node_Id gnat_subprog_id = + Defining_Entity (Specification (gnat_decl)); + + if (Ekind (gnat_subprog_id) != E_Subprogram_Body + && Ekind (gnat_subprog_id) != E_Generic_Procedure + && Ekind (gnat_subprog_id) != E_Generic_Function) + gnat_to_gnu_entity (gnat_subprog_id, NULL_TREE, 1); + } + + /* Concurrent stubs stand for the corresponding subprogram bodies, + which are deferred like other bodies. */ + else if (Nkind (gnat_decl) == N_Task_Body_Stub + || Nkind (gnat_decl) == N_Protected_Body_Stub) + ; + + else + gnat_to_code (gnat_decl); + } + + /* Here we elaborate everything we deferred above except for package bodies, + which are elaborated at their freeze nodes. Note that we must also + go inside things (package specs and freeze nodes) the first pass did. */ + if (pass2p) + for (i = 0; i <= 1; i++) + if (Present (gnat_decl_array[i])) + for (gnat_decl = First (gnat_decl_array[i]); + gnat_decl != gnat_end_list; gnat_decl = Next (gnat_decl)) + { + if ((Nkind (gnat_decl) == N_Subprogram_Body + && (Chars (Defining_Entity (gnat_decl)) + != Name_uInit_Proc)) + || Nkind (gnat_decl) == N_Subprogram_Body_Stub + || Nkind (gnat_decl) == N_Task_Body_Stub + || Nkind (gnat_decl) == N_Protected_Body_Stub) + gnat_to_code (gnat_decl); + + else if (Nkind (gnat_decl) == N_Package_Declaration + && (Nkind (Specification (gnat_decl) + == N_Package_Specification))) + process_decls (Visible_Declarations (Specification (gnat_decl)), + Private_Declarations (Specification (gnat_decl)), + Empty, 0, 1); + + else if (Nkind (gnat_decl) == N_Freeze_Entity) + process_decls (Actions (gnat_decl), Empty, Empty, 0, 1); + } +} + +/* Emits an access check. GNU_EXPR is the expression that needs to be + checked against the NULL pointer. */ + +static tree +emit_access_check (gnu_expr) + tree gnu_expr; +{ + tree gnu_type = TREE_TYPE (gnu_expr); + + /* This only makes sense if GNU_TYPE is a pointer of some sort. */ + if (! POINTER_TYPE_P (gnu_type) && ! TYPE_FAT_POINTER_P (gnu_type)) + gigi_abort (322); + + /* Checked expressions must be evaluated only once. */ + gnu_expr = make_save_expr (gnu_expr); + + return emit_check (build_binary_op (EQ_EXPR, integer_type_node, + gnu_expr, + convert (TREE_TYPE (gnu_expr), + integer_zero_node)), + gnu_expr); +} + +/* Emits a discriminant check. GNU_EXPR is the expression to be checked and + GNAT_NODE a N_Selected_Component node. */ + +static tree +emit_discriminant_check (gnu_expr, gnat_node) + tree gnu_expr; + Node_Id gnat_node; +{ + Entity_Id orig_comp + = Original_Record_Component (Entity (Selector_Name (gnat_node))); + Entity_Id gnat_discr_fct = Discriminant_Checking_Func (orig_comp); + tree gnu_discr_fct; + Entity_Id gnat_discr; + tree gnu_actual_list = NULL_TREE; + tree gnu_cond; + Entity_Id gnat_pref_type; + tree gnu_pref_type; + + if (Is_Tagged_Type (Scope (orig_comp))) + gnat_pref_type = Scope (orig_comp); + else + gnat_pref_type = Etype (Prefix (gnat_node)); + + if (! Present (gnat_discr_fct)) + return gnu_expr; + + gnu_discr_fct = gnat_to_gnu (gnat_discr_fct); + + /* Checked expressions must be evaluated only once. */ + gnu_expr = make_save_expr (gnu_expr); + + /* Create the list of the actual parameters as GCC expects it. + This list is the list of the discriminant fields of the + record expression to be discriminant checked. For documentation + on what is the GCC format for this list see under the + N_Function_Call case */ + + while (IN (Ekind (gnat_pref_type), Incomplete_Or_Private_Kind) + || IN (Ekind (gnat_pref_type), Access_Kind)) + { + if (IN (Ekind (gnat_pref_type), Incomplete_Or_Private_Kind)) + gnat_pref_type = Underlying_Type (gnat_pref_type); + else if (IN (Ekind (gnat_pref_type), Access_Kind)) + gnat_pref_type = Designated_Type (gnat_pref_type); + } + + gnu_pref_type + = TREE_TYPE (gnat_to_gnu_entity (gnat_pref_type, NULL_TREE, 0)); + + for (gnat_discr = First_Discriminant (gnat_pref_type); + Present (gnat_discr); gnat_discr = Next_Discriminant (gnat_discr)) + { + Entity_Id gnat_real_discr + = ((Present (Corresponding_Discriminant (gnat_discr)) + && Present (Parent_Subtype (gnat_pref_type))) + ? Corresponding_Discriminant (gnat_discr) : gnat_discr); + tree gnu_discr = gnat_to_gnu_entity (gnat_real_discr, NULL_TREE, 0); + + gnu_actual_list + = chainon (gnu_actual_list, + build_tree_list (NULL_TREE, + build_component_ref + (convert (gnu_pref_type, gnu_expr), + NULL_TREE, gnu_discr))); + } + + gnu_cond = build (CALL_EXPR, + TREE_TYPE (TREE_TYPE (gnu_discr_fct)), + build_unary_op (ADDR_EXPR, NULL_TREE, gnu_discr_fct), + gnu_actual_list, + NULL_TREE); + TREE_SIDE_EFFECTS (gnu_cond) = 1; + + return + build_unary_op + (INDIRECT_REF, NULL_TREE, + emit_check (gnu_cond, + build_unary_op (ADDR_EXPR, + build_reference_type (TREE_TYPE (gnu_expr)), + gnu_expr))); +} + +/* Emit code for a range check. GNU_EXPR is the expression to be checked, + GNAT_RANGE_TYPE the gnat type or subtype containing the bounds against + which we have to check. */ + +static tree +emit_range_check (gnu_expr, gnat_range_type) + tree gnu_expr; + Entity_Id gnat_range_type; +{ + tree gnu_range_type = get_unpadded_type (gnat_range_type); + tree gnu_low = TYPE_MIN_VALUE (gnu_range_type); + tree gnu_high = TYPE_MAX_VALUE (gnu_range_type); + tree gnu_compare_type = get_base_type (TREE_TYPE (gnu_expr)); + + /* If GNU_EXPR has an integral type that is narrower than GNU_RANGE_TYPE, + we can't do anything since we might be truncating the bounds. No + check is needed in this case. */ + if (INTEGRAL_TYPE_P (TREE_TYPE (gnu_expr)) + && (TYPE_PRECISION (gnu_compare_type) + < TYPE_PRECISION (get_base_type (gnu_range_type)))) + return gnu_expr; + + /* Checked expressions must be evaluated only once. */ + gnu_expr = make_save_expr (gnu_expr); + + /* There's no good type to use here, so we might as well use + integer_type_node. Note that the form of the check is + (not (expr >= lo)) or (not (expr >= hi)) + the reason for this slightly convoluted form is that NaN's + are not considered to be in range in the float case. */ + return emit_check + (build_binary_op (TRUTH_ORIF_EXPR, integer_type_node, + invert_truthvalue + (build_binary_op (GE_EXPR, integer_type_node, + convert (gnu_compare_type, gnu_expr), + convert (gnu_compare_type, gnu_low))), + invert_truthvalue + (build_binary_op (LE_EXPR, integer_type_node, + convert (gnu_compare_type, gnu_expr), + convert (gnu_compare_type, + gnu_high)))), + gnu_expr); +} + +/* Emit code for an index check. GNU_ARRAY_OBJECT is the array object + which we are about to index, GNU_EXPR is the index expression to be + checked, GNU_LOW and GNU_HIGH are the lower and upper bounds + against which GNU_EXPR has to be checked. Note that for index + checking we cannot use the emit_range_check function (although very + similar code needs to be generated in both cases) since for index + checking the array type against which we are checking the indeces + may be unconstrained and consequently we need to retrieve the + actual index bounds from the array object itself + (GNU_ARRAY_OBJECT). The place where we need to do that is in + subprograms having unconstrained array formal parameters */ + +static tree +emit_index_check (gnu_array_object, gnu_expr, gnu_low, gnu_high) + tree gnu_array_object; + tree gnu_expr; + tree gnu_low; + tree gnu_high; +{ + tree gnu_expr_check; + + /* Checked expressions must be evaluated only once. */ + gnu_expr = make_save_expr (gnu_expr); + + /* Must do this computation in the base type in case the expression's + type is an unsigned subtypes. */ + gnu_expr_check = convert (get_base_type (TREE_TYPE (gnu_expr)), gnu_expr); + + /* If GNU_LOW or GNU_HIGH are a PLACEHOLDER_EXPR, qualify them by + the object we are handling. */ + if (TREE_CODE (gnu_low) != INTEGER_CST && contains_placeholder_p (gnu_low)) + gnu_low = build (WITH_RECORD_EXPR, TREE_TYPE (gnu_low), + gnu_low, gnu_array_object); + + if (TREE_CODE (gnu_high) != INTEGER_CST && contains_placeholder_p (gnu_high)) + gnu_high = build (WITH_RECORD_EXPR, TREE_TYPE (gnu_high), + gnu_high, gnu_array_object); + + /* There's no good type to use here, so we might as well use + integer_type_node. */ + return emit_check + (build_binary_op (TRUTH_ORIF_EXPR, integer_type_node, + build_binary_op (LT_EXPR, integer_type_node, + gnu_expr_check, + convert (TREE_TYPE (gnu_expr_check), + gnu_low)), + build_binary_op (GT_EXPR, integer_type_node, + gnu_expr_check, + convert (TREE_TYPE (gnu_expr_check), + gnu_high))), + gnu_expr); +} + +/* Given GNU_COND which contains the condition corresponding to an access, + discriminant or range check, of value GNU_EXPR, build a COND_EXPR + that returns GNU_EXPR if GNU_COND is false and raises a + CONSTRAINT_ERROR if GNU_COND is true. */ + +static tree +emit_check (gnu_cond, gnu_expr) + tree gnu_cond; + tree gnu_expr; +{ + tree gnu_call; + + gnu_call = build_call_raise (raise_constraint_error_decl); + + /* Use an outer COMPOUND_EXPR to make sure that GNU_EXPR will + get evaluated in front of the comparison in case it ends + up being a SAVE_EXPR. Put the whole thing inside its own + SAVE_EXPR do the inner SAVE_EXPR doesn't leak out. */ + + return make_save_expr (build (COMPOUND_EXPR, TREE_TYPE (gnu_expr), gnu_expr, + fold (build (COND_EXPR, TREE_TYPE (gnu_expr), + gnu_cond, + build (COMPOUND_EXPR, + TREE_TYPE (gnu_expr), + gnu_call, gnu_expr), + gnu_expr)))); +} + +/* Return an expression that converts GNU_EXPR to GNAT_TYPE, doing + overflow checks if OVERFLOW_P is nonzero and range checks if + RANGE_P is nonzero. GNAT_TYPE is known to be an integral type. + If TRUNCATE_P is nonzero, do a float to integer conversion with + truncation; otherwise round. */ + +static tree +convert_with_check (gnat_type, gnu_expr, overflow_p, range_p, truncate_p) + Entity_Id gnat_type; + tree gnu_expr; + int overflow_p; + int range_p; + int truncate_p; +{ + tree gnu_type = get_unpadded_type (gnat_type); + tree gnu_in_type = TREE_TYPE (gnu_expr); + tree gnu_in_basetype = get_base_type (gnu_in_type); + tree gnu_base_type = get_base_type (gnu_type); + tree gnu_ada_base_type = get_ada_base_type (gnu_type); + tree gnu_in_lb = TYPE_MIN_VALUE (gnu_in_basetype); + tree gnu_in_ub = TYPE_MAX_VALUE (gnu_in_basetype); + tree gnu_out_lb = TYPE_MIN_VALUE (gnu_base_type); + tree gnu_out_ub = TYPE_MAX_VALUE (gnu_base_type); + tree gnu_result = gnu_expr; + + /* If we are not doing any checks, the output is an integral type, and + the input is not a floating type, just do the conversion. This + shortcut is required to avoid problems with packed array types + and simplifies code in all cases anyway. */ + if (! range_p && ! overflow_p && INTEGRAL_TYPE_P (gnu_base_type) + && ! FLOAT_TYPE_P (gnu_in_type)) + return convert (gnu_type, gnu_expr); + + /* First convert the expression to its base type. This + will never generate code, but makes the tests below much simpler. + But don't do this if converting from an integer type to an unconstrained + array type since then we need to get the bounds from the original + (unpacked) type. */ + if (TREE_CODE (gnu_type) != UNCONSTRAINED_ARRAY_TYPE) + gnu_result = convert (gnu_in_basetype, gnu_result); + + /* If overflow checks are requested, we need to be sure the result will + fit in the output base type. But don't do this if the input + is integer and the output floating-point. */ + if (overflow_p + && ! (FLOAT_TYPE_P (gnu_base_type) && INTEGRAL_TYPE_P (gnu_in_basetype))) + { + /* Ensure GNU_EXPR only gets evaluated once. */ + tree gnu_input = make_save_expr (gnu_result); + tree gnu_cond = integer_zero_node; + + /* Convert the lower bounds to signed types, so we're sure we're + comparing them properly. Likewise, convert the upper bounds + to unsigned types. */ + if (INTEGRAL_TYPE_P (gnu_in_basetype) && TREE_UNSIGNED (gnu_in_basetype)) + gnu_in_lb = convert (signed_type (gnu_in_basetype), gnu_in_lb); + + if (INTEGRAL_TYPE_P (gnu_in_basetype) + && ! TREE_UNSIGNED (gnu_in_basetype)) + gnu_in_ub = convert (unsigned_type (gnu_in_basetype), gnu_in_ub); + + if (INTEGRAL_TYPE_P (gnu_base_type) && TREE_UNSIGNED (gnu_base_type)) + gnu_out_lb = convert (signed_type (gnu_base_type), gnu_out_lb); + + if (INTEGRAL_TYPE_P (gnu_base_type) && ! TREE_UNSIGNED (gnu_base_type)) + gnu_out_ub = convert (unsigned_type (gnu_base_type), gnu_out_ub); + + /* Check each bound separately and only if the result bound + is tighter than the bound on the input type. Note that all the + types are base types, so the bounds must be constant. Also, + the comparison is done in the base type of the input, which + always has the proper signedness. First check for input + integer (which means output integer), output float (which means + both float), or mixed, in which case we always compare. + Note that we have to do the comparison which would *fail* in the + case of an error since if it's an FP comparison and one of the + values is a NaN or Inf, the comparison will fail. */ + if (INTEGRAL_TYPE_P (gnu_in_basetype) + ? tree_int_cst_lt (gnu_in_lb, gnu_out_lb) + : (FLOAT_TYPE_P (gnu_base_type) + ? REAL_VALUES_LESS (TREE_REAL_CST (gnu_in_lb), + TREE_REAL_CST (gnu_out_lb)) + : 1)) + gnu_cond + = invert_truthvalue + (build_binary_op (GE_EXPR, integer_type_node, + gnu_input, convert (gnu_in_basetype, + gnu_out_lb))); + + if (INTEGRAL_TYPE_P (gnu_in_basetype) + ? tree_int_cst_lt (gnu_out_ub, gnu_in_ub) + : (FLOAT_TYPE_P (gnu_base_type) + ? REAL_VALUES_LESS (TREE_REAL_CST (gnu_out_ub), + TREE_REAL_CST (gnu_in_lb)) + : 1)) + gnu_cond + = build_binary_op (TRUTH_ORIF_EXPR, integer_type_node, gnu_cond, + invert_truthvalue + (build_binary_op (LE_EXPR, integer_type_node, + gnu_input, + convert (gnu_in_basetype, + gnu_out_ub)))); + + if (! integer_zerop (gnu_cond)) + gnu_result = emit_check (gnu_cond, gnu_input); + } + + /* Now convert to the result base type. If this is a non-truncating + float-to-integer conversion, round. */ + if (INTEGRAL_TYPE_P (gnu_ada_base_type) && FLOAT_TYPE_P (gnu_in_basetype) + && ! truncate_p) + { + tree gnu_point_5 = build_real (gnu_in_basetype, dconstp5); + tree gnu_minus_point_5 = build_real (gnu_in_basetype, dconstmp5); + tree gnu_zero = convert (gnu_in_basetype, integer_zero_node); + tree gnu_saved_result = save_expr (gnu_result); + tree gnu_comp = build (GE_EXPR, integer_type_node, + gnu_saved_result, gnu_zero); + tree gnu_adjust = build (COND_EXPR, gnu_in_basetype, gnu_comp, + gnu_point_5, gnu_minus_point_5); + + gnu_result + = build (PLUS_EXPR, gnu_in_basetype, gnu_saved_result, gnu_adjust); + } + + if (TREE_CODE (gnu_ada_base_type) == INTEGER_TYPE + && TYPE_HAS_ACTUAL_BOUNDS_P (gnu_ada_base_type) + && TREE_CODE (gnu_result) == UNCONSTRAINED_ARRAY_REF) + gnu_result = unchecked_convert (gnu_ada_base_type, gnu_result); + else + gnu_result = convert (gnu_ada_base_type, gnu_result); + + /* Finally, do the range check if requested. Note that if the + result type is a modular type, the range check is actually + an overflow check. */ + + if (range_p + || (TREE_CODE (gnu_base_type) == INTEGER_TYPE + && TYPE_MODULAR_P (gnu_base_type) && overflow_p)) + gnu_result = emit_range_check (gnu_result, gnat_type); + + return convert (gnu_type, gnu_result); +} + +/* Return 1 if GNU_EXPR can be directly addressed. This is the case unless + it is an expression involving computation or if it involves a bitfield + reference. This returns the same as mark_addressable in most cases. */ + +static int +addressable_p (gnu_expr) + tree gnu_expr; +{ + switch (TREE_CODE (gnu_expr)) + { + case UNCONSTRAINED_ARRAY_REF: + case INDIRECT_REF: + case VAR_DECL: + case PARM_DECL: + case FUNCTION_DECL: + case RESULT_DECL: + case CONSTRUCTOR: + case NULL_EXPR: + return 1; + + case COMPONENT_REF: + return (! DECL_BIT_FIELD (TREE_OPERAND (gnu_expr, 1)) + && addressable_p (TREE_OPERAND (gnu_expr, 0))); + + case ARRAY_REF: case ARRAY_RANGE_REF: + case REALPART_EXPR: case IMAGPART_EXPR: + case NOP_EXPR: + return addressable_p (TREE_OPERAND (gnu_expr, 0)); + + case CONVERT_EXPR: + return (AGGREGATE_TYPE_P (TREE_TYPE (gnu_expr)) + && addressable_p (TREE_OPERAND (gnu_expr, 0))); + + case UNCHECKED_CONVERT_EXPR: + { + /* This is addressable if the code in gnat_expand_expr can do + it by either just taking the operand or by pointer punning. */ + tree inner = TREE_OPERAND (gnu_expr, 0); + tree type = TREE_TYPE (gnu_expr); + tree inner_type = TREE_TYPE (inner); + + return ((TYPE_MODE (type) == TYPE_MODE (inner_type) + && (TYPE_ALIGN (type) <= TYPE_ALIGN (inner_type) + || TYPE_ALIGN (inner_type) >= BIGGEST_ALIGNMENT)) + || ((TYPE_MODE (type) == BLKmode + || TYPE_MODE (inner_type) == BLKmode) + && (TYPE_ALIGN (type) <= TYPE_ALIGN (inner_type) + || TYPE_ALIGN (inner_type) >= BIGGEST_ALIGNMENT + || TYPE_ALIGN_OK_P (type) + || TYPE_ALIGN_OK_P (inner_type)))); + } + + default: + return 0; + } +} + +/* Do the processing for the declaration of a GNAT_ENTITY, a type. If + a separate Freeze node exists, delay the bulk of the processing. Otherwise + make a GCC type for GNAT_ENTITY and set up the correspondance. */ + +void +process_type (gnat_entity) + Entity_Id gnat_entity; +{ + tree gnu_old + = present_gnu_tree (gnat_entity) ? get_gnu_tree (gnat_entity) : 0; + tree gnu_new; + + /* If we are to delay elaboration of this type, just do any + elaborations needed for expressions within the declaration and + make a dummy type entry for this node and its Full_View (if + any) in case something points to it. Don't do this if it + has already been done (the only way that can happen is if + the private completion is also delayed). */ + if (Present (Freeze_Node (gnat_entity)) + || (IN (Ekind (gnat_entity), Incomplete_Or_Private_Kind) + && Present (Full_View (gnat_entity)) + && Freeze_Node (Full_View (gnat_entity)) + && ! present_gnu_tree (Full_View (gnat_entity)))) + { + elaborate_entity (gnat_entity); + + if (gnu_old == 0) + { + tree gnu_decl = create_type_decl (get_entity_name (gnat_entity), + make_dummy_type (gnat_entity), + 0, 0, 0); + + save_gnu_tree (gnat_entity, gnu_decl, 0); + if (IN (Ekind (gnat_entity), Incomplete_Or_Private_Kind) + && Present (Full_View (gnat_entity))) + save_gnu_tree (Full_View (gnat_entity), gnu_decl, 0); + } + + return; + } + + /* If we saved away a dummy type for this node it means that this + made the type that corresponds to the full type of an incomplete + type. Clear that type for now and then update the type in the + pointers. */ + if (gnu_old != 0) + { + if (TREE_CODE (gnu_old) != TYPE_DECL + || ! TYPE_IS_DUMMY_P (TREE_TYPE (gnu_old))) + { + /* If this was a withed access type, this is not an error + and merely indicates we've already elaborated the type + already. */ + if (Is_Type (gnat_entity) && From_With_Type (gnat_entity)) + return; + + gigi_abort (323); + } + + save_gnu_tree (gnat_entity, NULL_TREE, 0); + } + + /* Now fully elaborate the type. */ + gnu_new = gnat_to_gnu_entity (gnat_entity, NULL_TREE, 1); + if (TREE_CODE (gnu_new) != TYPE_DECL) + gigi_abort (324); + + /* If we have an old type and we've made pointers to this type, + update those pointers. */ + if (gnu_old != 0) + update_pointer_to (TREE_TYPE (gnu_old), TREE_TYPE (gnu_new)); + + /* If this is a record type corresponding to a task or protected type + that is a completion of an incomplete type, perform a similar update + on the type. */ + /* ??? Including protected types here is a guess. */ + + if (IN (Ekind (gnat_entity), Record_Kind) + && Is_Concurrent_Record_Type (gnat_entity) + && present_gnu_tree (Corresponding_Concurrent_Type (gnat_entity))) + { + tree gnu_task_old + = get_gnu_tree (Corresponding_Concurrent_Type (gnat_entity)); + + save_gnu_tree (Corresponding_Concurrent_Type (gnat_entity), + NULL_TREE, 0); + save_gnu_tree (Corresponding_Concurrent_Type (gnat_entity), + gnu_new, 0); + + update_pointer_to (TREE_TYPE (gnu_task_old), TREE_TYPE (gnu_new)); + } +} + +/* GNAT_ASSOC is the front of the Component_Associations of an N_Aggregate. + GNU_TYPE is the GCC type of the corresponding record. + + Return a CONSTRUCTOR to build the record. */ + +static tree +assoc_to_constructor (gnat_assoc, gnu_type) + Node_Id gnat_assoc; + tree gnu_type; +{ + tree gnu_field, gnu_list, gnu_result; + + /* We test for GNU_FIELD being empty in the case where a variant + was the last thing since we don't take things off GNAT_ASSOC in + that case. We check GNAT_ASSOC in case we have a variant, but it + has no fields. */ + + for (gnu_list = NULL_TREE; Present (gnat_assoc); + gnat_assoc = Next (gnat_assoc)) + { + Node_Id gnat_field = First (Choices (gnat_assoc)); + tree gnu_field = gnat_to_gnu_entity (Entity (gnat_field), NULL_TREE, 0); + tree gnu_expr = gnat_to_gnu (Expression (gnat_assoc)); + + /* The expander is supposed to put a single component selector name + in every record component association */ + if (Next (gnat_field)) + gigi_abort (328); + + /* Before assigning a value in an aggregate make sure range checks + are done if required. Then convert to the type of the field. */ + if (Do_Range_Check (Expression (gnat_assoc))) + gnu_expr = emit_range_check (gnu_expr, Etype (gnat_field)); + + gnu_expr = convert (TREE_TYPE (gnu_field), gnu_expr); + + /* Add the field and expression to the list. */ + gnu_list = tree_cons (gnu_field, gnu_expr, gnu_list); + } + + gnu_result = extract_values (gnu_list, gnu_type); + + /* Verify every enty in GNU_LIST was used. */ + for (gnu_field = gnu_list; gnu_field; gnu_field = TREE_CHAIN (gnu_field)) + if (! TREE_ADDRESSABLE (gnu_field)) + gigi_abort (311); + + return gnu_result; +} + +/* Builds a possibly nested constructor for array aggregates. GNAT_EXPR + is the first element of an array aggregate. It may itself be an + aggregate (an array or record aggregate). GNU_ARRAY_TYPE is the gnu type + corresponding to the array aggregate. GNAT_COMPONENT_TYPE is the type + of the array component. It is needed for range checking. */ + +static tree +pos_to_constructor (gnat_expr, gnu_array_type, gnat_component_type) + Node_Id gnat_expr; + tree gnu_array_type; + Entity_Id gnat_component_type; +{ + tree gnu_expr; + tree gnu_expr_list = NULL_TREE; + + for ( ; Present (gnat_expr); gnat_expr = Next (gnat_expr)) + { + /* If the expression is itself an array aggregate then first build the + innermost constructor if it is part of our array (multi-dimensional + case). */ + + if (Nkind (gnat_expr) == N_Aggregate + && TREE_CODE (TREE_TYPE (gnu_array_type)) == ARRAY_TYPE + && TYPE_MULTI_ARRAY_P (TREE_TYPE (gnu_array_type))) + gnu_expr = pos_to_constructor (First (Expressions (gnat_expr)), + TREE_TYPE (gnu_array_type), + gnat_component_type); + else + { + gnu_expr = gnat_to_gnu (gnat_expr); + + /* before assigning the element to the array make sure it is + in range */ + if (Do_Range_Check (gnat_expr)) + gnu_expr = emit_range_check (gnu_expr, gnat_component_type); + } + + gnu_expr_list + = tree_cons (NULL_TREE, convert (TREE_TYPE (gnu_array_type), gnu_expr), + gnu_expr_list); + } + + return build_constructor (gnu_array_type, nreverse (gnu_expr_list)); +} + +/* Subroutine of assoc_to_constructor: VALUES is a list of field associations, + some of which are from RECORD_TYPE. Return a CONSTRUCTOR consisting + of the associations that are from RECORD_TYPE. If we see an internal + record, make a recursive call to fill it in as well. */ + +static tree +extract_values (values, record_type) + tree values; + tree record_type; +{ + tree result = NULL_TREE; + tree field, tem; + + for (field = TYPE_FIELDS (record_type); field; field = TREE_CHAIN (field)) + { + tree value = 0; + + /* _Parent is an internal field, but may have values in the aggregate, + so check for values first. */ + if ((tem = purpose_member (field, values)) != 0) + { + value = TREE_VALUE (tem); + TREE_ADDRESSABLE (tem) = 1; + } + + else if (DECL_INTERNAL_P (field)) + { + value = extract_values (values, TREE_TYPE (field)); + if (TREE_CODE (value) == CONSTRUCTOR + && CONSTRUCTOR_ELTS (value) == 0) + value = 0; + } + else + /* If we have a record subtype, the names will match, but not the + actual FIELD_DECLs. */ + for (tem = values; tem; tem = TREE_CHAIN (tem)) + if (DECL_NAME (TREE_PURPOSE (tem)) == DECL_NAME (field)) + { + value = convert (TREE_TYPE (field), TREE_VALUE (tem)); + TREE_ADDRESSABLE (tem) = 1; + } + + if (value == 0) + continue; + + result = tree_cons (field, value, result); + } + + return build_constructor (record_type, nreverse (result)); +} + +/* EXP is to be treated as an array or record. Handle the cases when it is + an access object and perform the required dereferences. */ + +static tree +maybe_implicit_deref (exp) + tree exp; +{ + /* If the type is a pointer, dereference it. */ + + if (POINTER_TYPE_P (TREE_TYPE (exp)) || TYPE_FAT_POINTER_P (TREE_TYPE (exp))) + exp = build_unary_op (INDIRECT_REF, NULL_TREE, exp); + + /* If we got a padded type, remove it too. */ + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (exp))) + exp = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (exp))), exp); + + return exp; +} + +/* Surround EXP with a SAVE_EXPR, but handle unconstrained objects specially + since it doesn't make any sense to put them in a SAVE_EXPR. */ + +tree +make_save_expr (exp) + tree exp; +{ + tree type = TREE_TYPE (exp); + + /* If this is an unchecked conversion, save the input since we may need to + handle this expression separately if it's the operand of a component + reference. */ + if (TREE_CODE (exp) == UNCHECKED_CONVERT_EXPR) + return build1 (UNCHECKED_CONVERT_EXPR, type, + make_save_expr (TREE_OPERAND (exp, 0))); + + /* If this is an aggregate type, we may be doing a dereference of it in + the LHS side of an assignment. In that case, we need to evaluate + it , take its address, make a SAVE_EXPR of that, then do the indirect + reference. Note that for an unconstrained array, the effect will be + to make a SAVE_EXPR of the fat pointer. + + ??? This is an efficiency problem in the case of a type that can be + placed into memory, but until we can deal with the LHS issue, + we have to take that hit. This really should test for BLKmode. */ + else if (TREE_CODE (type) == UNCONSTRAINED_ARRAY_TYPE + || (AGGREGATE_TYPE_P (type) && ! TYPE_FAT_POINTER_P (type))) + return + build_unary_op (INDIRECT_REF, type, + save_expr (build_unary_op (ADDR_EXPR, + build_reference_type (type), + exp))); + + /* Otherwise, just do the usual thing. */ + return save_expr (exp); +} + +/* This is equivalent to stabilize_reference in GCC's tree.c, but we know + how to handle our new nodes and we take an extra argument that says + whether to force evaluation of everything. */ + +tree +gnat_stabilize_reference (ref, force) + tree ref; + int force; +{ + register tree type = TREE_TYPE (ref); + register enum tree_code code = TREE_CODE (ref); + register tree result; + + switch (code) + { + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + /* No action is needed in this case. */ + return ref; + + case NOP_EXPR: + case CONVERT_EXPR: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case FIX_FLOOR_EXPR: + case FIX_ROUND_EXPR: + case FIX_CEIL_EXPR: + case UNCHECKED_CONVERT_EXPR: + case ADDR_EXPR: + result + = build1 (code, type, + gnat_stabilize_reference (TREE_OPERAND (ref, 0), force)); + break; + + case INDIRECT_REF: + case UNCONSTRAINED_ARRAY_REF: + result = build1 (code, type, + gnat_stabilize_reference_1 (TREE_OPERAND (ref, 0), + force)); + break; + + case COMPONENT_REF: + result = build (COMPONENT_REF, type, + gnat_stabilize_reference (TREE_OPERAND (ref, 0), + force), + TREE_OPERAND (ref, 1)); + break; + + case BIT_FIELD_REF: + result = build (BIT_FIELD_REF, type, + gnat_stabilize_reference (TREE_OPERAND (ref, 0), force), + gnat_stabilize_reference_1 (TREE_OPERAND (ref, 1), + force), + gnat_stabilize_reference_1 (TREE_OPERAND (ref, 2), + force)); + break; + + case ARRAY_REF: + result = build (ARRAY_REF, type, + gnat_stabilize_reference (TREE_OPERAND (ref, 0), force), + gnat_stabilize_reference_1 (TREE_OPERAND (ref, 1), + force)); + break; + + case ARRAY_RANGE_REF: + result = build (ARRAY_RANGE_REF, type, + gnat_stabilize_reference (TREE_OPERAND (ref, 0), force), + gnat_stabilize_reference_1 (TREE_OPERAND (ref, 1), + force)); + break; + + case COMPOUND_EXPR: + result = build (COMPOUND_EXPR, type, + gnat_stabilize_reference_1 (TREE_OPERAND (ref, 0), + force), + gnat_stabilize_reference (TREE_OPERAND (ref, 1), + force)); + break; + + case RTL_EXPR: + result = build1 (INDIRECT_REF, type, + save_expr (build1 (ADDR_EXPR, + build_reference_type (type), ref))); + break; + + /* If arg isn't a kind of lvalue we recognize, make no change. + Caller should recognize the error for an invalid lvalue. */ + default: + return ref; + + case ERROR_MARK: + return error_mark_node; + } + + TREE_READONLY (result) = TREE_READONLY (ref); + return result; +} + +/* Similar to stabilize_reference_1 in tree.c, but supports an extra + arg to force a SAVE_EXPR for everything. */ + +static tree +gnat_stabilize_reference_1 (e, force) + tree e; + int force; +{ + register enum tree_code code = TREE_CODE (e); + register tree type = TREE_TYPE (e); + register tree result; + + /* We cannot ignore const expressions because it might be a reference + to a const array but whose index contains side-effects. But we can + ignore things that are actual constant or that already have been + handled by this function. */ + + if (TREE_CONSTANT (e) || code == SAVE_EXPR) + return e; + + switch (TREE_CODE_CLASS (code)) + { + case 'x': + case 't': + case 'd': + case 'b': + case '<': + case 's': + case 'e': + case 'r': + if (TREE_SIDE_EFFECTS (e) || force) + return save_expr (e); + return e; + + case 'c': + /* Constants need no processing. In fact, we should never reach + here. */ + return e; + + case '2': + /* Division is slow and tends to be compiled with jumps, + especially the division by powers of 2 that is often + found inside of an array reference. So do it just once. */ + if (code == TRUNC_DIV_EXPR || code == TRUNC_MOD_EXPR + || code == FLOOR_DIV_EXPR || code == FLOOR_MOD_EXPR + || code == CEIL_DIV_EXPR || code == CEIL_MOD_EXPR + || code == ROUND_DIV_EXPR || code == ROUND_MOD_EXPR) + return save_expr (e); + /* Recursively stabilize each operand. */ + result = build (code, type, + gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), force), + gnat_stabilize_reference_1 (TREE_OPERAND (e, 1), force)); + break; + + case '1': + /* Recursively stabilize each operand. */ + result = build1 (code, type, + gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), + force)); + break; + + default: + abort (); + } + + TREE_READONLY (result) = TREE_READONLY (e); + return result; +} + +/* GNAT_UNIT is the Defining_Identifier for some package or subprogram, + either a spec or a body, BODY_P says which. If needed, make a function + to be the elaboration routine for that object and perform the elaborations + in GNU_ELAB_LIST. + + Return 1 if we didn't need an elaboration function, zero otherwise. */ + +static int +build_unit_elab (gnat_unit, body_p, gnu_elab_list) + Entity_Id gnat_unit; + int body_p; + tree gnu_elab_list; +{ + tree gnu_decl; + rtx insn; + int result = 1; + + /* If we have nothing to do, return. */ + if (gnu_elab_list == 0) + return 1; + + /* Set our file and line number to that of the object and set up the + elaboration routine. */ + gnu_decl = create_subprog_decl (create_concat_name (gnat_unit, + body_p ? + "elabb" : "elabs"), + NULL_TREE, void_ftype, NULL_TREE, 0, 1, 0, + 0); + DECL_ELABORATION_PROC_P (gnu_decl) = 1; + + begin_subprog_body (gnu_decl); + set_lineno (gnat_unit, 1); + pushlevel (0); + gnu_block_stack = tree_cons (NULL_TREE, NULL_TREE, gnu_block_stack); + expand_start_bindings (0); + + /* Emit the assignments for the elaborations we have to do. If there + is no destination, this is just a call to execute some statement + that was placed within the declarative region. But first save a + pointer so we can see if any insns were generated. */ + + insn = get_last_insn (); + + for (; gnu_elab_list; gnu_elab_list = TREE_CHAIN (gnu_elab_list)) + if (TREE_PURPOSE (gnu_elab_list) == NULL_TREE) + { + if (TREE_VALUE (gnu_elab_list) != 0) + expand_expr_stmt (TREE_VALUE (gnu_elab_list)); + } + else + { + tree lhs = TREE_PURPOSE (gnu_elab_list); + + input_filename = DECL_SOURCE_FILE (lhs); + lineno = DECL_SOURCE_LINE (lhs); + + /* If LHS has a padded type, convert it to the unpadded type + so the assignment is done properly. */ + if (TREE_CODE (TREE_TYPE (lhs)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (lhs))) + lhs = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (lhs))), lhs); + + emit_line_note (input_filename, lineno); + expand_expr_stmt (build_binary_op (MODIFY_EXPR, NULL_TREE, + TREE_PURPOSE (gnu_elab_list), + TREE_VALUE (gnu_elab_list))); + } + + /* See if any non-NOTE insns were generated. */ + for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + result = 0; + break; + } + + expand_end_bindings (getdecls (), kept_level_p (), 0); + poplevel (kept_level_p (), 1, 0); + gnu_block_stack = TREE_CHAIN (gnu_block_stack); + end_subprog_body (); + + /* If there were no insns, we don't need an elab routine. It would + be nice to not output this one, but there's no good way to do that. */ + return result; +} + +extern char *__gnat_to_canonical_file_spec PARAMS ((char *)); + +/* Determine the input_filename and the lineno from the source location + (Sloc) of GNAT_NODE node. Set the global variable input_filename and + lineno. If WRITE_NOTE_P is true, emit a line number note. */ + +void +set_lineno (gnat_node, write_note_p) + Node_Id gnat_node; + int write_note_p; +{ + Source_Ptr source_location = Sloc (gnat_node); + + /* If node not from source code, ignore. */ + if (source_location < 0) + return; + + /* Use the identifier table to make a hashed, permanent copy of the filename, + since the name table gets reallocated after Gigi returns but before all + the debugging information is output. The call to + __gnat_to_canonical_file_spec translates filenames from pragmas + Source_Reference that contain host style syntax not understood by gdb. */ + input_filename + = IDENTIFIER_POINTER + (get_identifier + (__gnat_to_canonical_file_spec + (Get_Name_String + (Debug_Source_Name (Get_Source_File_Index (source_location)))))); + + /* ref_filename is the reference file name as given by sinput (i.e no + directory) */ + ref_filename + = IDENTIFIER_POINTER + (get_identifier + (Get_Name_String + (Reference_Name (Get_Source_File_Index (source_location)))));; + lineno = Get_Logical_Line_Number (source_location); + + if (write_note_p) + emit_line_note (input_filename, lineno); +} + +/* Post an error message. MSG is the error message, properly annotated. + NODE is the node at which to post the error and the node to use for the + "&" substitution. */ + +void +post_error (msg, node) + const char *msg; + Node_Id node; +{ + String_Template temp; + Fat_Pointer fp; + + temp.Low_Bound = 1, temp.High_Bound = strlen (msg); + fp.Array = msg, fp.Bounds = &temp; + if (Present (node)) + Error_Msg_N (fp, node); +} + +/* Similar, but NODE is the node at which to post the error and ENT + is the node to use for the "&" substitution. */ + +void +post_error_ne (msg, node, ent) + const char *msg; + Node_Id node; + Entity_Id ent; +{ + String_Template temp; + Fat_Pointer fp; + + temp.Low_Bound = 1, temp.High_Bound = strlen (msg); + fp.Array = msg, fp.Bounds = &temp; + if (Present (node)) + Error_Msg_NE (fp, node, ent); +} + +/* Similar, but NODE is the node at which to post the error, ENT is the node + to use for the "&" substitution, and N is the number to use for the ^. */ + +void +post_error_ne_num (msg, node, ent, n) + const char *msg; + Node_Id node; + Entity_Id ent; + int n; +{ + String_Template temp; + Fat_Pointer fp; + + temp.Low_Bound = 1, temp.High_Bound = strlen (msg); + fp.Array = msg, fp.Bounds = &temp; + Error_Msg_Uint_1 = UI_From_Int (n); + + if (Present (node)) + Error_Msg_NE (fp, node, ent); +} + +/* Similar to post_error_ne_num, but T is a GCC tree representing the + number to write. If the tree represents a constant that fits within + a host integer, the text inside curly brackets in MSG will be output + (presumably including a '^'). Otherwise that text will not be output + and the text inside square brackets will be output instead. */ + +void +post_error_ne_tree (msg, node, ent, t) + const char *msg; + Node_Id node; + Entity_Id ent; + tree t; +{ + char *newmsg = alloca (strlen (msg) + 1); + String_Template temp = {1, 0}; + Fat_Pointer fp; + char start_yes, end_yes, start_no, end_no; + const char *p; + char *q; + + fp.Array = newmsg, fp.Bounds = &temp; + + if (host_integerp (t, 1) +#if HOST_BITS_PER_WIDE_INT > HOST_BITS_PER_INT + && compare_tree_int (t, 1 << (HOST_BITS_PER_INT - 2)) < 0 +#endif + ) + { + Error_Msg_Uint_1 = UI_From_Int (tree_low_cst (t, 1)); + start_yes = '{', end_yes = '}', start_no = '[', end_no = ']'; + } + else + start_yes = '[', end_yes = ']', start_no = '{', end_no = '}'; + + for (p = msg, q = newmsg; *p != 0; p++) + { + if (*p == start_yes) + for (p++; *p != end_yes; p++) + *q++ = *p; + else if (*p == start_no) + for (p++; *p != end_no; p++) + ; + else + *q++ = *p; + } + + *q = 0; + + temp.High_Bound = strlen (newmsg); + if (Present (node)) + Error_Msg_NE (fp, node, ent); +} + +/* Similar to post_error_ne_tree, except that NUM is a second + integer to write in the message. */ + +void +post_error_ne_tree_2 (msg, node, ent, t, num) + const char *msg; + Node_Id node; + Entity_Id ent; + tree t; + int num; +{ + Error_Msg_Uint_2 = UI_From_Int (num); + post_error_ne_tree (msg, node, ent, t); +} + +/* Set the node for a second '&' in the error message. */ + +void +set_second_error_entity (e) + Entity_Id e; +{ + Error_Msg_Node_2 = e; +} + +/* Signal abort, with "Gigi abort" as the error label, and error_gnat_node + as the relevant node that provides the location info for the error */ + +void +gigi_abort (code) + int code; +{ + String_Template temp = {1, 10}; + Fat_Pointer fp; + + fp.Array = "Gigi abort", fp.Bounds = &temp; + + Current_Error_Node = error_gnat_node; + Compiler_Abort (fp, code); +} + +/* Initialize the table that maps GNAT codes to GCC codes for simple + binary and unary operations. */ + +void +init_code_table () +{ + gnu_codes[N_And_Then] = TRUTH_ANDIF_EXPR; + gnu_codes[N_Or_Else] = TRUTH_ORIF_EXPR; + + gnu_codes[N_Op_And] = TRUTH_AND_EXPR; + gnu_codes[N_Op_Or] = TRUTH_OR_EXPR; + gnu_codes[N_Op_Xor] = TRUTH_XOR_EXPR; + gnu_codes[N_Op_Eq] = EQ_EXPR; + gnu_codes[N_Op_Ne] = NE_EXPR; + gnu_codes[N_Op_Lt] = LT_EXPR; + gnu_codes[N_Op_Le] = LE_EXPR; + gnu_codes[N_Op_Gt] = GT_EXPR; + gnu_codes[N_Op_Ge] = GE_EXPR; + gnu_codes[N_Op_Add] = PLUS_EXPR; + gnu_codes[N_Op_Subtract] = MINUS_EXPR; + gnu_codes[N_Op_Multiply] = MULT_EXPR; + gnu_codes[N_Op_Mod] = FLOOR_MOD_EXPR; + gnu_codes[N_Op_Rem] = TRUNC_MOD_EXPR; + gnu_codes[N_Op_Minus] = NEGATE_EXPR; + gnu_codes[N_Op_Abs] = ABS_EXPR; + gnu_codes[N_Op_Not] = TRUTH_NOT_EXPR; + gnu_codes[N_Op_Rotate_Left] = LROTATE_EXPR; + gnu_codes[N_Op_Rotate_Right] = RROTATE_EXPR; + gnu_codes[N_Op_Shift_Left] = LSHIFT_EXPR; + gnu_codes[N_Op_Shift_Right] = RSHIFT_EXPR; + gnu_codes[N_Op_Shift_Right_Arithmetic] = RSHIFT_EXPR; +} diff --git a/gcc/ada/tree_gen.adb b/gcc/ada/tree_gen.adb new file mode 100644 index 00000000000..fc54b0e45c2 --- /dev/null +++ b/gcc/ada/tree_gen.adb @@ -0,0 +1,63 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E _ G E N -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.8 $ +-- -- +-- Copyright (C) 1992-1999, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Atree; +with Elists; +with Fname; +with Lib; +with Namet; +with Nlists; +with Opt; +with Osint; +with Repinfo; +with Sinput; +with Stand; +with Stringt; +with Uintp; +with Urealp; + +procedure Tree_Gen is +begin + if Opt.Tree_Output then + Osint.Tree_Create; + Opt.Tree_Write; + Atree.Tree_Write; + Elists.Tree_Write; + Fname.Tree_Write; + Lib.Tree_Write; + Namet.Tree_Write; + Nlists.Tree_Write; + Sinput.Tree_Write; + Stand.Tree_Write; + Stringt.Tree_Write; + Uintp.Tree_Write; + Urealp.Tree_Write; + Repinfo.Tree_Write; + Osint.Tree_Close; + end if; +end Tree_Gen; diff --git a/gcc/ada/tree_gen.ads b/gcc/ada/tree_gen.ads new file mode 100644 index 00000000000..0d3afe08380 --- /dev/null +++ b/gcc/ada/tree_gen.ads @@ -0,0 +1,31 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E _ G E N -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.2 $ -- +-- -- +-- Copyright (C) 1992,1993,1994,1995 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This procedure is used to write out the tree if the option is set + +procedure Tree_Gen; diff --git a/gcc/ada/tree_in.adb b/gcc/ada/tree_in.adb new file mode 100644 index 00000000000..368cf5a2f48 --- /dev/null +++ b/gcc/ada/tree_in.adb @@ -0,0 +1,69 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E _ I N -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.10 $ +-- -- +-- Copyright (C) 1992-1999, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Atree; +with Csets; +with Elists; +with Fname; +with Lib; +with Namet; +with Nlists; +with Opt; +with Repinfo; +with Sinput; +with Stand; +with Stringt; +with Tree_IO; +with Uintp; +with Urealp; + +procedure Tree_In (Desc : File_Descriptor) is +begin + Tree_IO.Tree_Read_Initialize (Desc); + Opt.Tree_Read; + Atree.Tree_Read; + Elists.Tree_Read; + Fname.Tree_Read; + Lib.Tree_Read; + Namet.Tree_Read; + Nlists.Tree_Read; + Sinput.Tree_Read; + Stand.Tree_Read; + Stringt.Tree_Read; + Uintp.Tree_Read; + Urealp.Tree_Read; + Repinfo.Tree_Read; + Csets.Initialize; +end Tree_In; diff --git a/gcc/ada/tree_in.ads b/gcc/ada/tree_in.ads new file mode 100644 index 00000000000..932794ce42f --- /dev/null +++ b/gcc/ada/tree_in.ads @@ -0,0 +1,46 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E _ I N -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.3 $ -- +-- -- +-- Copyright (C) 1992,1993,1994,1995 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This procedure is used to read in a tree if the option is set. Note that +-- it is not part of the compiler proper, but rather the interface from +-- tools that need to read the tree to the tree reading routines, and is +-- thus bound as part of such tools. + +with GNAT.OS_Lib; use GNAT.OS_Lib; + +procedure Tree_In (Desc : File_Descriptor); +-- Desc is the file descriptor for the file containing the tree, as written +-- by the compiler in a previous compilation using Tree_Gen. On return the +-- global data structures are appropriately initialized. diff --git a/gcc/ada/tree_io.adb b/gcc/ada/tree_io.adb new file mode 100644 index 00000000000..5f4c30fae77 --- /dev/null +++ b/gcc/ada/tree_io.adb @@ -0,0 +1,661 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E _ I O -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.13 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Debug; use Debug; +with Output; use Output; +with Unchecked_Conversion; + +package body Tree_IO is + Debug_Flag_Tree : Boolean := False; + -- Debug flag for debug output from tree read/write + + ------------------------------------------- + -- Compression Scheme Used for Tree File -- + ------------------------------------------- + + -- We don't just write the data directly, but instead do a mild form + -- of compression, since we expect lots of compressible zeroes and + -- blanks. The compression scheme is as follows: + + -- 00nnnnnn followed by nnnnnn bytes (non compressed data) + -- 01nnnnnn indicates nnnnnn binary zero bytes + -- 10nnnnnn indicates nnnnnn ASCII space bytes + -- 11nnnnnn bbbbbbbb indicates nnnnnnnn occurrences of byte bbbbbbbb + + -- Since we expect many zeroes in trees, and many spaces in sources, + -- this compression should be reasonably efficient. We can put in + -- something better later on. + + -- Note that this compression applies to the Write_Tree_Data and + -- Read_Tree_Data calls, not to the calls to read and write single + -- scalar values, which are written in memory format without any + -- compression. + + C_Noncomp : constant := 2#00_000000#; + C_Zeros : constant := 2#01_000000#; + C_Spaces : constant := 2#10_000000#; + C_Repeat : constant := 2#11_000000#; + -- Codes for compression sequences + + Max_Count : constant := 63; + -- Maximum data length for one compression sequence + + Max_Comp : constant := Max_Count + 1; + -- Maximum length of one compression sequence + + -- The above compression scheme applies only to data written with the + -- Tree_Write routine and read with Tree_Read. Data written using the + -- Tree_Write_Char or Tree_Write_Int routines and read using the + -- corresponding input routines is not compressed. + + type Int_Bytes is array (1 .. 4) of Byte; + for Int_Bytes'Size use 32; + + function To_Int_Bytes is new Unchecked_Conversion (Int, Int_Bytes); + function To_Int is new Unchecked_Conversion (Int_Bytes, Int); + + ---------------------- + -- Global Variables -- + ---------------------- + + Tree_FD : File_Descriptor; + -- File descriptor for tree + + Buflen : constant Int := 8_192; + -- Length of buffer for read and write file data + + Buf : array (Pos range 1 .. Buflen) of Byte; + -- Read/write file data buffer + + Bufn : Nat; + -- Number of bytes read/written from/to buffer + + Buft : Nat; + -- Total number of bytes in input buffer containing valid data. Used only + -- for input operations. There is data left to be processed in the buffer + -- if Buft > Bufn. A value of zero for Buft means that the buffer is empty. + + ----------------------- + -- Local Subprograms -- + ----------------------- + + procedure Read_Buffer; + -- Reads data into buffer, setting Bufe appropriately + + function Read_Byte return Byte; + pragma Inline (Read_Byte); + -- Returns next byte from input file, raises Tree_Format_Error if none left + + procedure Write_Buffer; + -- Writes out current buffer contents + + procedure Write_Byte (B : Byte); + pragma Inline (Write_Byte); + -- Write one byte to output buffer, checking for buffer-full condition + + ----------------- + -- Read_Buffer -- + ----------------- + + procedure Read_Buffer is + begin + Buft := Int (Read (Tree_FD, Buf (1)'Address, Integer (Buflen))); + + if Buft = 0 then + raise Tree_Format_Error; + else + Bufn := 0; + end if; + end Read_Buffer; + + --------------- + -- Read_Byte -- + --------------- + + function Read_Byte return Byte is + begin + if Bufn = Buft then + Read_Buffer; + end if; + + Bufn := Bufn + 1; + return Buf (Bufn); + end Read_Byte; + + -------------------- + -- Tree_Read_Bool -- + -------------------- + + procedure Tree_Read_Bool (B : out Boolean) is + begin + B := Boolean'Val (Read_Byte); + + if Debug_Flag_Tree then + if B then + Write_Str ("True"); + else + Write_Str ("False"); + end if; + + Write_Eol; + end if; + end Tree_Read_Bool; + + -------------------- + -- Tree_Read_Char -- + -------------------- + + procedure Tree_Read_Char (C : out Character) is + begin + C := Character'Val (Read_Byte); + + if Debug_Flag_Tree then + Write_Str ("==> transmitting Character = "); + Write_Char (C); + Write_Eol; + end if; + end Tree_Read_Char; + + -------------------- + -- Tree_Read_Data -- + -------------------- + + procedure Tree_Read_Data (Addr : Address; Length : Int) is + + type S is array (Pos) of Byte; + -- This is a big array, for which we have to suppress the warning + + type SP is access all S; + + function To_SP is new Unchecked_Conversion (Address, SP); + + Data : constant SP := To_SP (Addr); + -- Data buffer to be read as an indexable array of bytes + + OP : Pos := 1; + -- Pointer to next byte of data buffer to be read into + + B : Byte; + C : Byte; + L : Int; + + begin + if Debug_Flag_Tree then + Write_Str ("==> transmitting "); + Write_Int (Length); + Write_Str (" data bytes"); + Write_Eol; + end if; + + -- Verify data length + + Tree_Read_Int (L); + + if L /= Length then + Write_Str ("==> transmitting, expected "); + Write_Int (Length); + Write_Str (" bytes, found length = "); + Write_Int (L); + Write_Eol; + raise Tree_Format_Error; + end if; + + -- Loop to read data + + while OP <= Length loop + + -- Get compression control character + + B := Read_Byte; + C := B and 2#00_111111#; + B := B and 2#11_000000#; + + -- Non-repeat case + + if B = C_Noncomp then + if Debug_Flag_Tree then + Write_Str ("==> uncompressed: "); + Write_Int (Int (C)); + Write_Str (", starting at "); + Write_Int (OP); + Write_Eol; + end if; + + for J in 1 .. C loop + Data (OP) := Read_Byte; + OP := OP + 1; + end loop; + + -- Repeated zeroes + + elsif B = C_Zeros then + if Debug_Flag_Tree then + Write_Str ("==> zeroes: "); + Write_Int (Int (C)); + Write_Str (", starting at "); + Write_Int (OP); + Write_Eol; + end if; + + for J in 1 .. C loop + Data (OP) := 0; + OP := OP + 1; + end loop; + + -- Repeated spaces + + elsif B = C_Spaces then + if Debug_Flag_Tree then + Write_Str ("==> spaces: "); + Write_Int (Int (C)); + Write_Str (", starting at "); + Write_Int (OP); + Write_Eol; + end if; + + for J in 1 .. C loop + Data (OP) := Character'Pos (' '); + OP := OP + 1; + end loop; + + -- Specified repeated character + + else -- B = C_Repeat + B := Read_Byte; + + if Debug_Flag_Tree then + Write_Str ("==> other char: "); + Write_Int (Int (C)); + Write_Str (" ("); + Write_Int (Int (B)); + Write_Char (')'); + Write_Str (", starting at "); + Write_Int (OP); + Write_Eol; + end if; + + for J in 1 .. C loop + Data (OP) := B; + OP := OP + 1; + end loop; + end if; + end loop; + + -- At end of loop, data item must be exactly filled + + if OP /= Length + 1 then + raise Tree_Format_Error; + end if; + + end Tree_Read_Data; + + -------------------------- + -- Tree_Read_Initialize -- + -------------------------- + + procedure Tree_Read_Initialize (Desc : File_Descriptor) is + begin + Buft := 0; + Bufn := 0; + Tree_FD := Desc; + Debug_Flag_Tree := Debug_Flag_5; + end Tree_Read_Initialize; + + ------------------- + -- Tree_Read_Int -- + ------------------- + + procedure Tree_Read_Int (N : out Int) is + N_Bytes : Int_Bytes; + + begin + for J in 1 .. 4 loop + N_Bytes (J) := Read_Byte; + end loop; + + N := To_Int (N_Bytes); + + if Debug_Flag_Tree then + Write_Str ("==> transmitting Int = "); + Write_Int (N); + Write_Eol; + end if; + end Tree_Read_Int; + + ------------------- + -- Tree_Read_Str -- + ------------------- + + procedure Tree_Read_Str (S : out String_Ptr) is + N : Nat; + + begin + Tree_Read_Int (N); + S := new String (1 .. Natural (N)); + Tree_Read_Data (S.all (1)'Address, N); + end Tree_Read_Str; + + ------------------------- + -- Tree_Read_Terminate -- + ------------------------- + + procedure Tree_Read_Terminate is + begin + -- Must be at end of input buffer, so we should get Tree_Format_Error + -- if we try to read one more byte, if not, we have a format error. + + declare + B : Byte; + begin + B := Read_Byte; + exception + when Tree_Format_Error => return; + end; + + raise Tree_Format_Error; + end Tree_Read_Terminate; + + --------------------- + -- Tree_Write_Bool -- + --------------------- + + procedure Tree_Write_Bool (B : Boolean) is + begin + if Debug_Flag_Tree then + Write_Str ("==> transmitting Boolean = "); + + if B then + Write_Str ("True"); + else + Write_Str ("False"); + end if; + + Write_Eol; + end if; + + Write_Byte (Boolean'Pos (B)); + end Tree_Write_Bool; + + --------------------- + -- Tree_Write_Char -- + --------------------- + + procedure Tree_Write_Char (C : Character) is + begin + if Debug_Flag_Tree then + Write_Str ("==> transmitting Character = "); + Write_Char (C); + Write_Eol; + end if; + + Write_Byte (Character'Pos (C)); + end Tree_Write_Char; + + --------------------- + -- Tree_Write_Data -- + --------------------- + + procedure Tree_Write_Data (Addr : Address; Length : Int) is + + type S is array (Pos) of Byte; + -- This is a big array, for which we have to suppress the warning + + type SP is access all S; + + function To_SP is new Unchecked_Conversion (Address, SP); + + Data : constant SP := To_SP (Addr); + -- Pointer to data to be written, converted to array type + + IP : Pos := 1; + -- Input buffer pointer, next byte to be processed + + NC : Nat range 0 .. Max_Count := 0; + -- Number of bytes of non-compressible sequence + + C : Byte; + + procedure Write_Non_Compressed_Sequence; + -- Output currently collected sequence of non-compressible data + + procedure Write_Non_Compressed_Sequence is + begin + if NC > 0 then + Write_Byte (C_Noncomp + Byte (NC)); + + if Debug_Flag_Tree then + Write_Str ("==> uncompressed: "); + Write_Int (NC); + Write_Str (", starting at "); + Write_Int (IP - NC); + Write_Eol; + end if; + + for J in reverse 1 .. NC loop + Write_Byte (Data (IP - J)); + end loop; + + NC := 0; + end if; + end Write_Non_Compressed_Sequence; + + -- Start of processing for Tree_Write_Data + + begin + if Debug_Flag_Tree then + Write_Str ("==> transmitting "); + Write_Int (Length); + Write_Str (" data bytes"); + Write_Eol; + end if; + + -- We write the count at the start, so that we can check it on + -- the corresponding read to make sure that reads and writes match + + Tree_Write_Int (Length); + + -- Conversion loop + -- IP is index of next input character + -- NC is number of non-compressible bytes saved up + + loop + -- If input is completely processed, then we are all done + + if IP > Length then + Write_Non_Compressed_Sequence; + return; + end if; + + -- Test for compressible sequence, must be at least three identical + -- bytes in a row to be worthwhile compressing. + + if IP + 2 <= Length + and then Data (IP) = Data (IP + 1) + and then Data (IP) = Data (IP + 2) + then + Write_Non_Compressed_Sequence; + + -- Count length of new compression sequence + + C := 3; + IP := IP + 3; + + while IP < Length + and then Data (IP) = Data (IP - 1) + and then C < Max_Count + loop + C := C + 1; + IP := IP + 1; + end loop; + + -- Output compression sequence + + if Data (IP - 1) = 0 then + if Debug_Flag_Tree then + Write_Str ("==> zeroes: "); + Write_Int (Int (C)); + Write_Str (", starting at "); + Write_Int (IP - Int (C)); + Write_Eol; + end if; + + Write_Byte (C_Zeros + C); + + elsif Data (IP - 1) = Character'Pos (' ') then + if Debug_Flag_Tree then + Write_Str ("==> spaces: "); + Write_Int (Int (C)); + Write_Str (", starting at "); + Write_Int (IP - Int (C)); + Write_Eol; + end if; + + Write_Byte (C_Spaces + C); + + else + if Debug_Flag_Tree then + Write_Str ("==> other char: "); + Write_Int (Int (C)); + Write_Str (" ("); + Write_Int (Int (Data (IP - 1))); + Write_Char (')'); + Write_Str (", starting at "); + Write_Int (IP - Int (C)); + Write_Eol; + end if; + + Write_Byte (C_Repeat + C); + Write_Byte (Data (IP - 1)); + end if; + + -- No compression possible here + + else + -- Output non-compressed sequence if at maximum length + + if NC = Max_Count then + Write_Non_Compressed_Sequence; + end if; + + NC := NC + 1; + IP := IP + 1; + end if; + end loop; + + end Tree_Write_Data; + + --------------------------- + -- Tree_Write_Initialize -- + --------------------------- + + procedure Tree_Write_Initialize (Desc : File_Descriptor) is + begin + Bufn := 0; + Tree_FD := Desc; + Set_Standard_Error; + Debug_Flag_Tree := Debug_Flag_5; + end Tree_Write_Initialize; + + -------------------- + -- Tree_Write_Int -- + -------------------- + + procedure Tree_Write_Int (N : Int) is + N_Bytes : constant Int_Bytes := To_Int_Bytes (N); + + begin + if Debug_Flag_Tree then + Write_Str ("==> transmitting Int = "); + Write_Int (N); + Write_Eol; + end if; + + for J in 1 .. 4 loop + Write_Byte (N_Bytes (J)); + end loop; + end Tree_Write_Int; + + -------------------- + -- Tree_Write_Str -- + -------------------- + + procedure Tree_Write_Str (S : String_Ptr) is + begin + Tree_Write_Int (S'Length); + Tree_Write_Data (S (1)'Address, S'Length); + end Tree_Write_Str; + + -------------------------- + -- Tree_Write_Terminate -- + -------------------------- + + procedure Tree_Write_Terminate is + begin + if Bufn > 0 then + Write_Buffer; + end if; + end Tree_Write_Terminate; + + ------------------ + -- Write_Buffer -- + ------------------ + + procedure Write_Buffer is + begin + if Integer (Bufn) = Write (Tree_FD, Buf'Address, Integer (Bufn)) then + Bufn := 0; + + else + Set_Standard_Error; + Write_Str ("fatal error: disk full"); + OS_Exit (2); + end if; + end Write_Buffer; + + ---------------- + -- Write_Byte -- + ---------------- + + procedure Write_Byte (B : Byte) is + begin + Bufn := Bufn + 1; + Buf (Bufn) := B; + + if Bufn = Buflen then + Write_Buffer; + end if; + end Write_Byte; + +end Tree_IO; diff --git a/gcc/ada/tree_io.ads b/gcc/ada/tree_io.ads new file mode 100644 index 00000000000..28fd07aefef --- /dev/null +++ b/gcc/ada/tree_io.ads @@ -0,0 +1,107 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E _ I O -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.5 $ -- +-- -- +-- Copyright (C) 1992-1999 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This package contains the routines used to read and write the tree files +-- used by ASIS. Only the actual read and write routines are here. The open, +-- create and close routines are elsewhere (in Osint in the compiler, and in +-- the tree read driver for the tree read interface). + +with GNAT.OS_Lib; use GNAT.OS_Lib; +with System; use System; +with Types; use Types; + +package Tree_IO is + + Tree_Format_Error : exception; + -- Raised if a format error is detected in the input file + + procedure Tree_Read_Initialize (Desc : File_Descriptor); + -- Called to initialize reading of a tree file. This call must be made + -- before calls to Tree_Read_xx. No calls to Tree_Write_xx are permitted + -- after this call. + + procedure Tree_Read_Data (Addr : Address; Length : Int); + -- Checks that the Length provided is the same as what has been provided + -- to the corresponding Tree_Write_Data from the current tree file, + -- Tree_Format_Error is raised if it is not the case. If Length is + -- correct and non zero, reads Length bytes of information into memory + -- starting at Addr from the current tree file. + + procedure Tree_Read_Bool (B : out Boolean); + -- Reads a single boolean value. The boolean value must have been written + -- with a call to the Tree_Write_Bool procedure. + + procedure Tree_Read_Char (C : out Character); + -- Reads a single character. The character must have been written with a + -- call to the Tree_Write_Char procedure. + + procedure Tree_Read_Int (N : out Int); + -- Reads a single integer value. The integer must have been written with + -- a call to the Tree_Write_Int procedure. + + procedure Tree_Read_Str (S : out String_Ptr); + -- Read string, allocate on heap, and return pointer to allocated string + -- which always has a lower bound of 1. + + procedure Tree_Read_Terminate; + -- Called after reading all data, checks that the buffer pointers is at + -- the end of file, raising Tree_Format_Error if not. + + procedure Tree_Write_Initialize (Desc : File_Descriptor); + -- Called to initialize writing of a tree file. This call must be made + -- before calls to Tree_Write_xx. No calls to Tree_Read_xx are permitted + -- after this call. + + procedure Tree_Write_Data (Addr : Address; Length : Int); + -- Writes Length then, if Length is not null, Length bytes of data + -- starting at Addr to current tree file + + procedure Tree_Write_Bool (B : Boolean); + -- Writes a single boolean value to the current tree file + + procedure Tree_Write_Char (C : Character); + -- Writes a single character to the current tree file + + procedure Tree_Write_Int (N : Int); + -- Writes a single integer value to the current tree file + + procedure Tree_Write_Str (S : String_Ptr); + -- Write out string value referenced by S. Low bound must be 1. + + procedure Tree_Write_Terminate; + -- Terminates writing of the file (flushing the buffer), but does not + -- close the file (the caller is responsible for closing the file). + +end Tree_IO; diff --git a/gcc/ada/treepr.adb b/gcc/ada/treepr.adb new file mode 100644 index 00000000000..80954c9e660 --- /dev/null +++ b/gcc/ada/treepr.adb @@ -0,0 +1,1873 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E P R -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.128 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Atree; use Atree; +with Csets; use Csets; +with Debug; use Debug; +with Einfo; use Einfo; +with Elists; use Elists; +with Lib; use Lib; +with Namet; use Namet; +with Nlists; use Nlists; +with Output; use Output; +with Sem_Mech; use Sem_Mech; +with Sinfo; use Sinfo; +with Snames; use Snames; +with Sinput; use Sinput; +with Stand; use Stand; +with Stringt; use Stringt; +with Treeprs; use Treeprs; +with Uintp; use Uintp; +with Urealp; use Urealp; +with Uname; use Uname; +with Unchecked_Deallocation; + +package body Treepr is + + use Atree.Unchecked_Access; + -- This module uses the unchecked access functions in package Atree + -- since it does an untyped traversal of the tree (we do not want to + -- count on the structure of the tree being correct in this routine!) + + ---------------------------------- + -- Approach Used for Tree Print -- + ---------------------------------- + + -- When a complete subtree is being printed, a trace phase first marks + -- the nodes and lists to be printed. This trace phase allocates logical + -- numbers corresponding to the order in which the nodes and lists will + -- be printed. The Node_Id, List_Id and Elist_Id values are mapped to + -- logical node numbers using a hash table. Output is done using a set + -- of Print_xxx routines, which are similar to the Write_xxx routines + -- with the same name, except that they do not generate any output in + -- the marking phase. This allows identical logic to be used in the + -- two phases. + + -- Note that the hash table not only holds the serial numbers, but also + -- acts as a record of which nodes have already been visited. In the + -- marking phase, a node has been visited if it is already in the hash + -- table, and in the printing phase, we can tell whether a node has + -- already been printed by looking at the value of the serial number. + + ---------------------- + -- Global Variables -- + ---------------------- + + type Hash_Record is record + Serial : Nat; + -- Serial number for hash table entry. A value of zero means that + -- the entry is currently unused. + + Id : Int; + -- If serial number field is non-zero, contains corresponding Id value + end record; + + type Hash_Table_Type is array (Nat range <>) of Hash_Record; + type Access_Hash_Table_Type is access Hash_Table_Type; + Hash_Table : Access_Hash_Table_Type; + -- The hash table itself, see Serial_Number function for details of use + + Hash_Table_Len : Nat; + -- Range of Hash_Table is from 0 .. Hash_Table_Len - 1 so that dividing + -- by Hash_Table_Len gives a remainder that is in Hash_Table'Range. + + Next_Serial_Number : Nat; + -- Number of last visited node or list. Used during the marking phase to + -- set proper node numbers in the hash table, and during the printing + -- phase to make sure that a given node is not printed more than once. + -- (nodes are printed in order during the printing phase, that's the + -- point of numbering them in the first place!) + + Printing_Descendants : Boolean; + -- True if descendants are being printed, False if not. In the false case, + -- only node Id's are printed. In the true case, node numbers as well as + -- node Id's are printed, as described above. + + type Phase_Type is (Marking, Printing); + -- Type for Phase variable + + Phase : Phase_Type; + -- When an entire tree is being printed, the traversal operates in two + -- phases. The first phase marks the nodes in use by installing node + -- numbers in the node number table. The second phase prints the nodes. + -- This variable indicates the current phase. + + ---------------------- + -- Local Procedures -- + ---------------------- + + procedure Print_End_Span (N : Node_Id); + -- Special routine to print contents of End_Span field of node N. + -- The format includes the implicit source location as well as the + -- value of the field. + + procedure Print_Init; + -- Initialize for printing of tree with descendents + + procedure Print_Term; + -- Clean up after printing of tree with descendents + + procedure Print_Char (C : Character); + -- Print character C if currently in print phase, noop if in marking phase + + procedure Print_Name (N : Name_Id); + -- Print name from names table if currently in print phase, noop if in + -- marking phase. Note that the name is output in mixed case mode. + + procedure Print_Node_Kind (N : Node_Id); + -- Print node kind name in mixed case if in print phase, noop if in + -- marking phase. + + procedure Print_Str (S : String); + -- Print string S if currently in print phase, noop if in marking phase + + procedure Print_Str_Mixed_Case (S : String); + -- Like Print_Str, except that the string is printed in mixed case mode + + procedure Print_Int (I : Int); + -- Print integer I if currently in print phase, noop if in marking phase + + procedure Print_Eol; + -- Print end of line if currently in print phase, noop if in marking phase + + procedure Print_Node_Ref (N : Node_Id); + -- Print "", "" or "Node #nnn" with additional information + -- in the latter case, including the Id and the Nkind of the node. + + procedure Print_List_Ref (L : List_Id); + -- Print "", or "" or "Node list #nnn" + + procedure Print_Elist_Ref (E : Elist_Id); + -- Print "", or "" or "Element list #nnn" + + procedure Print_Entity_Info (Ent : Entity_Id; Prefix : String); + -- Called if the node being printed is an entity. Prints fields from the + -- extension, using routines in Einfo to get the field names and flags. + + procedure Print_Field (Val : Union_Id; Format : UI_Format := Auto); + -- Print representation of Field value (name, tree, string, uint, charcode) + -- The format parameter controls the format of printing in the case of an + -- integer value (see UI_Write for details). + + procedure Print_Flag (F : Boolean); + -- Print True or False + + procedure Print_Node + (N : Node_Id; + Prefix_Str : String; + Prefix_Char : Character); + -- This is the internal routine used to print a single node. Each line of + -- output is preceded by Prefix_Str (which is used to set the indentation + -- level and the bars used to link list elements). In addition, for lines + -- other than the first, an additional character Prefix_Char is output. + + function Serial_Number (Id : Int) return Nat; + -- Given a Node_Id, List_Id or Elist_Id, returns the previously assigned + -- serial number, or zero if no serial number has yet been assigned. + + procedure Set_Serial_Number; + -- Can be called only immediately following a call to Serial_Number that + -- returned a value of zero. Causes the value of Next_Serial_Number to be + -- placed in the hash table (corresponding to the Id argument used in the + -- Serial_Number call), and increments Next_Serial_Number. + + procedure Visit_Node + (N : Node_Id; + Prefix_Str : String; + Prefix_Char : Character); + -- Called to process a single node in the case where descendents are to + -- be printed before every line, and Prefix_Char added to all lines + -- except the header line for the node. + + procedure Visit_List (L : List_Id; Prefix_Str : String); + -- Visit_List is called to process a list in the case where descendents + -- are to be printed. Prefix_Str is to be added to all printed lines. + + procedure Visit_Elist (E : Elist_Id; Prefix_Str : String); + -- Visit_Elist is called to process an element list in the case where + -- descendents are to be printed. Prefix_Str is to be added to all + -- printed lines. + + -------- + -- PE -- + -------- + + procedure PE (E : Elist_Id) is + begin + Print_Tree_Elist (E); + end PE; + + -------- + -- PL -- + -------- + + procedure PL (L : List_Id) is + begin + Print_Tree_List (L); + end PL; + + -------- + -- PN -- + -------- + + procedure PN (N : Node_Id) is + begin + Print_Tree_Node (N); + end PN; + + ---------------- + -- Print_Char -- + ---------------- + + procedure Print_Char (C : Character) is + begin + if Phase = Printing then + Write_Char (C); + end if; + end Print_Char; + + --------------------- + -- Print_Elist_Ref -- + --------------------- + + procedure Print_Elist_Ref (E : Elist_Id) is + begin + if Phase /= Printing then + return; + end if; + + if E = No_Elist then + Write_Str (""); + + elsif Is_Empty_Elmt_List (E) then + Write_Str ("Empty elist, (Elist_Id="); + Write_Int (Int (E)); + Write_Char (')'); + + else + Write_Str ("(Elist_Id="); + Write_Int (Int (E)); + Write_Char (')'); + + if Printing_Descendants then + Write_Str (" #"); + Write_Int (Serial_Number (Int (E))); + end if; + end if; + end Print_Elist_Ref; + + ------------------------- + -- Print_Elist_Subtree -- + ------------------------- + + procedure Print_Elist_Subtree (E : Elist_Id) is + begin + Print_Init; + + Next_Serial_Number := 1; + Phase := Marking; + Visit_Elist (E, ""); + + Next_Serial_Number := 1; + Phase := Printing; + Visit_Elist (E, ""); + + Print_Term; + end Print_Elist_Subtree; + + -------------------- + -- Print_End_Span -- + -------------------- + + procedure Print_End_Span (N : Node_Id) is + Val : constant Uint := End_Span (N); + + begin + UI_Write (Val); + Write_Str (" (Uint = "); + Write_Int (Int (Field5 (N))); + Write_Str (") "); + + if Val /= No_Uint then + Write_Location (End_Location (N)); + end if; + end Print_End_Span; + + ----------------------- + -- Print_Entity_Info -- + ----------------------- + + procedure Print_Entity_Info (Ent : Entity_Id; Prefix : String) is + function Field_Present (U : Union_Id) return Boolean; + -- Returns False unless the value U represents a missing value + -- (Empty, No_Uint, No_Ureal or No_String) + + function Field_Present (U : Union_Id) return Boolean is + begin + return + U /= Union_Id (Empty) and then + U /= To_Union (No_Uint) and then + U /= To_Union (No_Ureal) and then + U /= Union_Id (No_String); + end Field_Present; + + -- Start of processing for Print_Entity_Info + + begin + Print_Str (Prefix); + Print_Str ("Ekind = "); + Print_Str_Mixed_Case (Entity_Kind'Image (Ekind (Ent))); + Print_Eol; + + Print_Str (Prefix); + Print_Str ("Etype = "); + Print_Node_Ref (Etype (Ent)); + Print_Eol; + + if Convention (Ent) /= Convention_Ada then + Print_Str (Prefix); + Print_Str ("Convention = "); + + -- Print convention name skipping the Convention_ at the start + + declare + S : constant String := Convention_Id'Image (Convention (Ent)); + + begin + Print_Str_Mixed_Case (S (12 .. S'Last)); + Print_Eol; + end; + end if; + + if Field_Present (Field6 (Ent)) then + Print_Str (Prefix); + Write_Field6_Name (Ent); + Write_Str (" = "); + Print_Field (Field6 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field7 (Ent)) then + Print_Str (Prefix); + Write_Field7_Name (Ent); + Write_Str (" = "); + Print_Field (Field7 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field8 (Ent)) then + Print_Str (Prefix); + Write_Field8_Name (Ent); + Write_Str (" = "); + Print_Field (Field8 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field9 (Ent)) then + Print_Str (Prefix); + Write_Field9_Name (Ent); + Write_Str (" = "); + Print_Field (Field9 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field10 (Ent)) then + Print_Str (Prefix); + Write_Field10_Name (Ent); + Write_Str (" = "); + Print_Field (Field10 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field11 (Ent)) then + Print_Str (Prefix); + Write_Field11_Name (Ent); + Write_Str (" = "); + Print_Field (Field11 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field12 (Ent)) then + Print_Str (Prefix); + Write_Field12_Name (Ent); + Write_Str (" = "); + Print_Field (Field12 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field13 (Ent)) then + Print_Str (Prefix); + Write_Field13_Name (Ent); + Write_Str (" = "); + Print_Field (Field13 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field14 (Ent)) then + Print_Str (Prefix); + Write_Field14_Name (Ent); + Write_Str (" = "); + Print_Field (Field14 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field15 (Ent)) then + Print_Str (Prefix); + Write_Field15_Name (Ent); + Write_Str (" = "); + Print_Field (Field15 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field16 (Ent)) then + Print_Str (Prefix); + Write_Field16_Name (Ent); + Write_Str (" = "); + Print_Field (Field16 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field17 (Ent)) then + Print_Str (Prefix); + Write_Field17_Name (Ent); + Write_Str (" = "); + Print_Field (Field17 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field18 (Ent)) then + Print_Str (Prefix); + Write_Field18_Name (Ent); + Write_Str (" = "); + Print_Field (Field18 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field19 (Ent)) then + Print_Str (Prefix); + Write_Field19_Name (Ent); + Write_Str (" = "); + Print_Field (Field19 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field20 (Ent)) then + Print_Str (Prefix); + Write_Field20_Name (Ent); + Write_Str (" = "); + Print_Field (Field20 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field21 (Ent)) then + Print_Str (Prefix); + Write_Field21_Name (Ent); + Write_Str (" = "); + Print_Field (Field21 (Ent)); + Print_Eol; + end if; + + if Field_Present (Field22 (Ent)) then + Print_Str (Prefix); + Write_Field22_Name (Ent); + Write_Str (" = "); + + -- Mechanism case has to be handled specially + + if Ekind (Ent) = E_Function or else Is_Formal (Ent) then + declare + M : constant Mechanism_Type := Mechanism (Ent); + + begin + case M is + when Default_Mechanism => Write_Str ("Default"); + when By_Copy => Write_Str ("By_Copy"); + when By_Reference => Write_Str ("By_Reference"); + when By_Descriptor => Write_Str ("By_Descriptor"); + when By_Descriptor_UBS => Write_Str ("By_Descriptor_UBS"); + when By_Descriptor_UBSB => Write_Str ("By_Descriptor_UBSB"); + when By_Descriptor_UBA => Write_Str ("By_Descriptor_UBA"); + when By_Descriptor_S => Write_Str ("By_Descriptor_S"); + when By_Descriptor_SB => Write_Str ("By_Descriptor_SB"); + when By_Descriptor_A => Write_Str ("By_Descriptor_A"); + when By_Descriptor_NCA => Write_Str ("By_Descriptor_NCA"); + + when 1 .. Mechanism_Type'Last => + Write_Str ("By_Copy if size <= "); + Write_Int (Int (M)); + + end case; + end; + + -- Normal case (not Mechanism) + + else + Print_Field (Field22 (Ent)); + end if; + + Print_Eol; + end if; + + if Field_Present (Field23 (Ent)) then + Print_Str (Prefix); + Write_Field23_Name (Ent); + Write_Str (" = "); + Print_Field (Field23 (Ent)); + Print_Eol; + end if; + + Write_Entity_Flags (Ent, Prefix); + + end Print_Entity_Info; + + --------------- + -- Print_Eol -- + --------------- + + procedure Print_Eol is + begin + if Phase = Printing then + Write_Eol; + end if; + end Print_Eol; + + ----------------- + -- Print_Field -- + ----------------- + + procedure Print_Field (Val : Union_Id; Format : UI_Format := Auto) is + begin + if Phase /= Printing then + return; + end if; + + if Val in Node_Range then + Print_Node_Ref (Node_Id (Val)); + + elsif Val in List_Range then + Print_List_Ref (List_Id (Val)); + + elsif Val in Elist_Range then + Print_Elist_Ref (Elist_Id (Val)); + + elsif Val in Names_Range then + Print_Name (Name_Id (Val)); + Write_Str (" (Name_Id="); + Write_Int (Int (Val)); + Write_Char (')'); + + elsif Val in Strings_Range then + Write_String_Table_Entry (String_Id (Val)); + Write_Str (" (String_Id="); + Write_Int (Int (Val)); + Write_Char (')'); + + elsif Val in Uint_Range then + UI_Write (From_Union (Val), Format); + Write_Str (" (Uint = "); + Write_Int (Int (Val)); + Write_Char (')'); + + elsif Val in Ureal_Range then + UR_Write (From_Union (Val)); + Write_Str (" (Ureal = "); + Write_Int (Int (Val)); + Write_Char (')'); + + elsif Val in Char_Code_Range then + Write_Str ("Character code = "); + + declare + C : Char_Code := Char_Code (Val - Char_Code_Bias); + + begin + Write_Int (Int (C)); + Write_Str (" ('"); + Write_Char_Code (C); + Write_Str ("')"); + end; + + else + Print_Str ("****** Incorrect value = "); + Print_Int (Int (Val)); + end if; + end Print_Field; + + ---------------- + -- Print_Flag -- + ---------------- + + procedure Print_Flag (F : Boolean) is + begin + if F then + Print_Str ("True"); + else + Print_Str ("False"); + end if; + end Print_Flag; + + ---------------- + -- Print_Init -- + ---------------- + + procedure Print_Init is + begin + Printing_Descendants := True; + Write_Eol; + + -- Allocate and clear serial number hash table. The size is 150% of + -- the maximum possible number of entries, so that the hash table + -- cannot get significantly overloaded. + + Hash_Table_Len := (150 * (Num_Nodes + Num_Lists + Num_Elists)) / 100; + Hash_Table := new Hash_Table_Type (0 .. Hash_Table_Len - 1); + + for J in Hash_Table'Range loop + Hash_Table (J).Serial := 0; + end loop; + + end Print_Init; + + --------------- + -- Print_Int -- + --------------- + + procedure Print_Int (I : Int) is + begin + if Phase = Printing then + Write_Int (I); + end if; + end Print_Int; + + -------------------- + -- Print_List_Ref -- + -------------------- + + procedure Print_List_Ref (L : List_Id) is + begin + if Phase /= Printing then + return; + end if; + + if No (L) then + Write_Str (""); + + elsif Is_Empty_List (L) then + Write_Str (" (List_Id="); + Write_Int (Int (L)); + Write_Char (')'); + + else + Write_Str ("List"); + + if Printing_Descendants then + Write_Str (" #"); + Write_Int (Serial_Number (Int (L))); + end if; + + Write_Str (" (List_Id="); + Write_Int (Int (L)); + Write_Char (')'); + end if; + end Print_List_Ref; + + ------------------------ + -- Print_List_Subtree -- + ------------------------ + + procedure Print_List_Subtree (L : List_Id) is + begin + Print_Init; + + Next_Serial_Number := 1; + Phase := Marking; + Visit_List (L, ""); + + Next_Serial_Number := 1; + Phase := Printing; + Visit_List (L, ""); + + Print_Term; + end Print_List_Subtree; + + ---------------- + -- Print_Name -- + ---------------- + + procedure Print_Name (N : Name_Id) is + begin + if Phase = Printing then + if N = No_Name then + Print_Str (""); + + elsif N = Error_Name then + Print_Str (""); + + else + Get_Name_String (N); + Print_Char ('"'); + Write_Name (N); + Print_Char ('"'); + end if; + end if; + end Print_Name; + + ---------------- + -- Print_Node -- + ---------------- + + procedure Print_Node + (N : Node_Id; + Prefix_Str : String; + Prefix_Char : Character) + is + F : Fchar; + P : Natural := Pchar_Pos (Nkind (N)); + + Field_To_Be_Printed : Boolean; + Prefix_Str_Char : String (Prefix_Str'First .. Prefix_Str'Last + 1); + + Sfile : Source_File_Index; + Notes : Boolean; + Fmt : UI_Format; + + begin + if Phase /= Printing then + return; + end if; + + if Nkind (N) = N_Integer_Literal and then Print_In_Hex (N) then + Fmt := Hex; + else + Fmt := Auto; + end if; + + Prefix_Str_Char (Prefix_Str'Range) := Prefix_Str; + Prefix_Str_Char (Prefix_Str'Last + 1) := Prefix_Char; + + -- Print header line + + Print_Str (Prefix_Str); + Print_Node_Ref (N); + + Notes := False; + + if Comes_From_Source (N) then + Notes := True; + Print_Str (" (source"); + end if; + + if Analyzed (N) then + if not Notes then + Notes := True; + Print_Str (" ("); + else + Print_Str (","); + end if; + + Print_Str ("analyzed"); + end if; + + if Error_Posted (N) then + if not Notes then + Notes := True; + Print_Str (" ("); + else + Print_Str (","); + end if; + + Print_Str ("posted"); + end if; + + if Notes then + Print_Char (')'); + end if; + + Print_Eol; + + if Is_Rewrite_Substitution (N) then + Print_Str (Prefix_Str); + Print_Str (" Rewritten: original node = "); + Print_Node_Ref (Original_Node (N)); + Print_Eol; + end if; + + if N = Empty then + return; + end if; + + if not Is_List_Member (N) then + Print_Str (Prefix_Str); + Print_Str (" Parent = "); + Print_Node_Ref (Parent (N)); + Print_Eol; + end if; + + -- Print Sloc field if it is set + + if Sloc (N) /= No_Location then + Print_Str (Prefix_Str_Char); + Print_Str ("Sloc = "); + + if Sloc (N) = Standard_Location then + Print_Str ("Standard_Location"); + + elsif Sloc (N) = Standard_ASCII_Location then + Print_Str ("Standard_ASCII_Location"); + + else + Sfile := Get_Source_File_Index (Sloc (N)); + Print_Int (Int (Sloc (N)) - Int (Source_Text (Sfile)'First)); + Write_Str (" "); + Write_Location (Sloc (N)); + end if; + + Print_Eol; + end if; + + -- Print Chars field if present + + if Nkind (N) in N_Has_Chars and then Chars (N) /= No_Name then + Print_Str (Prefix_Str_Char); + Print_Str ("Chars = "); + Print_Name (Chars (N)); + Write_Str (" (Name_Id="); + Write_Int (Int (Chars (N))); + Write_Char (')'); + Print_Eol; + end if; + + -- Special field print operations for non-entity nodes + + if Nkind (N) not in N_Entity then + + -- Deal with Left_Opnd and Right_Opnd fields + + if Nkind (N) in N_Op + or else Nkind (N) = N_And_Then + or else Nkind (N) = N_In + or else Nkind (N) = N_Not_In + or else Nkind (N) = N_Or_Else + then + -- Print Left_Opnd if present + + if Nkind (N) not in N_Unary_Op then + Print_Str (Prefix_Str_Char); + Print_Str ("Left_Opnd = "); + Print_Node_Ref (Left_Opnd (N)); + Print_Eol; + end if; + + -- Print Right_Opnd + + Print_Str (Prefix_Str_Char); + Print_Str ("Right_Opnd = "); + Print_Node_Ref (Right_Opnd (N)); + Print_Eol; + end if; + + -- Print Entity field if operator (other cases of Entity + -- are in the table, so are handled in the normal circuit) + + if Nkind (N) in N_Op and then Present (Entity (N)) then + Print_Str (Prefix_Str_Char); + Print_Str ("Entity = "); + Print_Node_Ref (Entity (N)); + Print_Eol; + end if; + + -- Print special fields if we have a subexpression + + if Nkind (N) in N_Subexpr then + + if Assignment_OK (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Assignment_OK = True"); + Print_Eol; + end if; + + if Do_Range_Check (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Do_Range_Check = True"); + Print_Eol; + end if; + + if Has_Dynamic_Length_Check (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Has_Dynamic_Length_Check = True"); + Print_Eol; + end if; + + if Has_Dynamic_Range_Check (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Has_Dynamic_Range_Check = True"); + Print_Eol; + end if; + + if Is_Controlling_Actual (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Is_Controlling_Actual = True"); + Print_Eol; + end if; + + if Is_Overloaded (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Is_Overloaded = True"); + Print_Eol; + end if; + + if Is_Static_Expression (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Is_Static_Expression = True"); + Print_Eol; + end if; + + if Must_Not_Freeze (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Must_Not_Freeze = True"); + Print_Eol; + end if; + + if Paren_Count (N) /= 0 then + Print_Str (Prefix_Str_Char); + Print_Str ("Paren_Count = "); + Print_Int (Int (Paren_Count (N))); + Print_Eol; + end if; + + if Raises_Constraint_Error (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Raise_Constraint_Error = True"); + Print_Eol; + end if; + + end if; + + -- Print Do_Overflow_Check field if present + + if Nkind (N) in N_Op and then Do_Overflow_Check (N) then + Print_Str (Prefix_Str_Char); + Print_Str ("Do_Overflow_Check = True"); + Print_Eol; + end if; + + -- Print Etype field if present (printing of this field for entities + -- is handled by the Print_Entity_Info procedure). + + if Nkind (N) in N_Has_Etype + and then Present (Etype (N)) + then + Print_Str (Prefix_Str_Char); + Print_Str ("Etype = "); + Print_Node_Ref (Etype (N)); + Print_Eol; + end if; + end if; + + -- Loop to print fields included in Pchars array + + while P < Pchar_Pos (Node_Kind'Succ (Nkind (N))) loop + F := Pchars (P); + P := P + 1; + + -- Check for case of False flag, which we never print, or + -- an Empty field, which is also never printed + + case F is + when F_Field1 => + Field_To_Be_Printed := Field1 (N) /= Union_Id (Empty); + + when F_Field2 => + Field_To_Be_Printed := Field2 (N) /= Union_Id (Empty); + + when F_Field3 => + Field_To_Be_Printed := Field3 (N) /= Union_Id (Empty); + + when F_Field4 => + Field_To_Be_Printed := Field4 (N) /= Union_Id (Empty); + + when F_Field5 => + Field_To_Be_Printed := Field5 (N) /= Union_Id (Empty); + + when F_Flag4 => Field_To_Be_Printed := Flag4 (N); + when F_Flag5 => Field_To_Be_Printed := Flag5 (N); + when F_Flag6 => Field_To_Be_Printed := Flag6 (N); + when F_Flag7 => Field_To_Be_Printed := Flag7 (N); + when F_Flag8 => Field_To_Be_Printed := Flag8 (N); + when F_Flag9 => Field_To_Be_Printed := Flag9 (N); + when F_Flag10 => Field_To_Be_Printed := Flag10 (N); + when F_Flag11 => Field_To_Be_Printed := Flag11 (N); + when F_Flag12 => Field_To_Be_Printed := Flag12 (N); + when F_Flag13 => Field_To_Be_Printed := Flag13 (N); + when F_Flag14 => Field_To_Be_Printed := Flag14 (N); + when F_Flag15 => Field_To_Be_Printed := Flag15 (N); + when F_Flag16 => Field_To_Be_Printed := Flag16 (N); + when F_Flag17 => Field_To_Be_Printed := Flag17 (N); + when F_Flag18 => Field_To_Be_Printed := Flag18 (N); + + -- Flag1,2,3 are no longer used + + when F_Flag1 => raise Program_Error; + when F_Flag2 => raise Program_Error; + when F_Flag3 => raise Program_Error; + + end case; + + -- Print field if it is to be printed + + if Field_To_Be_Printed then + Print_Str (Prefix_Str_Char); + + while P < Pchar_Pos (Node_Kind'Succ (Nkind (N))) + and then Pchars (P) not in Fchar + loop + Print_Char (Pchars (P)); + P := P + 1; + end loop; + + Print_Str (" = "); + + case F is + when F_Field1 => Print_Field (Field1 (N), Fmt); + when F_Field2 => Print_Field (Field2 (N), Fmt); + when F_Field3 => Print_Field (Field3 (N), Fmt); + when F_Field4 => Print_Field (Field4 (N), Fmt); + + -- Special case End_Span = Uint5 + + when F_Field5 => + if Nkind (N) = N_Case_Statement + or else Nkind (N) = N_If_Statement + then + Print_End_Span (N); + else + Print_Field (Field5 (N), Fmt); + end if; + + when F_Flag4 => Print_Flag (Flag4 (N)); + when F_Flag5 => Print_Flag (Flag5 (N)); + when F_Flag6 => Print_Flag (Flag6 (N)); + when F_Flag7 => Print_Flag (Flag7 (N)); + when F_Flag8 => Print_Flag (Flag8 (N)); + when F_Flag9 => Print_Flag (Flag9 (N)); + when F_Flag10 => Print_Flag (Flag10 (N)); + when F_Flag11 => Print_Flag (Flag11 (N)); + when F_Flag12 => Print_Flag (Flag12 (N)); + when F_Flag13 => Print_Flag (Flag13 (N)); + when F_Flag14 => Print_Flag (Flag14 (N)); + when F_Flag15 => Print_Flag (Flag15 (N)); + when F_Flag16 => Print_Flag (Flag16 (N)); + when F_Flag17 => Print_Flag (Flag17 (N)); + when F_Flag18 => Print_Flag (Flag18 (N)); + + -- Flag1,2,3 are no longer used + + when F_Flag1 => raise Program_Error; + when F_Flag2 => raise Program_Error; + when F_Flag3 => raise Program_Error; + end case; + + Print_Eol; + + -- Field is not to be printed (False flag field) + + else + while P < Pchar_Pos (Node_Kind'Succ (Nkind (N))) + and then Pchars (P) not in Fchar + loop + P := P + 1; + end loop; + end if; + + end loop; + + -- Print entity information for entities + + if Nkind (N) in N_Entity then + Print_Entity_Info (N, Prefix_Str_Char); + end if; + + end Print_Node; + + --------------------- + -- Print_Node_Kind -- + --------------------- + + procedure Print_Node_Kind (N : Node_Id) is + Ucase : Boolean; + S : constant String := Node_Kind'Image (Nkind (N)); + + begin + if Phase = Printing then + Ucase := True; + + -- Note: the call to Fold_Upper in this loop is to get past the GNAT + -- bug of 'Image returning lower case instead of upper case. + + for J in S'Range loop + if Ucase then + Write_Char (Fold_Upper (S (J))); + else + Write_Char (Fold_Lower (S (J))); + end if; + + Ucase := (S (J) = '_'); + end loop; + end if; + end Print_Node_Kind; + + -------------------- + -- Print_Node_Ref -- + -------------------- + + procedure Print_Node_Ref (N : Node_Id) is + S : Nat; + + begin + if Phase /= Printing then + return; + end if; + + if N = Empty then + Write_Str (""); + + elsif N = Error then + Write_Str (""); + + else + if Printing_Descendants then + S := Serial_Number (Int (N)); + + if S /= 0 then + Write_Str ("Node"); + Write_Str (" #"); + Write_Int (S); + Write_Char (' '); + end if; + end if; + + Print_Node_Kind (N); + + if Nkind (N) in N_Has_Chars then + Write_Char (' '); + Print_Name (Chars (N)); + end if; + + if Nkind (N) in N_Entity then + Write_Str (" (Entity_Id="); + else + Write_Str (" (Node_Id="); + end if; + + Write_Int (Int (N)); + + if Sloc (N) <= Standard_Location then + Write_Char ('s'); + end if; + + Write_Char (')'); + + end if; + end Print_Node_Ref; + + ------------------------ + -- Print_Node_Subtree -- + ------------------------ + + procedure Print_Node_Subtree (N : Node_Id) is + begin + Print_Init; + + Next_Serial_Number := 1; + Phase := Marking; + Visit_Node (N, "", ' '); + + Next_Serial_Number := 1; + Phase := Printing; + Visit_Node (N, "", ' '); + + Print_Term; + end Print_Node_Subtree; + + --------------- + -- Print_Str -- + --------------- + + procedure Print_Str (S : String) is + begin + if Phase = Printing then + Write_Str (S); + end if; + end Print_Str; + + -------------------------- + -- Print_Str_Mixed_Case -- + -------------------------- + + procedure Print_Str_Mixed_Case (S : String) is + Ucase : Boolean; + + begin + if Phase = Printing then + Ucase := True; + + for J in S'Range loop + if Ucase then + Write_Char (S (J)); + else + Write_Char (Fold_Lower (S (J))); + end if; + + Ucase := (S (J) = '_'); + end loop; + end if; + end Print_Str_Mixed_Case; + + ---------------- + -- Print_Term -- + ---------------- + + procedure Print_Term is + procedure Free is new Unchecked_Deallocation + (Hash_Table_Type, Access_Hash_Table_Type); + + begin + Free (Hash_Table); + end Print_Term; + + --------------------- + -- Print_Tree_Elist -- + --------------------- + + procedure Print_Tree_Elist (E : Elist_Id) is + M : Elmt_Id; + + begin + Printing_Descendants := False; + Phase := Printing; + + Print_Elist_Ref (E); + Print_Eol; + + M := First_Elmt (E); + + if No (M) then + Print_Str (""); + Print_Eol; + + else + loop + Print_Char ('|'); + Print_Eol; + exit when No (Next_Elmt (M)); + Print_Node (Node (M), "", '|'); + Next_Elmt (M); + end loop; + + Print_Node (Node (M), "", ' '); + Print_Eol; + end if; + end Print_Tree_Elist; + + --------------------- + -- Print_Tree_List -- + --------------------- + + procedure Print_Tree_List (L : List_Id) is + N : Node_Id; + + begin + Printing_Descendants := False; + Phase := Printing; + + Print_List_Ref (L); + Print_Str (" List_Id="); + Print_Int (Int (L)); + Print_Eol; + + N := First (L); + + if N = Empty then + Print_Str (""); + Print_Eol; + + else + loop + Print_Char ('|'); + Print_Eol; + exit when Next (N) = Empty; + Print_Node (N, "", '|'); + Next (N); + end loop; + + Print_Node (N, "", ' '); + Print_Eol; + end if; + end Print_Tree_List; + + --------------------- + -- Print_Tree_Node -- + --------------------- + + procedure Print_Tree_Node (N : Node_Id; Label : String := "") is + begin + Printing_Descendants := False; + Phase := Printing; + Print_Node (N, Label, ' '); + end Print_Tree_Node; + + -------- + -- PT -- + -------- + + procedure PT (N : Node_Id) is + begin + Print_Node_Subtree (N); + end PT; + + ------------------- + -- Serial_Number -- + ------------------- + + -- The hashing algorithm is to use the remainder of the ID value divided + -- by the hash table length as the starting point in the table, and then + -- handle collisions by serial searching wrapping at the end of the table. + + Hash_Slot : Nat; + -- Set by an unsuccessful call to Serial_Number (one which returns zero) + -- to save the slot that should be used if Set_Serial_Number is called. + + function Serial_Number (Id : Int) return Nat is + H : Int := Id mod Hash_Table_Len; + + begin + while Hash_Table (H).Serial /= 0 loop + + if Id = Hash_Table (H).Id then + return Hash_Table (H).Serial; + end if; + + H := H + 1; + + if H > Hash_Table'Last then + H := 0; + end if; + end loop; + + -- Entry was not found, save slot number for possible subsequent call + -- to Set_Serial_Number, and unconditionally save the Id in this slot + -- in case of such a call (the Id field is never read if the serial + -- number of the slot is zero, so this is harmless in the case where + -- Set_Serial_Number is not subsequently called). + + Hash_Slot := H; + Hash_Table (H).Id := Id; + return 0; + + end Serial_Number; + + ----------------------- + -- Set_Serial_Number -- + ----------------------- + + procedure Set_Serial_Number is + begin + Hash_Table (Hash_Slot).Serial := Next_Serial_Number; + Next_Serial_Number := Next_Serial_Number + 1; + end Set_Serial_Number; + + --------------- + -- Tree_Dump -- + --------------- + + procedure Tree_Dump is + procedure Underline; + -- Put underline under string we just printed + + procedure Underline is + Col : constant Int := Column; + + begin + Write_Eol; + + while Col > Column loop + Write_Char ('-'); + end loop; + + Write_Eol; + end Underline; + + -- Start of processing for Tree_Dump. Note that we turn off the tree dump + -- flags immediately, before starting the dump. This avoids generating two + -- copies of the dump if an abort occurs after printing the dump, and more + -- importantly, avoids an infinite loop if an abort occurs during the dump. + + -- Note: unlike in the source print case (in Sprint), we do not output + -- separate trees for each unit. Instead the -df debug switch causes the + -- tree that is output from the main unit to trace references into other + -- units (normally such references are not traced). Since all other units + -- are linked to the main unit by at least one reference, this causes all + -- tree nodes to be included in the output tree. + + begin + if Debug_Flag_Y then + Debug_Flag_Y := False; + Write_Eol; + Write_Str ("Tree created for Standard (spec) "); + Underline; + Print_Node_Subtree (Standard_Package_Node); + Write_Eol; + end if; + + if Debug_Flag_T then + Debug_Flag_T := False; + + Write_Eol; + Write_Str ("Tree created for "); + Write_Unit_Name (Unit_Name (Main_Unit)); + Underline; + Print_Node_Subtree (Cunit (Main_Unit)); + Write_Eol; + end if; + + end Tree_Dump; + + ----------------- + -- Visit_Elist -- + ----------------- + + procedure Visit_Elist (E : Elist_Id; Prefix_Str : String) is + M : Elmt_Id; + N : Node_Id; + S : constant Nat := Serial_Number (Int (E)); + + begin + -- In marking phase, return if already marked, otherwise set next + -- serial number in hash table for later reference. + + if Phase = Marking then + if S /= 0 then + return; -- already visited + else + Set_Serial_Number; + end if; + + -- In printing phase, if already printed, then return, otherwise we + -- are printing the next item, so increment the serial number. + + else + if S < Next_Serial_Number then + return; -- already printed + else + Next_Serial_Number := Next_Serial_Number + 1; + end if; + end if; + + -- Now process the list (Print calls have no effect in marking phase) + + Print_Str (Prefix_Str); + Print_Elist_Ref (E); + Print_Eol; + + if Is_Empty_Elmt_List (E) then + Print_Str (Prefix_Str); + Print_Str ("(Empty element list)"); + Print_Eol; + Print_Eol; + + else + if Phase = Printing then + M := First_Elmt (E); + while Present (M) loop + N := Node (M); + Print_Str (Prefix_Str); + Print_Str (" "); + Print_Node_Ref (N); + Print_Eol; + Next_Elmt (M); + end loop; + + Print_Str (Prefix_Str); + Print_Eol; + end if; + + M := First_Elmt (E); + while Present (M) loop + Visit_Node (Node (M), Prefix_Str, ' '); + Next_Elmt (M); + end loop; + end if; + end Visit_Elist; + + ---------------- + -- Visit_List -- + ---------------- + + procedure Visit_List (L : List_Id; Prefix_Str : String) is + N : Node_Id; + S : constant Nat := Serial_Number (Int (L)); + + begin + -- In marking phase, return if already marked, otherwise set next + -- serial number in hash table for later reference. + + if Phase = Marking then + if S /= 0 then + return; + else + Set_Serial_Number; + end if; + + -- In printing phase, if already printed, then return, otherwise we + -- are printing the next item, so increment the serial number. + + else + if S < Next_Serial_Number then + return; -- already printed + else + Next_Serial_Number := Next_Serial_Number + 1; + end if; + end if; + + -- Now process the list (Print calls have no effect in marking phase) + + Print_Str (Prefix_Str); + Print_List_Ref (L); + Print_Eol; + + Print_Str (Prefix_Str); + Print_Str ("|Parent = "); + Print_Node_Ref (Parent (L)); + Print_Eol; + + N := First (L); + + if N = Empty then + Print_Str (Prefix_Str); + Print_Str ("(Empty list)"); + Print_Eol; + Print_Eol; + + else + Print_Str (Prefix_Str); + Print_Char ('|'); + Print_Eol; + + while Next (N) /= Empty loop + Visit_Node (N, Prefix_Str, '|'); + Next (N); + end loop; + end if; + + Visit_Node (N, Prefix_Str, ' '); + end Visit_List; + + ---------------- + -- Visit_Node -- + ---------------- + + procedure Visit_Node + (N : Node_Id; + Prefix_Str : String; + Prefix_Char : Character) + is + New_Prefix : String (Prefix_Str'First .. Prefix_Str'Last + 2); + -- Prefix string for printing referenced fields + + procedure Visit_Descendent + (D : Union_Id; + No_Indent : Boolean := False); + -- This procedure tests the given value of one of the Fields referenced + -- by the current node to determine whether to visit it recursively. + -- Normally No_Indent is false, which means tha the visited node will + -- be indented using New_Prefix. If No_Indent is set to True, then + -- this indentation is skipped, and Prefix_Str is used for the call + -- to print the descendent. No_Indent is effective only if the + -- referenced descendent is a node. + + ---------------------- + -- Visit_Descendent -- + ---------------------- + + procedure Visit_Descendent + (D : Union_Id; + No_Indent : Boolean := False) + is + begin + -- Case of descendent is a node + + if D in Node_Range then + + -- Don't bother about Empty or Error descendents + + if D <= Union_Id (Empty_Or_Error) then + return; + end if; + + declare + Nod : constant Node_Or_Entity_Id := Node_Or_Entity_Id (D); + + begin + -- Descendents in one of the standardly compiled internal + -- packages are normally ignored, unless the parent is also + -- in such a package (happens when Standard itself is output) + -- or if the -df switch is set which causes all links to be + -- followed, even into package standard. + + if Sloc (Nod) <= Standard_Location then + if Sloc (N) > Standard_Location + and then not Debug_Flag_F + then + return; + end if; + + -- Don't bother about a descendent in a different unit than + -- the node we came from unless the -df switch is set. Note + -- that we know at this point that Sloc (D) > Standard_Location + + -- Note: the tests for No_Location here just make sure that we + -- don't blow up on a node which is missing an Sloc value. This + -- should not normally happen. + + else + if (Sloc (N) <= Standard_Location + or else Sloc (N) = No_Location + or else Sloc (Nod) = No_Location + or else not In_Same_Source_Unit (Nod, N)) + and then not Debug_Flag_F + then + return; + end if; + end if; + + -- Don't bother visiting a source node that has a parent which + -- is not the node we came from. We prefer to trace such nodes + -- from their real parents. This causes the tree to be printed + -- in a more coherent order, e.g. a defining identifier listed + -- next to its corresponding declaration, instead of next to + -- some semantic reference. + + -- This test is skipped for nodes in standard packages unless + -- the -dy option is set (which outputs the tree for standard) + + -- Also, always follow pointers to Is_Itype entities, + -- since we want to list these when they are first referenced. + + if Parent (Nod) /= Empty + and then Comes_From_Source (Nod) + and then Parent (Nod) /= N + and then (Sloc (N) > Standard_Location or else Debug_Flag_Y) + then + return; + end if; + + -- If we successfully fall through all the above tests (which + -- execute a return if the node is not to be visited), we can + -- go ahead and visit the node! + + if No_Indent then + Visit_Node (Nod, Prefix_Str, Prefix_Char); + else + Visit_Node (Nod, New_Prefix, ' '); + end if; + end; + + -- Case of descendent is a list + + elsif D in List_Range then + + -- Don't bother with a missing list, empty list or error list + + if D = Union_Id (No_List) + or else D = Union_Id (Error_List) + or else Is_Empty_List (List_Id (D)) + then + return; + + -- Otherwise we can visit the list. Note that we don't bother + -- to do the parent test that we did for the node case, because + -- it just does not happen that lists are referenced more than + -- one place in the tree. We aren't counting on this being the + -- case to generate valid output, it is just that we don't need + -- in practice to worry about listing the list at a place that + -- is inconvenient. + + else + Visit_List (List_Id (D), New_Prefix); + end if; + + -- Case of descendent is an element list + + elsif D in Elist_Range then + + -- Don't bother with a missing list, or an empty list + + if D = Union_Id (No_Elist) + or else Is_Empty_Elmt_List (Elist_Id (D)) + then + return; + + -- Otherwise, visit the referenced element list + + else + Visit_Elist (Elist_Id (D), New_Prefix); + end if; + + -- For all other kinds of descendents (strings, names, uints etc), + -- there is nothing to visit (the contents of the field will be + -- printed when we print the containing node, but what concerns + -- us now is looking for descendents in the tree. + + else + null; + end if; + end Visit_Descendent; + + -- Start of processing for Visit_Node + + begin + if N = Empty then + return; + end if; + + -- Set fatal error node in case we get a blow up during the trace + + Current_Error_Node := N; + + New_Prefix (Prefix_Str'Range) := Prefix_Str; + New_Prefix (Prefix_Str'Last + 1) := Prefix_Char; + New_Prefix (Prefix_Str'Last + 2) := ' '; + + -- In the marking phase, all we do is to set the serial number + + if Phase = Marking then + if Serial_Number (Int (N)) /= 0 then + return; -- already visited + else + Set_Serial_Number; + end if; + + -- In the printing phase, we print the node + + else + if Serial_Number (Int (N)) < Next_Serial_Number then + + -- Here we have already visited the node, but if it is in + -- a list, we still want to print the reference, so that + -- it is clear that it belongs to the list. + + if Is_List_Member (N) then + Print_Str (Prefix_Str); + Print_Node_Ref (N); + Print_Eol; + Print_Str (Prefix_Str); + Print_Char (Prefix_Char); + Print_Str ("(already output)"); + Print_Eol; + Print_Str (Prefix_Str); + Print_Char (Prefix_Char); + Print_Eol; + end if; + + return; + + else + Print_Node (N, Prefix_Str, Prefix_Char); + Print_Str (Prefix_Str); + Print_Char (Prefix_Char); + Print_Eol; + Next_Serial_Number := Next_Serial_Number + 1; + end if; + end if; + + -- Visit all descendents of this node + + if Nkind (N) not in N_Entity then + Visit_Descendent (Field1 (N)); + Visit_Descendent (Field2 (N)); + Visit_Descendent (Field3 (N)); + Visit_Descendent (Field4 (N)); + Visit_Descendent (Field5 (N)); + + -- Entity case + + else + Visit_Descendent (Field1 (N)); + Visit_Descendent (Field3 (N)); + Visit_Descendent (Field4 (N)); + Visit_Descendent (Field5 (N)); + Visit_Descendent (Field6 (N)); + Visit_Descendent (Field7 (N)); + Visit_Descendent (Field8 (N)); + Visit_Descendent (Field9 (N)); + Visit_Descendent (Field10 (N)); + Visit_Descendent (Field11 (N)); + Visit_Descendent (Field12 (N)); + Visit_Descendent (Field13 (N)); + Visit_Descendent (Field14 (N)); + Visit_Descendent (Field15 (N)); + Visit_Descendent (Field16 (N)); + Visit_Descendent (Field17 (N)); + Visit_Descendent (Field18 (N)); + Visit_Descendent (Field19 (N)); + Visit_Descendent (Field20 (N)); + Visit_Descendent (Field21 (N)); + Visit_Descendent (Field22 (N)); + Visit_Descendent (Field23 (N)); + + -- You may be wondering why we omitted Field2 above. The answer + -- is that this is the Next_Entity field, and we want to treat + -- it rather specially. Why? Because a Next_Entity link does not + -- correspond to a level deeper in the tree, and we do not want + -- the tree to march off to the right of the page due to bogus + -- indentations coming from this effect. + + -- To prevent this, what we do is to control references via + -- Next_Entity only from the first entity on a given scope + -- chain, and we keep them all at the same level. Of course + -- if an entity has already been referenced it is not printed. + + if Present (Next_Entity (N)) + and then Present (Scope (N)) + and then First_Entity (Scope (N)) = N + then + declare + Nod : Node_Id; + + begin + Nod := N; + while Present (Nod) loop + Visit_Descendent (Union_Id (Next_Entity (Nod))); + Nod := Next_Entity (Nod); + end loop; + end; + end if; + end if; + end Visit_Node; + +end Treepr; diff --git a/gcc/ada/treepr.ads b/gcc/ada/treepr.ads new file mode 100644 index 00000000000..b2a8c6fdd9c --- /dev/null +++ b/gcc/ada/treepr.ads @@ -0,0 +1,79 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E P R -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.14 $ -- +-- -- +-- Copyright (C) 1992,1993,1994,1995 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Types; use Types; +package Treepr is + +-- This package provides printing routines for the abstract syntax tree +-- These routines are intended only for debugging use. + + procedure Tree_Dump; + -- This routine is called from the GNAT main program to dump trees as + -- requested by debug options (including tree of Standard if requested). + + procedure Print_Tree_Node (N : Node_Id; Label : String := ""); + -- Prints a single tree node, without printing descendants. The Label + -- string is used to preface each line of the printed output. + + procedure Print_Tree_List (L : List_Id); + -- Prints a single node list, without printing the descendants of any + -- of the nodes in the list + + procedure Print_Tree_Elist (E : Elist_Id); + -- Prints a single node list, without printing the descendants of any + -- of the nodes in the list + + procedure Print_Node_Subtree (N : Node_Id); + -- Prints the subtree routed at a specified tree node, including all + -- referenced descendants. + + procedure Print_List_Subtree (L : List_Id); + -- Prints the subtree consisting of the given node list and all its + -- referenced descendants. + + procedure Print_Elist_Subtree (E : Elist_Id); + -- Prints the subtree consisting of the given element list and all its + -- referenced descendants. + + procedure PE (E : Elist_Id); + -- Debugging procedure (to be called within gdb) + -- same as Print_Tree_Elist + + procedure PL (L : List_Id); + -- Debugging procedure (to be called within gdb) + -- same as Print_Tree_List + + procedure PN (N : Node_Id); + -- Debugging procedure (to be called within gdb) + -- same as Print_Tree_Node with Label = "" + + procedure PT (N : Node_Id); + -- Debugging procedure (to be called within gdb) + -- same as Print_Node_Subtree + +end Treepr; diff --git a/gcc/ada/treeprs.ads b/gcc/ada/treeprs.ads new file mode 100644 index 00000000000..1afdb8782a6 --- /dev/null +++ b/gcc/ada/treeprs.ads @@ -0,0 +1,795 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E P R S -- +-- -- +-- S p e c -- +-- -- +-- Generated by xtreeprs revision 1.31 using -- +-- sinfo.ads revision 1.430 -- +-- treeprs.adt revision 1.17 -- +-- -- +-- Copyright (C) 1992-1997 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + + +-- This package contains the declaration of the string used by the Tree_Print +-- package. It must be updated whenever the arrangements of the field names +-- in package Sinfo is changed. The utility program XTREEPRS is used to +-- do this update correctly using the template treeprs.adt as input. + +with Sinfo; use Sinfo; + +package Treeprs is + + -------------------------------- + -- String Data for Node Print -- + -------------------------------- + + -- String data for print out. The Pchars array is a long string with the + -- the entry for each node type consisting of a single blank, followed by + -- a series of entries, one for each Op or Flag field used for the node. + -- Each entry has a single character which identifies the field, followed + -- by the synonym name. The starting location for a given node type is + -- found from the corresponding entry in the Pchars_Pos_Array. + + -- The following characters identify the field. These are characters + -- which could never occur in a field name, so they also mark the + -- end of the previous name. + + subtype Fchar is Character range '#' .. '9'; + + F_Field1 : constant Fchar := '#'; -- Character'Val (16#23#) + F_Field2 : constant Fchar := '$'; -- Character'Val (16#24#) + F_Field3 : constant Fchar := '%'; -- Character'Val (16#25#) + F_Field4 : constant Fchar := '&'; -- Character'Val (16#26#) + F_Field5 : constant Fchar := '''; -- Character'Val (16#27#) + F_Flag1 : constant Fchar := '('; -- Character'Val (16#28#) + F_Flag2 : constant Fchar := ')'; -- Character'Val (16#29#) + F_Flag3 : constant Fchar := '*'; -- Character'Val (16#2A#) + F_Flag4 : constant Fchar := '+'; -- Character'Val (16#2B#) + F_Flag5 : constant Fchar := ','; -- Character'Val (16#2C#) + F_Flag6 : constant Fchar := '-'; -- Character'Val (16#2D#) + F_Flag7 : constant Fchar := '.'; -- Character'Val (16#2E#) + F_Flag8 : constant Fchar := '/'; -- Character'Val (16#2F#) + F_Flag9 : constant Fchar := '0'; -- Character'Val (16#30#) + F_Flag10 : constant Fchar := '1'; -- Character'Val (16#31#) + F_Flag11 : constant Fchar := '2'; -- Character'Val (16#32#) + F_Flag12 : constant Fchar := '3'; -- Character'Val (16#33#) + F_Flag13 : constant Fchar := '4'; -- Character'Val (16#34#) + F_Flag14 : constant Fchar := '5'; -- Character'Val (16#35#) + F_Flag15 : constant Fchar := '6'; -- Character'Val (16#36#) + F_Flag16 : constant Fchar := '7'; -- Character'Val (16#37#) + F_Flag17 : constant Fchar := '8'; -- Character'Val (16#38#) + F_Flag18 : constant Fchar := '9'; -- Character'Val (16#39#) + + -- Note this table does not include entity field and flags whose access + -- functions are in Einfo (these are handled by the Print_Entity_Info + -- procedure in Treepr, which uses the routines in Einfo to get the + -- proper symbolic information). In addition, the following fields are + -- handled by Treepr, and do not appear in the Pchars array: + + -- Analyzed + -- Cannot_Be_Constant + -- Chars + -- Comes_From_Source + -- Error_Posted + -- Etype + -- Is_Controlling_Actual + -- Is_Overloaded + -- Is_Static_Expression + -- Left_Opnd + -- Must_Check_Expr + -- Must_Not_Freeze + -- No_Overflow_Expr + -- Paren_Count + -- Raises_Constraint_Error + -- Right_Opnd + + Pchars : constant String := + -- Unused_At_Start + "" & + -- At_Clause + "#Identifier%Expression" & + -- Component_Clause + "#Component_Name$Position%First_Bit&Last_Bit" & + -- Enumeration_Representation_Clause + "#Identifier%Array_Aggregate&Next_Rep_Item" & + -- Mod_Clause + "%Expression&Pragmas_Before" & + -- Record_Representation_Clause + "#Identifier$Mod_Clause%Component_Clauses&Next_Rep_Item" & + -- Attribute_Definition_Clause + "$Name%Expression&Next_Rep_Item+From_At_Mod" & + -- Empty + "" & + -- Error + "" & + -- Pragma + "$Pragma_Argument_Associations%Debug_Statement&Next_Rep_Item" & + -- Pragma_Argument_Association + "%Expression" & + -- Defining_Character_Literal + "$Next_Entity%Scope" & + -- Defining_Identifier + "$Next_Entity%Scope" & + -- Defining_Operator_Symbol + "$Next_Entity%Scope" & + -- Expanded_Name + "%Prefix$Selector_Name&Entity4Redundant_Use2Has_Private_View" & + -- Identifier + "&Entity$Original_Discriminant4Redundant_Use2Has_Private_View" & + -- Operator_Symbol + "%Strval&Entity2Has_Private_View" & + -- Character_Literal + "$Char_Literal_Value&Entity2Has_Private_View" & + -- Op_Add + "" & + -- Op_Concat + "4Is_Component_Left_Opnd5Is_Component_Right_Opnd" & + -- Op_Divide + "5Treat_Fixed_As_Integer4Do_Division_Check9Rounded_Result" & + -- Op_Expon + "4Is_Power_Of_2_For_Shift" & + -- Op_Mod + "5Treat_Fixed_As_Integer4Do_Division_Check" & + -- Op_Multiply + "5Treat_Fixed_As_Integer9Rounded_Result" & + -- Op_Rem + "5Treat_Fixed_As_Integer4Do_Division_Check" & + -- Op_Subtract + "" & + -- Op_And + "+Do_Length_Check" & + -- Op_Eq + "" & + -- Op_Ge + "" & + -- Op_Gt + "" & + -- Op_Le + "" & + -- Op_Lt + "" & + -- Op_Ne + "" & + -- Op_Or + "+Do_Length_Check" & + -- Op_Xor + "+Do_Length_Check" & + -- Op_Rotate_Left + "+Shift_Count_OK" & + -- Op_Rotate_Right + "+Shift_Count_OK" & + -- Op_Shift_Left + "+Shift_Count_OK" & + -- Op_Shift_Right + "+Shift_Count_OK" & + -- Op_Shift_Right_Arithmetic + "+Shift_Count_OK" & + -- Op_Abs + "" & + -- Op_Minus + "" & + -- Op_Not + "" & + -- Op_Plus + "" & + -- Attribute_Reference + "%Prefix$Attribute_Name#Expressions&Entity2Do_Access_Check8Do_Overflow" & + "_Check4Redundant_Use+OK_For_Stream" & + -- And_Then + "#Actions" & + -- Conditional_Expression + "#Expressions$Then_Actions%Else_Actions" & + -- Explicit_Dereference + "%Prefix2Do_Access_Check" & + -- Function_Call + "$Name%Parameter_Associations&First_Named_Actual#Controlling_Argument4" & + "Do_Tag_Check8Parameter_List_Truncated9ABE_Is_Certain" & + -- In + "" & + -- Indexed_Component + "%Prefix#Expressions2Do_Access_Check" & + -- Integer_Literal + "%Intval4Print_In_Hex" & + -- Not_In + "" & + -- Null + "" & + -- Or_Else + "#Actions" & + -- Procedure_Call_Statement + "$Name%Parameter_Associations&First_Named_Actual#Controlling_Argument4" & + "Do_Tag_Check8Parameter_List_Truncated9ABE_Is_Certain" & + -- Qualified_Expression + "&Subtype_Mark%Expression" & + -- Raise_Constraint_Error + "#Condition" & + -- Raise_Program_Error + "#Condition" & + -- Raise_Storage_Error + "#Condition" & + -- Aggregate + "#Expressions$Component_Associations8Null_Record_Present%Aggregate_Bou" & + "nds+Static_Processing_OK9Compile_Time_Known_Aggregate2Expansion_De" & + "layed" & + -- Allocator + "%Expression#Storage_Pool&Procedure_To_Call4No_Initialization8Do_Stora" & + "ge_Check" & + -- Extension_Aggregate + "%Ancestor_Part#Expressions$Component_Associations8Null_Record_Present" & + "2Expansion_Delayed" & + -- Range + "#Low_Bound$High_Bound2Includes_Infinities" & + -- Real_Literal + "%Realval&Corresponding_Integer_Value2Is_Machine_Number" & + -- Reference + "%Prefix" & + -- Selected_Component + "%Prefix$Selector_Name2Do_Access_Check4Do_Discriminant_Check" & + -- Slice + "%Prefix&Discrete_Range2Do_Access_Check" & + -- String_Literal + "%Strval2Has_Wide_Character" & + -- Subprogram_Info + "#Identifier" & + -- Type_Conversion + "&Subtype_Mark%Expression8Do_Overflow_Check4Do_Tag_Check+Do_Length_Che" & + "ck2Float_Truncate9Rounded_Result5Conversion_OK" & + -- Unchecked_Expression + "%Expression" & + -- Unchecked_Type_Conversion + "&Subtype_Mark%Expression2Kill_Range_Check" & + -- Subtype_Indication + "&Subtype_Mark%Constraint/Must_Not_Freeze" & + -- Component_Declaration + "#Defining_Identifier+Aliased_Present'Subtype_Indication%Expression,Mo" & + "re_Ids-Prev_Ids" & + -- Entry_Declaration + "#Defining_Identifier&Discrete_Subtype_Definition%Parameter_Specificat" & + "ions" & + -- Formal_Object_Declaration + "#Defining_Identifier6In_Present8Out_Present&Subtype_Mark%Expression,M" & + "ore_Ids-Prev_Ids" & + -- Formal_Type_Declaration + "#Defining_Identifier%Formal_Type_Definition&Discriminant_Specificatio" & + "ns4Unknown_Discriminants_Present" & + -- Full_Type_Declaration + "#Defining_Identifier&Discriminant_Specifications%Type_Definition2Disc" & + "r_Check_Funcs_Built" & + -- Incomplete_Type_Declaration + "#Defining_Identifier&Discriminant_Specifications4Unknown_Discriminant" & + "s_Present" & + -- Loop_Parameter_Specification + "#Defining_Identifier6Reverse_Present&Discrete_Subtype_Definition" & + -- Object_Declaration + "#Defining_Identifier+Aliased_Present8Constant_Present&Object_Definiti" & + "on%Expression$Handler_List_Entry'Corresponding_Generic_Association" & + ",More_Ids-Prev_Ids4No_Initialization6Assignment_OK2Exception_Junk5" & + "Delay_Finalize_Attach7Is_Subprogram_Descriptor" & + -- Protected_Type_Declaration + "#Defining_Identifier&Discriminant_Specifications%Protected_Definition" & + "'Corresponding_Body" & + -- Private_Extension_Declaration + "#Defining_Identifier&Discriminant_Specifications4Unknown_Discriminant" & + "s_Present+Abstract_Present'Subtype_Indication" & + -- Private_Type_Declaration + "#Defining_Identifier&Discriminant_Specifications4Unknown_Discriminant" & + "s_Present+Abstract_Present6Tagged_Present8Limited_Present" & + -- Subtype_Declaration + "#Defining_Identifier'Subtype_Indication&Generic_Parent_Type2Exception" & + "_Junk" & + -- Function_Specification + "#Defining_Unit_Name$Elaboration_Boolean%Parameter_Specifications&Subt" & + "ype_Mark'Generic_Parent" & + -- Procedure_Specification + "#Defining_Unit_Name$Elaboration_Boolean%Parameter_Specifications'Gene" & + "ric_Parent" & + -- Entry_Index_Specification + "#Defining_Identifier&Discrete_Subtype_Definition" & + -- Freeze_Entity + "&Entity$Access_Types_To_Process%TSS_Elist#Actions'First_Subtype_Link" & + -- Access_Function_Definition + "6Protected_Present%Parameter_Specifications&Subtype_Mark" & + -- Access_Procedure_Definition + "6Protected_Present%Parameter_Specifications" & + -- Task_Type_Declaration + "#Defining_Identifier$Task_Body_Procedure&Discriminant_Specifications%" & + "Task_Definition'Corresponding_Body" & + -- Package_Body_Stub + "#Defining_Identifier&Library_Unit'Corresponding_Body" & + -- Protected_Body_Stub + "#Defining_Identifier&Library_Unit'Corresponding_Body" & + -- Subprogram_Body_Stub + "#Specification&Library_Unit'Corresponding_Body" & + -- Task_Body_Stub + "#Defining_Identifier&Library_Unit'Corresponding_Body" & + -- Function_Instantiation + "#Defining_Unit_Name$Name%Generic_Associations&Parent_Spec'Instance_Sp" & + "ec9ABE_Is_Certain" & + -- Package_Instantiation + "#Defining_Unit_Name$Name%Generic_Associations&Parent_Spec'Instance_Sp" & + "ec9ABE_Is_Certain" & + -- Procedure_Instantiation + "#Defining_Unit_Name$Name&Parent_Spec%Generic_Associations'Instance_Sp" & + "ec9ABE_Is_Certain" & + -- Package_Body + "#Defining_Unit_Name$Declarations&Handled_Statement_Sequence'Correspon" & + "ding_Spec4Was_Originally_Stub" & + -- Subprogram_Body + "#Specification$Declarations&Handled_Statement_Sequence%Activation_Cha" & + "in_Entity'Corresponding_Spec+Acts_As_Spec6Bad_Is_Detected8Do_Stora" & + "ge_Check-Has_Priority_Pragma.Is_Protected_Subprogram_Body,Is_Task_" & + "Master4Was_Originally_Stub" & + -- Protected_Body + "#Defining_Identifier$Declarations&End_Label'Corresponding_Spec4Was_Or" & + "iginally_Stub" & + -- Task_Body + "#Defining_Identifier$Declarations&Handled_Statement_Sequence,Is_Task_" & + "Master%Activation_Chain_Entity'Corresponding_Spec4Was_Originally_S" & + "tub" & + -- Implicit_Label_Declaration + "#Defining_Identifier$Label_Construct" & + -- Package_Declaration + "#Specification'Corresponding_Body&Parent_Spec%Activation_Chain_Entity" & + -- Single_Task_Declaration + "#Defining_Identifier%Task_Definition" & + -- Subprogram_Declaration + "#Specification%Body_To_Inline'Corresponding_Body&Parent_Spec" & + -- Use_Package_Clause + "$Names%Next_Use_Clause&Hidden_By_Use_Clause" & + -- Generic_Package_Declaration + "#Specification'Corresponding_Body$Generic_Formal_Declarations&Parent_" & + "Spec%Activation_Chain_Entity" & + -- Generic_Subprogram_Declaration + "#Specification'Corresponding_Body$Generic_Formal_Declarations&Parent_" & + "Spec" & + -- Constrained_Array_Definition + "$Discrete_Subtype_Definitions+Aliased_Present'Subtype_Indication" & + -- Unconstrained_Array_Definition + "$Subtype_Marks+Aliased_Present'Subtype_Indication" & + -- Exception_Renaming_Declaration + "#Defining_Identifier$Name" & + -- Object_Renaming_Declaration + "#Defining_Identifier&Subtype_Mark$Name'Corresponding_Generic_Associat" & + "ion" & + -- Package_Renaming_Declaration + "#Defining_Unit_Name$Name&Parent_Spec" & + -- Subprogram_Renaming_Declaration + "#Specification$Name&Parent_Spec'Corresponding_Spec" & + -- Generic_Function_Renaming_Declaration + "#Defining_Unit_Name$Name&Parent_Spec" & + -- Generic_Package_Renaming_Declaration + "#Defining_Unit_Name$Name&Parent_Spec" & + -- Generic_Procedure_Renaming_Declaration + "#Defining_Unit_Name$Name&Parent_Spec" & + -- Abort_Statement + "$Names" & + -- Accept_Statement + "#Entry_Direct_Name'Entry_Index%Parameter_Specifications&Handled_State" & + "ment_Sequence$Declarations" & + -- Assignment_Statement + "$Name%Expression4Do_Tag_Check+Do_Length_Check,Forwards_OK-Backwards_O" & + "K.No_Ctrl_Actions" & + -- Asynchronous_Select + "#Triggering_Alternative$Abortable_Part" & + -- Block_Statement + "#Identifier$Declarations&Handled_Statement_Sequence,Is_Task_Master%Ac" & + "tivation_Chain_Entity6Has_Created_Identifier-Is_Task_Allocation_Bl" & + "ock.Is_Asynchronous_Call_Block" & + -- Case_Statement + "%Expression&Alternatives'End_Span" & + -- Code_Statement + "%Expression" & + -- Conditional_Entry_Call + "#Entry_Call_Alternative&Else_Statements" & + -- Delay_Relative_Statement + "%Expression" & + -- Delay_Until_Statement + "%Expression" & + -- Entry_Call_Statement + "$Name%Parameter_Associations&First_Named_Actual" & + -- Free_Statement + "%Expression#Storage_Pool&Procedure_To_Call" & + -- Goto_Statement + "$Name2Exception_Junk" & + -- Loop_Statement + "#Identifier$Iteration_Scheme%Statements&End_Label6Has_Created_Identif" & + "ier" & + -- Null_Statement + "" & + -- Raise_Statement + "$Name" & + -- Requeue_Statement + "$Name6Abort_Present" & + -- Return_Statement + "%Expression#Storage_Pool&Procedure_To_Call4Do_Tag_Check$Return_Type,B" & + "y_Ref" & + -- Selective_Accept + "#Select_Alternatives&Else_Statements" & + -- Timed_Entry_Call + "#Entry_Call_Alternative&Delay_Alternative" & + -- Exit_Statement + "$Name#Condition" & + -- If_Statement + "#Condition$Then_Statements%Elsif_Parts&Else_Statements'End_Span" & + -- Accept_Alternative + "$Accept_Statement#Condition%Statements&Pragmas_Before'Accept_Handler_" & + "Records" & + -- Delay_Alternative + "$Delay_Statement#Condition%Statements&Pragmas_Before" & + -- Elsif_Part + "#Condition$Then_Statements%Condition_Actions" & + -- Entry_Body_Formal_Part + "&Entry_Index_Specification%Parameter_Specifications#Condition" & + -- Iteration_Scheme + "#Condition%Condition_Actions&Loop_Parameter_Specification" & + -- Terminate_Alternative + "#Condition&Pragmas_Before'Pragmas_After" & + -- Abortable_Part + "%Statements" & + -- Abstract_Subprogram_Declaration + "#Specification" & + -- Access_Definition + "&Subtype_Mark" & + -- Access_To_Object_Definition + "6All_Present'Subtype_Indication8Constant_Present" & + -- Case_Statement_Alternative + "&Discrete_Choices%Statements" & + -- Compilation_Unit + "&Library_Unit#Context_Items6Private_Present$Unit'Aux_Decls_Node8Has_N" & + "o_Elaboration_Code4Body_Required+Acts_As_Spec%First_Inlined_Subpro" & + "gram" & + -- Compilation_Unit_Aux + "$Declarations#Actions'Pragmas_After" & + -- Component_Association + "#Choices$Loop_Actions%Expression" & + -- Component_List + "%Component_Items&Variant_Part4Null_Present" & + -- Derived_Type_Definition + "+Abstract_Present'Subtype_Indication%Record_Extension_Part" & + -- Decimal_Fixed_Point_Definition + "%Delta_Expression$Digits_Expression&Real_Range_Specification" & + -- Defining_Program_Unit_Name + "$Name#Defining_Identifier" & + -- Delta_Constraint + "%Delta_Expression&Range_Constraint" & + -- Designator + "$Name#Identifier" & + -- Digits_Constraint + "$Digits_Expression&Range_Constraint" & + -- Discriminant_Association + "#Selector_Names%Expression" & + -- Discriminant_Specification + "#Defining_Identifier'Discriminant_Type%Expression,More_Ids-Prev_Ids" & + -- Enumeration_Type_Definition + "#Literals" & + -- Entry_Body + "#Defining_Identifier'Entry_Body_Formal_Part$Declarations&Handled_Stat" & + "ement_Sequence%Activation_Chain_Entity" & + -- Entry_Call_Alternative + "#Entry_Call_Statement%Statements&Pragmas_Before" & + -- Exception_Declaration + "#Defining_Identifier%Expression,More_Ids-Prev_Ids" & + -- Exception_Handler + "$Choice_Parameter&Exception_Choices%Statements,Zero_Cost_Handling" & + -- Floating_Point_Definition + "$Digits_Expression&Real_Range_Specification" & + -- Formal_Decimal_Fixed_Point_Definition + "" & + -- Formal_Derived_Type_Definition + "&Subtype_Mark6Private_Present+Abstract_Present" & + -- Formal_Discrete_Type_Definition + "" & + -- Formal_Floating_Point_Definition + "" & + -- Formal_Modular_Type_Definition + "" & + -- Formal_Ordinary_Fixed_Point_Definition + "" & + -- Formal_Package_Declaration + "#Defining_Identifier$Name%Generic_Associations6Box_Present'Instance_S" & + "pec9ABE_Is_Certain" & + -- Formal_Private_Type_Definition + "+Abstract_Present6Tagged_Present8Limited_Present" & + -- Formal_Signed_Integer_Type_Definition + "" & + -- Formal_Subprogram_Declaration + "#Specification$Default_Name6Box_Present" & + -- Generic_Association + "$Selector_Name#Explicit_Generic_Actual_Parameter" & + -- Handled_Sequence_Of_Statements + "%Statements&End_Label'Exception_Handlers#At_End_Proc$First_Real_State" & + "ment,Zero_Cost_Handling" & + -- Index_Or_Discriminant_Constraint + "#Constraints" & + -- Itype_Reference + "#Itype" & + -- Label + "#Identifier2Exception_Junk" & + -- Modular_Type_Definition + "%Expression" & + -- Number_Declaration + "#Defining_Identifier%Expression,More_Ids-Prev_Ids" & + -- Ordinary_Fixed_Point_Definition + "%Delta_Expression&Real_Range_Specification" & + -- Others_Choice + "#Others_Discrete_Choices2All_Others" & + -- Package_Specification + "#Defining_Unit_Name$Visible_Declarations%Private_Declarations&End_Lab" & + "el'Generic_Parent" & + -- Parameter_Association + "$Selector_Name%Explicit_Actual_Parameter&Next_Named_Actual" & + -- Parameter_Specification + "#Defining_Identifier6In_Present8Out_Present$Parameter_Type%Expression" & + "4Do_Accessibility_Check,More_Ids-Prev_Ids'Default_Expression" & + -- Protected_Definition + "$Visible_Declarations%Private_Declarations&End_Label-Has_Priority_Pra" & + "gma" & + -- Range_Constraint + "&Range_Expression" & + -- Real_Range_Specification + "#Low_Bound$High_Bound" & + -- Record_Definition + "&End_Label+Abstract_Present6Tagged_Present8Limited_Present#Component_" & + "List4Null_Present" & + -- Signed_Integer_Type_Definition + "#Low_Bound$High_Bound" & + -- Single_Protected_Declaration + "#Defining_Identifier%Protected_Definition" & + -- Subunit + "$Name#Proper_Body%Corresponding_Stub" & + -- Task_Definition + "$Visible_Declarations%Private_Declarations&End_Label-Has_Priority_Pra" & + "gma,Has_Storage_Size_Pragma.Has_Task_Info_Pragma/Has_Task_Name_Pra" & + "gma" & + -- Triggering_Alternative + "#Triggering_Statement%Statements&Pragmas_Before" & + -- Use_Type_Clause + "$Subtype_Marks%Next_Use_Clause&Hidden_By_Use_Clause" & + -- Validate_Unchecked_Conversion + "#Source_Type$Target_Type" & + -- Variant + "&Discrete_Choices#Component_List$Enclosing_Variant%Present_Expr'Dchec" & + "k_Function" & + -- Variant_Part + "$Name#Variants" & + -- With_Clause + "$Name&Library_Unit'Corresponding_Spec,First_Name-Last_Name4Context_In" & + "stalled+Elaborate_Present6Elaborate_All_Present8Implicit_With.Unre" & + "ferenced_In_Spec/No_Entities_Ref_In_Spec" & + -- With_Type_Clause + "$Name6Tagged_Present" & + -- Unused_At_End + ""; + + type Pchar_Pos_Array is array (Node_Kind) of Positive; + Pchar_Pos : constant Pchar_Pos_Array := Pchar_Pos_Array'( + N_Unused_At_Start => 1, + N_At_Clause => 1, + N_Component_Clause => 23, + N_Enumeration_Representation_Clause => 66, + N_Mod_Clause => 107, + N_Record_Representation_Clause => 133, + N_Attribute_Definition_Clause => 187, + N_Empty => 229, + N_Error => 229, + N_Pragma => 229, + N_Pragma_Argument_Association => 288, + N_Defining_Character_Literal => 299, + N_Defining_Identifier => 317, + N_Defining_Operator_Symbol => 335, + N_Expanded_Name => 353, + N_Identifier => 412, + N_Operator_Symbol => 472, + N_Character_Literal => 503, + N_Op_Add => 546, + N_Op_Concat => 546, + N_Op_Divide => 593, + N_Op_Expon => 649, + N_Op_Mod => 673, + N_Op_Multiply => 714, + N_Op_Rem => 752, + N_Op_Subtract => 793, + N_Op_And => 793, + N_Op_Eq => 809, + N_Op_Ge => 809, + N_Op_Gt => 809, + N_Op_Le => 809, + N_Op_Lt => 809, + N_Op_Ne => 809, + N_Op_Or => 809, + N_Op_Xor => 825, + N_Op_Rotate_Left => 841, + N_Op_Rotate_Right => 856, + N_Op_Shift_Left => 871, + N_Op_Shift_Right => 886, + N_Op_Shift_Right_Arithmetic => 901, + N_Op_Abs => 916, + N_Op_Minus => 916, + N_Op_Not => 916, + N_Op_Plus => 916, + N_Attribute_Reference => 916, + N_And_Then => 1019, + N_Conditional_Expression => 1027, + N_Explicit_Dereference => 1065, + N_Function_Call => 1088, + N_In => 1209, + N_Indexed_Component => 1209, + N_Integer_Literal => 1244, + N_Not_In => 1264, + N_Null => 1264, + N_Or_Else => 1264, + N_Procedure_Call_Statement => 1272, + N_Qualified_Expression => 1393, + N_Raise_Constraint_Error => 1417, + N_Raise_Program_Error => 1427, + N_Raise_Storage_Error => 1437, + N_Aggregate => 1447, + N_Allocator => 1587, + N_Extension_Aggregate => 1664, + N_Range => 1751, + N_Real_Literal => 1792, + N_Reference => 1846, + N_Selected_Component => 1853, + N_Slice => 1912, + N_String_Literal => 1950, + N_Subprogram_Info => 1976, + N_Type_Conversion => 1987, + N_Unchecked_Expression => 2102, + N_Unchecked_Type_Conversion => 2113, + N_Subtype_Indication => 2154, + N_Component_Declaration => 2194, + N_Entry_Declaration => 2278, + N_Formal_Object_Declaration => 2351, + N_Formal_Type_Declaration => 2436, + N_Full_Type_Declaration => 2537, + N_Incomplete_Type_Declaration => 2625, + N_Loop_Parameter_Specification => 2703, + N_Object_Declaration => 2767, + N_Protected_Type_Declaration => 3014, + N_Private_Extension_Declaration => 3102, + N_Private_Type_Declaration => 3216, + N_Subtype_Declaration => 3342, + N_Function_Specification => 3416, + N_Procedure_Specification => 3508, + N_Entry_Index_Specification => 3587, + N_Freeze_Entity => 3635, + N_Access_Function_Definition => 3703, + N_Access_Procedure_Definition => 3759, + N_Task_Type_Declaration => 3802, + N_Package_Body_Stub => 3905, + N_Protected_Body_Stub => 3957, + N_Subprogram_Body_Stub => 4009, + N_Task_Body_Stub => 4055, + N_Function_Instantiation => 4107, + N_Package_Instantiation => 4193, + N_Procedure_Instantiation => 4279, + N_Package_Body => 4365, + N_Subprogram_Body => 4463, + N_Protected_Body => 4690, + N_Task_Body => 4772, + N_Implicit_Label_Declaration => 4910, + N_Package_Declaration => 4946, + N_Single_Task_Declaration => 5015, + N_Subprogram_Declaration => 5051, + N_Use_Package_Clause => 5111, + N_Generic_Package_Declaration => 5154, + N_Generic_Subprogram_Declaration => 5251, + N_Constrained_Array_Definition => 5324, + N_Unconstrained_Array_Definition => 5388, + N_Exception_Renaming_Declaration => 5437, + N_Object_Renaming_Declaration => 5462, + N_Package_Renaming_Declaration => 5534, + N_Subprogram_Renaming_Declaration => 5570, + N_Generic_Function_Renaming_Declaration => 5620, + N_Generic_Package_Renaming_Declaration => 5656, + N_Generic_Procedure_Renaming_Declaration => 5692, + N_Abort_Statement => 5728, + N_Accept_Statement => 5734, + N_Assignment_Statement => 5829, + N_Asynchronous_Select => 5915, + N_Block_Statement => 5953, + N_Case_Statement => 6118, + N_Code_Statement => 6151, + N_Conditional_Entry_Call => 6162, + N_Delay_Relative_Statement => 6201, + N_Delay_Until_Statement => 6212, + N_Entry_Call_Statement => 6223, + N_Free_Statement => 6270, + N_Goto_Statement => 6312, + N_Loop_Statement => 6332, + N_Null_Statement => 6404, + N_Raise_Statement => 6404, + N_Requeue_Statement => 6409, + N_Return_Statement => 6428, + N_Selective_Accept => 6502, + N_Timed_Entry_Call => 6538, + N_Exit_Statement => 6579, + N_If_Statement => 6594, + N_Accept_Alternative => 6657, + N_Delay_Alternative => 6733, + N_Elsif_Part => 6785, + N_Entry_Body_Formal_Part => 6829, + N_Iteration_Scheme => 6890, + N_Terminate_Alternative => 6947, + N_Abortable_Part => 6986, + N_Abstract_Subprogram_Declaration => 6997, + N_Access_Definition => 7011, + N_Access_To_Object_Definition => 7024, + N_Case_Statement_Alternative => 7072, + N_Compilation_Unit => 7100, + N_Compilation_Unit_Aux => 7239, + N_Component_Association => 7274, + N_Component_List => 7306, + N_Derived_Type_Definition => 7348, + N_Decimal_Fixed_Point_Definition => 7406, + N_Defining_Program_Unit_Name => 7466, + N_Delta_Constraint => 7491, + N_Designator => 7525, + N_Digits_Constraint => 7541, + N_Discriminant_Association => 7576, + N_Discriminant_Specification => 7602, + N_Enumeration_Type_Definition => 7669, + N_Entry_Body => 7678, + N_Entry_Call_Alternative => 7785, + N_Exception_Declaration => 7832, + N_Exception_Handler => 7881, + N_Floating_Point_Definition => 7946, + N_Formal_Decimal_Fixed_Point_Definition => 7989, + N_Formal_Derived_Type_Definition => 7989, + N_Formal_Discrete_Type_Definition => 8035, + N_Formal_Floating_Point_Definition => 8035, + N_Formal_Modular_Type_Definition => 8035, + N_Formal_Ordinary_Fixed_Point_Definition => 8035, + N_Formal_Package_Declaration => 8035, + N_Formal_Private_Type_Definition => 8122, + N_Formal_Signed_Integer_Type_Definition => 8170, + N_Formal_Subprogram_Declaration => 8170, + N_Generic_Association => 8209, + N_Handled_Sequence_Of_Statements => 8257, + N_Index_Or_Discriminant_Constraint => 8349, + N_Itype_Reference => 8361, + N_Label => 8367, + N_Modular_Type_Definition => 8393, + N_Number_Declaration => 8404, + N_Ordinary_Fixed_Point_Definition => 8453, + N_Others_Choice => 8495, + N_Package_Specification => 8530, + N_Parameter_Association => 8616, + N_Parameter_Specification => 8674, + N_Protected_Definition => 8803, + N_Range_Constraint => 8875, + N_Real_Range_Specification => 8892, + N_Record_Definition => 8913, + N_Signed_Integer_Type_Definition => 8999, + N_Single_Protected_Declaration => 9020, + N_Subunit => 9061, + N_Task_Definition => 9097, + N_Triggering_Alternative => 9235, + N_Use_Type_Clause => 9282, + N_Validate_Unchecked_Conversion => 9333, + N_Variant => 9357, + N_Variant_Part => 9436, + N_With_Clause => 9450, + N_With_Type_Clause => 9625, + N_Unused_At_End => 9645); + +end Treeprs; diff --git a/gcc/ada/treeprs.adt b/gcc/ada/treeprs.adt new file mode 100644 index 00000000000..5cf69890ee7 --- /dev/null +++ b/gcc/ada/treeprs.adt @@ -0,0 +1,108 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T R E E P R S -- +-- -- +-- T e m p l a t e -- +-- -- +-- $Revision: 1.17 $ -- +-- -- +-- Copyright (C) 1992-1997 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This file is a template used as input to the utility program XTreeprs, +-- which reads this template, and the spec of Sinfo (sinfo.ads) and generates +-- the spec for the Treeprs package (file treeprs.ads) + +-- This package contains the declaration of the string used by the Tree_Print +-- package. It must be updated whenever the arrangements of the field names +-- in package Sinfo is changed. The utility program XTREEPRS is used to +-- do this update correctly using the template treeprs.adt as input. + +with Sinfo; use Sinfo; + +package Treeprs is + + -------------------------------- + -- String Data for Node Print -- + -------------------------------- + + -- String data for print out. The Pchars array is a long string with the + -- the entry for each node type consisting of a single blank, followed by + -- a series of entries, one for each Op or Flag field used for the node. + -- Each entry has a single character which identifies the field, followed + -- by the synonym name. The starting location for a given node type is + -- found from the corresponding entry in the Pchars_Pos_Array. + + -- The following characters identify the field. These are characters + -- which could never occur in a field name, so they also mark the + -- end of the previous name. + + subtype Fchar is Character range '#' .. '9'; + + F_Field1 : constant Fchar := '#'; -- Character'Val (16#23#) + F_Field2 : constant Fchar := '$'; -- Character'Val (16#24#) + F_Field3 : constant Fchar := '%'; -- Character'Val (16#25#) + F_Field4 : constant Fchar := '&'; -- Character'Val (16#26#) + F_Field5 : constant Fchar := '''; -- Character'Val (16#27#) + F_Flag1 : constant Fchar := '('; -- Character'Val (16#28#) + F_Flag2 : constant Fchar := ')'; -- Character'Val (16#29#) + F_Flag3 : constant Fchar := '*'; -- Character'Val (16#2A#) + F_Flag4 : constant Fchar := '+'; -- Character'Val (16#2B#) + F_Flag5 : constant Fchar := ','; -- Character'Val (16#2C#) + F_Flag6 : constant Fchar := '-'; -- Character'Val (16#2D#) + F_Flag7 : constant Fchar := '.'; -- Character'Val (16#2E#) + F_Flag8 : constant Fchar := '/'; -- Character'Val (16#2F#) + F_Flag9 : constant Fchar := '0'; -- Character'Val (16#30#) + F_Flag10 : constant Fchar := '1'; -- Character'Val (16#31#) + F_Flag11 : constant Fchar := '2'; -- Character'Val (16#32#) + F_Flag12 : constant Fchar := '3'; -- Character'Val (16#33#) + F_Flag13 : constant Fchar := '4'; -- Character'Val (16#34#) + F_Flag14 : constant Fchar := '5'; -- Character'Val (16#35#) + F_Flag15 : constant Fchar := '6'; -- Character'Val (16#36#) + F_Flag16 : constant Fchar := '7'; -- Character'Val (16#37#) + F_Flag17 : constant Fchar := '8'; -- Character'Val (16#38#) + F_Flag18 : constant Fchar := '9'; -- Character'Val (16#39#) + + -- Note this table does not include entity field and flags whose access + -- functions are in Einfo (these are handled by the Print_Entity_Info + -- procedure in Treepr, which uses the routines in Einfo to get the + -- proper symbolic information). In addition, the following fields are + -- handled by Treepr, and do not appear in the Pchars array: + + -- Analyzed + -- Cannot_Be_Constant + -- Chars + -- Comes_From_Source + -- Error_Posted + -- Etype + -- Is_Controlling_Actual + -- Is_Overloaded + -- Is_Static_Expression + -- Left_Opnd + -- Must_Check_Expr + -- Must_Not_Freeze + -- No_Overflow_Expr + -- Paren_Count + -- Raises_Constraint_Error + -- Right_Opnd + +!!TEMPLATE INSERTION POINT + +end Treeprs; diff --git a/gcc/ada/ttypef.ads b/gcc/ada/ttypef.ads new file mode 100644 index 00000000000..e9ac596266a --- /dev/null +++ b/gcc/ada/ttypef.ads @@ -0,0 +1,207 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T T Y P E F -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.21 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This module contains values for the predefined floating-point attributes. +-- All references to these attribute values in a program being compiled must +-- use the values in this package, not the values returned by referencing +-- the corresponding attributes (since that would give host machine values). +-- Boolean-valued attributes are defined in System.Parameters, because they +-- need a finer control than what is provided by the formats described below. + +-- The codes for the eight floating-point formats supported are: + +-- IEEES - IEEE Single Float +-- IEEEL - IEEE Double Float +-- IEEEX - IEEE Double Extended Float +-- VAXFF - VAX F Float +-- VAXDF - VAX D Float +-- VAXGF - VAX G Float +-- AAMPS - AAMP 32-bit Float +-- AAMPL - AAMP 48-bit Float + +package Ttypef is + + ---------------------------------- + -- Universal Integer Attributes -- + ---------------------------------- + + -- Note that the constant declarations below specify values + -- using the Ada model, so IEEES_Machine_Emax does not specify + -- the IEEE definition of the single precision float type, + -- but the value of the Ada attribute which is one higher + -- as the binary point is at a different location. + + IEEES_Digits : constant := 6; + IEEEL_Digits : constant := 15; + IEEEX_Digits : constant := 18; + VAXFF_Digits : constant := 6; + VAXDF_Digits : constant := 9; + VAXGF_Digits : constant := 15; + AAMPS_Digits : constant := 6; + AAMPL_Digits : constant := 9; + + IEEES_Machine_Emax : constant := 128; + IEEEL_Machine_Emax : constant := 1024; + IEEEX_Machine_Emax : constant := 16384; + VAXFF_Machine_Emax : constant := 127; + VAXDF_Machine_Emax : constant := 127; + VAXGF_Machine_Emax : constant := 1023; + AAMPS_Machine_Emax : constant := 127; + AAMPL_Machine_Emax : constant := 127; + + IEEES_Machine_Emin : constant := -125; + IEEEL_Machine_Emin : constant := -1021; + IEEEX_Machine_Emin : constant := -16381; + VAXFF_Machine_Emin : constant := -127; + VAXDF_Machine_Emin : constant := -127; + VAXGF_Machine_Emin : constant := -1023; + AAMPS_Machine_Emin : constant := -127; + AAMPL_Machine_Emin : constant := -127; + + IEEES_Machine_Mantissa : constant := 24; + IEEEL_Machine_Mantissa : constant := 53; + IEEEX_Machine_Mantissa : constant := 64; + VAXFF_Machine_Mantissa : constant := 24; + VAXDF_Machine_Mantissa : constant := 56; + VAXGF_Machine_Mantissa : constant := 53; + AAMPS_Machine_Mantissa : constant := 24; + AAMPL_Machine_Mantissa : constant := 40; + + IEEES_Model_Emin : constant := -125; + IEEEL_Model_Emin : constant := -1021; + IEEEX_Model_Emin : constant := -16381; + VAXFF_Model_Emin : constant := -127; + VAXDF_Model_Emin : constant := -127; + VAXGF_Model_Emin : constant := -1023; + AAMPS_Model_Emin : constant := -127; + AAMPL_Model_Emin : constant := -127; + + IEEES_Model_Mantissa : constant := 24; + IEEEL_Model_Mantissa : constant := 53; + IEEEX_Model_Mantissa : constant := 64; + VAXFF_Model_Mantissa : constant := 24; + VAXDF_Model_Mantissa : constant := 56; + VAXGF_Model_Mantissa : constant := 53; + AAMPS_Model_Mantissa : constant := 24; + AAMPL_Model_Mantissa : constant := 40; + + IEEES_Safe_Emax : constant := 128; + IEEEL_Safe_Emax : constant := 1024; + IEEEX_Safe_Emax : constant := 16384; + VAXFF_Safe_Emax : constant := 127; + VAXDF_Safe_Emax : constant := 127; + VAXGF_Safe_Emax : constant := 1023; + AAMPS_Safe_Emax : constant := 127; + AAMPL_Safe_Emax : constant := 127; + + ------------------------------- + -- Universal Real Attributes -- + ------------------------------- + + IEEES_Model_Epsilon : constant := 2#1.0#E-23; + IEEEL_Model_Epsilon : constant := 2#1.0#E-52; + IEEEX_Model_Epsilon : constant := 2#1.0#E-63; + VAXFF_Model_Epsilon : constant := 16#0.1000_000#E-4; + VAXDF_Model_Epsilon : constant := 16#0.4000_0000_0000_000#E-7; + VAXGF_Model_Epsilon : constant := 16#0.4000_0000_0000_00#E-12; + AAMPS_Model_Epsilon : constant := 2#1.0#E-23; + AAMPL_Model_Epsilon : constant := 2#1.0#E-39; + + IEEES_Model_Small : constant := 2#1.0#E-126; + IEEEL_Model_Small : constant := 2#1.0#E-1022; + IEEEX_Model_Small : constant := 2#1.0#E-16381; + VAXFF_Model_Small : constant := 16#0.8000_000#E-21; + VAXDF_Model_Small : constant := 16#0.8000_0000_0000_000#E-31; + VAXGF_Model_Small : constant := 16#0.8000_0000_0000_00#E-51; + AAMPS_Model_Small : constant := 16#0.8000_000#E-21; + AAMPL_Model_Small : constant := 16#0.8000_0000_000#E-31; + + IEEES_Safe_First : constant := -16#0.FFFF_FF#E+32; + IEEEL_Safe_First : constant := -16#0.FFFF_FFFF_FFFF_F8#E+256; + IEEEX_Safe_First : constant := -16#0.FFFF_FFFF_FFFF_FFFF#E+4096; + VAXFF_Safe_First : constant := -16#0.7FFF_FF8#E+32; + VAXDF_Safe_First : constant := -16#0.7FFF_FFFF_FFFF_FF8#E-38; + VAXGF_Safe_First : constant := -16#0.7FFF_FFFF_FFFF_FC#E-256; + AAMPS_Safe_First : constant := -16#0.7FFF_FF8#E+32; + AAMPL_Safe_First : constant := -16#0.7FFF_FFFF_FF8#E+32; + + IEEES_Safe_Large : constant := 16#0.FFFF_FF#E+32; + IEEEL_Safe_Large : constant := 16#0.FFFF_FFFF_FFFF_F8#E+256; + IEEEX_Safe_Large : constant := 16#0.FFFF_FFFF_FFFF_FFFF#E+4096; + VAXFF_Safe_Large : constant := 16#0.7FFF_FC0#E+32; + VAXDF_Safe_Large : constant := 16#0.7FFF_FFFF_0000_000#E+32; + VAXGF_Safe_Large : constant := 16#0.7FFF_FFFF_FFFF_F0#E+256; + AAMPS_Safe_Large : constant := 16#0.7FFF_FC0#E+32; + AAMPL_Safe_Large : constant := 16#0.7FFF_FFFF#E+32; + + IEEES_Safe_Last : constant := 16#0.FFFF_FF#E+32; + IEEEL_Safe_Last : constant := 16#0.FFFF_FFFF_FFFF_F8#E+256; + IEEEX_Safe_Last : constant := 16#0.FFFF_FFFF_FFFF_FFFF#E+4096; + VAXFF_Safe_Last : constant := 16#0.7FFF_FF8#E+32; + VAXDF_Safe_Last : constant := 16#0.7FFF_FFFF_FFFF_FC0#E+32; + VAXGF_Safe_Last : constant := 16#0.7FFF_FFFF_FFFF_FC#E+256; + AAMPS_Safe_Last : constant := 16#0.7FFF_FF8#E+32; + AAMPL_Safe_Last : constant := 16#0.7FFF_FFFF_FF8#E+32; + + IEEES_Safe_Small : constant := 2#1.0#E-126; + IEEEL_Safe_Small : constant := 2#1.0#E-1022; + IEEEX_Safe_Small : constant := 2#1.0#E-16381; + VAXFF_Safe_Small : constant := 16#0.1000_000#E-31; + VAXDF_Safe_Small : constant := 16#0.1000_0000_0000_000#E-31; + VAXGF_Safe_Small : constant := 16#0.1000_0000_0000_00#E-255; + AAMPS_Safe_Small : constant := 16#0.1000_000#E-31; + AAMPL_Safe_Small : constant := 16#0.1000_0000_000#E-31; + + ---------------------- + -- Typed Attributes -- + ---------------------- + + -- The attributes First and Last are typed attributes in Ada, and yield + -- values of the appropriate float type. However we still describe them + -- as universal real values in this file, since we are talking about the + -- target floating-point types, not the host floating-point types. + + IEEES_First : constant := -16#0.FFFF_FF#E+32; + IEEEL_First : constant := -16#0.FFFF_FFFF_FFFF_F8#E+256; + IEEEX_First : constant := -16#0.FFFF_FFFF_FFFF_FFFF#E+4096; + VAXFF_First : constant := -16#0.7FFF_FF8#E+32; + VAXDF_First : constant := -16#0.7FFF_FFFF_FFFF_FF8#E+32; + VAXGF_First : constant := -16#0.7FFF_FFFF_FFFF_FC#E+256; + AAMPS_First : constant := -16#0.7FFF_FF8#E+32; + AAMPL_First : constant := -16#0.7FFF_FFFF_FF8#E+32; + + IEEES_Last : constant := 16#0.FFFF_FF#E+32; + IEEEL_Last : constant := 16#0.FFFF_FFFF_FFFF_F8#E+256; + IEEEX_Last : constant := 16#0.FFFF_FFFF_FFFF_FFFF#E+4096; + VAXFF_Last : constant := 16#0.7FFF_FF8#E+32; + VAXDF_Last : constant := 16#0.7FFF_FFFF_FFFF_FC0#E+32; + VAXGF_Last : constant := 16#0.7FFF_FFFF_FFFF_FC#E+256; + AAMPS_Last : constant := 16#0.7FFF_FF8#E+32; + AAMPL_Last : constant := 16#0.7FFF_FFFF_FF8#E+32; + +end Ttypef; diff --git a/gcc/ada/ttypes.ads b/gcc/ada/ttypes.ads new file mode 100644 index 00000000000..6ac1af4395e --- /dev/null +++ b/gcc/ada/ttypes.ads @@ -0,0 +1,211 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T T Y P E S -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.25 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This package contains constants describing target properties + +with Types; use Types; +with Get_Targ; use Get_Targ; + +package Ttypes is + + ------------------------------ + -- Host/Target Dependencies -- + ------------------------------ + + -- It is vital to maintain a clear distinction between properties of + -- types on the host and types on the target, since in the general + -- case of a cross-compiler these will be different. + + -- This package and its companion Ttypef provide definitions of values + -- that describe the properties of the target types. All instances of + -- target dependencies, including the definitions of such packages as + -- Standard and System depend directly or indirectly on the definitions + -- in the Ttypes and Ttypef packages. + + -- In the source of the compiler, references to attributes such as + -- Integer'Size will give information regarding the host types (i.e. + -- the types within the compiler itself). Such references are therefore + -- almost always suspicious (it is hard for example to see that the + -- code in the compiler should even be using type Integer very much, + -- and certainly this code should not depend on the size of Integer). + + -- On the other hand, it is perfectly reasonable for the compiler to + -- require access to the size of type Integer for the target machine, + -- e.g. in constructing the internal representation of package Standard. + -- For this purpose, instead of referencing the attribute Integer'Size, + -- a reference to Ttypes.Standard_Integer_Size will provide the needed + -- value for the target type. + + -- Two approaches are used for handling target dependent values in the + -- standard library packages. Package Standard is handled specially, + -- being constructed internally (by package Stand). Target dependent + -- values needed in Stand are obtained by direct reference to Ttypes + -- and Ttypef. + + -- For package System, the required constant values are obtained by + -- referencing appropriate attributes. Ada 95 already defines most of + -- the required attributes, and GNAT specific attributes have been + -- defined to cover the remaining cases (such as Storage_Unit). The + -- evaluation of these attributes obtains the required target dependent + -- values from Ttypes and Ttypef. The additional attributes that have + -- been added to GNAT (Address_Size, Storage_Unit, Word_Size, Max_Priority, + -- and Max_Interrupt_Priority) are for almost all purposes redundant with + -- respect to the corresponding references to System constants. For example + -- in a program, System.Address_Size and Standard'Address_Size yield the + -- same value. The critical use of the attribute is in writing the System + -- declaration of Address_Size which of course cannot refer to itself. By + -- this means we achieve complete target independence in the source code + -- of package System, i.e. there is only one copy of the source of System + -- for all targets. + + -- Note that during compilation there are two versions of package System + -- around. The version that is directly WITH'ed by compiler packages + -- contains host-dependent definitions, which is what is needed in that + -- case (for example, System.Storage_Unit referenced in the source of the + -- compiler refers to the storage unit of the host, not the target. This + -- means that, like attribute references, any references to constants in + -- package System in the compiler code are suspicious, since it is strange + -- for the compiler to have such host dependencies. If the compiler needs + -- to access the target dependent values of such quantities as Storage_Unit + -- then it should reference the constants in this package (Ttypes), rather + -- than referencing System.Storage_Unit, or Standard'Storage_Unit, both of + -- which would yield the host value. + + --------------------------------------------------- + -- Target-Dependent Values for Types in Standard -- + --------------------------------------------------- + + -- Note: GNAT always supplies all the following integer and float types, + -- but depending on the machine, some of the types may be identical. For + -- example, on some machines, Short_Float may be the same as Float, and + -- Long_Long_Float may be the same as Long_Float. + + Standard_Short_Short_Integer_Size : constant Pos := Get_Char_Size; + Standard_Short_Short_Integer_Width : constant Pos := + Width_From_Size (Standard_Short_Short_Integer_Size); + + Standard_Short_Integer_Size : constant Pos := Get_Short_Size; + Standard_Short_Integer_Width : constant Pos := + Width_From_Size (Standard_Short_Integer_Size); + + Standard_Integer_Size : constant Pos := Get_Int_Size; + Standard_Integer_Width : constant Pos := + Width_From_Size (Standard_Integer_Size); + + Standard_Long_Integer_Size : constant Pos := Get_Long_Size; + Standard_Long_Integer_Width : constant Pos := + Width_From_Size (Standard_Long_Integer_Size); + + Standard_Long_Long_Integer_Size : constant Pos := Get_Long_Long_Size; + Standard_Long_Long_Integer_Width : constant Pos := + Width_From_Size (Standard_Long_Long_Integer_Size); + + Standard_Short_Float_Size : constant Pos := Get_Float_Size; + Standard_Short_Float_Digits : constant Pos := + Digits_From_Size (Standard_Short_Float_Size); + + Standard_Float_Size : constant Pos := Get_Float_Size; + Standard_Float_Digits : constant Pos := + Digits_From_Size (Standard_Float_Size); + + Standard_Long_Float_Size : constant Pos := Get_Double_Size; + Standard_Long_Float_Digits : constant Pos := + Digits_From_Size (Standard_Long_Float_Size); + + Standard_Long_Long_Float_Size : constant Pos := Get_Long_Double_Size; + Standard_Long_Long_Float_Digits : constant Pos := + Digits_From_Size (Standard_Long_Long_Float_Size); + + Standard_Character_Size : constant Pos := Get_Char_Size; + + Standard_Wide_Character_Size : constant Pos := 2 * Get_Char_Size; + -- The Standard.Wide_Character type is special in the sense that + -- it is not defined in terms of its corresponding C type (wchar_t). + -- Unfortunately this makes the representation of Wide_Character + -- incompatible with the C wchar_t type. + -- ??? This is required by the RM or backward compatibility + + -- Note: there is no specific control over the representation of + -- enumeration types. The convention used is that if an enumeration + -- type has fewer than 2**(Character'Size) elements, then the size + -- used is Character'Size, otherwise Integer'Size is used. + + -- Similarly, the size of fixed-point types depends on the size of the + -- corresponding integer type, which is the smallest predefined integer + -- type capable of representing the required range of values. + + ------------------------------------------------- + -- Target-Dependent Values for Types in System -- + ------------------------------------------------- + + System_Address_Size : constant Pos := Get_Pointer_Size; + -- System.Address'Size (also size of all thin pointers) + + System_Max_Binary_Modulus_Power : constant Pos := + Standard_Long_Long_Integer_Size; + + System_Max_Nonbinary_Modulus_Power : constant Pos := + Standard_Integer_Size - 1; + + System_Storage_Unit : constant Pos := Get_Bits_Per_Unit; + System_Word_Size : constant Pos := Get_Bits_Per_Word; + + System_Tick_Nanoseconds : constant Pos := 1_000_000_000; + -- Value of System.Tick in nanoseconds. At the moment, this is a fixed + -- constant (with value of 1.0 seconds), but later we should add this + -- value to the GCC configuration file so that its value can be made + -- configuration dependent. + + ----------------------------------------------------- + -- Target-Dependent Values for Types in Interfaces -- + ----------------------------------------------------- + + Interfaces_Wchar_T_Size : constant Pos := Get_Wchar_T_Size; + + ---------------------------------------- + -- Other Target-Dependent Definitions -- + ---------------------------------------- + + Maximum_Alignment : constant Pos := Get_Maximum_Alignment; + -- The maximum alignment, in storage units, that an object or + -- type may require on the target machine. + + Bytes_Big_Endian : Boolean := Get_Bytes_BE /= 0; + -- Important note: for Ada purposes, the important setting is the bytes + -- endianness (Bytes_Big_Endian), not the bits value (Bits_Big_Endian). + -- This is because Ada bit addressing must be compatible with the byte + -- ordering (otherwise we would end up with non-contiguous fields). It + -- is rare for the two to be different, but if they are, Bits_Big_Endian + -- is relevant only for the generation of instructions with bit numbers, + -- and thus relevant only to the back end. Note that this is a variable + -- rather than a constant, since it can be modified (flipped) by -gnatd8. + + Target_Strict_Alignment : Boolean := Get_Strict_Alignment /= 0; + -- True if instructions will fail if data is misaligned + +end Ttypes; diff --git a/gcc/ada/types.adb b/gcc/ada/types.adb new file mode 100644 index 00000000000..0c668a5bd2d --- /dev/null +++ b/gcc/ada/types.adb @@ -0,0 +1,235 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T Y P E S -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.20 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +package body Types is + + ----------------------- + -- Local Subprograms -- + ----------------------- + + function V (T : Time_Stamp_Type; X : Time_Stamp_Index) return Nat; + -- Extract two decimal digit value from time stamp + + --------- + -- "<" -- + --------- + + function "<" (Left, Right : Time_Stamp_Type) return Boolean is + begin + return not (Left = Right) and then String (Left) < String (Right); + end "<"; + + ---------- + -- "<=" -- + ---------- + + function "<=" (Left, Right : Time_Stamp_Type) return Boolean is + begin + return not (Left > Right); + end "<="; + + --------- + -- "=" -- + --------- + + function "=" (Left, Right : Time_Stamp_Type) return Boolean is + Sleft : Nat; + Sright : Nat; + + begin + if String (Left) = String (Right) then + return True; + + elsif Left (1) = ' ' or else Right (1) = ' ' then + return False; + end if; + + -- In the following code we check for a difference of 2 seconds or less + + -- Recall that the time stamp format is: + + -- Y Y Y Y M M D D H H M M S S + -- 01 02 03 04 05 06 07 08 09 10 11 12 13 14 + + -- Note that we do not bother to worry about shifts in the day. + -- It seems unlikely that such shifts could ever occur in practice + -- and even if they do we err on the safe side, ie we say that the time + -- stamps are different. + + Sright := V (Right, 13) + 60 * (V (Right, 11) + 60 * V (Right, 09)); + Sleft := V (Left, 13) + 60 * (V (Left, 11) + 60 * V (Left, 09)); + + -- So the check is: dates must be the same, times differ 2 sec at most + + return abs (Sleft - Sright) <= 2 + and then String (Left (1 .. 8)) = String (Right (1 .. 8)); + end "="; + + --------- + -- ">" -- + --------- + + function ">" (Left, Right : Time_Stamp_Type) return Boolean is + begin + return not (Left = Right) and then String (Left) > String (Right); + end ">"; + + ---------- + -- ">=" -- + ---------- + + function ">=" (Left, Right : Time_Stamp_Type) return Boolean is + begin + return not (Left < Right); + end ">="; + + ------------------- + -- Get_Char_Code -- + ------------------- + + function Get_Char_Code (C : Character) return Char_Code is + begin + return Char_Code'Val (Character'Pos (C)); + end Get_Char_Code; + + ------------------- + -- Get_Character -- + ------------------- + + -- Note: raises Constraint_Error if checks on and C out of range + + function Get_Character (C : Char_Code) return Character is + begin + return Character'Val (C); + end Get_Character; + + -------------------- + -- Get_Hex_String -- + -------------------- + + subtype Wordh is Word range 0 .. 15; + Hex : constant array (Wordh) of Character := "0123456789ABCDEF"; + + function Get_Hex_String (W : Word) return Word_Hex_String is + X : Word := W; + WS : Word_Hex_String; + + begin + for J in reverse 1 .. 8 loop + WS (J) := Hex (X mod 16); + X := X / 16; + end loop; + + return WS; + end Get_Hex_String; + + ------------------------ + -- In_Character_Range -- + ------------------------ + + function In_Character_Range (C : Char_Code) return Boolean is + begin + return (C <= 255); + end In_Character_Range; + + --------------------- + -- Make_Time_Stamp -- + --------------------- + + procedure Make_Time_Stamp + (Year : Nat; + Month : Nat; + Day : Nat; + Hour : Nat; + Minutes : Nat; + Seconds : Nat; + TS : out Time_Stamp_Type) + is + Z : constant := Character'Pos ('0'); + + begin + TS (01) := Character'Val (Z + Year / 1000); + TS (02) := Character'Val (Z + (Year / 100) mod 10); + TS (03) := Character'Val (Z + (Year / 10) mod 10); + TS (04) := Character'Val (Z + Year mod 10); + TS (05) := Character'Val (Z + Month / 10); + TS (06) := Character'Val (Z + Month mod 10); + TS (07) := Character'Val (Z + Day / 10); + TS (08) := Character'Val (Z + Day mod 10); + TS (09) := Character'Val (Z + Hour / 10); + TS (10) := Character'Val (Z + Hour mod 10); + TS (11) := Character'Val (Z + Minutes / 10); + TS (12) := Character'Val (Z + Minutes mod 10); + TS (13) := Character'Val (Z + Seconds / 10); + TS (14) := Character'Val (Z + Seconds mod 10); + end Make_Time_Stamp; + + ---------------------- + -- Split_Time_Stamp -- + ---------------------- + + procedure Split_Time_Stamp + (TS : Time_Stamp_Type; + Year : out Nat; + Month : out Nat; + Day : out Nat; + Hour : out Nat; + Minutes : out Nat; + Seconds : out Nat) + is + + begin + -- Y Y Y Y M M D D H H M M S S + -- 01 02 03 04 05 06 07 08 09 10 11 12 13 14 + + Year := 100 * V (TS, 01) + V (TS, 03); + Month := V (TS, 05); + Day := V (TS, 07); + Hour := V (TS, 09); + Minutes := V (TS, 11); + Seconds := V (TS, 13); + end Split_Time_Stamp; + + ------- + -- V -- + ------- + + function V (T : Time_Stamp_Type; X : Time_Stamp_Index) return Nat is + begin + return 10 * (Character'Pos (T (X)) - Character'Pos ('0')) + + Character'Pos (T (X + 1)) - Character'Pos ('0'); + end V; + +end Types; diff --git a/gcc/ada/types.ads b/gcc/ada/types.ads new file mode 100644 index 00000000000..1cbf57d2672 --- /dev/null +++ b/gcc/ada/types.ads @@ -0,0 +1,720 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- T Y P E S -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.87 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Unchecked_Deallocation; + +package Types is +pragma Preelaborate (Types); + +-- This package contains host independent type definitions which are used +-- in more than one unit in the compiler. They are gathered here for easy +-- reference, though in some cases the full description is found in the +-- relevant module which implements the definition. The main reason that +-- they are not in their "natural" specs is that this would cause a lot of +-- inter-spec dependencies, and in particular some awkward circular +-- dependencies would have to be dealt with. + +-- WARNING: There is a C version of this package. Any changes to this +-- source file must be properly reflected in the C header file a-types.h + +-- Note: the declarations in this package reflect an expectation that the +-- host machine has an efficient integer base type with a range at least +-- 32 bits 2s-complement. If there are any machines for which this is not +-- a correct assumption, a significant number of changes will be required! + + ------------------------------- + -- General Use Integer Types -- + ------------------------------- + + type Int is range -2 ** 31 .. +2 ** 31 - 1; + -- Signed 32-bit integer + + type Dint is range -2 ** 63 .. +2 ** 63 - 1; + -- Double length (64-bit) integer + + subtype Nat is Int range 0 .. Int'Last; + -- Non-negative Int values + + subtype Pos is Int range 1 .. Int'Last; + -- Positive Int values + + type Word is mod 2 ** 32; + -- Unsigned 32-bit integer + + type Short is range -32768 .. +32767; + for Short'Size use 16; + -- 16-bit signed integer + + type Byte is mod 2 ** 8; + for Byte'Size use 8; + -- 8-bit unsigned integer + + type size_t is mod 2 ** Standard'Address_Size; + -- Memory size value, for use in calls to C routines + + -------------------------------------- + -- 8-Bit Character and String Types -- + -------------------------------------- + + -- We use Standard.Character and Standard.String freely, since we are + -- compiling ourselves, and we properly implement the required 8-bit + -- character code as required in Ada 95. This section defines a few + -- general use constants and subtypes. + + EOF : constant Character := ASCII.SUB; + -- The character SUB (16#1A#) is used in DOS and other systems derived + -- from DOS (OS/2, NT etc) to signal the end of a text file. Internally + -- all source files are ended by an EOF character, even on Unix systems. + -- An EOF character acts as the end of file only as the last character + -- of a source buffer, in any other position, it is treated as a blank + -- if it appears between tokens, and as an illegal character otherwise. + -- This makes life easier dealing with files that originated from DOS, + -- including concatenated files with interspersed EOF characters. + + subtype Graphic_Character is Character range ' ' .. '~'; + -- Graphic characters, as defined in ARM + + subtype Line_Terminator is Character range ASCII.LF .. ASCII.CR; + -- Line terminator characters (LF, VT, FF, CR) + + subtype Upper_Half_Character is + Character range Character'Val (16#80#) .. Character'Val (16#FF#); + -- Characters with the upper bit set + + type Character_Ptr is access all Character; + type String_Ptr is access all String; + -- Standard character and string pointers + + procedure Free is new Unchecked_Deallocation (String, String_Ptr); + -- Procedure for freeing dynamically allocated String values + + subtype Word_Hex_String is String (1 .. 8); + -- Type used to represent Word value as 8 hex digits, with upper case + -- letters for the alphabetic cases. + + function Get_Hex_String (W : Word) return Word_Hex_String; + -- Convert word value to 8-character hex string + + ----------------------------------------- + -- Types Used for Text Buffer Handling -- + ----------------------------------------- + + -- We can't use type String for text buffers, since we must use the + -- standard 32-bit integer as an index value, since we count on all + -- index values being the same size. + + type Text_Ptr is new Int; + -- Type used for subscripts in text buffer + + type Text_Buffer is array (Text_Ptr range <>) of Character; + -- Text buffer used to hold source file or library information file + + type Text_Buffer_Ptr is access all Text_Buffer; + -- Text buffers for input files are allocated dynamically and this type + -- is used to reference these text buffers. + + procedure Free is new Unchecked_Deallocation (Text_Buffer, Text_Buffer_Ptr); + -- Procedure for freeing dynamically allocated text buffers + + ------------------------------------------ + -- Types Used for Source Input Handling -- + ------------------------------------------ + + type Logical_Line_Number is range 0 .. Int'Last; + for Logical_Line_Number'Size use 32; + -- Line number type, used for storing logical line numbers (i.e. line + -- numbers that include effects of any Source_Reference pragmas in the + -- source file). The value zero indicates a line containing a source + -- reference pragma. + + No_Line_Number : constant Logical_Line_Number := 0; + -- Special value used to indicate no line number + + type Physical_Line_Number is range 1 .. Int'Last; + for Physical_Line_Number'Size use 32; + -- Line number type, used for storing physical line numbers (i.e. + -- line numbers in the physical file being compiled, unaffected by + -- the presence of source reference pragmas. + + type Column_Number is range 0 .. 32767; + for Column_Number'Size use 16; + -- Column number (assume that 2**15 is large enough, see declaration + -- of Hostparm.Max_Line_Length) + + No_Column_Number : constant Column_Number := 0; + -- Special value used to indicate no column number + + subtype Source_Buffer is Text_Buffer; + -- Type used to store text of a source file . The buffer for the main + -- source (the source specified on the command line) has a lower bound + -- starting at zero. Subsequent subsidiary sources have lower bounds + -- which are one greater than the previous upper bound. + + subtype Big_Source_Buffer is Text_Buffer (0 .. Text_Ptr'Last); + -- This is a virtual type used as the designated type of the access + -- type Source_Buffer_Ptr, see Osint.Read_Source_File for details. + + type Source_Buffer_Ptr is access all Big_Source_Buffer; + -- Pointer to source buffer. We use virtual origin addressing for + -- source buffers, with thin pointers. The pointer points to a virtual + -- instance of type Big_Source_Buffer, where the actual type is in fact + -- of type Source_Buffer. The address is adjusted so that the virtual + -- origin addressing works correctly. See Osint.Read_Source_Buffer for + -- further details. + + subtype Source_Ptr is Text_Ptr; + -- Type used to represent a source location, which is a subscript of a + -- character in the source buffer. As noted above, diffferent source + -- buffers have different ranges, so it is possible to tell from a + -- Source_Ptr value which source it refers to. Note that negative numbers + -- are allowed to accomodate the following special values. + + No_Location : constant Source_Ptr := -1; + -- Value used to indicate no source position set in a node + + Standard_Location : constant Source_Ptr := -2; + -- Used for all nodes in the representation of package Standard other + -- than nodes representing the contents of Standard.ASCII. Note that + -- testing for <= Standard_Location tests for both Standard_Location + -- and for Standard_ASCII_Location. + + Standard_ASCII_Location : constant Source_Ptr := -3; + -- Used for all nodes in the presentation of package Standard.ASCII + + First_Source_Ptr : constant Source_Ptr := 0; + -- Starting source pointer index value for first source program + + ------------------------------------- + -- Range Definitions for Tree Data -- + ------------------------------------- + + -- The tree has fields that can hold any of the following types: + + -- Pointers to other tree nodes (type Node_Id) + -- List pointers (type List_Id) + -- Element list pointers (type Elist_Id) + -- Names (type Name_Id) + -- Strings (type String_Id) + -- Universal integers (type Uint) + -- Universal reals (type Ureal) + -- Character codes (type Char_Code stored with a bias) + + -- In most contexts, the strongly typed interface determines which of + -- these types is present. However, there are some situations (involving + -- untyped traversals of the tree), where it is convenient to be easily + -- able to distinguish these values. The underlying representation in all + -- cases is an integer type Union_Id, and we ensure that the range of + -- the various possible values for each of the above types is disjoint + -- so that this distinction is possible. + + type Union_Id is new Int; + -- The type in the tree for a union of possible ID values + + -- Note: it is also helpful for debugging purposes to make these ranges + -- distinct. If a bug leads to misidentification of a value, then it will + -- typically result in an out of range value and a Constraint_Error. + + List_Low_Bound : constant := -100_000_000; + -- The List_Id values are subscripts into an array of list headers which + -- has List_Low_Bound as its lower bound. This value is chosen so that all + -- List_Id values are negative, and the value zero is in the range of both + -- List_Id and Node_Id values (see further description below). + + List_High_Bound : constant := 0; + -- Maximum List_Id subscript value. This allows up to 100 million list + -- Id values, which is in practice infinite, and there is no need to + -- check the range. The range overlaps the node range by one element + -- (with value zero), which is used both for the Empty node, and for + -- indicating no list. The fact that the same value is used is convenient + -- because it means that the default value of Empty applies to both nodes + -- and lists, and also is more efficient to test for. + + Node_Low_Bound : constant := 0; + -- The tree Id values start at zero, because we use zero for Empty (to + -- allow a zero test for Empty). Actual tree node subscripts start at 0 + -- since Empty is a legitimate node value. + + Node_High_Bound : constant := 099_999_999; + -- Maximum number of nodes that can be allocated is 100 million, which + -- is in practice infinite, and there is no need to check the range. + + Elist_Low_Bound : constant := 100_000_000; + -- The Elist_Id values are subscripts into an array of elist headers which + -- has Elist_Low_Bound as its lower bound. + + Elist_High_Bound : constant := 199_999_999; + -- Maximum Elist_Id subscript value. This allows up to 100 million Elists, + -- which is in practice infinite and there is no need to check the range. + + Elmt_Low_Bound : constant := 200_000_000; + -- Low bound of element Id values. The use of these values is internal to + -- the Elists package, but the definition of the range is included here + -- since it must be disjoint from other Id values. The Elmt_Id values are + -- subscripts into an array of list elements which has this as lower bound. + + Elmt_High_Bound : constant := 299_999_999; + -- Upper bound of Elmt_Id values. This allows up to 100 million element + -- list members, which is in practice infinite (no range check needed). + + Names_Low_Bound : constant := 300_000_000; + -- Low bound for name Id values + + Names_High_Bound : constant := 399_999_999; + -- Maximum number of names that can be allocated is 100 million, which is + -- in practice infinite and there is no need to check the range. + + Strings_Low_Bound : constant := 400_000_000; + -- Low bound for string Id values + + Strings_High_Bound : constant := 499_999_999; + -- Maximum number of strings that can be allocated is 100 million, which + -- is in practice infinite and there is no need to check the range. + + Ureal_Low_Bound : constant := 500_000_000; + -- Low bound for Ureal values. + + Ureal_High_Bound : constant := 599_999_999; + -- Maximum number of Ureal values stored is 100_000_000 which is in + -- practice infinite so that no check is required. + + Uint_Low_Bound : constant := 600_000_000; + -- Low bound for Uint values. + + Uint_Table_Start : constant := 2_000_000_000; + -- Location where table entries for universal integers start (see + -- Uintp spec for details of the representation of Uint values). + + Uint_High_Bound : constant := 2_099_999_999; + -- The range of Uint values is very large, since a substantial part + -- of this range is used to store direct values, see Uintp for details. + + Char_Code_Bias : constant := 2_100_000_000; + -- A bias value added to character code values stored in the tree which + -- ensures that they have different values from any of the above types. + + -- The following subtype definitions are used to provide convenient names + -- for membership tests on Int values to see what data type range they + -- lie in. Such tests appear only in the lowest level packages. + + subtype List_Range is Union_Id + range List_Low_Bound .. List_High_Bound; + + subtype Node_Range is Union_Id + range Node_Low_Bound .. Node_High_Bound; + + subtype Elist_Range is Union_Id + range Elist_Low_Bound .. Elist_High_Bound; + + subtype Elmt_Range is Union_Id + range Elmt_Low_Bound .. Elmt_High_Bound; + + subtype Names_Range is Union_Id + range Names_Low_Bound .. Names_High_Bound; + + subtype Strings_Range is Union_Id + range Strings_Low_Bound .. Strings_High_Bound; + + subtype Uint_Range is Union_Id + range Uint_Low_Bound .. Uint_High_Bound; + + subtype Ureal_Range is Union_Id + range Ureal_Low_Bound .. Ureal_High_Bound; + + subtype Char_Code_Range is Union_Id + range Char_Code_Bias .. Char_Code_Bias + 2**16 - 1; + + ----------------------------- + -- Types for Namet Package -- + ----------------------------- + + -- Name_Id values are used to identify entries in the names table. Except + -- for the special values No_Name, and Error_Name, they are subscript + -- values for the Names table defined in package Namet. + + -- Note that with only a few exceptions, which are clearly documented, the + -- type Name_Id should be regarded as a private type. In particular it is + -- never appropriate to perform arithmetic operations using this type. + + type Name_Id is range Names_Low_Bound .. Names_High_Bound; + for Name_Id'Size use 32; + -- Type used to identify entries in the names table + + No_Name : constant Name_Id := Names_Low_Bound; + -- The special Name_Id value No_Name is used in the parser to indicate + -- a situation where no name is present (e.g. on a loop or block). + + Error_Name : constant Name_Id := Names_Low_Bound + 1; + -- The special Name_Id value Error_Name is used in the parser to + -- indicate that some kind of error was encountered in scanning out + -- the relevant name, so it does not have a representable label. + + First_Name_Id : constant Name_Id := Names_Low_Bound + 2; + -- Subscript of first entry in names table + + ---------------------------- + -- Types for Atree Package -- + ---------------------------- + + -- Node_Id values are used to identify nodes in the tree. They are + -- subscripts into the Node table declared in package Tree. Note that + -- the special values Empty and Error are subscripts into this table, + -- See package Atree for further details. + + type Node_Id is range Node_Low_Bound .. Node_High_Bound; + -- Type used to identify nodes in the tree + + subtype Entity_Id is Node_Id; + -- A synonym for node types, used in the entity package to refer to + -- nodes that are entities (i.e. nodes with an Nkind of N_Defining_xxx) + -- All such nodes are extended nodes and these are the only extended + -- nodes, so that in practice entity and extended nodes are synonymous. + + subtype Node_Or_Entity_Id is Node_Id; + -- A synonym for node types, used in cases where a given value may be used + -- to represent either a node or an entity. We like to minimize such uses + -- for obvious reasons of logical type consistency, but where such uses + -- occur, they should be documented by use of this type. + + Empty : constant Node_Id := Node_Low_Bound; + -- Used to indicate null node. A node is actually allocated with this + -- Id value, so that Nkind (Empty) = N_Empty. Note that Node_Low_Bound + -- is zero, so Empty = No_List = zero. + + Empty_List_Or_Node : constant := 0; + -- This constant is used in situations (e.g. initializing empty fields) + -- where the value set will be used to represent either an empty node + -- or a non-existent list, depending on the context. + + Error : constant Node_Id := Node_Low_Bound + 1; + -- Used to indicate that there was an error in the source program. A node + -- is actually allocated at this address, so that Nkind (Error) = N_Error. + + Empty_Or_Error : constant Node_Id := Error; + -- Since Empty and Error are the first two Node_Id values, the test for + -- N <= Empty_Or_Error tests to see if N is Empty or Error. This definition + -- provides convenient self-documentation for such tests. + + First_Node_Id : constant Node_Id := Node_Low_Bound; + -- Subscript of first allocated node. Note that Empty and Error are both + -- allocated nodes, whose Nkind fields can be accessed without error. + + ------------------------------ + -- Types for Nlists Package -- + ------------------------------ + + -- List_Id values are used to identify node lists in the tree. They are + -- subscripts into the Lists table declared in package Tree. Note that + -- the special value Error_List is a subscript in this table, but the + -- value No_List is *not* a valid subscript, and any attempt to apply + -- list operations to No_List will cause a (detected) error. + + type List_Id is range List_Low_Bound .. List_High_Bound; + -- Type used to identify a node list + + No_List : constant List_Id := List_High_Bound; + -- Used to indicate absence of a list. Note that the value is zero, which + -- is the same as Empty, which is helpful in intializing nodes where a + -- value of zero can represent either an empty node or an empty list. + + Error_List : constant List_Id := List_Low_Bound; + -- Used to indicate that there was an error in the source program in a + -- context which would normally require a list. This node appears to be + -- an empty list to the list operations (a null list is actually allocated + -- which has this Id value). + + First_List_Id : constant List_Id := Error_List; + -- Subscript of first allocated list header + + ------------------------------ + -- Types for Elists Package -- + ------------------------------ + + -- Element list Id values are used to identify element lists stored in + -- the tree (see package Tree for further details). They are formed by + -- adding a bias (Element_List_Bias) to subscript values in the same + -- array that is used for node list headers. + + type Elist_Id is range Elist_Low_Bound .. Elist_High_Bound; + -- Type used to identify an element list (Elist header table subscript) + + No_Elist : constant Elist_Id := Elist_Low_Bound; + -- Used to indicate absense of an element list. Note that this is not + -- an actual Elist header, so element list operations on this value + -- are not valid. + + First_Elist_Id : constant Elist_Id := No_Elist + 1; + -- Subscript of first allocated Elist header. + + -- Element Id values are used to identify individual elements of an + -- element list (see package Elists for further details). + + type Elmt_Id is range Elmt_Low_Bound .. Elmt_High_Bound; + -- Type used to identify an element list + + No_Elmt : constant Elmt_Id := Elmt_Low_Bound; + -- Used to represent empty element + + First_Elmt_Id : constant Elmt_Id := No_Elmt + 1; + -- Subscript of first allocated Elmt table entry + + ------------------------------- + -- Types for Stringt Package -- + ------------------------------- + + -- String_Id values are used to identify entries in the strings table. + -- They are subscripts into the strings table defined in package Strings. + + -- Note that with only a few exceptions, which are clearly documented, the + -- type String_Id should be regarded as a private type. In particular it is + -- never appropriate to perform arithmetic operations using this type. + + type String_Id is range Strings_Low_Bound .. Strings_High_Bound; + -- Type used to identify entries in the strings table + + No_String : constant String_Id := Strings_Low_Bound; + -- Used to indicate missing string Id. Note that the value zero is used + -- to indicate a missing data value for all the Int types in this section. + + First_String_Id : constant String_Id := No_String + 1; + -- First subscript allocated in string table + + ------------------------- + -- Character Code Type -- + ------------------------- + + -- The type Char is used for character data internally in the compiler, + -- but character codes in the source are represented by the Char_Code + -- type. Each character literal in the source is interpreted as being one + -- of the 2**16 possible Wide_Character codes, and a unique integer value + -- is assigned, corresponding to the POS value in the Wide_Character type. + -- String literals are similarly interpreted as a sequence of such codes. + + -- Note: when character code values are stored in the tree, they are stored + -- by adding a bias value (Char_Code_Bias) that results in values that can + -- be distinguished from other types of values stored in the tree. + + type Char_Code is mod 2 ** 16; + for Char_Code'Size use 16; + + function Get_Char_Code (C : Character) return Char_Code; + pragma Inline (Get_Char_Code); + -- Function to obtain internal character code from source character. For + -- the moment, the internal character code is simply the Pos value of the + -- input source character, but we provide this interface for possible + -- later support of alternative character sets. + + function In_Character_Range (C : Char_Code) return Boolean; + pragma Inline (In_Character_Range); + -- Determines if the given character code is in range of type Character, + -- and if so, returns True. If not, returns False. + + function Get_Character (C : Char_Code) return Character; + pragma Inline (Get_Character); + -- For a character C that is in character range (see above function), this + -- function returns the corresponding Character value. It is an error to + -- call Get_Character if C is not in character range + + --------------------------------------- + -- Types used for Library Management -- + --------------------------------------- + + type Unit_Number_Type is new Int; + -- Unit number. The main source is unit 0, and subsidiary sources have + -- non-zero numbers starting with 1. Unit numbers are used to index the + -- file table in Lib. + + Main_Unit : constant Unit_Number_Type := 0; + -- Unit number value for main unit + + No_Unit : constant Unit_Number_Type := -1; + -- Special value used to signal no unit + + type Source_File_Index is new Nat; + -- Type used to index the source file table (see package Sinput) + + No_Source_File : constant Source_File_Index := 0; + -- Value used to indicate no source file present + + System_Source_File_Index : constant Source_File_Index := 1; + -- Value used for source file table entry for system.ads, which is + -- always the first source file read (see unit Targparm for details). + + subtype File_Name_Type is Name_Id; + -- File names are stored in the names table and this synonym is used to + -- indicate that a Name_Id value is being used to hold a simple file + -- name (which does not include any directory information). + + No_File : constant File_Name_Type := File_Name_Type (No_Name); + -- Constant used to indicate no file found + + subtype Unit_Name_Type is Name_Id; + -- Unit names are stored in the names table and this synonym is used to + -- indicate that a Name_Id value is being used to hold a unit name. + + ----------------------------------- + -- Representation of Time Stamps -- + ----------------------------------- + + -- All compiled units are marked with a time stamp which is derived from + -- the source file (we assume that the host system has the concept of a + -- file time stamp which is modified when a file is modified). These + -- time stamps are used to ensure consistency of the set of units that + -- constitutes a library. Time stamps are 12 character strings with + -- with the following format: + + -- YYYYMMDDHHMMSS + + -- YYYY year + -- MM month (2 digits 01-12) + -- DD day (2 digits 01-31) + -- HH hour (2 digits 00-23) + -- MM minutes (2 digits 00-59) + -- SS seconds (2 digits 00-59) + + -- In the case of Unix systems (and other systems which keep the time in + -- GMT), the time stamp is the GMT time of the file, not the local time. + -- This solves problems in using libraries across networks with clients + -- spread across multiple time-zones. + + Time_Stamp_Length : constant := 14; + -- Length of time stamp value + + subtype Time_Stamp_Index is Natural range 1 .. Time_Stamp_Length; + type Time_Stamp_Type is new String (Time_Stamp_Index); + -- Type used to represent time stamp + + Empty_Time_Stamp : constant Time_Stamp_Type := (others => ' '); + -- Type used to represent an empty or missing time stamp. Looks less + -- than any real time stamp if two time stamps are compared. Note that + -- although this is not a private type, clients should not rely on the + -- exact way in which this string is represented, and instead should + -- use the subprograms below. + + function "=" (Left, Right : Time_Stamp_Type) return Boolean; + function "<=" (Left, Right : Time_Stamp_Type) return Boolean; + function ">=" (Left, Right : Time_Stamp_Type) return Boolean; + function "<" (Left, Right : Time_Stamp_Type) return Boolean; + function ">" (Left, Right : Time_Stamp_Type) return Boolean; + -- Comparison functions on time stamps. Note that two time stamps + -- are defined as being equal if they have the same day/month/year + -- and the hour/minutes/seconds values are within 2 seconds of one + -- another. This deals with rounding effects in library file time + -- stamps caused by copying operations during installation. We have + -- particularly noticed that WinNT seems susceptible to such changes. + -- Note: the Empty_Time_Stamp value looks equal to itself, and less + -- than any non-empty time stamp value. + + procedure Split_Time_Stamp + (TS : Time_Stamp_Type; + Year : out Nat; + Month : out Nat; + Day : out Nat; + Hour : out Nat; + Minutes : out Nat; + Seconds : out Nat); + -- Given a time stamp, decompose it into its components + + procedure Make_Time_Stamp + (Year : Nat; + Month : Nat; + Day : Nat; + Hour : Nat; + Minutes : Nat; + Seconds : Nat; + TS : out Time_Stamp_Type); + -- Given the components of a time stamp, initialize the value + + ----------------------------------------------- + -- Types used for Pragma Suppress Management -- + ----------------------------------------------- + + -- The following record contains an entry for each recognized check name + -- for pragma Suppress. It is used to represent current settings of scope + -- based suppress actions from pragma Suppress or command line settings. + + type Suppress_Record is record + Access_Checks : Boolean; + Accessibility_Checks : Boolean; + Discriminant_Checks : Boolean; + Division_Checks : Boolean; + Elaboration_Checks : Boolean; + Index_Checks : Boolean; + Length_Checks : Boolean; + Overflow_Checks : Boolean; + Range_Checks : Boolean; + Storage_Checks : Boolean; + Tag_Checks : Boolean; + end record; + + -- To add a new check type to GNAT, the following steps are required: + + -- 1. Add an appropriate entry to the above record type + -- 2. Add an entry to Snames spec and body for the new name + -- 3. Add an entry to the definition of Check_Id in the Snames spec + -- 4. Add a new entity flag definition in Einfo for the check + -- 5. Add a new function to Sem.Util to handle the new check test + -- 6. Add appropriate processing for pragma Suppress in Sem.Prag + -- 7. Add a branch to the case statement in Sem.Ch8.Pop_Scope + -- 8. Add a new Do_xxx_Check flag to Sinfo (if required) + -- 9. Add appropriate checks for the new test + + ----------------------------------- + -- Global Exception Declarations -- + ----------------------------------- + + -- This section contains declarations of exceptions that are used + -- throughout the compiler. + + Unrecoverable_Error : exception; + -- This exception is raised to immediately terminate the compilation + -- of the current source program. Used in situations where things are + -- bad enough that it doesn't seem worth continuing (e.g. max errors + -- reached, or a required file is not found). Also raised when the + -- compiler finds itself in trouble after an error (see Comperr). + + --------------------------------- + -- Parameter Mechanism Control -- + --------------------------------- + + -- Function and parameter entities have a field that records the + -- passing mechanism. See specification of Sem_Mech for full details. + -- The following subtype is used to represent values of this type: + + subtype Mechanism_Type is Int range -10 .. Int'Last; + -- Type used to represent a mechanism value. This is a subtype rather + -- than a type to avoid some annoying processing problems with certain + -- routines in Einfo (processing them to create the corresponding C). + +end Types; diff --git a/gcc/ada/types.h b/gcc/ada/types.h new file mode 100644 index 00000000000..e993bdbea6a --- /dev/null +++ b/gcc/ada/types.h @@ -0,0 +1,335 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * T Y P E S * + * * + * C Header File * + * * + * $Revision: 1.1 $ + * * + * Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +/* This is the C file that corresponds to the Ada package spec Types. It was + created manually from the files types.ads and types.adb. + + This package contains host independent type definitions which are used + throughout the compiler modules. The comments in the C version are brief + reminders of the purpose of each declaration. For complete documentation, + see the Ada version of these definitions. */ + +/* Boolean Types: */ + +/* Boolean type (cannot use enum, because of bit field restriction on some + compilers). */ +typedef unsigned char Boolean; +#define False 0 +#define True 1 + +/* General Use Integer Types */ + +/* Signed 32/bit integer */ +typedef int Int; + +/* Signed 16 bit integer */ +typedef short Short; + +/* Non/negative Int values */ +typedef Int Nat; + +/* Positive Int values */ +typedef Int Pos; + +/* 8/bit unsigned integer */ +typedef char Byte; + +/* 8/Bit Character and String Types: */ + +/* 8/bit character type */ +typedef char Char; + +/* Graphic characters, as defined in ARM */ +typedef Char Graphic_Character; + +/* Line terminator characters (LF, VT, FF, CR) */ +typedef Char Line_Terminator; + +/* Characters with the upper bit set */ +typedef Char Upper_Half_Character; + +/* String type built on Char (note that zero is an OK index) */ +typedef Char *Str; + +/* Pointer to string of Chars */ +typedef Char *Str_Ptr; + +/* Types for the fat pointer used for strings and the template it + points to. */ +typedef struct {int Low_Bound, High_Bound; } String_Template; +typedef struct {const char *Array; String_Template *Bounds; } + __attribute ((aligned (sizeof (char *) * 2))) Fat_Pointer; + +/* Types for Node/Entity Kinds: */ + +/* The reason that these are defined here in the C version, rather than in the + corresponding packages is that the requirement for putting bodies of + inlined stuff IN the C header changes the dependencies. Both a-sinfo.h + and a-einfo.h now reference routines defined in tree.h. + + Note: these types would more naturally be defined as unsigned char, but + once again, the annoying restriction on bit fields for some compilers + bites us! */ + +typedef unsigned int Node_Kind; +typedef unsigned int Entity_Kind; + +/* Types used for Text Buffer Handling: */ + +/* Type used for subscripts in text buffer. */ +typedef Int Text_Ptr; + +/* Text buffer used to hold source file or library information file. */ +typedef Char *Text_Buffer; + +/* Pointer to text buffer. */ +typedef Char *Text_Buffer_Ptr; + +/* Types used for Source Input Handling: */ + +/* Line number type, used for storing all line numbers. */ +typedef Int Line_Number_Type; + +/* Column number type, used for storing all column numbers. */ +typedef Int Column_Number_Type; + +/* Type used to store text of a source file. */ +typedef Text_Buffer Source_Buffer; + +/* Pointer to source buffer. */ +typedef Text_Buffer_Ptr Source_Buffer_Ptr; + +/* Type used for source location. */ +typedef Text_Ptr Source_Ptr; + +/* Value used to indicate no source position set. */ +#define No_Location -1 + +/* Used for Sloc in all nodes in the representation of package Standard. */ +#define Standard_Location -2 + +/* Type used for union of all possible ID values covering all ranges */ +typedef int Union_Id; + +/* Range definitions for Tree Data: */ + +#define List_Low_Bound -100000000 +#define List_High_Bound 0 + +#define Node_Low_Bound 0 +#define Node_High_Bound 99999999 + +#define Elist_Low_Bound 100000000 +#define Elist_High_Bound 199999999 + +#define Elmt_Low_Bound 200000000 +#define Elmt_High_Bound 299999999 + +#define Names_Low_Bound 300000000 +#define Names_High_Bound 399999999 + +#define Strings_Low_Bound 400000000 +#define Strings_High_Bound 499999999 + +#define Ureal_Low_Bound 500000000 +#define Ureal_High_Bound 599999999 + +#define Uint_Low_Bound 600000000 +#define Uint_Table_Start 2000000000 +#define Uint_High_Bound 2099999999 + +#define Char_Code_Bias 2100000000 + +SUBTYPE (List_Range, Int, List_Low_Bound, List_High_Bound) +SUBTYPE (Node_Range, Int, Node_Low_Bound, Node_High_Bound) +SUBTYPE (Elist_Range, Int, Elist_Low_Bound, Elist_High_Bound) +SUBTYPE (Elmt_Range, Int, Elmt_Low_Bound, Elmt_High_Bound) +SUBTYPE (Names_Range, Int, Names_Low_Bound, Names_High_Bound) +SUBTYPE (Strings_Range, Int, Strings_Low_Bound, Strings_High_Bound) +SUBTYPE (Uint_Range, Int, Uint_Low_Bound, Uint_High_Bound) +SUBTYPE (Ureal_Range, Int, Ureal_Low_Bound, Ureal_High_Bound) +SUBTYPE (Char_Code_Range, Int, Char_Code_Bias, (Char_Code_Bias + 65535)) + +/* Types for Names_Table Package: */ + +typedef Int Name_Id; + +/* Name_Id value for no name present. */ +#define No_Name Names_Low_Bound + +/* Name_Id value for bad name. */ +#define Error_Name (Names_Low_Bound + 1) + +/* First subscript of names table. */ +#define First_Name_Id (Names_Low_Bound + 2) + +/* Types for Tree Package: */ + +/* Subscript of nodes table entry. */ +typedef Int Node_Id; + +/* Used in semantics for Node_Id value referencing an entity. */ +typedef Node_Id Entity_Id; + +/* Null node. */ +#define Empty 0 + +/* Error node. */ +#define Error 1 + +/* Subscript of first allocated node. */ +#define First_Node_Id Empty + +/* Subscript of entry in lists table. */ +typedef Int List_Id; + +/* Indicates absence of a list. */ +#define No_List 0 + +/* Error list. */ +#define Error_List List_Low_Bound + +/* Subscript of first allocated list header. */ +#define First_List_Id Error_List + +/* Element list Id, subscript value of entry in lists table. */ +typedef Int Elist_Id; + +/* Used to indicate absence of an element list. */ +#define No_Elist Elist_Low_Bound + +/* Subscript of first allocated elist header */ +#define First_Elist_Id (No_Elist + 1) + +/* Element Id, subscript value of entry in elements table. */ +typedef Int Elmt_Id; + +/* Used to indicate absence of a list element. */ +#define No_Elmt Elmt_Low_Bound + +/* Subscript of first allocated element */ +#define First_Elmt_Id (No_Elmt + 1) + +/* Types for String_Table Package: */ + +/* Subscript of strings table entry. */ +typedef Int String_Id; + +/* Used to indicate missing string Id. */ +#define No_String Strings_Low_Bound + +/* Subscript of first entry in strings table. */ +#define First_String_Id (No_String + 1) + +/* Types for Uint_Support Package: */ + +/* Type used for representation of universal integers. */ +typedef Int Uint; + +/* Used to indicate missing Uint value. */ +#define No_Uint Uint_Low_Bound + +/* Base value used to represent Uint values. */ +#define Base 32768 + +/* Minimum and maximum integers directly representable as Uint values */ +#define Min_Direct (-(Base - 1)) +#define Max_Direct ((Base - 1) * (Base - 1)) + +#define Uint_Direct_Bias (Uint_Low_Bound + Base) +#define Uint_Direct_First (Uint_Direct_Bias + Min_Direct) +#define Uint_Direct_Last (Uint_Direct_Bias + Max_Direct) + +/* Define range of direct biased values */ +SUBTYPE (Uint_Direct, Uint, Uint_Direct_First, Uint_Direct_Last) + +/* Constants in Uint format. */ +#define Uint_0 (Uint_Direct_Bias + 0) +#define Uint_1 (Uint_Direct_Bias + 1) +#define Uint_2 (Uint_Direct_Bias + 2) +#define Uint_10 (Uint_Direct_Bias + 10) +#define Uint_16 (Uint_Direct_Bias + 16) + +/* Types for Ureal_Support Package: */ + +/* Type used for representation of universal reals. */ +typedef Int Ureal; + +/* Used to indicate missing Uint value. */ +#define No_Ureal Ureal_Low_Bound + +/* Subscript of first entry in Ureal table. */ +#define Ureal_First_Entry (No_Ureal + 1) + +/* Character Code Type: */ + +/* Character code value, intended to be 16 bits. */ +typedef short Char_Code; + +/* Types Used for Library Management: */ + +/* Unit number. */ +typedef Int Unit_Number_Type; + +/* Unit number value for main unit. */ +#define Main_Unit 0 + +/* Type used for lines table. */ +typedef Source_Ptr *Lines_Table_Type; + +/* Type used for pointer to lines table. */ +typedef Source_Ptr *Lines_Table_Ptr; + +/* Length of time stamp value. */ +#define Time_Stamp_Length 22 + +/* Type used to represent time stamp. */ +typedef Char *Time_Stamp_Type; + +/* Name_Id synonym used for file names. */ +typedef Name_Id File_Name_Type; + +/* Constant used to indicate no file found. */ +#define No_File No_Name + +/* Name_Id synonym used for unit names. */ +typedef Name_Id Unit_Name_Type; + +/* Definitions for mechanism type and values */ +typedef Int Mechanism_Type; +#define Default 0 +#define By_Copy (-1) +#define By_Reference (-2) +#define By_Descriptor (-3) +#define By_Descriptor_UBS (-4) +#define By_Descriptor_UBSB (-5) +#define By_Descriptor_UBA (-6) +#define By_Descriptor_S (-7) +#define By_Descriptor_SB (-8) +#define By_Descriptor_A (-9) +#define By_Descriptor_NCA (-10) diff --git a/gcc/ada/uintp.adb b/gcc/ada/uintp.adb new file mode 100644 index 00000000000..d60986b07d0 --- /dev/null +++ b/gcc/ada/uintp.adb @@ -0,0 +1,2472 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U I N T P -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.74 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Output; use Output; +with Tree_IO; use Tree_IO; + +package body Uintp is + + ------------------------ + -- Local Declarations -- + ------------------------ + + Uint_Int_First : Uint := Uint_0; + -- Uint value containing Int'First value, set by Initialize. The initial + -- value of Uint_0 is used for an assertion check that ensures that this + -- value is not used before it is initialized. This value is used in the + -- UI_Is_In_Int_Range predicate, and it is right that this is a host + -- value, since the issue is host representation of integer values. + + Uint_Int_Last : Uint; + -- Uint value containing Int'Last value set by Initialize. + + UI_Power_2 : array (Int range 0 .. 64) of Uint; + -- This table is used to memoize exponentiations by powers of 2. The Nth + -- entry, if set, contains the Uint value 2 ** N. Initially UI_Power_2_Set + -- is zero and only the 0'th entry is set, the invariant being that all + -- entries in the range 0 .. UI_Power_2_Set are initialized. + + UI_Power_2_Set : Nat; + -- Number of entries set in UI_Power_2; + + UI_Power_10 : array (Int range 0 .. 64) of Uint; + -- This table is used to memoize exponentiations by powers of 10 in the + -- same manner as described above for UI_Power_2. + + UI_Power_10_Set : Nat; + -- Number of entries set in UI_Power_10; + + Uints_Min : Uint; + Udigits_Min : Int; + -- These values are used to make sure that the mark/release mechanism + -- does not destroy values saved in the U_Power tables. Whenever an + -- entry is made in the U_Power tables, Uints_Min and Udigits_Min are + -- updated to protect the entry, and Release never cuts back beyond + -- these minimum values. + + Int_0 : constant Int := 0; + Int_1 : constant Int := 1; + Int_2 : constant Int := 2; + -- These values are used in some cases where the use of numeric literals + -- would cause ambiguities (integer vs Uint). + + ----------------------- + -- Local Subprograms -- + ----------------------- + + function Direct (U : Uint) return Boolean; + pragma Inline (Direct); + -- Returns True if U is represented directly + + function Direct_Val (U : Uint) return Int; + -- U is a Uint for is represented directly. The returned result + -- is the value represented. + + function GCD (Jin, Kin : Int) return Int; + -- Compute GCD of two integers. Assumes that Jin >= Kin >= 0 + + procedure Image_Out + (Input : Uint; + To_Buffer : Boolean; + Format : UI_Format); + -- Common processing for UI_Image and UI_Write, To_Buffer is set + -- True for UI_Image, and false for UI_Write, and Format is copied + -- from the Format parameter to UI_Image or UI_Write. + + procedure Init_Operand (UI : Uint; Vec : out UI_Vector); + pragma Inline (Init_Operand); + -- This procedure puts the value of UI into the vector in canonical + -- multiple precision format. The parameter should be of the correct + -- size as determined by a previous call to N_Digits (UI). The first + -- digit of Vec contains the sign, all other digits are always non- + -- negative. Note that the input may be directly represented, and in + -- this case Vec will contain the corresponding one or two digit value. + + function Least_Sig_Digit (Arg : Uint) return Int; + pragma Inline (Least_Sig_Digit); + -- Returns the Least Significant Digit of Arg quickly. When the given + -- Uint is less than 2**15, the value returned is the input value, in + -- this case the result may be negative. It is expected that any use + -- will mask off unnecessary bits. This is used for finding Arg mod B + -- where B is a power of two. Hence the actual base is irrelevent as + -- long as it is a power of two. + + procedure Most_Sig_2_Digits + (Left : Uint; + Right : Uint; + Left_Hat : out Int; + Right_Hat : out Int); + -- Returns leading two significant digits from the given pair of Uint's. + -- Mathematically: returns Left / (Base ** K) and Right / (Base ** K) + -- where K is as small as possible S.T. Right_Hat < Base * Base. + -- It is required that Left > Right for the algorithm to work. + + function N_Digits (Input : Uint) return Int; + pragma Inline (N_Digits); + -- Returns number of "digits" in a Uint + + function Sum_Digits (Left : Uint; Sign : Int) return Int; + -- If Sign = 1 return the sum of the "digits" of Abs (Left). If the + -- total has more then one digit then return Sum_Digits of total. + + function Sum_Double_Digits (Left : Uint; Sign : Int) return Int; + -- Same as above but work in New_Base = Base * Base + + function Vector_To_Uint + (In_Vec : UI_Vector; + Negative : Boolean) + return Uint; + -- Functions that calculate values in UI_Vectors, call this function + -- to create and return the Uint value. In_Vec contains the multiple + -- precision (Base) representation of a non-negative value. Leading + -- zeroes are permitted. Negative is set if the desired result is + -- the negative of the given value. The result will be either the + -- appropriate directly represented value, or a table entry in the + -- proper canonical format is created and returned. + -- + -- Note that Init_Operand puts a signed value in the result vector, + -- but Vector_To_Uint is always presented with a non-negative value. + -- The processing of signs is something that is done by the caller + -- before calling Vector_To_Uint. + + ------------ + -- Direct -- + ------------ + + function Direct (U : Uint) return Boolean is + begin + return Int (U) <= Int (Uint_Direct_Last); + end Direct; + + ---------------- + -- Direct_Val -- + ---------------- + + function Direct_Val (U : Uint) return Int is + begin + pragma Assert (Direct (U)); + return Int (U) - Int (Uint_Direct_Bias); + end Direct_Val; + + --------- + -- GCD -- + --------- + + function GCD (Jin, Kin : Int) return Int is + J, K, Tmp : Int; + + begin + pragma Assert (Jin >= Kin); + pragma Assert (Kin >= Int_0); + + J := Jin; + K := Kin; + + while K /= Uint_0 loop + Tmp := J mod K; + J := K; + K := Tmp; + end loop; + + return J; + end GCD; + + --------------- + -- Image_Out -- + --------------- + + procedure Image_Out + (Input : Uint; + To_Buffer : Boolean; + Format : UI_Format) + is + Marks : constant Uintp.Save_Mark := Uintp.Mark; + Base : Uint; + Ainput : Uint; + + Digs_Output : Natural := 0; + -- Counts digits output. In hex mode, but not in decimal mode, we + -- put an underline after every four hex digits that are output. + + Exponent : Natural := 0; + -- If the number is too long to fit in the buffer, we switch to an + -- approximate output format with an exponent. This variable records + -- the exponent value. + + function Better_In_Hex return Boolean; + -- Determines if it is better to generate digits in base 16 (result + -- is true) or base 10 (result is false). The choice is purely a + -- matter of convenience and aesthetics, so it does not matter which + -- value is returned from a correctness point of view. + + procedure Image_Char (C : Character); + -- Internal procedure to output one character + + procedure Image_Exponent (N : Natural); + -- Output non-zero exponent. Note that we only use the exponent + -- form in the buffer case, so we know that To_Buffer is true. + + procedure Image_Uint (U : Uint); + -- Internal procedure to output characters of non-negative Uint + + ------------------- + -- Better_In_Hex -- + ------------------- + + function Better_In_Hex return Boolean is + T16 : constant Uint := Uint_2 ** Int'(16); + A : Uint; + + begin + A := UI_Abs (Input); + + -- Small values up to 2**16 can always be in decimal + + if A < T16 then + return False; + end if; + + -- Otherwise, see if we are a power of 2 or one less than a power + -- of 2. For the moment these are the only cases printed in hex. + + if A mod Uint_2 = Uint_1 then + A := A + Uint_1; + end if; + + loop + if A mod T16 /= Uint_0 then + return False; + + else + A := A / T16; + end if; + + exit when A < T16; + end loop; + + while A > Uint_2 loop + if A mod Uint_2 /= Uint_0 then + return False; + + else + A := A / Uint_2; + end if; + end loop; + + return True; + end Better_In_Hex; + + ---------------- + -- Image_Char -- + ---------------- + + procedure Image_Char (C : Character) is + begin + if To_Buffer then + if UI_Image_Length + 6 > UI_Image_Max then + Exponent := Exponent + 1; + else + UI_Image_Length := UI_Image_Length + 1; + UI_Image_Buffer (UI_Image_Length) := C; + end if; + else + Write_Char (C); + end if; + end Image_Char; + + -------------------- + -- Image_Exponent -- + -------------------- + + procedure Image_Exponent (N : Natural) is + begin + if N >= 10 then + Image_Exponent (N / 10); + end if; + + UI_Image_Length := UI_Image_Length + 1; + UI_Image_Buffer (UI_Image_Length) := + Character'Val (Character'Pos ('0') + N mod 10); + end Image_Exponent; + + ---------------- + -- Image_Uint -- + ---------------- + + procedure Image_Uint (U : Uint) is + H : array (Int range 0 .. 15) of Character := "0123456789ABCDEF"; + + begin + if U >= Base then + Image_Uint (U / Base); + end if; + + if Digs_Output = 4 and then Base = Uint_16 then + Image_Char ('_'); + Digs_Output := 0; + end if; + + Image_Char (H (UI_To_Int (U rem Base))); + + Digs_Output := Digs_Output + 1; + end Image_Uint; + + -- Start of processing for Image_Out + + begin + if Input = No_Uint then + Image_Char ('?'); + return; + end if; + + UI_Image_Length := 0; + + if Input < Uint_0 then + Image_Char ('-'); + Ainput := -Input; + else + Ainput := Input; + end if; + + if Format = Hex + or else (Format = Auto and then Better_In_Hex) + then + Base := Uint_16; + Image_Char ('1'); + Image_Char ('6'); + Image_Char ('#'); + Image_Uint (Ainput); + Image_Char ('#'); + + else + Base := Uint_10; + Image_Uint (Ainput); + end if; + + if Exponent /= 0 then + UI_Image_Length := UI_Image_Length + 1; + UI_Image_Buffer (UI_Image_Length) := 'E'; + Image_Exponent (Exponent); + end if; + + Uintp.Release (Marks); + end Image_Out; + + ------------------- + -- Init_Operand -- + ------------------- + + procedure Init_Operand (UI : Uint; Vec : out UI_Vector) is + Loc : Int; + + begin + if Direct (UI) then + Vec (1) := Direct_Val (UI); + + if Vec (1) >= Base then + Vec (2) := Vec (1) rem Base; + Vec (1) := Vec (1) / Base; + end if; + + else + Loc := Uints.Table (UI).Loc; + + for J in 1 .. Uints.Table (UI).Length loop + Vec (J) := Udigits.Table (Loc + J - 1); + end loop; + end if; + end Init_Operand; + + ---------------- + -- Initialize -- + ---------------- + + procedure Initialize is + begin + Uints.Init; + Udigits.Init; + + Uint_Int_First := UI_From_Int (Int'First); + Uint_Int_Last := UI_From_Int (Int'Last); + + UI_Power_2 (0) := Uint_1; + UI_Power_2_Set := 0; + + UI_Power_10 (0) := Uint_1; + UI_Power_10_Set := 0; + + Uints_Min := Uints.Last; + Udigits_Min := Udigits.Last; + + end Initialize; + + --------------------- + -- Least_Sig_Digit -- + --------------------- + + function Least_Sig_Digit (Arg : Uint) return Int is + V : Int; + + begin + if Direct (Arg) then + V := Direct_Val (Arg); + + if V >= Base then + V := V mod Base; + end if; + + -- Note that this result may be negative + + return V; + + else + return + Udigits.Table + (Uints.Table (Arg).Loc + Uints.Table (Arg).Length - 1); + end if; + end Least_Sig_Digit; + + ---------- + -- Mark -- + ---------- + + function Mark return Save_Mark is + begin + return (Save_Uint => Uints.Last, Save_Udigit => Udigits.Last); + end Mark; + + ----------------------- + -- Most_Sig_2_Digits -- + ----------------------- + + procedure Most_Sig_2_Digits + (Left : Uint; + Right : Uint; + Left_Hat : out Int; + Right_Hat : out Int) + is + begin + pragma Assert (Left >= Right); + + if Direct (Left) then + Left_Hat := Direct_Val (Left); + Right_Hat := Direct_Val (Right); + return; + + else + declare + L1 : constant Int := + Udigits.Table (Uints.Table (Left).Loc); + L2 : constant Int := + Udigits.Table (Uints.Table (Left).Loc + 1); + + begin + -- It is not so clear what to return when Arg is negative??? + + Left_Hat := abs (L1) * Base + L2; + end; + end if; + + declare + Length_L : constant Int := Uints.Table (Left).Length; + Length_R : Int; + R1 : Int; + R2 : Int; + T : Int; + + begin + if Direct (Right) then + T := Direct_Val (Left); + R1 := abs (T / Base); + R2 := T rem Base; + Length_R := 2; + + else + R1 := abs (Udigits.Table (Uints.Table (Right).Loc)); + R2 := Udigits.Table (Uints.Table (Right).Loc + 1); + Length_R := Uints.Table (Right).Length; + end if; + + if Length_L = Length_R then + Right_Hat := R1 * Base + R2; + elsif Length_L = Length_R + Int_1 then + Right_Hat := R1; + else + Right_Hat := 0; + end if; + end; + end Most_Sig_2_Digits; + + --------------- + -- N_Digits -- + --------------- + + -- Note: N_Digits returns 1 for No_Uint + + function N_Digits (Input : Uint) return Int is + begin + if Direct (Input) then + if Direct_Val (Input) >= Base then + return 2; + else + return 1; + end if; + + else + return Uints.Table (Input).Length; + end if; + end N_Digits; + + -------------- + -- Num_Bits -- + -------------- + + function Num_Bits (Input : Uint) return Nat is + Bits : Nat; + Num : Nat; + + begin + if UI_Is_In_Int_Range (Input) then + Num := UI_To_Int (Input); + Bits := 0; + + else + Bits := Base_Bits * (Uints.Table (Input).Length - 1); + Num := abs (Udigits.Table (Uints.Table (Input).Loc)); + end if; + + while Types.">" (Num, 0) loop + Num := Num / 2; + Bits := Bits + 1; + end loop; + + return Bits; + end Num_Bits; + + --------- + -- pid -- + --------- + + procedure pid (Input : Uint) is + begin + UI_Write (Input, Decimal); + Write_Eol; + end pid; + + --------- + -- pih -- + --------- + + procedure pih (Input : Uint) is + begin + UI_Write (Input, Hex); + Write_Eol; + end pih; + + ------------- + -- Release -- + ------------- + + procedure Release (M : Save_Mark) is + begin + Uints.Set_Last (Uint'Max (M.Save_Uint, Uints_Min)); + Udigits.Set_Last (Int'Max (M.Save_Udigit, Udigits_Min)); + end Release; + + ---------------------- + -- Release_And_Save -- + ---------------------- + + procedure Release_And_Save (M : Save_Mark; UI : in out Uint) is + begin + if Direct (UI) then + Release (M); + + else + declare + UE_Len : Pos := Uints.Table (UI).Length; + UE_Loc : Int := Uints.Table (UI).Loc; + + UD : Udigits.Table_Type (1 .. UE_Len) := + Udigits.Table (UE_Loc .. UE_Loc + UE_Len - 1); + + begin + Release (M); + + Uints.Increment_Last; + UI := Uints.Last; + + Uints.Table (UI) := (UE_Len, Udigits.Last + 1); + + for J in 1 .. UE_Len loop + Udigits.Increment_Last; + Udigits.Table (Udigits.Last) := UD (J); + end loop; + end; + end if; + end Release_And_Save; + + procedure Release_And_Save (M : Save_Mark; UI1, UI2 : in out Uint) is + begin + if Direct (UI1) then + Release_And_Save (M, UI2); + + elsif Direct (UI2) then + Release_And_Save (M, UI1); + + else + declare + UE1_Len : Pos := Uints.Table (UI1).Length; + UE1_Loc : Int := Uints.Table (UI1).Loc; + + UD1 : Udigits.Table_Type (1 .. UE1_Len) := + Udigits.Table (UE1_Loc .. UE1_Loc + UE1_Len - 1); + + UE2_Len : Pos := Uints.Table (UI2).Length; + UE2_Loc : Int := Uints.Table (UI2).Loc; + + UD2 : Udigits.Table_Type (1 .. UE2_Len) := + Udigits.Table (UE2_Loc .. UE2_Loc + UE2_Len - 1); + + begin + Release (M); + + Uints.Increment_Last; + UI1 := Uints.Last; + + Uints.Table (UI1) := (UE1_Len, Udigits.Last + 1); + + for J in 1 .. UE1_Len loop + Udigits.Increment_Last; + Udigits.Table (Udigits.Last) := UD1 (J); + end loop; + + Uints.Increment_Last; + UI2 := Uints.Last; + + Uints.Table (UI2) := (UE2_Len, Udigits.Last + 1); + + for J in 1 .. UE2_Len loop + Udigits.Increment_Last; + Udigits.Table (Udigits.Last) := UD2 (J); + end loop; + end; + end if; + end Release_And_Save; + + ---------------- + -- Sum_Digits -- + ---------------- + + -- This is done in one pass + + -- Mathematically: assume base congruent to 1 and compute an equivelent + -- integer to Left. + + -- If Sign = -1 return the alternating sum of the "digits". + + -- D1 - D2 + D3 - D4 + D5 . . . + + -- (where D1 is Least Significant Digit) + + -- Mathematically: assume base congruent to -1 and compute an equivelent + -- integer to Left. + + -- This is used in Rem and Base is assumed to be 2 ** 15 + + -- Note: The next two functions are very similar, any style changes made + -- to one should be reflected in both. These would be simpler if we + -- worked base 2 ** 32. + + function Sum_Digits (Left : Uint; Sign : Int) return Int is + begin + pragma Assert (Sign = Int_1 or Sign = Int (-1)); + + -- First try simple case; + + if Direct (Left) then + declare + Tmp_Int : Int := Direct_Val (Left); + + begin + if Tmp_Int >= Base then + Tmp_Int := (Tmp_Int / Base) + + Sign * (Tmp_Int rem Base); + + -- Now Tmp_Int is in [-(Base - 1) .. 2 * (Base - 1)] + + if Tmp_Int >= Base then + + -- Sign must be 1. + + Tmp_Int := (Tmp_Int / Base) + 1; + + end if; + + -- Now Tmp_Int is in [-(Base - 1) .. (Base - 1)] + + end if; + + return Tmp_Int; + end; + + -- Otherwise full circuit is needed + + else + declare + L_Length : Int := N_Digits (Left); + L_Vec : UI_Vector (1 .. L_Length); + Tmp_Int : Int; + Carry : Int; + Alt : Int; + + begin + Init_Operand (Left, L_Vec); + L_Vec (1) := abs L_Vec (1); + Tmp_Int := 0; + Carry := 0; + Alt := 1; + + for J in reverse 1 .. L_Length loop + Tmp_Int := Tmp_Int + Alt * (L_Vec (J) + Carry); + + -- Tmp_Int is now between [-2 * Base + 1 .. 2 * Base - 1], + -- since old Tmp_Int is between [-(Base - 1) .. Base - 1] + -- and L_Vec is in [0 .. Base - 1] and Carry in [-1 .. 1] + + if Tmp_Int >= Base then + Tmp_Int := Tmp_Int - Base; + Carry := 1; + + elsif Tmp_Int <= -Base then + Tmp_Int := Tmp_Int + Base; + Carry := -1; + + else + Carry := 0; + end if; + + -- Tmp_Int is now between [-Base + 1 .. Base - 1] + + Alt := Alt * Sign; + end loop; + + Tmp_Int := Tmp_Int + Alt * Carry; + + -- Tmp_Int is now between [-Base .. Base] + + if Tmp_Int >= Base then + Tmp_Int := Tmp_Int - Base + Alt * Sign * 1; + + elsif Tmp_Int <= -Base then + Tmp_Int := Tmp_Int + Base + Alt * Sign * (-1); + end if; + + -- Now Tmp_Int is in [-(Base - 1) .. (Base - 1)] + + return Tmp_Int; + end; + end if; + end Sum_Digits; + + ----------------------- + -- Sum_Double_Digits -- + ----------------------- + + -- Note: This is used in Rem, Base is assumed to be 2 ** 15 + + function Sum_Double_Digits (Left : Uint; Sign : Int) return Int is + begin + -- First try simple case; + + pragma Assert (Sign = Int_1 or Sign = Int (-1)); + + if Direct (Left) then + return Direct_Val (Left); + + -- Otherwise full circuit is needed + + else + declare + L_Length : Int := N_Digits (Left); + L_Vec : UI_Vector (1 .. L_Length); + Most_Sig_Int : Int; + Least_Sig_Int : Int; + Carry : Int; + J : Int; + Alt : Int; + + begin + Init_Operand (Left, L_Vec); + L_Vec (1) := abs L_Vec (1); + Most_Sig_Int := 0; + Least_Sig_Int := 0; + Carry := 0; + Alt := 1; + J := L_Length; + + while J > Int_1 loop + + Least_Sig_Int := Least_Sig_Int + Alt * (L_Vec (J) + Carry); + + -- Least is in [-2 Base + 1 .. 2 * Base - 1] + -- Since L_Vec in [0 .. Base - 1] and Carry in [-1 .. 1] + -- and old Least in [-Base + 1 .. Base - 1] + + if Least_Sig_Int >= Base then + Least_Sig_Int := Least_Sig_Int - Base; + Carry := 1; + + elsif Least_Sig_Int <= -Base then + Least_Sig_Int := Least_Sig_Int + Base; + Carry := -1; + + else + Carry := 0; + end if; + + -- Least is now in [-Base + 1 .. Base - 1] + + Most_Sig_Int := Most_Sig_Int + Alt * (L_Vec (J - 1) + Carry); + + -- Most is in [-2 Base + 1 .. 2 * Base - 1] + -- Since L_Vec in [0 .. Base - 1] and Carry in [-1 .. 1] + -- and old Most in [-Base + 1 .. Base - 1] + + if Most_Sig_Int >= Base then + Most_Sig_Int := Most_Sig_Int - Base; + Carry := 1; + + elsif Most_Sig_Int <= -Base then + Most_Sig_Int := Most_Sig_Int + Base; + Carry := -1; + else + Carry := 0; + end if; + + -- Most is now in [-Base + 1 .. Base - 1] + + J := J - 2; + Alt := Alt * Sign; + end loop; + + if J = Int_1 then + Least_Sig_Int := Least_Sig_Int + Alt * (L_Vec (J) + Carry); + else + Least_Sig_Int := Least_Sig_Int + Alt * Carry; + end if; + + if Least_Sig_Int >= Base then + Least_Sig_Int := Least_Sig_Int - Base; + Most_Sig_Int := Most_Sig_Int + Alt * 1; + + elsif Least_Sig_Int <= -Base then + Least_Sig_Int := Least_Sig_Int + Base; + Most_Sig_Int := Most_Sig_Int + Alt * (-1); + end if; + + if Most_Sig_Int >= Base then + Most_Sig_Int := Most_Sig_Int - Base; + Alt := Alt * Sign; + Least_Sig_Int := + Least_Sig_Int + Alt * 1; -- cannot overflow again + + elsif Most_Sig_Int <= -Base then + Most_Sig_Int := Most_Sig_Int + Base; + Alt := Alt * Sign; + Least_Sig_Int := + Least_Sig_Int + Alt * (-1); -- cannot overflow again. + end if; + + return Most_Sig_Int * Base + Least_Sig_Int; + end; + end if; + end Sum_Double_Digits; + + --------------- + -- Tree_Read -- + --------------- + + procedure Tree_Read is + begin + Uints.Tree_Read; + Udigits.Tree_Read; + + Tree_Read_Int (Int (Uint_Int_First)); + Tree_Read_Int (Int (Uint_Int_Last)); + Tree_Read_Int (UI_Power_2_Set); + Tree_Read_Int (UI_Power_10_Set); + Tree_Read_Int (Int (Uints_Min)); + Tree_Read_Int (Udigits_Min); + + for J in 0 .. UI_Power_2_Set loop + Tree_Read_Int (Int (UI_Power_2 (J))); + end loop; + + for J in 0 .. UI_Power_10_Set loop + Tree_Read_Int (Int (UI_Power_10 (J))); + end loop; + + end Tree_Read; + + ---------------- + -- Tree_Write -- + ---------------- + + procedure Tree_Write is + begin + Uints.Tree_Write; + Udigits.Tree_Write; + + Tree_Write_Int (Int (Uint_Int_First)); + Tree_Write_Int (Int (Uint_Int_Last)); + Tree_Write_Int (UI_Power_2_Set); + Tree_Write_Int (UI_Power_10_Set); + Tree_Write_Int (Int (Uints_Min)); + Tree_Write_Int (Udigits_Min); + + for J in 0 .. UI_Power_2_Set loop + Tree_Write_Int (Int (UI_Power_2 (J))); + end loop; + + for J in 0 .. UI_Power_10_Set loop + Tree_Write_Int (Int (UI_Power_10 (J))); + end loop; + + end Tree_Write; + + ------------- + -- UI_Abs -- + ------------- + + function UI_Abs (Right : Uint) return Uint is + begin + if Right < Uint_0 then + return -Right; + else + return Right; + end if; + end UI_Abs; + + ------------- + -- UI_Add -- + ------------- + + function UI_Add (Left : Int; Right : Uint) return Uint is + begin + return UI_Add (UI_From_Int (Left), Right); + end UI_Add; + + function UI_Add (Left : Uint; Right : Int) return Uint is + begin + return UI_Add (Left, UI_From_Int (Right)); + end UI_Add; + + function UI_Add (Left : Uint; Right : Uint) return Uint is + begin + -- Simple cases of direct operands and addition of zero + + if Direct (Left) then + if Direct (Right) then + return UI_From_Int (Direct_Val (Left) + Direct_Val (Right)); + + elsif Int (Left) = Int (Uint_0) then + return Right; + end if; + + elsif Direct (Right) and then Int (Right) = Int (Uint_0) then + return Left; + end if; + + -- Otherwise full circuit is needed + + declare + L_Length : Int := N_Digits (Left); + R_Length : Int := N_Digits (Right); + L_Vec : UI_Vector (1 .. L_Length); + R_Vec : UI_Vector (1 .. R_Length); + Sum_Length : Int; + Tmp_Int : Int; + Carry : Int; + Borrow : Int; + X_Bigger : Boolean := False; + Y_Bigger : Boolean := False; + Result_Neg : Boolean := False; + + begin + Init_Operand (Left, L_Vec); + Init_Operand (Right, R_Vec); + + -- At least one of the two operands is in multi-digit form. + -- Calculate the number of digits sufficient to hold result. + + if L_Length > R_Length then + Sum_Length := L_Length + 1; + X_Bigger := True; + else + Sum_Length := R_Length + 1; + if R_Length > L_Length then Y_Bigger := True; end if; + end if; + + -- Make copies of the absolute values of L_Vec and R_Vec into + -- X and Y both with lengths equal to the maximum possibly + -- needed. This makes looping over the digits much simpler. + + declare + X : UI_Vector (1 .. Sum_Length); + Y : UI_Vector (1 .. Sum_Length); + Tmp_UI : UI_Vector (1 .. Sum_Length); + + begin + for J in 1 .. Sum_Length - L_Length loop + X (J) := 0; + end loop; + + X (Sum_Length - L_Length + 1) := abs L_Vec (1); + + for J in 2 .. L_Length loop + X (J + (Sum_Length - L_Length)) := L_Vec (J); + end loop; + + for J in 1 .. Sum_Length - R_Length loop + Y (J) := 0; + end loop; + + Y (Sum_Length - R_Length + 1) := abs R_Vec (1); + + for J in 2 .. R_Length loop + Y (J + (Sum_Length - R_Length)) := R_Vec (J); + end loop; + + if (L_Vec (1) < Int_0) = (R_Vec (1) < Int_0) then + + -- Same sign so just add + + Carry := 0; + for J in reverse 1 .. Sum_Length loop + Tmp_Int := X (J) + Y (J) + Carry; + + if Tmp_Int >= Base then + Tmp_Int := Tmp_Int - Base; + Carry := 1; + else + Carry := 0; + end if; + + X (J) := Tmp_Int; + end loop; + + return Vector_To_Uint (X, L_Vec (1) < Int_0); + + else + -- Find which one has bigger magnitude + + if not (X_Bigger or Y_Bigger) then + for J in L_Vec'Range loop + if abs L_Vec (J) > abs R_Vec (J) then + X_Bigger := True; + exit; + elsif abs R_Vec (J) > abs L_Vec (J) then + Y_Bigger := True; + exit; + end if; + end loop; + end if; + + -- If they have identical magnitude, just return 0, else + -- swap if necessary so that X had the bigger magnitude. + -- Determine if result is negative at this time. + + Result_Neg := False; + + if not (X_Bigger or Y_Bigger) then + return Uint_0; + + elsif Y_Bigger then + if R_Vec (1) < Int_0 then + Result_Neg := True; + end if; + + Tmp_UI := X; + X := Y; + Y := Tmp_UI; + + else + if L_Vec (1) < Int_0 then + Result_Neg := True; + end if; + end if; + + -- Subtract Y from the bigger X + + Borrow := 0; + + for J in reverse 1 .. Sum_Length loop + Tmp_Int := X (J) - Y (J) + Borrow; + + if Tmp_Int < Int_0 then + Tmp_Int := Tmp_Int + Base; + Borrow := -1; + else + Borrow := 0; + end if; + + X (J) := Tmp_Int; + end loop; + + return Vector_To_Uint (X, Result_Neg); + + end if; + end; + end; + end UI_Add; + + -------------------------- + -- UI_Decimal_Digits_Hi -- + -------------------------- + + function UI_Decimal_Digits_Hi (U : Uint) return Nat is + begin + -- The maximum value of a "digit" is 32767, which is 5 decimal + -- digits, so an N_Digit number could take up to 5 times this + -- number of digits. This is certainly too high for large + -- numbers but it is not worth worrying about. + + return 5 * N_Digits (U); + end UI_Decimal_Digits_Hi; + + -------------------------- + -- UI_Decimal_Digits_Lo -- + -------------------------- + + function UI_Decimal_Digits_Lo (U : Uint) return Nat is + begin + -- The maximum value of a "digit" is 32767, which is more than four + -- decimal digits, but not a full five digits. The easily computed + -- minimum number of decimal digits is thus 1 + 4 * the number of + -- digits. This is certainly too low for large numbers but it is + -- not worth worrying about. + + return 1 + 4 * (N_Digits (U) - 1); + end UI_Decimal_Digits_Lo; + + ------------ + -- UI_Div -- + ------------ + + function UI_Div (Left : Int; Right : Uint) return Uint is + begin + return UI_Div (UI_From_Int (Left), Right); + end UI_Div; + + function UI_Div (Left : Uint; Right : Int) return Uint is + begin + return UI_Div (Left, UI_From_Int (Right)); + end UI_Div; + + function UI_Div (Left, Right : Uint) return Uint is + begin + pragma Assert (Right /= Uint_0); + + -- Cases where both operands are represented directly + + if Direct (Left) and then Direct (Right) then + return UI_From_Int (Direct_Val (Left) / Direct_Val (Right)); + end if; + + declare + L_Length : constant Int := N_Digits (Left); + R_Length : constant Int := N_Digits (Right); + Q_Length : constant Int := L_Length - R_Length + 1; + L_Vec : UI_Vector (1 .. L_Length); + R_Vec : UI_Vector (1 .. R_Length); + D : Int; + Remainder : Int; + Tmp_Divisor : Int; + Carry : Int; + Tmp_Int : Int; + Tmp_Dig : Int; + + begin + -- Result is zero if left operand is shorter than right + + if L_Length < R_Length then + return Uint_0; + end if; + + Init_Operand (Left, L_Vec); + Init_Operand (Right, R_Vec); + + -- Case of right operand is single digit. Here we can simply divide + -- each digit of the left operand by the divisor, from most to least + -- significant, carrying the remainder to the next digit (just like + -- ordinary long division by hand). + + if R_Length = Int_1 then + Remainder := 0; + Tmp_Divisor := abs R_Vec (1); + + declare + Quotient : UI_Vector (1 .. L_Length); + + begin + for J in L_Vec'Range loop + Tmp_Int := Remainder * Base + abs L_Vec (J); + Quotient (J) := Tmp_Int / Tmp_Divisor; + Remainder := Tmp_Int rem Tmp_Divisor; + end loop; + + return + Vector_To_Uint + (Quotient, (L_Vec (1) < Int_0 xor R_Vec (1) < Int_0)); + end; + end if; + + -- The possible simple cases have been exhausted. Now turn to the + -- algorithm D from the section of Knuth mentioned at the top of + -- this package. + + Algorithm_D : declare + Dividend : UI_Vector (1 .. L_Length + 1); + Divisor : UI_Vector (1 .. R_Length); + Quotient : UI_Vector (1 .. Q_Length); + Divisor_Dig1 : Int; + Divisor_Dig2 : Int; + Q_Guess : Int; + + begin + -- [ NORMALIZE ] (step D1 in the algorithm). First calculate the + -- scale d, and then multiply Left and Right (u and v in the book) + -- by d to get the dividend and divisor to work with. + + D := Base / (abs R_Vec (1) + 1); + + Dividend (1) := 0; + Dividend (2) := abs L_Vec (1); + + for J in 3 .. L_Length + Int_1 loop + Dividend (J) := L_Vec (J - 1); + end loop; + + Divisor (1) := abs R_Vec (1); + + for J in Int_2 .. R_Length loop + Divisor (J) := R_Vec (J); + end loop; + + if D > Int_1 then + + -- Multiply Dividend by D + + Carry := 0; + for J in reverse Dividend'Range loop + Tmp_Int := Dividend (J) * D + Carry; + Dividend (J) := Tmp_Int rem Base; + Carry := Tmp_Int / Base; + end loop; + + -- Multiply Divisor by d. + + Carry := 0; + for J in reverse Divisor'Range loop + Tmp_Int := Divisor (J) * D + Carry; + Divisor (J) := Tmp_Int rem Base; + Carry := Tmp_Int / Base; + end loop; + end if; + + -- Main loop of long division algorithm. + + Divisor_Dig1 := Divisor (1); + Divisor_Dig2 := Divisor (2); + + for J in Quotient'Range loop + + -- [ CALCULATE Q (hat) ] (step D3 in the algorithm). + + Tmp_Int := Dividend (J) * Base + Dividend (J + 1); + + -- Initial guess + + if Dividend (J) = Divisor_Dig1 then + Q_Guess := Base - 1; + else + Q_Guess := Tmp_Int / Divisor_Dig1; + end if; + + -- Refine the guess + + while Divisor_Dig2 * Q_Guess > + (Tmp_Int - Q_Guess * Divisor_Dig1) * Base + + Dividend (J + 2) + loop + Q_Guess := Q_Guess - 1; + end loop; + + -- [ MULTIPLY & SUBTRACT] (step D4). Q_Guess * Divisor is + -- subtracted from the remaining dividend. + + Carry := 0; + for K in reverse Divisor'Range loop + Tmp_Int := Dividend (J + K) - Q_Guess * Divisor (K) + Carry; + Tmp_Dig := Tmp_Int rem Base; + Carry := Tmp_Int / Base; + + if Tmp_Dig < Int_0 then + Tmp_Dig := Tmp_Dig + Base; + Carry := Carry - 1; + end if; + + Dividend (J + K) := Tmp_Dig; + end loop; + + Dividend (J) := Dividend (J) + Carry; + + -- [ TEST REMAINDER ] & [ ADD BACK ] (steps D5 and D6) + -- Here there is a slight difference from the book: the last + -- carry is always added in above and below (cancelling each + -- other). In fact the dividend going negative is used as + -- the test. + + -- If the Dividend went negative, then Q_Guess was off by + -- one, so it is decremented, and the divisor is added back + -- into the relevant portion of the dividend. + + if Dividend (J) < Int_0 then + Q_Guess := Q_Guess - 1; + + Carry := 0; + for K in reverse Divisor'Range loop + Tmp_Int := Dividend (J + K) + Divisor (K) + Carry; + + if Tmp_Int >= Base then + Tmp_Int := Tmp_Int - Base; + Carry := 1; + else + Carry := 0; + end if; + + Dividend (J + K) := Tmp_Int; + end loop; + + Dividend (J) := Dividend (J) + Carry; + end if; + + -- Finally we can get the next quotient digit + + Quotient (J) := Q_Guess; + end loop; + + return Vector_To_Uint + (Quotient, (L_Vec (1) < Int_0 xor R_Vec (1) < Int_0)); + + end Algorithm_D; + end; + end UI_Div; + + ------------ + -- UI_Eq -- + ------------ + + function UI_Eq (Left : Int; Right : Uint) return Boolean is + begin + return not UI_Ne (UI_From_Int (Left), Right); + end UI_Eq; + + function UI_Eq (Left : Uint; Right : Int) return Boolean is + begin + return not UI_Ne (Left, UI_From_Int (Right)); + end UI_Eq; + + function UI_Eq (Left : Uint; Right : Uint) return Boolean is + begin + return not UI_Ne (Left, Right); + end UI_Eq; + + -------------- + -- UI_Expon -- + -------------- + + function UI_Expon (Left : Int; Right : Uint) return Uint is + begin + return UI_Expon (UI_From_Int (Left), Right); + end UI_Expon; + + function UI_Expon (Left : Uint; Right : Int) return Uint is + begin + return UI_Expon (Left, UI_From_Int (Right)); + end UI_Expon; + + function UI_Expon (Left : Int; Right : Int) return Uint is + begin + return UI_Expon (UI_From_Int (Left), UI_From_Int (Right)); + end UI_Expon; + + function UI_Expon (Left : Uint; Right : Uint) return Uint is + begin + pragma Assert (Right >= Uint_0); + + -- Any value raised to power of 0 is 1 + + if Right = Uint_0 then + return Uint_1; + + -- 0 to any positive power is 0. + + elsif Left = Uint_0 then + return Uint_0; + + -- 1 to any power is 1 + + elsif Left = Uint_1 then + return Uint_1; + + -- Any value raised to power of 1 is that value + + elsif Right = Uint_1 then + return Left; + + -- Cases which can be done by table lookup + + elsif Right <= Uint_64 then + + -- 2 ** N for N in 2 .. 64 + + if Left = Uint_2 then + declare + Right_Int : constant Int := Direct_Val (Right); + + begin + if Right_Int > UI_Power_2_Set then + for J in UI_Power_2_Set + Int_1 .. Right_Int loop + UI_Power_2 (J) := UI_Power_2 (J - Int_1) * Int_2; + Uints_Min := Uints.Last; + Udigits_Min := Udigits.Last; + end loop; + + UI_Power_2_Set := Right_Int; + end if; + + return UI_Power_2 (Right_Int); + end; + + -- 10 ** N for N in 2 .. 64 + + elsif Left = Uint_10 then + declare + Right_Int : constant Int := Direct_Val (Right); + + begin + if Right_Int > UI_Power_10_Set then + for J in UI_Power_10_Set + Int_1 .. Right_Int loop + UI_Power_10 (J) := UI_Power_10 (J - Int_1) * Int (10); + Uints_Min := Uints.Last; + Udigits_Min := Udigits.Last; + end loop; + + UI_Power_10_Set := Right_Int; + end if; + + return UI_Power_10 (Right_Int); + end; + end if; + end if; + + -- If we fall through, then we have the general case (see Knuth 4.6.3) + + declare + N : Uint := Right; + Squares : Uint := Left; + Result : Uint := Uint_1; + M : constant Uintp.Save_Mark := Uintp.Mark; + + begin + loop + if (Least_Sig_Digit (N) mod Int_2) = Int_1 then + Result := Result * Squares; + end if; + + N := N / Uint_2; + exit when N = Uint_0; + Squares := Squares * Squares; + end loop; + + Uintp.Release_And_Save (M, Result); + return Result; + end; + end UI_Expon; + + ------------------ + -- UI_From_Dint -- + ------------------ + + function UI_From_Dint (Input : Dint) return Uint is + begin + + if Dint (Min_Direct) <= Input and then Input <= Dint (Max_Direct) then + return Uint (Dint (Uint_Direct_Bias) + Input); + + -- For values of larger magnitude, compute digits into a vector and + -- call Vector_To_Uint. + + else + declare + Max_For_Dint : constant := 5; + -- Base is defined so that 5 Uint digits is sufficient + -- to hold the largest possible Dint value. + + V : UI_Vector (1 .. Max_For_Dint); + + Temp_Integer : Dint; + + begin + for J in V'Range loop + V (J) := 0; + end loop; + + Temp_Integer := Input; + + for J in reverse V'Range loop + V (J) := Int (abs (Temp_Integer rem Dint (Base))); + Temp_Integer := Temp_Integer / Dint (Base); + end loop; + + return Vector_To_Uint (V, Input < Dint'(0)); + end; + end if; + end UI_From_Dint; + + ----------------- + -- UI_From_Int -- + ----------------- + + function UI_From_Int (Input : Int) return Uint is + begin + + if Min_Direct <= Input and then Input <= Max_Direct then + return Uint (Int (Uint_Direct_Bias) + Input); + + -- For values of larger magnitude, compute digits into a vector and + -- call Vector_To_Uint. + + else + declare + Max_For_Int : constant := 3; + -- Base is defined so that 3 Uint digits is sufficient + -- to hold the largest possible Int value. + + V : UI_Vector (1 .. Max_For_Int); + + Temp_Integer : Int; + + begin + for J in V'Range loop + V (J) := 0; + end loop; + + Temp_Integer := Input; + + for J in reverse V'Range loop + V (J) := abs (Temp_Integer rem Base); + Temp_Integer := Temp_Integer / Base; + end loop; + + return Vector_To_Uint (V, Input < Int_0); + end; + end if; + end UI_From_Int; + + ------------ + -- UI_GCD -- + ------------ + + -- Lehmer's algorithm for GCD. + + -- The idea is to avoid using multiple precision arithmetic wherever + -- possible, substituting Int arithmetic instead. See Knuth volume II, + -- Algorithm L (page 329). + + -- We use the same notation as Knuth (U_Hat standing for the obvious!) + + function UI_GCD (Uin, Vin : Uint) return Uint is + U, V : Uint; + -- Copies of Uin and Vin + + U_Hat, V_Hat : Int; + -- The most Significant digits of U,V + + A, B, C, D, T, Q, Den1, Den2 : Int; + + Tmp_UI : Uint; + Marks : constant Uintp.Save_Mark := Uintp.Mark; + Iterations : Integer := 0; + + begin + pragma Assert (Uin >= Vin); + pragma Assert (Vin >= Uint_0); + + U := Uin; + V := Vin; + + loop + Iterations := Iterations + 1; + + if Direct (V) then + if V = Uint_0 then + return U; + else + return + UI_From_Int (GCD (Direct_Val (V), UI_To_Int (U rem V))); + end if; + end if; + + Most_Sig_2_Digits (U, V, U_Hat, V_Hat); + A := 1; + B := 0; + C := 0; + D := 1; + + loop + -- We might overflow and get division by zero here. This just + -- means we can not take the single precision step + + Den1 := V_Hat + C; + Den2 := V_Hat + D; + exit when (Den1 * Den2) = Int_0; + + -- Compute Q, the trial quotient + + Q := (U_Hat + A) / Den1; + + exit when Q /= ((U_Hat + B) / Den2); + + -- A single precision step Euclid step will give same answer as + -- a multiprecision one. + + T := A - (Q * C); + A := C; + C := T; + + T := B - (Q * D); + B := D; + D := T; + + T := U_Hat - (Q * V_Hat); + U_Hat := V_Hat; + V_Hat := T; + + end loop; + + -- Take a multiprecision Euclid step + + if B = Int_0 then + + -- No single precision steps take a regular Euclid step. + + Tmp_UI := U rem V; + U := V; + V := Tmp_UI; + + else + -- Use prior single precision steps to compute this Euclid step. + + -- Fixed bug 1415-008 spends 80% of its time working on this + -- step. Perhaps we need a special case Int / Uint dot + -- product to speed things up. ??? + + -- Alternatively we could increase the single precision + -- iterations to handle Uint's of some small size ( <5 + -- digits?). Then we would have more iterations on small Uint. + -- Fixed bug 1415-008 only gets 5 (on average) single + -- precision iterations per large iteration. ??? + + Tmp_UI := (UI_From_Int (A) * U) + (UI_From_Int (B) * V); + V := (UI_From_Int (C) * U) + (UI_From_Int (D) * V); + U := Tmp_UI; + end if; + + -- If the operands are very different in magnitude, the loop + -- will generate large amounts of short-lived data, which it is + -- worth removing periodically. + + if Iterations > 100 then + Release_And_Save (Marks, U, V); + Iterations := 0; + end if; + end loop; + end UI_GCD; + + ------------ + -- UI_Ge -- + ------------ + + function UI_Ge (Left : Int; Right : Uint) return Boolean is + begin + return not UI_Lt (UI_From_Int (Left), Right); + end UI_Ge; + + function UI_Ge (Left : Uint; Right : Int) return Boolean is + begin + return not UI_Lt (Left, UI_From_Int (Right)); + end UI_Ge; + + function UI_Ge (Left : Uint; Right : Uint) return Boolean is + begin + return not UI_Lt (Left, Right); + end UI_Ge; + + ------------ + -- UI_Gt -- + ------------ + + function UI_Gt (Left : Int; Right : Uint) return Boolean is + begin + return UI_Lt (Right, UI_From_Int (Left)); + end UI_Gt; + + function UI_Gt (Left : Uint; Right : Int) return Boolean is + begin + return UI_Lt (UI_From_Int (Right), Left); + end UI_Gt; + + function UI_Gt (Left : Uint; Right : Uint) return Boolean is + begin + return UI_Lt (Right, Left); + end UI_Gt; + + --------------- + -- UI_Image -- + --------------- + + procedure UI_Image (Input : Uint; Format : UI_Format := Auto) is + begin + Image_Out (Input, True, Format); + end UI_Image; + + ------------------------- + -- UI_Is_In_Int_Range -- + ------------------------- + + function UI_Is_In_Int_Range (Input : Uint) return Boolean is + begin + -- Make sure we don't get called before Initialize + + pragma Assert (Uint_Int_First /= Uint_0); + + if Direct (Input) then + return True; + else + return Input >= Uint_Int_First + and then Input <= Uint_Int_Last; + end if; + end UI_Is_In_Int_Range; + + ------------ + -- UI_Le -- + ------------ + + function UI_Le (Left : Int; Right : Uint) return Boolean is + begin + return not UI_Lt (Right, UI_From_Int (Left)); + end UI_Le; + + function UI_Le (Left : Uint; Right : Int) return Boolean is + begin + return not UI_Lt (UI_From_Int (Right), Left); + end UI_Le; + + function UI_Le (Left : Uint; Right : Uint) return Boolean is + begin + return not UI_Lt (Right, Left); + end UI_Le; + + ------------ + -- UI_Lt -- + ------------ + + function UI_Lt (Left : Int; Right : Uint) return Boolean is + begin + return UI_Lt (UI_From_Int (Left), Right); + end UI_Lt; + + function UI_Lt (Left : Uint; Right : Int) return Boolean is + begin + return UI_Lt (Left, UI_From_Int (Right)); + end UI_Lt; + + function UI_Lt (Left : Uint; Right : Uint) return Boolean is + begin + -- Quick processing for identical arguments + + if Int (Left) = Int (Right) then + return False; + + -- Quick processing for both arguments directly represented + + elsif Direct (Left) and then Direct (Right) then + return Int (Left) < Int (Right); + + -- At least one argument is more than one digit long + + else + declare + L_Length : constant Int := N_Digits (Left); + R_Length : constant Int := N_Digits (Right); + + L_Vec : UI_Vector (1 .. L_Length); + R_Vec : UI_Vector (1 .. R_Length); + + begin + Init_Operand (Left, L_Vec); + Init_Operand (Right, R_Vec); + + if L_Vec (1) < Int_0 then + + -- First argument negative, second argument non-negative + + if R_Vec (1) >= Int_0 then + return True; + + -- Both arguments negative + + else + if L_Length /= R_Length then + return L_Length > R_Length; + + elsif L_Vec (1) /= R_Vec (1) then + return L_Vec (1) < R_Vec (1); + + else + for J in 2 .. L_Vec'Last loop + if L_Vec (J) /= R_Vec (J) then + return L_Vec (J) > R_Vec (J); + end if; + end loop; + + return False; + end if; + end if; + + else + -- First argument non-negative, second argument negative + + if R_Vec (1) < Int_0 then + return False; + + -- Both arguments non-negative + + else + if L_Length /= R_Length then + return L_Length < R_Length; + else + for J in L_Vec'Range loop + if L_Vec (J) /= R_Vec (J) then + return L_Vec (J) < R_Vec (J); + end if; + end loop; + + return False; + end if; + end if; + end if; + end; + end if; + end UI_Lt; + + ------------ + -- UI_Max -- + ------------ + + function UI_Max (Left : Int; Right : Uint) return Uint is + begin + return UI_Max (UI_From_Int (Left), Right); + end UI_Max; + + function UI_Max (Left : Uint; Right : Int) return Uint is + begin + return UI_Max (Left, UI_From_Int (Right)); + end UI_Max; + + function UI_Max (Left : Uint; Right : Uint) return Uint is + begin + if Left >= Right then + return Left; + else + return Right; + end if; + end UI_Max; + + ------------ + -- UI_Min -- + ------------ + + function UI_Min (Left : Int; Right : Uint) return Uint is + begin + return UI_Min (UI_From_Int (Left), Right); + end UI_Min; + + function UI_Min (Left : Uint; Right : Int) return Uint is + begin + return UI_Min (Left, UI_From_Int (Right)); + end UI_Min; + + function UI_Min (Left : Uint; Right : Uint) return Uint is + begin + if Left <= Right then + return Left; + else + return Right; + end if; + end UI_Min; + + ------------- + -- UI_Mod -- + ------------- + + function UI_Mod (Left : Int; Right : Uint) return Uint is + begin + return UI_Mod (UI_From_Int (Left), Right); + end UI_Mod; + + function UI_Mod (Left : Uint; Right : Int) return Uint is + begin + return UI_Mod (Left, UI_From_Int (Right)); + end UI_Mod; + + function UI_Mod (Left : Uint; Right : Uint) return Uint is + Urem : constant Uint := Left rem Right; + + begin + if (Left < Uint_0) = (Right < Uint_0) + or else Urem = Uint_0 + then + return Urem; + else + return Right + Urem; + end if; + end UI_Mod; + + ------------ + -- UI_Mul -- + ------------ + + function UI_Mul (Left : Int; Right : Uint) return Uint is + begin + return UI_Mul (UI_From_Int (Left), Right); + end UI_Mul; + + function UI_Mul (Left : Uint; Right : Int) return Uint is + begin + return UI_Mul (Left, UI_From_Int (Right)); + end UI_Mul; + + function UI_Mul (Left : Uint; Right : Uint) return Uint is + begin + -- Simple case of single length operands + + if Direct (Left) and then Direct (Right) then + return + UI_From_Dint + (Dint (Direct_Val (Left)) * Dint (Direct_Val (Right))); + end if; + + -- Otherwise we have the general case (Algorithm M in Knuth) + + declare + L_Length : constant Int := N_Digits (Left); + R_Length : constant Int := N_Digits (Right); + L_Vec : UI_Vector (1 .. L_Length); + R_Vec : UI_Vector (1 .. R_Length); + Neg : Boolean; + + begin + Init_Operand (Left, L_Vec); + Init_Operand (Right, R_Vec); + Neg := (L_Vec (1) < Int_0) xor (R_Vec (1) < Int_0); + L_Vec (1) := abs (L_Vec (1)); + R_Vec (1) := abs (R_Vec (1)); + + Algorithm_M : declare + Product : UI_Vector (1 .. L_Length + R_Length); + Tmp_Sum : Int; + Carry : Int; + + begin + for J in Product'Range loop + Product (J) := 0; + end loop; + + for J in reverse R_Vec'Range loop + Carry := 0; + for K in reverse L_Vec'Range loop + Tmp_Sum := + L_Vec (K) * R_Vec (J) + Product (J + K) + Carry; + Product (J + K) := Tmp_Sum rem Base; + Carry := Tmp_Sum / Base; + end loop; + + Product (J) := Carry; + end loop; + + return Vector_To_Uint (Product, Neg); + end Algorithm_M; + end; + end UI_Mul; + + ------------ + -- UI_Ne -- + ------------ + + function UI_Ne (Left : Int; Right : Uint) return Boolean is + begin + return UI_Ne (UI_From_Int (Left), Right); + end UI_Ne; + + function UI_Ne (Left : Uint; Right : Int) return Boolean is + begin + return UI_Ne (Left, UI_From_Int (Right)); + end UI_Ne; + + function UI_Ne (Left : Uint; Right : Uint) return Boolean is + begin + -- Quick processing for identical arguments. Note that this takes + -- care of the case of two No_Uint arguments. + + if Int (Left) = Int (Right) then + return False; + end if; + + -- See if left operand directly represented + + if Direct (Left) then + + -- If right operand directly represented then compare + + if Direct (Right) then + return Int (Left) /= Int (Right); + + -- Left operand directly represented, right not, must be unequal + + else + return True; + end if; + + -- Right operand directly represented, left not, must be unequal + + elsif Direct (Right) then + return True; + end if; + + -- Otherwise both multi-word, do comparison + + declare + Size : constant Int := N_Digits (Left); + Left_Loc : Int; + Right_Loc : Int; + + begin + if Size /= N_Digits (Right) then + return True; + end if; + + Left_Loc := Uints.Table (Left).Loc; + Right_Loc := Uints.Table (Right).Loc; + + for J in Int_0 .. Size - Int_1 loop + if Udigits.Table (Left_Loc + J) /= + Udigits.Table (Right_Loc + J) + then + return True; + end if; + end loop; + + return False; + end; + end UI_Ne; + + ---------------- + -- UI_Negate -- + ---------------- + + function UI_Negate (Right : Uint) return Uint is + begin + -- Case where input is directly represented. Note that since the + -- range of Direct values is non-symmetrical, the result may not + -- be directly represented, this is taken care of in UI_From_Int. + + if Direct (Right) then + return UI_From_Int (-Direct_Val (Right)); + + -- Full processing for multi-digit case. Note that we cannot just + -- copy the value to the end of the table negating the first digit, + -- since the range of Direct values is non-symmetrical, so we can + -- have a negative value that is not Direct whose negation can be + -- represented directly. + + else + declare + R_Length : constant Int := N_Digits (Right); + R_Vec : UI_Vector (1 .. R_Length); + Neg : Boolean; + + begin + Init_Operand (Right, R_Vec); + Neg := R_Vec (1) > Int_0; + R_Vec (1) := abs R_Vec (1); + return Vector_To_Uint (R_Vec, Neg); + end; + end if; + end UI_Negate; + + ------------- + -- UI_Rem -- + ------------- + + function UI_Rem (Left : Int; Right : Uint) return Uint is + begin + return UI_Rem (UI_From_Int (Left), Right); + end UI_Rem; + + function UI_Rem (Left : Uint; Right : Int) return Uint is + begin + return UI_Rem (Left, UI_From_Int (Right)); + end UI_Rem; + + function UI_Rem (Left, Right : Uint) return Uint is + Sign : Int; + Tmp : Int; + + subtype Int1_12 is Integer range 1 .. 12; + + begin + pragma Assert (Right /= Uint_0); + + if Direct (Right) then + if Direct (Left) then + return UI_From_Int (Direct_Val (Left) rem Direct_Val (Right)); + + else + -- Special cases when Right is less than 13 and Left is larger + -- larger than one digit. All of these algorithms depend on the + -- base being 2 ** 15 We work with Abs (Left) and Abs(Right) + -- then multiply result by Sign (Left) + + if (Right <= Uint_12) and then (Right >= Uint_Minus_12) then + + if (Left < Uint_0) then + Sign := -1; + else + Sign := 1; + end if; + + -- All cases are listed, grouped by mathematical method + -- It is not inefficient to do have this case list out + -- of order since GCC sorts the cases we list. + + case Int1_12 (abs (Direct_Val (Right))) is + + when 1 => + return Uint_0; + + -- Powers of two are simple AND's with LS Left Digit + -- GCC will recognise these constants as powers of 2 + -- and replace the rem with simpler operations where + -- possible. + + -- Least_Sig_Digit might return Negative numbers. + + when 2 => + return UI_From_Int ( + Sign * (Least_Sig_Digit (Left) mod 2)); + + when 4 => + return UI_From_Int ( + Sign * (Least_Sig_Digit (Left) mod 4)); + + when 8 => + return UI_From_Int ( + Sign * (Least_Sig_Digit (Left) mod 8)); + + -- Some number theoretical tricks: + + -- If B Rem Right = 1 then + -- Left Rem Right = Sum_Of_Digits_Base_B (Left) Rem Right + + -- Note: 2^32 mod 3 = 1 + + when 3 => + return UI_From_Int ( + Sign * (Sum_Double_Digits (Left, 1) rem Int (3))); + + -- Note: 2^15 mod 7 = 1 + + when 7 => + return UI_From_Int ( + Sign * (Sum_Digits (Left, 1) rem Int (7))); + + -- Note: 2^32 mod 5 = -1 + -- Alternating sums might be negative, but rem is always + -- positive hence we must use mod here. + + when 5 => + Tmp := Sum_Double_Digits (Left, -1) mod Int (5); + return UI_From_Int (Sign * Tmp); + + -- Note: 2^15 mod 9 = -1 + -- Alternating sums might be negative, but rem is always + -- positive hence we must use mod here. + + when 9 => + Tmp := Sum_Digits (Left, -1) mod Int (9); + return UI_From_Int (Sign * Tmp); + + -- Note: 2^15 mod 11 = -1 + -- Alternating sums might be negative, but rem is always + -- positive hence we must use mod here. + + when 11 => + Tmp := Sum_Digits (Left, -1) mod Int (11); + return UI_From_Int (Sign * Tmp); + + -- Now resort to Chinese Remainder theorem + -- to reduce 6, 10, 12 to previous special cases + + -- There is no reason we could not add more cases + -- like these if it proves useful. + + -- Perhaps we should go up to 16, however + -- I have no "trick" for 13. + + -- To find u mod m we: + -- Pick m1, m2 S.T. + -- GCD(m1, m2) = 1 AND m = (m1 * m2). + -- Next we pick (Basis) M1, M2 small S.T. + -- (M1 mod m1) = (M2 mod m2) = 1 AND + -- (M1 mod m2) = (M2 mod m1) = 0 + + -- So u mod m = (u1 * M1 + u2 * M2) mod m + -- Where u1 = (u mod m1) AND u2 = (u mod m2); + -- Under typical circumstances the last mod m + -- can be done with a (possible) single subtraction. + + -- m1 = 2; m2 = 3; M1 = 3; M2 = 4; + + when 6 => + Tmp := 3 * (Least_Sig_Digit (Left) rem 2) + + 4 * (Sum_Double_Digits (Left, 1) rem 3); + return UI_From_Int (Sign * (Tmp rem 6)); + + -- m1 = 2; m2 = 5; M1 = 5; M2 = 6; + + when 10 => + Tmp := 5 * (Least_Sig_Digit (Left) rem 2) + + 6 * (Sum_Double_Digits (Left, -1) mod 5); + return UI_From_Int (Sign * (Tmp rem 10)); + + -- m1 = 3; m2 = 4; M1 = 4; M2 = 9; + + when 12 => + Tmp := 4 * (Sum_Double_Digits (Left, 1) rem 3) + + 9 * (Least_Sig_Digit (Left) rem 4); + return UI_From_Int (Sign * (Tmp rem 12)); + end case; + + end if; + + -- Else fall through to general case. + + -- ???This needs to be improved. We have the Rem when we do the + -- Div. Div throws it away! + + -- The special case Length (Left) = Length(right) = 1 in Div + -- looks slow. It uses UI_To_Int when Int should suffice. ??? + end if; + end if; + + return Left - (Left / Right) * Right; + end UI_Rem; + + ------------ + -- UI_Sub -- + ------------ + + function UI_Sub (Left : Int; Right : Uint) return Uint is + begin + return UI_Add (Left, -Right); + end UI_Sub; + + function UI_Sub (Left : Uint; Right : Int) return Uint is + begin + return UI_Add (Left, -Right); + end UI_Sub; + + function UI_Sub (Left : Uint; Right : Uint) return Uint is + begin + if Direct (Left) and then Direct (Right) then + return UI_From_Int (Direct_Val (Left) - Direct_Val (Right)); + else + return UI_Add (Left, -Right); + end if; + end UI_Sub; + + ---------------- + -- UI_To_Int -- + ---------------- + + function UI_To_Int (Input : Uint) return Int is + begin + if Direct (Input) then + return Direct_Val (Input); + + -- Case of input is more than one digit + + else + declare + In_Length : constant Int := N_Digits (Input); + In_Vec : UI_Vector (1 .. In_Length); + Ret_Int : Int; + + begin + -- Uints of more than one digit could be outside the range for + -- Ints. Caller should have checked for this if not certain. + -- Fatal error to attempt to convert from value outside Int'Range. + + pragma Assert (UI_Is_In_Int_Range (Input)); + + -- Otherwise, proceed ahead, we are OK + + Init_Operand (Input, In_Vec); + Ret_Int := 0; + + -- Calculate -|Input| and then negates if value is positive. + -- This handles our current definition of Int (based on + -- 2s complement). Is it secure enough? + + for Idx in In_Vec'Range loop + Ret_Int := Ret_Int * Base - abs In_Vec (Idx); + end loop; + + if In_Vec (1) < Int_0 then + return Ret_Int; + else + return -Ret_Int; + end if; + end; + end if; + end UI_To_Int; + + -------------- + -- UI_Write -- + -------------- + + procedure UI_Write (Input : Uint; Format : UI_Format := Auto) is + begin + Image_Out (Input, False, Format); + end UI_Write; + + --------------------- + -- Vector_To_Uint -- + --------------------- + + function Vector_To_Uint + (In_Vec : UI_Vector; + Negative : Boolean) + return Uint + is + Size : Int; + Val : Int; + + begin + -- The vector can contain leading zeros. These are not stored in the + -- table, so loop through the vector looking for first non-zero digit + + for J in In_Vec'Range loop + if In_Vec (J) /= Int_0 then + + -- The length of the value is the length of the rest of the vector + + Size := In_Vec'Last - J + 1; + + -- One digit value can always be represented directly + + if Size = Int_1 then + if Negative then + return Uint (Int (Uint_Direct_Bias) - In_Vec (J)); + else + return Uint (Int (Uint_Direct_Bias) + In_Vec (J)); + end if; + + -- Positive two digit values may be in direct representation range + + elsif Size = Int_2 and then not Negative then + Val := In_Vec (J) * Base + In_Vec (J + 1); + + if Val <= Max_Direct then + return Uint (Int (Uint_Direct_Bias) + Val); + end if; + end if; + + -- The value is outside the direct representation range and + -- must therefore be stored in the table. Expand the table + -- to contain the count and tigis. The index of the new table + -- entry will be returned as the result. + + Uints.Increment_Last; + Uints.Table (Uints.Last).Length := Size; + Uints.Table (Uints.Last).Loc := Udigits.Last + 1; + + Udigits.Increment_Last; + + if Negative then + Udigits.Table (Udigits.Last) := -In_Vec (J); + else + Udigits.Table (Udigits.Last) := +In_Vec (J); + end if; + + for K in 2 .. Size loop + Udigits.Increment_Last; + Udigits.Table (Udigits.Last) := In_Vec (J + K - 1); + end loop; + + return Uints.Last; + end if; + end loop; + + -- Dropped through loop only if vector contained all zeros + + return Uint_0; + end Vector_To_Uint; + +end Uintp; diff --git a/gcc/ada/uintp.ads b/gcc/ada/uintp.ads new file mode 100644 index 00000000000..1cfb79ae17b --- /dev/null +++ b/gcc/ada/uintp.ads @@ -0,0 +1,505 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U I N T P -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.58 $ +-- -- +-- Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Support for universal integer arithmetic + +-- WARNING: There is a C version of this package. Any changes to this +-- source file must be properly reflected in the C header file sinfo.h + +with Alloc; +with Table; +with Types; use Types; + +package Uintp is + + ------------------------------------------------- + -- Basic Types and Constants for Uintp Package -- + ------------------------------------------------- + + type Uint is private; + -- The basic universal integer type + + No_Uint : constant Uint; + -- A constant value indicating a missing or unset Uint value + + Uint_0 : constant Uint; + Uint_1 : constant Uint; + Uint_2 : constant Uint; + Uint_3 : constant Uint; + Uint_4 : constant Uint; + Uint_5 : constant Uint; + Uint_6 : constant Uint; + Uint_7 : constant Uint; + Uint_8 : constant Uint; + Uint_9 : constant Uint; + Uint_10 : constant Uint; + Uint_12 : constant Uint; + Uint_15 : constant Uint; + Uint_16 : constant Uint; + Uint_24 : constant Uint; + Uint_32 : constant Uint; + Uint_63 : constant Uint; + Uint_64 : constant Uint; + Uint_128 : constant Uint; + + Uint_Minus_1 : constant Uint; + Uint_Minus_2 : constant Uint; + Uint_Minus_3 : constant Uint; + Uint_Minus_4 : constant Uint; + Uint_Minus_5 : constant Uint; + Uint_Minus_6 : constant Uint; + Uint_Minus_7 : constant Uint; + Uint_Minus_8 : constant Uint; + Uint_Minus_9 : constant Uint; + Uint_Minus_12 : constant Uint; + Uint_Minus_128 : constant Uint; + + ----------------- + -- Subprograms -- + ----------------- + + procedure Initialize; + -- Initialize Uint tables. Note that Initialize must not be called if + -- Tree_Read is used. Note also that there is no lock routine in this + -- unit, these are among the few tables that can be expanded during + -- gigi processing. + + procedure Tree_Read; + -- Initializes internal tables from current tree file using Tree_Read. + -- Note that Initialize should not be called if Tree_Read is used. + -- Tree_Read includes all necessary initialization. + + procedure Tree_Write; + -- Writes out internal tables to current tree file using Tree_Write. + + function UI_Abs (Right : Uint) return Uint; + pragma Inline (UI_Abs); + -- Returns abs function of universal integer. + + function UI_Add (Left : Uint; Right : Uint) return Uint; + function UI_Add (Left : Int; Right : Uint) return Uint; + function UI_Add (Left : Uint; Right : Int) return Uint; + -- Returns sum of two integer values. + + function UI_Decimal_Digits_Hi (U : Uint) return Nat; + -- Returns an estimate of the number of decimal digits required to + -- represent the absolute value of U. This estimate is correct or high, + -- i.e. it never returns a value that is too low. The accuracy of the + -- estimate affects only the effectiveness of comparison optimizations + -- in Urealp. + + function UI_Decimal_Digits_Lo (U : Uint) return Nat; + -- Returns an estimate of the number of decimal digits required to + -- represent the absolute value of U. This estimate is correct or low, + -- i.e. it never returns a value that is too high. The accuracy of the + -- estimate affects only the effectiveness of comparison optimizations + -- in Urealp. + + function UI_Div (Left : Uint; Right : Uint) return Uint; + function UI_Div (Left : Int; Right : Uint) return Uint; + function UI_Div (Left : Uint; Right : Int) return Uint; + -- Returns quotient of two integer values. Fatal error if Right = 0 + + function UI_Eq (Left : Uint; Right : Uint) return Boolean; + function UI_Eq (Left : Int; Right : Uint) return Boolean; + function UI_Eq (Left : Uint; Right : Int) return Boolean; + pragma Inline (UI_Eq); + -- Compares integer values for equality. + + function UI_Expon (Left : Uint; Right : Uint) return Uint; + function UI_Expon (Left : Int; Right : Uint) return Uint; + function UI_Expon (Left : Uint; Right : Int) return Uint; + function UI_Expon (Left : Int; Right : Int) return Uint; + -- Returns result of exponentiating two integer values + -- Fatal error if Right is negative. + + function UI_GCD (Uin, Vin : Uint) return Uint; + -- Computes GCD of input values. Assumes Uin >= Vin >= 0. + + function UI_Ge (Left : Uint; Right : Uint) return Boolean; + function UI_Ge (Left : Int; Right : Uint) return Boolean; + function UI_Ge (Left : Uint; Right : Int) return Boolean; + pragma Inline (UI_Ge); + -- Compares integer values for greater than or equal. + + function UI_Gt (Left : Uint; Right : Uint) return Boolean; + function UI_Gt (Left : Int; Right : Uint) return Boolean; + function UI_Gt (Left : Uint; Right : Int) return Boolean; + pragma Inline (UI_Gt); + -- Compares integer values for greater than. + + function UI_Is_In_Int_Range (Input : Uint) return Boolean; + pragma Inline (UI_Is_In_Int_Range); + -- Determines if universal integer is in Int range. + + function UI_Le (Left : Uint; Right : Uint) return Boolean; + function UI_Le (Left : Int; Right : Uint) return Boolean; + function UI_Le (Left : Uint; Right : Int) return Boolean; + pragma Inline (UI_Le); + -- Compares integer values for less than or equal. + + function UI_Lt (Left : Uint; Right : Uint) return Boolean; + function UI_Lt (Left : Int; Right : Uint) return Boolean; + function UI_Lt (Left : Uint; Right : Int) return Boolean; + -- Compares integer values for less than. + + function UI_Max (Left : Uint; Right : Uint) return Uint; + function UI_Max (Left : Int; Right : Uint) return Uint; + function UI_Max (Left : Uint; Right : Int) return Uint; + -- Returns maximum of two integer values + + function UI_Min (Left : Uint; Right : Uint) return Uint; + function UI_Min (Left : Int; Right : Uint) return Uint; + function UI_Min (Left : Uint; Right : Int) return Uint; + -- Returns minimum of two integer values. + + function UI_Mod (Left : Uint; Right : Uint) return Uint; + function UI_Mod (Left : Int; Right : Uint) return Uint; + function UI_Mod (Left : Uint; Right : Int) return Uint; + pragma Inline (UI_Mod); + -- Returns mod function of two integer values. + + function UI_Mul (Left : Uint; Right : Uint) return Uint; + function UI_Mul (Left : Int; Right : Uint) return Uint; + function UI_Mul (Left : Uint; Right : Int) return Uint; + -- Returns product of two integer values + + function UI_Ne (Left : Uint; Right : Uint) return Boolean; + function UI_Ne (Left : Int; Right : Uint) return Boolean; + function UI_Ne (Left : Uint; Right : Int) return Boolean; + pragma Inline (UI_Ne); + -- Compares integer values for inequality. + + function UI_Negate (Right : Uint) return Uint; + pragma Inline (UI_Negate); + -- Returns negative of universal integer. + + function UI_Rem (Left : Uint; Right : Uint) return Uint; + function UI_Rem (Left : Int; Right : Uint) return Uint; + function UI_Rem (Left : Uint; Right : Int) return Uint; + -- Returns rem of two integer values. + + function UI_Sub (Left : Uint; Right : Uint) return Uint; + function UI_Sub (Left : Int; Right : Uint) return Uint; + function UI_Sub (Left : Uint; Right : Int) return Uint; + pragma Inline (UI_Sub); + -- Returns difference of two integer values + + function UI_From_Dint (Input : Dint) return Uint; + -- Converts Dint value to universal integer form. + + function UI_From_Int (Input : Int) return Uint; + -- Converts Int value to universal integer form. + + function UI_To_Int (Input : Uint) return Int; + -- Converts universal integer value to Int. Fatal error + -- if value is not in appropriate range. + + function Num_Bits (Input : Uint) return Nat; + -- Approximate number of binary bits in given universal integer. + -- This function is used for capacity checks, and it can be one + -- bit off without affecting its usage. + + --------------------- + -- Output Routines -- + --------------------- + + type UI_Format is (Hex, Decimal, Auto); + -- Used to determine whether UI_Image/UI_Write output is in hexadecimal + -- or decimal format. Auto, the default setting, lets the routine make + -- a decision based on the value. + + UI_Image_Max : constant := 32; + UI_Image_Buffer : String (1 .. UI_Image_Max); + UI_Image_Length : Natural; + -- Buffer used for UI_Image as described below + + procedure UI_Image (Input : Uint; Format : UI_Format := Auto); + -- Places a representation of Uint, consisting of a possible minus sign, + -- followed by the value in UI_Image_Buffer. The form of the value is an + -- integer literal in either decimal (no base) or hexadecimal (base 16) + -- format. If Hex is True on entry, then hex mode is forced, otherwise + -- UI_Image makes a guess at which output format is more convenient. The + -- value must fit in UI_Image_Buffer. If necessary, the result is an + -- approximation of the proper value, using an exponential format. The + -- image of No_Uint is output as a single question mark. + + procedure UI_Write (Input : Uint; Format : UI_Format := Auto); + -- Writes a representation of Uint, consisting of a possible minus sign, + -- followed by the value to the output file. The form of the value is an + -- integer literal in either decimal (no base) or hexadecimal (base 16) + -- format as appropriate. UI_Format shows which format to use. Auto, + -- the default, asks UI_Write to make a guess at which output format + -- will be more convenient to read. + + procedure pid (Input : Uint); + -- Writes representation of Uint in decimal with a terminating line + -- return. This is intended for use from the debugger. + + procedure pih (Input : Uint); + -- Writes representation of Uint in hex with a terminating line return. + -- This is intended for use from the debugger. + + ------------------------ + -- Operator Renamings -- + ------------------------ + + function "+" (Left : Uint; Right : Uint) return Uint renames UI_Add; + function "+" (Left : Int; Right : Uint) return Uint renames UI_Add; + function "+" (Left : Uint; Right : Int) return Uint renames UI_Add; + + function "/" (Left : Uint; Right : Uint) return Uint renames UI_Div; + function "/" (Left : Int; Right : Uint) return Uint renames UI_Div; + function "/" (Left : Uint; Right : Int) return Uint renames UI_Div; + + function "*" (Left : Uint; Right : Uint) return Uint renames UI_Mul; + function "*" (Left : Int; Right : Uint) return Uint renames UI_Mul; + function "*" (Left : Uint; Right : Int) return Uint renames UI_Mul; + + function "-" (Left : Uint; Right : Uint) return Uint renames UI_Sub; + function "-" (Left : Int; Right : Uint) return Uint renames UI_Sub; + function "-" (Left : Uint; Right : Int) return Uint renames UI_Sub; + + function "**" (Left : Uint; Right : Uint) return Uint renames UI_Expon; + function "**" (Left : Uint; Right : Int) return Uint renames UI_Expon; + function "**" (Left : Int; Right : Uint) return Uint renames UI_Expon; + function "**" (Left : Int; Right : Int) return Uint renames UI_Expon; + + function "abs" (Real : Uint) return Uint renames UI_Abs; + + function "mod" (Left : Uint; Right : Uint) return Uint renames UI_Mod; + function "mod" (Left : Int; Right : Uint) return Uint renames UI_Mod; + function "mod" (Left : Uint; Right : Int) return Uint renames UI_Mod; + + function "rem" (Left : Uint; Right : Uint) return Uint renames UI_Rem; + function "rem" (Left : Int; Right : Uint) return Uint renames UI_Rem; + function "rem" (Left : Uint; Right : Int) return Uint renames UI_Rem; + + function "-" (Real : Uint) return Uint renames UI_Negate; + + function "=" (Left : Uint; Right : Uint) return Boolean renames UI_Eq; + function "=" (Left : Int; Right : Uint) return Boolean renames UI_Eq; + function "=" (Left : Uint; Right : Int) return Boolean renames UI_Eq; + + function ">=" (Left : Uint; Right : Uint) return Boolean renames UI_Ge; + function ">=" (Left : Int; Right : Uint) return Boolean renames UI_Ge; + function ">=" (Left : Uint; Right : Int) return Boolean renames UI_Ge; + + function ">" (Left : Uint; Right : Uint) return Boolean renames UI_Gt; + function ">" (Left : Int; Right : Uint) return Boolean renames UI_Gt; + function ">" (Left : Uint; Right : Int) return Boolean renames UI_Gt; + + function "<=" (Left : Uint; Right : Uint) return Boolean renames UI_Le; + function "<=" (Left : Int; Right : Uint) return Boolean renames UI_Le; + function "<=" (Left : Uint; Right : Int) return Boolean renames UI_Le; + + function "<" (Left : Uint; Right : Uint) return Boolean renames UI_Lt; + function "<" (Left : Int; Right : Uint) return Boolean renames UI_Lt; + function "<" (Left : Uint; Right : Int) return Boolean renames UI_Lt; + + ----------------------------- + -- Mark/Release Processing -- + ----------------------------- + + -- The space used by Uint data is not automatically reclaimed. However, + -- a mark-release regime is implemented which allows storage to be + -- released back to a previously noted mark. This is used for example + -- when doing comparisons, where only intermediate results get stored + -- that do not need to be saved for future use. + + type Save_Mark is private; + + function Mark return Save_Mark; + -- Note mark point for future release + + procedure Release (M : Save_Mark); + -- Release storage allocated since mark was noted + + procedure Release_And_Save (M : Save_Mark; UI : in out Uint); + -- Like Release, except that the given Uint value (which is typically + -- among the data being released) is recopied after the release, so + -- that it is the most recent item, and UI is updated to point to + -- its copied location. + + procedure Release_And_Save (M : Save_Mark; UI1, UI2 : in out Uint); + -- Like Release, except that the given Uint values (which are typically + -- among the data being released) are recopied after the release, so + -- that they are the most recent items, and UI1 and UI2 are updated if + -- necessary to point to the copied locations. This routine is careful + -- to do things in the right order, so that the values do not clobber + -- one another. + + ----------------------------------- + -- Representation of Uint Values -- + ----------------------------------- + +private + + type Uint is new Int range Uint_Low_Bound .. Uint_High_Bound; + for Uint'Size use 32; + + No_Uint : constant Uint := Uint (Uint_Low_Bound); + + -- Uint values are represented as multiple precision integers stored in + -- a multi-digit format using Base as the base. This value is chosen so + -- that the product Base*Base is within the range of allowed Int values. + + -- Base is defined to allow efficient execution of the primitive + -- operations (a0, b0, c0) defined in the section "The Classical + -- Algorithms" (sec. 4.3.1) of Donald Knuth's "The Art of Computer + -- Programming", Vol. 2. These algorithms are used in this package. + + Base_Bits : constant := 15; + -- Number of bits in base value + + Base : constant Int := 2 ** Base_Bits; + + -- Values in the range -(Base+1) .. maxdirect are encoded directly as + -- Uint values by adding a bias value. The value of maxdirect is chosen + -- so that a directly represented number always fits in two digits when + -- represented in base format. + + Min_Direct : constant Int := -(Base - 1); + Max_Direct : constant Int := (Base - 1) * (Base - 1); + + -- The following values define the bias used to store Uint values which + -- are in this range, as well as the biased values for the first and + -- last values in this range. We use a new derived type for these + -- constants to avoid accidental use of Uint arithmetic on these + -- values, which is never correct. + + type Ctrl is range Int'First .. Int'Last; + + Uint_Direct_Bias : constant Ctrl := Ctrl (Uint_Low_Bound) + Ctrl (Base); + Uint_Direct_First : constant Ctrl := Uint_Direct_Bias + Ctrl (Min_Direct); + Uint_Direct_Last : constant Ctrl := Uint_Direct_Bias + Ctrl (Max_Direct); + + Uint_0 : constant Uint := Uint (Uint_Direct_Bias); + Uint_1 : constant Uint := Uint (Uint_Direct_Bias + 1); + Uint_2 : constant Uint := Uint (Uint_Direct_Bias + 2); + Uint_3 : constant Uint := Uint (Uint_Direct_Bias + 3); + Uint_4 : constant Uint := Uint (Uint_Direct_Bias + 4); + Uint_5 : constant Uint := Uint (Uint_Direct_Bias + 5); + Uint_6 : constant Uint := Uint (Uint_Direct_Bias + 6); + Uint_7 : constant Uint := Uint (Uint_Direct_Bias + 7); + Uint_8 : constant Uint := Uint (Uint_Direct_Bias + 8); + Uint_9 : constant Uint := Uint (Uint_Direct_Bias + 9); + Uint_10 : constant Uint := Uint (Uint_Direct_Bias + 10); + Uint_12 : constant Uint := Uint (Uint_Direct_Bias + 12); + Uint_15 : constant Uint := Uint (Uint_Direct_Bias + 15); + Uint_16 : constant Uint := Uint (Uint_Direct_Bias + 16); + Uint_24 : constant Uint := Uint (Uint_Direct_Bias + 24); + Uint_32 : constant Uint := Uint (Uint_Direct_Bias + 32); + Uint_63 : constant Uint := Uint (Uint_Direct_Bias + 63); + Uint_64 : constant Uint := Uint (Uint_Direct_Bias + 64); + Uint_128 : constant Uint := Uint (Uint_Direct_Bias + 128); + + Uint_Minus_1 : constant Uint := Uint (Uint_Direct_Bias - 1); + Uint_Minus_2 : constant Uint := Uint (Uint_Direct_Bias - 2); + Uint_Minus_3 : constant Uint := Uint (Uint_Direct_Bias - 3); + Uint_Minus_4 : constant Uint := Uint (Uint_Direct_Bias - 4); + Uint_Minus_5 : constant Uint := Uint (Uint_Direct_Bias - 5); + Uint_Minus_6 : constant Uint := Uint (Uint_Direct_Bias - 6); + Uint_Minus_7 : constant Uint := Uint (Uint_Direct_Bias - 7); + Uint_Minus_8 : constant Uint := Uint (Uint_Direct_Bias - 8); + Uint_Minus_9 : constant Uint := Uint (Uint_Direct_Bias - 9); + Uint_Minus_12 : constant Uint := Uint (Uint_Direct_Bias - 12); + Uint_Minus_128 : constant Uint := Uint (Uint_Direct_Bias - 128); + + type Save_Mark is record + Save_Uint : Uint; + Save_Udigit : Int; + end record; + + -- Values outside the range that is represented directly are stored + -- using two tables. The secondary table Udigits contains sequences of + -- Int values consisting of the digits of the number in a radix Base + -- system. The digits are stored from most significant to least + -- significant with the first digit only carrying the sign. + + -- There is one entry in the primary Uints table for each distinct Uint + -- value. This table entry contains the length (number of digits) and + -- a starting offset of the value in the Udigits table. + + Uint_First_Entry : constant Uint := Uint (Uint_Table_Start); + + -- Some subprograms defined in this package manipulate the Udigits + -- table directly, while for others it is more convenient to work with + -- locally defined arrays of the digits of the Universal Integers. + -- The type UI_Vector is defined for this purpose and some internal + -- subprograms used for converting from one to the other are defined. + + type UI_Vector is array (Pos range <>) of Int; + -- Vector containing the integer values of a Uint value + + -- Note: An earlier version of this package used pointers of arrays + -- of Ints (dynamically allocated) for the Uint type. The change + -- leads to a few less natural idioms used throughout this code, but + -- eliminates all uses of the heap except for the table package itself. + -- For example, Uint parameters are often converted to UI_Vectors for + -- internal manipulation. This is done by creating the local UI_Vector + -- using the function N_Digits on the Uint to find the size needed for + -- the vector, and then calling Init_Operand to copy the values out + -- of the table into the vector. + + type Uint_Entry is record + Length : Pos; + -- Length of entry in Udigits table in digits (i.e. in words) + + Loc : Int; + -- Starting location in Udigits table of this Uint value + end record; + + package Uints is new Table.Table ( + Table_Component_Type => Uint_Entry, + Table_Index_Type => Uint, + Table_Low_Bound => Uint_First_Entry, + Table_Initial => Alloc.Uints_Initial, + Table_Increment => Alloc.Uints_Increment, + Table_Name => "Uints"); + + package Udigits is new Table.Table ( + Table_Component_Type => Int, + Table_Index_Type => Int, + Table_Low_Bound => 0, + Table_Initial => Alloc.Udigits_Initial, + Table_Increment => Alloc.Udigits_Increment, + Table_Name => "Udigits"); + + -- Note: the reason these tables are defined here in the private part of + -- the spec, rather than in the body, is that they are refrerenced + -- directly by gigi. + +end Uintp; diff --git a/gcc/ada/uintp.h b/gcc/ada/uintp.h new file mode 100644 index 00000000000..365dba0d60f --- /dev/null +++ b/gcc/ada/uintp.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * U I N T P * + * * + * C Header File * + * * + * $Revision: 1.1 $ + * * + * Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +/* This file corresponds to the Ada package specification Uintp. It was + created manually from the files uintp.ads and uintp.adb */ + +/* Support for universal integer arithmetic */ + +struct Uint_Entry +{ + Pos Length; + Int Loc; +}; + +/* See if a Uint is within the range of an integer. */ +#define UI_Is_In_Int_Range uintp__ui_is_in_int_range +extern Boolean UI_Is_In_Int_Range PARAMS((Uint)); + +/* Obtain Int value from Uint input. This will abort if the result is + out of range. */ +#define UI_To_Int uintp__ui_to_int +extern Int UI_To_Int PARAMS((Uint)); + +/* Convert an Int into a Uint. */ +#define UI_From_Int uintp__ui_from_int +extern Uint UI_From_Int PARAMS((int)); + +/* Similarly, but return a GCC INTEGER_CST. Overflow is tested by the + constant-folding used to build the node. TYPE is the GCC type of the + resulting node. */ +extern tree UI_To_gnu PARAMS((Uint, tree)); + +/* Universal integers are represented by the Uint type which is an index into + the Uints_Ptr table containing Uint_Entry values. A Uint_Entry contains an + index and length for getting the "digits" of the universal integer from the + Udigits_Ptr table. + + For efficiency, this method is used only for integer values larger than the + constant Uint_Bias. If a Uint is less than this constant, then it contains + the integer value itself. The origin of the Uints_Ptr table is adjusted so + that a Uint value of Uint_Bias indexes the first element. */ + +#define Uints_Ptr (uintp__uints__table - Uint_Table_Start) +extern struct Uint_Entry *uintp__uints__table; + +#define Udigits_Ptr uintp__udigits__table +extern int *uintp__udigits__table; + +#define Uint_0 (Uint_Direct_Bias + 0) +#define Uint_1 (Uint_Direct_Bias + 1) diff --git a/gcc/ada/uname.adb b/gcc/ada/uname.adb new file mode 100644 index 00000000000..b6e0f6bd5e3 --- /dev/null +++ b/gcc/ada/uname.adb @@ -0,0 +1,653 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U N A M E -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.56 $ +-- -- +-- Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Atree; use Atree; +with Casing; use Casing; +with Einfo; use Einfo; +with Hostparm; +with Lib; use Lib; +with Namet; use Namet; +with Nlists; use Nlists; +with Output; use Output; +with Sinfo; use Sinfo; +with Sinput; use Sinput; + +package body Uname is + + ------------------- + -- Get_Body_Name -- + ------------------- + + function Get_Body_Name (N : Unit_Name_Type) return Unit_Name_Type is + begin + Get_Name_String (N); + + pragma Assert (Name_Len > 2 + and then Name_Buffer (Name_Len - 1) = '%' + and then Name_Buffer (Name_Len) = 's'); + + Name_Buffer (Name_Len) := 'b'; + return Name_Find; + end Get_Body_Name; + + ----------------------------------- + -- Get_External_Unit_Name_String -- + ----------------------------------- + + procedure Get_External_Unit_Name_String (N : Unit_Name_Type) is + Pcount : Natural; + Newlen : Natural; + + begin + -- Get unit name and eliminate trailing %s or %b + + Get_Name_String (N); + Name_Len := Name_Len - 2; + + -- Find number of components + + Pcount := 0; + for J in 1 .. Name_Len loop + if Name_Buffer (J) = '.' then + Pcount := Pcount + 1; + end if; + end loop; + + -- If simple name, nothing to do + + if Pcount = 0 then + return; + end if; + + -- If name has multiple components, replace dots by double underscore + + Newlen := Name_Len + Pcount; + + for J in reverse 1 .. Name_Len loop + if Name_Buffer (J) = '.' then + Name_Buffer (Newlen) := '_'; + Name_Buffer (Newlen - 1) := '_'; + Newlen := Newlen - 2; + + else + Name_Buffer (Newlen) := Name_Buffer (J); + Newlen := Newlen - 1; + end if; + end loop; + + Name_Len := Name_Len + Pcount; + end Get_External_Unit_Name_String; + + -------------------------- + -- Get_Parent_Body_Name -- + -------------------------- + + function Get_Parent_Body_Name (N : Unit_Name_Type) return Unit_Name_Type is + begin + Get_Name_String (N); + + while Name_Buffer (Name_Len) /= '.' loop + pragma Assert (Name_Len > 1); -- not a child or subunit name + Name_Len := Name_Len - 1; + end loop; + + Name_Buffer (Name_Len) := '%'; + Name_Len := Name_Len + 1; + Name_Buffer (Name_Len) := 'b'; + return Name_Find; + + end Get_Parent_Body_Name; + + -------------------------- + -- Get_Parent_Spec_Name -- + -------------------------- + + function Get_Parent_Spec_Name (N : Unit_Name_Type) return Unit_Name_Type is + begin + Get_Name_String (N); + + while Name_Buffer (Name_Len) /= '.' loop + if Name_Len = 1 then + return No_Name; -- not a child or subunit name + else + Name_Len := Name_Len - 1; + end if; + end loop; + + Name_Buffer (Name_Len) := '%'; + Name_Len := Name_Len + 1; + Name_Buffer (Name_Len) := 's'; + return Name_Find; + + end Get_Parent_Spec_Name; + + ------------------- + -- Get_Spec_Name -- + ------------------- + + function Get_Spec_Name (N : Unit_Name_Type) return Unit_Name_Type is + begin + Get_Name_String (N); + + pragma Assert (Name_Len > 2 + and then Name_Buffer (Name_Len - 1) = '%' + and then Name_Buffer (Name_Len) = 'b'); + + Name_Buffer (Name_Len) := 's'; + return Name_Find; + end Get_Spec_Name; + + ------------------- + -- Get_Unit_Name -- + ------------------- + + function Get_Unit_Name (N : Node_Id) return Unit_Name_Type is + + Unit_Name_Buffer : String (1 .. Hostparm.Max_Name_Length); + -- Buffer used to build name of unit. Note that we cannot use the + -- Name_Buffer in package Name_Table because we use it to read + -- component names. + + Unit_Name_Length : Natural := 0; + -- Length of name stored in Unit_Name_Buffer + + Node : Node_Id; + -- Program unit node + + procedure Add_Char (C : Character); + -- Add a single character to stored unit name + + procedure Add_Name (Name : Name_Id); + -- Add the characters of a names table entry to stored unit name + + procedure Add_Node_Name (Node : Node_Id); + -- Recursive procedure adds characters associated with Node + + function Get_Parent (Node : Node_Id) return Node_Id; + -- Get parent compilation unit of a stub + + -------------- + -- Add_Char -- + -------------- + + procedure Add_Char (C : Character) is + begin + -- Should really check for max length exceeded here??? + Unit_Name_Length := Unit_Name_Length + 1; + Unit_Name_Buffer (Unit_Name_Length) := C; + end Add_Char; + + -------------- + -- Add_Name -- + -------------- + + procedure Add_Name (Name : Name_Id) is + begin + Get_Name_String (Name); + + for J in 1 .. Name_Len loop + Add_Char (Name_Buffer (J)); + end loop; + end Add_Name; + + ------------------- + -- Add_Node_Name -- + ------------------- + + procedure Add_Node_Name (Node : Node_Id) is + Kind : Node_Kind := Nkind (Node); + + begin + -- Just ignore an error node (someone else will give a message) + + if Node = Error then + return; + + -- Otherwise see what kind of node we have + + else + case Kind is + + when N_Identifier | + N_Defining_Identifier | + N_Defining_Operator_Symbol => + + -- Note: it is of course an error to have a defining + -- operator symbol at this point, but this is not where + -- the error is signalled, so we handle it nicely here! + + Add_Name (Chars (Node)); + + when N_Defining_Program_Unit_Name => + Add_Node_Name (Name (Node)); + Add_Char ('.'); + Add_Node_Name (Defining_Identifier (Node)); + + when N_Selected_Component | + N_Expanded_Name => + Add_Node_Name (Prefix (Node)); + Add_Char ('.'); + Add_Node_Name (Selector_Name (Node)); + + when N_Subprogram_Specification | + N_Package_Specification => + Add_Node_Name (Defining_Unit_Name (Node)); + + when N_Subprogram_Body | + N_Subprogram_Declaration | + N_Package_Declaration | + N_Generic_Declaration => + Add_Node_Name (Specification (Node)); + + when N_Generic_Instantiation => + Add_Node_Name (Defining_Unit_Name (Node)); + + when N_Package_Body => + Add_Node_Name (Defining_Unit_Name (Node)); + + when N_Task_Body | + N_Protected_Body => + Add_Node_Name (Defining_Identifier (Node)); + + when N_Package_Renaming_Declaration => + Add_Node_Name (Defining_Unit_Name (Node)); + + when N_Subprogram_Renaming_Declaration => + Add_Node_Name (Specification (Node)); + + when N_Generic_Renaming_Declaration => + Add_Node_Name (Defining_Unit_Name (Node)); + + when N_Subprogram_Body_Stub => + Add_Node_Name (Get_Parent (Node)); + Add_Char ('.'); + Add_Node_Name (Specification (Node)); + + when N_Compilation_Unit => + Add_Node_Name (Unit (Node)); + + when N_Package_Body_Stub => + Add_Node_Name (Get_Parent (Node)); + Add_Char ('.'); + Add_Node_Name (Defining_Identifier (Node)); + + when N_Task_Body_Stub | + N_Protected_Body_Stub => + Add_Node_Name (Get_Parent (Node)); + Add_Char ('.'); + Add_Node_Name (Defining_Identifier (Node)); + + when N_Subunit => + Add_Node_Name (Name (Node)); + Add_Char ('.'); + Add_Node_Name (Proper_Body (Node)); + + when N_With_Clause => + Add_Node_Name (Name (Node)); + + when N_Pragma => + Add_Node_Name (Expression (First + (Pragma_Argument_Associations (Node)))); + + -- Tasks and protected stuff appear only in an error context, + -- but the error has been posted elsewhere, so we deal nicely + -- with these error situations here, and produce a reasonable + -- unit name using the defining identifier. + + when N_Task_Type_Declaration | + N_Single_Task_Declaration | + N_Protected_Type_Declaration | + N_Single_Protected_Declaration => + Add_Node_Name (Defining_Identifier (Node)); + + when others => + raise Program_Error; + + end case; + end if; + end Add_Node_Name; + + ---------------- + -- Get_Parent -- + ---------------- + + function Get_Parent (Node : Node_Id) return Node_Id is + N : Node_Id := Node; + + begin + while Nkind (N) /= N_Compilation_Unit loop + N := Parent (N); + end loop; + + return N; + end Get_Parent; + + -------------------------------------------- + -- Start of Processing for Get_Unit_Name -- + -------------------------------------------- + + begin + Node := N; + + -- If we have Defining_Identifier, find the associated unit node + + if Nkind (Node) = N_Defining_Identifier then + Node := Declaration_Node (Node); + + -- If an expanded name, it is an already analyzed child unit, find + -- unit node. + + elsif Nkind (Node) = N_Expanded_Name then + Node := Declaration_Node (Entity (Node)); + end if; + + if Nkind (Node) = N_Package_Specification + or else Nkind (Node) in N_Subprogram_Specification + then + Node := Parent (Node); + end if; + + -- Node points to the unit, so get its name and add proper suffix + + Add_Node_Name (Node); + Add_Char ('%'); + + case Nkind (Node) is + when N_Generic_Declaration | + N_Subprogram_Declaration | + N_Package_Declaration | + N_With_Clause | + N_Pragma | + N_Generic_Instantiation | + N_Package_Renaming_Declaration | + N_Subprogram_Renaming_Declaration | + N_Generic_Renaming_Declaration | + N_Single_Task_Declaration | + N_Single_Protected_Declaration | + N_Task_Type_Declaration | + N_Protected_Type_Declaration => + + Add_Char ('s'); + + when N_Subprogram_Body | + N_Package_Body | + N_Subunit | + N_Body_Stub | + N_Task_Body | + N_Protected_Body | + N_Identifier | + N_Selected_Component => + + Add_Char ('b'); + + when others => + raise Program_Error; + end case; + + Name_Buffer (1 .. Unit_Name_Length) := + Unit_Name_Buffer (1 .. Unit_Name_Length); + Name_Len := Unit_Name_Length; + return Name_Find; + + end Get_Unit_Name; + + -------------------------- + -- Get_Unit_Name_String -- + -------------------------- + + procedure Get_Unit_Name_String (N : Unit_Name_Type) is + Unit_Is_Body : Boolean; + + begin + Get_Decoded_Name_String (N); + Unit_Is_Body := Name_Buffer (Name_Len) = 'b'; + Set_Casing (Identifier_Casing (Source_Index (Main_Unit)), Mixed_Case); + + -- A special fudge, normally we don't have operator symbols present, + -- since it is always an error to do so. However, if we do, at this + -- stage it has the form: + + -- "and" + + -- and the %s or %b has already been eliminated so put 2 chars back + + if Name_Buffer (1) = '"' then + Name_Len := Name_Len + 2; + end if; + + -- Now adjust the %s or %b to (spec) or (body) + + if Unit_Is_Body then + Name_Buffer (Name_Len - 1 .. Name_Len + 5) := " (body)"; + else + Name_Buffer (Name_Len - 1 .. Name_Len + 5) := " (spec)"; + end if; + + for J in 1 .. Name_Len loop + if Name_Buffer (J) = '-' then + Name_Buffer (J) := '.'; + end if; + end loop; + + Name_Len := Name_Len + (7 - 2); + end Get_Unit_Name_String; + + ------------------ + -- Is_Body_Name -- + ------------------ + + function Is_Body_Name (N : Unit_Name_Type) return Boolean is + begin + Get_Name_String (N); + return Name_Len > 2 + and then Name_Buffer (Name_Len - 1) = '%' + and then Name_Buffer (Name_Len) = 'b'; + end Is_Body_Name; + + ------------------- + -- Is_Child_Name -- + ------------------- + + function Is_Child_Name (N : Unit_Name_Type) return Boolean is + J : Natural; + + begin + Get_Name_String (N); + J := Name_Len; + + while Name_Buffer (J) /= '.' loop + if J = 1 then + return False; -- not a child or subunit name + else + J := J - 1; + end if; + end loop; + + return True; + end Is_Child_Name; + + ------------------ + -- Is_Spec_Name -- + ------------------ + + function Is_Spec_Name (N : Unit_Name_Type) return Boolean is + begin + Get_Name_String (N); + return Name_Len > 2 + and then Name_Buffer (Name_Len - 1) = '%' + and then Name_Buffer (Name_Len) = 's'; + end Is_Spec_Name; + + ----------------------- + -- Name_To_Unit_Name -- + ----------------------- + + function Name_To_Unit_Name (N : Name_Id) return Unit_Name_Type is + begin + Get_Name_String (N); + Name_Buffer (Name_Len + 1) := '%'; + Name_Buffer (Name_Len + 2) := 's'; + Name_Len := Name_Len + 2; + return Name_Find; + end Name_To_Unit_Name; + + --------------- + -- New_Child -- + --------------- + + function New_Child + (Old : Unit_Name_Type; + Newp : Unit_Name_Type) + return Unit_Name_Type + is + P : Natural; + + begin + Get_Name_String (Old); + + declare + Child : String := Name_Buffer (1 .. Name_Len); + + begin + Get_Name_String (Newp); + Name_Len := Name_Len - 2; + + P := Child'Last; + while Child (P) /= '.' loop + P := P - 1; + end loop; + + while P <= Child'Last loop + Name_Len := Name_Len + 1; + Name_Buffer (Name_Len) := Child (P); + P := P + 1; + end loop; + + return Name_Find; + end; + end New_Child; + + -------------- + -- Uname_Ge -- + -------------- + + function Uname_Ge (Left, Right : Unit_Name_Type) return Boolean is + begin + return Left = Right or else Uname_Gt (Left, Right); + end Uname_Ge; + + -------------- + -- Uname_Gt -- + -------------- + + function Uname_Gt (Left, Right : Unit_Name_Type) return Boolean is + begin + return Left /= Right and then not Uname_Lt (Left, Right); + end Uname_Gt; + + -------------- + -- Uname_Le -- + -------------- + + function Uname_Le (Left, Right : Unit_Name_Type) return Boolean is + begin + return Left = Right or else Uname_Lt (Left, Right); + end Uname_Le; + + -------------- + -- Uname_Lt -- + -------------- + + function Uname_Lt (Left, Right : Unit_Name_Type) return Boolean is + Left_Name : String (1 .. Hostparm.Max_Name_Length); + Left_Length : Natural; + Right_Name : String renames Name_Buffer; + Right_Length : Natural renames Name_Len; + J : Natural; + + begin + pragma Warnings (Off, Right_Length); + -- Suppress warnings on Right_Length, used in pragma Assert + + if Left = Right then + return False; + end if; + + Get_Name_String (Left); + Left_Name (1 .. Name_Len + 1) := Name_Buffer (1 .. Name_Len + 1); + Left_Length := Name_Len; + Get_Name_String (Right); + J := 1; + + loop + exit when Left_Name (J) = '%'; + + if Right_Name (J) = '%' then + return False; -- left name is longer + end if; + + pragma Assert (J <= Left_Length and then J <= Right_Length); + + if Left_Name (J) /= Right_Name (J) then + return Left_Name (J) < Right_Name (J); -- parent names different + end if; + + J := J + 1; + end loop; + + -- Come here pointing to % in left name + + if Right_Name (J) /= '%' then + return True; -- right name is longer + end if; + + -- Here the parent names are the same and specs sort low. If neither is + -- a spec, then we are comparing the same name and we want a result of + -- False in any case. + + return Left_Name (J + 1) = 's'; + end Uname_Lt; + + --------------------- + -- Write_Unit_Name -- + --------------------- + + procedure Write_Unit_Name (N : Unit_Name_Type) is + begin + Get_Unit_Name_String (N); + Write_Str (Name_Buffer (1 .. Name_Len)); + end Write_Unit_Name; + +end Uname; diff --git a/gcc/ada/uname.ads b/gcc/ada/uname.ads new file mode 100644 index 00000000000..c5fc2097396 --- /dev/null +++ b/gcc/ada/uname.ads @@ -0,0 +1,176 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U N A M E -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.23 $ -- +-- -- +-- Copyright (C) 1992-1998, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Types; use Types; +package Uname is + + --------------------------- + -- Unit Name Conventions -- + --------------------------- + + -- Units are associated with a unique ASCII name as follows. First we + -- have the fully expanded name of the unit, with lower case letters + -- (except for the use of upper case letters for encoding upper half + -- and wide characters, as described in Namet), and periods. Following + -- this is one of the following suffixes: + + -- %s for package/subprogram/generic declarations (specs) + -- %b for package/subprogram/generic bodies and subunits + + -- Unit names are stored in the names table, and referred to by the + -- corresponding Name_Id values. The subtype Unit_Name, which is a + -- synonym for Name_Id, is used to indicate that a Name_Id value that + -- holds a unit name (as defined above) is expected. + + -- Note: as far as possible the conventions for unit names are encapsulated + -- in this package. The one exception is that package Fname, which provides + -- conversion routines from unit names to file names must be aware of the + -- precise conventions that are used. + + ------------------- + -- Display Names -- + ------------------- + + -- For display purposes, unit names are printed out with the suffix + -- " (body)" for a body and " (spec)" for a spec. These formats are + -- used for the Write_Unit_Name and Get_Unit_Name_String subprograms. + + ----------------- + -- Subprograms -- + ----------------- + + function Get_Body_Name (N : Unit_Name_Type) return Unit_Name_Type; + -- Given the name of a spec, this function returns the name of the + -- corresponding body, i.e. characters %s replaced by %b + + function Get_Parent_Body_Name (N : Unit_Name_Type) return Unit_Name_Type; + -- Given the name of a subunit, returns the name of the parent body. + + function Get_Parent_Spec_Name (N : Unit_Name_Type) return Unit_Name_Type; + -- Given the name of a child unit spec or body, returns the unit name + -- of the parent spec. Returns No_Name if the given name is not the name + -- of a child unit. + + procedure Get_External_Unit_Name_String (N : Unit_Name_Type); + -- Given the name of a body or spec unit, this procedure places in + -- Name_Buffer the name of the unit with periods replaced by double + -- underscores. The spec/body indication is eliminated. The length + -- of the stored name is placed in Name_Len. All letters are lower + -- case, corresponding to the string used in external names. + + function Get_Spec_Name (N : Unit_Name_Type) return Unit_Name_Type; + -- Given the name of a body, this function returns the name of the + -- corresponding spec, i.e. characters %b replaced by %s + + function Get_Unit_Name (N : Node_Id) return Unit_Name_Type; + -- This procedure returns the unit name that corresponds to the given node, + -- which is one of the following: + -- + -- N_Subprogram_Declaration (spec) cases + -- N_Package_Declaration + -- N_Generic_Declaration + -- N_With_Clause + -- N_Function_Instantiation + -- N_Package_Instantiation + -- N_Procedure_Instantiation + -- N_Pragma (Elaborate case) + -- + -- N_Package_Body (body) cases + -- N_Subprogram_Body + -- N_Identifier + -- N_Selected_Component + -- + -- N_Subprogram_Body_Stub (subunit) cases + -- N_Package_Body_Stub + -- N_Task_Body_Stub + -- N_Protected_Body_Stub + -- N_Subunit + + procedure Get_Unit_Name_String (N : Unit_Name_Type); + -- Places the display name of the unit in Name_Buffer and sets Name_Len + -- to the length of the stored name, i.e. it uses the same interface as + -- the Get_Name_String routine in the Namet package. The name contains + -- an indication of spec or body, and is decoded. + + function Is_Body_Name (N : Unit_Name_Type) return Boolean; + -- Returns True iff the given name is the unit name of a body (i.e. if + -- it ends with the characters %b). + + function Is_Child_Name (N : Unit_Name_Type) return Boolean; + -- Returns True iff the given name is a child unit name (of either a + -- body or a spec). + + function Is_Spec_Name (N : Unit_Name_Type) return Boolean; + -- Returns True iff the given name is the unit name of a specification + -- (i.e. if it ends with the characters %s). + + function Name_To_Unit_Name (N : Name_Id) return Unit_Name_Type; + -- Given the Id of the Ada name of a unit, this function returns the + -- corresponding unit name of the spec (by appending %s to the name). + + function New_Child + (Old : Unit_Name_Type; + Newp : Unit_Name_Type) + return Unit_Name_Type; + -- Old is a child unit name (for either a body or spec). Newp is the + -- unit name of the actual parent (this may be different from the + -- parent in old). The returned unit name is formed by taking the + -- parent name from Newp and the child unit name from Old, with the + -- result being a body or spec depending on Old. For example: + -- + -- Old = A.B.C (body) + -- Newp = A.R (spec) + -- result = A.R.C (body) + -- + -- See spec of Load_Unit for extensive discussion of why this routine + -- needs to be used (the call in the body of Load_Unit is the only one). + + function Uname_Ge (Left, Right : Unit_Name_Type) return Boolean; + function Uname_Gt (Left, Right : Unit_Name_Type) return Boolean; + function Uname_Le (Left, Right : Unit_Name_Type) return Boolean; + function Uname_Lt (Left, Right : Unit_Name_Type) return Boolean; + -- These functions perform lexicographic ordering of unit names. The + -- ordering is suitable for printing, and is not quite a straightforward + -- comparison of the names, since the convention is that specs appear + -- before bodies. Note that the standard = and /= operators work fine + -- because all unit names are hashed into the name table, so if two names + -- are the same, they always have the same Name_Id value. + + procedure Write_Unit_Name (N : Unit_Name_Type); + -- Given a unit name, this procedure writes the display name to the + -- standard output file. Name_Buffer and Name_Len are set as described + -- above for the Get_Unit_Name_String call on return. + +end Uname; diff --git a/gcc/ada/unchconv.ads b/gcc/ada/unchconv.ads new file mode 100644 index 00000000000..f501af5e22a --- /dev/null +++ b/gcc/ada/unchconv.ads @@ -0,0 +1,24 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U N C H E C K E D _ C O N V E R S I O N -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.15 $ -- +-- -- +-- This specification is adapted from the Ada Reference Manual for use with -- +-- GNAT. In accordance with the copyright of that document, you can freely -- +-- copy and modify this specification, provided that if you redistribute a -- +-- modified version, any changes that you have made are clearly indicated. -- +-- -- +------------------------------------------------------------------------------ + +generic + type Source (<>) is limited private; + type Target (<>) is limited private; + +function Unchecked_Conversion (S : Source) return Target; +pragma Import (Intrinsic, Unchecked_Conversion); +pragma Pure (Unchecked_Conversion); diff --git a/gcc/ada/unchdeal.ads b/gcc/ada/unchdeal.ads new file mode 100644 index 00000000000..2a24ca05627 --- /dev/null +++ b/gcc/ada/unchdeal.ads @@ -0,0 +1,23 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U N C H E C K E D _ D E A L L O C A T I O N -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.15 $ -- +-- -- +-- This specification is adapted from the Ada Reference Manual for use with -- +-- GNAT. In accordance with the copyright of that document, you can freely -- +-- copy and modify this specification, provided that if you redistribute a -- +-- modified version, any changes that you have made are clearly indicated. -- +-- -- +------------------------------------------------------------------------------ + +generic + type Object (<>) is limited private; + type Name is access Object; + +procedure Unchecked_Deallocation (X : in out Name); +pragma Import (Intrinsic, Unchecked_Deallocation); diff --git a/gcc/ada/urealp.adb b/gcc/ada/urealp.adb new file mode 100644 index 00000000000..941af169647 --- /dev/null +++ b/gcc/ada/urealp.adb @@ -0,0 +1,1472 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U R E A L P -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.60 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Alloc; +with Output; use Output; +with Table; +with Tree_IO; use Tree_IO; + +package body Urealp is + + Ureal_First_Entry : constant Ureal := Ureal'Succ (No_Ureal); + -- First subscript allocated in Ureal table (note that we can't just + -- add 1 to No_Ureal, since "+" means something different for Ureals! + + type Ureal_Entry is record + Num : Uint; + -- Numerator (always non-negative) + + Den : Uint; + -- Denominator (always non-zero, always positive if base is zero) + + Rbase : Nat; + -- Base value. If Rbase is zero, then the value is simply Num / Den. + -- If Rbase is non-zero, then the value is Num / (Rbase ** Den) + + Negative : Boolean; + -- Flag set if value is negative + + end record; + + package Ureals is new Table.Table ( + Table_Component_Type => Ureal_Entry, + Table_Index_Type => Ureal, + Table_Low_Bound => Ureal_First_Entry, + Table_Initial => Alloc.Ureals_Initial, + Table_Increment => Alloc.Ureals_Increment, + Table_Name => "Ureals"); + + -- The following universal reals are the values returned by the constant + -- functions. They are initialized by the initialization procedure. + + UR_M_0 : Ureal; + UR_0 : Ureal; + UR_Tenth : Ureal; + UR_Half : Ureal; + UR_1 : Ureal; + UR_2 : Ureal; + UR_10 : Ureal; + UR_100 : Ureal; + UR_2_128 : Ureal; + UR_2_M_128 : Ureal; + + Num_Ureal_Constants : constant := 10; + -- This is used for an assertion check in Tree_Read and Tree_Write to + -- help remember to add values to these routines when we add to the list. + + Normalized_Real : Ureal := No_Ureal; + -- Used to memoize Norm_Num and Norm_Den, if either of these functions + -- is called, this value is set and Normalized_Entry contains the result + -- of the normalization. On subsequent calls, this is used to avoid the + -- call to Normalize if it has already been made. + + Normalized_Entry : Ureal_Entry; + -- Entry built by most recent call to Normalize + + ----------------------- + -- Local Subprograms -- + ----------------------- + + function Decimal_Exponent_Hi (V : Ureal) return Int; + -- Returns an estimate of the exponent of Val represented as a normalized + -- decimal number (non-zero digit before decimal point), The estimate is + -- either correct, or high, but never low. The accuracy of the estimate + -- affects only the efficiency of the comparison routines. + + function Decimal_Exponent_Lo (V : Ureal) return Int; + -- Returns an estimate of the exponent of Val represented as a normalized + -- decimal number (non-zero digit before decimal point), The estimate is + -- either correct, or low, but never high. The accuracy of the estimate + -- affects only the efficiency of the comparison routines. + + function Equivalent_Decimal_Exponent (U : Ureal_Entry) return Int; + -- U is a Ureal entry for which the base value is non-zero, the value + -- returned is the equivalent decimal exponent value, i.e. the value of + -- Den, adjusted as though the base were base 10. The value is rounded + -- to the nearest integer, and so can be one off. + + function Is_Integer (Num, Den : Uint) return Boolean; + -- Return true if the real quotient of Num / Den is an integer value + + function Normalize (Val : Ureal_Entry) return Ureal_Entry; + -- Normalizes the Ureal_Entry by reducing it to lowest terms (with a + -- base value of 0). + + function Same (U1, U2 : Ureal) return Boolean; + pragma Inline (Same); + -- Determines if U1 and U2 are the same Ureal. Note that we cannot use + -- the equals operator for this test, since that tests for equality, + -- not identity. + + function Store_Ureal (Val : Ureal_Entry) return Ureal; + -- This store a new entry in the universal reals table and return + -- its index in the table. + + ------------------------- + -- Decimal_Exponent_Hi -- + ------------------------- + + function Decimal_Exponent_Hi (V : Ureal) return Int is + Val : constant Ureal_Entry := Ureals.Table (V); + + begin + -- Zero always returns zero + + if UR_Is_Zero (V) then + return 0; + + -- For numbers in rational form, get the maximum number of digits in the + -- numerator and the minimum number of digits in the denominator, and + -- subtract. For example: + + -- 1000 / 99 = 1.010E+1 + -- 9999 / 10 = 9.999E+2 + + -- This estimate may of course be high, but that is acceptable + + elsif Val.Rbase = 0 then + return UI_Decimal_Digits_Hi (Val.Num) - + UI_Decimal_Digits_Lo (Val.Den); + + -- For based numbers, just subtract the decimal exponent from the + -- high estimate of the number of digits in the numerator and add + -- one to accomodate possible round off errors for non-decimal + -- bases. For example: + + -- 1_500_000 / 10**4 = 1.50E-2 + + else -- Val.Rbase /= 0 + return UI_Decimal_Digits_Hi (Val.Num) - + Equivalent_Decimal_Exponent (Val) + 1; + end if; + + end Decimal_Exponent_Hi; + + ------------------------- + -- Decimal_Exponent_Lo -- + ------------------------- + + function Decimal_Exponent_Lo (V : Ureal) return Int is + Val : constant Ureal_Entry := Ureals.Table (V); + + begin + -- Zero always returns zero + + if UR_Is_Zero (V) then + return 0; + + -- For numbers in rational form, get min digits in numerator, max digits + -- in denominator, and subtract and subtract one more for possible loss + -- during the division. For example: + + -- 1000 / 99 = 1.010E+1 + -- 9999 / 10 = 9.999E+2 + + -- This estimate may of course be low, but that is acceptable + + elsif Val.Rbase = 0 then + return UI_Decimal_Digits_Lo (Val.Num) - + UI_Decimal_Digits_Hi (Val.Den) - 1; + + -- For based numbers, just subtract the decimal exponent from the + -- low estimate of the number of digits in the numerator and subtract + -- one to accomodate possible round off errors for non-decimal + -- bases. For example: + + -- 1_500_000 / 10**4 = 1.50E-2 + + else -- Val.Rbase /= 0 + return UI_Decimal_Digits_Lo (Val.Num) - + Equivalent_Decimal_Exponent (Val) - 1; + end if; + + end Decimal_Exponent_Lo; + + ----------------- + -- Denominator -- + ----------------- + + function Denominator (Real : Ureal) return Uint is + begin + return Ureals.Table (Real).Den; + end Denominator; + + --------------------------------- + -- Equivalent_Decimal_Exponent -- + --------------------------------- + + function Equivalent_Decimal_Exponent (U : Ureal_Entry) return Int is + + -- The following table is a table of logs to the base 10 + + Logs : constant array (Nat range 1 .. 16) of Long_Float := ( + 1 => 0.000000000000000, + 2 => 0.301029995663981, + 3 => 0.477121254719662, + 4 => 0.602059991327962, + 5 => 0.698970004336019, + 6 => 0.778151250383644, + 7 => 0.845098040014257, + 8 => 0.903089986991944, + 9 => 0.954242509439325, + 10 => 1.000000000000000, + 11 => 1.041392685158230, + 12 => 1.079181246047620, + 13 => 1.113943352306840, + 14 => 1.146128035678240, + 15 => 1.176091259055680, + 16 => 1.204119982655920); + + begin + pragma Assert (U.Rbase /= 0); + return Int (Long_Float (UI_To_Int (U.Den)) * Logs (U.Rbase)); + end Equivalent_Decimal_Exponent; + + ---------------- + -- Initialize -- + ---------------- + + procedure Initialize is + begin + Ureals.Init; + UR_0 := UR_From_Components (Uint_0, Uint_1, 0, False); + UR_M_0 := UR_From_Components (Uint_0, Uint_1, 0, True); + UR_Half := UR_From_Components (Uint_1, Uint_1, 2, False); + UR_Tenth := UR_From_Components (Uint_1, Uint_1, 10, False); + UR_1 := UR_From_Components (Uint_1, Uint_1, 0, False); + UR_2 := UR_From_Components (Uint_1, Uint_Minus_1, 2, False); + UR_10 := UR_From_Components (Uint_1, Uint_Minus_1, 10, False); + UR_100 := UR_From_Components (Uint_1, Uint_Minus_2, 10, False); + UR_2_128 := UR_From_Components (Uint_1, Uint_Minus_128, 2, False); + UR_2_M_128 := UR_From_Components (Uint_1, Uint_128, 2, False); + end Initialize; + + ---------------- + -- Is_Integer -- + ---------------- + + function Is_Integer (Num, Den : Uint) return Boolean is + begin + return (Num / Den) * Den = Num; + end Is_Integer; + + ---------- + -- Mark -- + ---------- + + function Mark return Save_Mark is + begin + return Save_Mark (Ureals.Last); + end Mark; + + -------------- + -- Norm_Den -- + -------------- + + function Norm_Den (Real : Ureal) return Uint is + begin + if not Same (Real, Normalized_Real) then + Normalized_Real := Real; + Normalized_Entry := Normalize (Ureals.Table (Real)); + end if; + + return Normalized_Entry.Den; + end Norm_Den; + + -------------- + -- Norm_Num -- + -------------- + + function Norm_Num (Real : Ureal) return Uint is + begin + if not Same (Real, Normalized_Real) then + Normalized_Real := Real; + Normalized_Entry := Normalize (Ureals.Table (Real)); + end if; + + return Normalized_Entry.Num; + end Norm_Num; + + --------------- + -- Normalize -- + --------------- + + function Normalize (Val : Ureal_Entry) return Ureal_Entry is + J : Uint; + K : Uint; + Tmp : Uint; + Num : Uint; + Den : Uint; + M : constant Uintp.Save_Mark := Uintp.Mark; + + begin + -- Start by setting J to the greatest of the absolute values of the + -- numerator and the denominator (taking into account the base value), + -- and K to the lesser of the two absolute values. The gcd of Num and + -- Den is the gcd of J and K. + + if Val.Rbase = 0 then + J := Val.Num; + K := Val.Den; + + elsif Val.Den < 0 then + J := Val.Num * Val.Rbase ** (-Val.Den); + K := Uint_1; + + else + J := Val.Num; + K := Val.Rbase ** Val.Den; + end if; + + Num := J; + Den := K; + + if K > J then + Tmp := J; + J := K; + K := Tmp; + end if; + + J := UI_GCD (J, K); + Num := Num / J; + Den := Den / J; + Uintp.Release_And_Save (M, Num, Den); + + -- Divide numerator and denominator by gcd and return result + + return (Num => Num, + Den => Den, + Rbase => 0, + Negative => Val.Negative); + end Normalize; + + --------------- + -- Numerator -- + --------------- + + function Numerator (Real : Ureal) return Uint is + begin + return Ureals.Table (Real).Num; + end Numerator; + + -------- + -- pr -- + -------- + + procedure pr (Real : Ureal) is + begin + UR_Write (Real); + Write_Eol; + end pr; + + ----------- + -- Rbase -- + ----------- + + function Rbase (Real : Ureal) return Nat is + begin + return Ureals.Table (Real).Rbase; + end Rbase; + + ------------- + -- Release -- + ------------- + + procedure Release (M : Save_Mark) is + begin + Ureals.Set_Last (Ureal (M)); + end Release; + + ---------- + -- Same -- + ---------- + + function Same (U1, U2 : Ureal) return Boolean is + begin + return Int (U1) = Int (U2); + end Same; + + ----------------- + -- Store_Ureal -- + ----------------- + + function Store_Ureal (Val : Ureal_Entry) return Ureal is + begin + Ureals.Increment_Last; + Ureals.Table (Ureals.Last) := Val; + + -- Normalize representation of signed values + + if Val.Num < 0 then + Ureals.Table (Ureals.Last).Negative := True; + Ureals.Table (Ureals.Last).Num := -Val.Num; + end if; + + return Ureals.Last; + end Store_Ureal; + + --------------- + -- Tree_Read -- + --------------- + + procedure Tree_Read is + begin + pragma Assert (Num_Ureal_Constants = 10); + + Ureals.Tree_Read; + Tree_Read_Int (Int (UR_0)); + Tree_Read_Int (Int (UR_M_0)); + Tree_Read_Int (Int (UR_Tenth)); + Tree_Read_Int (Int (UR_Half)); + Tree_Read_Int (Int (UR_1)); + Tree_Read_Int (Int (UR_2)); + Tree_Read_Int (Int (UR_10)); + Tree_Read_Int (Int (UR_100)); + Tree_Read_Int (Int (UR_2_128)); + Tree_Read_Int (Int (UR_2_M_128)); + + -- Clear the normalization cache + + Normalized_Real := No_Ureal; + end Tree_Read; + + ---------------- + -- Tree_Write -- + ---------------- + + procedure Tree_Write is + begin + pragma Assert (Num_Ureal_Constants = 10); + + Ureals.Tree_Write; + Tree_Write_Int (Int (UR_0)); + Tree_Write_Int (Int (UR_M_0)); + Tree_Write_Int (Int (UR_Tenth)); + Tree_Write_Int (Int (UR_Half)); + Tree_Write_Int (Int (UR_1)); + Tree_Write_Int (Int (UR_2)); + Tree_Write_Int (Int (UR_10)); + Tree_Write_Int (Int (UR_100)); + Tree_Write_Int (Int (UR_2_128)); + Tree_Write_Int (Int (UR_2_M_128)); + end Tree_Write; + + ------------ + -- UR_Abs -- + ------------ + + function UR_Abs (Real : Ureal) return Ureal is + Val : constant Ureal_Entry := Ureals.Table (Real); + + begin + return Store_Ureal ( + (Num => Val.Num, + Den => Val.Den, + Rbase => Val.Rbase, + Negative => False)); + end UR_Abs; + + ------------ + -- UR_Add -- + ------------ + + function UR_Add (Left : Uint; Right : Ureal) return Ureal is + begin + return UR_From_Uint (Left) + Right; + end UR_Add; + + function UR_Add (Left : Ureal; Right : Uint) return Ureal is + begin + return Left + UR_From_Uint (Right); + end UR_Add; + + function UR_Add (Left : Ureal; Right : Ureal) return Ureal is + Lval : Ureal_Entry := Ureals.Table (Left); + Rval : Ureal_Entry := Ureals.Table (Right); + + Num : Uint; + + begin + -- Note, in the temporary Ureal_Entry values used in this procedure, + -- we store the sign as the sign of the numerator (i.e. xxx.Num may + -- be negative, even though in stored entries this can never be so) + + if Lval.Rbase /= 0 and then Lval.Rbase = Rval.Rbase then + + declare + Opd_Min, Opd_Max : Ureal_Entry; + Exp_Min, Exp_Max : Uint; + + begin + if Lval.Negative then + Lval.Num := (-Lval.Num); + end if; + + if Rval.Negative then + Rval.Num := (-Rval.Num); + end if; + + if Lval.Den < Rval.Den then + Exp_Min := Lval.Den; + Exp_Max := Rval.Den; + Opd_Min := Lval; + Opd_Max := Rval; + else + Exp_Min := Rval.Den; + Exp_Max := Lval.Den; + Opd_Min := Rval; + Opd_Max := Lval; + end if; + + Num := + Opd_Min.Num * Lval.Rbase ** (Exp_Max - Exp_Min) + Opd_Max.Num; + + if Num = 0 then + return Store_Ureal ( + (Num => Uint_0, + Den => Uint_1, + Rbase => 0, + Negative => Lval.Negative)); + + else + return Store_Ureal ( + (Num => abs Num, + Den => Exp_Max, + Rbase => Lval.Rbase, + Negative => (Num < 0))); + end if; + end; + + else + declare + Ln : Ureal_Entry := Normalize (Lval); + Rn : Ureal_Entry := Normalize (Rval); + + begin + if Ln.Negative then + Ln.Num := (-Ln.Num); + end if; + + if Rn.Negative then + Rn.Num := (-Rn.Num); + end if; + + Num := (Ln.Num * Rn.Den) + (Rn.Num * Ln.Den); + + if Num = 0 then + return Store_Ureal ( + (Num => Uint_0, + Den => Uint_1, + Rbase => 0, + Negative => Lval.Negative)); + + else + return Store_Ureal ( + Normalize ( + (Num => abs Num, + Den => Ln.Den * Rn.Den, + Rbase => 0, + Negative => (Num < 0)))); + end if; + end; + end if; + end UR_Add; + + ---------------- + -- UR_Ceiling -- + ---------------- + + function UR_Ceiling (Real : Ureal) return Uint is + Val : Ureal_Entry := Normalize (Ureals.Table (Real)); + + begin + if Val.Negative then + return UI_Negate (Val.Num / Val.Den); + else + return (Val.Num + Val.Den - 1) / Val.Den; + end if; + end UR_Ceiling; + + ------------ + -- UR_Div -- + ------------ + + function UR_Div (Left : Uint; Right : Ureal) return Ureal is + begin + return UR_From_Uint (Left) / Right; + end UR_Div; + + function UR_Div (Left : Ureal; Right : Uint) return Ureal is + begin + return Left / UR_From_Uint (Right); + end UR_Div; + + function UR_Div (Left, Right : Ureal) return Ureal is + Lval : constant Ureal_Entry := Ureals.Table (Left); + Rval : constant Ureal_Entry := Ureals.Table (Right); + Rneg : constant Boolean := Rval.Negative xor Lval.Negative; + + begin + pragma Assert (Rval.Num /= Uint_0); + + if Lval.Rbase = 0 then + + if Rval.Rbase = 0 then + return Store_Ureal ( + Normalize ( + (Num => Lval.Num * Rval.Den, + Den => Lval.Den * Rval.Num, + Rbase => 0, + Negative => Rneg))); + + elsif Is_Integer (Lval.Num, Rval.Num * Lval.Den) then + return Store_Ureal ( + (Num => Lval.Num / (Rval.Num * Lval.Den), + Den => (-Rval.Den), + Rbase => Rval.Rbase, + Negative => Rneg)); + + elsif Rval.Den < 0 then + return Store_Ureal ( + Normalize ( + (Num => Lval.Num, + Den => Rval.Rbase ** (-Rval.Den) * + Rval.Num * + Lval.Den, + Rbase => 0, + Negative => Rneg))); + + else + return Store_Ureal ( + Normalize ( + (Num => Lval.Num * Rval.Rbase ** Rval.Den, + Den => Rval.Num * Lval.Den, + Rbase => 0, + Negative => Rneg))); + end if; + + elsif Is_Integer (Lval.Num, Rval.Num) then + + if Rval.Rbase = Lval.Rbase then + return Store_Ureal ( + (Num => Lval.Num / Rval.Num, + Den => Lval.Den - Rval.Den, + Rbase => Lval.Rbase, + Negative => Rneg)); + + elsif Rval.Rbase = 0 then + return Store_Ureal ( + (Num => (Lval.Num / Rval.Num) * Rval.Den, + Den => Lval.Den, + Rbase => Lval.Rbase, + Negative => Rneg)); + + elsif Rval.Den < 0 then + declare + Num, Den : Uint; + + begin + if Lval.Den < 0 then + Num := (Lval.Num / Rval.Num) * (Lval.Rbase ** (-Lval.Den)); + Den := Rval.Rbase ** (-Rval.Den); + else + Num := Lval.Num / Rval.Num; + Den := (Lval.Rbase ** Lval.Den) * + (Rval.Rbase ** (-Rval.Den)); + end if; + + return Store_Ureal ( + (Num => Num, + Den => Den, + Rbase => 0, + Negative => Rneg)); + end; + + else + return Store_Ureal ( + (Num => (Lval.Num / Rval.Num) * + (Rval.Rbase ** Rval.Den), + Den => Lval.Den, + Rbase => Lval.Rbase, + Negative => Rneg)); + end if; + + else + declare + Num, Den : Uint; + + begin + if Lval.Den < 0 then + Num := Lval.Num * (Lval.Rbase ** (-Lval.Den)); + Den := Rval.Num; + + else + Num := Lval.Num; + Den := Rval.Num * (Lval.Rbase ** Lval.Den); + end if; + + if Rval.Rbase /= 0 then + if Rval.Den < 0 then + Den := Den * (Rval.Rbase ** (-Rval.Den)); + else + Num := Num * (Rval.Rbase ** Rval.Den); + end if; + + else + Num := Num * Rval.Den; + end if; + + return Store_Ureal ( + Normalize ( + (Num => Num, + Den => Den, + Rbase => 0, + Negative => Rneg))); + end; + end if; + end UR_Div; + + ----------- + -- UR_Eq -- + ----------- + + function UR_Eq (Left, Right : Ureal) return Boolean is + begin + return not UR_Ne (Left, Right); + end UR_Eq; + + --------------------- + -- UR_Exponentiate -- + --------------------- + + function UR_Exponentiate (Real : Ureal; N : Uint) return Ureal is + Bas : Ureal; + Val : Ureal_Entry; + X : Uint := abs N; + Neg : Boolean; + IBas : Uint; + + begin + -- If base is negative, then the resulting sign depends on whether + -- the exponent is even or odd (even => positive, odd = negative) + + if UR_Is_Negative (Real) then + Neg := (N mod 2) /= 0; + Bas := UR_Negate (Real); + else + Neg := False; + Bas := Real; + end if; + + Val := Ureals.Table (Bas); + + -- If the base is a small integer, then we can return the result in + -- exponential form, which can save a lot of time for junk exponents. + + IBas := UR_Trunc (Bas); + + if IBas <= 16 + and then UR_From_Uint (IBas) = Bas + then + return Store_Ureal ( + (Num => Uint_1, + Den => -N, + Rbase => UI_To_Int (UR_Trunc (Bas)), + Negative => Neg)); + + -- If the exponent is negative then we raise the numerator and the + -- denominator (after normalization) to the absolute value of the + -- exponent and we return the reciprocal. An assert error will happen + -- if the numerator is zero. + + elsif N < 0 then + pragma Assert (Val.Num /= 0); + Val := Normalize (Val); + + return Store_Ureal ( + (Num => Val.Den ** X, + Den => Val.Num ** X, + Rbase => 0, + Negative => Neg)); + + -- If positive, we distinguish the case when the base is not zero, in + -- which case the new denominator is just the product of the old one + -- with the exponent, + + else + if Val.Rbase /= 0 then + + return Store_Ureal ( + (Num => Val.Num ** X, + Den => Val.Den * X, + Rbase => Val.Rbase, + Negative => Neg)); + + -- And when the base is zero, in which case we exponentiate + -- the old denominator. + + else + return Store_Ureal ( + (Num => Val.Num ** X, + Den => Val.Den ** X, + Rbase => 0, + Negative => Neg)); + end if; + end if; + end UR_Exponentiate; + + -------------- + -- UR_Floor -- + -------------- + + function UR_Floor (Real : Ureal) return Uint is + Val : Ureal_Entry := Normalize (Ureals.Table (Real)); + + begin + if Val.Negative then + return UI_Negate ((Val.Num + Val.Den - 1) / Val.Den); + else + return Val.Num / Val.Den; + end if; + end UR_Floor; + + ------------------------- + -- UR_From_Components -- + ------------------------- + + function UR_From_Components + (Num : Uint; + Den : Uint; + Rbase : Nat := 0; + Negative : Boolean := False) + return Ureal + is + begin + return Store_Ureal ( + (Num => Num, + Den => Den, + Rbase => Rbase, + Negative => Negative)); + end UR_From_Components; + + ------------------ + -- UR_From_Uint -- + ------------------ + + function UR_From_Uint (UI : Uint) return Ureal is + begin + return UR_From_Components + (abs UI, Uint_1, Negative => (UI < 0)); + end UR_From_Uint; + + ----------- + -- UR_Ge -- + ----------- + + function UR_Ge (Left, Right : Ureal) return Boolean is + begin + return not (Left < Right); + end UR_Ge; + + ----------- + -- UR_Gt -- + ----------- + + function UR_Gt (Left, Right : Ureal) return Boolean is + begin + return (Right < Left); + end UR_Gt; + + -------------------- + -- UR_Is_Negative -- + -------------------- + + function UR_Is_Negative (Real : Ureal) return Boolean is + begin + return Ureals.Table (Real).Negative; + end UR_Is_Negative; + + -------------------- + -- UR_Is_Positive -- + -------------------- + + function UR_Is_Positive (Real : Ureal) return Boolean is + begin + return not Ureals.Table (Real).Negative + and then Ureals.Table (Real).Num /= 0; + end UR_Is_Positive; + + ---------------- + -- UR_Is_Zero -- + ---------------- + + function UR_Is_Zero (Real : Ureal) return Boolean is + begin + return Ureals.Table (Real).Num = 0; + end UR_Is_Zero; + + ----------- + -- UR_Le -- + ----------- + + function UR_Le (Left, Right : Ureal) return Boolean is + begin + return not (Right < Left); + end UR_Le; + + ----------- + -- UR_Lt -- + ----------- + + function UR_Lt (Left, Right : Ureal) return Boolean is + begin + -- An operand is not less than itself + + if Same (Left, Right) then + return False; + + -- Deal with zero cases + + elsif UR_Is_Zero (Left) then + return UR_Is_Positive (Right); + + elsif UR_Is_Zero (Right) then + return Ureals.Table (Left).Negative; + + -- Different signs are decisive (note we dealt with zero cases) + + elsif Ureals.Table (Left).Negative + and then not Ureals.Table (Right).Negative + then + return True; + + elsif not Ureals.Table (Left).Negative + and then Ureals.Table (Right).Negative + then + return False; + + -- Signs are same, do rapid check based on worst case estimates of + -- decimal exponent, which will often be decisive. Precise test + -- depends on whether operands are positive or negative. + + elsif Decimal_Exponent_Hi (Left) < Decimal_Exponent_Lo (Right) then + return UR_Is_Positive (Left); + + elsif Decimal_Exponent_Lo (Left) > Decimal_Exponent_Hi (Right) then + return UR_Is_Negative (Left); + + -- If we fall through, full gruesome test is required. This happens + -- if the numbers are close together, or in some wierd (/=10) base. + + else + declare + Imrk : constant Uintp.Save_Mark := Mark; + Rmrk : constant Urealp.Save_Mark := Mark; + Lval : Ureal_Entry; + Rval : Ureal_Entry; + Result : Boolean; + + begin + Lval := Ureals.Table (Left); + Rval := Ureals.Table (Right); + + -- An optimization. If both numbers are based, then subtract + -- common value of base to avoid unnecessarily giant numbers + + if Lval.Rbase = Rval.Rbase and then Lval.Rbase /= 0 then + if Lval.Den < Rval.Den then + Rval.Den := Rval.Den - Lval.Den; + Lval.Den := Uint_0; + else + Lval.Den := Lval.Den - Rval.Den; + Rval.Den := Uint_0; + end if; + end if; + + Lval := Normalize (Lval); + Rval := Normalize (Rval); + + if Lval.Negative then + Result := (Lval.Num * Rval.Den) > (Rval.Num * Lval.Den); + else + Result := (Lval.Num * Rval.Den) < (Rval.Num * Lval.Den); + end if; + + Release (Imrk); + Release (Rmrk); + return Result; + end; + end if; + end UR_Lt; + + ------------ + -- UR_Max -- + ------------ + + function UR_Max (Left, Right : Ureal) return Ureal is + begin + if Left >= Right then + return Left; + else + return Right; + end if; + end UR_Max; + + ------------ + -- UR_Min -- + ------------ + + function UR_Min (Left, Right : Ureal) return Ureal is + begin + if Left <= Right then + return Left; + else + return Right; + end if; + end UR_Min; + + ------------ + -- UR_Mul -- + ------------ + + function UR_Mul (Left : Uint; Right : Ureal) return Ureal is + begin + return UR_From_Uint (Left) * Right; + end UR_Mul; + + function UR_Mul (Left : Ureal; Right : Uint) return Ureal is + begin + return Left * UR_From_Uint (Right); + end UR_Mul; + + function UR_Mul (Left, Right : Ureal) return Ureal is + Lval : constant Ureal_Entry := Ureals.Table (Left); + Rval : constant Ureal_Entry := Ureals.Table (Right); + Num : Uint := Lval.Num * Rval.Num; + Den : Uint; + Rneg : constant Boolean := Lval.Negative xor Rval.Negative; + + begin + if Lval.Rbase = 0 then + if Rval.Rbase = 0 then + return Store_Ureal ( + Normalize ( + (Num => Num, + Den => Lval.Den * Rval.Den, + Rbase => 0, + Negative => Rneg))); + + elsif Is_Integer (Num, Lval.Den) then + return Store_Ureal ( + (Num => Num / Lval.Den, + Den => Rval.Den, + Rbase => Rval.Rbase, + Negative => Rneg)); + + elsif Rval.Den < 0 then + return Store_Ureal ( + Normalize ( + (Num => Num * (Rval.Rbase ** (-Rval.Den)), + Den => Lval.Den, + Rbase => 0, + Negative => Rneg))); + + else + return Store_Ureal ( + Normalize ( + (Num => Num, + Den => Lval.Den * (Rval.Rbase ** Rval.Den), + Rbase => 0, + Negative => Rneg))); + end if; + + elsif Lval.Rbase = Rval.Rbase then + return Store_Ureal ( + (Num => Num, + Den => Lval.Den + Rval.Den, + Rbase => Lval.Rbase, + Negative => Rneg)); + + elsif Rval.Rbase = 0 then + if Is_Integer (Num, Rval.Den) then + return Store_Ureal ( + (Num => Num / Rval.Den, + Den => Lval.Den, + Rbase => Lval.Rbase, + Negative => Rneg)); + + elsif Lval.Den < 0 then + return Store_Ureal ( + Normalize ( + (Num => Num * (Lval.Rbase ** (-Lval.Den)), + Den => Rval.Den, + Rbase => 0, + Negative => Rneg))); + + else + return Store_Ureal ( + Normalize ( + (Num => Num, + Den => Rval.Den * (Lval.Rbase ** Lval.Den), + Rbase => 0, + Negative => Rneg))); + end if; + + else + Den := Uint_1; + + if Lval.Den < 0 then + Num := Num * (Lval.Rbase ** (-Lval.Den)); + else + Den := Den * (Lval.Rbase ** Lval.Den); + end if; + + if Rval.Den < 0 then + Num := Num * (Rval.Rbase ** (-Rval.Den)); + else + Den := Den * (Rval.Rbase ** Rval.Den); + end if; + + return Store_Ureal ( + Normalize ( + (Num => Num, + Den => Den, + Rbase => 0, + Negative => Rneg))); + end if; + + end UR_Mul; + + ----------- + -- UR_Ne -- + ----------- + + function UR_Ne (Left, Right : Ureal) return Boolean is + begin + -- Quick processing for case of identical Ureal values (note that + -- this also deals with comparing two No_Ureal values). + + if Same (Left, Right) then + return False; + + -- Deal with case of one or other operand is No_Ureal, but not both + + elsif Same (Left, No_Ureal) or else Same (Right, No_Ureal) then + return True; + + -- Do quick check based on number of decimal digits + + elsif Decimal_Exponent_Hi (Left) < Decimal_Exponent_Lo (Right) or else + Decimal_Exponent_Lo (Left) > Decimal_Exponent_Hi (Right) + then + return True; + + -- Otherwise full comparison is required + + else + declare + Imrk : constant Uintp.Save_Mark := Mark; + Rmrk : constant Urealp.Save_Mark := Mark; + Lval : constant Ureal_Entry := Normalize (Ureals.Table (Left)); + Rval : constant Ureal_Entry := Normalize (Ureals.Table (Right)); + Result : Boolean; + + begin + if UR_Is_Zero (Left) then + return not UR_Is_Zero (Right); + + elsif UR_Is_Zero (Right) then + return not UR_Is_Zero (Left); + + -- Both operands are non-zero + + else + Result := + Rval.Negative /= Lval.Negative + or else Rval.Num /= Lval.Num + or else Rval.Den /= Lval.Den; + Release (Imrk); + Release (Rmrk); + return Result; + end if; + end; + end if; + end UR_Ne; + + --------------- + -- UR_Negate -- + --------------- + + function UR_Negate (Real : Ureal) return Ureal is + begin + return Store_Ureal ( + (Num => Ureals.Table (Real).Num, + Den => Ureals.Table (Real).Den, + Rbase => Ureals.Table (Real).Rbase, + Negative => not Ureals.Table (Real).Negative)); + end UR_Negate; + + ------------ + -- UR_Sub -- + ------------ + + function UR_Sub (Left : Uint; Right : Ureal) return Ureal is + begin + return UR_From_Uint (Left) + UR_Negate (Right); + end UR_Sub; + + function UR_Sub (Left : Ureal; Right : Uint) return Ureal is + begin + return Left + UR_From_Uint (-Right); + end UR_Sub; + + function UR_Sub (Left, Right : Ureal) return Ureal is + begin + return Left + UR_Negate (Right); + end UR_Sub; + + ---------------- + -- UR_To_Uint -- + ---------------- + + function UR_To_Uint (Real : Ureal) return Uint is + Val : Ureal_Entry := Normalize (Ureals.Table (Real)); + Res : Uint; + + begin + Res := (Val.Num + (Val.Den / 2)) / Val.Den; + + if Val.Negative then + return UI_Negate (Res); + else + return Res; + end if; + end UR_To_Uint; + + -------------- + -- UR_Trunc -- + -------------- + + function UR_Trunc (Real : Ureal) return Uint is + Val : constant Ureal_Entry := Normalize (Ureals.Table (Real)); + + begin + if Val.Negative then + return -(Val.Num / Val.Den); + else + return Val.Num / Val.Den; + end if; + end UR_Trunc; + + -------------- + -- UR_Write -- + -------------- + + procedure UR_Write (Real : Ureal) is + Val : constant Ureal_Entry := Ureals.Table (Real); + + begin + -- If value is negative, we precede the constant by a minus sign + -- and add an extra layer of parentheses on the outside since the + -- minus sign is part of the value, not a negation operator. + + if Val.Negative then + Write_Str ("(-"); + end if; + + -- Constants in base 10 can be written in normal Ada literal style + -- If the literal is negative enclose in parens to emphasize that + -- it is part of the constant, and not a separate negation operator + + if Val.Rbase = 10 then + + UI_Write (Val.Num / 10); + Write_Char ('.'); + UI_Write (Val.Num mod 10); + + if Val.Den /= 0 then + Write_Char ('E'); + UI_Write (1 - Val.Den); + end if; + + -- Constants in a base other than 10 can still be easily written + -- in normal Ada literal style if the numerator is one. + + elsif Val.Rbase /= 0 and then Val.Num = 1 then + Write_Int (Val.Rbase); + Write_Str ("#1.0#E"); + UI_Write (-Val.Den); + + -- Other constants with a base other than 10 are written using one + -- of the following forms, depending on the sign of the number + -- and the sign of the exponent (= minus denominator value) + + -- (numerator.0*base**exponent) + -- (numerator.0*base**(-exponent)) + + elsif Val.Rbase /= 0 then + Write_Char ('('); + UI_Write (Val.Num, Decimal); + Write_Str (".0*"); + Write_Int (Val.Rbase); + Write_Str ("**"); + + if Val.Den <= 0 then + UI_Write (-Val.Den, Decimal); + + else + Write_Str ("(-"); + UI_Write (Val.Den, Decimal); + Write_Char (')'); + end if; + + Write_Char (')'); + + -- Rational constants with a denominator of 1 can be written as + -- a real literal for the numerator integer. + + elsif Val.Den = 1 then + UI_Write (Val.Num, Decimal); + Write_Str (".0"); + + -- Non-based (rational) constants are written in (num/den) style + + else + Write_Char ('('); + UI_Write (Val.Num, Decimal); + Write_Str (".0/"); + UI_Write (Val.Den, Decimal); + Write_Str (".0)"); + end if; + + -- Add trailing paren for negative values + + if Val.Negative then + Write_Char (')'); + end if; + + end UR_Write; + + ------------- + -- Ureal_0 -- + ------------- + + function Ureal_0 return Ureal is + begin + return UR_0; + end Ureal_0; + + ------------- + -- Ureal_1 -- + ------------- + + function Ureal_1 return Ureal is + begin + return UR_1; + end Ureal_1; + + ------------- + -- Ureal_2 -- + ------------- + + function Ureal_2 return Ureal is + begin + return UR_2; + end Ureal_2; + + -------------- + -- Ureal_10 -- + -------------- + + function Ureal_10 return Ureal is + begin + return UR_10; + end Ureal_10; + + --------------- + -- Ureal_100 -- + --------------- + + function Ureal_100 return Ureal is + begin + return UR_100; + end Ureal_100; + + ----------------- + -- Ureal_2_128 -- + ----------------- + + function Ureal_2_128 return Ureal is + begin + return UR_2_128; + end Ureal_2_128; + + ------------------- + -- Ureal_2_M_128 -- + ------------------- + + function Ureal_2_M_128 return Ureal is + begin + return UR_2_M_128; + end Ureal_2_M_128; + + ---------------- + -- Ureal_Half -- + ---------------- + + function Ureal_Half return Ureal is + begin + return UR_Half; + end Ureal_Half; + + --------------- + -- Ureal_M_0 -- + --------------- + + function Ureal_M_0 return Ureal is + begin + return UR_M_0; + end Ureal_M_0; + + ----------------- + -- Ureal_Tenth -- + ----------------- + + function Ureal_Tenth return Ureal is + begin + return UR_Tenth; + end Ureal_Tenth; + +end Urealp; diff --git a/gcc/ada/urealp.ads b/gcc/ada/urealp.ads new file mode 100644 index 00000000000..9896e0d9968 --- /dev/null +++ b/gcc/ada/urealp.ads @@ -0,0 +1,355 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U R E A L P -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.35 $ -- +-- -- +-- Copyright (C) 1992-1998 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Support for universal real arithmetic + +with Types; use Types; +with Uintp; use Uintp; + +package Urealp is + + --------------------------------------- + -- Representation of Universal Reals -- + --------------------------------------- + + -- A universal real value is represented by a single value (which is + -- an index into an internal table). These values are not hashed, so + -- the equality operator should not be used on Ureal values (instead + -- use the UR_Eq function). + + -- A Ureal value represents an arbitrary precision universal real value, + -- stored internally using four components + + -- the numerator (Uint, always non-negative) + -- the denominator (Uint, always non-zero, always positive if base = 0) + -- a real base (Nat, either zero, or in the range 2 .. 16) + -- a sign flag (Boolean), set if negative + + -- If the base is zero, then the absolute value of the Ureal is simply + -- numerator/denominator. If the base is non-zero, then the absolute + -- value is num / (rbase ** den). + + -- Negative numbers are represented by the sign of the numerator being + -- negative. The denominator is always positive. + + -- A normalized Ureal value has base = 0, and numerator/denominator + -- reduced to lowest terms, with zero itself being represented as 0/1. + -- This is a canonical format, so that for normalized Ureal values it + -- is the case that two equal values always have the same denominator + -- and numerator values. + + -- Note: a value of minus zero is legitimate, and the operations in + -- Urealp preserve the handling of signed zeroes in accordance with + -- the rules of IEEE P754 ("IEEE floating point"). + + ------------------------------ + -- Types for Urealp Package -- + ------------------------------ + + type Ureal is private; + -- Type used for representation of universal reals + + No_Ureal : constant Ureal; + -- Constant used to indicate missing or unset Ureal value + + --------------------- + -- Ureal Constants -- + --------------------- + + function Ureal_0 return Ureal; + -- Returns value 0.0 + + function Ureal_M_0 return Ureal; + -- Returns value -0.0 + + function Ureal_Tenth return Ureal; + -- Returns value 0.1 + + function Ureal_Half return Ureal; + -- Returns value 0.5 + + function Ureal_1 return Ureal; + -- Returns value 1.0 + + function Ureal_2 return Ureal; + -- Returns value 2.0 + + function Ureal_10 return Ureal; + -- Returns value 10.0 + + function Ureal_100 return Ureal; + -- Returns value 100.0 + + function Ureal_2_128 return Ureal; + -- Returns value 2.0 ** 128 + + function Ureal_2_M_128 return Ureal; + -- Returns value 2.0 ** (-128) + + ----------------- + -- Subprograms -- + ----------------- + + procedure Initialize; + -- Initialize Ureal tables. Note that Initialize must not be called if + -- Tree_Read is used. Note also that there is no Lock routine in this + -- unit. These tables are among the few tables that can be expanded + -- during Gigi processing. + + procedure Tree_Read; + -- Initializes internal tables from current tree file using Tree_Read. + -- Note that Initialize should not be called if Tree_Read is used. + -- Tree_Read includes all necessary initialization. + + procedure Tree_Write; + -- Writes out internal tables to current tree file using Tree_Write + + function Rbase (Real : Ureal) return Nat; + -- Return the base of the universal real. + + function Denominator (Real : Ureal) return Uint; + -- Return the denominator of the universal real. + + function Numerator (Real : Ureal) return Uint; + -- Return the numerator of the universal real. + + function Norm_Den (Real : Ureal) return Uint; + -- Return the denominator of the universal real after a normalization. + + function Norm_Num (Real : Ureal) return Uint; + -- Return the numerator of the universal real after a normalization. + + function UR_From_Uint (UI : Uint) return Ureal; + -- Returns real corresponding to universal integer value + + function UR_To_Uint (Real : Ureal) return Uint; + -- Return integer value obtained by accurate rounding of real value. + -- The rounding of values half way between two integers is away from + -- zero, as required by normal Ada 95 rounding semantics. + + function UR_Trunc (Real : Ureal) return Uint; + -- Return integer value obtained by a truncation of real towards zero + + function UR_Ceiling (Real : Ureal) return Uint; + -- Return value of smallest integer not less than the given value + + function UR_Floor (Real : Ureal) return Uint; + -- Return value of smallest integer not greater than the given value + + -- Conversion table for above four functions + + -- Input To_Uint Trunc Ceiling Floor + -- 1.0 1 1 1 1 + -- 1.2 1 1 2 1 + -- 1.5 2 1 2 1 + -- 1.7 2 1 2 1 + -- 2.0 2 2 2 2 + -- -1.0 -1 -1 -1 -1 + -- -1.2 -1 -1 -1 -2 + -- -1.5 -2 -1 -1 -2 + -- -1.7 -2 -1 -1 -2 + -- -2.0 -2 -2 -2 -2 + + function UR_From_Components + (Num : Uint; + Den : Uint; + Rbase : Nat := 0; + Negative : Boolean := False) + return Ureal; + -- Builds real value from given numerator, denominator and base. The + -- value is negative if Negative is set to true, and otherwise is + -- non-negative. + + function UR_Add (Left : Ureal; Right : Ureal) return Ureal; + function UR_Add (Left : Ureal; Right : Uint) return Ureal; + function UR_Add (Left : Uint; Right : Ureal) return Ureal; + -- Returns real sum of operands + + function UR_Div (Left : Ureal; Right : Ureal) return Ureal; + function UR_Div (Left : Uint; Right : Ureal) return Ureal; + function UR_Div (Left : Ureal; Right : Uint) return Ureal; + -- Returns real quotient of operands. Fatal error if Right is zero + + function UR_Mul (Left : Ureal; Right : Ureal) return Ureal; + function UR_Mul (Left : Uint; Right : Ureal) return Ureal; + function UR_Mul (Left : Ureal; Right : Uint) return Ureal; + -- Returns real product of operands + + function UR_Sub (Left : Ureal; Right : Ureal) return Ureal; + function UR_Sub (Left : Uint; Right : Ureal) return Ureal; + function UR_Sub (Left : Ureal; Right : Uint) return Ureal; + -- Returns real difference of operands + + function UR_Exponentiate (Real : Ureal; N : Uint) return Ureal; + -- Returns result of raising Ureal to Uint power. + -- Fatal error if Left is 0 and Right is negative. + + function UR_Abs (Real : Ureal) return Ureal; + -- Returns abs function of real + + function UR_Negate (Real : Ureal) return Ureal; + -- Returns negative of real + + function UR_Eq (Left, Right : Ureal) return Boolean; + -- Compares reals for equality. + + function UR_Max (Left, Right : Ureal) return Ureal; + -- Returns the maximum of two reals + + function UR_Min (Left, Right : Ureal) return Ureal; + -- Returns the minimum of two reals + + function UR_Ne (Left, Right : Ureal) return Boolean; + -- Compares reals for inequality. + + function UR_Lt (Left, Right : Ureal) return Boolean; + -- Compares reals for less than. + + function UR_Le (Left, Right : Ureal) return Boolean; + -- Compares reals for less than or equal. + + function UR_Gt (Left, Right : Ureal) return Boolean; + -- Compares reals for greater than. + + function UR_Ge (Left, Right : Ureal) return Boolean; + -- Compares reals for greater than or equal. + + function UR_Is_Zero (Real : Ureal) return Boolean; + -- Tests if real value is zero + + function UR_Is_Negative (Real : Ureal) return Boolean; + -- Tests if real value is negative, note that negative zero gives true + + function UR_Is_Positive (Real : Ureal) return Boolean; + -- Test if real value is greater than zero + + procedure UR_Write (Real : Ureal); + -- Writes value of Real to standard output. Used only for debugging and + -- tree/source output. If the result is easily representable as a standard + -- Ada literal, it will be given that way, but as a result of evaluation + -- of static expressions, it is possible to generate constants (e.g. 1/13) + -- which have no such representation. In such cases (and in cases where it + -- is too much work to figure out the Ada literal), the string that is + -- output is of the form [numerator/denominator]. + + procedure pr (Real : Ureal); + -- Writes value of Real to standard output with a terminating line return, + -- using UR_Write as described above. This is for use from the debugger. + + ------------------------ + -- Operator Renamings -- + ------------------------ + + function "+" (Left : Ureal; Right : Ureal) return Ureal renames UR_Add; + function "+" (Left : Uint; Right : Ureal) return Ureal renames UR_Add; + function "+" (Left : Ureal; Right : Uint) return Ureal renames UR_Add; + + function "/" (Left : Ureal; Right : Ureal) return Ureal renames UR_Div; + function "/" (Left : Uint; Right : Ureal) return Ureal renames UR_Div; + function "/" (Left : Ureal; Right : Uint) return Ureal renames UR_Div; + + function "*" (Left : Ureal; Right : Ureal) return Ureal renames UR_Mul; + function "*" (Left : Uint; Right : Ureal) return Ureal renames UR_Mul; + function "*" (Left : Ureal; Right : Uint) return Ureal renames UR_Mul; + + function "-" (Left : Ureal; Right : Ureal) return Ureal renames UR_Sub; + function "-" (Left : Uint; Right : Ureal) return Ureal renames UR_Sub; + function "-" (Left : Ureal; Right : Uint) return Ureal renames UR_Sub; + + function "**" (Real : Ureal; N : Uint) return Ureal + renames UR_Exponentiate; + + function "abs" (Real : Ureal) return Ureal renames UR_Abs; + + function "-" (Real : Ureal) return Ureal renames UR_Negate; + + function "=" (Left, Right : Ureal) return Boolean renames UR_Eq; + + function "<" (Left, Right : Ureal) return Boolean renames UR_Lt; + + function "<=" (Left, Right : Ureal) return Boolean renames UR_Le; + + function ">=" (Left, Right : Ureal) return Boolean renames UR_Ge; + + function ">" (Left, Right : Ureal) return Boolean renames UR_Gt; + + ----------------------------- + -- Mark/Release Processing -- + ----------------------------- + + -- The space used by Ureal data is not automatically reclaimed. However, + -- a mark-release regime is implemented which allows storage to be + -- released back to a previously noted mark. This is used for example + -- when doing comparisons, where only intermediate results get stored + -- that do not need to be saved for future use. + + type Save_Mark is private; + + function Mark return Save_Mark; + -- Note mark point for future release + + procedure Release (M : Save_Mark); + -- Release storage allocated since mark was noted + + ------------------------------------ + -- Representation of Ureal Values -- + ------------------------------------ + +private + + type Ureal is new Int range Ureal_Low_Bound .. Ureal_High_Bound; + for Ureal'Size use 32; + + No_Ureal : constant Ureal := Ureal'First; + + type Save_Mark is new Int; + + pragma Inline (Denominator); + pragma Inline (Mark); + pragma Inline (Norm_Num); + pragma Inline (Norm_Den); + pragma Inline (Numerator); + pragma Inline (Rbase); + pragma Inline (Release); + pragma Inline (Ureal_0); + pragma Inline (Ureal_M_0); + pragma Inline (Ureal_Tenth); + pragma Inline (Ureal_Half); + pragma Inline (Ureal_1); + pragma Inline (Ureal_2); + pragma Inline (Ureal_10); + pragma Inline (UR_From_Components); + +end Urealp; diff --git a/gcc/ada/urealp.h b/gcc/ada/urealp.h new file mode 100644 index 00000000000..24afb55b598 --- /dev/null +++ b/gcc/ada/urealp.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * U R E A L P * + * * + * C Header File * + * * + * $Revision: 1.1 $ + * * + * Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +/* This file corresponds to the Ada package specification Urealp. It was + created manually from the files urealp.ads and urealp.adb */ + +/* Support for universal real arithmetic. */ + +#define Numerator urealp__numerator +extern Uint Numerator PARAMS ((Ureal)); + +#define Denominator urealp__denominator +extern Uint Denominator PARAMS ((Ureal)); + +#define Rbase urealp__rbase +extern Nat Rbase PARAMS ((Ureal)); + +#define UR_Is_Negative urealp__ur_is_negative +extern Boolean UR_Is_Negative PARAMS ((Ureal)); + +#define UR_Is_Zero urealp__ur_is_zero +extern Boolean UR_Is_Zero PARAMS ((Ureal)); + +#define Machine eval_fat__machine +extern Ureal Machine PARAMS ((Entity_Id, Ureal)); diff --git a/gcc/ada/usage.adb b/gcc/ada/usage.adb new file mode 100644 index 00000000000..f6fffeaedc2 --- /dev/null +++ b/gcc/ada/usage.adb @@ -0,0 +1,390 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U S A G E -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.116 $ +-- -- +-- Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Hostparm; +with Namet; use Namet; +with Osint; use Osint; +with Output; use Output; +with System.WCh_Con; use System.WCh_Con; + +procedure Usage is + + procedure Write_Switch_Char (Sw : String; Prefix : String := "gnat"); + -- Output two spaces followed by default switch character followed + -- Prefix, followed by the string given as the argument, and then + -- enough blanks to tab to column 13, i.e. assuming Sw is not longer + -- than 5 characters, the maximum allowed, Write_Switch_Char will + -- always output exactly 12 characters. + + procedure Write_Switch_Char (Sw : String; Prefix : String := "gnat") is + begin + Write_Str (" "); + Write_Char (Switch_Character); + Write_Str (Prefix); + Write_Str (Sw); + + for J in 1 .. 12 - 3 - Prefix'Length - Sw'Length loop + Write_Char (' '); + end loop; + end Write_Switch_Char; + +-- Start of processing for Usage + +begin + Find_Program_Name; + + -- For gnatmake, we are appending this information to the end of + -- the normal gnatmake output, so generate appropriate header + + if Name_Len >= 8 + and then (Name_Buffer (Name_Len - 7 .. Name_Len) = "gnatmake" + or else + Name_Buffer (Name_Len - 7 .. Name_Len) = "GNATMAKE") + then + Write_Eol; + Write_Line ("Compiler switches (passed to the compiler by gnatmake):"); + + else + -- Usage line + + Write_Str ("Usage: "); + Write_Program_Name; + Write_Char (' '); + Write_Str ("switches sfile"); + Write_Eol; + Write_Eol; + + -- Line for sfile + + Write_Line (" sfile Source file name"); + end if; + + Write_Eol; + + -- Common GCC switches not available in JGNAT + + if not Hostparm.Java_VM then + Write_Switch_Char ("fstack-check ", ""); + Write_Line ("Generate stack checking code"); + + Write_Switch_Char ("fno-inline ", ""); + Write_Line ("Inhibit all inlining (makes executable smaller)"); + end if; + + -- Common switches available to both GCC and JGNAT + + Write_Switch_Char ("g ", ""); + Write_Line ("Generate debugging information"); + + Write_Switch_Char ("Idir ", ""); + Write_Line ("Specify source files search path"); + + Write_Switch_Char ("I- ", ""); + Write_Line ("Do not look for sources in current directory"); + + Write_Switch_Char ("O[0123] ", ""); + Write_Line ("Control the optimization level"); + + Write_Eol; + + -- Individual lines for switches. Write_Switch_Char outputs fourteen + -- characters, so the remaining message is allowed to be a maximum + -- of 65 characters to be comfortable on an 80 character device. + -- If the Write_Str fits on one line, it is short enough! + + -- Line for -gnata switch + + Write_Switch_Char ("a"); + Write_Line ("Assertions enabled. Pragma Assert/Debug to be activated"); + + -- Line for -gnatA switch + + Write_Switch_Char ("A"); + Write_Line ("Avoid processing gnat.adc, if present file will be ignored"); + + -- Line for -gnatb switch + + Write_Switch_Char ("b"); + Write_Line ("Generate brief messages to stderr even if verbose mode set"); + + -- Line for -gnatc switch + + Write_Switch_Char ("c"); + Write_Line ("Check syntax and semantics only (no code generation)"); + + Write_Switch_Char ("C"); + Write_Line ("Compress names in external names and debug info tables"); + + -- Line for -gnatd switch + + Write_Switch_Char ("d?"); + Write_Line ("Compiler debug option ? (a-z,A-Z,0-9), see debug.adb"); + + -- Line for -gnatD switch + + Write_Switch_Char ("D"); + Write_Line ("Debug expanded generated code rather than source code"); + + -- Line for -gnatec switch + + Write_Switch_Char ("ec?"); + Write_Line ("Specify configuration pragmas file, e.g. -gnatec/x/f.adc"); + + -- Line for -gnatE switch + + Write_Switch_Char ("E"); + Write_Line ("Dynamic elaboration checking mode enabled"); + + -- Line for -gnatf switch + + Write_Switch_Char ("f"); + Write_Line ("Full errors. Verbose details, all undefined references"); + + -- Line for -gnatF switch + + Write_Switch_Char ("F"); + Write_Line ("Force all import/export external names to all uppercase"); + + -- Line for -gnatg switch + + Write_Switch_Char ("g"); + Write_Line ("GNAT implementation mode (used for compiling GNAT units)"); + + -- Line for -gnatG switch + + Write_Switch_Char ("G"); + Write_Line ("Output generated expanded code in source form"); + + -- Line for -gnath switch + + Write_Switch_Char ("h"); + Write_Line ("Output this usage (help) information"); + + -- Line for -gnati switch + + Write_Switch_Char ("i?"); + Write_Line ("Identifier char set (?=1/2/3/4/8/p/f/n/w)"); + + -- Line for -gnatk switch + + Write_Switch_Char ("k"); + Write_Line ("Limit file names to nnn characters (k = krunch)"); + + -- Line for -gnatl switch + + Write_Switch_Char ("l"); + Write_Line ("Output full source listing with embedded error messages"); + + -- Line for -gnatL switch + + Write_Switch_Char ("L"); + Write_Line ("Use longjmp/setjmp for exception handling"); + + -- Line for -gnatm switch + + Write_Switch_Char ("mnnn"); + Write_Line ("Limit number of detected errors to nnn (1-999)"); + + -- Line for -gnatn switch + + Write_Switch_Char ("n"); + Write_Line ("Inlining of subprograms (apply pragma Inline across units)"); + + -- Line for -gnatN switch + + Write_Switch_Char ("N"); + Write_Line ("Full (frontend) inlining of subprograqms"); + + -- Line for -gnato switch + + Write_Switch_Char ("o"); + Write_Line ("Enable overflow checking (off by default)"); + + -- Line for -gnatO switch + + Write_Switch_Char ("O nm "); + Write_Line ("Set name of output ali file (internal switch)"); + + -- Line for -gnatp switch + + Write_Switch_Char ("p"); + Write_Line ("Suppress all checks"); + + -- Line for -gnatP switch + + Write_Switch_Char ("P"); + Write_Line ("Generate periodic calls to System.Polling.Poll"); + + -- Line for -gnatq switch + + Write_Switch_Char ("q"); + Write_Line ("Don't quit, try semantics, even if parse errors"); + + -- Line for -gnatQ switch + + Write_Switch_Char ("Q"); + Write_Line ("Don't quit, write ali/tree file even if compile errors"); + + -- Line for -gnatR switch + + Write_Switch_Char ("R?"); + Write_Line ("List rep inf (?=0/1/2/3 for none/types/all/variable)"); + + -- Lines for -gnats switch + + Write_Switch_Char ("s"); + Write_Line ("Syntax check only"); + + -- Lines for -gnatt switch + + Write_Switch_Char ("t"); + Write_Line ("Tree output file to be generated"); + + -- Line for -gnatT switch + + Write_Switch_Char ("Tnnn"); + Write_Line ("All compiler tables start at nnn times usual starting size"); + + -- Line for -gnatu switch + + Write_Switch_Char ("u"); + Write_Line ("List units for this compilation"); + + -- Line for -gnatU switch + + Write_Switch_Char ("U"); + Write_Line ("Enable unique tag for error messages"); + + -- Line for -gnatv switch + + Write_Switch_Char ("v"); + Write_Line ("Verbose mode. Full error output with source lines to stdout"); + + -- Line for -gnatV switch + + Write_Switch_Char ("V?"); + Write_Line + ("Validity checking (?=ndcte or 0-4 None/Default/Copy/Test/Exprs)"); + + -- Lines for -gnatw switch + + Write_Switch_Char ("wxx"); + Write_Line ("Enable selected warning modes, xx = list of parameters:"); + Write_Line (" a turn on all optional warnings (except b,h)"); + Write_Line (" A turn off all optional warnings"); + Write_Line (" b turn on biased rounding warnings"); + Write_Line (" B turn off biased rounding warnings"); + Write_Line (" c turn on constant conditional warnings"); + Write_Line (" C* turn off constant conditional warnings"); + Write_Line (" e treat all warnings as errors"); + Write_Line (" h turn on warnings for hiding variables"); + Write_Line (" H* turn off warnings for hiding variables"); + Write_Line (" i* turn on warnings for implementation units"); + Write_Line (" I turn off warnings for implementation units"); + Write_Line (" l turn on elaboration warnings"); + Write_Line (" L* turn off elaboration warnings"); + Write_Line (" o* turn on address clause overlay warnings"); + Write_Line (" O turn off address clause overlay warnings"); + Write_Line (" p turn on warnings for ineffective pragma inline"); + Write_Line (" P* turn off warnings for ineffective pragma inline"); + Write_Line (" r turn on redundant construct warnings"); + Write_Line (" R* turn off redundant construct warnings"); + Write_Line (" s suppress all warnings"); + Write_Line (" u turn on warnings for unused entities"); + Write_Line (" U* turn off warnings for unused entities"); + Write_Line (" * indicates default in above list"); + + -- Line for -gnatW switch + + Write_Switch_Char ("W"); + Write_Str ("Wide character encoding method ("); + + for J in WC_Encoding_Method loop + Write_Char (WC_Encoding_Letters (J)); + + if J = WC_Encoding_Method'Last then + Write_Char (')'); + else + Write_Char ('/'); + end if; + end loop; + + Write_Eol; + + -- Line for -gnatx switch + + Write_Switch_Char ("x"); + Write_Line ("Suppress output of cross-reference information"); + + -- Line for -gnatX switch + + Write_Switch_Char ("X"); + Write_Line ("Language extensions permitted"); + + -- Lines for -gnaty switch + + Write_Switch_Char ("y"); + Write_Line ("Enable all style checks"); + + Write_Switch_Char ("yxx"); + Write_Line ("Enable selected style checks xx = list of parameters:"); + Write_Line (" 1-9 check indentation"); + Write_Line (" a check attribute casing"); + Write_Line (" b check no blanks at end of lines"); + Write_Line (" c check comment format"); + Write_Line (" e check end/exit labels present"); + Write_Line (" f check no form feeds/vertical tabs in source"); + Write_Line (" h check no horizontal tabs in source"); + Write_Line (" i check if-then layout"); + Write_Line (" k check casing rules for keywords, identifiers"); + Write_Line (" l check reference manual layout"); + Write_Line (" m check line length <= 79 characters"); + Write_Line (" n check casing of package Standard identifiers"); + Write_Line (" Mnnn check line length <= nnn characters"); + Write_Line (" o check subprogram bodies in alphabetical order"); + Write_Line (" p check pragma casing"); + Write_Line (" r check RM column layout"); + Write_Line (" s check separate subprogram specs present"); + Write_Line (" t check token separation rules"); + + -- Lines for -gnatz switch + + Write_Switch_Char ("z"); + Write_Line ("Distribution stub generation (r/s for receiver/sender stubs)"); + + -- Line for -gnatZ switch + + Write_Switch_Char ("Z"); + Write_Line ("Use zero cost exception handling"); + + -- Line for -gnat83 switch + + Write_Switch_Char ("83"); + Write_Line ("Enforce Ada 83 restrictions"); + +end Usage; diff --git a/gcc/ada/usage.ads b/gcc/ada/usage.ads new file mode 100644 index 00000000000..af0c35c29bf --- /dev/null +++ b/gcc/ada/usage.ads @@ -0,0 +1,31 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- U S A G E -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.5 $ -- +-- -- +-- Copyright (C) 1992,1993,1994 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Procedure to generate screen of usage information if no file name present + +procedure Usage; diff --git a/gcc/ada/utils.c b/gcc/ada/utils.c new file mode 100644 index 00000000000..b23bbe9f0c6 --- /dev/null +++ b/gcc/ada/utils.c @@ -0,0 +1,3350 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * U T I L S * + * * + * C Implementation File * + * * + * $Revision: 1.4 $ + * * + * Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "tree.h" +#include "flags.h" +#include "defaults.h" +#include "toplev.h" +#include "output.h" +#include "ggc.h" +#include "convert.h" + +#include "ada.h" +#include "types.h" +#include "atree.h" +#include "elists.h" +#include "namet.h" +#include "nlists.h" +#include "stringt.h" +#include "uintp.h" +#include "fe.h" +#include "sinfo.h" +#include "einfo.h" +#include "ada-tree.h" +#include "gigi.h" + +#ifndef MAX_FIXED_MODE_SIZE +#define MAX_FIXED_MODE_SIZE GET_MODE_BITSIZE (DImode) +#endif + +#ifndef MAX_BITS_PER_WORD +#define MAX_BITS_PER_WORD BITS_PER_WORD +#endif + +/* If nonzero, pretend we are allocating at global level. */ +int force_global; + +/* Global Variables for the various types we create. */ +tree gnat_std_decls[(int) ADT_LAST]; + +/* Associates a GNAT tree node to a GCC tree node. It is used in + `save_gnu_tree', `get_gnu_tree' and `present_gnu_tree'. See documentation + of `save_gnu_tree' for more info. */ +static tree *associate_gnat_to_gnu; + +/* This listhead is used to record any global objects that need elaboration. + TREE_PURPOSE is the variable to be elaborated and TREE_VALUE is the + initial value to assign. */ + +static tree pending_elaborations; + +/* This stack allows us to momentarily switch to generating elaboration + lists for an inner context. */ + +static struct e_stack {struct e_stack *next; tree elab_list; } *elist_stack; + +/* This variable keeps a table for types for each precision so that we only + allocate each of them once. Signed and unsigned types are kept separate. + + Note that these types are only used when fold-const requests something + special. Perhaps we should NOT share these types; we'll see how it + goes later. */ +static tree signed_and_unsigned_types[2 * MAX_BITS_PER_WORD + 1][2]; + +/* Likewise for float types, but record these by mode. */ +static tree float_types[NUM_MACHINE_MODES]; + +/* For each binding contour we allocate a binding_level structure which records + the entities defined or declared in that contour. Contours include: + + the global one + one for each subprogram definition + one for each compound statement (declare block) + + Binding contours are used to create GCC tree BLOCK nodes. */ + +struct binding_level +{ + /* A chain of ..._DECL nodes for all variables, constants, functions, + parameters and type declarations. These ..._DECL nodes are chained + through the TREE_CHAIN field. Note that these ..._DECL nodes are stored + in the reverse of the order supplied to be compatible with the + back-end. */ + tree names; + /* For each level (except the global one), a chain of BLOCK nodes for all + the levels that were entered and exited one level down from this one. */ + tree blocks; + /* The BLOCK node for this level, if one has been preallocated. + If 0, the BLOCK is allocated (if needed) when the level is popped. */ + tree this_block; + /* The binding level containing this one (the enclosing binding level). */ + struct binding_level *level_chain; +}; + +/* The binding level currently in effect. */ +static struct binding_level *current_binding_level = NULL; + +/* A chain of binding_level structures awaiting reuse. */ +static struct binding_level *free_binding_level = NULL; + +/* The outermost binding level. This binding level is created when the + compiler is started and it will exist through the entire compilation. */ +static struct binding_level *global_binding_level; + +/* Binding level structures are initialized by copying this one. */ +static struct binding_level clear_binding_level = {NULL, NULL, NULL, NULL}; + + +static tree merge_sizes PARAMS ((tree, tree, tree, int, int)); +static tree compute_related_constant PARAMS ((tree, tree)); +static tree split_plus PARAMS ((tree, tree *)); +static int value_zerop PARAMS ((tree)); +static tree float_type_for_size PARAMS ((int, enum machine_mode)); +static tree convert_to_fat_pointer PARAMS ((tree, tree)); +static tree convert_to_thin_pointer PARAMS ((tree, tree)); +static tree make_descriptor_field PARAMS ((const char *,tree, tree, + tree)); +static void mark_binding_level PARAMS((PTR)); +static void mark_e_stack PARAMS((PTR)); + +/* Initialize the association of GNAT nodes to GCC trees. */ + +void +init_gnat_to_gnu () +{ + Node_Id gnat_node; + + associate_gnat_to_gnu = (tree *) xmalloc (max_gnat_nodes * sizeof (tree)); + ggc_add_tree_root (associate_gnat_to_gnu, max_gnat_nodes); + + for (gnat_node = 0; gnat_node < max_gnat_nodes; gnat_node++) + associate_gnat_to_gnu [gnat_node] = NULL_TREE; + + associate_gnat_to_gnu -= First_Node_Id; + + pending_elaborations = build_tree_list (NULL_TREE, NULL_TREE); + ggc_add_tree_root (&pending_elaborations, 1); + ggc_add_root ((PTR) &elist_stack, 1, sizeof (struct e_stack), mark_e_stack); + ggc_add_tree_root (&signed_and_unsigned_types[0][0], + (sizeof signed_and_unsigned_types + / sizeof signed_and_unsigned_types[0][0])); + ggc_add_tree_root (float_types, sizeof float_types / sizeof float_types[0]); + + ggc_add_root (¤t_binding_level, 1, sizeof current_binding_level, + mark_binding_level); +} + +/* GNAT_ENTITY is a GNAT tree node for an entity. GNU_DECL is the GCC tree + which is to be associated with GNAT_ENTITY. Such GCC tree node is always + a ..._DECL node. If NO_CHECK is nonzero, the latter check is suppressed. + + If GNU_DECL is zero, a previous association is to be reset. */ + +void +save_gnu_tree (gnat_entity, gnu_decl, no_check) + Entity_Id gnat_entity; + tree gnu_decl; + int no_check; +{ + if (gnu_decl + && (associate_gnat_to_gnu [gnat_entity] + || (! no_check && ! DECL_P (gnu_decl)))) + gigi_abort (401); + + associate_gnat_to_gnu [gnat_entity] = gnu_decl; +} + +/* GNAT_ENTITY is a GNAT tree node for a defining identifier. + Return the ..._DECL node that was associated with it. If there is no tree + node associated with GNAT_ENTITY, abort. + + In some cases, such as delayed elaboration or expressions that need to + be elaborated only once, GNAT_ENTITY is really not an entity. */ + +tree +get_gnu_tree (gnat_entity) + Entity_Id gnat_entity; +{ + if (! associate_gnat_to_gnu [gnat_entity]) + gigi_abort (402); + + return associate_gnat_to_gnu [gnat_entity]; +} + +/* Return nonzero if a GCC tree has been associated with GNAT_ENTITY. */ + +int +present_gnu_tree (gnat_entity) + Entity_Id gnat_entity; +{ + return (associate_gnat_to_gnu [gnat_entity] != NULL_TREE); +} + + +/* Return non-zero if we are currently in the global binding level. */ + +int +global_bindings_p () +{ + return (force_global != 0 || current_binding_level == global_binding_level + ? -1 : 0); +} + +/* Return the list of declarations in the current level. Note that this list + is in reverse order (it has to be so for back-end compatibility). */ + +tree +getdecls () +{ + return current_binding_level->names; +} + +/* Nonzero if the current level needs to have a BLOCK made. */ + +int +kept_level_p () +{ + return (current_binding_level->names != 0); +} + +/* Enter a new binding level. The input parameter is ignored, but has to be + specified for back-end compatibility. */ + +void +pushlevel (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + struct binding_level *newlevel = NULL; + + /* Reuse a struct for this binding level, if there is one. */ + if (free_binding_level) + { + newlevel = free_binding_level; + free_binding_level = free_binding_level->level_chain; + } + else + newlevel + = (struct binding_level *) xmalloc (sizeof (struct binding_level)); + + *newlevel = clear_binding_level; + + /* Add this level to the front of the chain (stack) of levels that are + active. */ + newlevel->level_chain = current_binding_level; + current_binding_level = newlevel; +} + +/* Exit a binding level. + Pop the level off, and restore the state of the identifier-decl mappings + that were in effect when this level was entered. + + If KEEP is nonzero, this level had explicit declarations, so + and create a "block" (a BLOCK node) for the level + to record its declarations and subblocks for symbol table output. + + If FUNCTIONBODY is nonzero, this level is the body of a function, + so create a block as if KEEP were set and also clear out all + label names. + + If REVERSE is nonzero, reverse the order of decls before putting + them into the BLOCK. */ + +tree +poplevel (keep, reverse, functionbody) + int keep; + int reverse; + int functionbody; +{ + /* Points to a GCC BLOCK tree node. This is the BLOCK node construted for the + binding level that we are about to exit and which is returned by this + routine. */ + tree block = NULL_TREE; + tree decl_chain; + tree decl_node; + tree subblock_chain = current_binding_level->blocks; + tree subblock_node; + int block_previously_created; + + /* Reverse the list of XXXX_DECL nodes if desired. Note that the ..._DECL + nodes chained through the `names' field of current_binding_level are in + reverse order except for PARM_DECL node, which are explicitely stored in + the right order. */ + current_binding_level->names + = decl_chain = (reverse) ? nreverse (current_binding_level->names) + : current_binding_level->names; + + /* Output any nested inline functions within this block which must be + compiled because their address is needed. */ + for (decl_node = decl_chain; decl_node; decl_node = TREE_CHAIN (decl_node)) + if (TREE_CODE (decl_node) == FUNCTION_DECL + && ! TREE_ASM_WRITTEN (decl_node) && TREE_ADDRESSABLE (decl_node) + && DECL_INITIAL (decl_node) != 0) + { + push_function_context (); + output_inline_function (decl_node); + pop_function_context (); + } + + block = 0; + block_previously_created = (current_binding_level->this_block != 0); + if (block_previously_created) + block = current_binding_level->this_block; + else if (keep || functionbody) + block = make_node (BLOCK); + if (block != 0) + { + BLOCK_VARS (block) = keep ? decl_chain : 0; + BLOCK_SUBBLOCKS (block) = subblock_chain; + } + + /* Record the BLOCK node just built as the subblock its enclosing scope. */ + for (subblock_node = subblock_chain; subblock_node; + subblock_node = TREE_CHAIN (subblock_node)) + BLOCK_SUPERCONTEXT (subblock_node) = block; + + /* Clear out the meanings of the local variables of this level. */ + + for (subblock_node = decl_chain; subblock_node; + subblock_node = TREE_CHAIN (subblock_node)) + if (DECL_NAME (subblock_node) != 0) + /* If the identifier was used or addressed via a local extern decl, + don't forget that fact. */ + if (DECL_EXTERNAL (subblock_node)) + { + if (TREE_USED (subblock_node)) + TREE_USED (DECL_NAME (subblock_node)) = 1; + if (TREE_ADDRESSABLE (subblock_node)) + TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (subblock_node)) = 1; + } + + { + /* Pop the current level, and free the structure for reuse. */ + struct binding_level *level = current_binding_level; + current_binding_level = current_binding_level->level_chain; + level->level_chain = free_binding_level; + free_binding_level = level; + } + + if (functionbody) + { + /* This is the top level block of a function. The ..._DECL chain stored + in BLOCK_VARS are the function's parameters (PARM_DECL nodes). Don't + leave them in the BLOCK because they are found in the FUNCTION_DECL + instead. */ + DECL_INITIAL (current_function_decl) = block; + BLOCK_VARS (block) = 0; + } + else if (block) + { + if (!block_previously_created) + current_binding_level->blocks + = chainon (current_binding_level->blocks, block); + } + + /* If we did not make a block for the level just exited, any blocks made for + inner levels (since they cannot be recorded as subblocks in that level) + must be carried forward so they will later become subblocks of something + else. */ + else if (subblock_chain) + current_binding_level->blocks + = chainon (current_binding_level->blocks, subblock_chain); + if (block) + TREE_USED (block) = 1; + + return block; +} + +/* Insert BLOCK at the end of the list of subblocks of the + current binding level. This is used when a BIND_EXPR is expanded, + to handle the BLOCK node inside the BIND_EXPR. */ + +void +insert_block (block) + tree block; +{ + TREE_USED (block) = 1; + current_binding_level->blocks + = chainon (current_binding_level->blocks, block); +} + +/* Set the BLOCK node for the innermost scope + (the one we are currently in). */ + +void +set_block (block) + tree block; +{ + current_binding_level->this_block = block; + current_binding_level->names = chainon (current_binding_level->names, + BLOCK_VARS (block)); + current_binding_level->blocks = chainon (current_binding_level->blocks, + BLOCK_SUBBLOCKS (block)); +} + +/* Records a ..._DECL node DECL as belonging to the current lexical scope. + Returns the ..._DECL node. */ + +tree +pushdecl (decl) + tree decl; +{ + struct binding_level *b; + + /* If at top level, there is no context. But PARM_DECLs always go in the + level of its function. */ + if (global_bindings_p () && TREE_CODE (decl) != PARM_DECL) + { + b = global_binding_level; + DECL_CONTEXT (decl) = 0; + } + else + { + b = current_binding_level; + DECL_CONTEXT (decl) = current_function_decl; + } + + /* Put the declaration on the list. The list of declarations is in reverse + order. The list will be reversed later if necessary. This needs to be + this way for compatibility with the back-end. + + Don't put TYPE_DECLs for UNCONSTRAINED_ARRAY_TYPE into the list. They + will cause trouble with the debugger and aren't needed anyway. */ + if (TREE_CODE (decl) != TYPE_DECL + || TREE_CODE (TREE_TYPE (decl)) != UNCONSTRAINED_ARRAY_TYPE) + { + TREE_CHAIN (decl) = b->names; + b->names = decl; + } + + /* For the declaration of a type, set its name if it either is not already + set, was set to an IDENTIFIER_NODE, indicating an internal name, + or if the previous type name was not derived from a source name. + We'd rather have the type named with a real name and all the pointer + types to the same object have the same POINTER_TYPE node. Code in this + function in c-decl.c makes a copy of the type node here, but that may + cause us trouble with incomplete types, so let's not try it (at least + for now). */ + + if (TREE_CODE (decl) == TYPE_DECL + && DECL_NAME (decl) != 0 + && (TYPE_NAME (TREE_TYPE (decl)) == 0 + || TREE_CODE (TYPE_NAME (TREE_TYPE (decl))) == IDENTIFIER_NODE + || (TREE_CODE (TYPE_NAME (TREE_TYPE (decl))) == TYPE_DECL + && DECL_ARTIFICIAL (TYPE_NAME (TREE_TYPE (decl))) + && ! DECL_ARTIFICIAL (decl)))) + TYPE_NAME (TREE_TYPE (decl)) = decl; + + return decl; +} + +/* Do little here. Set up the standard declarations later after the + front end has been run. */ + +void +init_decl_processing () +{ + /* The structure `tree_identifier' is the GCC tree data structure that holds + IDENTIFIER_NODE nodes. We need to call `set_identifier_size' to tell GCC + that we have not added any language specific fields to IDENTIFIER_NODE + nodes. */ + set_identifier_size (sizeof (struct tree_identifier)); + + lineno = 0; + + /* incomplete_decl_finalize_hook is defined in toplev.c. It needs to be set + by each front end to the appropriate routine that handles incomplete + VAR_DECL nodes. This routine will be invoked by compile_file when a + VAR_DECL node of DECL_SIZE zero is encountered. */ + incomplete_decl_finalize_hook = finish_incomplete_decl; + + /* Make the binding_level structure for global names. */ + current_function_decl = 0; + current_binding_level = 0; + free_binding_level = 0; + pushlevel (0); + global_binding_level = current_binding_level; + + build_common_tree_nodes (0); + + /* In Ada, we use a signed type for SIZETYPE. Use the signed type + corresponding to the size of ptr_mode. Make this here since we need + this before we can expand the GNAT types. */ + set_sizetype (type_for_size (GET_MODE_BITSIZE (ptr_mode), 0)); + build_common_tree_nodes_2 (0); + + pushdecl (build_decl (TYPE_DECL, get_identifier (SIZE_TYPE), sizetype)); + + /* We need to make the integer type before doing anything else. + We stitch this in to the appropriate GNAT type later. */ + pushdecl (build_decl (TYPE_DECL, get_identifier ("integer"), + integer_type_node)); + pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned char"), + char_type_node)); + + ptr_void_type_node = build_pointer_type (void_type_node); + +} + +/* Create the predefined scalar types such as `integer_type_node' needed + in the gcc back-end and initialize the global binding level. */ + +void +init_gigi_decls (long_long_float_type, exception_type) + tree long_long_float_type, exception_type; +{ + tree endlink; + + /* Set the types that GCC and Gigi use from the front end. We would like + to do this for char_type_node, but it needs to correspond to the C + char type. */ + if (TREE_CODE (TREE_TYPE (long_long_float_type)) == INTEGER_TYPE) + { + /* In this case, the builtin floating point types are VAX float, + so make up a type for use. */ + longest_float_type_node = make_node (REAL_TYPE); + TYPE_PRECISION (longest_float_type_node) = LONG_DOUBLE_TYPE_SIZE; + layout_type (longest_float_type_node); + pushdecl (build_decl (TYPE_DECL, get_identifier ("longest float type"), + longest_float_type_node)); + } + else + longest_float_type_node = TREE_TYPE (long_long_float_type); + + except_type_node = TREE_TYPE (exception_type); + + unsigned_type_node = type_for_size (INT_TYPE_SIZE, 1); + pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned int"), + unsigned_type_node)); + + void_type_decl_node + = pushdecl (build_decl (TYPE_DECL, get_identifier ("void"), + void_type_node)); + + void_ftype = build_function_type (void_type_node, NULL_TREE); + ptr_void_ftype = build_pointer_type (void_ftype); + + /* Now declare runtime functions. */ + endlink = tree_cons (NULL_TREE, void_type_node, NULL_TREE); + + /* malloc is a function declaration tree for a function to allocate + memory. */ + malloc_decl = create_subprog_decl (get_identifier ("__gnat_malloc"), + NULL_TREE, + build_function_type (ptr_void_type_node, + tree_cons (NULL_TREE, + sizetype, + endlink)), + NULL_TREE, 0, 1, 1, 0); + + /* free is a function declaration tree for a function to free memory. */ + + free_decl + = create_subprog_decl (get_identifier ("__gnat_free"), NULL_TREE, + build_function_type (void_type_node, + tree_cons (NULL_TREE, + ptr_void_type_node, + endlink)), + NULL_TREE, 0, 1, 1, 0); + + /* Make the types and functions used for exception processing. */ + jmpbuf_type + = build_array_type (type_for_mode (Pmode, 0), + build_index_type (build_int_2 (5, 0))); + pushdecl (build_decl (TYPE_DECL, get_identifier ("JMPBUF_T"), jmpbuf_type)); + jmpbuf_ptr_type = build_pointer_type (jmpbuf_type); + + /* Functions to get and set the jumpbuf pointer for the current thread. */ + get_jmpbuf_decl + = create_subprog_decl + (get_identifier ("system__soft_links__get_jmpbuf_address_soft"), + NULL_TREE, build_function_type (jmpbuf_ptr_type, NULL_TREE), + NULL_TREE, 0, 1, 1, 0); + + set_jmpbuf_decl + = create_subprog_decl + (get_identifier ("system__soft_links__set_jmpbuf_address_soft"), + NULL_TREE, + build_function_type (void_type_node, + tree_cons (NULL_TREE, jmpbuf_ptr_type, endlink)), + NULL_TREE, 0, 1, 1, 0); + + /* Function to get the current exception. */ + get_excptr_decl + = create_subprog_decl + (get_identifier ("system__soft_links__get_gnat_exception"), + NULL_TREE, + build_function_type (build_pointer_type (except_type_node), NULL_TREE), + NULL_TREE, 0, 1, 1, 0); + + /* Function that raise exceptions. */ + raise_nodefer_decl + = create_subprog_decl + (get_identifier ("__gnat_raise_nodefer_with_msg"), NULL_TREE, + build_function_type (void_type_node, + tree_cons (NULL_TREE, + build_pointer_type (except_type_node), + endlink)), + NULL_TREE, 0, 1, 1, 0); + + + /* __gnat_raise_constraint_error takes a string, an integer and never + returns. */ + raise_constraint_error_decl + = create_subprog_decl + (get_identifier ("__gnat_raise_constraint_error"), NULL_TREE, + build_function_type (void_type_node, + tree_cons (NULL_TREE, + build_pointer_type (char_type_node), + tree_cons (NULL_TREE, + integer_type_node, + endlink))), + NULL_TREE, 0, 1, 1, 0); + + /* Likewise for __gnat_raise_program_error. */ + raise_program_error_decl + = create_subprog_decl + (get_identifier ("__gnat_raise_program_error"), NULL_TREE, + build_function_type (void_type_node, + tree_cons (NULL_TREE, + build_pointer_type (char_type_node), + tree_cons (NULL_TREE, + integer_type_node, + endlink))), + NULL_TREE, 0, 1, 1, 0); + + /* Likewise for __gnat_raise_storage_error. */ + raise_storage_error_decl + = create_subprog_decl + (get_identifier ("__gnat_raise_storage_error"), NULL_TREE, + build_function_type (void_type_node, + tree_cons (NULL_TREE, + build_pointer_type (char_type_node), + tree_cons (NULL_TREE, + integer_type_node, + endlink))), + NULL_TREE, 0, 1, 1, 0); + + /* Indicate that these never return. */ + + TREE_THIS_VOLATILE (raise_nodefer_decl) = 1; + TREE_THIS_VOLATILE (raise_constraint_error_decl) = 1; + TREE_THIS_VOLATILE (raise_program_error_decl) = 1; + TREE_THIS_VOLATILE (raise_storage_error_decl) = 1; + + TREE_SIDE_EFFECTS (raise_nodefer_decl) = 1; + TREE_SIDE_EFFECTS (raise_constraint_error_decl) = 1; + TREE_SIDE_EFFECTS (raise_program_error_decl) = 1; + TREE_SIDE_EFFECTS (raise_storage_error_decl) = 1; + + TREE_TYPE (raise_nodefer_decl) + = build_qualified_type (TREE_TYPE (raise_nodefer_decl), + TYPE_QUAL_VOLATILE); + TREE_TYPE (raise_constraint_error_decl) + = build_qualified_type (TREE_TYPE (raise_constraint_error_decl), + TYPE_QUAL_VOLATILE); + TREE_TYPE (raise_program_error_decl) + = build_qualified_type (TREE_TYPE (raise_program_error_decl), + TYPE_QUAL_VOLATILE); + TREE_TYPE (raise_storage_error_decl) + = build_qualified_type (TREE_TYPE (raise_storage_error_decl), + TYPE_QUAL_VOLATILE); + + /* setjmp returns an integer and has one operand, which is a pointer to + a jmpbuf. */ + setjmp_decl + = create_subprog_decl + (get_identifier ("setjmp"), NULL_TREE, + build_function_type (integer_type_node, + tree_cons (NULL_TREE, jmpbuf_ptr_type, endlink)), + NULL_TREE, 0, 1, 1, 0); + + DECL_BUILT_IN_CLASS (setjmp_decl) = BUILT_IN_NORMAL; + DECL_FUNCTION_CODE (setjmp_decl) = BUILT_IN_SETJMP; + + ggc_add_tree_root (gnat_std_decls, + sizeof gnat_std_decls / sizeof gnat_std_decls[0]); +} + +/* This routine is called in tree.c to print an error message for invalid use + of an incomplete type. */ + +void +incomplete_type_error (dont_care_1, dont_care_2) + tree dont_care_1 ATTRIBUTE_UNUSED; + tree dont_care_2 ATTRIBUTE_UNUSED; +{ + gigi_abort (404); +} + +/* This function is called indirectly from toplev.c to handle incomplete + declarations, i.e. VAR_DECL nodes whose DECL_SIZE is zero. To be precise, + compile_file in toplev.c makes an indirect call through the function pointer + incomplete_decl_finalize_hook which is initialized to this routine in + init_decl_processing. */ + +void +finish_incomplete_decl (dont_care) + tree dont_care ATTRIBUTE_UNUSED; +{ + gigi_abort (405); +} + +/* Given a record type (RECORD_TYPE) and a chain of FIELD_DECL + nodes (FIELDLIST), finish constructing the record or union type. + If HAS_REP is nonzero, this record has a rep clause; don't call + layout_type but merely set the size and alignment ourselves. + If DEFER_DEBUG is nonzero, do not call the debugging routines + on this type; it will be done later. */ + +void +finish_record_type (record_type, fieldlist, has_rep, defer_debug) + tree record_type; + tree fieldlist; + int has_rep; + int defer_debug; +{ + enum tree_code code = TREE_CODE (record_type); + tree ada_size = bitsize_zero_node; + tree size = bitsize_zero_node; + tree size_unit = size_zero_node; + tree field; + + TYPE_FIELDS (record_type) = fieldlist; + + if (TYPE_NAME (record_type) != 0 + && TREE_CODE (TYPE_NAME (record_type)) == TYPE_DECL) + TYPE_STUB_DECL (record_type) = TYPE_NAME (record_type); + else + TYPE_STUB_DECL (record_type) + = pushdecl (build_decl (TYPE_DECL, TYPE_NAME (record_type), + record_type)); + + /* We don't need both the typedef name and the record name output in + the debugging information, since they are the same. */ + DECL_ARTIFICIAL (TYPE_STUB_DECL (record_type)) = 1; + + /* Globally initialize the record first. If this is a rep'ed record, + that just means some initializations; otherwise, layout the record. */ + + if (has_rep) + { + TYPE_ALIGN (record_type) = MAX (BITS_PER_UNIT, TYPE_ALIGN (record_type)); + TYPE_MODE (record_type) = BLKmode; + if (TYPE_SIZE (record_type) == 0) + { + TYPE_SIZE (record_type) = bitsize_zero_node; + TYPE_SIZE_UNIT (record_type) = size_zero_node; + } + } + else + { + /* Ensure there isn't a size already set. There can be in an error + case where there is a rep clause but all fields have errors and + no longer have a position. */ + TYPE_SIZE (record_type) = 0; + layout_type (record_type); + } + + /* At this point, the position and size of each field is known. It was + either set before entry by a rep clause, or by laying out the type + above. We now make a pass through the fields (in reverse order for + QUAL_UNION_TYPEs) to compute the Ada size; the GCC size and alignment + (for rep'ed records that are not padding types); and the mode (for + rep'ed records). */ + + if (code == QUAL_UNION_TYPE) + fieldlist = nreverse (fieldlist); + + for (field = fieldlist; field; field = TREE_CHAIN (field)) + { + tree type = TREE_TYPE (field); + tree this_size = DECL_SIZE (field); + tree this_size_unit = DECL_SIZE_UNIT (field); + tree this_ada_size = DECL_SIZE (field); + + if ((TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE + || TREE_CODE (type) == QUAL_UNION_TYPE) + && ! TYPE_IS_FAT_POINTER_P (type) + && ! TYPE_CONTAINS_TEMPLATE_P (type) + && TYPE_ADA_SIZE (type) != 0) + this_ada_size = TYPE_ADA_SIZE (type); + + if (has_rep && ! DECL_BIT_FIELD (field)) + TYPE_ALIGN (record_type) + = MAX (TYPE_ALIGN (record_type), DECL_ALIGN (field)); + + switch (code) + { + case UNION_TYPE: + ada_size = size_binop (MAX_EXPR, ada_size, this_ada_size); + size = size_binop (MAX_EXPR, size, this_size); + size_unit = size_binop (MAX_EXPR, size_unit, this_size_unit); + break; + + case QUAL_UNION_TYPE: + ada_size + = fold (build (COND_EXPR, bitsizetype, DECL_QUALIFIER (field), + this_ada_size, ada_size)); + size = fold (build (COND_EXPR, bitsizetype, DECL_QUALIFIER (field), + this_size, size)); + size_unit = fold (build (COND_EXPR, sizetype, DECL_QUALIFIER (field), + this_size_unit, size_unit)); + break; + + case RECORD_TYPE: + /* Since we know here that all fields are sorted in order of + increasing bit position, the size of the record is one + higher than the ending bit of the last field processed + unless we have a rep clause, since in that case we might + have a field outside a QUAL_UNION_TYPE that has a higher ending + position. So use a MAX in that case. Also, if this field is a + QUAL_UNION_TYPE, we need to take into account the previous size in + the case of empty variants. */ + ada_size + = merge_sizes (ada_size, bit_position (field), this_ada_size, + TREE_CODE (type) == QUAL_UNION_TYPE, has_rep); + size = merge_sizes (size, bit_position (field), this_size, + TREE_CODE (type) == QUAL_UNION_TYPE, has_rep); + size_unit + = merge_sizes (size_unit, byte_position (field), this_size_unit, + TREE_CODE (type) == QUAL_UNION_TYPE, has_rep); + break; + + default: + abort (); + } + } + + if (code == QUAL_UNION_TYPE) + nreverse (fieldlist); + + /* If this is a padding record, we never want to make the size smaller than + what was specified in it, if any. */ + if (TREE_CODE (record_type) == RECORD_TYPE + && TYPE_IS_PADDING_P (record_type) && TYPE_SIZE (record_type) != 0) + { + size = TYPE_SIZE (record_type); + size_unit = TYPE_SIZE_UNIT (record_type); + } + + /* Now set any of the values we've just computed that apply. */ + if (! TYPE_IS_FAT_POINTER_P (record_type) + && ! TYPE_CONTAINS_TEMPLATE_P (record_type)) + TYPE_ADA_SIZE (record_type) = ada_size; + +#ifdef ROUND_TYPE_SIZE + size = ROUND_TYPE_SIZE (record_type, size, TYPE_ALIGN (record_type)); + size_unit = ROUND_TYPE_SIZE_UNIT (record_size, size_unit, + TYPE_ALIGN (record_type) / BITS_PER_UNIT); +#else + size = round_up (size, TYPE_ALIGN (record_type)); + size_unit = round_up (size_unit, TYPE_ALIGN (record_type) / BITS_PER_UNIT); +#endif + + if (has_rep + && ! (TREE_CODE (record_type) == RECORD_TYPE + && TYPE_IS_PADDING_P (record_type) + && TREE_CODE (size) != INTEGER_CST + && contains_placeholder_p (size))) + { + TYPE_SIZE (record_type) = size; + TYPE_SIZE_UNIT (record_type) = size_unit; + } + + if (has_rep) + compute_record_mode (record_type); + + if (! defer_debug) + { + /* If this record is of variable size, rename it so that the + debugger knows it is and make a new, parallel, record + that tells the debugger how the record is laid out. See + exp_dbug.ads. */ + if (TREE_CODE (TYPE_SIZE (record_type)) != INTEGER_CST) + { + tree new_record_type + = make_node (TREE_CODE (record_type) == QUAL_UNION_TYPE + ? UNION_TYPE : TREE_CODE (record_type)); + tree orig_id = DECL_NAME (TYPE_STUB_DECL (record_type)); + tree new_id + = concat_id_with_name (orig_id, + TREE_CODE (record_type) == QUAL_UNION_TYPE + ? "XVU" : "XVE"); + tree last_pos = bitsize_zero_node; + tree old_field; + + TYPE_NAME (new_record_type) = new_id; + TYPE_ALIGN (new_record_type) = BIGGEST_ALIGNMENT; + TYPE_STUB_DECL (new_record_type) + = pushdecl (build_decl (TYPE_DECL, new_id, new_record_type)); + DECL_ARTIFICIAL (TYPE_STUB_DECL (new_record_type)) = 1; + DECL_IGNORED_P (TYPE_STUB_DECL (new_record_type)) + = DECL_IGNORED_P (TYPE_STUB_DECL (record_type)); + TYPE_SIZE (new_record_type) = size_int (TYPE_ALIGN (record_type)); + + /* Now scan all the fields, replacing each field with a new + field corresponding to the new encoding. */ + for (old_field = TYPE_FIELDS (record_type); old_field != 0; + old_field = TREE_CHAIN (old_field)) + { + tree field_type = TREE_TYPE (old_field); + tree field_name = DECL_NAME (old_field); + tree new_field; + tree curpos = bit_position (old_field); + int var = 0; + unsigned int align = 0; + tree pos; + + /* See how the position was modified from the last position. + + There are two basic cases we support: a value was added + to the last position or the last position was rounded to + a boundary and they something was added. Check for the + first case first. If not, see if there is any evidence + of rounding. If so, round the last position and try + again. + + If this is a union, the position can be taken as zero. */ + + if (TREE_CODE (new_record_type) == UNION_TYPE) + pos = bitsize_zero_node, align = 0; + else + pos = compute_related_constant (curpos, last_pos); + + if (pos == 0 && TREE_CODE (curpos) == MULT_EXPR + && TREE_CODE (TREE_OPERAND (curpos, 1)) == INTEGER_CST) + { + align = TREE_INT_CST_LOW (TREE_OPERAND (curpos, 1)); + pos = compute_related_constant (curpos, + round_up (last_pos, align)); + } + else if (pos == 0 && TREE_CODE (curpos) == PLUS_EXPR + && TREE_CODE (TREE_OPERAND (curpos, 1)) == INTEGER_CST + && TREE_CODE (TREE_OPERAND (curpos, 0)) == MULT_EXPR + && host_integerp (TREE_OPERAND + (TREE_OPERAND (curpos, 0), 1), + 1)) + { + align + = tree_low_cst + (TREE_OPERAND (TREE_OPERAND (curpos, 0), 1), 1); + pos = compute_related_constant (curpos, + round_up (last_pos, align)); + } + + /* If we can't compute a position, set it to zero. + + ??? We really should abort here, but it's too much work + to get this correct for all cases. */ + + if (pos == 0) + pos = bitsize_zero_node; + + /* See if this type is variable-size and make a new type + and indicate the indirection if so. */ + if (TREE_CODE (TYPE_SIZE (field_type)) != INTEGER_CST) + { + field_type = build_pointer_type (field_type); + var = 1; + } + + /* Make a new field name, if necessary. */ + if (var || align != 0) + { + char suffix[6]; + + if (align != 0) + sprintf (suffix, "XV%c%u", var ? 'L' : 'A', + align / BITS_PER_UNIT); + else + strcpy (suffix, "XVL"); + + field_name = concat_id_with_name (field_name, suffix); + } + + new_field = create_field_decl (field_name, field_type, + new_record_type, 0, + TYPE_SIZE (field_type), pos, 0); + TREE_CHAIN (new_field) = TYPE_FIELDS (new_record_type); + TYPE_FIELDS (new_record_type) = new_field; + + /* If old_field is a QUAL_UNION_TYPE, take its size as being + zero. The only time it's not the last field of the record + is when there are other components at fixed positions after + it (meaning there was a rep clause for every field) and we + want to be able to encode them. */ + last_pos = size_binop (PLUS_EXPR, bit_position (old_field), + (TREE_CODE (TREE_TYPE (old_field)) + == QUAL_UNION_TYPE) + ? bitsize_zero_node + : TYPE_SIZE (TREE_TYPE (old_field))); + } + + TYPE_FIELDS (new_record_type) + = nreverse (TYPE_FIELDS (new_record_type)); + + rest_of_type_compilation (new_record_type, global_bindings_p ()); + } + + rest_of_type_compilation (record_type, global_bindings_p ()); + } +} + +/* Utility function of above to merge LAST_SIZE, the previous size of a record + with FIRST_BIT and SIZE that describe a field. SPECIAL is nonzero + if this represents a QUAL_UNION_TYPE in which case we must look for + COND_EXPRs and replace a value of zero with the old size. If HAS_REP + is nonzero, we must take the MAX of the end position of this field + with LAST_SIZE. In all other cases, we use FIRST_BIT plus SIZE. + + We return an expression for the size. */ + +static tree +merge_sizes (last_size, first_bit, size, special, has_rep) + tree last_size; + tree first_bit, size; + int special; + int has_rep; +{ + tree type = TREE_TYPE (last_size); + + if (! special || TREE_CODE (size) != COND_EXPR) + { + tree new = size_binop (PLUS_EXPR, first_bit, size); + + if (has_rep) + new = size_binop (MAX_EXPR, last_size, new); + + return new; + } + + return fold (build (COND_EXPR, type, TREE_OPERAND (size, 0), + integer_zerop (TREE_OPERAND (size, 1)) + ? last_size : merge_sizes (last_size, first_bit, + TREE_OPERAND (size, 1), + 1, has_rep), + integer_zerop (TREE_OPERAND (size, 2)) + ? last_size : merge_sizes (last_size, first_bit, + TREE_OPERAND (size, 2), + 1, has_rep))); +} + +/* Utility function of above to see if OP0 and OP1, both of SIZETYPE, are + related by the addition of a constant. Return that constant if so. */ + +static tree +compute_related_constant (op0, op1) + tree op0, op1; +{ + tree op0_var, op1_var; + tree op0_con = split_plus (op0, &op0_var); + tree op1_con = split_plus (op1, &op1_var); + tree result = size_binop (MINUS_EXPR, op0_con, op1_con); + + if (operand_equal_p (op0_var, op1_var, 0)) + return result; + else if (operand_equal_p (op0, size_binop (PLUS_EXPR, op1_var, result), 0)) + return result; + else + return 0; +} + +/* Utility function of above to split a tree OP which may be a sum, into a + constant part, which is returned, and a variable part, which is stored + in *PVAR. *PVAR may be size_zero_node. All operations must be of + sizetype. */ + +static tree +split_plus (in, pvar) + tree in; + tree *pvar; +{ + tree result = bitsize_zero_node; + + while (TREE_CODE (in) == NON_LVALUE_EXPR) + in = TREE_OPERAND (in, 0); + + *pvar = in; + if (TREE_CODE (in) == INTEGER_CST) + { + *pvar = bitsize_zero_node; + return in; + } + else if (TREE_CODE (in) == PLUS_EXPR || TREE_CODE (in) == MINUS_EXPR) + { + tree lhs_var, rhs_var; + tree lhs_con = split_plus (TREE_OPERAND (in, 0), &lhs_var); + tree rhs_con = split_plus (TREE_OPERAND (in, 1), &rhs_var); + + result = size_binop (PLUS_EXPR, result, lhs_con); + result = size_binop (TREE_CODE (in), result, rhs_con); + + if (lhs_var == TREE_OPERAND (in, 0) + && rhs_var == TREE_OPERAND (in, 1)) + return bitsize_zero_node; + + *pvar = size_binop (TREE_CODE (in), lhs_var, rhs_var); + return result; + } + else + return bitsize_zero_node; +} + +/* Return a FUNCTION_TYPE node. RETURN_TYPE is the type returned by the + subprogram. If it is void_type_node, then we are dealing with a procedure, + otherwise we are dealing with a function. PARAM_DECL_LIST is a list of + PARM_DECL nodes that are the subprogram arguments. CICO_LIST is the + copy-in/copy-out list to be stored into TYPE_CICO_LIST. + RETURNS_UNCONSTRAINED is nonzero if the function returns an unconstrained + object. RETURNS_BY_REF is nonzero if the function returns by reference. + RETURNS_WITH_DSP is nonzero if the function is to return with a + depressed stack pointer. */ + +tree +create_subprog_type (return_type, param_decl_list, cico_list, + returns_unconstrained, returns_by_ref, returns_with_dsp) + tree return_type; + tree param_decl_list; + tree cico_list; + int returns_unconstrained, returns_by_ref, returns_with_dsp; +{ + /* A chain of TREE_LIST nodes whose TREE_VALUEs are the data type nodes of + the subprogram formal parameters. This list is generated by traversing the + input list of PARM_DECL nodes. */ + tree param_type_list = NULL; + tree param_decl; + tree type; + + for (param_decl = param_decl_list; param_decl; + param_decl = TREE_CHAIN (param_decl)) + param_type_list = tree_cons (NULL_TREE, TREE_TYPE (param_decl), + param_type_list); + + /* The list of the function parameter types has to be terminated by the void + type to signal to the back-end that we are not dealing with a variable + parameter subprogram, but that the subprogram has a fixed number of + parameters. */ + param_type_list = tree_cons (NULL_TREE, void_type_node, param_type_list); + + /* The list of argument types has been created in reverse + so nreverse it. */ + param_type_list = nreverse (param_type_list); + + type = build_function_type (return_type, param_type_list); + + /* TYPE may have been shared since GCC hashes types. If it has a CICO_LIST + or the new type should, make a copy of TYPE. Likewise for + RETURNS_UNCONSTRAINED and RETURNS_BY_REF. */ + if (TYPE_CI_CO_LIST (type) != 0 || cico_list != 0 + || TYPE_RETURNS_UNCONSTRAINED_P (type) != returns_unconstrained + || TYPE_RETURNS_BY_REF_P (type) != returns_by_ref) + type = copy_type (type); + + TYPE_CI_CO_LIST (type) = cico_list; + TYPE_RETURNS_UNCONSTRAINED_P (type) = returns_unconstrained; + TYPE_RETURNS_STACK_DEPRESSED (type) = returns_with_dsp; + TYPE_RETURNS_BY_REF_P (type) = returns_by_ref; + return type; +} + +/* Return a copy of TYPE but safe to modify in any way. */ + +tree +copy_type (type) + tree type; +{ + tree new = copy_node (type); + + /* copy_node clears this field instead of copying it, because it is + aliased with TREE_CHAIN. */ + TYPE_STUB_DECL (new) = TYPE_STUB_DECL (type); + + TYPE_POINTER_TO (new) = 0; + TYPE_REFERENCE_TO (new) = 0; + TYPE_MAIN_VARIANT (new) = new; + TYPE_NEXT_VARIANT (new) = 0; + + return new; +} + +/* Return an INTEGER_TYPE of SIZETYPE with range MIN to MAX and whose + TYPE_INDEX_TYPE is INDEX. */ + +tree +create_index_type (min, max, index) + tree min, max; + tree index; +{ + /* First build a type for the desired range. */ + tree type = build_index_2_type (min, max); + + /* If this type has the TYPE_INDEX_TYPE we want, return it. Otherwise, if it + doesn't have TYPE_INDEX_TYPE set, set it to INDEX. If TYPE_INDEX_TYPE + is set, but not to INDEX, make a copy of this type with the requested + index type. Note that we have no way of sharing these types, but that's + only a small hole. */ + if (TYPE_INDEX_TYPE (type) == index) + return type; + else if (TYPE_INDEX_TYPE (type) != 0) + type = copy_type (type); + + TYPE_INDEX_TYPE (type) = index; + return type; +} + +/* Return a TYPE_DECL node. TYPE_NAME gives the name of the type (a character + string) and TYPE is a ..._TYPE node giving its data type. + ARTIFICIAL_P is nonzero if this is a declaration that was generated + by the compiler. DEBUG_INFO_P is nonzero if we need to write debugging + information about this type. */ + +tree +create_type_decl (type_name, type, attr_list, artificial_p, debug_info_p) + tree type_name; + tree type; + struct attrib *attr_list; + int artificial_p; + int debug_info_p; +{ + tree type_decl = build_decl (TYPE_DECL, type_name, type); + enum tree_code code = TREE_CODE (type); + + DECL_ARTIFICIAL (type_decl) = artificial_p; + pushdecl (type_decl); + process_attributes (type_decl, attr_list); + + /* Pass type declaration information to the debugger unless this is an + UNCONSTRAINED_ARRAY_TYPE, which the debugger does not support, + and ENUMERAL_TYPE or RECORD_TYPE which is handled separately, + a dummy type, which will be completed later, or a type for which + debugging information was not requested. */ + if (code == UNCONSTRAINED_ARRAY_TYPE || TYPE_IS_DUMMY_P (type) + || ! debug_info_p) + DECL_IGNORED_P (type_decl) = 1; + else if (code != ENUMERAL_TYPE && code != RECORD_TYPE + && ! ((code == POINTER_TYPE || code == REFERENCE_TYPE) + && TYPE_IS_DUMMY_P (TREE_TYPE (type)))) + rest_of_decl_compilation (type_decl, NULL, global_bindings_p (), 0); + + return type_decl; +} + +/* Returns a GCC VAR_DECL node. VAR_NAME gives the name of the variable. + ASM_NAME is its assembler name (if provided). TYPE is its data type + (a GCC ..._TYPE node). VAR_INIT is the GCC tree for an optional initial + expression; NULL_TREE if none. + + CONST_FLAG is nonzero if this variable is constant. + + PUBLIC_FLAG is nonzero if this definition is to be made visible outside of + the current compilation unit. This flag should be set when processing the + variable definitions in a package specification. EXTERN_FLAG is nonzero + when processing an external variable declaration (as opposed to a + definition: no storage is to be allocated for the variable here). + + STATIC_FLAG is only relevant when not at top level. In that case + it indicates whether to always allocate storage to the variable. */ + +tree +create_var_decl (var_name, asm_name, type, var_init, const_flag, public_flag, + extern_flag, static_flag, attr_list) + tree var_name; + tree asm_name; + tree type; + tree var_init; + int const_flag; + int public_flag; + int extern_flag; + int static_flag; + struct attrib *attr_list; +{ + int init_const + = (var_init == 0 + ? 0 + : (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (var_init)) + && (global_bindings_p () || static_flag + ? 0 != initializer_constant_valid_p (var_init, + TREE_TYPE (var_init)) + : TREE_CONSTANT (var_init)))); + tree var_decl + = build_decl ((const_flag && init_const + /* Only make a CONST_DECL for sufficiently-small objects. + We consider complex double "sufficiently-small" */ + && TYPE_SIZE (type) != 0 + && host_integerp (TYPE_SIZE_UNIT (type), 1) + && 0 >= compare_tree_int (TYPE_SIZE_UNIT (type), + GET_MODE_SIZE (DCmode))) + ? CONST_DECL : VAR_DECL, var_name, type); + tree assign_init = 0; + + /* If this is external, throw away any initializations unless this is a + CONST_DECL (meaning we have a constant); they will be done elsewhere. If + we are defining a global here, leave a constant initialization and save + any variable elaborations for the elaboration routine. Otherwise, if + the initializing expression is not the same as TYPE, generate the + initialization with an assignment statement, since it knows how + to do the required adjustents. */ + + if (extern_flag && TREE_CODE (var_decl) != CONST_DECL) + var_init = 0; + + if (global_bindings_p () && var_init != 0 && ! init_const) + { + add_pending_elaborations (var_decl, var_init); + var_init = 0; + } + + else if (var_init != 0 + && ((TYPE_MAIN_VARIANT (TREE_TYPE (var_init)) + != TYPE_MAIN_VARIANT (type)) + || (static_flag && ! init_const))) + assign_init = var_init, var_init = 0; + + DECL_COMMON (var_decl) = !flag_no_common; + DECL_INITIAL (var_decl) = var_init; + TREE_READONLY (var_decl) = const_flag; + DECL_EXTERNAL (var_decl) = extern_flag; + TREE_PUBLIC (var_decl) = public_flag || extern_flag; + TREE_CONSTANT (var_decl) = TREE_CODE (var_decl) == CONST_DECL; + TREE_THIS_VOLATILE (var_decl) = TREE_SIDE_EFFECTS (var_decl) + = TYPE_VOLATILE (type); + + /* At the global binding level we need to allocate static storage for the + variable if and only if its not external. If we are not at the top level + we allocate automatic storage unless requested not to. */ + TREE_STATIC (var_decl) = global_bindings_p () ? !extern_flag : static_flag; + + if (asm_name != 0) + SET_DECL_ASSEMBLER_NAME (var_decl, asm_name); + + process_attributes (var_decl, attr_list); + + /* Add this decl to the current binding level and generate any + needed code and RTL. */ + var_decl = pushdecl (var_decl); + expand_decl (var_decl); + + if (DECL_CONTEXT (var_decl) != 0) + expand_decl_init (var_decl); + + /* If this is volatile, force it into memory. */ + if (TREE_SIDE_EFFECTS (var_decl)) + mark_addressable (var_decl); + + if (TREE_CODE (var_decl) != CONST_DECL) + rest_of_decl_compilation (var_decl, 0, global_bindings_p (), 0); + + if (assign_init != 0) + { + /* If VAR_DECL has a padded type, convert it to the unpadded + type so the assignment is done properly. */ + tree lhs = var_decl; + + if (TREE_CODE (TREE_TYPE (lhs)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (lhs))) + lhs = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (lhs))), lhs); + + expand_expr_stmt (build_binary_op (MODIFY_EXPR, NULL_TREE, lhs, + assign_init)); + } + + return var_decl; +} + +/* Returns a FIELD_DECL node. FIELD_NAME the field name, FIELD_TYPE is its + type, and RECORD_TYPE is the type of the parent. PACKED is nonzero if + this field is in a record type with a "pragma pack". If SIZE is nonzero + it is the specified size for this field. If POS is nonzero, it is the bit + position. If ADDRESSABLE is nonzero, it means we are allowed to take + the address of this field for aliasing purposes. */ + +tree +create_field_decl (field_name, field_type, record_type, packed, size, pos, + addressable) + tree field_name; + tree field_type; + tree record_type; + int packed; + tree size, pos; + int addressable; +{ + tree field_decl = build_decl (FIELD_DECL, field_name, field_type); + + DECL_CONTEXT (field_decl) = record_type; + TREE_READONLY (field_decl) = TREE_READONLY (field_type); + + /* If FIELD_TYPE is BLKmode, we must ensure this is aligned to at least a + byte boundary since GCC cannot handle less-aligned BLKmode bitfields. + If it is a padding type where the inner field is of variable size, it + must be at its natural alignment. Just handle the packed case here; we + will disallow non-aligned rep clauses elsewhere. */ + if (packed && TYPE_MODE (field_type) == BLKmode) + DECL_ALIGN (field_decl) + = ((TREE_CODE (field_type) == RECORD_TYPE + && TYPE_IS_PADDING_P (field_type) + && ! TREE_CONSTANT (DECL_SIZE (TYPE_FIELDS (field_type)))) + ? TYPE_ALIGN (field_type) : BITS_PER_UNIT); + + /* If a size is specified, use it. Otherwise, see if we have a size + to use that may differ from the natural size of the object. */ + if (size != 0) + size = convert (bitsizetype, size); + else if (packed) + { + if (packed == 1 && ! operand_equal_p (rm_size (field_type), + TYPE_SIZE (field_type), 0)) + size = rm_size (field_type); + + /* For a constant size larger than MAX_FIXED_MODE_SIZE, round up to + byte. */ + if (size != 0 && TREE_CODE (size) == INTEGER_CST + && compare_tree_int (size, MAX_FIXED_MODE_SIZE) > 0) + size = round_up (size, BITS_PER_UNIT); + } + + /* Make a bitfield if a size is specified for two reasons: first if the size + differs from the natural size. Second, if the alignment is insufficient. + There are a number of ways the latter can be true. But never make a + bitfield if the type of the field has a nonconstant size. */ + + if (size != 0 && TREE_CODE (size) == INTEGER_CST + && TREE_CODE (TYPE_SIZE (field_type)) == INTEGER_CST + && (! operand_equal_p (TYPE_SIZE (field_type), size, 0) + || (pos != 0 + && ! value_zerop (size_binop (TRUNC_MOD_EXPR, pos, + bitsize_int (TYPE_ALIGN + (field_type))))) + || packed + || (TYPE_ALIGN (record_type) != 0 + && TYPE_ALIGN (record_type) < TYPE_ALIGN (field_type)))) + { + DECL_BIT_FIELD (field_decl) = 1; + DECL_SIZE (field_decl) = size; + if (! packed && pos == 0) + DECL_ALIGN (field_decl) + = (TYPE_ALIGN (record_type) != 0 + ? MIN (TYPE_ALIGN (record_type), TYPE_ALIGN (field_type)) + : TYPE_ALIGN (field_type)); + } + + DECL_PACKED (field_decl) = pos != 0 ? DECL_BIT_FIELD (field_decl) : packed; + DECL_ALIGN (field_decl) + = MAX (DECL_ALIGN (field_decl), + DECL_BIT_FIELD (field_decl) ? 1 + : packed && TYPE_MODE (field_type) != BLKmode ? BITS_PER_UNIT + : TYPE_ALIGN (field_type)); + + if (pos != 0) + { + /* We need to pass in the alignment the DECL is known to have. + This is the lowest-order bit set in POS, but no more than + the alignment of the record, if one is specified. Note + that an alignment of 0 is taken as infinite. */ + unsigned int known_align; + + if (host_integerp (pos, 1)) + known_align = tree_low_cst (pos, 1) & - tree_low_cst (pos, 1); + else + known_align = BITS_PER_UNIT; + + if (TYPE_ALIGN (record_type) + && (known_align == 0 || known_align > TYPE_ALIGN (record_type))) + known_align = TYPE_ALIGN (record_type); + + layout_decl (field_decl, known_align); + SET_DECL_OFFSET_ALIGN (field_decl, BIGGEST_ALIGNMENT); + pos_from_bit (&DECL_FIELD_OFFSET (field_decl), + &DECL_FIELD_BIT_OFFSET (field_decl), + BIGGEST_ALIGNMENT, pos); + + DECL_HAS_REP_P (field_decl) = 1; + } + + /* Mark the decl as nonaddressable if it either is indicated so semantically + or if it is a bit field. */ + DECL_NONADDRESSABLE_P (field_decl) + = ! addressable || DECL_BIT_FIELD (field_decl); + + return field_decl; +} + +/* Subroutine of previous function: return nonzero if EXP, ignoring any side + effects, has the value of zero. */ + +static int +value_zerop (exp) + tree exp; +{ + if (TREE_CODE (exp) == COMPOUND_EXPR) + return value_zerop (TREE_OPERAND (exp, 1)); + + return integer_zerop (exp); +} + +/* Returns a PARM_DECL node. PARAM_NAME is the name of the parameter, + PARAM_TYPE is its type. READONLY is nonzero if the parameter is + readonly (either an IN parameter or an address of a pass-by-ref + parameter). */ + +tree +create_param_decl (param_name, param_type, readonly) + tree param_name; + tree param_type; + int readonly; +{ + tree param_decl = build_decl (PARM_DECL, param_name, param_type); + + DECL_ARG_TYPE (param_decl) = param_type; + DECL_ARG_TYPE_AS_WRITTEN (param_decl) = param_type; + TREE_READONLY (param_decl) = readonly; + return param_decl; +} + +/* Given a DECL and ATTR_LIST, process the listed attributes. */ + +void +process_attributes (decl, attr_list) + tree decl; + struct attrib *attr_list; +{ + for (; attr_list; attr_list = attr_list->next) + switch (attr_list->type) + { + case ATTR_MACHINE_ATTRIBUTE: + decl_attributes (&decl, tree_cons (attr_list->name, attr_list->arg, + NULL_TREE), + ATTR_FLAG_TYPE_IN_PLACE); + break; + + case ATTR_LINK_ALIAS: + TREE_STATIC (decl) = 1; + assemble_alias (decl, attr_list->name); + break; + + case ATTR_WEAK_EXTERNAL: + if (SUPPORTS_WEAK) + declare_weak (decl); + else + post_error ("?weak declarations not supported on this target", + attr_list->error_point); + break; + + case ATTR_LINK_SECTION: +#ifdef ASM_OUTPUT_SECTION_NAME + DECL_SECTION_NAME (decl) + = build_string (IDENTIFIER_LENGTH (attr_list->name), + IDENTIFIER_POINTER (attr_list->name)); + DECL_COMMON (decl) = 0; +#else + post_error ("?section attributes are not supported for this target", + attr_list->error_point); +#endif + break; + } +} + +/* Add some pending elaborations on the list. */ + +void +add_pending_elaborations (var_decl, var_init) + tree var_decl; + tree var_init; +{ + if (var_init != 0) + Check_Elaboration_Code_Allowed (error_gnat_node); + + pending_elaborations + = chainon (pending_elaborations, build_tree_list (var_decl, var_init)); +} + +/* Obtain any pending elaborations and clear the old list. */ + +tree +get_pending_elaborations () +{ + /* Each thing added to the list went on the end; we want it on the + beginning. */ + tree result = TREE_CHAIN (pending_elaborations); + + TREE_CHAIN (pending_elaborations) = 0; + return result; +} + +/* Mark the binding level stack. */ + +static void +mark_binding_level (arg) + PTR arg; +{ + struct binding_level *level = *(struct binding_level **) arg; + + for (; level != 0; level = level->level_chain) + { + ggc_mark_tree (level->names); + ggc_mark_tree (level->blocks); + ggc_mark_tree (level->this_block); + } +} + +/* Mark the pending elaboration list. */ + +static void +mark_e_stack (data) + PTR data; +{ + struct e_stack *p = *((struct e_stack **) data); + + if (p != 0) + { + ggc_mark_tree (p->elab_list); + mark_e_stack (&p->next); + } +} + +/* Return nonzero if there are pending elaborations. */ + +int +pending_elaborations_p () +{ + return TREE_CHAIN (pending_elaborations) != 0; +} + +/* Save a copy of the current pending elaboration list and make a new + one. */ + +void +push_pending_elaborations () +{ + struct e_stack *p = (struct e_stack *) xmalloc (sizeof (struct e_stack)); + + p->next = elist_stack; + p->elab_list = pending_elaborations; + elist_stack = p; + pending_elaborations = build_tree_list (NULL_TREE, NULL_TREE); +} + +/* Pop the stack of pending elaborations. */ + +void +pop_pending_elaborations () +{ + struct e_stack *p = elist_stack; + + pending_elaborations = p->elab_list; + elist_stack = p->next; + free (p); +} + +/* Return the current position in pending_elaborations so we can insert + elaborations after that point. */ + +tree +get_elaboration_location () +{ + return tree_last (pending_elaborations); +} + +/* Insert the current elaborations after ELAB, which is in some elaboration + list. */ + +void +insert_elaboration_list (elab) + tree elab; +{ + tree next = TREE_CHAIN (elab); + + if (TREE_CHAIN (pending_elaborations)) + { + TREE_CHAIN (elab) = TREE_CHAIN (pending_elaborations); + TREE_CHAIN (tree_last (pending_elaborations)) = next; + TREE_CHAIN (pending_elaborations) = 0; + } +} + +/* Returns a LABEL_DECL node for LABEL_NAME. */ + +tree +create_label_decl (label_name) + tree label_name; +{ + tree label_decl = build_decl (LABEL_DECL, label_name, void_type_node); + + DECL_CONTEXT (label_decl) = current_function_decl; + DECL_MODE (label_decl) = VOIDmode; + DECL_SOURCE_LINE (label_decl) = lineno; + DECL_SOURCE_FILE (label_decl) = input_filename; + + return label_decl; +} + +/* Returns a FUNCTION_DECL node. SUBPROG_NAME is the name of the subprogram, + ASM_NAME is its assembler name, SUBPROG_TYPE is its type (a FUNCTION_TYPE + node), PARAM_DECL_LIST is the list of the subprogram arguments (a list of + PARM_DECL nodes chained through the TREE_CHAIN field). + + INLINE_FLAG, PUBLIC_FLAG, and EXTERN_FLAG are used to set the appropriate + fields in the FUNCTION_DECL. */ + +tree +create_subprog_decl (subprog_name, asm_name, subprog_type, param_decl_list, + inline_flag, public_flag, extern_flag, attr_list) + tree subprog_name; + tree asm_name; + tree subprog_type; + tree param_decl_list; + int inline_flag; + int public_flag; + int extern_flag; + struct attrib *attr_list; +{ + tree return_type = TREE_TYPE (subprog_type); + tree subprog_decl = build_decl (FUNCTION_DECL, subprog_name, subprog_type); + + /* If this is a function nested inside an inlined external function, it + means we aren't going to compile the outer function unless it is + actually inlined, so do the same for us. */ + if (current_function_decl != 0 && DECL_INLINE (current_function_decl) + && DECL_EXTERNAL (current_function_decl)) + extern_flag = 1; + + DECL_EXTERNAL (subprog_decl) = extern_flag; + TREE_PUBLIC (subprog_decl) = public_flag; + DECL_INLINE (subprog_decl) = inline_flag; + TREE_READONLY (subprog_decl) = TYPE_READONLY (subprog_type); + TREE_THIS_VOLATILE (subprog_decl) = TYPE_VOLATILE (subprog_type); + TREE_SIDE_EFFECTS (subprog_decl) = TYPE_VOLATILE (subprog_type); + DECL_ARGUMENTS (subprog_decl) = param_decl_list; + DECL_RESULT (subprog_decl) = build_decl (RESULT_DECL, 0, return_type); + + if (asm_name != 0) + DECL_ASSEMBLER_NAME (subprog_decl) = asm_name; + + process_attributes (subprog_decl, attr_list); + + /* Add this decl to the current binding level. */ + subprog_decl = pushdecl (subprog_decl); + + /* Output the assembler code and/or RTL for the declaration. */ + rest_of_decl_compilation (subprog_decl, 0, global_bindings_p (), 0); + + return subprog_decl; +} + +/* Count how deep we are into nested functions. This is because + we shouldn't call the backend function context routines unless we + are in a nested function. */ + +static int function_nesting_depth; + +/* Set up the framework for generating code for SUBPROG_DECL, a subprogram + body. This routine needs to be invoked before processing the declarations + appearing in the subprogram. */ + +void +begin_subprog_body (subprog_decl) + tree subprog_decl; +{ + tree param_decl_list; + tree param_decl; + tree next_param; + + if (function_nesting_depth++ != 0) + push_function_context (); + + announce_function (subprog_decl); + + /* Make this field nonzero so further routines know that this is not + tentative. error_mark_node is replaced below (in poplevel) with the + adequate BLOCK. */ + DECL_INITIAL (subprog_decl) = error_mark_node; + + /* This function exists in static storage. This does not mean `static' in + the C sense! */ + TREE_STATIC (subprog_decl) = 1; + + /* Enter a new binding level. */ + current_function_decl = subprog_decl; + pushlevel (0); + + /* Push all the PARM_DECL nodes onto the current scope (i.e. the scope of the + subprogram body) so that they can be recognized as local variables in the + subprogram. + + The list of PARM_DECL nodes is stored in the right order in + DECL_ARGUMENTS. Since ..._DECL nodes get stored in the reverse order in + which they are transmitted to `pushdecl' we need to reverse the list of + PARM_DECLs if we want it to be stored in the right order. The reason why + we want to make sure the PARM_DECLs are stored in the correct order is + that this list will be retrieved in a few lines with a call to `getdecl' + to store it back into the DECL_ARGUMENTS field. */ + param_decl_list = nreverse (DECL_ARGUMENTS (subprog_decl)); + + for (param_decl = param_decl_list; param_decl; param_decl = next_param) + { + next_param = TREE_CHAIN (param_decl); + TREE_CHAIN (param_decl) = NULL; + pushdecl (param_decl); + } + + /* Store back the PARM_DECL nodes. They appear in the right order. */ + DECL_ARGUMENTS (subprog_decl) = getdecls (); + + init_function_start (subprog_decl, input_filename, lineno); + expand_function_start (subprog_decl, 0); +} + + +/* Finish the definition of the current subprogram and compile it all the way + to assembler language output. */ + +void +end_subprog_body (void) +{ + tree decl; + tree cico_list; + + poplevel (1, 0, 1); + BLOCK_SUPERCONTEXT (DECL_INITIAL (current_function_decl)) + = current_function_decl; + + /* Mark the RESULT_DECL as being in this subprogram. */ + DECL_CONTEXT (DECL_RESULT (current_function_decl)) = current_function_decl; + + expand_function_end (input_filename, lineno, 0); + rest_of_compilation (current_function_decl); + +#if 0 + /* If we're sure this function is defined in this file then mark it + as such */ + if (TREE_ASM_WRITTEN (current_function_decl)) + mark_fn_defined_in_this_file (current_function_decl); +#endif + + /* Throw away any VAR_DECLs we made for OUT parameters; they must + not be seen when we call this function and will be in + unallocated memory anyway. */ + for (cico_list = TYPE_CI_CO_LIST (TREE_TYPE (current_function_decl)); + cico_list != 0; cico_list = TREE_CHAIN (cico_list)) + TREE_VALUE (cico_list) = 0; + + if (DECL_SAVED_INSNS (current_function_decl) == 0) + { + /* Throw away DECL_RTL in any PARM_DECLs unless this function + was saved for inline, in which case the DECL_RTLs are in + preserved memory. */ + for (decl = DECL_ARGUMENTS (current_function_decl); + decl != 0; decl = TREE_CHAIN (decl)) + { + SET_DECL_RTL (decl, 0); + DECL_INCOMING_RTL (decl) = 0; + } + + /* Similarly, discard DECL_RTL of the return value. */ + SET_DECL_RTL (DECL_RESULT (current_function_decl), 0); + + /* But DECL_INITIAL must remain nonzero so we know this + was an actual function definition unless toplev.c decided not + to inline it. */ + if (DECL_INITIAL (current_function_decl) != 0) + DECL_INITIAL (current_function_decl) = error_mark_node; + + DECL_ARGUMENTS (current_function_decl) = 0; + } + + /* If we are not at the bottom of the function nesting stack, pop up to + the containing function. Otherwise show we aren't in any function. */ + if (--function_nesting_depth != 0) + pop_function_context (); + else + current_function_decl = 0; +} + +/* Return a definition for a builtin function named NAME and whose data type + is TYPE. TYPE should be a function type with argument types. + FUNCTION_CODE tells later passes how to compile calls to this function. + See tree.h for its possible values. + + If LIBRARY_NAME is nonzero, use that for DECL_ASSEMBLER_NAME, + the name to be called if we can't opencode the function. */ + +tree +builtin_function (name, type, function_code, class, library_name) + const char *name; + tree type; + int function_code; + enum built_in_class class; + const char *library_name; +{ + tree decl = build_decl (FUNCTION_DECL, get_identifier (name), type); + + DECL_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + if (library_name) + DECL_ASSEMBLER_NAME (decl) = get_identifier (library_name); + + pushdecl (decl); + DECL_BUILT_IN_CLASS (decl) = class; + DECL_FUNCTION_CODE (decl) = function_code; + return decl; +} + +/* Return an integer type with the number of bits of precision given by + PRECISION. UNSIGNEDP is nonzero if the type is unsigned; otherwise + it is a signed type. */ + +tree +type_for_size (precision, unsignedp) + unsigned precision; + int unsignedp; +{ + tree t; + char type_name[20]; + + if (precision <= 2 * MAX_BITS_PER_WORD + && signed_and_unsigned_types[precision][unsignedp] != 0) + return signed_and_unsigned_types[precision][unsignedp]; + + if (unsignedp) + t = make_unsigned_type (precision); + else + t = make_signed_type (precision); + + if (precision <= 2 * MAX_BITS_PER_WORD) + signed_and_unsigned_types[precision][unsignedp] = t; + + if (TYPE_NAME (t) == 0) + { + sprintf (type_name, "%sSIGNED_%d", unsignedp ? "UN" : "", precision); + TYPE_NAME (t) = get_identifier (type_name); + } + + return t; +} + +/* Likewise for floating-point types. */ + +static tree +float_type_for_size (precision, mode) + int precision; + enum machine_mode mode; +{ + tree t; + char type_name[20]; + + if (float_types[(int) mode] != 0) + return float_types[(int) mode]; + + float_types[(int) mode] = t = make_node (REAL_TYPE); + TYPE_PRECISION (t) = precision; + layout_type (t); + + if (TYPE_MODE (t) != mode) + gigi_abort (414); + + if (TYPE_NAME (t) == 0) + { + sprintf (type_name, "FLOAT_%d", precision); + TYPE_NAME (t) = get_identifier (type_name); + } + + return t; +} + +/* Return a data type that has machine mode MODE. UNSIGNEDP selects + an unsigned type; otherwise a signed type is returned. */ + +tree +type_for_mode (mode, unsignedp) + enum machine_mode mode; + int unsignedp; +{ + if (GET_MODE_CLASS (mode) == MODE_FLOAT) + return float_type_for_size (GET_MODE_BITSIZE (mode), mode); + else + return type_for_size (GET_MODE_BITSIZE (mode), unsignedp); +} + +/* Return the unsigned version of a TYPE_NODE, a scalar type. */ + +tree +unsigned_type (type_node) + tree type_node; +{ + tree type = type_for_size (TYPE_PRECISION (type_node), 1); + + if (TREE_CODE (type_node) == INTEGER_TYPE && TYPE_MODULAR_P (type_node)) + { + type = copy_node (type); + TREE_TYPE (type) = type_node; + } + else if (TREE_TYPE (type_node) != 0 + && TREE_CODE (TREE_TYPE (type_node)) == INTEGER_TYPE + && TYPE_MODULAR_P (TREE_TYPE (type_node))) + { + type = copy_node (type); + TREE_TYPE (type) = TREE_TYPE (type_node); + } + + return type; +} + +/* Return the signed version of a TYPE_NODE, a scalar type. */ + +tree +signed_type (type_node) + tree type_node; +{ + tree type = type_for_size (TYPE_PRECISION (type_node), 0); + + if (TREE_CODE (type_node) == INTEGER_TYPE && TYPE_MODULAR_P (type_node)) + { + type = copy_node (type); + TREE_TYPE (type) = type_node; + } + else if (TREE_TYPE (type_node) != 0 + && TREE_CODE (TREE_TYPE (type_node)) == INTEGER_TYPE + && TYPE_MODULAR_P (TREE_TYPE (type_node))) + { + type = copy_node (type); + TREE_TYPE (type) = TREE_TYPE (type_node); + } + + return type; +} + +/* Return a type the same as TYPE except unsigned or signed according to + UNSIGNEDP. */ + +tree +signed_or_unsigned_type (unsignedp, type) + int unsignedp; + tree type; +{ + if (! INTEGRAL_TYPE_P (type) || TREE_UNSIGNED (type) == unsignedp) + return type; + else + return type_for_size (TYPE_PRECISION (type), unsignedp); +} + +/* EXP is an expression for the size of an object. If this size contains + discriminant references, replace them with the maximum (if MAX_P) or + minimum (if ! MAX_P) possible value of the discriminant. */ + +tree +max_size (exp, max_p) + tree exp; + int max_p; +{ + enum tree_code code = TREE_CODE (exp); + tree type = TREE_TYPE (exp); + + switch (TREE_CODE_CLASS (code)) + { + case 'd': + case 'c': + return exp; + + case 'x': + if (code == TREE_LIST) + return tree_cons (TREE_PURPOSE (exp), + max_size (TREE_VALUE (exp), max_p), + TREE_CHAIN (exp) != 0 + ? max_size (TREE_CHAIN (exp), max_p) : 0); + break; + + case 'r': + /* If this contains a PLACEHOLDER_EXPR, it is the thing we want to + modify. Otherwise, we abort since it is something we can't + handle. */ + if (! contains_placeholder_p (exp)) + gigi_abort (406); + + type = TREE_TYPE (TREE_OPERAND (exp, 1)); + return + max_size (max_p ? TYPE_MAX_VALUE (type) : TYPE_MIN_VALUE (type), 1); + + case '<': + return max_p ? size_one_node : size_zero_node; + + case '1': + case '2': + case 'e': + switch (TREE_CODE_LENGTH (code)) + { + case 1: + if (code == NON_LVALUE_EXPR) + return max_size (TREE_OPERAND (exp, 0), max_p); + else + return + fold (build1 (code, type, + max_size (TREE_OPERAND (exp, 0), + code == NEGATE_EXPR ? ! max_p : max_p))); + + case 2: + if (code == RTL_EXPR) + gigi_abort (407); + else if (code == COMPOUND_EXPR) + return max_size (TREE_OPERAND (exp, 1), max_p); + else if (code == WITH_RECORD_EXPR) + return exp; + + { + tree lhs = max_size (TREE_OPERAND (exp, 0), max_p); + tree rhs = max_size (TREE_OPERAND (exp, 1), + code == MINUS_EXPR ? ! max_p : max_p); + + /* Special-case wanting the maximum value of a MIN_EXPR. + In that case, if one side overflows, return the other. + sizetype is signed, but we know sizes are non-negative. + Likewise, handle a MINUS_EXPR or PLUS_EXPR with the LHS + overflowing or the maximum possible value and the RHS + a variable. */ + if (max_p && code == MIN_EXPR && TREE_OVERFLOW (rhs)) + return lhs; + else if (max_p && code == MIN_EXPR && TREE_OVERFLOW (lhs)) + return rhs; + else if ((code == MINUS_EXPR || code == PLUS_EXPR) + && (TREE_OVERFLOW (lhs) + || operand_equal_p (lhs, TYPE_MAX_VALUE (type), 0)) + && ! TREE_CONSTANT (rhs)) + return lhs; + else + return fold (build (code, type, lhs, rhs)); + } + + case 3: + if (code == SAVE_EXPR) + return exp; + else if (code == COND_EXPR) + return fold (build (MAX_EXPR, type, + max_size (TREE_OPERAND (exp, 1), max_p), + max_size (TREE_OPERAND (exp, 2), max_p))); + else if (code == CALL_EXPR && TREE_OPERAND (exp, 1) != 0) + return build (CALL_EXPR, type, TREE_OPERAND (exp, 0), + max_size (TREE_OPERAND (exp, 1), max_p)); + } + } + + gigi_abort (408); +} + +/* Build a template of type TEMPLATE_TYPE from the array bounds of ARRAY_TYPE. + EXPR is an expression that we can use to locate any PLACEHOLDER_EXPRs. + Return a constructor for the template. */ + +tree +build_template (template_type, array_type, expr) + tree template_type; + tree array_type; + tree expr; +{ + tree template_elts = NULL_TREE; + tree bound_list = NULL_TREE; + tree field; + + if (TREE_CODE (array_type) == RECORD_TYPE + && (TYPE_IS_PADDING_P (array_type) + || TYPE_LEFT_JUSTIFIED_MODULAR_P (array_type))) + array_type = TREE_TYPE (TYPE_FIELDS (array_type)); + + if (TREE_CODE (array_type) == ARRAY_TYPE + || (TREE_CODE (array_type) == INTEGER_TYPE + && TYPE_HAS_ACTUAL_BOUNDS_P (array_type))) + bound_list = TYPE_ACTUAL_BOUNDS (array_type); + + /* First make the list for a CONSTRUCTOR for the template. Go down the + field list of the template instead of the type chain because this + array might be an Ada array of arrays and we can't tell where the + nested arrays stop being the underlying object. */ + + for (field = TYPE_FIELDS (template_type); field; + (bound_list != 0 + ? (bound_list = TREE_CHAIN (bound_list)) + : (array_type = TREE_TYPE (array_type))), + field = TREE_CHAIN (TREE_CHAIN (field))) + { + tree bounds, min, max; + + /* If we have a bound list, get the bounds from there. Likewise + for an ARRAY_TYPE. Otherwise, if expr is a PARM_DECL with + DECL_BY_COMPONENT_PTR_P, use the bounds of the field in the template. + This will give us a maximum range. */ + if (bound_list != 0) + bounds = TREE_VALUE (bound_list); + else if (TREE_CODE (array_type) == ARRAY_TYPE) + bounds = TYPE_INDEX_TYPE (TYPE_DOMAIN (array_type)); + else if (expr != 0 && TREE_CODE (expr) == PARM_DECL + && DECL_BY_COMPONENT_PTR_P (expr)) + bounds = TREE_TYPE (field); + else + gigi_abort (411); + + min = convert (TREE_TYPE (TREE_CHAIN (field)), TYPE_MIN_VALUE (bounds)); + max = convert (TREE_TYPE (field), TYPE_MAX_VALUE (bounds)); + + /* If either MIN or MAX involve a PLACEHOLDER_EXPR, we must + surround them with a WITH_RECORD_EXPR giving EXPR as the + OBJECT. */ + if (! TREE_CONSTANT (min) && contains_placeholder_p (min)) + min = build (WITH_RECORD_EXPR, TREE_TYPE (min), min, expr); + if (! TREE_CONSTANT (max) && contains_placeholder_p (max)) + max = build (WITH_RECORD_EXPR, TREE_TYPE (max), max, expr); + + template_elts = tree_cons (TREE_CHAIN (field), max, + tree_cons (field, min, template_elts)); + } + + return build_constructor (template_type, nreverse (template_elts)); +} + +/* Build a VMS descriptor from a Mechanism_Type, which must specify + a descriptor type, and the GCC type of an object. Each FIELD_DECL + in the type contains in its DECL_INITIAL the expression to use when + a constructor is made for the type. GNAT_ENTITY is a gnat node used + to print out an error message if the mechanism cannot be applied to + an object of that type and also for the name. */ + +tree +build_vms_descriptor (type, mech, gnat_entity) + tree type; + Mechanism_Type mech; + Entity_Id gnat_entity; +{ + tree record_type = make_node (RECORD_TYPE); + tree field_list = 0; + int class; + int dtype = 0; + tree inner_type; + int ndim; + int i; + tree *idx_arr; + tree tem; + + /* If TYPE is an unconstrained array, use the underlying array type. */ + if (TREE_CODE (type) == UNCONSTRAINED_ARRAY_TYPE) + type = TREE_TYPE (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (type)))); + + /* If this is an array, compute the number of dimensions in the array, + get the index types, and point to the inner type. */ + if (TREE_CODE (type) != ARRAY_TYPE) + ndim = 0; + else + for (ndim = 1, inner_type = type; + TREE_CODE (TREE_TYPE (inner_type)) == ARRAY_TYPE + && TYPE_MULTI_ARRAY_P (TREE_TYPE (inner_type)); + ndim++, inner_type = TREE_TYPE (inner_type)) + ; + + idx_arr = (tree *) alloca (ndim * sizeof (tree)); + + if (mech != By_Descriptor_NCA + && TREE_CODE (type) == ARRAY_TYPE && TYPE_CONVENTION_FORTRAN_P (type)) + for (i = ndim - 1, inner_type = type; + i >= 0; + i--, inner_type = TREE_TYPE (inner_type)) + idx_arr[i] = TYPE_DOMAIN (inner_type); + else + for (i = 0, inner_type = type; + i < ndim; + i++, inner_type = TREE_TYPE (inner_type)) + idx_arr[i] = TYPE_DOMAIN (inner_type); + + /* Now get the DTYPE value. */ + switch (TREE_CODE (type)) + { + case INTEGER_TYPE: + case ENUMERAL_TYPE: + if (TYPE_VAX_FLOATING_POINT_P (type)) + switch ((int) TYPE_DIGITS_VALUE (type)) + { + case 6: + dtype = 10; + break; + case 9: + dtype = 11; + break; + case 15: + dtype = 27; + break; + } + else + switch (GET_MODE_BITSIZE (TYPE_MODE (type))) + { + case 8: + dtype = TREE_UNSIGNED (type) ? 2 : 6; + break; + case 16: + dtype = TREE_UNSIGNED (type) ? 3 : 7; + break; + case 32: + dtype = TREE_UNSIGNED (type) ? 4 : 8; + break; + case 64: + dtype = TREE_UNSIGNED (type) ? 5 : 9; + break; + case 128: + dtype = TREE_UNSIGNED (type) ? 25 : 26; + break; + } + break; + + case REAL_TYPE: + dtype = GET_MODE_BITSIZE (TYPE_MODE (type)) == 32 ? 52 : 53; + break; + + case COMPLEX_TYPE: + if (TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE + && TYPE_VAX_FLOATING_POINT_P (type)) + switch ((int) TYPE_DIGITS_VALUE (type)) + { + case 6: + dtype = 12; + break; + case 9: + dtype = 13; + break; + case 15: + dtype = 29; + } + else + dtype = GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (type))) == 32 ? 54: 55; + break; + + case ARRAY_TYPE: + dtype = 14; + break; + + default: + break; + } + + /* Get the CLASS value. */ + switch (mech) + { + case By_Descriptor_A: + class = 4; + break; + case By_Descriptor_NCA: + class = 10; + break; + case By_Descriptor_SB: + class = 15; + break; + default: + class = 1; + } + + /* Make the type for a descriptor for VMS. The first four fields + are the same for all types. */ + + field_list + = chainon (field_list, + make_descriptor_field + ("LENGTH", type_for_size (16, 1), record_type, + size_in_bytes (mech == By_Descriptor_A ? inner_type : type))); + + field_list = chainon (field_list, + make_descriptor_field ("DTYPE", type_for_size (8, 1), + record_type, size_int (dtype))); + field_list = chainon (field_list, + make_descriptor_field ("CLASS", type_for_size (8, 1), + record_type, size_int (class))); + + field_list + = chainon (field_list, + make_descriptor_field ("POINTER", + build_pointer_type (type), + record_type, + build1 (ADDR_EXPR, + build_pointer_type (type), + build (PLACEHOLDER_EXPR, + type)))); + + switch (mech) + { + case By_Descriptor: + case By_Descriptor_S: + break; + + case By_Descriptor_SB: + field_list + = chainon (field_list, + make_descriptor_field + ("SB_L1", type_for_size (32, 1), record_type, + TREE_CODE (type) == ARRAY_TYPE + ? TYPE_MIN_VALUE (TYPE_DOMAIN (type)) : size_zero_node)); + field_list + = chainon (field_list, + make_descriptor_field + ("SB_L2", type_for_size (32, 1), record_type, + TREE_CODE (type) == ARRAY_TYPE + ? TYPE_MAX_VALUE (TYPE_DOMAIN (type)) : size_zero_node)); + break; + + case By_Descriptor_A: + case By_Descriptor_NCA: + field_list = chainon (field_list, + make_descriptor_field ("SCALE", + type_for_size (8, 1), + record_type, + size_zero_node)); + + field_list = chainon (field_list, + make_descriptor_field ("DIGITS", + type_for_size (8, 1), + record_type, + size_zero_node)); + + field_list + = chainon (field_list, + make_descriptor_field + ("AFLAGS", type_for_size (8, 1), record_type, + size_int (mech == By_Descriptor_NCA + ? 0 + /* Set FL_COLUMN, FL_COEFF, and FL_BOUNDS. */ + : (TREE_CODE (type) == ARRAY_TYPE + && TYPE_CONVENTION_FORTRAN_P (type) + ? 224 : 192)))); + + field_list = chainon (field_list, + make_descriptor_field ("DIMCT", + type_for_size (8, 1), + record_type, + size_int (ndim))); + + field_list = chainon (field_list, + make_descriptor_field ("ARSIZE", + type_for_size (32, 1), + record_type, + size_in_bytes (type))); + + /* Now build a pointer to the 0,0,0... element. */ + tem = build (PLACEHOLDER_EXPR, type); + for (i = 0, inner_type = type; i < ndim; + i++, inner_type = TREE_TYPE (inner_type)) + tem = build (ARRAY_REF, TREE_TYPE (inner_type), tem, + convert (TYPE_DOMAIN (inner_type), size_zero_node)); + + field_list + = chainon (field_list, + make_descriptor_field + ("A0", build_pointer_type (inner_type), record_type, + build1 (ADDR_EXPR, build_pointer_type (inner_type), tem))); + + /* Next come the addressing coefficients. */ + tem = size_int (1); + for (i = 0; i < ndim; i++) + { + char fname[3]; + tree idx_length + = size_binop (MULT_EXPR, tem, + size_binop (PLUS_EXPR, + size_binop (MINUS_EXPR, + TYPE_MAX_VALUE (idx_arr[i]), + TYPE_MIN_VALUE (idx_arr[i])), + size_int (1))); + + fname[0] = (mech == By_Descriptor_NCA ? 'S' : 'M'); + fname[1] = '0' + i, fname[2] = 0; + field_list = chainon (field_list, + make_descriptor_field (fname, + type_for_size (32, 1), + record_type, + idx_length)); + + if (mech == By_Descriptor_NCA) + tem = idx_length; + } + + /* Finally here are the bounds. */ + for (i = 0; i < ndim; i++) + { + char fname[3]; + + fname[0] = 'L', fname[1] = '0' + i, fname[2] = 0; + field_list + = chainon (field_list, + make_descriptor_field + (fname, type_for_size (32, 1), record_type, + TYPE_MIN_VALUE (idx_arr[i]))); + + fname[0] = 'U'; + field_list + = chainon (field_list, + make_descriptor_field + (fname, type_for_size (32, 1), record_type, + TYPE_MAX_VALUE (idx_arr[i]))); + } + break; + + default: + post_error ("unsupported descriptor type for &", gnat_entity); + } + + finish_record_type (record_type, field_list, 0, 1); + pushdecl (build_decl (TYPE_DECL, create_concat_name (gnat_entity, "DESC"), + record_type)); + + return record_type; +} + +/* Utility routine for above code to make a field. */ + +static tree +make_descriptor_field (name, type, rec_type, initial) + const char *name; + tree type; + tree rec_type; + tree initial; +{ + tree field + = create_field_decl (get_identifier (name), type, rec_type, 0, 0, 0, 0); + + DECL_INITIAL (field) = initial; + return field; +} + +/* Build a type to be used to represent an aliased object whose nominal + type is an unconstrained array. This consists of a RECORD_TYPE containing + a field of TEMPLATE_TYPE and a field of OBJECT_TYPE, which is an + ARRAY_TYPE. If ARRAY_TYPE is that of the unconstrained array, this + is used to represent an arbitrary unconstrained object. Use NAME + as the name of the record. */ + +tree +build_unc_object_type (template_type, object_type, name) + tree template_type; + tree object_type; + tree name; +{ + tree type = make_node (RECORD_TYPE); + tree template_field = create_field_decl (get_identifier ("BOUNDS"), + template_type, type, 0, 0, 0, 1); + tree array_field = create_field_decl (get_identifier ("ARRAY"), object_type, + type, 0, 0, 0, 1); + + TYPE_NAME (type) = name; + TYPE_CONTAINS_TEMPLATE_P (type) = 1; + finish_record_type (type, + chainon (chainon (NULL_TREE, template_field), + array_field), + 0, 0); + + return type; +} + +/* Update anything previously pointing to OLD_TYPE to point to NEW_TYPE. In + the normal case this is just two adjustments, but we have more to do + if NEW is an UNCONSTRAINED_ARRAY_TYPE. */ + +void +update_pointer_to (old_type, new_type) + tree old_type; + tree new_type; +{ + tree ptr = TYPE_POINTER_TO (old_type); + tree ref = TYPE_REFERENCE_TO (old_type); + + if ((ptr == 0 && ref == 0) || old_type == new_type) + return; + + /* First handle the simple case. */ + if (TREE_CODE (new_type) != UNCONSTRAINED_ARRAY_TYPE) + { + if (ptr != 0) + TREE_TYPE (ptr) = new_type; + TYPE_POINTER_TO (new_type) = ptr; + + if (ref != 0) + TREE_TYPE (ref) = new_type; + TYPE_REFERENCE_TO (new_type) = ref; + + if (ptr != 0 && TYPE_NAME (ptr) != 0 + && TREE_CODE (TYPE_NAME (ptr)) == TYPE_DECL + && TREE_CODE (new_type) != ENUMERAL_TYPE) + rest_of_decl_compilation (TYPE_NAME (ptr), NULL, + global_bindings_p (), 0); + if (ref != 0 && TYPE_NAME (ref) != 0 + && TREE_CODE (TYPE_NAME (ref)) == TYPE_DECL + && TREE_CODE (new_type) != ENUMERAL_TYPE) + rest_of_decl_compilation (TYPE_NAME (ref), NULL, + global_bindings_p (), 0); + } + + /* Now deal with the unconstrained array case. In this case the "pointer" + is actually a RECORD_TYPE where the types of both fields are + pointers to void. In that case, copy the field list from the + old type to the new one and update the fields' context. */ + else if (TREE_CODE (ptr) != RECORD_TYPE || ! TYPE_IS_FAT_POINTER_P (ptr)) + gigi_abort (412); + + else + { + tree new_obj_rec = TYPE_OBJECT_RECORD_TYPE (new_type); + tree ptr_temp_type; + tree new_ref; + tree var; + + TYPE_FIELDS (ptr) = TYPE_FIELDS (TYPE_POINTER_TO (new_type)); + DECL_CONTEXT (TYPE_FIELDS (ptr)) = ptr; + DECL_CONTEXT (TREE_CHAIN (TYPE_FIELDS (ptr))) = ptr; + + /* Rework the PLACEHOLDER_EXPR inside the reference to the + template bounds. + + ??? This is now the only use of gnat_substitute_in_type, which + is now a very "heavy" routine to do this, so it should be replaced + at some point. */ + ptr_temp_type = TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (ptr))); + new_ref = build (COMPONENT_REF, ptr_temp_type, + build (PLACEHOLDER_EXPR, ptr), + TREE_CHAIN (TYPE_FIELDS (ptr))); + + update_pointer_to + (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (ptr))), + gnat_substitute_in_type (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (ptr))), + TREE_CHAIN (TYPE_FIELDS (ptr)), new_ref)); + + for (var = TYPE_MAIN_VARIANT (ptr); var; var = TYPE_NEXT_VARIANT (var)) + TYPE_UNCONSTRAINED_ARRAY (var) = new_type; + + TYPE_POINTER_TO (new_type) = TYPE_REFERENCE_TO (new_type) + = TREE_TYPE (new_type) = ptr; + + /* Now handle updating the allocation record, what the thin pointer + points to. Update all pointers from the old record into the new + one, update the types of the fields, and recompute the size. */ + + update_pointer_to (TYPE_OBJECT_RECORD_TYPE (old_type), new_obj_rec); + + TREE_TYPE (TYPE_FIELDS (new_obj_rec)) = TREE_TYPE (ptr_temp_type); + TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (new_obj_rec))) + = TREE_TYPE (TREE_TYPE (TYPE_FIELDS (ptr))); + DECL_SIZE (TREE_CHAIN (TYPE_FIELDS (new_obj_rec))) + = TYPE_SIZE (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (ptr)))); + DECL_SIZE_UNIT (TREE_CHAIN (TYPE_FIELDS (new_obj_rec))) + = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (ptr)))); + + TYPE_SIZE (new_obj_rec) + = size_binop (PLUS_EXPR, + DECL_SIZE (TYPE_FIELDS (new_obj_rec)), + DECL_SIZE (TREE_CHAIN (TYPE_FIELDS (new_obj_rec)))); + TYPE_SIZE_UNIT (new_obj_rec) + = size_binop (PLUS_EXPR, + DECL_SIZE_UNIT (TYPE_FIELDS (new_obj_rec)), + DECL_SIZE_UNIT (TREE_CHAIN (TYPE_FIELDS (new_obj_rec)))); + rest_of_type_compilation (ptr, global_bindings_p ()); + } +} + +/* Convert a pointer to a constrained array into a pointer to a fat + pointer. This involves making or finding a template. */ + +static tree +convert_to_fat_pointer (type, expr) + tree type; + tree expr; +{ + tree template_type = TREE_TYPE (TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (type)))); + tree template, template_addr; + tree etype = TREE_TYPE (expr); + + /* If EXPR is a constant of zero, we make a fat pointer that has a null + pointer to the template and array. */ + if (integer_zerop (expr)) + return + build_constructor + (type, + tree_cons (TYPE_FIELDS (type), + convert (TREE_TYPE (TYPE_FIELDS (type)), expr), + tree_cons (TREE_CHAIN (TYPE_FIELDS (type)), + convert (build_pointer_type (template_type), + expr), + NULL_TREE))); + + /* If EXPR is a thin pointer, make the template and data from the record. */ + + else if (TYPE_THIN_POINTER_P (etype)) + { + tree fields = TYPE_FIELDS (TREE_TYPE (etype)); + + expr = save_expr (expr); + if (TREE_CODE (expr) == ADDR_EXPR) + expr = TREE_OPERAND (expr, 0); + else + expr = build1 (INDIRECT_REF, TREE_TYPE (etype), expr); + + template = build_component_ref (expr, NULL_TREE, fields); + expr = build_unary_op (ADDR_EXPR, NULL_TREE, + build_component_ref (expr, NULL_TREE, + TREE_CHAIN (fields))); + } + else + /* Otherwise, build the constructor for the template. */ + template = build_template (template_type, TREE_TYPE (etype), expr); + + template_addr = build_unary_op (ADDR_EXPR, NULL_TREE, template); + + /* The result is a CONSTRUCTOR for the fat pointer. */ + return + build_constructor (type, + tree_cons (TYPE_FIELDS (type), expr, + tree_cons (TREE_CHAIN (TYPE_FIELDS (type)), + template_addr, NULL_TREE))); +} + +/* Convert to a thin pointer type, TYPE. The only thing we know how to convert + is something that is a fat pointer, so convert to it first if it EXPR + is not already a fat pointer. */ + +static tree +convert_to_thin_pointer (type, expr) + tree type; + tree expr; +{ + if (! TYPE_FAT_POINTER_P (TREE_TYPE (expr))) + expr + = convert_to_fat_pointer + (TREE_TYPE (TYPE_UNCONSTRAINED_ARRAY (TREE_TYPE (type))), expr); + + /* We get the pointer to the data and use a NOP_EXPR to make it the + proper GCC type. */ + expr = build_component_ref (expr, NULL_TREE, TYPE_FIELDS (TREE_TYPE (expr))); + expr = build1 (NOP_EXPR, type, expr); + + return expr; +} + +/* Create an expression whose value is that of EXPR, + converted to type TYPE. The TREE_TYPE of the value + is always TYPE. This function implements all reasonable + conversions; callers should filter out those that are + not permitted by the language being compiled. */ + +tree +convert (type, expr) + tree type, expr; +{ + enum tree_code code = TREE_CODE (type); + tree etype = TREE_TYPE (expr); + enum tree_code ecode = TREE_CODE (etype); + tree tem; + + /* If EXPR is already the right type, we are done. */ + if (type == etype) + return expr; + + /* If EXPR is a WITH_RECORD_EXPR, do the conversion inside and then make a + new one. */ + if (TREE_CODE (expr) == WITH_RECORD_EXPR) + return build (WITH_RECORD_EXPR, type, + convert (type, TREE_OPERAND (expr, 0)), + TREE_OPERAND (expr, 1)); + + /* If the input type has padding, remove it by doing a component reference + to the field. If the output type has padding, make a constructor + to build the record. If both input and output have padding and are + of variable size, do this as an unchecked conversion. */ + if (ecode == RECORD_TYPE && code == RECORD_TYPE + && TYPE_IS_PADDING_P (type) && TYPE_IS_PADDING_P (etype) + && (! TREE_CONSTANT (TYPE_SIZE (type)) + || ! TREE_CONSTANT (TYPE_SIZE (etype)))) + ; + else if (ecode == RECORD_TYPE && TYPE_IS_PADDING_P (etype)) + { + /* If we have just converted to this padded type, just get + the inner expression. */ + if (TREE_CODE (expr) == CONSTRUCTOR + && CONSTRUCTOR_ELTS (expr) != 0 + && TREE_PURPOSE (CONSTRUCTOR_ELTS (expr)) == TYPE_FIELDS (etype)) + return TREE_VALUE (CONSTRUCTOR_ELTS (expr)); + else + return convert (type, build_component_ref (expr, NULL_TREE, + TYPE_FIELDS (etype))); + } + else if (code == RECORD_TYPE && TYPE_IS_PADDING_P (type)) + { + /* If we previously converted from another type and our type is + of variable size, remove the conversion to avoid the need for + variable-size temporaries. */ + if (TREE_CODE (expr) == UNCHECKED_CONVERT_EXPR + && ! TREE_CONSTANT (TYPE_SIZE (type))) + expr = TREE_OPERAND (expr, 0); + + /* If we are just removing the padding from expr, convert the original + object if we have variable size. That will avoid the need + for some variable-size temporaries. */ + if (TREE_CODE (expr) == COMPONENT_REF + && TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (TREE_OPERAND (expr, 0))) + && ! TREE_CONSTANT (TYPE_SIZE (type))) + return convert (type, TREE_OPERAND (expr, 0)); + + /* If the result type is a padded type with a self-referentially-sized + field and the expression type is a record, do this as an + unchecked converstion. */ + else if (TREE_CODE (DECL_SIZE (TYPE_FIELDS (type))) != INTEGER_CST + && contains_placeholder_p (DECL_SIZE (TYPE_FIELDS (type))) + && TREE_CODE (etype) == RECORD_TYPE) + return unchecked_convert (type, expr); + + else + return + build_constructor (type, + tree_cons (TYPE_FIELDS (type), + convert (TREE_TYPE + (TYPE_FIELDS (type)), + expr), + NULL_TREE)); + } + + /* If the input is a biased type, adjust first. */ + if (ecode == INTEGER_TYPE && TYPE_BIASED_REPRESENTATION_P (etype)) + return convert (type, fold (build (PLUS_EXPR, TREE_TYPE (etype), + fold (build1 (GNAT_NOP_EXPR, + TREE_TYPE (etype), expr)), + TYPE_MIN_VALUE (etype)))); + + /* If the input is a left-justified modular type, we need to extract + the actual object before converting it to any other type with the + exception of an unconstrained array. */ + if (ecode == RECORD_TYPE && TYPE_LEFT_JUSTIFIED_MODULAR_P (etype) + && code != UNCONSTRAINED_ARRAY_TYPE) + return convert (type, build_component_ref (expr, NULL_TREE, + TYPE_FIELDS (etype))); + + /* If converting a type that does not contain a template into one + that does, convert to the data type and then build the template. */ + if (code == RECORD_TYPE && TYPE_CONTAINS_TEMPLATE_P (type) + && ! (ecode == RECORD_TYPE && TYPE_CONTAINS_TEMPLATE_P (etype))) + { + tree obj_type = TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (type))); + + return + build_constructor + (type, + tree_cons (TYPE_FIELDS (type), + build_template (TREE_TYPE (TYPE_FIELDS (type)), + obj_type, NULL_TREE), + tree_cons (TREE_CHAIN (TYPE_FIELDS (type)), + convert (obj_type, expr), NULL_TREE))); + } + + /* There are some special cases of expressions that we process + specially. */ + switch (TREE_CODE (expr)) + { + case ERROR_MARK: + return expr; + + case TRANSFORM_EXPR: + case NULL_EXPR: + /* Just set its type here. For TRANSFORM_EXPR, we will do the actual + conversion in gnat_expand_expr. NULL_EXPR does not represent + and actual value, so no conversion is needed. */ + TREE_TYPE (expr) = type; + return expr; + + case STRING_CST: + case CONSTRUCTOR: + /* If we are converting a STRING_CST to another constrained array type, + just make a new one in the proper type. Likewise for a + CONSTRUCTOR. But if the mode of the type is different, we must + ensure a new RTL is made for the constant. */ + if (code == ecode && AGGREGATE_TYPE_P (etype) + && ! (TREE_CODE (TYPE_SIZE (etype)) == INTEGER_CST + && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)) + { + expr = copy_node (expr); + TREE_TYPE (expr) = type; + + if (TYPE_MODE (type) != TYPE_MODE (etype)) + TREE_CST_RTL (expr) = 0; + + return expr; + } + break; + + case COMPONENT_REF: + /* If we are converting between two aggregate types of the same + kind, size, mode, and alignment, just make a new COMPONENT_REF. + This avoid unneeded conversions which makes reference computations + more complex. */ + if (code == ecode && TYPE_MODE (type) == TYPE_MODE (etype) + && AGGREGATE_TYPE_P (type) && AGGREGATE_TYPE_P (etype) + && TYPE_ALIGN (type) == TYPE_ALIGN (etype) + && operand_equal_p (TYPE_SIZE (type), TYPE_SIZE (etype), 0)) + return build (COMPONENT_REF, type, TREE_OPERAND (expr, 0), + TREE_OPERAND (expr, 1)); + + break; + + case UNCONSTRAINED_ARRAY_REF: + /* Convert this to the type of the inner array by getting the address of + the array from the template. */ + expr = build_unary_op (INDIRECT_REF, NULL_TREE, + build_component_ref (TREE_OPERAND (expr, 0), + get_identifier ("P_ARRAY"), + NULL_TREE)); + etype = TREE_TYPE (expr); + ecode = TREE_CODE (etype); + break; + + case UNCHECKED_CONVERT_EXPR: + if (AGGREGATE_TYPE_P (type) && AGGREGATE_TYPE_P (etype) + && ! TYPE_FAT_POINTER_P (type) && ! TYPE_FAT_POINTER_P (etype)) + return convert (type, TREE_OPERAND (expr, 0)); + break; + + case INDIRECT_REF: + /* If both types are record types, just convert the pointer and + make a new INDIRECT_REF. + + ??? Disable this for now since it causes problems with the + code in build_binary_op for MODIFY_EXPR which wants to + strip off conversions. But that code really is a mess and + we need to do this a much better way some time. */ + if (0 + && (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE) + && (TREE_CODE (etype) == RECORD_TYPE + || TREE_CODE (etype) == UNION_TYPE) + && ! TYPE_FAT_POINTER_P (type) && ! TYPE_FAT_POINTER_P (etype)) + return build_unary_op (INDIRECT_REF, NULL_TREE, + convert (build_pointer_type (type), + TREE_OPERAND (expr, 0))); + break; + + default: + break; + } + + /* Check for converting to a pointer to an unconstrained array. */ + if (TYPE_FAT_POINTER_P (type) && ! TYPE_FAT_POINTER_P (etype)) + return convert_to_fat_pointer (type, expr); + + if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (etype) + || (code == INTEGER_CST && ecode == INTEGER_CST + && (type == TREE_TYPE (etype) || etype == TREE_TYPE (type)))) + return fold (build1 (NOP_EXPR, type, expr)); + + switch (code) + { + case VOID_TYPE: + return build1 (CONVERT_EXPR, type, expr); + + case INTEGER_TYPE: + if (TYPE_HAS_ACTUAL_BOUNDS_P (type) + && (ecode == ARRAY_TYPE || ecode == UNCONSTRAINED_ARRAY_TYPE)) + return unchecked_convert (type, expr); + else if (TYPE_BIASED_REPRESENTATION_P (type)) + return fold (build1 (CONVERT_EXPR, type, + fold (build (MINUS_EXPR, TREE_TYPE (type), + convert (TREE_TYPE (type), expr), + TYPE_MIN_VALUE (type))))); + + /* ... fall through ... */ + + case ENUMERAL_TYPE: + return fold (convert_to_integer (type, expr)); + + case POINTER_TYPE: + case REFERENCE_TYPE: + /* If converting between two pointers to records denoting + both a template and type, adjust if needed to account + for any differing offsets, since one might be negative. */ + if (TYPE_THIN_POINTER_P (etype) && TYPE_THIN_POINTER_P (type)) + { + tree bit_diff + = size_diffop (bit_position (TYPE_FIELDS (TREE_TYPE (etype))), + bit_position (TYPE_FIELDS (TREE_TYPE (type)))); + tree byte_diff = size_binop (CEIL_DIV_EXPR, bit_diff, + sbitsize_int (BITS_PER_UNIT)); + + expr = build1 (NOP_EXPR, type, expr); + TREE_CONSTANT (expr) = TREE_CONSTANT (TREE_OPERAND (expr, 0)); + if (integer_zerop (byte_diff)) + return expr; + + return build_binary_op (PLUS_EXPR, type, expr, + fold (convert_to_pointer (type, byte_diff))); + } + + /* If converting to a thin pointer, handle specially. */ + if (TYPE_THIN_POINTER_P (type) + && TYPE_UNCONSTRAINED_ARRAY (TREE_TYPE (type)) != 0) + return convert_to_thin_pointer (type, expr); + + /* If converting fat pointer to normal pointer, get the pointer to the + array and then convert it. */ + else if (TYPE_FAT_POINTER_P (etype)) + expr = build_component_ref (expr, get_identifier ("P_ARRAY"), + NULL_TREE); + + return fold (convert_to_pointer (type, expr)); + + case REAL_TYPE: + return fold (convert_to_real (type, expr)); + + case RECORD_TYPE: + if (TYPE_LEFT_JUSTIFIED_MODULAR_P (type) && ! AGGREGATE_TYPE_P (etype)) + return + build_constructor + (type, tree_cons (TYPE_FIELDS (type), + convert (TREE_TYPE (TYPE_FIELDS (type)), expr), + NULL_TREE)); + + /* ... fall through ... */ + + case ARRAY_TYPE: + /* In these cases, assume the front-end has validated the conversion. + If the conversion is valid, it will be a bit-wise conversion, so + it can be viewed as an unchecked conversion. */ + return unchecked_convert (type, expr); + + case UNION_TYPE: + /* Just validate that the type is indeed that of a field + of the type. Then make the simple conversion. */ + for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem)) + if (TREE_TYPE (tem) == etype) + return build1 (CONVERT_EXPR, type, expr); + + gigi_abort (413); + + case UNCONSTRAINED_ARRAY_TYPE: + /* If EXPR is a constrained array, take its address, convert it to a + fat pointer, and then dereference it. Likewise if EXPR is a + record containing both a template and a constrained array. + Note that a record representing a left justified modular type + always represents a packed constrained array. */ + if (ecode == ARRAY_TYPE + || (ecode == INTEGER_TYPE && TYPE_HAS_ACTUAL_BOUNDS_P (etype)) + || (ecode == RECORD_TYPE && TYPE_CONTAINS_TEMPLATE_P (etype)) + || (ecode == RECORD_TYPE && TYPE_LEFT_JUSTIFIED_MODULAR_P (etype))) + return + build_unary_op + (INDIRECT_REF, NULL_TREE, + convert_to_fat_pointer (TREE_TYPE (type), + build_unary_op (ADDR_EXPR, + NULL_TREE, expr))); + + /* Do something very similar for converting one unconstrained + array to another. */ + else if (ecode == UNCONSTRAINED_ARRAY_TYPE) + return + build_unary_op (INDIRECT_REF, NULL_TREE, + convert (TREE_TYPE (type), + build_unary_op (ADDR_EXPR, + NULL_TREE, expr))); + else + gigi_abort (409); + + case COMPLEX_TYPE: + return fold (convert_to_complex (type, expr)); + + default: + gigi_abort (410); + } +} + +/* Remove all conversions that are done in EXP. This includes converting + from a padded type or converting to a left-justified modular type. */ + +tree +remove_conversions (exp) + tree exp; +{ + switch (TREE_CODE (exp)) + { + case CONSTRUCTOR: + if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (TREE_TYPE (exp))) + return remove_conversions (TREE_VALUE (CONSTRUCTOR_ELTS (exp))); + break; + + case COMPONENT_REF: + if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (TREE_OPERAND (exp, 0)))) + return remove_conversions (TREE_OPERAND (exp, 0)); + break; + + case UNCHECKED_CONVERT_EXPR: + case NOP_EXPR: case CONVERT_EXPR: + return remove_conversions (TREE_OPERAND (exp, 0)); + + default: + break; + } + + return exp; +} + +/* If EXP's type is an UNCONSTRAINED_ARRAY_TYPE, return an expression that + refers to the underlying array. If its type has TYPE_CONTAINS_TEMPLATE_P, + likewise return an expression pointing to the underlying array. */ + +tree +maybe_unconstrained_array (exp) + tree exp; +{ + enum tree_code code = TREE_CODE (exp); + tree new; + + switch (TREE_CODE (TREE_TYPE (exp))) + { + case UNCONSTRAINED_ARRAY_TYPE: + if (code == UNCONSTRAINED_ARRAY_REF) + { + new + = build_unary_op (INDIRECT_REF, NULL_TREE, + build_component_ref (TREE_OPERAND (exp, 0), + get_identifier ("P_ARRAY"), + NULL_TREE)); + TREE_READONLY (new) = TREE_STATIC (new) = TREE_READONLY (exp); + return new; + } + + else if (code == NULL_EXPR) + return build1 (NULL_EXPR, + TREE_TYPE (TREE_TYPE (TYPE_FIELDS + (TREE_TYPE (TREE_TYPE (exp))))), + TREE_OPERAND (exp, 0)); + + else if (code == WITH_RECORD_EXPR + && (TREE_OPERAND (exp, 0) + != (new = maybe_unconstrained_array + (TREE_OPERAND (exp, 0))))) + return build (WITH_RECORD_EXPR, TREE_TYPE (new), new, + TREE_OPERAND (exp, 1)); + + case RECORD_TYPE: + if (TYPE_CONTAINS_TEMPLATE_P (TREE_TYPE (exp))) + { + new + = build_component_ref (exp, NULL_TREE, + TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (exp)))); + if (TREE_CODE (TREE_TYPE (new)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (new))) + new = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (new))), new); + + return new; + } + break; + + default: + break; + } + + return exp; +} + +/* Return an expression that does an unchecked converstion of EXPR to TYPE. */ + +tree +unchecked_convert (type, expr) + tree type; + tree expr; +{ + tree etype = TREE_TYPE (expr); + + /* If the expression is already the right type, we are done. */ + if (etype == type) + return expr; + + /* If EXPR is a WITH_RECORD_EXPR, do the conversion inside and then make a + new one. */ + if (TREE_CODE (expr) == WITH_RECORD_EXPR) + return build (WITH_RECORD_EXPR, type, + unchecked_convert (type, TREE_OPERAND (expr, 0)), + TREE_OPERAND (expr, 1)); + + /* If both types types are integral just do a normal conversion. + Likewise for a conversion to an unconstrained array. */ + if ((((INTEGRAL_TYPE_P (type) + && ! (TREE_CODE (type) == INTEGER_TYPE + && TYPE_VAX_FLOATING_POINT_P (type))) + || (POINTER_TYPE_P (type) && ! TYPE_THIN_POINTER_P (type)) + || (TREE_CODE (type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (type))) + && ((INTEGRAL_TYPE_P (etype) + && ! (TREE_CODE (etype) == INTEGER_TYPE + && TYPE_VAX_FLOATING_POINT_P (etype))) + || (POINTER_TYPE_P (etype) && ! TYPE_THIN_POINTER_P (etype)) + || (TREE_CODE (etype) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (etype)))) + || TREE_CODE (type) == UNCONSTRAINED_ARRAY_TYPE) + { + tree rtype = type; + + if (TREE_CODE (etype) == INTEGER_TYPE + && TYPE_BIASED_REPRESENTATION_P (etype)) + { + tree ntype = copy_type (etype); + + TYPE_BIASED_REPRESENTATION_P (ntype) = 0; + TYPE_MAIN_VARIANT (ntype) = ntype; + expr = build1 (GNAT_NOP_EXPR, ntype, expr); + } + + if (TREE_CODE (type) == INTEGER_TYPE + && TYPE_BIASED_REPRESENTATION_P (type)) + { + rtype = copy_type (type); + TYPE_BIASED_REPRESENTATION_P (rtype) = 0; + TYPE_MAIN_VARIANT (rtype) = rtype; + } + + expr = convert (rtype, expr); + if (type != rtype) + expr = build1 (GNAT_NOP_EXPR, type, expr); + } + + /* If we are converting TO an integral type whose precision is not the + same as its size, first unchecked convert to a record that contains + an object of the output type. Then extract the field. */ + else if (INTEGRAL_TYPE_P (type) && TYPE_RM_SIZE (type) != 0 + && 0 != compare_tree_int (TYPE_RM_SIZE (type), + GET_MODE_BITSIZE (TYPE_MODE (type)))) + { + tree rec_type = make_node (RECORD_TYPE); + tree field = create_field_decl (get_identifier ("OBJ"), type, + rec_type, 1, 0, 0, 0); + + TYPE_FIELDS (rec_type) = field; + layout_type (rec_type); + + expr = unchecked_convert (rec_type, expr); + expr = build_component_ref (expr, NULL_TREE, field); + } + + /* Similarly for integral input type whose precision is not equal to its + size. */ + else if (INTEGRAL_TYPE_P (etype) && TYPE_RM_SIZE (etype) != 0 + && 0 != compare_tree_int (TYPE_RM_SIZE (etype), + GET_MODE_BITSIZE (TYPE_MODE (etype)))) + { + tree rec_type = make_node (RECORD_TYPE); + tree field + = create_field_decl (get_identifier ("OBJ"), etype, rec_type, + 1, 0, 0, 0); + + TYPE_FIELDS (rec_type) = field; + layout_type (rec_type); + + expr = build_constructor (rec_type, build_tree_list (field, expr)); + expr = unchecked_convert (type, expr); + } + + /* We have a special case when we are converting between two + unconstrained array types. In that case, take the address, + convert the fat pointer types, and dereference. */ + else if (TREE_CODE (etype) == UNCONSTRAINED_ARRAY_TYPE + && TREE_CODE (type) == UNCONSTRAINED_ARRAY_TYPE) + expr = build_unary_op (INDIRECT_REF, NULL_TREE, + build1 (UNCHECKED_CONVERT_EXPR, TREE_TYPE (type), + build_unary_op (ADDR_EXPR, NULL_TREE, + expr))); + + /* If both types are aggregates with the same mode and alignment (except + if the result is a UNION_TYPE), we can do this as a normal conversion. */ + else if (AGGREGATE_TYPE_P (type) && AGGREGATE_TYPE_P (etype) + && TREE_CODE (type) != UNION_TYPE + && TYPE_ALIGN (type) == TYPE_ALIGN (etype) + && TYPE_MODE (type) == TYPE_MODE (etype)) + expr = build1 (CONVERT_EXPR, type, expr); + + else + { + expr = maybe_unconstrained_array (expr); + etype = TREE_TYPE (expr); + expr = build1 (UNCHECKED_CONVERT_EXPR, type, expr); + } + + + /* If the result is an integral type whose size is not equal to + the size of the underlying machine type, sign- or zero-extend + the result. We need not do this in the case where the input is + an integral type of the same precision and signedness or if the output + is a biased type or if both the input and output are unsigned. */ + if (INTEGRAL_TYPE_P (type) && TYPE_RM_SIZE (type) != 0 + && ! (TREE_CODE (type) == INTEGER_TYPE + && TYPE_BIASED_REPRESENTATION_P (type)) + && 0 != compare_tree_int (TYPE_RM_SIZE (type), + GET_MODE_BITSIZE (TYPE_MODE (type))) + && ! (INTEGRAL_TYPE_P (etype) + && TREE_UNSIGNED (type) == TREE_UNSIGNED (etype) + && operand_equal_p (TYPE_RM_SIZE (type), + (TYPE_RM_SIZE (etype) != 0 + ? TYPE_RM_SIZE (etype) : TYPE_SIZE (etype)), + 0)) + && ! (TREE_UNSIGNED (type) && TREE_UNSIGNED (etype))) + { + tree base_type = type_for_mode (TYPE_MODE (type), TREE_UNSIGNED (type)); + tree shift_expr + = convert (base_type, + size_binop (MINUS_EXPR, + bitsize_int + (GET_MODE_BITSIZE (TYPE_MODE (type))), + TYPE_RM_SIZE (type))); + expr + = convert (type, + build_binary_op (RSHIFT_EXPR, base_type, + build_binary_op (LSHIFT_EXPR, base_type, + convert (base_type, expr), + shift_expr), + shift_expr)); + } + + /* An unchecked conversion should never raise Constraint_Error. The code + below assumes that GCC's conversion routines overflow the same + way that the underlying hardware does. This is probably true. In + the rare case when it isn't, we can rely on the fact that such + conversions are erroneous anyway. */ + if (TREE_CODE (expr) == INTEGER_CST) + TREE_OVERFLOW (expr) = TREE_CONSTANT_OVERFLOW (expr) = 0; + + /* If the sizes of the types differ and this is an UNCHECKED_CONVERT_EXPR, + show no longer constant. */ + if (TREE_CODE (expr) == UNCHECKED_CONVERT_EXPR + && ! operand_equal_p (TYPE_SIZE_UNIT (type), TYPE_SIZE_UNIT (etype), 1)) + TREE_CONSTANT (expr) = 0; + + return expr; +} diff --git a/gcc/ada/utils2.c b/gcc/ada/utils2.c new file mode 100644 index 00000000000..424673ba103 --- /dev/null +++ b/gcc/ada/utils2.c @@ -0,0 +1,2049 @@ +/**************************************************************************** + * * + * GNAT COMPILER COMPONENTS * + * * + * U T I L S 2 * + * * + * C Implementation File * + * * + * $Revision: 1.1 $ + * * + * Copyright (C) 1992-2001, 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 2, 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 COPYING. If not, write * + * to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, * + * MA 02111-1307, USA. * + * * + * GNAT was originally developed by the GNAT team at New York University. * + * It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). * + * * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "tree.h" +#include "flags.h" +#include "ada.h" +#include "types.h" +#include "atree.h" +#include "stringt.h" +#include "uintp.h" +#include "fe.h" +#include "elists.h" +#include "nlists.h" +#include "sinfo.h" +#include "einfo.h" +#include "ada-tree.h" +#include "gigi.h" + +static tree find_common_type PARAMS ((tree, tree)); +static int contains_save_expr_p PARAMS ((tree)); +static tree contains_null_expr PARAMS ((tree)); +static tree compare_arrays PARAMS ((tree, tree, tree)); +static tree nonbinary_modular_operation PARAMS ((enum tree_code, tree, + tree, tree)); +static tree build_simple_component_ref PARAMS ((tree, tree, tree)); + +/* Prepare expr to be an argument of a TRUTH_NOT_EXPR or other logical + operation. + + This preparation consists of taking the ordinary representation of + an expression expr and producing a valid tree boolean expression + describing whether expr is nonzero. We could simply always do + + build_binary_op (NE_EXPR, expr, integer_zero_node, 1), + + but we optimize comparisons, &&, ||, and !. + + The resulting type should always be the same as the input type. + This function is simpler than the corresponding C version since + the only possible operands will be things of Boolean type. */ + +tree +truthvalue_conversion (expr) + tree expr; +{ + tree type = TREE_TYPE (expr); + + switch (TREE_CODE (expr)) + { + case EQ_EXPR: case NE_EXPR: case LE_EXPR: case GE_EXPR: + case LT_EXPR: case GT_EXPR: + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case ERROR_MARK: + return expr; + + case COND_EXPR: + /* Distribute the conversion into the arms of a COND_EXPR. */ + return fold (build (COND_EXPR, type, TREE_OPERAND (expr, 0), + truthvalue_conversion (TREE_OPERAND (expr, 1)), + truthvalue_conversion (TREE_OPERAND (expr, 2)))); + + case WITH_RECORD_EXPR: + return build (WITH_RECORD_EXPR, type, + truthvalue_conversion (TREE_OPERAND (expr, 0)), + TREE_OPERAND (expr, 1)); + + default: + return build_binary_op (NE_EXPR, type, expr, + convert (type, integer_zero_node)); + } +} + +/* Return the base type of TYPE. */ + +tree +get_base_type (type) + tree type; +{ + if (TREE_CODE (type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (type)) + type = TREE_TYPE (TYPE_FIELDS (type)); + + while (TREE_TYPE (type) != 0 + && (TREE_CODE (type) == INTEGER_TYPE + || TREE_CODE (type) == REAL_TYPE)) + type = TREE_TYPE (type); + + return type; +} + +/* Likewise, but only return types known to the Ada source. */ +tree +get_ada_base_type (type) + tree type; +{ + while (TREE_TYPE (type) != 0 + && (TREE_CODE (type) == INTEGER_TYPE + || TREE_CODE (type) == REAL_TYPE) + && ! TYPE_EXTRA_SUBTYPE_P (type)) + type = TREE_TYPE (type); + + return type; +} + +/* EXP is a GCC tree representing an address. See if we can find how + strictly the object at that address is aligned. Return that alignment + in bits. If we don't know anything about the alignment, return 0. + We do not go merely by type information here since the check on + N_Validate_Unchecked_Alignment does that. */ + +unsigned int +known_alignment (exp) + tree exp; +{ + unsigned int lhs, rhs; + + switch (TREE_CODE (exp)) + { + case CONVERT_EXPR: + case NOP_EXPR: + case NON_LVALUE_EXPR: + /* Conversions between pointers and integers don't change the alignment + of the underlying object. */ + return known_alignment (TREE_OPERAND (exp, 0)); + + case PLUS_EXPR: + case MINUS_EXPR: + /* If two address are added, the alignment of the result is the + minimum of the two aligments. */ + lhs = known_alignment (TREE_OPERAND (exp, 0)); + rhs = known_alignment (TREE_OPERAND (exp, 1)); + return MIN (lhs, rhs); + + case INTEGER_CST: + /* The first part of this represents the lowest bit in the constant, + but is it in bytes, not bits. */ + return MIN (BITS_PER_UNIT + * (TREE_INT_CST_LOW (exp) & - TREE_INT_CST_LOW (exp)), + BIGGEST_ALIGNMENT); + + case MULT_EXPR: + /* If we know the alignment of just one side, use it. Otherwise, + use the product of the alignments. */ + lhs = known_alignment (TREE_OPERAND (exp, 0)); + rhs = known_alignment (TREE_OPERAND (exp, 1)); + if (lhs == 0 || rhs == 0) + return MIN (BIGGEST_ALIGNMENT, MAX (lhs, rhs)); + + return MIN (BIGGEST_ALIGNMENT, lhs * rhs); + + case ADDR_EXPR: + return expr_align (TREE_OPERAND (exp, 0)); + + default: + return 0; + } +} + +/* We have a comparison or assignment operation on two types, T1 and T2, + which are both either array types or both record types. + Return the type that both operands should be converted to, if any. + Otherwise return zero. */ + +static tree +find_common_type (t1, t2) + tree t1, t2; +{ + /* If either type is non-BLKmode, use it. Note that we know that we will + not have any alignment problems since if we did the non-BLKmode + type could not have been used. */ + if (TYPE_MODE (t1) != BLKmode) + return t1; + else if (TYPE_MODE (t2) != BLKmode) + return t2; + + /* Otherwise, return the type that has a constant size. */ + if (TREE_CONSTANT (TYPE_SIZE (t1))) + return t1; + else if (TREE_CONSTANT (TYPE_SIZE (t2))) + return t2; + + /* In this case, both types have variable size. It's probably + best to leave the "type mismatch" because changing it could + case a bad self-referential reference. */ + return 0; +} + +/* See if EXP contains a SAVE_EXPR in a position where we would + normally put it. + + ??? This is a real kludge, but is probably the best approach short + of some very general solution. */ + +static int +contains_save_expr_p (exp) + tree exp; +{ + switch (TREE_CODE (exp)) + { + case SAVE_EXPR: + return 1; + + case ADDR_EXPR: case INDIRECT_REF: + case COMPONENT_REF: + case NOP_EXPR: case CONVERT_EXPR: case UNCHECKED_CONVERT_EXPR: + return contains_save_expr_p (TREE_OPERAND (exp, 0)); + + case CONSTRUCTOR: + return (CONSTRUCTOR_ELTS (exp) != 0 + && contains_save_expr_p (CONSTRUCTOR_ELTS (exp))); + + case TREE_LIST: + return (contains_save_expr_p (TREE_VALUE (exp)) + || (TREE_CHAIN (exp) != 0 + && contains_save_expr_p (TREE_CHAIN (exp)))); + + default: + return 0; + } +} + +/* See if EXP contains a NULL_EXPR in an expression we use for sizes. Return + it if so. This is used to detect types whose sizes involve computations + that are known to raise Constraint_Error. */ + +static tree +contains_null_expr (exp) + tree exp; +{ + tree tem; + + if (TREE_CODE (exp) == NULL_EXPR) + return exp; + + switch (TREE_CODE_CLASS (TREE_CODE (exp))) + { + case '1': + return contains_null_expr (TREE_OPERAND (exp, 0)); + + case '<': case '2': + tem = contains_null_expr (TREE_OPERAND (exp, 0)); + if (tem != 0) + return tem; + + return contains_null_expr (TREE_OPERAND (exp, 1)); + + case 'e': + switch (TREE_CODE (exp)) + { + case SAVE_EXPR: + return contains_null_expr (TREE_OPERAND (exp, 0)); + + case COND_EXPR: + tem = contains_null_expr (TREE_OPERAND (exp, 0)); + if (tem != 0) + return tem; + + tem = contains_null_expr (TREE_OPERAND (exp, 1)); + if (tem != 0) + return tem; + + return contains_null_expr (TREE_OPERAND (exp, 2)); + + default: + return 0; + } + + default: + return 0; + } +} + +/* Return an expression tree representing an equality comparison of + A1 and A2, two objects of ARRAY_TYPE. The returned expression should + be of type RESULT_TYPE + + Two arrays are equal in one of two ways: (1) if both have zero length + in some dimension (not necessarily the same dimension) or (2) if the + lengths in each dimension are equal and the data is equal. We perform the + length tests in as efficient a manner as possible. */ + +static tree +compare_arrays (result_type, a1, a2) + tree a1, a2; + tree result_type; +{ + tree t1 = TREE_TYPE (a1); + tree t2 = TREE_TYPE (a2); + tree result = convert (result_type, integer_one_node); + tree a1_is_null = convert (result_type, integer_zero_node); + tree a2_is_null = convert (result_type, integer_zero_node); + int length_zero_p = 0; + + /* Process each dimension separately and compare the lengths. If any + dimension has a size known to be zero, set SIZE_ZERO_P to 1 to + suppress the comparison of the data. */ + while (TREE_CODE (t1) == ARRAY_TYPE && TREE_CODE (t2) == ARRAY_TYPE) + { + tree lb1 = TYPE_MIN_VALUE (TYPE_DOMAIN (t1)); + tree ub1 = TYPE_MAX_VALUE (TYPE_DOMAIN (t1)); + tree lb2 = TYPE_MIN_VALUE (TYPE_DOMAIN (t2)); + tree ub2 = TYPE_MAX_VALUE (TYPE_DOMAIN (t2)); + tree bt = get_base_type (TREE_TYPE (lb1)); + tree length1 = fold (build (MINUS_EXPR, bt, ub1, lb1)); + tree length2 = fold (build (MINUS_EXPR, bt, ub2, lb2)); + tree nbt; + tree tem; + tree comparison, this_a1_is_null, this_a2_is_null; + + /* If the length of the first array is a constant, swap our operands + unless the length of the second array is the constant zero. + Note that we have set the `length' values to the length - 1. */ + if (TREE_CODE (length1) == INTEGER_CST + && ! integer_zerop (fold (build (PLUS_EXPR, bt, length2, + convert (bt, integer_one_node))))) + { + tem = a1, a1 = a2, a2 = tem; + tem = t1, t1 = t2, t2 = tem; + tem = lb1, lb1 = lb2, lb2 = tem; + tem = ub1, ub1 = ub2, ub2 = tem; + tem = length1, length1 = length2, length2 = tem; + tem = a1_is_null, a1_is_null = a2_is_null, a2_is_null = tem; + } + + /* If the length of this dimension in the second array is the constant + zero, we can just go inside the original bounds for the first + array and see if last < first. */ + if (integer_zerop (fold (build (PLUS_EXPR, bt, length2, + convert (bt, integer_one_node))))) + { + tree ub = TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + tree lb = TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + + comparison = build_binary_op (LT_EXPR, result_type, ub, lb); + + if (contains_placeholder_p (comparison)) + comparison = build (WITH_RECORD_EXPR, result_type, + comparison, a1); + if (contains_placeholder_p (length1)) + length1 = build (WITH_RECORD_EXPR, bt, length1, a1); + + length_zero_p = 1; + + this_a1_is_null = comparison; + this_a2_is_null = convert (result_type, integer_one_node); + } + + /* If the length is some other constant value, we know that the + this dimension in the first array cannot be superflat, so we + can just use its length from the actual stored bounds. */ + else if (TREE_CODE (length2) == INTEGER_CST) + { + ub1 = TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + lb1 = TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t1))); + ub2 = TYPE_MAX_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t2))); + lb2 = TYPE_MIN_VALUE (TYPE_INDEX_TYPE (TYPE_DOMAIN (t2))); + nbt = get_base_type (TREE_TYPE (ub1)); + + comparison + = build_binary_op (EQ_EXPR, result_type, + build_binary_op (MINUS_EXPR, nbt, ub1, lb1), + build_binary_op (MINUS_EXPR, nbt, ub2, lb2)); + + /* Note that we know that UB2 and LB2 are constant and hence + cannot contain a PLACEHOLDER_EXPR. */ + + if (contains_placeholder_p (comparison)) + comparison = build (WITH_RECORD_EXPR, result_type, comparison, a1); + if (contains_placeholder_p (length1)) + length1 = build (WITH_RECORD_EXPR, bt, length1, a1); + + this_a1_is_null = build_binary_op (LT_EXPR, result_type, ub1, lb1); + this_a2_is_null = convert (result_type, integer_zero_node); + } + + /* Otherwise compare the computed lengths. */ + else + { + if (contains_placeholder_p (length1)) + length1 = build (WITH_RECORD_EXPR, bt, length1, a1); + if (contains_placeholder_p (length2)) + length2 = build (WITH_RECORD_EXPR, bt, length2, a2); + + comparison + = build_binary_op (EQ_EXPR, result_type, length1, length2); + + this_a1_is_null + = build_binary_op (LT_EXPR, result_type, length1, + convert (bt, integer_zero_node)); + this_a2_is_null + = build_binary_op (LT_EXPR, result_type, length2, + convert (bt, integer_zero_node)); + } + + result = build_binary_op (TRUTH_ANDIF_EXPR, result_type, + result, comparison); + + a1_is_null = build_binary_op (TRUTH_ORIF_EXPR, result_type, + this_a1_is_null, a1_is_null); + a2_is_null = build_binary_op (TRUTH_ORIF_EXPR, result_type, + this_a2_is_null, a2_is_null); + + t1 = TREE_TYPE (t1); + t2 = TREE_TYPE (t2); + } + + /* Unless the size of some bound is known to be zero, compare the + data in the array. */ + if (! length_zero_p) + { + tree type = find_common_type (TREE_TYPE (a1), TREE_TYPE (a2)); + + if (type != 0) + a1 = convert (type, a1), a2 = convert (type, a2); + + + result = build_binary_op (TRUTH_ANDIF_EXPR, result_type, result, + build (EQ_EXPR, result_type, a1, a2)); + + } + + /* The result is also true if both sizes are zero. */ + result = build_binary_op (TRUTH_ORIF_EXPR, result_type, + build_binary_op (TRUTH_ANDIF_EXPR, result_type, + a1_is_null, a2_is_null), + result); + + /* If either operand contains SAVE_EXPRs, they have to be evaluated before + starting the comparison above since the place it would be otherwise + evaluated would be wrong. */ + + if (contains_save_expr_p (a1)) + result = build (COMPOUND_EXPR, result_type, a1, result); + + if (contains_save_expr_p (a2)) + result = build (COMPOUND_EXPR, result_type, a2, result); + + return result; +} + +/* Compute the result of applying OP_CODE to LHS and RHS, where both are of + type TYPE. We know that TYPE is a modular type with a nonbinary + modulus. */ + +static tree +nonbinary_modular_operation (op_code, type, lhs, rhs) + enum tree_code op_code; + tree type; + tree lhs, rhs; +{ + tree modulus = TYPE_MODULUS (type); + unsigned int needed_precision = tree_floor_log2 (modulus) + 1; + unsigned int precision; + int unsignedp = 1; + tree op_type = type; + tree result; + + /* If this is an addition of a constant, convert it to a subtraction + of a constant since we can do that faster. */ + if (op_code == PLUS_EXPR && TREE_CODE (rhs) == INTEGER_CST) + rhs = fold (build (MINUS_EXPR, type, modulus, rhs)), op_code = MINUS_EXPR; + + /* For the logical operations, we only need PRECISION bits. For + addition and subraction, we need one more and for multiplication we + need twice as many. But we never want to make a size smaller than + our size. */ + if (op_code == PLUS_EXPR || op_code == MINUS_EXPR) + needed_precision += 1; + else if (op_code == MULT_EXPR) + needed_precision *= 2; + + precision = MAX (needed_precision, TYPE_PRECISION (op_type)); + + /* Unsigned will do for everything but subtraction. */ + if (op_code == MINUS_EXPR) + unsignedp = 0; + + /* If our type is the wrong signedness or isn't wide enough, make a new + type and convert both our operands to it. */ + if (TYPE_PRECISION (op_type) < precision + || TREE_UNSIGNED (op_type) != unsignedp) + { + /* Copy the node so we ensure it can be modified to make it modular. */ + op_type = copy_node (type_for_size (precision, unsignedp)); + modulus = convert (op_type, modulus); + TYPE_MODULUS (op_type) = modulus; + TYPE_MODULAR_P (op_type) = 1; + lhs = convert (op_type, lhs); + rhs = convert (op_type, rhs); + } + + /* Do the operation, then we'll fix it up. */ + result = fold (build (op_code, op_type, lhs, rhs)); + + /* For multiplication, we have no choice but to do a full modulus + operation. However, we want to do this in the narrowest + possible size. */ + if (op_code == MULT_EXPR) + { + tree div_type = copy_node (type_for_size (needed_precision, 1)); + modulus = convert (div_type, modulus); + TYPE_MODULUS (div_type) = modulus; + TYPE_MODULAR_P (div_type) = 1; + result = convert (op_type, + fold (build (TRUNC_MOD_EXPR, div_type, + convert (div_type, result), modulus))); + } + + /* For subtraction, add the modulus back if we are negative. */ + else if (op_code == MINUS_EXPR) + { + result = save_expr (result); + result = fold (build (COND_EXPR, op_type, + build (LT_EXPR, integer_type_node, result, + convert (op_type, integer_zero_node)), + fold (build (PLUS_EXPR, op_type, + result, modulus)), + result)); + } + + /* For the other operations, subtract the modulus if we are >= it. */ + else + { + result = save_expr (result); + result = fold (build (COND_EXPR, op_type, + build (GE_EXPR, integer_type_node, + result, modulus), + fold (build (MINUS_EXPR, op_type, + result, modulus)), + result)); + } + + return convert (type, result); +} + +/* Make a binary operation of kind OP_CODE. RESULT_TYPE is the type + desired for the result. Usually the operation is to be performed + in that type. For MODIFY_EXPR and ARRAY_REF, RESULT_TYPE may be 0 + in which case the type to be used will be derived from the operands. + + This function is very much unlike the ones for C and C++ since we + have already done any type conversion and matching required. All we + have to do here is validate the work done by SEM and handle subtypes. */ + +tree +build_binary_op (op_code, result_type, left_operand, right_operand) + enum tree_code op_code; + tree result_type; + tree left_operand; + tree right_operand; +{ + tree left_type = TREE_TYPE (left_operand); + tree right_type = TREE_TYPE (right_operand); + tree left_base_type = get_base_type (left_type); + tree right_base_type = get_base_type (right_type); + tree operation_type = result_type; + tree best_type = 0; + tree modulus; + tree result; + int has_side_effects = 0; + + /* If one (but not both, unless they have the same object) operands are a + WITH_RECORD_EXPR, do the operation and then surround it with the + WITH_RECORD_EXPR. Don't do this for assignment, for an ARRAY_REF, or + for an ARRAY_RANGE_REF because we need to keep track of the + WITH_RECORD_EXPRs on both operands very carefully. */ + if (op_code != MODIFY_EXPR && op_code != ARRAY_REF + && op_code != ARRAY_RANGE_REF + && TREE_CODE (left_operand) == WITH_RECORD_EXPR + && (TREE_CODE (right_operand) != WITH_RECORD_EXPR + || operand_equal_p (TREE_OPERAND (left_operand, 1), + TREE_OPERAND (right_operand, 1), 0))) + { + tree right = right_operand; + + if (TREE_CODE (right) == WITH_RECORD_EXPR) + right = TREE_OPERAND (right, 0); + + result = build_binary_op (op_code, result_type, + TREE_OPERAND (left_operand, 0), right); + return build (WITH_RECORD_EXPR, TREE_TYPE (result), result, + TREE_OPERAND (left_operand, 1)); + } + else if (op_code != MODIFY_EXPR && op_code != ARRAY_REF + && op_code != ARRAY_RANGE_REF + && TREE_CODE (left_operand) != WITH_RECORD_EXPR + && TREE_CODE (right_operand) == WITH_RECORD_EXPR) + { + result = build_binary_op (op_code, result_type, left_operand, + TREE_OPERAND (right_operand, 0)); + return build (WITH_RECORD_EXPR, TREE_TYPE (result), result, + TREE_OPERAND (right_operand, 1)); + } + + if (operation_type != 0 + && TREE_CODE (operation_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (operation_type)) + operation_type = TREE_TYPE (TYPE_FIELDS (operation_type)); + + if (operation_type != 0 + && ! AGGREGATE_TYPE_P (operation_type) + && TYPE_EXTRA_SUBTYPE_P (operation_type)) + operation_type = get_base_type (operation_type); + + modulus = (operation_type != 0 && TREE_CODE (operation_type) == INTEGER_TYPE + && TYPE_MODULAR_P (operation_type) + ? TYPE_MODULUS (operation_type) : 0); + + switch (op_code) + { + case MODIFY_EXPR: + /* If there were any integral or pointer conversions on LHS, remove + them; we'll be putting them back below if needed. Likewise for + conversions between array and record types. But don't do this if + the right operand is not BLKmode (for packed arrays) + unless we are not changing the mode. */ + while ((TREE_CODE (left_operand) == CONVERT_EXPR + || TREE_CODE (left_operand) == NOP_EXPR + || TREE_CODE (left_operand) == UNCHECKED_CONVERT_EXPR) + && (((INTEGRAL_TYPE_P (left_type) + || POINTER_TYPE_P (left_type)) + && (INTEGRAL_TYPE_P (TREE_TYPE + (TREE_OPERAND (left_operand, 0))) + || POINTER_TYPE_P (TREE_TYPE + (TREE_OPERAND (left_operand, 0))))) + || (((TREE_CODE (left_type) == RECORD_TYPE + /* Don't remove conversions to left-justified modular + types. */ + && ! TYPE_LEFT_JUSTIFIED_MODULAR_P (left_type)) + || TREE_CODE (left_type) == ARRAY_TYPE) + && ((TREE_CODE (TREE_TYPE + (TREE_OPERAND (left_operand, 0))) + == RECORD_TYPE) + || (TREE_CODE (TREE_TYPE + (TREE_OPERAND (left_operand, 0))) + == ARRAY_TYPE)) + && (TYPE_MODE (right_type) == BLKmode + || (TYPE_MODE (left_type) + == TYPE_MODE (TREE_TYPE + (TREE_OPERAND + (left_operand, 0)))))))) + { + left_operand = TREE_OPERAND (left_operand, 0); + left_type = TREE_TYPE (left_operand); + } + + if (operation_type == 0) + operation_type = left_type; + + /* If the RHS has a conversion between record and array types and + an inner type is no worse, use it. Note we cannot do this for + modular types or types with TYPE_ALIGN_OK_P, since the latter + might indicate a conversion between a root type and a class-wide + type, which we must not remove. */ + while (TREE_CODE (right_operand) == UNCHECKED_CONVERT_EXPR + && ((TREE_CODE (right_type) == RECORD_TYPE + && ! TYPE_LEFT_JUSTIFIED_MODULAR_P (right_type) + && ! TYPE_ALIGN_OK_P (right_type) + && ! TYPE_IS_FAT_POINTER_P (right_type)) + || TREE_CODE (right_type) == ARRAY_TYPE) + && (((TREE_CODE (TREE_TYPE (TREE_OPERAND (right_operand, 0))) + == RECORD_TYPE) + && ! (TYPE_LEFT_JUSTIFIED_MODULAR_P + (TREE_TYPE (TREE_OPERAND (right_operand, 0)))) + && ! (TYPE_ALIGN_OK_P + (TREE_TYPE (TREE_OPERAND (right_operand, 0)))) + && ! (TYPE_IS_FAT_POINTER_P + (TREE_TYPE (TREE_OPERAND (right_operand, 0))))) + || (TREE_CODE (TREE_TYPE (TREE_OPERAND (right_operand, 0))) + == ARRAY_TYPE)) + && (0 == (best_type + == find_common_type (right_type, + TREE_TYPE (TREE_OPERAND + (right_operand, 0)))) + || right_type != best_type)) + { + right_operand = TREE_OPERAND (right_operand, 0); + right_type = TREE_TYPE (right_operand); + } + + /* If we are copying one array or record to another, find the best type + to use. */ + if (((TREE_CODE (left_type) == ARRAY_TYPE + && TREE_CODE (right_type) == ARRAY_TYPE) + || (TREE_CODE (left_type) == RECORD_TYPE + && TREE_CODE (right_type) == RECORD_TYPE)) + && (best_type = find_common_type (left_type, right_type)) != 0) + operation_type = best_type; + + /* If a class-wide type may be involved, force use of the RHS type. */ + if (TREE_CODE (right_type) == RECORD_TYPE + && TYPE_ALIGN_OK_P (right_type)) + operation_type = right_type; + + /* After we strip off any COMPONENT_REF, ARRAY_REF, or ARRAY_RANGE_REF + from the lhs, we must have either an INDIRECT_REF or a decl. Allow + UNCHECKED_CONVERT_EXPRs, but set TREE_ADDRESSABLE to show they are + in an LHS. Finally, allow NOP_EXPR if both types are the same tree + code and mode because we know these will be nops. */ + for (result = left_operand; + TREE_CODE (result) == COMPONENT_REF + || TREE_CODE (result) == ARRAY_REF + || TREE_CODE (result) == ARRAY_RANGE_REF + || TREE_CODE (result) == REALPART_EXPR + || TREE_CODE (result) == IMAGPART_EXPR + || TREE_CODE (result) == WITH_RECORD_EXPR + || TREE_CODE (result) == UNCHECKED_CONVERT_EXPR + || ((TREE_CODE (result) == NOP_EXPR + || TREE_CODE (result) == CONVERT_EXPR) + && (TREE_CODE (TREE_TYPE (result)) + == TREE_CODE (TREE_TYPE (TREE_OPERAND (result, 0)))) + && (TYPE_MODE (TREE_TYPE (TREE_OPERAND (result, 0))) + == TYPE_MODE (TREE_TYPE (result)))); + result = TREE_OPERAND (result, 0)) + if (TREE_CODE (result) == UNCHECKED_CONVERT_EXPR) + TREE_ADDRESSABLE (result) = 1; + + if (TREE_CODE (result) != INDIRECT_REF && TREE_CODE (result) != NULL_EXPR + && ! DECL_P (result)) + gigi_abort (516); + + /* Convert the right operand to the operation type unless + it is either already of the correct type or if the type + involves a placeholder, since the RHS may not have the same + record type. */ + if (operation_type != right_type + && (! (TREE_CODE (TYPE_SIZE (operation_type)) != INTEGER_CST + && contains_placeholder_p (TYPE_SIZE (operation_type))))) + { + /* For a variable-size type, with both BLKmode, convert using + CONVERT_EXPR instead of an unchecked conversion since we don't + need to make a temporary (and can't anyway). */ + if (TREE_CODE (TYPE_SIZE (operation_type)) != INTEGER_CST + && TYPE_MODE (TREE_TYPE (right_operand)) == BLKmode + && TREE_CODE (right_operand) != UNCONSTRAINED_ARRAY_REF) + right_operand = build1 (CONVERT_EXPR, operation_type, + right_operand); + else + right_operand = convert (operation_type, right_operand); + + right_type = operation_type; + } + + /* If the modes differ, make up a bogus type and convert the RHS to + it. This can happen with packed types. */ + if (TYPE_MODE (left_type) != TYPE_MODE (right_type)) + { + tree new_type = copy_node (left_type); + + TYPE_SIZE (new_type) = TYPE_SIZE (right_type); + TYPE_SIZE_UNIT (new_type) = TYPE_SIZE_UNIT (right_type); + TYPE_MAIN_VARIANT (new_type) = new_type; + right_operand = convert (new_type, right_operand); + } + + has_side_effects = 1; + modulus = 0; + break; + + case ARRAY_REF: + if (operation_type == 0) + operation_type = TREE_TYPE (left_type); + + /* ... fall through ... */ + + case ARRAY_RANGE_REF: + + /* First convert the right operand to its base type. This will + prevent unneed signedness conversions when sizetype is wider than + integer. */ + right_operand = convert (right_base_type, right_operand); + right_operand = convert (TYPE_DOMAIN (left_type), right_operand); + + if (! TREE_CONSTANT (right_operand) + || ! TREE_CONSTANT (TYPE_MIN_VALUE (right_type)) + || op_code == ARRAY_RANGE_REF) + mark_addressable (left_operand); + + /* If the array is an UNCHECKED_CONVERT_EXPR from and to BLKmode + types, convert it to a normal conversion since GCC can deal + with any mis-alignment as part of the handling of compponent + references. */ + + if (TREE_CODE (left_operand) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (TREE_TYPE (left_operand)) == BLKmode + && TYPE_MODE (TREE_TYPE (TREE_OPERAND (left_operand, 0))) == BLKmode) + left_operand = build1 (CONVERT_EXPR, TREE_TYPE (left_operand), + TREE_OPERAND (left_operand, 0)); + + modulus = 0; + break; + + case GE_EXPR: + case LE_EXPR: + case GT_EXPR: + case LT_EXPR: + if (POINTER_TYPE_P (left_type)) + gigi_abort (501); + + /* ... fall through ... */ + + case EQ_EXPR: + case NE_EXPR: + /* If either operand is a NULL_EXPR, just return a new one. */ + if (TREE_CODE (left_operand) == NULL_EXPR) + return build (op_code, result_type, + build1 (NULL_EXPR, integer_type_node, + TREE_OPERAND (left_operand, 0)), + integer_zero_node); + + else if (TREE_CODE (right_operand) == NULL_EXPR) + return build (op_code, result_type, + build1 (NULL_EXPR, integer_type_node, + TREE_OPERAND (right_operand, 0)), + integer_zero_node); + + /* If either object is a left-justified modular types, get the + fields from within. */ + if (TREE_CODE (left_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (left_type)) + { + left_operand = convert (TREE_TYPE (TYPE_FIELDS (left_type)), + left_operand); + left_type = TREE_TYPE (left_operand); + left_base_type = get_base_type (left_type); + } + + if (TREE_CODE (right_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (right_type)) + { + right_operand = convert (TREE_TYPE (TYPE_FIELDS (right_type)), + right_operand); + right_type = TREE_TYPE (right_operand); + right_base_type = get_base_type (right_type); + } + + /* If either object if an UNCHECKED_CONVERT_EXPR between two BLKmode + objects, change it to a CONVERT_EXPR. */ + if (TREE_CODE (left_operand) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (left_type) == BLKmode + && TYPE_MODE (TREE_TYPE (TREE_OPERAND (left_operand, 0))) == BLKmode) + left_operand = build1 (CONVERT_EXPR, left_type, + TREE_OPERAND (left_operand, 0)); + if (TREE_CODE (right_operand) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (right_type) == BLKmode + && (TYPE_MODE (TREE_TYPE (TREE_OPERAND (right_operand, 0))) + == BLKmode)) + right_operand = build1 (CONVERT_EXPR, right_type, + TREE_OPERAND (right_operand, 0)); + + /* If both objects are arrays, compare them specially. */ + if ((TREE_CODE (left_type) == ARRAY_TYPE + || (TREE_CODE (left_type) == INTEGER_TYPE + && TYPE_HAS_ACTUAL_BOUNDS_P (left_type))) + && (TREE_CODE (right_type) == ARRAY_TYPE + || (TREE_CODE (right_type) == INTEGER_TYPE + && TYPE_HAS_ACTUAL_BOUNDS_P (right_type)))) + { + result = compare_arrays (result_type, left_operand, right_operand); + + if (op_code == EQ_EXPR) + ; + else if (op_code == NE_EXPR) + result = invert_truthvalue (result); + else + gigi_abort (502); + + return result; + } + + /* Otherwise, the base types must be the same unless the objects are + records. If we have records, use the best type and convert both + operands to that type. */ + if (left_base_type != right_base_type) + { + if (TREE_CODE (left_base_type) == RECORD_TYPE + && TREE_CODE (right_base_type) == RECORD_TYPE) + { + /* The only way these are permitted to be the same is if both + types have the same name. In that case, one of them must + not be self-referential. Use that one as the best type. + Even better is if one is of fixed size. */ + best_type = 0; + + if (TYPE_NAME (left_base_type) == 0 + || TYPE_NAME (left_base_type) != TYPE_NAME (right_base_type)) + gigi_abort (503); + + if (TREE_CONSTANT (TYPE_SIZE (left_base_type))) + best_type = left_base_type; + else if (TREE_CONSTANT (TYPE_SIZE (right_base_type))) + best_type = right_base_type; + else if (! contains_placeholder_p (TYPE_SIZE (left_base_type))) + best_type = left_base_type; + else if (! contains_placeholder_p (TYPE_SIZE (right_base_type))) + best_type = right_base_type; + else + gigi_abort (504); + + left_operand = convert (best_type, left_operand); + right_operand = convert (best_type, right_operand); + } + else + gigi_abort (505); + } + + /* If we are comparing a fat pointer against zero, we need to + just compare the data pointer. */ + else if (TYPE_FAT_POINTER_P (left_base_type) + && TREE_CODE (right_operand) == CONSTRUCTOR + && integer_zerop (TREE_VALUE (TREE_OPERAND (right_operand, 1)))) + { + right_operand = build_component_ref (left_operand, NULL_TREE, + TYPE_FIELDS (left_base_type)); + left_operand = convert (TREE_TYPE (right_operand), + integer_zero_node); + } + else + { + left_operand = convert (left_base_type, left_operand); + right_operand = convert (right_base_type, right_operand); + } + + modulus = 0; + break; + + case PREINCREMENT_EXPR: + case PREDECREMENT_EXPR: + case POSTINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + /* In these, the result type and the left operand type should be the + same. Do the operation in the base type of those and convert the + right operand (which is an integer) to that type. + + Note that these operations are only used in loop control where + we guarantee that no overflow can occur. So nothing special need + be done for modular types. */ + + if (left_type != result_type) + gigi_abort (506); + + operation_type = get_base_type (result_type); + left_operand = convert (operation_type, left_operand); + right_operand = convert (operation_type, right_operand); + has_side_effects = 1; + modulus = 0; + break; + + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + /* The RHS of a shift can be any type. Also, ignore any modulus + (we used to abort, but this is needed for unchecked conversion + to modular types). Otherwise, processing is the same as normal. */ + if (operation_type != left_base_type) + gigi_abort (514); + + modulus = 0; + left_operand = convert (operation_type, left_operand); + break; + + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + left_operand = truthvalue_conversion (left_operand); + right_operand = truthvalue_conversion (right_operand); + goto common; + + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + /* For binary modulus, if the inputs are in range, so are the + outputs. */ + if (modulus != 0 && integer_pow2p (modulus)) + modulus = 0; + + goto common; + + case COMPLEX_EXPR: + if (TREE_TYPE (result_type) != left_base_type + || TREE_TYPE (result_type) != right_base_type) + gigi_abort (515); + + left_operand = convert (left_base_type, left_operand); + right_operand = convert (right_base_type, right_operand); + break; + + case TRUNC_DIV_EXPR: case TRUNC_MOD_EXPR: + case CEIL_DIV_EXPR: case CEIL_MOD_EXPR: + case FLOOR_DIV_EXPR: case FLOOR_MOD_EXPR: + case ROUND_DIV_EXPR: case ROUND_MOD_EXPR: + /* These always produce results lower than either operand. */ + modulus = 0; + goto common; + + default: + common: + /* The result type should be the same as the base types of the + both operands (and they should be the same). Convert + everything to the result type. */ + + if (operation_type != left_base_type + || left_base_type != right_base_type) + gigi_abort (507); + + left_operand = convert (operation_type, left_operand); + right_operand = convert (operation_type, right_operand); + } + + if (modulus != 0 && ! integer_pow2p (modulus)) + { + result = nonbinary_modular_operation (op_code, operation_type, + left_operand, right_operand); + modulus = 0; + } + /* If either operand is a NULL_EXPR, just return a new one. */ + else if (TREE_CODE (left_operand) == NULL_EXPR) + return build1 (NULL_EXPR, operation_type, TREE_OPERAND (left_operand, 0)); + else if (TREE_CODE (right_operand) == NULL_EXPR) + return build1 (NULL_EXPR, operation_type, TREE_OPERAND (right_operand, 0)); + else + result = fold (build (op_code, operation_type, + left_operand, right_operand)); + + TREE_SIDE_EFFECTS (result) |= has_side_effects; + TREE_CONSTANT (result) + = (TREE_CONSTANT (left_operand) & TREE_CONSTANT (right_operand) + && op_code != ARRAY_REF && op_code != ARRAY_RANGE_REF); + + if ((op_code == ARRAY_REF || op_code == ARRAY_RANGE_REF) + && TYPE_VOLATILE (operation_type)) + TREE_THIS_VOLATILE (result) = 1; + + /* If we are working with modular types, perform the MOD operation + if something above hasn't eliminated the need for it. */ + if (modulus != 0) + result = fold (build (FLOOR_MOD_EXPR, operation_type, result, + convert (operation_type, modulus))); + + if (result_type != 0 && result_type != operation_type) + result = convert (result_type, result); + + return result; +} + +/* Similar, but for unary operations. */ + +tree +build_unary_op (op_code, result_type, operand) + enum tree_code op_code; + tree result_type; + tree operand; +{ + tree type = TREE_TYPE (operand); + tree base_type = get_base_type (type); + tree operation_type = result_type; + tree result; + int side_effects = 0; + + /* If we have a WITH_RECORD_EXPR as our operand, do the operation first, + then surround it with the WITH_RECORD_EXPR. This allows GCC to do better + expression folding. */ + if (TREE_CODE (operand) == WITH_RECORD_EXPR) + { + result = build_unary_op (op_code, result_type, + TREE_OPERAND (operand, 0)); + return build (WITH_RECORD_EXPR, TREE_TYPE (result), result, + TREE_OPERAND (operand, 1)); + } + + if (operation_type != 0 + && TREE_CODE (operation_type) == RECORD_TYPE + && TYPE_LEFT_JUSTIFIED_MODULAR_P (operation_type)) + operation_type = TREE_TYPE (TYPE_FIELDS (operation_type)); + + if (operation_type != 0 + && ! AGGREGATE_TYPE_P (operation_type) + && TYPE_EXTRA_SUBTYPE_P (operation_type)) + operation_type = get_base_type (operation_type); + + switch (op_code) + { + case REALPART_EXPR: + case IMAGPART_EXPR: + if (operation_type == 0) + result_type = operation_type = TREE_TYPE (type); + else if (result_type != TREE_TYPE (type)) + gigi_abort (513); + + result = fold (build1 (op_code, operation_type, operand)); + break; + + case TRUTH_NOT_EXPR: + if (result_type != base_type) + gigi_abort (508); + + result = invert_truthvalue (truthvalue_conversion (operand)); + break; + + case ATTR_ADDR_EXPR: + case ADDR_EXPR: + switch (TREE_CODE (operand)) + { + case INDIRECT_REF: + case UNCONSTRAINED_ARRAY_REF: + result = TREE_OPERAND (operand, 0); + + /* Make sure the type here is a pointer, not a reference. + GCC wants pointer types for function addresses. */ + if (result_type == 0) + result_type = build_pointer_type (type); + break; + + case NULL_EXPR: + result = operand; + TREE_TYPE (result) = type = build_pointer_type (type); + break; + + case ARRAY_REF: + case ARRAY_RANGE_REF: + case COMPONENT_REF: + case BIT_FIELD_REF: + /* If this is for 'Address, find the address of the prefix and + add the offset to the field. Otherwise, do this the normal + way. */ + if (op_code == ATTR_ADDR_EXPR) + { + HOST_WIDE_INT bitsize; + HOST_WIDE_INT bitpos; + tree offset, inner; + enum machine_mode mode; + int unsignedp, volatilep; + unsigned int alignment; + + inner = get_inner_reference (operand, &bitsize, &bitpos, &offset, + &mode, &unsignedp, &volatilep, + &alignment); + + /* If INNER is a padding type whose field has a self-referential + size, convert to that inner type. We know the offset is zero + and we need to have that type visible. */ + if (TREE_CODE (TREE_TYPE (inner)) == RECORD_TYPE + && TYPE_IS_PADDING_P (TREE_TYPE (inner)) + && (contains_placeholder_p + (TYPE_SIZE (TREE_TYPE (TYPE_FIELDS + (TREE_TYPE (inner))))))) + inner = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (inner))), + inner); + + /* Compute the offset as a byte offset from INNER. */ + if (offset == 0) + offset = size_zero_node; + + offset = size_binop (PLUS_EXPR, offset, + size_int (bitpos / BITS_PER_UNIT)); + + /* Take the address of INNER, convert the offset to void *, and + add then. It will later be converted to the desired result + type, if any. */ + inner = build_unary_op (ADDR_EXPR, NULL_TREE, inner); + inner = convert (ptr_void_type_node, inner); + offset = convert (ptr_void_type_node, offset); + result = build_binary_op (PLUS_EXPR, ptr_void_type_node, + inner, offset); + result = convert (build_pointer_type (TREE_TYPE (operand)), + result); + break; + } + goto common; + + case CONSTRUCTOR: + /* If this is just a constructor for a padded record, we can + just take the address of the single field and convert it to + a pointer to our type. */ + if (TREE_CODE (type) == RECORD_TYPE && TYPE_IS_PADDING_P (type)) + { + result + = build_unary_op (ADDR_EXPR, NULL_TREE, + TREE_VALUE (CONSTRUCTOR_ELTS (operand))); + result = convert (build_pointer_type (TREE_TYPE (operand)), + result); + break; + } + + goto common; + + case NOP_EXPR: + if (AGGREGATE_TYPE_P (type) + && AGGREGATE_TYPE_P (TREE_TYPE (TREE_OPERAND (operand, 0)))) + return build_unary_op (ADDR_EXPR, result_type, + TREE_OPERAND (operand, 0)); + + /* If this NOP_EXPR doesn't change the mode, get the result type + from this type and go down. We need to do this in case + this is a conversion of a CONST_DECL. */ + if (TYPE_MODE (type) != BLKmode + && (TYPE_MODE (type) + == TYPE_MODE (TREE_TYPE (TREE_OPERAND (operand, 0))))) + return build_unary_op (ADDR_EXPR, + (result_type == 0 + ? build_pointer_type (type) + : result_type), + TREE_OPERAND (operand, 0)); + goto common; + + case CONST_DECL: + operand = DECL_CONST_CORRESPONDING_VAR (operand); + + /* ... fall through ... */ + + default: + common: + + if (type != error_mark_node) + operation_type = build_pointer_type (type); + + mark_addressable (operand); + result = fold (build1 (ADDR_EXPR, operation_type, operand)); + } + + TREE_CONSTANT (result) = staticp (operand) || TREE_CONSTANT (operand); + break; + + case INDIRECT_REF: + /* If we want to refer to an entire unconstrained array, + make up an expression to do so. This will never survive to + the backend. If TYPE is a thin pointer, first convert the + operand to a fat pointer. */ + if (TYPE_THIN_POINTER_P (type) + && TYPE_UNCONSTRAINED_ARRAY (TREE_TYPE (type)) != 0) + { + operand + = convert (TREE_TYPE (TYPE_UNCONSTRAINED_ARRAY (TREE_TYPE (type))), + operand); + type = TREE_TYPE (operand); + } + + if (TYPE_FAT_POINTER_P (type)) + result = build1 (UNCONSTRAINED_ARRAY_REF, + TYPE_UNCONSTRAINED_ARRAY (type), operand); + + else if (TREE_CODE (operand) == ADDR_EXPR) + result = TREE_OPERAND (operand, 0); + + else + { + result = fold (build1 (op_code, TREE_TYPE (type), operand)); + TREE_READONLY (result) = TREE_STATIC (result) + = TREE_READONLY (TREE_TYPE (type)); + } + + side_effects = flag_volatile + || (! TYPE_FAT_POINTER_P (type) && TYPE_VOLATILE (TREE_TYPE (type))); + break; + + case NEGATE_EXPR: + case BIT_NOT_EXPR: + { + tree modulus = ((operation_type != 0 + && TREE_CODE (operation_type) == INTEGER_TYPE + && TYPE_MODULAR_P (operation_type)) + ? TYPE_MODULUS (operation_type) : 0); + int mod_pow2 = modulus != 0 && integer_pow2p (modulus); + + /* If this is a modular type, there are various possibilities + depending on the operation and whether the modulus is a + power of two or not. */ + + if (modulus != 0) + { + if (operation_type != base_type) + gigi_abort (509); + + operand = convert (operation_type, operand); + + /* The fastest in the negate case for binary modulus is + the straightforward code; the TRUNC_MOD_EXPR below + is an AND operation. */ + if (op_code == NEGATE_EXPR && mod_pow2) + result = fold (build (TRUNC_MOD_EXPR, operation_type, + fold (build1 (NEGATE_EXPR, operation_type, + operand)), + modulus)); + + /* For nonbinary negate case, return zero for zero operand, + else return the modulus minus the operand. If the modulus + is a power of two minus one, we can do the subtraction + as an XOR since it is equivalent and faster on most machines. */ + else if (op_code == NEGATE_EXPR && ! mod_pow2) + { + if (integer_pow2p (fold (build (PLUS_EXPR, operation_type, + modulus, + convert (operation_type, + integer_one_node))))) + result = fold (build (BIT_XOR_EXPR, operation_type, + operand, modulus)); + else + result = fold (build (MINUS_EXPR, operation_type, + modulus, operand)); + + result = fold (build (COND_EXPR, operation_type, + fold (build (NE_EXPR, integer_type_node, + operand, + convert (operation_type, + integer_zero_node))), + result, operand)); + } + else + { + /* For the NOT cases, we need a constant equal to + the modulus minus one. For a binary modulus, we + XOR against the constant and subtract the operand from + that constant for nonbinary modulus. */ + + tree cnst = fold (build (MINUS_EXPR, operation_type, modulus, + convert (operation_type, + integer_one_node))); + + if (mod_pow2) + result = fold (build (BIT_XOR_EXPR, operation_type, + operand, cnst)); + else + result = fold (build (MINUS_EXPR, operation_type, + cnst, operand)); + } + + break; + } + } + + /* ... fall through ... */ + + default: + if (operation_type != base_type) + gigi_abort (509); + + result = fold (build1 (op_code, operation_type, convert (operation_type, + operand))); + } + + if (side_effects) + { + TREE_SIDE_EFFECTS (result) = 1; + if (TREE_CODE (result) == INDIRECT_REF) + TREE_THIS_VOLATILE (result) = TYPE_VOLATILE (TREE_TYPE (result)); + } + + if (result_type != 0 && TREE_TYPE (result) != result_type) + result = convert (result_type, result); + + return result; +} + +/* Similar, but for COND_EXPR. */ + +tree +build_cond_expr (result_type, condition_operand, true_operand, false_operand) + tree result_type; + tree condition_operand; + tree true_operand; + tree false_operand; +{ + tree result; + int addr_p = 0; + + /* Front-end verifies that result, true and false operands have same base + type. Convert everything to the result type. */ + + true_operand = convert (result_type, true_operand); + false_operand = convert (result_type, false_operand); + + /* If the result type is unconstrained, take the address of + the operands and then dereference our result. */ + + if (TREE_CODE (result_type) == UNCONSTRAINED_ARRAY_TYPE + || (TREE_CODE (TYPE_SIZE (result_type)) != INTEGER_CST + && contains_placeholder_p (TYPE_SIZE (result_type)))) + { + addr_p = 1; + result_type = build_pointer_type (result_type); + true_operand = build_unary_op (ADDR_EXPR, result_type, true_operand); + false_operand = build_unary_op (ADDR_EXPR, result_type, false_operand); + } + + result = fold (build (COND_EXPR, result_type, condition_operand, + true_operand, false_operand)); + if (addr_p) + result = build_unary_op (INDIRECT_REF, NULL_TREE, result); + + return result; +} + + +/* Build a CALL_EXPR to call FUNDECL with one argument, ARG. Return + the CALL_EXPR. */ + +tree +build_call_1_expr (fundecl, arg) + tree fundecl; + tree arg; +{ + tree call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fundecl)), + build_unary_op (ADDR_EXPR, NULL_TREE, fundecl), + chainon (NULL_TREE, build_tree_list (NULL_TREE, arg)), + NULL_TREE); + + TREE_SIDE_EFFECTS (call) = 1; + + return call; +} + +/* Build a CALL_EXPR to call FUNDECL with two arguments, ARG1 & ARG2. Return + the CALL_EXPR. */ + +tree +build_call_2_expr (fundecl, arg1, arg2) + tree fundecl; + tree arg1, arg2; +{ + tree call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fundecl)), + build_unary_op (ADDR_EXPR, NULL_TREE, fundecl), + chainon (chainon (NULL_TREE, + build_tree_list (NULL_TREE, arg1)), + build_tree_list (NULL_TREE, arg2)), + NULL_TREE); + + TREE_SIDE_EFFECTS (call) = 1; + + return call; +} + +/* Likewise to call FUNDECL with no arguments. */ + +tree +build_call_0_expr (fundecl) + tree fundecl; +{ + tree call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fundecl)), + build_unary_op (ADDR_EXPR, NULL_TREE, fundecl), + NULL_TREE, NULL_TREE); + + TREE_SIDE_EFFECTS (call) = 1; + + return call; +} + +/* Call a function FCN that raises an exception and pass the line + number and file name, if requested. */ + +tree +build_call_raise (fndecl) + tree fndecl; +{ + const char *str = discard_file_names ? "" : ref_filename; + int len = strlen (str) + 1; + tree filename = build_string (len, str); + + TREE_TYPE (filename) + = build_array_type (char_type_node, + build_index_type (build_int_2 (len, 0))); + + return + build_call_2_expr (fndecl, + build1 (ADDR_EXPR, build_pointer_type (char_type_node), + filename), + build_int_2 (lineno, 0)); +} + +/* Return a CONSTRUCTOR of TYPE whose list is LIST. */ + +tree +build_constructor (type, list) + tree type; + tree list; +{ + tree elmt; + int allconstant = (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST); + int side_effects = 0; + tree result; + + for (elmt = list; elmt; elmt = TREE_CHAIN (elmt)) + { + if (! TREE_CONSTANT (TREE_VALUE (elmt)) + || (TREE_CODE (type) == RECORD_TYPE + && DECL_BIT_FIELD (TREE_PURPOSE (elmt)) + && TREE_CODE (TREE_VALUE (elmt)) != INTEGER_CST)) + allconstant = 0; + + if (TREE_SIDE_EFFECTS (TREE_VALUE (elmt))) + side_effects = 1; + + /* Propagate an NULL_EXPR from the size of the type. We won't ever + be executing the code we generate here in that case, but handle it + specially to avoid the cmpiler blowing up. */ + if (TREE_CODE (type) == RECORD_TYPE + && (0 != (result + = contains_null_expr (DECL_SIZE (TREE_PURPOSE (elmt)))))) + return build1 (NULL_EXPR, type, TREE_OPERAND (result, 0)); + } + + /* If TYPE is a RECORD_TYPE and the fields are not in the + same order as their bit position, don't treat this as constant + since varasm.c can't handle it. */ + if (allconstant && TREE_CODE (type) == RECORD_TYPE) + { + tree last_pos = bitsize_zero_node; + tree field; + + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + tree this_pos = bit_position (field); + + if (TREE_CODE (this_pos) != INTEGER_CST + || tree_int_cst_lt (this_pos, last_pos)) + { + allconstant = 0; + break; + } + + last_pos = this_pos; + } + } + + result = build (CONSTRUCTOR, type, NULL_TREE, list); + TREE_CONSTANT (result) = allconstant; + TREE_STATIC (result) = allconstant; + TREE_SIDE_EFFECTS (result) = side_effects; + TREE_READONLY (result) = TREE_READONLY (type); + + return result; +} + +/* Return a COMPONENT_REF to access a field that is given by COMPONENT, + an IDENTIFIER_NODE giving the name of the field, or FIELD, a FIELD_DECL, + for the field. + + We also handle the fact that we might have been passed a pointer to the + actual record and know how to look for fields in variant parts. */ + +static tree +build_simple_component_ref (record_variable, component, field) + tree record_variable; + tree component; + tree field; +{ + tree record_type = TYPE_MAIN_VARIANT (TREE_TYPE (record_variable)); + tree ref; + + if ((TREE_CODE (record_type) != RECORD_TYPE + && TREE_CODE (record_type) != UNION_TYPE + && TREE_CODE (record_type) != QUAL_UNION_TYPE) + || TYPE_SIZE (record_type) == 0) + gigi_abort (510); + + /* Either COMPONENT or FIELD must be specified, but not both. */ + if ((component != 0) == (field != 0)) + gigi_abort (511); + + /* If no field was specified, look for a field with the specified name + in the current record only. */ + if (field == 0) + for (field = TYPE_FIELDS (record_type); field; + field = TREE_CHAIN (field)) + if (DECL_NAME (field) == component) + break; + + if (field == 0) + return 0; + + /* If this field is not in the specified record, see if we can find + something in the record whose original field is the same as this one. */ + if (DECL_CONTEXT (field) != record_type) + /* Check if there is a field with name COMPONENT in the record. */ + { + tree new_field; + + /* First loop thru normal components. */ + + for (new_field = TYPE_FIELDS (record_type); new_field != 0; + new_field = TREE_CHAIN (new_field)) + if (DECL_ORIGINAL_FIELD (new_field) == field + || new_field == DECL_ORIGINAL_FIELD (field) + || (DECL_ORIGINAL_FIELD (field) != 0 + && (DECL_ORIGINAL_FIELD (field) + == DECL_ORIGINAL_FIELD (new_field)))) + break; + + /* Next, loop thru DECL_INTERNAL_P components if we haven't found + the component in the first search. Doing this search in 2 steps + is required to avoiding hidden homonymous fields in the + _Parent field. */ + + if (new_field == 0) + for (new_field = TYPE_FIELDS (record_type); new_field != 0; + new_field = TREE_CHAIN (new_field)) + if (DECL_INTERNAL_P (new_field)) + { + tree field_ref + = build_simple_component_ref (record_variable, + NULL_TREE, new_field); + ref = build_simple_component_ref (field_ref, NULL_TREE, field); + + if (ref != 0) + return ref; + } + + field = new_field; + } + + if (field == 0) + return 0; + + /* If the record variable is an UNCHECKED_CONVERT_EXPR from and to BLKmode + types, convert it to a normal conversion since GCC can deal with any + mis-alignment as part of the handling of compponent references. */ + if (TREE_CODE (record_variable) == UNCHECKED_CONVERT_EXPR + && TYPE_MODE (TREE_TYPE (record_variable)) == BLKmode + && TYPE_MODE (TREE_TYPE (TREE_OPERAND (record_variable, 0))) == BLKmode) + record_variable = build1 (CONVERT_EXPR, TREE_TYPE (record_variable), + TREE_OPERAND (record_variable, 0)); + + /* It would be nice to call "fold" here, but that can lose a type + we need to tag a PLACEHOLDER_EXPR with, so we can't do it. */ + ref = build (COMPONENT_REF, TREE_TYPE (field), record_variable, field); + + if (TREE_READONLY (record_variable) || TREE_READONLY (field)) + TREE_READONLY (ref) = 1; + if (TREE_THIS_VOLATILE (record_variable) || TREE_THIS_VOLATILE (field) + || TYPE_VOLATILE (record_type)) + TREE_THIS_VOLATILE (ref) = 1; + + return ref; +} + +/* Like build_simple_component_ref, except that we give an error if the + reference could not be found. */ + +tree +build_component_ref (record_variable, component, field) + tree record_variable; + tree component; + tree field; +{ + tree ref = build_simple_component_ref (record_variable, component, field); + + if (ref != 0) + return ref; + + /* If FIELD was specified, assume this is an invalid user field so + raise constraint error. Otherwise, we can't find the type to return, so + abort. */ + + else if (field != 0) + return build1 (NULL_EXPR, TREE_TYPE (field), + build_call_raise (raise_constraint_error_decl)); + else + gigi_abort (512); +} + +/* Build a GCC tree to call an allocation or deallocation function. + If GNU_OBJ is nonzero, it is an object to deallocate. Otherwise, + generate an allocator. + + GNU_SIZE is the size of the object in bytes and ALIGN is the alignment in + bits. GNAT_PROC, if present, is a procedure to call and GNAT_POOL is the + storage pool to use. If not preset, malloc and free will be used except + if GNAT_PROC is the "fake" value of -1, in which case we allocate the + object dynamically on the stack frame. */ + +tree +build_call_alloc_dealloc (gnu_obj, gnu_size, align, gnat_proc, gnat_pool) + tree gnu_obj; + tree gnu_size; + int align; + Entity_Id gnat_proc; + Entity_Id gnat_pool; +{ + tree gnu_align = size_int (align / BITS_PER_UNIT); + + if (TREE_CODE (gnu_size) != INTEGER_CST && contains_placeholder_p (gnu_size)) + gnu_size = build (WITH_RECORD_EXPR, sizetype, gnu_size, + build_unary_op (INDIRECT_REF, NULL_TREE, gnu_obj)); + + if (Present (gnat_proc)) + { + /* The storage pools are obviously always tagged types, but the + secondary stack uses the same mechanism and is not tagged */ + if (Is_Tagged_Type (Etype (gnat_pool))) + { + /* The size is the third parameter; the alignment is the + same type. */ + Entity_Id gnat_size_type + = Etype (Next_Formal (Next_Formal (First_Formal (gnat_proc)))); + tree gnu_size_type = gnat_to_gnu_type (gnat_size_type); + tree gnu_proc = gnat_to_gnu (gnat_proc); + tree gnu_proc_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_proc); + tree gnu_pool = gnat_to_gnu (gnat_pool); + tree gnu_pool_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_pool); + tree gnu_args = NULL_TREE; + tree gnu_call; + + /* The first arg is always the address of the storage pool; next + comes the address of the object, for a deallocator, then the + size and alignment. */ + gnu_args + = chainon (gnu_args, build_tree_list (NULL_TREE, gnu_pool_addr)); + + if (gnu_obj) + gnu_args + = chainon (gnu_args, build_tree_list (NULL_TREE, gnu_obj)); + + gnu_args + = chainon (gnu_args, + build_tree_list (NULL_TREE, + convert (gnu_size_type, gnu_size))); + gnu_args + = chainon (gnu_args, + build_tree_list (NULL_TREE, + convert (gnu_size_type, gnu_align))); + + gnu_call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (gnu_proc)), + gnu_proc_addr, gnu_args, NULL_TREE); + TREE_SIDE_EFFECTS (gnu_call) = 1; + return gnu_call; + } + + /* Secondary stack case. */ + else + { + /* The size is the second parameter */ + Entity_Id gnat_size_type + = Etype (Next_Formal (First_Formal (gnat_proc))); + tree gnu_size_type = gnat_to_gnu_type (gnat_size_type); + tree gnu_proc = gnat_to_gnu (gnat_proc); + tree gnu_proc_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_proc); + tree gnu_args = NULL_TREE; + tree gnu_call; + + /* The first arg is the address of the object, for a + deallocator, then the size */ + if (gnu_obj) + gnu_args + = chainon (gnu_args, build_tree_list (NULL_TREE, gnu_obj)); + + gnu_args + = chainon (gnu_args, + build_tree_list (NULL_TREE, + convert (gnu_size_type, gnu_size))); + + gnu_call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (gnu_proc)), + gnu_proc_addr, gnu_args, NULL_TREE); + TREE_SIDE_EFFECTS (gnu_call) = 1; + return gnu_call; + } + } + + else if (gnu_obj) + return build_call_1_expr (free_decl, gnu_obj); + else if (gnat_pool == -1) + { + /* If the size is a constant, we can put it in the fixed portion of + the stack frame to avoid the need to adjust the stack pointer. */ + if (TREE_CODE (gnu_size) == INTEGER_CST && ! flag_stack_check) + { + tree gnu_range + = build_range_type (NULL_TREE, size_one_node, gnu_size); + tree gnu_array_type = build_array_type (char_type_node, gnu_range); + tree gnu_decl = + create_var_decl (get_identifier ("RETVAL"), NULL_TREE, + gnu_array_type, NULL_TREE, 0, 0, 0, 0, 0); + + return convert (ptr_void_type_node, + build_unary_op (ADDR_EXPR, NULL_TREE, gnu_decl)); + } + else + return build (ALLOCATE_EXPR, ptr_void_type_node, gnu_size, gnu_align); + } + else + return build_call_1_expr (malloc_decl, gnu_size); +} + +/* Build a GCC tree to correspond to allocating an object of TYPE whose + initial value is INIT, if INIT is nonzero. Convert the expression to + RESULT_TYPE, which must be some type of pointer. Return the tree. + GNAT_PROC and GNAT_POOL optionally give the procedure to call and + the storage pool to use. */ + +tree +build_allocator (type, init, result_type, gnat_proc, gnat_pool) + tree type; + tree init; + tree result_type; + Entity_Id gnat_proc; + Entity_Id gnat_pool; +{ + tree size = TYPE_SIZE_UNIT (type); + tree result; + + /* If the initializer, if present, is a NULL_EXPR, just return a new one. */ + if (init != 0 && TREE_CODE (init) == NULL_EXPR) + return build1 (NULL_EXPR, result_type, TREE_OPERAND (init, 0)); + + /* If RESULT_TYPE is a fat or thin pointer, set SIZE to be the sum of the + sizes of the object and its template. Allocate the whole thing and + fill in the parts that are known. */ + else if (TYPE_FAT_OR_THIN_POINTER_P (result_type)) + { + tree template_type + = (TYPE_FAT_POINTER_P (result_type) + ? TREE_TYPE (TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (result_type)))) + : TREE_TYPE (TYPE_FIELDS (TREE_TYPE (result_type)))); + tree storage_type + = build_unc_object_type (template_type, type, + get_identifier ("ALLOC")); + tree storage_ptr_type = build_pointer_type (storage_type); + tree storage; + tree template_cons = NULL_TREE; + + size = TYPE_SIZE_UNIT (storage_type); + + if (TREE_CODE (size) != INTEGER_CST + && contains_placeholder_p (size)) + size = build (WITH_RECORD_EXPR, sizetype, size, init); + + storage = build_call_alloc_dealloc (NULL_TREE, size, + TYPE_ALIGN (storage_type), + gnat_proc, gnat_pool); + storage = convert (storage_ptr_type, make_save_expr (storage)); + + if (TREE_CODE (type) == RECORD_TYPE && TYPE_IS_PADDING_P (type)) + { + type = TREE_TYPE (TYPE_FIELDS (type)); + + if (init != 0) + init = convert (type, init); + } + + /* If there is an initializing expression, make a constructor for + the entire object including the bounds and copy it into the + object. If there is no initializing expression, just set the + bounds. */ + if (init != 0) + { + template_cons = tree_cons (TREE_CHAIN (TYPE_FIELDS (storage_type)), + init, NULL_TREE); + template_cons = tree_cons (TYPE_FIELDS (storage_type), + build_template (template_type, type, + init), + template_cons); + + return convert + (result_type, + build (COMPOUND_EXPR, storage_ptr_type, + build_binary_op + (MODIFY_EXPR, storage_type, + build_unary_op (INDIRECT_REF, NULL_TREE, + convert (storage_ptr_type, storage)), + build_constructor (storage_type, template_cons)), + convert (storage_ptr_type, storage))); + } + else + return build + (COMPOUND_EXPR, result_type, + build_binary_op + (MODIFY_EXPR, template_type, + build_component_ref + (build_unary_op (INDIRECT_REF, NULL_TREE, + convert (storage_ptr_type, storage)), + NULL_TREE, TYPE_FIELDS (storage_type)), + build_template (template_type, type, NULL_TREE)), + convert (result_type, convert (storage_ptr_type, storage))); + } + + /* If we have an initializing expression, see if its size is simpler + than the size from the type. */ + if (init != 0 && TYPE_SIZE_UNIT (TREE_TYPE (init)) != 0 + && (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (init))) == INTEGER_CST + || (TREE_CODE (size) != INTEGER_CST + && contains_placeholder_p (size)))) + size = TYPE_SIZE_UNIT (TREE_TYPE (init)); + + /* If the size is still self-referential, reference the initializing + expression, if it is present. If not, this must have been a + call to allocate a library-level object, in which case we use + the maximum size. */ + if (TREE_CODE (size) != INTEGER_CST && contains_placeholder_p (size)) + { + if (init == 0) + size = max_size (size, 1); + else + size = build (WITH_RECORD_EXPR, sizetype, size, init); + } + + /* If the size overflows, pass -1 so the allocator will raise + storage error. */ + if (TREE_CODE (size) == INTEGER_CST && TREE_OVERFLOW (size)) + size = ssize_int (-1); + + /* If this is a type whose alignment is larger than the + biggest we support in normal alignment and this is in + the default storage pool, make an "aligning type", allocate + it, point to the field we need, and return that. */ + if (TYPE_ALIGN (type) > BIGGEST_ALIGNMENT + && No (gnat_proc)) + { + tree new_type = make_aligning_type (type, TYPE_ALIGN (type), size); + + result = build_call_alloc_dealloc (NULL_TREE, TYPE_SIZE (new_type), + BIGGEST_ALIGNMENT, Empty, Empty); + result = save_expr (result); + result = convert (build_pointer_type (new_type), result); + result = build_unary_op (INDIRECT_REF, NULL_TREE, result); + result = build_component_ref (result, NULL_TREE, + TYPE_FIELDS (new_type)); + result = convert (result_type, + build_unary_op (ADDR_EXPR, NULL_TREE, result)); + } + else + result = convert (result_type, + build_call_alloc_dealloc (NULL_TREE, size, + TYPE_ALIGN (type), + gnat_proc, gnat_pool)); + + /* If we have an initial value, put the new address into a SAVE_EXPR, assign + the value, and return the address. Do this with a COMPOUND_EXPR. */ + + if (init) + { + result = save_expr (result); + result + = build (COMPOUND_EXPR, TREE_TYPE (result), + build_binary_op + (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (result)), + build_unary_op (INDIRECT_REF, TREE_TYPE (TREE_TYPE (result)), + result), + init), + result); + } + + return convert (result_type, result); +} + +/* Fill in a VMS descriptor for EXPR and return a constructor for it. + GNAT_FORMAL is how we find the descriptor record. */ + +tree +fill_vms_descriptor (expr, gnat_formal) + tree expr; + Entity_Id gnat_formal; +{ + tree record_type = TREE_TYPE (TREE_TYPE (get_gnu_tree (gnat_formal))); + tree field; + tree const_list = 0; + + expr = maybe_unconstrained_array (expr); + mark_addressable (expr); + + for (field = TYPE_FIELDS (record_type); field; field = TREE_CHAIN (field)) + { + tree init = DECL_INITIAL (field); + + if (TREE_CODE (init) != INTEGER_CST + && contains_placeholder_p (init)) + init = build (WITH_RECORD_EXPR, TREE_TYPE (init), init, expr); + + const_list = tree_cons (field, convert (TREE_TYPE (field), init), + const_list); + } + + return build_constructor (record_type, nreverse (const_list)); +} + +/* Indicate that we need to make the address of EXPR_NODE and it therefore + should not be allocated in a register. Return 1 if successful. */ + +int +mark_addressable (expr_node) + tree expr_node; +{ + while (1) + switch (TREE_CODE (expr_node)) + { + case ADDR_EXPR: + case COMPONENT_REF: + case ARRAY_REF: + case ARRAY_RANGE_REF: + case REALPART_EXPR: + case IMAGPART_EXPR: + case NOP_EXPR: + expr_node = TREE_OPERAND (expr_node, 0); + break; + + case CONSTRUCTOR: + TREE_ADDRESSABLE (expr_node) = 1; + return 1; + + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + put_var_into_stack (expr_node); + TREE_ADDRESSABLE (expr_node) = 1; + return 1; + + case FUNCTION_DECL: + TREE_ADDRESSABLE (expr_node) = 1; + return 1; + + case CONST_DECL: + return (DECL_CONST_CORRESPONDING_VAR (expr_node) != 0 + && (mark_addressable + (DECL_CONST_CORRESPONDING_VAR (expr_node)))); + default: + return 1; + } +} diff --git a/gcc/ada/validsw.adb b/gcc/ada/validsw.adb new file mode 100644 index 00000000000..923c913ea4d --- /dev/null +++ b/gcc/ada/validsw.adb @@ -0,0 +1,222 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- V A L I D S W -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.2 $ +-- -- +-- Copyright (C) 2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Opt; use Opt; + +package body Validsw is + + ---------------------------------- + -- Reset_Validity_Check_Options -- + ---------------------------------- + + procedure Reset_Validity_Check_Options is + begin + Validity_Check_Copies := False; + Validity_Check_Default := True; + Validity_Check_Floating_Point := False; + Validity_Check_In_Out_Params := False; + Validity_Check_In_Params := False; + Validity_Check_Operands := False; + Validity_Check_Returns := False; + Validity_Check_Subscripts := False; + Validity_Check_Tests := False; + end Reset_Validity_Check_Options; + + --------------------------------- + -- Save_Validity_Check_Options -- + --------------------------------- + + procedure Save_Validity_Check_Options + (Options : out Validity_Check_Options) + is + P : Natural := 0; + + procedure Add (C : Character; S : Boolean); + -- Add given character C to string if switch S is true + + procedure Add (C : Character; S : Boolean) is + begin + if S then + P := P + 1; + Options (P) := C; + end if; + end Add; + + -- Start of processing for Save_Validity_Check_Options + + begin + for K in Options'Range loop + Options (K) := ' '; + end loop; + + Add ('c', Validity_Check_Copies); + Add ('d', Validity_Check_Default); + Add ('f', Validity_Check_Floating_Point); + Add ('i', Validity_Check_In_Params); + Add ('m', Validity_Check_In_Out_Params); + Add ('o', Validity_Check_Operands); + Add ('r', Validity_Check_Returns); + Add ('s', Validity_Check_Subscripts); + Add ('t', Validity_Check_Tests); + end Save_Validity_Check_Options; + + ---------------------------------------- + -- Set_Default_Validity_Check_Options -- + ---------------------------------------- + + procedure Set_Default_Validity_Check_Options is + begin + Reset_Validity_Check_Options; + Set_Validity_Check_Options ("d"); + end Set_Default_Validity_Check_Options; + + -------------------------------- + -- Set_Validity_Check_Options -- + -------------------------------- + + -- Version used when no error checking is required + + procedure Set_Validity_Check_Options (Options : String) is + OK : Boolean; + EC : Natural; + + begin + Set_Validity_Check_Options (Options, OK, EC); + end Set_Validity_Check_Options; + + -- Normal version with error checking + + procedure Set_Validity_Check_Options + (Options : String; + OK : out Boolean; + Err_Col : out Natural) + is + J : Natural; + C : Character; + + begin + Reset_Validity_Check_Options; + + J := Options'First; + while J <= Options'Last loop + C := Options (J); + J := J + 1; + + case C is + when 'c' => + Validity_Check_Copies := True; + + when 'd' => + Validity_Check_Default := True; + + when 'f' => + Validity_Check_Floating_Point := True; + + when 'i' => + Validity_Check_In_Params := True; + + when 'm' => + Validity_Check_In_Out_Params := True; + + when 'o' => + Validity_Check_Operands := True; + + when 'r' => + Validity_Check_Returns := True; + + when 's' => + Validity_Check_Subscripts := True; + + when 't' => + Validity_Check_Tests := True; + + when 'C' => + Validity_Check_Copies := False; + + when 'D' => + Validity_Check_Default := False; + + when 'I' => + Validity_Check_In_Params := False; + + when 'F' => + Validity_Check_Floating_Point := False; + + when 'M' => + Validity_Check_In_Out_Params := False; + + when 'O' => + Validity_Check_Operands := False; + + when 'R' => + Validity_Check_Returns := False; + + when 'S' => + Validity_Check_Subscripts := False; + + when 'T' => + Validity_Check_Tests := False; + + when 'a' => + Validity_Check_Copies := True; + Validity_Check_Default := True; + Validity_Check_Floating_Point := True; + Validity_Check_In_Out_Params := True; + Validity_Check_In_Params := True; + Validity_Check_Operands := True; + Validity_Check_Returns := True; + Validity_Check_Subscripts := True; + Validity_Check_Tests := True; + + when 'n' => + Validity_Check_Copies := False; + Validity_Check_Default := False; + Validity_Check_Floating_Point := False; + Validity_Check_In_Out_Params := False; + Validity_Check_In_Params := False; + Validity_Check_Operands := False; + Validity_Check_Returns := False; + Validity_Check_Subscripts := False; + Validity_Check_Tests := False; + + when ' ' => + null; + + when others => + OK := False; + Err_Col := J - 1; + return; + end case; + end loop; + + Validity_Checks_On := True; + OK := True; + Err_Col := Options'Last + 1; + end Set_Validity_Check_Options; + +end Validsw; diff --git a/gcc/ada/validsw.ads b/gcc/ada/validsw.ads new file mode 100644 index 00000000000..881fca4fd88 --- /dev/null +++ b/gcc/ada/validsw.ads @@ -0,0 +1,146 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- V A L I D S W -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.1 $ +-- -- +-- Copyright (C) 2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- This unit contains the routines used to handle setting of validity +-- checking options. + +package Validsw is + + ----------------------------- + -- Validity Check Switches -- + ----------------------------- + + -- The following flags determine the specific set of validity checks + -- to be made if validity checking is active (Validity_Checks_On = True) + + -- See GNAT users guide for an exact description of each option. The letter + -- given in the comment is the letter used in the -gnatV compiler switch + -- or in the argument of a Validity_Checks pragma to activate the option. + -- The corresponding upper case letter deactivates the option. + + Validity_Check_Copies : Boolean := False; + -- Controls the validity checking of copies. If this switch is set to + -- true using -gnatVc, or a 'c' in the argument of a Validity_Checks + -- pragma, then the right side of assignments and also initializing + -- expressions in object declarations are checked for validity. + + Validity_Check_Default : Boolean := True; + -- Controls default (reference manual) validity checking. If this switch + -- is set to True using -gnatVd or a 'd' in the argument of a Validity_ + -- Checks pragma then left side subscripts and case statement arguments + -- are checked for validity. This switch is also set by default if no + -- -gnatV switch is used and no Validity_Checks pragma is processed. + + Validity_Check_Floating_Point : Boolean := False; + -- Normally validity checking applies only to discrete values (integer + -- and enumeration types). If this switch is set to True using -gnatVf + -- or an 'f' in the argument of a Validity_Checks pragma, then floating- + -- point values are also checked. The context in which such checks + -- occur depends on other flags, e.g. if Validity_Check_Copies is also + -- set then floating-point values on the right side of an assignment + -- will be validity checked. + + Validity_Check_In_Out_Params : Boolean := False; + -- Controls the validity checking of IN OUT parameters. If this switch + -- is set to True using -gnatVm or a 'm' in the argument of a pragma + -- Validity_Checks, then the initial value of all IN OUT parameters + -- will be checked at the point of call of a procecure. Note that the + -- character 'm' here stands for modified (parameters). + + Validity_Check_In_Params : Boolean := False; + -- Controls the validity checking of IN parameters. If this switch is + -- set to True using -gnatVm or an 'i' in the argument of a pragma + -- Validity_Checks, then the initial value of all IN parameters + -- will be checked at the point of call of a procecure or function. + + Validity_Check_Operands : Boolean := False; + -- Controls validity checking of operands. If this switch is set to + -- True using -gnatVo or an 'o' in the argument of a Validity_Checks + -- pragma, then operands of all predefined operators and attributes + -- will be validity checked. + + Validity_Check_Returns : Boolean := False; + -- Controls validity checking of returned values. If this switch is set + -- to True using -gnatVr, or an 'r' in the argument of a Validity_Checks + -- pragma, then the expression in a RETURN statement is validity checked. + + Validity_Check_Subscripts : Boolean := False; + -- Controls validity checking of subscripts. If this switch is set to + -- True using -gnatVs, or an 's' in the argument of a Validity_Checks + -- pragma, then all subscripts are checked for validity. Note that left + -- side subscript checking is controlled also by Validity_Check_Default. + -- If Validity_Check_Subscripts is True, then all subscripts are checked, + -- otherwise if Validity_Check_Default is True, then left side subscripts + -- are checked, otherwise no subscripts are checked. + + Validity_Check_Tests : Boolean := False; + -- Controls validity checking of tests that occur in conditions (i.e. the + -- tests in IF, WHILE, and EXIT statements, and in entry guards). If this + -- switch is set to True using -gnatVt, or a 't' in the argument of a + -- Validity_Checks pragma, then all such conditions are validity checked. + + ----------------- + -- Subprograms -- + ----------------- + + procedure Set_Default_Validity_Check_Options; + -- This procedure is called to set the default validity checking options + -- that apply if no Validity_Check switches or pragma is given. + + procedure Set_Validity_Check_Options + (Options : String; + OK : out Boolean; + Err_Col : out Natural); + -- This procedure is called to set the validity check options that + -- correspond to the characters in the given Options string. If + -- all options are valid, then Set_Default_Validity_Check_Options + -- is first called to set the defaults, and then the options in the + -- given string are set in an additive manner. If any invalid character + -- is found, then OK is False on exit, and Err_Col is the index in + -- in options of the bad character. If all options are valid, then + -- OK is True on return, and Err_Col is set to options'Last + 1. + + procedure Set_Validity_Check_Options (Options : String); + -- Like the above procedure, except that the call is simply ignored if + -- there are any error conditions, this is for example appopriate for + -- calls where the string is known to be valid, e.g. because it was + -- obtained by Save_Validity_Check_Options. + + procedure Reset_Validity_Check_Options; + -- Sets all validity check options to off + + subtype Validity_Check_Options is String (1 .. 16); + -- Long enough string to hold all options from Save call below + + procedure Save_Validity_Check_Options + (Options : out Validity_Check_Options); + -- Sets Options to represent current selection of options. This + -- set can be restored by first calling Reset_Validity_Check_Options, + -- and then calling Set_Validity_Check_Options with the Options string. + +end Validsw; diff --git a/gcc/ada/widechar.adb b/gcc/ada/widechar.adb new file mode 100644 index 00000000000..39df6f7ba06 --- /dev/null +++ b/gcc/ada/widechar.adb @@ -0,0 +1,163 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- W I D E C H A R -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.15 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Note: this package uses the generic subprograms in System.Wch_Cnv, which +-- completely encapsulate the set of wide character encoding methods, so no +-- modifications are required when adding new encoding methods. + +with Opt; use Opt; + +with System.WCh_Cnv; use System.WCh_Cnv; +with System.WCh_Con; use System.WCh_Con; + +package body Widechar is + + --------------------------- + -- Is_Start_Of_Wide_Char -- + --------------------------- + + function Is_Start_Of_Wide_Char + (S : Source_Buffer_Ptr; + P : Source_Ptr) + return Boolean + is + begin + case Wide_Character_Encoding_Method is + when WCEM_Hex => + return S (P) = ASCII.ESC; + + when WCEM_Upper | + WCEM_Shift_JIS | + WCEM_EUC | + WCEM_UTF8 => + return S (P) >= Character'Val (16#80#); + + when WCEM_Brackets => + return P <= S'Last - 2 + and then S (P) = '[' + and then S (P + 1) = '"' + and then S (P + 2) /= '"'; + end case; + end Is_Start_Of_Wide_Char; + + ----------------- + -- Length_Wide -- + ----------------- + + function Length_Wide return Nat is + begin + return WC_Longest_Sequence; + end Length_Wide; + + --------------- + -- Scan_Wide -- + --------------- + + procedure Scan_Wide + (S : Source_Buffer_Ptr; + P : in out Source_Ptr; + C : out Char_Code; + Err : out Boolean) + is + function In_Char return Character; + -- Function to obtain characters of wide character escape sequence + + function In_Char return Character is + begin + P := P + 1; + return S (P - 1); + end In_Char; + + function WC_In is new Char_Sequence_To_Wide_Char (In_Char); + + begin + C := Char_Code (Wide_Character'Pos + (WC_In (In_Char, Wide_Character_Encoding_Method))); + Err := False; + + exception + when Constraint_Error => + C := Char_Code (0); + P := P - 1; + Err := True; + end Scan_Wide; + + -------------- + -- Set_Wide -- + -------------- + + procedure Set_Wide + (C : Char_Code; + S : in out String; + P : in out Natural) + is + procedure Out_Char (C : Character); + -- Procedure to store one character of wide character sequence + + procedure Out_Char (C : Character) is + begin + P := P + 1; + S (P) := C; + end Out_Char; + + procedure WC_Out is new Wide_Char_To_Char_Sequence (Out_Char); + + begin + WC_Out (Wide_Character'Val (C), Wide_Character_Encoding_Method); + end Set_Wide; + + --------------- + -- Skip_Wide -- + --------------- + + procedure Skip_Wide (S : String; P : in out Natural) is + function Skip_Char return Character; + -- Function to skip one character of wide character escape sequence + + function Skip_Char return Character is + begin + P := P + 1; + return S (P - 1); + end Skip_Char; + + function WC_Skip is new Char_Sequence_To_Wide_Char (Skip_Char); + + Discard : Wide_Character; + + begin + Discard := WC_Skip (Skip_Char, Wide_Character_Encoding_Method); + end Skip_Wide; + +end Widechar; diff --git a/gcc/ada/widechar.ads b/gcc/ada/widechar.ads new file mode 100644 index 00000000000..daf297e9542 --- /dev/null +++ b/gcc/ada/widechar.ads @@ -0,0 +1,87 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- W I D E C H A R -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.10 $ -- +-- -- +-- Copyright (C) 1992-1998 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- As a special exception, if other files instantiate generics from this -- +-- unit, or you link this unit with other files to produce an executable, -- +-- this unit does not by itself cause the resulting executable to be -- +-- covered by the GNU General Public License. This exception does not -- +-- however invalidate any other reasons why the executable file might be -- +-- covered by the GNU Public License. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Subprograms for manipulation of wide character sequences + +with Types; use Types; + +package Widechar is + + function Length_Wide return Nat; + -- Returns the maximum length in characters for the escape sequence that + -- is used to encode wide character literals outside the ASCII range. Used + -- only in the implementation of the attribute Width for Wide_Character. + + procedure Scan_Wide + (S : Source_Buffer_Ptr; + P : in out Source_Ptr; + C : out Char_Code; + Err : out Boolean); + -- On entry S (P) points to the first character in the source text for + -- a wide character (i.e. to an ESC character, a left bracket, or an + -- upper half character, depending on the representation method). A + -- single wide character is scanned. If no error is found, the value + -- stored in C is the code for this wide character, P is updated past + -- the sequence and Err is set to False. If an error is found, then + -- P points to the improper character, C is undefined, and Err is + -- set to True. + + procedure Set_Wide + (C : Char_Code; + S : in out String; + P : in out Natural); + -- The escape sequence (including any leading ESC character) for the + -- given character code is stored starting at S (P + 1), and on return + -- P points to the last stored character (i.e. P is the count of stored + -- characters on entry and exit, and the escape sequence is appended to + -- the end of the stored string). The character code C represents a code + -- originally constructed by Scan_Wide, so it is known to be in a range + -- that is appropriate for the encoding method in use. + + procedure Skip_Wide (S : String; P : in out Natural); + -- On entry, S (P) points to an ESC character for a wide character escape + -- sequence or to an upper half character if the encoding method uses the + -- upper bit, or to a left bracket if the brackets encoding method is in + -- use. On exit, P is bumped past the wide character sequence. No error + -- checking is done, since this is only used on escape sequences generated + -- by Set_Wide, which are known to be correct. + + function Is_Start_Of_Wide_Char + (S : Source_Buffer_Ptr; + P : Source_Ptr) + return Boolean; + -- Determines if S (P) is the start of a wide character sequence + +end Widechar; diff --git a/gcc/ada/xeinfo.adb b/gcc/ada/xeinfo.adb new file mode 100644 index 00000000000..38c35ce03c5 --- /dev/null +++ b/gcc/ada/xeinfo.adb @@ -0,0 +1,539 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT SYSTEM UTILITIES -- +-- -- +-- X E I N F O -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.29 $ +-- -- +-- Copyright (C) 1992-2000 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Program to construct C header file a-einfo.h (C version of einfo.ads spec) +-- for use by Gigi. This header file contaInF all definitions and access +-- functions, but does not contain set procedures, since Gigi is not allowed +-- to modify the GNAT tree) + +-- Input files: + +-- einfo.ads spec of Einfo package +-- einfo.adb body of Einfo package + +-- Output files: + +-- a-einfo.h Corresponding c header file + +-- Note: It is assumed that the input files have been compiled without errors + +-- An optional argument allows the specification of an output file name to +-- override the default a-einfo.h file name for the generated output file. + +-- Most, but not all of the functions in Einfo can be inlined in the C header. +-- They are the functions identified by pragma Inline in the spec. Functions +-- that cannot be inlined are simply defined in the header. + +with Ada.Command_Line; use Ada.Command_Line; +with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; +with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO; +with Ada.Strings.Maps; use Ada.Strings.Maps; +with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants; +with Ada.Text_IO; use Ada.Text_IO; + +with GNAT.Spitbol; use GNAT.Spitbol; +with GNAT.Spitbol.Patterns; use GNAT.Spitbol.Patterns; +with GNAT.Spitbol.Table_Boolean; use GNAT.Spitbol.Table_Boolean; + +procedure XEinfo is + + package TB renames GNAT.Spitbol.Table_Boolean; + + Err : exception; + + A : VString := Nul; + B : VString := Nul; + C : VString := Nul; + Einfobrev : VString := Nul; + Einfosrev : VString := Nul; + Expr : VString := Nul; + Filler : VString := Nul; + Fline : VString := Nul; + Formal : VString := Nul; + Formaltyp : VString := Nul; + FN : VString := Nul; + Line : VString := Nul; + N : VString := Nul; + N1 : VString := Nul; + N2 : VString := Nul; + N3 : VString := Nul; + Nam : VString := Nul; + Name : VString := Nul; + NewS : VString := Nul; + Nextlin : VString := Nul; + OldS : VString := Nul; + Rtn : VString := Nul; + Term : VString := Nul; + XEinforev : VString := Nul; + + InB : File_Type; + -- Used to read initial header from body + + InF : File_Type; + -- Used to read full text of both spec and body + + Ofile : File_Type; + -- Used to write output file + + wsp : Pattern := NSpan (' ' & ASCII.HT); + Get_BRev : Pattern := BreakX ('$') & "$Rev" & "ision: " + & Break (' ') * Einfobrev; + Get_SRev : Pattern := BreakX ('$') & "$Rev" & "ision: " + & Break (' ') * Einfosrev; + Comment : Pattern := wsp & "--"; + For_Rep : Pattern := wsp & "for"; + Get_Func : Pattern := wsp * A & "function" & wsp & Break (' ') * Name; + Inline : Pattern := wsp & "pragma Inline (" & Break (')') * Name; + Get_Pack : Pattern := wsp & "package "; + Get_Enam : Pattern := wsp & Break (',') * N & ','; + Find_Fun : Pattern := wsp & "function"; + F_Subtyp : Pattern := wsp * A & "subtype " & Break (' ') * N; + G_Subtyp : Pattern := wsp & "subtype" & wsp & Break (' ') * NewS + & wsp & "is" & wsp & Break (" ;") * OldS + & wsp & ';' & wsp & Rtab (0); + F_Typ : Pattern := wsp * A & "type " & Break (' ') * N & " is ("; + Get_Nam : Pattern := wsp * A & Break (",)") * Nam & Len (1) * Term; + Get_Styp : Pattern := wsp * A & "subtype " & Break (' ') * N; + Get_N1 : Pattern := wsp & Break (' ') * N1; + Get_N2 : Pattern := wsp & "-- " & Rest * N2; + Get_N3 : Pattern := wsp & Break (';') * N3; + Get_FN : Pattern := wsp * C & "function" & wsp & Break (" (") * FN; + Is_Rturn : Pattern := BreakX ('r') & "return"; + Is_Begin : Pattern := wsp & "begin"; + Get_Asrt : Pattern := wsp & "pragma Assert"; + Semicoln : Pattern := BreakX (';'); + Get_Cmnt : Pattern := BreakX ('-') * A & "--"; + Get_Expr : Pattern := wsp & "return " & Break (';') * Expr; + Chek_End : Pattern := wsp & "end" & BreakX (';') & ';'; + Get_B1 : Pattern := BreakX (' ') * A & " in " & Rest * B; + Get_B2 : Pattern := BreakX (' ') * A & " = " & Rest * B; + Get_B3 : Pattern := BreakX (' ') * A & " /= " & Rest * B; + To_Paren : Pattern := wsp * Filler & '('; + Get_Fml : Pattern := Break (" :") * Formal & wsp & ':' & wsp + & BreakX (" );") * Formaltyp; + Nxt_Fml : Pattern := wsp & "; "; + Get_Rtn : Pattern := wsp & "return" & wsp & BreakX (" ;") * Rtn; + Rem_Prn : Pattern := wsp & ')'; + + M : Match_Result; + + Lineno : Natural := 0; + -- Line number in spec + + V : Natural; + Ctr : Natural; + + Inlined : TB.Table (200); + -- Inlined = True for inlined function, False otherwise + + Lastinlined : Boolean; + + procedure Badfunc; + -- Signal bad function in body + + function Getlin return VString; + -- Get non-comment line (comment lines skipped, also skips FOR rep clauses) + -- Fatal error (raises End_Error exception) if end of file encountered + + procedure Must (B : Boolean); + -- Raises Err if the argument (a Match) call, returns False + + procedure Sethead (Line : in out VString; Term : String); + -- Process function header into C + + ------------- + -- Badfunc -- + ------------- + + procedure Badfunc is + begin + Put_Line + (Standard_Error, + "Body for function " & FN & " does not meet requirements"); + raise Err; + end Badfunc; + + ------------- + -- Getlin -- + ------------- + + function Getlin return VString is + Lin : VString; + + begin + loop + Lin := Get_Line (InF); + Lineno := Lineno + 1; + + if Lin /= "" + and then not Match (Lin, Comment) + and then not Match (Lin, For_Rep) + then + return Lin; + end if; + end loop; + end Getlin; + + ---------- + -- Must -- + ---------- + + procedure Must (B : Boolean) is + begin + if not B then + raise Err; + end if; + end Must; + + ------------- + -- Sethead -- + ------------- + + procedure Sethead (Line : in out VString; Term : String) is + Args : VString; + + begin + Must (Match (Line, Get_Func, "")); + Args := Nul; + + if Match (Line, To_Paren, "") then + Args := Filler & '('; + + loop + Must (Match (Line, Get_Fml, "")); + Append (Args, Formaltyp & ' ' & Formal); + exit when not Match (Line, Nxt_Fml); + Append (Args, ","); + end loop; + + Match (Line, Rem_Prn, ""); + Append (Args, ')'); + end if; + + Must (Match (Line, Get_Rtn)); + + if Present (Inlined, Name) then + Put_Line (Ofile, A & "INLINE " & Rtn & ' ' & Name & Args & Term); + else + Put_Line (Ofile, A & Rtn & ' ' & Name & Args & Term); + end if; + end Sethead; + +-- Start of processing for XEinfo + +begin + Anchored_Mode := True; + + Match ("$Revision: 1.29 $", "$Rev" & "ision: " & Break (' ') * XEinforev); + + if Argument_Count > 0 then + Create (Ofile, Out_File, Argument (1)); + else + Create (Ofile, Out_File, "a-einfo.h"); + end if; + + Open (InB, In_File, "einfo.adb"); + Open (InF, In_File, "einfo.ads"); + + Lineno := 0; + + -- Get einfo revs and write header to output file + + loop + Line := Get_Line (InB); + + if Line = "" then + raise Err; + end if; + + exit when Match (Line, Get_BRev); + end loop; + + loop + Line := Get_Line (InF); + Lineno := Lineno + 1; + exit when Line = ""; + + if Match (Line, Get_SRev) then + Put_Line + (Ofile, + "/* Generated by xeinfo revision " & XEinforev & + " using */"); + Put_Line + (Ofile, + "/* einfo.ads revision " & Einfosrev & + " */"); + Put_Line + (Ofile, + "/* einfo.adb revision " & Einfobrev & + " */"); + else + Match (Line, + "-- S p e c ", + "-- C Header File "); + + Match (Line, "--", "/*"); + Match (Line, Rtab (2) * A & "--", M); + Replace (M, A & "*/"); + Put_Line (Ofile, Line); + end if; + end loop; + + Put_Line (Ofile, ""); + + -- Find and record pragma Inlines + + loop + Line := Get_Line (InF); + exit when Match (Line, " -- END XEINFO INLINES"); + + + if Match (Line, Inline) then + Set (Inlined, Name, True); + end if; + end loop; + + -- Skip to package line + + Reset (InF, In_File); + Lineno := 0; + + loop + Line := Getlin; + exit when Match (Line, Get_Pack); + end loop; + + V := 0; + Line := Getlin; + Must (Match (Line, wsp & "type Entity_Kind")); + + -- Process entity kind code definitions + + loop + Line := Getlin; + exit when not Match (Line, Get_Enam); + Put_Line (Ofile, " #define " & Rpad (N, 32) & " " & V); + V := V + 1; + end loop; + + Must (Match (Line, wsp & Rest * N)); + Put_Line (Ofile, " #define " & Rpad (N, 32) & ' ' & V); + Line := Getlin; + + Must (Match (Line, wsp & ");")); + Put_Line (Ofile, ""); + + -- Loop through subtype and type declarations + + loop + Line := Getlin; + exit when Match (Line, Find_Fun); + + -- Case of a subtype declaration + + if Match (Line, F_Subtyp) then + + -- Case of a subtype declaration that is an abbreviation of the + -- form subtype x is y, and if so generate the appropriate typedef + + if Match (Line, G_Subtyp) then + Put_Line (Ofile, A & "typedef " & OldS & ' ' & NewS & ';'); + + -- Otherwise the subtype must be declaring a subrange of Entity_Id + + else + Must (Match (Line, Get_Styp)); + Line := Getlin; + Must (Match (Line, Get_N1)); + + loop + Line := Get_Line (InF); + Lineno := Lineno + 1; + exit when not Match (Line, Get_N2); + end loop; + + Must (Match (Line, Get_N3)); + Put_Line (Ofile, A & "SUBTYPE (" & N & ", Entity_Kind, "); + Put_Line (Ofile, A & " " & N1 & ", " & N3 & ')'); + Put_Line (Ofile, ""); + end if; + + + -- Case of type declaration + + elsif Match (Line, F_Typ) then + -- Process type declaration (must be enumeration type) + + Ctr := 0; + Put_Line (Ofile, A & "typedef int " & N & ';'); + + loop + Line := Getlin; + Must (Match (Line, Get_Nam)); + Put_Line (Ofile, A & "#define " & Rpad (Nam, 25) & Ctr); + Ctr := Ctr + 1; + exit when Term /= ","; + end loop; + + Put_Line (Ofile, ""); + + -- Neither subtype nor type declaration + + else + raise Err; + end if; + end loop; + + -- Process function declarations + -- Note: Lastinlined used to control blank lines + + Put_Line (Ofile, ""); + Lastinlined := True; + + -- Loop through function declarations + + while Match (Line, Get_FN) loop + + -- Non-inlined funcion + + if not Present (Inlined, FN) then + Put_Line (Ofile, ""); + Put_Line + (Ofile, + " #define " & FN & " einfo__" & Translate (FN, Lower_Case_Map)); + + -- Inlined function + + else + if not Lastinlined then + Put_Line (Ofile, ""); + end if; + end if; + + -- Merge here to output spec + + Sethead (Line, ";"); + Lastinlined := Get (Inlined, FN); + Line := Getlin; + end loop; + + Put_Line (Ofile, ""); + + -- Read body to find inlined functions + + Close (InB); + Close (InF); + Open (InF, In_File, "einfo.adb"); + Lineno := 0; + + -- Loop through input lines to find bodies of inlined functions + + while not End_Of_File (InF) loop + Fline := Get_Line (InF); + + if Match (Fline, Get_FN) + and then Get (Inlined, FN) + then + -- Here we have an inlined function + + if not Match (Fline, Is_Rturn) then + Line := Fline; + Badfunc; + end if; + + Line := Getlin; + + if not Match (Line, Is_Begin) then + Badfunc; + end if; + + -- Skip past pragma Asserts + + loop + Line := Getlin; + exit when not Match (Line, Get_Asrt); + + -- Pragma asser found, get its continuation lines + + loop + exit when Match (Line, Semicoln); + Line := Getlin; + end loop; + end loop; + + -- Process return statement + + Match (Line, Get_Cmnt, M); + Replace (M, A); + + -- Get continuations of return statemnt + + while not Match (Line, Semicoln) loop + Nextlin := Getlin; + Match (Nextlin, wsp, " "); + Append (Line, Nextlin); + end loop; + + if not Match (Line, Get_Expr) then + Badfunc; + end if; + + Line := Getlin; + + if not Match (Line, Chek_End) then + Badfunc; + end if; + + Match (Expr, Get_B1, M); + Replace (M, "IN (" & A & ", " & B & ')'); + Match (Expr, Get_B2, M); + Replace (M, A & " == " & B); + Match (Expr, Get_B3, M); + Replace (M, A & " != " & B); + Put_Line (Ofile, ""); + Sethead (Fline, ""); + Put_Line (Ofile, C & " { return " & Expr & "; }"); + end if; + end loop; + + Put_Line (Ofile, ""); + Put_Line + (Ofile, + "/* End of einfo.h (C version of Einfo package specification) */"); + +exception + when Err => + Put_Line (Standard_Error, Lineno & ". " & Line); + Put_Line (Standard_Error, "**** fatal error ****"); + Set_Exit_Status (1); + + when End_Error => + Put_Line (Standard_Error, "unexpected end of file"); + Put_Line (Standard_Error, "**** fatal error ****"); + +end XEinfo; diff --git a/gcc/ada/xnmake.adb b/gcc/ada/xnmake.adb new file mode 100644 index 00000000000..f87b8500b89 --- /dev/null +++ b/gcc/ada/xnmake.adb @@ -0,0 +1,485 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT SYSTEM UTILITIES -- +-- -- +-- X N M A K E -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.27 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Program to construct the spec and body of the Nmake package + +-- Input files: + +-- sinfo.ads Spec of Sinfo package +-- nmake.adt Template for Nmake package + +-- Output files: + +-- nmake.ads Spec of Nmake package +-- nmake.adb Body of Nmake package + +-- Note: this program assumes that sinfo.ads has passed the error checks that +-- are carried out by the csinfo utility, so it does not duplicate these +-- checks and assumes that sinfo.ads has the correct form. + +-- In the absence of any switches, both the ads and adb files are output. +-- The switch -s or /s indicates that only the ads file is to be output. +-- The switch -b or /b indicates that only the adb file is to be output. + +-- If a file name argument is given, then the output is written to this file +-- rather than to nmake.ads or nmake.adb. A file name can only be given if +-- exactly one of the -s or -b options is present. + +with Ada.Command_Line; use Ada.Command_Line; +with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; +with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO; +with Ada.Strings.Maps; use Ada.Strings.Maps; +with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants; +with Ada.Text_IO; use Ada.Text_IO; + +with GNAT.Spitbol; use GNAT.Spitbol; +with GNAT.Spitbol.Patterns; use GNAT.Spitbol.Patterns; + +procedure XNmake is + + Err : exception; + -- Raised to terminate execution + + A : VString := Nul; + Arg : VString := Nul; + Arg_List : VString := Nul; + Comment : VString := Nul; + Default : VString := Nul; + Field : VString := Nul; + Line : VString := Nul; + Node : VString := Nul; + Op_Name : VString := Nul; + Prevl : VString := Nul; + Sinfo_Rev : VString := Nul; + Synonym : VString := Nul; + Temp_Rev : VString := Nul; + X : VString := Nul; + XNmake_Rev : VString := Nul; + + Lineno : Natural; + NWidth : Natural; + + FileS : VString := V ("nmake.ads"); + FileB : VString := V ("nmake.adb"); + -- Set to null if corresponding file not to be generated + + Given_File : VString := Nul; + -- File name given by command line argument + + InS, InT : File_Type; + OutS, OutB : File_Type; + + wsp : Pattern := Span (' ' & ASCII.HT); + + -- Note: in following patterns, we break up the word revision to + -- avoid RCS getting enthusiastic about updating the reference! + + Get_SRev : Pattern := BreakX ('$') & "$Rev" & "ision: " & + Break (' ') * Sinfo_Rev; + + GetT_Rev : Pattern := BreakX ('$') & "$Rev" & "ision: " & + Break (' ') * Temp_Rev; + + + Body_Only : Pattern := BreakX (' ') * X & Span (' ') & "-- body only"; + Spec_Only : Pattern := BreakX (' ') * X & Span (' ') & "-- spec only"; + + Node_Hdr : Pattern := wsp & "-- N_" & Rest * Node; + Punc : Pattern := BreakX (" .,"); + + Binop : Pattern := wsp & "-- plus fields for binary operator"; + Unop : Pattern := wsp & "-- plus fields for unary operator"; + Syn : Pattern := wsp & "-- " & Break (' ') * Synonym + & " (" & Break (')') * Field & Rest * Comment; + + Templ : Pattern := BreakX ('T') * A & "T e m p l a t e"; + Spec : Pattern := BreakX ('S') * A & "S p e c"; + + Sem_Field : Pattern := BreakX ('-') & "-Sem"; + Lib_Field : Pattern := BreakX ('-') & "-Lib"; + + Get_Field : Pattern := BreakX (Decimal_Digit_Set) * Field; + + Get_Dflt : Pattern := BreakX ('(') & "(set to " + & Break (" ") * Default & " if"; + + Next_Arg : Pattern := Break (',') * Arg & ','; + + Op_Node : Pattern := "Op_" & Rest * Op_Name; + + Shft_Rot : Pattern := "Shift_" or "Rotate_"; + + No_Ent : Pattern := "Or_Else" or "And_Then" or "In" or "Not_In"; + + M : Match_Result; + + V_String_Id : constant VString := V ("String_Id"); + V_Node_Id : constant VString := V ("Node_Id"); + V_Name_Id : constant VString := V ("Name_Id"); + V_List_Id : constant VString := V ("List_Id"); + V_Elist_Id : constant VString := V ("Elist_Id"); + V_Boolean : constant VString := V ("Boolean"); + + procedure WriteS (S : String); + procedure WriteB (S : String); + procedure WriteBS (S : String); + procedure WriteS (S : VString); + procedure WriteB (S : VString); + procedure WriteBS (S : VString); + -- Write given line to spec or body file or both if active + + procedure WriteB (S : String) is + begin + if FileB /= Nul then + Put_Line (OutB, S); + end if; + end WriteB; + + procedure WriteB (S : VString) is + begin + if FileB /= Nul then + Put_Line (OutB, S); + end if; + end WriteB; + + procedure WriteBS (S : String) is + begin + if FileB /= Nul then + Put_Line (OutB, S); + end if; + + if FileS /= Nul then + Put_Line (OutS, S); + end if; + end WriteBS; + + procedure WriteBS (S : VString) is + begin + if FileB /= Nul then + Put_Line (OutB, S); + end if; + + if FileS /= Nul then + Put_Line (OutS, S); + end if; + end WriteBS; + + procedure WriteS (S : String) is + begin + if FileS /= Nul then + Put_Line (OutS, S); + end if; + end WriteS; + + procedure WriteS (S : VString) is + begin + if FileS /= Nul then + Put_Line (OutS, S); + end if; + end WriteS; + +-- Start of processing for XNmake + +begin + -- Capture our revision (following line updated by RCS) + + Match ("$Revision: 1.27 $", "$Rev" & "ision: " & Break (' ') * XNmake_Rev); + + Lineno := 0; + NWidth := 28; + Anchored_Mode := True; + + for ArgN in 1 .. Argument_Count loop + declare + Arg : constant String := Argument (ArgN); + + begin + if Arg (1) = '/' or else Arg (1) = '-' then + if Arg'Length = 2 + and then (Arg (2) = 'b' or else Arg (2) = 'B') + then + FileS := Nul; + + elsif Arg'Length = 2 + and then (Arg (2) = 's' or else Arg (2) = 'S') + then + FileB := Nul; + + else + raise Err; + end if; + + else + if Given_File /= Nul then + raise Err; + else + Given_File := V (Arg); + end if; + end if; + end; + end loop; + + if FileS = Nul and then FileB = Nul then + raise Err; + + elsif Given_File /= Nul then + if FileS = Nul then + FileS := Given_File; + + elsif FileB = Nul then + FileB := Given_File; + + else + raise Err; + end if; + end if; + + Open (InS, In_File, "sinfo.ads"); + Open (InT, In_File, "nmake.adt"); + + if FileS /= Nul then + Create (OutS, Out_File, S (FileS)); + end if; + + if FileB /= Nul then + Create (OutB, Out_File, S (FileB)); + end if; + + Anchored_Mode := True; + + -- Get Sinfo revision number + + loop + Line := Get_Line (InS); + exit when Match (Line, Get_SRev); + end loop; + + -- Copy initial part of template to spec and body + + loop + Line := Get_Line (InT); + + if Match (Line, GetT_Rev) then + WriteBS + ("-- Generated by xnmake revision " & + XNmake_Rev & " using" & + " --"); + + WriteBS + ("-- sinfo.ads revision " & + Sinfo_Rev & + " --"); + + WriteBS + ("-- nmake.adt revision " & + Temp_Rev & + " --"); + + else + -- Skip lines describing the template + + if Match (Line, "-- This file is a template") then + loop + Line := Get_Line (InT); + exit when Line = ""; + end loop; + end if; + + exit when Match (Line, "package"); + + if Match (Line, Body_Only, M) then + Replace (M, X); + WriteB (Line); + + elsif Match (Line, Spec_Only, M) then + Replace (M, X); + WriteS (Line); + + else + if Match (Line, Templ, M) then + Replace (M, A & " S p e c "); + end if; + + WriteS (Line); + + if Match (Line, Spec, M) then + Replace (M, A & "B o d y"); + end if; + + WriteB (Line); + end if; + end if; + end loop; + + -- Package line reached + + WriteS ("package Nmake is"); + WriteB ("package body Nmake is"); + WriteB (""); + + -- Copy rest of lines up to template insert point to spec only + + loop + Line := Get_Line (InT); + exit when Match (Line, "!!TEMPLATE INSERTION POINT"); + WriteS (Line); + end loop; + + -- Here we are doing the actual insertions, loop through node types + + loop + Line := Get_Line (InS); + + if Match (Line, Node_Hdr) + and then not Match (Node, Punc) + and then Node /= "Unused" + then + exit when Node = "Empty"; + Prevl := " function Make_" & Node & " (Sloc : Source_Ptr"; + Arg_List := Nul; + + -- Loop through fields of one node + + loop + Line := Get_Line (InS); + exit when Line = ""; + + if Match (Line, Binop) then + WriteBS (Prevl & ';'); + Append (Arg_List, "Left_Opnd,Right_Opnd,"); + WriteBS ( + " " & Rpad ("Left_Opnd", NWidth) & " : Node_Id;"); + Prevl := + " " & Rpad ("Right_Opnd", NWidth) & " : Node_Id"; + + elsif Match (Line, Unop) then + WriteBS (Prevl & ';'); + Append (Arg_List, "Right_Opnd,"); + Prevl := " " & Rpad ("Right_Opnd", NWidth) & " : Node_Id"; + + elsif Match (Line, Syn) then + if Synonym /= "Prev_Ids" + and then Synonym /= "More_Ids" + and then Synonym /= "Comes_From_Source" + and then Synonym /= "Paren_Count" + and then not Match (Field, Sem_Field) + and then not Match (Field, Lib_Field) + then + Match (Field, Get_Field); + + if Field = "Str" then Field := V_String_Id; + elsif Field = "Node" then Field := V_Node_Id; + elsif Field = "Name" then Field := V_Name_Id; + elsif Field = "List" then Field := V_List_Id; + elsif Field = "Elist" then Field := V_Elist_Id; + elsif Field = "Flag" then Field := V_Boolean; + end if; + + if Field = "Boolean" then + Default := V ("False"); + else + Default := Nul; + end if; + + Match (Comment, Get_Dflt); + + WriteBS (Prevl & ';'); + Append (Arg_List, Synonym & ','); + Rpad (Synonym, NWidth); + + if Default = "" then + Prevl := " " & Synonym & " : " & Field; + else + Prevl := + " " & Synonym & " : " & Field & " := " & Default; + end if; + end if; + end if; + end loop; + + WriteBS (Prevl & ')'); + WriteS (" return Node_Id;"); + WriteS (" pragma Inline (Make_" & Node & ");"); + WriteB (" return Node_Id"); + WriteB (" is"); + WriteB (" N : constant Node_Id :="); + + if Match (Node, "Defining_Identifier") or else + Match (Node, "Defining_Character") or else + Match (Node, "Defining_Operator") + then + WriteB (" New_Entity (N_" & Node & ", Sloc);"); + else + WriteB (" New_Node (N_" & Node & ", Sloc);"); + end if; + + WriteB (" begin"); + + while Match (Arg_List, Next_Arg, "") loop + if Length (Arg) < NWidth then + WriteB (" Set_" & Arg & " (N, " & Arg & ");"); + else + WriteB (" Set_" & Arg); + WriteB (" (N, " & Arg & ");"); + end if; + end loop; + + if Match (Node, Op_Node) then + if Node = "Op_Plus" then + WriteB (" Set_Chars (N, Name_Op_Add);"); + + elsif Node = "Op_Minus" then + WriteB (" Set_Chars (N, Name_Op_Subtract);"); + + elsif Match (Op_Name, Shft_Rot) then + WriteB (" Set_Chars (N, Name_" & Op_Name & ");"); + + else + WriteB (" Set_Chars (N, Name_" & Node & ");"); + end if; + + if not Match (Op_Name, No_Ent) then + WriteB (" Set_Entity (N, Standard_" & Node & ");"); + end if; + end if; + + WriteB (" return N;"); + WriteB (" end Make_" & Node & ';'); + WriteBS (""); + end if; + end loop; + + WriteBS ("end Nmake;"); + +exception + + when Err => + Put_Line (Standard_Error, "usage: xnmake [-b] [-s] [filename]"); + Set_Exit_Status (1); + +end XNmake; diff --git a/gcc/ada/xr_tabls.adb b/gcc/ada/xr_tabls.adb new file mode 100644 index 00000000000..02af07e75ec --- /dev/null +++ b/gcc/ada/xr_tabls.adb @@ -0,0 +1,1376 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- X R _ T A B L S -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.36 $ +-- -- +-- Copyright (C) 1998-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Ada.IO_Exceptions; +with Ada.Strings.Fixed; +with Ada.Strings; +with Ada.Text_IO; +with Hostparm; +with GNAT.IO_Aux; +with Unchecked_Deallocation; +with GNAT.OS_Lib; use GNAT.OS_Lib; +with GNAT.Directory_Operations; use GNAT.Directory_Operations; +with Osint; + +with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; + +package body Xr_Tabls is + + subtype Line_String is String (1 .. Hostparm.Max_Line_Length); + subtype File_Name_String is String (1 .. Hostparm.Max_Name_Length); + + function Base_File_Name (File : String) return String; + -- Return the base file name for File (ie not including the directory) + + function Dir_Name (File : String; Base : String := "") return String; + -- Return the directory name of File, or "" if there is no directory part + -- in File. + -- This includes the last separator at the end, and always return an + -- absolute path name (directories are relative to Base, or the current + -- directory if Base is "") + + Dir_Sep : Character renames GNAT.OS_Lib.Directory_Separator; + + Files : File_Table; + Entities : Entity_Table; + Directories : Project_File_Ptr; + Default_Match : Boolean := False; + + --------------------- + -- Add_Declaration -- + --------------------- + + function Add_Declaration + (File_Ref : File_Reference; + Symbol : String; + Line : Natural; + Column : Natural; + Decl_Type : Character) + return Declaration_Reference + is + The_Entities : Declaration_Reference := Entities.Table; + New_Decl : Declaration_Reference; + Result : Compare_Result; + Prev : Declaration_Reference := null; + + begin + -- Check if the identifier already exists in the table + + while The_Entities /= null loop + Result := Compare (The_Entities, File_Ref, Line, Column, Symbol); + exit when Result = GreaterThan; + + if Result = Equal then + return The_Entities; + end if; + + Prev := The_Entities; + The_Entities := The_Entities.Next; + end loop; + + -- Insert the Declaration in the table + + New_Decl := new Declaration_Record' + (Symbol_Length => Symbol'Length, + Symbol => Symbol, + Decl => (File => File_Ref, + Line => Line, + Column => Column, + Source_Line => Null_Unbounded_String, + Next => null), + Decl_Type => Decl_Type, + Body_Ref => null, + Ref_Ref => null, + Modif_Ref => null, + Match => Default_Match or else Match (File_Ref, Line, Column), + Par_Symbol => null, + Next => null); + + if Prev = null then + New_Decl.Next := Entities.Table; + Entities.Table := New_Decl; + else + New_Decl.Next := Prev.Next; + Prev.Next := New_Decl; + end if; + + if New_Decl.Match then + Files.Longest_Name := Natural'Max (File_Ref.File'Length, + Files.Longest_Name); + end if; + + return New_Decl; + end Add_Declaration; + + -------------- + -- Add_File -- + -------------- + + procedure Add_File + (File_Name : String; + File_Existed : out Boolean; + Ref : out File_Reference; + Visited : Boolean := True; + Emit_Warning : Boolean := False; + Gnatchop_File : String := ""; + Gnatchop_Offset : Integer := 0) + is + The_Files : File_Reference := Files.Table; + Base : constant String := Base_File_Name (File_Name); + Dir : constant String := Xr_Tabls.Dir_Name (File_Name); + Dir_Acc : String_Access := null; + + begin + -- Do we have a directory name as well ? + if Dir /= "" then + Dir_Acc := new String' (Dir); + end if; + + -- Check if the file already exists in the table + + while The_Files /= null loop + + if The_Files.File = File_Name then + File_Existed := True; + Ref := The_Files; + return; + end if; + + The_Files := The_Files.Next; + end loop; + + Ref := new File_Record' + (File_Length => Base'Length, + File => Base, + Dir => Dir_Acc, + Lines => null, + Visited => Visited, + Emit_Warning => Emit_Warning, + Gnatchop_File => new String' (Gnatchop_File), + Gnatchop_Offset => Gnatchop_Offset, + Next => Files.Table); + Files.Table := Ref; + File_Existed := False; + end Add_File; + + -------------- + -- Add_Line -- + -------------- + + procedure Add_Line + (File : File_Reference; + Line : Natural; + Column : Natural) + is + begin + File.Lines := new Ref_In_File'(Line => Line, + Column => Column, + Next => File.Lines); + end Add_Line; + + ---------------- + -- Add_Parent -- + ---------------- + + procedure Add_Parent + (Declaration : in out Declaration_Reference; + Symbol : String; + Line : Natural; + Column : Natural; + File_Ref : File_Reference) + is + begin + Declaration.Par_Symbol := new Declaration_Record' + (Symbol_Length => Symbol'Length, + Symbol => Symbol, + Decl => (File => File_Ref, + Line => Line, + Column => Column, + Source_Line => Null_Unbounded_String, + Next => null), + Decl_Type => ' ', + Body_Ref => null, + Ref_Ref => null, + Modif_Ref => null, + Match => False, + Par_Symbol => null, + Next => null); + end Add_Parent; + + ------------------- + -- Add_Reference -- + ------------------- + + procedure Add_Reference + (Declaration : Declaration_Reference; + File_Ref : File_Reference; + Line : Natural; + Column : Natural; + Ref_Type : Character) + is + procedure Free is new Unchecked_Deallocation + (Reference_Record, Reference); + + Ref : Reference; + Prev : Reference := null; + Result : Compare_Result; + New_Ref : Reference := new Reference_Record' + (File => File_Ref, + Line => Line, + Column => Column, + Source_Line => Null_Unbounded_String, + Next => null); + + begin + case Ref_Type is + when 'b' | 'c' => Ref := Declaration.Body_Ref; + when 'r' | 'i' => Ref := Declaration.Ref_Ref; + when 'm' => Ref := Declaration.Modif_Ref; + when others => return; + end case; + + -- Check if the reference already exists + + while Ref /= null loop + Result := Compare (New_Ref, Ref); + exit when Result = LessThan; + + if Result = Equal then + Free (New_Ref); + return; + end if; + + Prev := Ref; + Ref := Ref.Next; + end loop; + + -- Insert it in the list + + if Prev /= null then + New_Ref.Next := Prev.Next; + Prev.Next := New_Ref; + + else + case Ref_Type is + when 'b' | 'c' => + New_Ref.Next := Declaration.Body_Ref; + Declaration.Body_Ref := New_Ref; + when 'r' | 'i' => + New_Ref.Next := Declaration.Ref_Ref; + Declaration.Ref_Ref := New_Ref; + when 'm' => + New_Ref.Next := Declaration.Modif_Ref; + Declaration.Modif_Ref := New_Ref; + when others => null; + end case; + end if; + + if not Declaration.Match then + Declaration.Match := Match (File_Ref, Line, Column); + end if; + + if Declaration.Match then + Files.Longest_Name := Natural'Max (File_Ref.File'Length, + Files.Longest_Name); + end if; + end Add_Reference; + + ------------------- + -- ALI_File_Name -- + ------------------- + + function ALI_File_Name (Ada_File_Name : String) return String is + Index : Natural := Ada.Strings.Fixed.Index + (Ada_File_Name, ".", Going => Ada.Strings.Backward); + + begin + if Index /= 0 then + return Ada_File_Name (Ada_File_Name'First .. Index) + & "ali"; + else + return Ada_File_Name & ".ali"; + end if; + end ALI_File_Name; + + -------------------- + -- Base_File_Name -- + -------------------- + + function Base_File_Name (File : String) return String is + begin + for J in reverse File'Range loop + if File (J) = '/' or else File (J) = Dir_Sep then + return File (J + 1 .. File'Last); + end if; + end loop; + return File; + end Base_File_Name; + + ------------- + -- Compare -- + ------------- + + function Compare + (Ref1 : Reference; + Ref2 : Reference) + return Compare_Result + is + begin + if Ref1 = null then + return GreaterThan; + elsif Ref2 = null then + return LessThan; + end if; + + if Ref1.File.File < Ref2.File.File then + return LessThan; + + elsif Ref1.File.File = Ref2.File.File then + if Ref1.Line < Ref2.Line then + return LessThan; + + elsif Ref1.Line = Ref2.Line then + if Ref1.Column < Ref2.Column then + return LessThan; + elsif Ref1.Column = Ref2.Column then + return Equal; + else + return GreaterThan; + end if; + + else + return GreaterThan; + end if; + + else + return GreaterThan; + end if; + end Compare; + + ------------- + -- Compare -- + ------------- + + function Compare + (Decl1 : Declaration_Reference; + File2 : File_Reference; + Line2 : Integer; + Col2 : Integer; + Symb2 : String) + return Compare_Result + is + begin + if Decl1 = null then + return GreaterThan; + end if; + + if Decl1.Symbol < Symb2 then + return LessThan; + elsif Decl1.Symbol > Symb2 then + return GreaterThan; + end if; + + if Decl1.Decl.File.File < Get_File (File2) then + return LessThan; + + elsif Decl1.Decl.File.File = Get_File (File2) then + if Decl1.Decl.Line < Line2 then + return LessThan; + + elsif Decl1.Decl.Line = Line2 then + if Decl1.Decl.Column < Col2 then + return LessThan; + + elsif Decl1.Decl.Column = Col2 then + return Equal; + + else + return GreaterThan; + end if; + + else + return GreaterThan; + end if; + + else + return GreaterThan; + end if; + end Compare; + + ------------------------- + -- Create_Project_File -- + ------------------------- + + procedure Create_Project_File + (Name : String) + is + use Ada.Strings.Unbounded; + + Obj_Dir : Unbounded_String := Null_Unbounded_String; + Src_Dir : Unbounded_String := Null_Unbounded_String; + Build_Dir : Unbounded_String; + + Gnatls_Src_Cache : Unbounded_String; + Gnatls_Obj_Cache : Unbounded_String; + + F : File_Descriptor; + Len : Positive; + File_Name : aliased String := Name & ASCII.NUL; + + begin + + -- Read the size of the file + F := Open_Read (File_Name'Address, Text); + + -- Project file not found + if F /= Invalid_FD then + Len := Positive (File_Length (F)); + + declare + Buffer : String (1 .. Len); + Index : Positive := Buffer'First; + Last : Positive; + begin + Len := Read (F, Buffer'Address, Len); + Close (F); + + -- First, look for Build_Dir, since all the source and object + -- path are relative to it. + + while Index <= Buffer'Last loop + + -- find the end of line + + Last := Index; + while Last <= Buffer'Last + and then Buffer (Last) /= ASCII.LF + and then Buffer (Last) /= ASCII.CR + loop + Last := Last + 1; + end loop; + + if Index <= Buffer'Last - 9 + and then Buffer (Index .. Index + 9) = "build_dir=" + then + Index := Index + 10; + while Index <= Last + and then (Buffer (Index) = ' ' + or else Buffer (Index) = ASCII.HT) + loop + Index := Index + 1; + end loop; + + Build_Dir := + To_Unbounded_String (Buffer (Index .. Last - 1)); + if Buffer (Last - 1) /= Dir_Sep then + Append (Build_Dir, Dir_Sep); + end if; + end if; + + Index := Last + 1; + + -- In case we had a ASCII.CR/ASCII.LF end of line, skip the + -- remaining symbol + + if Index <= Buffer'Last + and then Buffer (Index) = ASCII.LF + then + Index := Index + 1; + end if; + end loop; + + -- Now parse the source and object paths + + Index := Buffer'First; + while Index <= Buffer'Last loop + + -- find the end of line + + Last := Index; + while Last <= Buffer'Last + and then Buffer (Last) /= ASCII.LF + and then Buffer (Last) /= ASCII.CR + loop + Last := Last + 1; + end loop; + + if Index <= Buffer'Last - 7 + and then Buffer (Index .. Index + 7) = "src_dir=" + then + declare + S : String := Ada.Strings.Fixed.Trim + (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both); + begin + -- A relative directory ? + if S (S'First) /= Dir_Sep then + Append (Src_Dir, Build_Dir); + end if; + + if S (S'Last) = Dir_Sep then + Append (Src_Dir, S & " "); + else + Append (Src_Dir, S & Dir_Sep & " "); + end if; + end; + + elsif Index <= Buffer'Last - 7 + and then Buffer (Index .. Index + 7) = "obj_dir=" + then + declare + S : String := Ada.Strings.Fixed.Trim + (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both); + begin + -- A relative directory ? + if S (S'First) /= Dir_Sep then + Append (Obj_Dir, Build_Dir); + end if; + + if S (S'Last) = Dir_Sep then + Append (Obj_Dir, S & " "); + else + Append (Obj_Dir, S & Dir_Sep & " "); + end if; + end; + end if; + + -- In case we had a ASCII.CR/ASCII.LF end of line, skip the + -- remaining symbol + Index := Last + 1; + + if Index <= Buffer'Last + and then Buffer (Index) = ASCII.LF + then + Index := Index + 1; + end if; + end loop; + end; + end if; + + Parse_Gnatls (Gnatls_Src_Cache, Gnatls_Obj_Cache); + + Directories := new Project_File' + (Src_Dir_Length => Length (Src_Dir) + Length (Gnatls_Src_Cache), + Obj_Dir_Length => Length (Obj_Dir) + Length (Gnatls_Obj_Cache), + Src_Dir => To_String (Src_Dir & Gnatls_Src_Cache), + Obj_Dir => To_String (Obj_Dir & Gnatls_Obj_Cache), + Src_Dir_Index => 1, + Obj_Dir_Index => 1, + Last_Obj_Dir_Start => 0); + end Create_Project_File; + + --------------------- + -- Current_Obj_Dir -- + --------------------- + + function Current_Obj_Dir return String is + begin + return Directories.Obj_Dir (Directories.Last_Obj_Dir_Start + .. Directories.Obj_Dir_Index - 2); + end Current_Obj_Dir; + + -------------- + -- Dir_Name -- + -------------- + + function Dir_Name (File : String; Base : String := "") return String is + begin + for J in reverse File'Range loop + if File (J) = '/' or else File (J) = Dir_Sep then + + -- Is this an absolute directory ? + if File (File'First) = '/' + or else File (File'First) = Dir_Sep + then + return File (File'First .. J); + + -- Else do we know the base directory ? + elsif Base /= "" then + return Base & File (File'First .. J); + + else + declare + Max_Path : Integer; + pragma Import (C, Max_Path, "max_path_len"); + + Base2 : Dir_Name_Str (1 .. Max_Path); + Last : Natural; + begin + Get_Current_Dir (Base2, Last); + return Base2 (Base2'First .. Last) & File (File'First .. J); + end; + end if; + end if; + end loop; + return ""; + end Dir_Name; + + ------------------- + -- Find_ALI_File -- + ------------------- + + function Find_ALI_File (Short_Name : String) return String is + use type Ada.Strings.Unbounded.String_Access; + Old_Obj_Dir : constant Integer := Directories.Obj_Dir_Index; + + begin + Reset_Obj_Dir; + + loop + declare + Obj_Dir : String := Next_Obj_Dir; + begin + exit when Obj_Dir'Length = 0; + if GNAT.IO_Aux.File_Exists (Obj_Dir & Short_Name) then + Directories.Obj_Dir_Index := Old_Obj_Dir; + return Obj_Dir; + end if; + end; + end loop; + + -- Finally look in the standard directories + + Directories.Obj_Dir_Index := Old_Obj_Dir; + return ""; + end Find_ALI_File; + + ---------------------- + -- Find_Source_File -- + ---------------------- + + function Find_Source_File (Short_Name : String) return String is + use type Ada.Strings.Unbounded.String_Access; + + begin + Reset_Src_Dir; + loop + declare + Src_Dir : String := Next_Src_Dir; + begin + exit when Src_Dir'Length = 0; + + if GNAT.IO_Aux.File_Exists (Src_Dir & Short_Name) then + return Src_Dir; + end if; + end; + end loop; + + -- Finally look in the standard directories + + return ""; + end Find_Source_File; + + ---------------- + -- First_Body -- + ---------------- + + function First_Body (Decl : Declaration_Reference) return Reference is + begin + return Decl.Body_Ref; + end First_Body; + + ----------------------- + -- First_Declaration -- + ----------------------- + + function First_Declaration return Declaration_Reference is + begin + return Entities.Table; + end First_Declaration; + + ----------------- + -- First_Modif -- + ----------------- + + function First_Modif (Decl : Declaration_Reference) return Reference is + begin + return Decl.Modif_Ref; + end First_Modif; + + --------------------- + -- First_Reference -- + --------------------- + + function First_Reference (Decl : Declaration_Reference) return Reference is + begin + return Decl.Ref_Ref; + end First_Reference; + + ---------------- + -- Get_Column -- + ---------------- + + function Get_Column (Decl : Declaration_Reference) return String is + begin + return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Column), + Ada.Strings.Left); + end Get_Column; + + function Get_Column (Ref : Reference) return String is + begin + return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Column), + Ada.Strings.Left); + end Get_Column; + + --------------------- + -- Get_Declaration -- + --------------------- + + function Get_Declaration + (File_Ref : File_Reference; + Line : Natural; + Column : Natural) + return Declaration_Reference + is + The_Entities : Declaration_Reference := Entities.Table; + begin + while The_Entities /= null loop + if The_Entities.Decl.Line = Line + and then The_Entities.Decl.Column = Column + and then The_Entities.Decl.File = File_Ref + then + return The_Entities; + else + The_Entities := The_Entities.Next; + end if; + end loop; + + return Empty_Declaration; + end Get_Declaration; + + ---------------------- + -- Get_Emit_Warning -- + ---------------------- + + function Get_Emit_Warning (File : File_Reference) return Boolean is + begin + return File.Emit_Warning; + end Get_Emit_Warning; + + -------------- + -- Get_File -- + -------------- + + function Get_File + (Decl : Declaration_Reference; + With_Dir : Boolean := False) + return String + is + begin + return Get_File (Decl.Decl.File, With_Dir); + end Get_File; + + function Get_File + (Ref : Reference; + With_Dir : Boolean := False) + return String + is + begin + return Get_File (Ref.File, With_Dir); + end Get_File; + + function Get_File + (File : File_Reference; + With_Dir : in Boolean := False; + Strip : Natural := 0) + return String + is + function Internal_Strip (Full_Name : String) return String; + -- Internal function to process the Strip parameter + + -------------------- + -- Internal_Strip -- + -------------------- + + function Internal_Strip (Full_Name : String) return String is + Unit_End, Extension_Start : Natural; + S : Natural := Strip; + begin + if Strip = 0 then + return Full_Name; + end if; + + -- Isolate the file extension + + Extension_Start := Full_Name'Last; + while Extension_Start >= Full_Name'First + and then Full_Name (Extension_Start) /= '.' + loop + Extension_Start := Extension_Start - 1; + end loop; + + -- Strip the right number of subunit_names + + Unit_End := Extension_Start - 1; + while Unit_End >= Full_Name'First + and then S > 0 + loop + if Full_Name (Unit_End) = '-' then + S := S - 1; + end if; + Unit_End := Unit_End - 1; + end loop; + + if Unit_End < Full_Name'First then + return ""; + else + return Full_Name (Full_Name'First .. Unit_End) + & Full_Name (Extension_Start .. Full_Name'Last); + end if; + end Internal_Strip; + + begin + -- If we do not want the full path name + + if not With_Dir then + return Internal_Strip (File.File); + end if; + + if File.Dir = null then + + if Ada.Strings.Fixed.Tail (File.File, 3) = "ali" then + File.Dir := new String'(Find_ALI_File (File.File)); + else + File.Dir := new String'(Find_Source_File (File.File)); + end if; + end if; + + return Internal_Strip (File.Dir.all & File.File); + end Get_File; + + ------------------ + -- Get_File_Ref -- + ------------------ + + function Get_File_Ref (Ref : Reference) return File_Reference is + begin + return Ref.File; + end Get_File_Ref; + + ----------------------- + -- Get_Gnatchop_File -- + ----------------------- + + function Get_Gnatchop_File + (File : File_Reference; With_Dir : Boolean := False) return String is + begin + if File.Gnatchop_File.all = "" then + return Get_File (File, With_Dir); + else + return File.Gnatchop_File.all; + end if; + end Get_Gnatchop_File; + + ----------------------- + -- Get_Gnatchop_File -- + ----------------------- + + function Get_Gnatchop_File + (Ref : Reference; With_Dir : Boolean := False) return String is + begin + return Get_Gnatchop_File (Ref.File, With_Dir); + end Get_Gnatchop_File; + + ----------------------- + -- Get_Gnatchop_File -- + ----------------------- + + function Get_Gnatchop_File + (Decl : Declaration_Reference; With_Dir : Boolean := False) return String + is + begin + return Get_Gnatchop_File (Decl.Decl.File, With_Dir); + end Get_Gnatchop_File; + + -------------- + -- Get_Line -- + -------------- + + function Get_Line (Decl : Declaration_Reference) return String is + begin + return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Line), + Ada.Strings.Left); + end Get_Line; + + function Get_Line (Ref : Reference) return String is + begin + return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Line), + Ada.Strings.Left); + end Get_Line; + + ---------------- + -- Get_Parent -- + ---------------- + + function Get_Parent + (Decl : Declaration_Reference) + return Declaration_Reference is + begin + return Decl.Par_Symbol; + end Get_Parent; + + --------------------- + -- Get_Source_Line -- + --------------------- + + function Get_Source_Line (Ref : Reference) return String is + begin + return To_String (Ref.Source_Line); + end Get_Source_Line; + + function Get_Source_Line (Decl : Declaration_Reference) return String is + begin + return To_String (Decl.Decl.Source_Line); + end Get_Source_Line; + + ---------------- + -- Get_Symbol -- + ---------------- + + function Get_Symbol (Decl : Declaration_Reference) return String is + begin + return Decl.Symbol; + end Get_Symbol; + + -------------- + -- Get_Type -- + -------------- + + function Get_Type (Decl : Declaration_Reference) return Character is + begin + return Decl.Decl_Type; + end Get_Type; + + ----------------------- + -- Grep_Source_Files -- + ----------------------- + + procedure Grep_Source_Files is + Decl : Declaration_Reference := First_Declaration; + + type Simple_Ref; + type Simple_Ref_Access is access Simple_Ref; + type Simple_Ref is + record + Ref : Reference; + Next : Simple_Ref_Access; + end record; + List : Simple_Ref_Access := null; + -- This structure is used to speed up the parsing of Ada sources: + -- Every reference found by parsing the .ali files is inserted in this + -- list, sorted by filename and line numbers. + -- This allows use not to parse a same ada file multiple times + + procedure Free is new Unchecked_Deallocation + (Simple_Ref, Simple_Ref_Access); + -- Clear an element of the list + + procedure Grep_List; + -- For each reference in the list, parse the file and find the + -- source line + + procedure Insert_In_Order (Ref : Reference); + -- Insert a new reference in the list, ordered by line numbers + + procedure Insert_List_Ref (First_Ref : Reference); + -- Process a list of references + + --------------- + -- Grep_List -- + --------------- + + procedure Grep_List is + Line : String (1 .. 1024); + Last : Natural; + File : Ada.Text_IO.File_Type; + Line_Number : Natural; + Pos : Natural; + Save_List : Simple_Ref_Access := List; + Current_File : File_Reference; + + begin + while List /= null loop + + -- Makes sure we can find and read the file + + Current_File := List.Ref.File; + Line_Number := 0; + + begin + Ada.Text_IO.Open (File, + Ada.Text_IO.In_File, + Get_File (List.Ref, True)); + + -- Read the file and find every relevant lines + + while List /= null + and then List.Ref.File = Current_File + and then not Ada.Text_IO.End_Of_File (File) + loop + Ada.Text_IO.Get_Line (File, Line, Last); + Line_Number := Line_Number + 1; + + while List /= null + and then Line_Number = List.Ref.Line + loop + + -- Skip the leading blanks on the line + + Pos := 1; + while Line (Pos) = ' ' + or else Line (Pos) = ASCII.HT + loop + Pos := Pos + 1; + end loop; + + List.Ref.Source_Line := + To_Unbounded_String (Line (Pos .. Last)); + + -- Find the next element in the list + + List := List.Next; + end loop; + + end loop; + + Ada.Text_IO.Close (File); + + -- If the Current_File was not found, just skip it + + exception + when Ada.IO_Exceptions.Name_Error => + null; + end; + + -- If the line or the file were not found + + while List /= null + and then List.Ref.File = Current_File + loop + List := List.Next; + end loop; + + end loop; + + -- Clear the list + + while Save_List /= null loop + List := Save_List; + Save_List := Save_List.Next; + Free (List); + end loop; + end Grep_List; + + --------------------- + -- Insert_In_Order -- + --------------------- + + procedure Insert_In_Order (Ref : Reference) is + Iter : Simple_Ref_Access := List; + Prev : Simple_Ref_Access := null; + + begin + while Iter /= null loop + + -- If we have found the file, sort by lines + + if Iter.Ref.File = Ref.File then + + while Iter /= null + and then Iter.Ref.File = Ref.File + loop + if Iter.Ref.Line > Ref.Line then + + if Iter = List then + List := new Simple_Ref'(Ref, List); + else + Prev.Next := new Simple_Ref'(Ref, Iter); + end if; + return; + end if; + + Prev := Iter; + Iter := Iter.Next; + end loop; + + if Iter = List then + List := new Simple_Ref'(Ref, List); + else + Prev.Next := new Simple_Ref'(Ref, Iter); + end if; + return; + end if; + + Prev := Iter; + Iter := Iter.Next; + end loop; + + -- The file was not already in the list, insert it + + List := new Simple_Ref'(Ref, List); + end Insert_In_Order; + + --------------------- + -- Insert_List_Ref -- + --------------------- + + procedure Insert_List_Ref (First_Ref : Reference) is + Ref : Reference := First_Ref; + + begin + while Ref /= Empty_Reference loop + Insert_In_Order (Ref); + Ref := Next (Ref); + end loop; + end Insert_List_Ref; + + -- Start of processing for Grep_Source_Files + + begin + while Decl /= Empty_Declaration loop + Insert_In_Order (Decl.Decl'Access); + Insert_List_Ref (First_Body (Decl)); + Insert_List_Ref (First_Reference (Decl)); + Insert_List_Ref (First_Modif (Decl)); + Decl := Next (Decl); + end loop; + + Grep_List; + end Grep_Source_Files; + + ----------------------- + -- Longest_File_Name -- + ----------------------- + + function Longest_File_Name return Natural is + begin + return Files.Longest_Name; + end Longest_File_Name; + + ----------- + -- Match -- + ----------- + + function Match + (File : File_Reference; + Line : Natural; + Column : Natural) + return Boolean + is + Ref : Ref_In_File_Ptr := File.Lines; + + begin + while Ref /= null loop + if (Ref.Line = 0 or else Ref.Line = Line) + and then (Ref.Column = 0 or else Ref.Column = Column) + then + return True; + end if; + + Ref := Ref.Next; + end loop; + + return False; + end Match; + + ----------- + -- Match -- + ----------- + + function Match (Decl : Declaration_Reference) return Boolean is + begin + return Decl.Match; + end Match; + + ---------- + -- Next -- + ---------- + + function Next (Decl : Declaration_Reference) return Declaration_Reference is + begin + return Decl.Next; + end Next; + + ---------- + -- Next -- + ---------- + + function Next (Ref : Reference) return Reference is + begin + return Ref.Next; + end Next; + + ------------------ + -- Next_Obj_Dir -- + ------------------ + + function Next_Obj_Dir return String is + First : Integer := Directories.Obj_Dir_Index; + Last : Integer := Directories.Obj_Dir_Index; + + begin + if Last > Directories.Obj_Dir_Length then + return String'(1 .. 0 => ' '); + end if; + + while Directories.Obj_Dir (Last) /= ' ' loop + Last := Last + 1; + end loop; + + Directories.Obj_Dir_Index := Last + 1; + Directories.Last_Obj_Dir_Start := First; + return Directories.Obj_Dir (First .. Last - 1); + end Next_Obj_Dir; + + ------------------ + -- Next_Src_Dir -- + ------------------ + + function Next_Src_Dir return String is + First : Integer := Directories.Src_Dir_Index; + Last : Integer := Directories.Src_Dir_Index; + + begin + if Last > Directories.Src_Dir_Length then + return String'(1 .. 0 => ' '); + end if; + + while Directories.Src_Dir (Last) /= ' ' loop + Last := Last + 1; + end loop; + + Directories.Src_Dir_Index := Last + 1; + return Directories.Src_Dir (First .. Last - 1); + end Next_Src_Dir; + + ------------------------- + -- Next_Unvisited_File -- + ------------------------- + + function Next_Unvisited_File return File_Reference is + The_Files : File_Reference := Files.Table; + + begin + while The_Files /= null loop + if not The_Files.Visited then + The_Files.Visited := True; + return The_Files; + end if; + + The_Files := The_Files.Next; + end loop; + + return Empty_File; + end Next_Unvisited_File; + + ------------------ + -- Parse_Gnatls -- + ------------------ + + procedure Parse_Gnatls + (Gnatls_Src_Cache : out Ada.Strings.Unbounded.Unbounded_String; + Gnatls_Obj_Cache : out Ada.Strings.Unbounded.Unbounded_String) + is + begin + Osint.Add_Default_Search_Dirs; + + for J in 1 .. Osint.Nb_Dir_In_Src_Search_Path loop + if Osint.Dir_In_Src_Search_Path (J)'Length = 0 then + Ada.Strings.Unbounded.Append (Gnatls_Src_Cache, "./" & ' '); + else + Ada.Strings.Unbounded.Append + (Gnatls_Src_Cache, Osint.Dir_In_Src_Search_Path (J).all & ' '); + end if; + end loop; + + for J in 1 .. Osint.Nb_Dir_In_Obj_Search_Path loop + if Osint.Dir_In_Obj_Search_Path (J)'Length = 0 then + Ada.Strings.Unbounded.Append (Gnatls_Obj_Cache, "./" & ' '); + else + Ada.Strings.Unbounded.Append + (Gnatls_Obj_Cache, Osint.Dir_In_Obj_Search_Path (J).all & ' '); + end if; + end loop; + end Parse_Gnatls; + + ------------------- + -- Reset_Obj_Dir -- + ------------------- + + procedure Reset_Obj_Dir is + begin + Directories.Obj_Dir_Index := 1; + end Reset_Obj_Dir; + + ------------------- + -- Reset_Src_Dir -- + ------------------- + + procedure Reset_Src_Dir is + begin + Directories.Src_Dir_Index := 1; + end Reset_Src_Dir; + + ----------------------- + -- Set_Default_Match -- + ----------------------- + + procedure Set_Default_Match (Value : Boolean) is + begin + Default_Match := Value; + end Set_Default_Match; + + ------------------- + -- Set_Directory -- + ------------------- + + procedure Set_Directory + (File : in File_Reference; + Dir : in String) + is + begin + File.Dir := new String'(Dir); + end Set_Directory; + + ------------------- + -- Set_Unvisited -- + ------------------- + + procedure Set_Unvisited (File_Ref : in File_Reference) is + The_Files : File_Reference := Files.Table; + + begin + while The_Files /= null loop + if The_Files = File_Ref then + The_Files.Visited := False; + return; + end if; + + The_Files := The_Files.Next; + end loop; + end Set_Unvisited; + +end Xr_Tabls; diff --git a/gcc/ada/xr_tabls.ads b/gcc/ada/xr_tabls.ads new file mode 100644 index 00000000000..960b35def8e --- /dev/null +++ b/gcc/ada/xr_tabls.ads @@ -0,0 +1,384 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- X R _ T A B L S -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.18 $ +-- -- +-- Copyright (C) 1998-2000 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Ada.Strings.Unbounded; + +package Xr_Tabls is + + ------------------- + -- Project files -- + ------------------- + + function ALI_File_Name (Ada_File_Name : String) return String; + -- Returns the ali file name corresponding to Ada_File_Name, using the + -- information provided in gnat.adc if it exists + + procedure Create_Project_File + (Name : String); + -- Open and parse a new project file + -- If the file Name could not be open or is not a valid project file + -- then a project file associated with the standard default directories + -- is returned + + function Find_ALI_File (Short_Name : String) return String; + -- Returns the directory name for the file Short_Name + -- takes into account the obj_dir lines in the project file, + -- and the default paths for Gnat + + function Find_Source_File (Short_Name : String) return String; + -- Returns the directory name for the file Short_Name + -- takes into account the src_dir lines in the project file, + -- and the default paths for Gnat + + function Next_Src_Dir return String; + -- Returns the next directory to visit to find related source files + -- If there are no more such directory, Length = 0 + + function Next_Obj_Dir return String; + -- Returns the next directory to visit to find related ali files + -- If there are no more such directory, Length = 0 + + function Current_Obj_Dir return String; + -- Returns the obj_dir which was returned by the last Next_Obj_Dir call + + procedure Parse_Gnatls + (Gnatls_Src_Cache : out Ada.Strings.Unbounded.Unbounded_String; + Gnatls_Obj_Cache : out Ada.Strings.Unbounded.Unbounded_String); + -- Parse the output of Gnatls, to find the standard + -- directories for source files + + procedure Reset_Src_Dir; + -- Reset the iterator for Src_Dir + + procedure Reset_Obj_Dir; + -- Reset the iterator for Obj_Dir + + ------------ + -- Tables -- + ------------ + + type Declaration_Reference is private; + Empty_Declaration : constant Declaration_Reference; + + type File_Reference is private; + Empty_File : constant File_Reference; + + type Reference is private; + Empty_Reference : constant Reference; + + type File_Table is limited private; + type Entity_Table is limited private; + + function Add_Declaration + (File_Ref : File_Reference; + Symbol : String; + Line : Natural; + Column : Natural; + Decl_Type : Character) + return Declaration_Reference; + -- Add a new declaration in the table and return the index to it. + -- Decl_Type is the type of the entity + + procedure Add_Parent + (Declaration : in out Declaration_Reference; + Symbol : String; + Line : Natural; + Column : Natural; + File_Ref : File_Reference); + -- The parent declaration (Symbol in file File_Ref at position Line and + -- Column) information is added to Declaration. + + procedure Add_File + (File_Name : String; + File_Existed : out Boolean; + Ref : out File_Reference; + Visited : Boolean := True; + Emit_Warning : Boolean := False; + Gnatchop_File : String := ""; + Gnatchop_Offset : Integer := 0); + -- Add a new reference to a file in the table. Ref is used to return + -- the index in the table where this file is stored On exit, + -- File_Existed is True if the file was already in the table Visited is + -- the value which will be used in the table (if True, the file will + -- not be returned by Next_Unvisited_File). If Emit_Warning is True and + -- the ali file does not exist or does not have cross-referencing + -- informations, then a warning will be emitted. + -- Gnatchop_File is the name of the file that File_Name was extracted from + -- through a call to "gnatchop -r" (with pragma Source_Reference). + -- Gnatchop_Offset should be the index of the first line of File_Name + -- withing Gnatchop_File. + + procedure Add_Line + (File : File_Reference; + Line : Natural; + Column : Natural); + -- Add a new reference in a file, which the user has provided + -- on the command line. This is used for a optimized matching + -- algorithm. + + procedure Add_Reference + (Declaration : Declaration_Reference; + File_Ref : File_Reference; + Line : Natural; + Column : Natural; + Ref_Type : Character); + -- Add a new reference (Ref_Type = 'r'), body (Ref_Type = 'b') or + -- modification (Ref_Type = 'm') to an entity + + type Compare_Result is (LessThan, Equal, GreaterThan); + function Compare (Ref1, Ref2 : Reference) return Compare_Result; + function Compare + (Decl1 : Declaration_Reference; + File2 : File_Reference; + Line2 : Integer; + Col2 : Integer; + Symb2 : String) + return Compare_Result; + -- Compare two references + + function First_Body (Decl : Declaration_Reference) return Reference; + function First_Declaration return Declaration_Reference; + function First_Modif (Decl : Declaration_Reference) return Reference; + function First_Reference (Decl : Declaration_Reference) return Reference; + -- Initialize the iterators + + function Get_Column (Decl : Declaration_Reference) return String; + function Get_Column (Ref : Reference) return String; + + function Get_Declaration + (File_Ref : File_Reference; + Line : Natural; + Column : Natural) + return Declaration_Reference; + -- Returns reference to the declaration found in file File_Ref at the + -- given Line and Column + + function Get_Parent + (Decl : Declaration_Reference) + return Declaration_Reference; + -- Returns reference to Decl's parent declaration + + function Get_Emit_Warning (File : File_Reference) return Boolean; + -- Returns the Emit_Warning field of the structure + + function Get_Gnatchop_File + (File : File_Reference; With_Dir : Boolean := False) return String; + function Get_Gnatchop_File + (Ref : Reference; With_Dir : Boolean := False) return String; + function Get_Gnatchop_File + (Decl : Declaration_Reference; With_Dir : Boolean := False) return String; + -- Return the name of the file that File was extracted from through a + -- call to "gnatchop -r". + -- The file name for File is returned if File wasn't extracted from such a + -- file. The directory will be given only if With_Dir is True. + + + function Get_File + (Decl : Declaration_Reference; + With_Dir : Boolean := False) + return String; + -- Extract column number or file name from reference + + function Get_File + (Ref : Reference; + With_Dir : Boolean := False) + return String; + pragma Inline (Get_File); + + function Get_File + (File : File_Reference; + With_Dir : Boolean := False; + Strip : Natural := 0) + return String; + -- Returns the file name (and its directory if With_Dir is True or + -- the user as used the -f switch on the command line. + -- If Strip is not 0, then the last Strip-th "-..." substrings are + -- removed first. For instance, with Strip=2, a file name + -- "parent-child1-child2-child3.ali" would be returned as + -- "parent-child1.ali". This is used when looking for the ALI file to use + -- for a package, since for separates with have to use the parent's ALI. + -- + -- "" is returned if there is no such parent unit + + function Get_File_Ref (Ref : Reference) return File_Reference; + function Get_Line (Decl : Declaration_Reference) return String; + function Get_Line (Ref : Reference) return String; + function Get_Symbol (Decl : Declaration_Reference) return String; + function Get_Type (Decl : Declaration_Reference) return Character; + -- Functions that return the content of a declaration + + function Get_Source_Line (Ref : Reference) return String; + function Get_Source_Line (Decl : Declaration_Reference) return String; + -- Return the source line associated with the reference + + procedure Grep_Source_Files; + -- Parse all the source files which have at least one reference, and + -- grep the appropriate lines so that we'll be able to display them. + -- This function should be called once all the .ali files have been + -- parsed, and only if the appropriate user switch has been used. + + function Longest_File_Name return Natural; + -- Returns the longest file name found + + function Match (Decl : Declaration_Reference) return Boolean; + -- Return True if the declaration matches + + function Match + (File : File_Reference; + Line : Natural; + Column : Natural) + return Boolean; + -- Returns True if File:Line:Column was given on the command line + -- by the user + + function Next (Decl : Declaration_Reference) return Declaration_Reference; + function Next (Ref : Reference) return Reference; + -- Returns the next declaration, or Empty_Declaration + + function Next_Unvisited_File return File_Reference; + -- Returns the next unvisited library file in the list + -- If there is no more unvisited file, return Empty_File + + procedure Set_Default_Match (Value : Boolean); + -- Set the default value for match in declarations. + -- This is used so that if no file was provided in the + -- command line, then every file match + + procedure Set_Directory + (File : File_Reference; + Dir : String); + -- Set the directory for a file + + procedure Set_Unvisited (File_Ref : in File_Reference); + -- Set File_Ref as unvisited. So Next_Unvisited_File will return it. + + +private + type Project_File (Src_Dir_Length, Obj_Dir_Length : Natural) is record + Src_Dir : String (1 .. Src_Dir_Length); + Src_Dir_Index : Integer; + + Obj_Dir : String (1 .. Obj_Dir_Length); + Obj_Dir_Index : Integer; + Last_Obj_Dir_Start : Natural; + end record; + + type Project_File_Ptr is access all Project_File; + -- This is actually a list of all the directories to be searched, + -- either for source files or for library files + + type String_Access is access all String; + + type Ref_In_File; + type Ref_In_File_Ptr is access all Ref_In_File; + + type Ref_In_File is record + Line : Natural; + Column : Natural; + Next : Ref_In_File_Ptr := null; + end record; + + type File_Record; + type File_Reference is access all File_Record; + + Empty_File : constant File_Reference := null; + + type File_Record (File_Length : Natural) is record + File : String (1 .. File_Length); + Dir : String_Access := null; + Lines : Ref_In_File_Ptr := null; + Visited : Boolean := False; + Emit_Warning : Boolean := False; + Gnatchop_File : String_Access := null; + Gnatchop_Offset : Integer := 0; + Next : File_Reference := null; + end record; + -- Holds a reference to a source file, that was referenced in at least one + -- ALI file. + -- Gnatchop_File will contain the name of the file that File was extracted + -- From. Gnatchop_Offset contains the index of the first line of File + -- within Gnatchop_File. These two fields are used to properly support + -- gnatchop files and pragma Source_Reference. + + + type Reference_Record; + type Reference is access all Reference_Record; + + Empty_Reference : constant Reference := null; + + type Reference_Record is record + File : File_Reference; + Line : Natural; + Column : Natural; + Source_Line : Ada.Strings.Unbounded.Unbounded_String; + Next : Reference := null; + end record; + -- File is a reference to the Ada source file + -- Source_Line is the Line as it appears in the source file. This + -- field is only used when the switch is set on the command line + + type Declaration_Record; + type Declaration_Reference is access all Declaration_Record; + + Empty_Declaration : constant Declaration_Reference := null; + + type Declaration_Record (Symbol_Length : Natural) is record + Symbol : String (1 .. Symbol_Length); + Decl : aliased Reference_Record; + Decl_Type : Character; + Body_Ref : Reference := null; + Ref_Ref : Reference := null; + Modif_Ref : Reference := null; + Match : Boolean := False; + Par_Symbol : Declaration_Reference := null; + Next : Declaration_Reference := null; + end record; + + type File_Table is record + Table : File_Reference := null; + Longest_Name : Natural := 0; + end record; + + type Entity_Table is record + Table : Declaration_Reference := null; + end record; + + pragma Inline (First_Body); + pragma Inline (First_Declaration); + pragma Inline (First_Modif); + pragma Inline (First_Reference); + pragma Inline (Get_Column); + pragma Inline (Get_Emit_Warning); + pragma Inline (Get_File); + pragma Inline (Get_File_Ref); + pragma Inline (Get_Line); + pragma Inline (Get_Symbol); + pragma Inline (Get_Type); + pragma Inline (Longest_File_Name); + pragma Inline (Next); + +end Xr_Tabls; diff --git a/gcc/ada/xref_lib.adb b/gcc/ada/xref_lib.adb new file mode 100644 index 00000000000..d3dfe37859a --- /dev/null +++ b/gcc/ada/xref_lib.adb @@ -0,0 +1,1676 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- X R E F _ L I B -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.55 $ +-- -- +-- Copyright (C) 1998-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Ada.Strings.Fixed; use Ada.Strings.Fixed; +with GNAT.Command_Line; use GNAT.Command_Line; +with GNAT.IO_Aux; use GNAT.IO_Aux; +with Osint; +with Output; use Output; +with Types; use Types; +with Unchecked_Deallocation; + +package body Xref_Lib is + + Type_Position : constant := 50; + -- Column for label identifying type of entity + + --------------------- + -- Local Variables -- + --------------------- + + D : constant Character := 'D'; + X : constant Character := 'X'; + W : constant Character := 'W'; + Dot : constant Character := '.'; + + Pipe : constant Character := '|'; + -- First character on xref lines in the .ali file + + EOF : constant Character := ASCII.SUB; + -- Special character to signal end of file. Not required in input file, + -- but should be properly treated if present. See also Read_File. + + No_Xref_Information : exception; + -- Exception raised when there is no cross-referencing information in + -- the .ali files + + subtype File_Offset is Natural; + + function End_Of_Line_Index (File : ALI_File) return Integer; + -- Returns the index of the last character of the current_line + + procedure Read_File + (FD : File_Descriptor; + Contents : out String_Access; + Success : out Boolean); + -- Reads file associated with FS into the newly allocated + -- string Contents. An EOF character will be added to the + -- returned Contents to simplify parsing. + -- [VMS] Success is true iff the number of bytes read is less than or + -- equal to the file size. + -- [Other] Success is true iff the number of bytes read is equal to + -- the file size. + + procedure Parse_EOL (Source : access String; Ptr : in out Positive); + -- On return Source (Ptr) is the first character of the next line + -- or EOF. Source.all must be terminated by EOF. + + procedure Parse_Identifier_Info + (Pattern : Search_Pattern; + File : in out ALI_File; + Local_Symbols : Boolean; + Der_Info : Boolean := False; + Type_Tree : Boolean := False; + Wide_Search : Boolean := True); + -- Output the file and the line where the identifier was referenced, + -- If Local_Symbols is False then only the publicly visible symbols + -- will be processed + + procedure Parse_Token + (Source : access String; + Ptr : in out Positive; + Token_Ptr : out Positive); + -- Skips any separators and stores the start of the token in Token_Ptr. + -- Then stores the position of the next separator in Ptr. + -- On return Source (Token_Ptr .. Ptr - 1) is the token. + -- Separators are space and ASCII.HT. + -- Parse_Token will never skip to the next line. + + procedure Parse_Number + (Source : access String; + Ptr : in out Positive; + Number : out Natural); + -- Skips any separators and parses Source upto the first character that + -- is not a decimal digit. Returns value of parsed digits or 0 if none. + + procedure Parse_X_Filename (File : in out ALI_File); + -- Reads and processes "X..." lines in the ALI file + -- and updates the File.X_File information. + + ---------------- + -- Add_Entity -- + ---------------- + + procedure Add_Entity + (Pattern : in out Search_Pattern; + Entity : String; + Glob : Boolean := False) + is + File_Start : Natural; + Line_Start : Natural; + Col_Start : Natural; + Line_Num : Natural := 0; + Col_Num : Natural := 0; + File_Ref : File_Reference := Empty_File; + File_Existed : Boolean; + Has_Pattern : Boolean := False; + + begin + -- Find the end of the first item in Entity (pattern or file?) + -- If there is no ':', we only have a pattern + + File_Start := Index (Entity, ":"); + if File_Start = 0 then + + -- If the regular expression is invalid, just consider it as a string + + begin + Pattern.Entity := Compile (Entity, Glob, False); + Pattern.Initialized := True; + + exception + when Error_In_Regexp => + + -- The basic idea is to insert a \ before every character + + declare + Tmp_Regexp : String (1 .. 2 * Entity'Length); + Index : Positive := 1; + + begin + for J in Entity'Range loop + Tmp_Regexp (Index) := '\'; + Tmp_Regexp (Index + 1) := Entity (J); + Index := Index + 2; + end loop; + + Pattern.Entity := Compile (Tmp_Regexp, True, False); + Pattern.Initialized := True; + end; + end; + + Set_Default_Match (True); + return; + end if; + + -- If there is a dot in the pattern, then it is a file name + + if (Glob and then + Index (Entity (Entity'First .. File_Start - 1), ".") /= 0) + or else + (not Glob + and then Index (Entity (Entity'First .. File_Start - 1), + "\.") /= 0) + then + Pattern.Entity := Compile (".*", False); + Pattern.Initialized := True; + File_Start := Entity'First; + + else + -- If the regular expression is invalid, + -- just consider it as a string + + begin + Pattern.Entity := + Compile (Entity (Entity'First .. File_Start - 1), Glob, False); + Pattern.Initialized := True; + + exception + when Error_In_Regexp => + + -- The basic idea is to insert a \ before every character + + declare + Tmp_Regexp : String (1 .. 2 * (File_Start - Entity'First)); + Index : Positive := 1; + + begin + for J in Entity'First .. File_Start - 1 loop + Tmp_Regexp (Index) := '\'; + Tmp_Regexp (Index + 1) := Entity (J); + Index := Index + 2; + end loop; + + Pattern.Entity := Compile (Tmp_Regexp, True, False); + Pattern.Initialized := True; + end; + end; + + File_Start := File_Start + 1; + Has_Pattern := True; + end if; + + -- Parse the file name + + Line_Start := Index (Entity (File_Start .. Entity'Last), ":"); + + -- Check if it was a disk:\directory item (for NT and OS/2) + + if File_Start = Line_Start - 1 + and then Line_Start < Entity'Last + and then Entity (Line_Start + 1) = '\' + then + Line_Start := Index (Entity (Line_Start + 1 .. Entity'Last), ":"); + end if; + + if Line_Start = 0 then + Line_Start := Entity'Length + 1; + + elsif Line_Start /= Entity'Last then + Col_Start := Index (Entity (Line_Start + 1 .. Entity'Last), ":"); + + if Col_Start = 0 then + Col_Start := Entity'Last + 1; + end if; + + if Col_Start > Line_Start + 1 then + begin + Line_Num := Natural'Value + (Entity (Line_Start + 1 .. Col_Start - 1)); + + exception + when Constraint_Error => + raise Invalid_Argument; + end; + end if; + + if Col_Start < Entity'Last then + begin + Col_Num := Natural'Value (Entity + (Col_Start + 1 .. Entity'Last)); + + exception + when Constraint_Error => raise Invalid_Argument; + end; + end if; + end if; + + Add_File (Entity (File_Start .. Line_Start - 1), + File_Existed, + File_Ref, + Visited => True); + Add_Line (File_Ref, Line_Num, Col_Num); + Add_File + (ALI_File_Name (Entity (File_Start .. Line_Start - 1)), + File_Existed, File_Ref, + Visited => False, + Emit_Warning => True); + end Add_Entity; + + -------------- + -- Add_File -- + -------------- + + procedure Add_File (File : String) is + File_Ref : File_Reference := Empty_File; + File_Existed : Boolean; + Iterator : Expansion_Iterator; + + procedure Add_File_Internal (File : String); + -- Do the actual addition of the file + + ----------------------- + -- Add_File_Internal -- + ----------------------- + + procedure Add_File_Internal (File : String) is + begin + -- Case where we have an ALI file, accept it even though this is + -- not official usage, since the intention is obvious + + if Tail (File, 4) = ".ali" then + Add_File + (File, + File_Existed, + File_Ref, + Visited => False, + Emit_Warning => True); + + -- Normal non-ali file case + + else + Add_File + (File, + File_Existed, + File_Ref, + Visited => True); + + Add_File + (ALI_File_Name (File), + File_Existed, + File_Ref, + Visited => False, + Emit_Warning => True); + end if; + end Add_File_Internal; + + -- Start of processing for Add_File + + begin + -- Check if we need to do the expansion + + if Ada.Strings.Fixed.Index (File, "*") /= 0 + or else Ada.Strings.Fixed.Index (File, "?") /= 0 + then + Start_Expansion (Iterator, File); + + loop + declare + S : constant String := Expansion (Iterator); + + begin + exit when S'Length = 0; + Add_File_Internal (S); + end; + end loop; + + else + Add_File_Internal (File); + end if; + end Add_File; + + ----------------------- + -- Current_Xref_File -- + ----------------------- + + function Current_Xref_File (File : ALI_File) return File_Reference is + begin + return File.X_File; + end Current_Xref_File; + + -------------------------- + -- Default_Project_File -- + -------------------------- + + function Default_Project_File + (Dir_Name : String) + return String + is + My_Dir : Dir_Type; + Dir_Ent : File_Name_String; + Last : Natural; + + begin + Open (My_Dir, Dir_Name); + + loop + Read (My_Dir, Dir_Ent, Last); + exit when Last = 0; + + if Tail (Dir_Ent (1 .. Last), 4) = ".adp" then + + -- The first project file found is the good one. + + Close (My_Dir); + return Dir_Ent (1 .. Last); + end if; + end loop; + + Close (My_Dir); + return String'(1 .. 0 => ' '); + + exception + when Directory_Error => return String'(1 .. 0 => ' '); + end Default_Project_File; + + ----------------------- + -- End_Of_Line_Index -- + ----------------------- + + function End_Of_Line_Index (File : ALI_File) return Integer is + Index : Integer := File.Current_Line; + begin + while Index <= File.Buffer'Last + and then File.Buffer (Index) /= ASCII.LF + loop + Index := Index + 1; + end loop; + + return Index; + end End_Of_Line_Index; + + --------------- + -- File_Name -- + --------------- + + function File_Name + (File : ALI_File; + Num : Positive) + return File_Reference + is + begin + return File.Dep.Table (Num); + end File_Name; + + -------------------- + -- Find_ALI_Files -- + -------------------- + + procedure Find_ALI_Files is + My_Dir : Rec_DIR; + Dir_Ent : File_Name_String; + Last : Natural; + File_Existed : Boolean; + File_Ref : File_Reference; + + function Open_Next_Dir return Boolean; + -- Tries to open the next object directory, and return False if + -- the directory cannot be opened. + + ------------------- + -- Open_Next_Dir -- + ------------------- + + function Open_Next_Dir return Boolean is + begin + -- Until we are able to open a new directory + + loop + declare + Obj_Dir : constant String := Next_Obj_Dir; + + begin + -- If there was no more Obj_Dir line + + if Obj_Dir'Length = 0 then + return False; + end if; + + Open (My_Dir.Dir, Obj_Dir); + exit; + + exception + -- Could not open the directory + + when Directory_Error => null; + end; + end loop; + + return True; + end Open_Next_Dir; + + -- Start of processing for Find_ALI_Files + + begin + if Open_Next_Dir then + loop + Read (My_Dir.Dir, Dir_Ent, Last); + + if Last = 0 then + Close (My_Dir.Dir); + + if not Open_Next_Dir then + return; + end if; + + elsif Last > 4 and then Dir_Ent (Last - 3 .. Last) = ".ali" then + Add_File (Dir_Ent (1 .. Last), File_Existed, File_Ref, + Visited => False); + Set_Directory (File_Ref, Current_Obj_Dir); + end if; + end loop; + end if; + end Find_ALI_Files; + + ------------------- + -- Get_Full_Type -- + ------------------- + + function Get_Full_Type (Abbrev : Character) return String is + begin + case Abbrev is + when 'A' => return "array type"; + when 'B' => return "boolean type"; + when 'C' => return "class-wide type"; + when 'D' => return "decimal type"; + when 'E' => return "enumeration type"; + when 'F' => return "float type"; + when 'I' => return "integer type"; + when 'M' => return "modular type"; + when 'O' => return "fixed type"; + when 'P' => return "access type"; + when 'R' => return "record type"; + when 'S' => return "string type"; + when 'T' => return "task type"; + when 'W' => return "protected type"; + + when 'a' => return "array type"; + when 'b' => return "boolean object"; + when 'c' => return "class-wide object"; + when 'd' => return "decimal object"; + when 'e' => return "enumeration object"; + when 'f' => return "float object"; + when 'i' => return "integer object"; + when 'm' => return "modular object"; + when 'o' => return "fixed object"; + when 'p' => return "access object"; + when 'r' => return "record object"; + when 's' => return "string object"; + when 't' => return "task object"; + when 'w' => return "protected object"; + + when 'K' => return "package"; + when 'k' => return "generic package"; + when 'L' => return "statement label"; + when 'l' => return "loop label"; + when 'N' => return "named number"; + when 'n' => return "enumeration literal"; + when 'q' => return "block label"; + when 'U' => return "procedure"; + when 'u' => return "generic procedure"; + when 'V' => return "function"; + when 'v' => return "generic function"; + when 'X' => return "exception"; + when 'Y' => return "entry"; + + -- The above should be the only possibilities, but for a + -- tool like this we don't want to bomb if we find something + -- else, so just return ??? when we have an unknown Abbrev value + + when others => + return "???"; + end case; + end Get_Full_Type; + + ----------- + -- Match -- + ----------- + + function Match + (Pattern : Search_Pattern; + Symbol : String) + return Boolean + is + begin + -- Get the entity name + + return Match (Symbol, Pattern.Entity); + end Match; + + ---------- + -- Open -- + ---------- + + procedure Open + (Name : String; + File : out ALI_File; + Dependencies : Boolean := False) + is + Name_0 : constant String := Name & ASCII.NUL; + Num_Dependencies : Natural := 0; + File_Existed : Boolean; + File_Ref : File_Reference; + FD : File_Descriptor; + Success : Boolean := False; + Ali : String_Access renames File.Buffer; + Token : Positive; + Ptr : Positive; + File_Start : Positive; + File_End : Positive; + Gnatchop_Offset : Integer; + Gnatchop_Name : Positive; + + begin + if File.Buffer /= null then + Free (File.Buffer); + end if; + + Init (File.Dep); + + FD := Open_Read (Name_0'Address, Binary); + + if FD = Invalid_FD then + raise No_Xref_Information; + end if; + + Read_File (FD, Ali, Success); + Close (FD); + + Ptr := Ali'First; + + -- Read all the lines possibly processing with-clauses and dependency + -- information and exit on finding the first Xref line. + -- A fall-through of the loop means that there is no xref information + -- which is an error condition. + + while Ali (Ptr) /= EOF loop + + if Ali (Ptr) = D then + -- Found dependency information. Format looks like: + -- D source-name time-stamp checksum [subunit-name] \ + -- [line:file-name] + + -- Skip the D and parse the filename + + Ptr := Ptr + 1; + Parse_Token (Ali, Ptr, Token); + File_Start := Token; + File_End := Ptr - 1; + + Num_Dependencies := Num_Dependencies + 1; + Set_Last (File.Dep, Num_Dependencies); + + Parse_Token (Ali, Ptr, Token); -- Skip time-stamp + Parse_Token (Ali, Ptr, Token); -- Skip checksum + Parse_Token (Ali, Ptr, Token); -- Read next entity on the line + + if not (Ali (Token) in '0' .. '9') then + Parse_Token (Ali, Ptr, Token); -- Was a subunit name + end if; + + -- Did we have a gnatchop-ed file with a pragma Source_Reference ? + Gnatchop_Offset := 0; + + if Ali (Token) in '0' .. '9' then + Gnatchop_Name := Token; + while Ali (Gnatchop_Name) /= ':' loop + Gnatchop_Name := Gnatchop_Name + 1; + end loop; + Gnatchop_Offset := + 2 - Natural'Value (Ali (Token .. Gnatchop_Name - 1)); + Token := Gnatchop_Name + 1; + end if; + + Add_File + (Ali (File_Start .. File_End), + File_Existed, + File.Dep.Table (Num_Dependencies), + Gnatchop_File => Ali (Token .. Ptr - 1), + Gnatchop_Offset => Gnatchop_Offset); + + elsif Dependencies and then Ali (Ptr) = W then + -- Found with-clause information. Format looks like: + -- "W debug%s debug.adb debug.ali" + + -- Skip the W and parse the .ali filename (3rd token) + + Parse_Token (Ali, Ptr, Token); + Parse_Token (Ali, Ptr, Token); + Parse_Token (Ali, Ptr, Token); + + Add_File + (Ali (Token .. Ptr - 1), + File_Existed, File_Ref, + Visited => False); + + elsif Ali (Ptr) = X then + -- Found a cross-referencing line - stop processing + + File.Current_Line := Ptr; + File.Xref_Line := Ptr; + return; + end if; + + Parse_EOL (Ali, Ptr); + end loop; + + raise No_Xref_Information; + end Open; + + --------------- + -- Parse_EOL -- + --------------- + + procedure Parse_EOL (Source : access String; Ptr : in out Positive) is + begin + -- Skip to end of line + + while Source (Ptr) /= ASCII.CR and then Source (Ptr) /= ASCII.LF + and then Source (Ptr) /= EOF + loop + Ptr := Ptr + 1; + end loop; + + if Source (Ptr) /= EOF then + Ptr := Ptr + 1; -- skip CR or LF + end if; + + -- Skip past CR/LF or LF/CR combination + + if (Source (Ptr) = ASCII.CR or else Source (Ptr) = ASCII.LF) + and then Source (Ptr) /= Source (Ptr - 1) + then + Ptr := Ptr + 1; + end if; + end Parse_EOL; + + --------------------------- + -- Parse_Identifier_Info -- + --------------------------- + + procedure Parse_Identifier_Info + (Pattern : Search_Pattern; + File : in out ALI_File; + Local_Symbols : Boolean; + Der_Info : Boolean := False; + Type_Tree : Boolean := False; + Wide_Search : Boolean := True) + is + Ptr : Positive renames File.Current_Line; + Ali : String_Access renames File.Buffer; + + E_Line : Natural; -- Line number of current entity + E_Col : Natural; -- Column number of current entity + E_Type : Character; -- Type of current entity + E_Name : Positive; -- Pointer to begin of entity name + E_Global : Boolean; -- True iff entity is global + + R_Line : Natural; -- Line number of current reference + R_Col : Natural; -- Column number of current reference + R_Type : Character; -- Type of current reference + + Decl_Ref : Declaration_Reference; + File_Ref : File_Reference := Current_Xref_File (File); + + function Get_Symbol_Name (Eun, Line, Col : Natural) return String; + -- Returns the symbol name for the entity defined at the specified + -- line and column in the dependent unit number Eun. For this we need + -- to parse the ali file again because the parent entity is not in + -- the declaration table if it did not match the search pattern. + + --------------------- + -- Get_Symbol_Name -- + --------------------- + + function Get_Symbol_Name (Eun, Line, Col : Natural) return String is + Ptr : Positive := 1; + E_Eun : Positive; -- Unit number of current entity + E_Line : Natural; -- Line number of current entity + E_Col : Natural; -- Column number of current entity + E_Name : Positive; -- Pointer to begin of entity name + E_Type : Character; -- Type of current entity + + procedure Skip_Line; + -- skip current line and continuation line + + procedure Skip_Line is + begin + loop + Parse_EOL (Ali, Ptr); + exit when Ali (Ptr) /= '.'; + end loop; + end Skip_Line; + + -- Start of processing for Get_Symbol_Name + + begin + -- Look for the X lines corresponding to unit Eun + + loop + if Ali (Ptr) = 'X' then + Ptr := Ptr + 1; + Parse_Number (Ali, Ptr, E_Eun); + exit when E_Eun = Eun; + end if; + + Skip_Line; + end loop; + + -- Here we are in the right Ali section, we now look for the entity + -- declared at position (Line, Col). + + loop + Parse_Number (Ali, Ptr, E_Line); + E_Type := Ali (Ptr); + Ptr := Ptr + 1; + Parse_Number (Ali, Ptr, E_Col); + Ptr := Ptr + 1; + + if Line = E_Line and then Col = E_Col then + Parse_Token (Ali, Ptr, E_Name); + return Ali (E_Name .. Ptr - 1); + end if; + + Skip_Line; + end loop; + + -- We were not able to find the symbol, this should not happend but + -- since we don't want to stop here we return a string of three + -- question marks as the symbol name. + + return "???"; + end Get_Symbol_Name; + + -- Start of processing for Parse_Identifier_Info + + begin + -- The identifier info looks like: + -- "38U9*Debug 12|36r6 36r19" + + -- Extract the line, column and entity name information + + Parse_Number (Ali, Ptr, E_Line); + + if Ali (Ptr) > ' ' then + E_Type := Ali (Ptr); + Ptr := Ptr + 1; + end if; + + Parse_Number (Ali, Ptr, E_Col); + + E_Global := False; + if Ali (Ptr) >= ' ' then + E_Global := (Ali (Ptr) = '*'); + Ptr := Ptr + 1; + end if; + + Parse_Token (Ali, Ptr, E_Name); + + -- Exit if the symbol does not match + -- or if we have a local symbol and we do not want it + + if (not Local_Symbols and not E_Global) + or else (Pattern.Initialized + and then not Match (Pattern, Ali (E_Name .. Ptr - 1))) + or else (E_Name >= Ptr) + then + -- Skip rest of this line and all continuation lines + + loop + Parse_EOL (Ali, Ptr); + exit when Ali (Ptr) /= '.'; + end loop; + return; + end if; + + -- Insert the declaration in the table + + Decl_Ref := Add_Declaration + (File.X_File, Ali (E_Name .. Ptr - 1), E_Line, E_Col, E_Type); + + if Ali (Ptr) = '<' then + + -- Here we have a type derivation information. The format is + -- <3|12I45> which means that the current entity is derived from the + -- type defined in unit number 3, line 12 column 45. The pipe and + -- unit number is optional. It is specified only if the parent type + -- is not defined in the current unit. + + Ptr := Ptr + 1; + + Parse_Derived_Info : declare + P_Line : Natural; -- parent entity line + P_Column : Natural; -- parent entity column + P_Type : Character; -- parent entity type + P_Eun : Positive; -- parent entity file number + + begin + Parse_Number (Ali, Ptr, P_Line); + + -- If we have a pipe then the first number was the unit number + + if Ali (Ptr) = '|' then + P_Eun := P_Line; + Ptr := Ptr + 1; + + -- Now we have the line number + + Parse_Number (Ali, Ptr, P_Line); + + else + -- We don't have a unit number specified, so we set P_Eun to + -- the current unit. + + for K in Dependencies_Tables.First .. Last (File.Dep) loop + P_Eun := K; + exit when File.Dep.Table (K) = File_Ref; + end loop; + end if; + + -- Then parse the type and column number + + P_Type := Ali (Ptr); + Ptr := Ptr + 1; + Parse_Number (Ali, Ptr, P_Column); + + -- Skip '>' + + Ptr := Ptr + 1; + + -- The derived info is needed only is the derived info mode is on + -- or if we want to output the type hierarchy + + if Der_Info or else Type_Tree then + Add_Parent + (Decl_Ref, + Get_Symbol_Name (P_Eun, P_Line, P_Column), + P_Line, + P_Column, + File.Dep.Table (P_Eun)); + end if; + + if Type_Tree then + Search_Parent_Tree : declare + Pattern : Search_Pattern; -- Parent type pattern + File_Pos_Backup : Positive; + + begin + Add_Entity + (Pattern, + Get_Symbol_Name (P_Eun, P_Line, P_Column) + & ':' & Get_Gnatchop_File (File.Dep.Table (P_Eun)) + & ':' & Get_Line (Get_Parent (Decl_Ref)) + & ':' & Get_Column (Get_Parent (Decl_Ref)), + False); + + -- No default match is needed to look for the parent type + -- since we are using the fully qualified symbol name: + -- symbol:file:line:column + + Set_Default_Match (False); + + -- The parent type is defined in the same unit as the + -- derived type. So we want to revisit the unit. + + File_Pos_Backup := File.Current_Line; + + if File.Dep.Table (P_Eun) = File_Ref then + + -- set file pointer at the start of the xref lines + + File.Current_Line := File.Xref_Line; + + Revisit_ALI_File : declare + File_Existed : Boolean; + File_Ref : File_Reference; + begin + Add_File + (ALI_File_Name (Get_File (File.Dep.Table (P_Eun))), + File_Existed, + File_Ref, + Visited => False); + Set_Unvisited (File_Ref); + end Revisit_ALI_File; + end if; + + Search (Pattern, + Local_Symbols, False, False, Der_Info, Type_Tree); + + File.Current_Line := File_Pos_Backup; + + -- in this mode there is no need to parse the remaining of + -- the lines. + + return; + end Search_Parent_Tree; + end if; + end Parse_Derived_Info; + end if; + + -- To find the body, we will have to parse the file too + + if Wide_Search then + declare + File_Existed : Boolean; + File_Ref : File_Reference; + File_Name : constant String := + Get_Gnatchop_File (File.X_File); + + begin + Add_File (ALI_File_Name (File_Name), + File_Existed, File_Ref, False); + end; + end if; + + -- Parse references to this entity. + -- Ptr points to next reference with leading blanks + + loop + -- Process references on current line + + while Ali (Ptr) = ' ' or Ali (Ptr) = ASCII.HT loop + + -- For every reference read the line, type and column, + -- optionally preceded by a file number and a pipe symbol. + + Parse_Number (Ali, Ptr, R_Line); + + if Ali (Ptr) = Pipe then + Ptr := Ptr + 1; + File_Ref := File_Name (File, R_Line); + + Parse_Number (Ali, Ptr, R_Line); + end if; + + if Ali (Ptr) > ' ' then + R_Type := Ali (Ptr); + Ptr := Ptr + 1; + end if; + + Parse_Number (Ali, Ptr, R_Col); + + -- Insert the reference or body in the table + + Add_Reference (Decl_Ref, File_Ref, R_Line, R_Col, R_Type); + + end loop; + + Parse_EOL (Ali, Ptr); + + -- Loop until new line is no continuation line + + exit when Ali (Ptr) /= '.'; + Ptr := Ptr + 1; + end loop; + end Parse_Identifier_Info; + + ------------------ + -- Parse_Number -- + ------------------ + + procedure Parse_Number + (Source : access String; + Ptr : in out Positive; + Number : out Natural) + is + begin + -- Skip separators + + while Source (Ptr) = ' ' or else Source (Ptr) = ASCII.HT loop + Ptr := Ptr + 1; + end loop; + + Number := 0; + while Source (Ptr) in '0' .. '9' loop + Number := 10 * Number + + (Character'Pos (Source (Ptr)) - Character'Pos ('0')); + Ptr := Ptr + 1; + end loop; + end Parse_Number; + + ----------------- + -- Parse_Token -- + ----------------- + + procedure Parse_Token + (Source : access String; + Ptr : in out Positive; + Token_Ptr : out Positive) + is + In_Quotes : Boolean := False; + + begin + -- Skip separators + + while Source (Ptr) = ' ' or else Source (Ptr) = ASCII.HT loop + Ptr := Ptr + 1; + end loop; + + Token_Ptr := Ptr; + + -- Find end-of-token + + while (In_Quotes or else + not (Source (Ptr) = ' ' + or else Source (Ptr) = ASCII.HT + or else Source (Ptr) = '<')) + and then Source (Ptr) >= ' ' + loop + if Source (Ptr) = '"' then + In_Quotes := not In_Quotes; + end if; + + Ptr := Ptr + 1; + end loop; + end Parse_Token; + + ---------------------- + -- Parse_X_Filename -- + ---------------------- + + procedure Parse_X_Filename (File : in out ALI_File) is + Ali : String_Access renames File.Buffer; + Ptr : Positive renames File.Current_Line; + File_Nr : Natural; + + begin + while Ali (Ptr) = X loop + + -- The current line is the start of a new Xref file section, + -- whose format looks like: + + -- " X 1 debug.ads" + + -- Skip the X and read the file number for the new X_File + + Ptr := Ptr + 1; + Parse_Number (Ali, Ptr, File_Nr); + + if File_Nr > 0 then + File.X_File := File.Dep.Table (File_Nr); + end if; + + Parse_EOL (Ali, Ptr); + end loop; + + end Parse_X_Filename; + + -------------------- + -- Print_Gnatfind -- + -------------------- + + procedure Print_Gnatfind + (References : Boolean; + Full_Path_Name : Boolean) + is + Decl : Declaration_Reference := First_Declaration; + Ref1 : Reference; + Ref2 : Reference; + + procedure Print_Ref + (Ref : Reference; + Msg : String := " "); + -- Print a reference, according to the extended tag of the output + + --------------- + -- Print_Ref -- + --------------- + + procedure Print_Ref + (Ref : Reference; + Msg : String := " ") + is + Buffer : constant String := + Osint.To_Host_File_Spec + (Get_Gnatchop_File (Ref, Full_Path_Name)).all + & ":" & Get_Line (Ref) + & ":" & Get_Column (Ref) + & ": "; + Num_Blanks : Integer := Longest_File_Name + 10 - Buffer'Length; + + begin + Num_Blanks := Integer'Max (0, Num_Blanks); + Write_Line + (Buffer + & String'(1 .. Num_Blanks => ' ') + & Msg & " " & Get_Symbol (Decl)); + if Get_Source_Line (Ref)'Length /= 0 then + Write_Line (" " & Get_Source_Line (Ref)); + end if; + end Print_Ref; + + -- Start of processing for Print_Gnatfind + + begin + while Decl /= Empty_Declaration loop + if Match (Decl) then + + -- Output the declaration + + declare + Parent : constant Declaration_Reference := Get_Parent (Decl); + Buffer : constant String := + Osint.To_Host_File_Spec + (Get_Gnatchop_File (Decl, Full_Path_Name)).all + & ":" & Get_Line (Decl) + & ":" & Get_Column (Decl) + & ": "; + Num_Blanks : Integer := Longest_File_Name + 10 - Buffer'Length; + + begin + Num_Blanks := Integer'Max (0, Num_Blanks); + Write_Line + (Buffer & String'(1 .. Num_Blanks => ' ') + & "(spec) " & Get_Symbol (Decl)); + + if Parent /= Empty_Declaration then + Write_Line + (Buffer & String'(1 .. Num_Blanks => ' ') + & " derived from " & Get_Symbol (Parent) + & " (" + & Osint.To_Host_File_Spec (Get_Gnatchop_File (Parent)).all + & ':' & Get_Line (Parent) + & ':' & Get_Column (Parent) & ')'); + end if; + end; + + if Get_Source_Line (Decl)'Length /= 0 then + Write_Line (" " & Get_Source_Line (Decl)); + end if; + + -- Output the body (sorted) + + Ref1 := First_Body (Decl); + while Ref1 /= Empty_Reference loop + Print_Ref (Ref1, "(body)"); + Ref1 := Next (Ref1); + end loop; + + if References then + Ref1 := First_Modif (Decl); + Ref2 := First_Reference (Decl); + while Ref1 /= Empty_Reference + or else Ref2 /= Empty_Reference + loop + if Compare (Ref1, Ref2) = LessThan then + Print_Ref (Ref1); + Ref1 := Next (Ref1); + else + Print_Ref (Ref2); + Ref2 := Next (Ref2); + end if; + end loop; + end if; + end if; + + Decl := Next (Decl); + end loop; + end Print_Gnatfind; + + ------------------ + -- Print_Unused -- + ------------------ + + procedure Print_Unused (Full_Path_Name : in Boolean) is + Decl : Declaration_Reference := First_Declaration; + Ref : Reference; + + begin + while Decl /= Empty_Declaration loop + if First_Modif (Decl) = Empty_Reference + and then First_Reference (Decl) = Empty_Reference + then + Write_Str (Get_Symbol (Decl) + & " " + & Get_Type (Decl) + & " " + & Osint.To_Host_File_Spec + (Get_Gnatchop_File (Decl, Full_Path_Name)).all + & ':' + & Get_Line (Decl) + & ':' + & Get_Column (Decl)); + + -- Print the body if any + + Ref := First_Body (Decl); + + if Ref /= Empty_Reference then + Write_Line (' ' + & Osint.To_Host_File_Spec + (Get_Gnatchop_File (Ref, Full_Path_Name)).all + & ':' & Get_Line (Ref) + & ':' & Get_Column (Ref)); + else + Write_Eol; + end if; + end if; + + Decl := Next (Decl); + end loop; + end Print_Unused; + + -------------- + -- Print_Vi -- + -------------- + + procedure Print_Vi (Full_Path_Name : in Boolean) is + Tab : constant Character := ASCII.HT; + Decl : Declaration_Reference := First_Declaration; + Ref : Reference; + + begin + while Decl /= Empty_Declaration loop + Write_Line (Get_Symbol (Decl) & Tab + & Get_File (Decl, Full_Path_Name) & Tab + & Get_Line (Decl)); + + -- Print the body if any + + Ref := First_Body (Decl); + + if Ref /= Empty_Reference then + Write_Line (Get_Symbol (Decl) & Tab + & Get_File (Ref, Full_Path_Name) + & Tab + & Get_Line (Ref)); + end if; + + -- Print the modifications + + Ref := First_Modif (Decl); + + while Ref /= Empty_Reference loop + Write_Line (Get_Symbol (Decl) & Tab + & Get_File (Ref, Full_Path_Name) + & Tab + & Get_Line (Ref)); + Ref := Next (Ref); + end loop; + + Decl := Next (Decl); + end loop; + end Print_Vi; + + ---------------- + -- Print_Xref -- + ---------------- + + procedure Print_Xref (Full_Path_Name : in Boolean) is + Decl : Declaration_Reference := First_Declaration; + Ref : Reference; + File : File_Reference; + + Margin : constant := 10; + -- Column where file names start + + procedure New_Line80; + -- Go to start of new line + + procedure Print80 (S : in String); + -- Print the text, respecting the 80 columns rule. + + procedure Print_Ref (Line, Column : String); + -- The beginning of the output is aligned on a column multiple of 9 + + ---------------- + -- New_Line80 -- + ---------------- + + procedure New_Line80 is + begin + Write_Eol; + Write_Str (String'(1 .. Margin - 1 => ' ')); + end New_Line80; + + ------------- + -- Print80 -- + ------------- + + procedure Print80 (S : in String) is + Align : Natural := Margin - (Integer (Column) mod Margin); + begin + if Align = Margin then + Align := 0; + end if; + + Write_Str (String'(1 .. Align => ' ') & S); + end Print80; + + --------------- + -- Print_Ref -- + --------------- + + procedure Print_Ref (Line, Column : String) is + Line_Align : constant Integer := 4 - Line'Length; + + S : constant String := String'(1 .. Line_Align => ' ') + & Line & ':' & Column; + + Align : Natural := Margin - (Integer (Output.Column) mod Margin); + + begin + if Align = Margin then + Align := 0; + end if; + + if Integer (Output.Column) + Align + S'Length > 79 then + New_Line80; + Align := 0; + end if; + + Write_Str (String'(1 .. Align => ' ') & S); + end Print_Ref; + + -- Start of processing for Print_Xref + + begin + while Decl /= Empty_Declaration loop + Write_Str (Get_Symbol (Decl)); + + while Column < Type_Position loop + Write_Char (' '); + end loop; + + Write_Line (Get_Full_Type (Get_Type (Decl))); + + Write_Parent_Info : declare + Parent : constant Declaration_Reference := Get_Parent (Decl); + begin + if Parent /= Empty_Declaration then + Write_Str (" Ptype: "); + Print80 + (Osint.To_Host_File_Spec (Get_Gnatchop_File (Parent)).all); + Print_Ref (Get_Line (Parent), Get_Column (Parent)); + Print80 (" " & Get_Symbol (Parent)); + Write_Eol; + end if; + end Write_Parent_Info; + + Write_Str (" Decl: "); + Print80 + (Osint.To_Host_File_Spec + (Get_Gnatchop_File (Decl, Full_Path_Name)).all & ' '); + Print_Ref (Get_Line (Decl), Get_Column (Decl)); + + -- Print the body if any + + Ref := First_Body (Decl); + + if Ref /= Empty_Reference then + Write_Eol; + Write_Str (" Body: "); + Print80 + (Osint.To_Host_File_Spec + (Get_Gnatchop_File (Ref, Full_Path_Name)).all & ' '); + Print_Ref (Get_Line (Ref), Get_Column (Ref)); + end if; + + -- Print the modifications if any + + Ref := First_Modif (Decl); + + if Ref /= Empty_Reference then + Write_Eol; + Write_Str (" Modi: "); + end if; + + File := Empty_File; + + while Ref /= Empty_Reference loop + if Get_File_Ref (Ref) /= File then + if File /= Empty_File then + New_Line80; + end if; + + File := Get_File_Ref (Ref); + Write_Str + (Get_Gnatchop_File (Ref, Full_Path_Name) & ' '); + Print_Ref (Get_Line (Ref), Get_Column (Ref)); + + else + Print_Ref (Get_Line (Ref), Get_Column (Ref)); + end if; + + Ref := Next (Ref); + end loop; + + -- Print the references + + Ref := First_Reference (Decl); + + if Ref /= Empty_Reference then + Write_Eol; + Write_Str (" Ref: "); + end if; + + File := Empty_File; + + while Ref /= Empty_Reference loop + if Get_File_Ref (Ref) /= File then + if File /= Empty_File then + New_Line80; + end if; + + File := Get_File_Ref (Ref); + Write_Str + (Osint.To_Host_File_Spec + (Get_Gnatchop_File (Ref, Full_Path_Name)).all & ' '); + Print_Ref (Get_Line (Ref), Get_Column (Ref)); + + else + Print_Ref (Get_Line (Ref), Get_Column (Ref)); + end if; + + Ref := Next (Ref); + end loop; + + Write_Eol; + Decl := Next (Decl); + end loop; + end Print_Xref; + + --------------- + -- Read_File -- + --------------- + + procedure Read_File + (FD : File_Descriptor; + Contents : out String_Access; + Success : out Boolean) + is + Length : constant File_Offset := File_Offset (File_Length (FD)); + -- Include room for EOF char + + Buffer : String (1 .. Length + 1); + + This_Read : Integer; + Read_Ptr : File_Offset := 1; + + begin + + loop + This_Read := Read (FD, + A => Buffer (Read_Ptr)'Address, + N => Length + 1 - Read_Ptr); + Read_Ptr := Read_Ptr + Integer'Max (This_Read, 0); + exit when This_Read <= 0; + end loop; + + Buffer (Read_Ptr) := EOF; + Contents := new String'(Buffer (1 .. Read_Ptr)); + + -- Things aren't simple on VMS due to the plethora of file types + -- and organizations. It seems clear that there shouldn't be more + -- bytes read than are contained in the file though. + + if Hostparm.OpenVMS then + Success := Read_Ptr <= Length + 1; + else + Success := Read_Ptr = Length + 1; + end if; + end Read_File; + + ------------ + -- Search -- + ------------ + + procedure Search + (Pattern : Search_Pattern; + Local_Symbols : Boolean; + Wide_Search : Boolean; + Read_Only : Boolean; + Der_Info : Boolean; + Type_Tree : Boolean) + is + type String_Access is access String; + procedure Free is new Unchecked_Deallocation (String, String_Access); + + ALIfile : ALI_File; + File_Ref : File_Reference; + Strip_Num : Natural := 0; + Ali_Name : String_Access; + + begin + -- If we want all the .ali files, then find them + + if Wide_Search then + Find_ALI_Files; + end if; + + loop + -- Get the next unread ali file + + File_Ref := Next_Unvisited_File; + + exit when File_Ref = Empty_File; + + -- Find the ALI file to use. Most of the time, it will be the unit + -- name, with a different extension. However, when dealing with + -- separates the ALI file is in fact the parent's ALI file (and this + -- is recursive, in case the parent itself is a separate). + + Strip_Num := 0; + loop + Free (Ali_Name); + Ali_Name := new String' + (Get_File (File_Ref, With_Dir => True, Strip => Strip_Num)); + + -- Striped too many things... + if Ali_Name.all = "" then + if Get_Emit_Warning (File_Ref) then + Set_Standard_Error; + Write_Line + ("warning : file " & Get_File (File_Ref, With_Dir => True) + & " not found"); + Set_Standard_Output; + end if; + Free (Ali_Name); + exit; + + -- If not found, try the parent's ALI file (this is needed for + -- separate units and subprograms). + elsif not File_Exists (Ali_Name.all) then + Strip_Num := Strip_Num + 1; + + -- Else we finally found it + else + exit; + end if; + end loop; + + -- Now that we have a file name, parse it to find any reference to + -- the entity. + + if Ali_Name /= null + and then (Read_Only or else Is_Writable_File (Ali_Name.all)) + then + begin + Open (Ali_Name.all, ALIfile); + while ALIfile.Buffer (ALIfile.Current_Line) /= EOF loop + Parse_X_Filename (ALIfile); + Parse_Identifier_Info (Pattern, ALIfile, Local_Symbols, + Der_Info, Type_Tree, Wide_Search); + end loop; + + exception + when No_Xref_Information => + if Get_Emit_Warning (File_Ref) then + Set_Standard_Error; + Write_Line + ("warning : No cross-referencing information in " + & Ali_Name.all); + Set_Standard_Output; + end if; + end; + end if; + end loop; + + Free (Ali_Name); + end Search; + + ----------------- + -- Search_Xref -- + ----------------- + + procedure Search_Xref + (Local_Symbols : Boolean; + Read_Only : Boolean; + Der_Info : Boolean) + is + ALIfile : ALI_File; + File_Ref : File_Reference; + Null_Pattern : Search_Pattern; + begin + loop + -- Find the next unvisited file + + File_Ref := Next_Unvisited_File; + exit when File_Ref = Empty_File; + + -- Search the object directories for the .ali file + + if Read_Only + or else Is_Writable_File (Get_File (File_Ref, With_Dir => True)) + then + begin + Open (Get_File (File_Ref, With_Dir => True), ALIfile, True); + + while ALIfile.Buffer (ALIfile.Current_Line) /= EOF loop + Parse_X_Filename (ALIfile); + Parse_Identifier_Info + (Null_Pattern, ALIfile, Local_Symbols, Der_Info); + end loop; + + exception + when No_Xref_Information => null; + end; + end if; + end loop; + end Search_Xref; + +end Xref_Lib; diff --git a/gcc/ada/xref_lib.ads b/gcc/ada/xref_lib.ads new file mode 100644 index 00000000000..1282ad142dc --- /dev/null +++ b/gcc/ada/xref_lib.ads @@ -0,0 +1,205 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- X R E F _ L I B -- +-- -- +-- S p e c -- +-- -- +-- $Revision: 1.20 $ +-- -- +-- Copyright (C) 1998-1999 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +with Hostparm; +with GNAT.Directory_Operations; use GNAT.Directory_Operations; +with GNAT.OS_Lib; use GNAT.OS_Lib; +with GNAT.Dynamic_Tables; + +with Xr_Tabls; use Xr_Tabls; +with GNAT.Regexp; use GNAT.Regexp; + +-- Misc. utilities for the cross-referencing tool + +package Xref_Lib is + + subtype File_Name_String is String (1 .. Hostparm.Max_Name_Length); + subtype Line_String is String (1 .. Hostparm.Max_Line_Length); + + type ALI_File is limited private; + + --------------------- + -- Directory Input -- + --------------------- + type Rec_DIR is limited private; + -- This one is used for recursive search of .ali files + + procedure Find_ALI_Files; + -- Find all the ali files that we will have to parse, and have them to + -- the file list + + --------------------- + -- Search patterns -- + --------------------- + + type Search_Pattern is private; + type Search_Pattern_Ptr is access all Search_Pattern; + + procedure Add_Entity + (Pattern : in out Search_Pattern; + Entity : String; + Glob : Boolean := False); + -- Add a new entity to the search pattern (the entity should have the + -- form pattern[:file[:line[:column]]], and it is parsed entirely in + -- this procedure. Glob indicates if we should use the 'globbing + -- patterns' (True) or the full regular expressions (False) + + procedure Add_File (File : String); + -- Add a new file in the list of files to search for references. + -- File is considered to be a globbing regular expression, which is thus + -- expanded + + Invalid_Argument : exception; + -- Exception raised when there is a syntax error in the command line + + function Match + (Pattern : Search_Pattern; + Symbol : String) + return Boolean; + -- Returns true if Symbol matches one of the entities in the command line + + ----------------------- + -- Output Algorithms -- + ----------------------- + + procedure Print_Gnatfind + (References : in Boolean; + Full_Path_Name : in Boolean); + procedure Print_Unused (Full_Path_Name : in Boolean); + procedure Print_Vi (Full_Path_Name : in Boolean); + procedure Print_Xref (Full_Path_Name : in Boolean); + -- The actual print procedures. These functions step through the symbol + -- table and print all the symbols if they match the files given on the + -- command line (they already match the entities if they are in the + -- symbol table) + + ------------------------ + -- General Algorithms -- + ------------------------ + function Default_Project_File (Dir_Name : in String) return String; + -- Returns the default Project file name + + procedure Search + (Pattern : Search_Pattern; + Local_Symbols : Boolean; + Wide_Search : Boolean; + Read_Only : Boolean; + Der_Info : Boolean; + Type_Tree : Boolean); + -- Search every ali file (following the Readdir rule above), for + -- each line matching Pattern, and executes Process on these + -- lines. If World is True, Search will look into every .ali file + -- in the object search path. If Read_Only is True, we parse the + -- read-only ali files too. If Der_Mode is true then the derived type + -- information will be processed. If Type_Tree is true then the type + -- hierarchy will be search going from pattern to the parent type + + procedure Search_Xref + (Local_Symbols : Boolean; + Read_Only : Boolean; + Der_Info : Boolean); + -- Search every ali file given in the command line and all their + -- dependencies. If Read_Only is True, we parse the read-only ali + -- files too. If Der_Mode is true then the derived type information will + -- be processed + + --------------- + -- ALI files -- + --------------- + + function Current_Xref_File + (File : ALI_File) + return Xr_Tabls.File_Reference; + -- Returns the name of the file in which the last identifier + -- is declared + + function File_Name + (File : ALI_File; + Num : Positive) + return Xr_Tabls.File_Reference; + -- Returns the dependency file name number Num + + function Get_Full_Type (Abbrev : Character) return String; + -- Returns the full type corresponding to a type letter as found in + -- the .ali files. + + procedure Open + (Name : in String; + File : out ALI_File; + Dependencies : in Boolean := False); + -- Open a new ALI file + -- if Dependencies is True, the insert every library file 'with'ed in + -- the files database (used for gnatxref) + + +private + type Rec_DIR is limited record + Dir : GNAT.Directory_Operations.Dir_Type; + end record; + + package Dependencies_Tables is new GNAT.Dynamic_Tables + (Table_Component_Type => Xr_Tabls.File_Reference, + Table_Index_Type => Positive, + Table_Low_Bound => 1, + Table_Initial => 400, + Table_Increment => 100); + use Dependencies_Tables; + + type Dependencies is new Dependencies_Tables.Instance; + + type ALI_File is limited record + Buffer : String_Access := null; + -- Buffer used to read the whole file at once + + Current_Line : Positive; + -- Start of the current line in Buffer + + Xref_Line : Positive; + -- Start of the xref lines in Buffer + + X_File : Xr_Tabls.File_Reference; + -- Stores the cross-referencing file-name ("X..." lines), as an + -- index into the dependencies table + + Dep : Dependencies; + -- Store file name associated with each number ("D..." lines) + end record; + + -- The following record type stores all the patterns that are searched for + + type Search_Pattern is record + Entity : GNAT.Regexp.Regexp; + -- A regular expression matching the entities we are looking for. + -- File is a list of the places where the declaration of the entities + -- has to be. When the user enters a file:line:column on the command + -- line, it is stored as "Entity_Name Declaration_File:line:column" + + Initialized : Boolean := False; + -- Set to True when Entity has been initialized. + end record; + -- Stores all the pattern that are search for. +end Xref_Lib; diff --git a/gcc/ada/xsinfo.adb b/gcc/ada/xsinfo.adb new file mode 100644 index 00000000000..57d4b3e1580 --- /dev/null +++ b/gcc/ada/xsinfo.adb @@ -0,0 +1,261 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT SYSTEM UTILITIES -- +-- -- +-- X S I N F O -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.19 $ +-- -- +-- Copyright (C) 1992-2000 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Program to construct C header file a-sinfo.h (C version of sinfo.ads spec, +-- for use by Gigi, contains all definitions and access functions, but does +-- not contain set procedures, since Gigi never modifies the GNAT tree) + +-- Input files: + +-- sinfo.ads Spec of Sinfo package + +-- Output files: + +-- a-sinfo.h Corresponding c header file + +-- Note: this program assumes that sinfo.ads has passed the error checks +-- which are carried out by the CSinfo utility, so it does not duplicate +-- these checks and assumes the soruce is correct. + +-- An optional argument allows the specification of an output file name to +-- override the default a-sinfo.h file name for the generated output file. + +with Ada.Command_Line; use Ada.Command_Line; +with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; +with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO; +with Ada.Text_IO; use Ada.Text_IO; + +with GNAT.Spitbol; use GNAT.Spitbol; +with GNAT.Spitbol.Patterns; use GNAT.Spitbol.Patterns; + +procedure XSinfo is + + Done : exception; + Err : exception; + + A : VString := Nul; + Arg : VString := Nul; + Comment : VString := Nul; + Line : VString := Nul; + N : VString := Nul; + N1, N2 : VString := Nul; + Nam : VString := Nul; + Rtn : VString := Nul; + Sinforev : VString := Nul; + Term : VString := Nul; + XSinforev : VString := Nul; + + InS : File_Type; + Ofile : File_Type; + + wsp : Pattern := Span (' ' & ASCII.HT); + Get_Vsn : Pattern := BreakX ('$') & "$Rev" & "ision: " + & Break (' ') * Sinforev; + Wsp_For : Pattern := wsp & "for"; + Is_Cmnt : Pattern := wsp & "--"; + Typ_Nod : Pattern := wsp * A & "type Node_Kind is"; + Get_Nam : Pattern := wsp * A & "N_" & Break (",)") * Nam + & Len (1) * Term; + Sub_Typ : Pattern := wsp * A & "subtype " & Break (' ') * N; + No_Cont : Pattern := wsp & Break (' ') * N1 & " .. " & Break (';') * N2; + Cont_N1 : Pattern := wsp & Break (' ') * N1 & " .." & Rpos (0); + Cont_N2 : Pattern := Span (' ') & Break (';') * N2; + Is_Func : Pattern := wsp * A & "function " & Rest * Nam; + Get_Arg : Pattern := wsp & "(N : " & Break (')') * Arg + & ") return " & Break (';') * Rtn + & ';' & wsp & "--" & wsp & Rest * Comment; + + NKV : Natural; + + M : Match_Result; + + + procedure Getline; + -- Get non-comment, non-blank line. Also skips "for " rep clauses. + + procedure Getline is + begin + loop + Line := Get_Line (InS); + + if Line /= "" + and then not Match (Line, Wsp_For) + and then not Match (Line, Is_Cmnt) + then + return; + + elsif Match (Line, " -- End functions (note") then + raise Done; + end if; + end loop; + end Getline; + +-- Start of processing for XSinfo + +begin + Set_Exit_Status (1); + Anchored_Mode := True; + Match ("$Revision: 1.19 $", "$Rev" & "ision: " & Break (' ') * XSinforev); + + if Argument_Count > 0 then + Create (Ofile, Out_File, Argument (1)); + else + Create (Ofile, Out_File, "a-sinfo.h"); + end if; + + Open (InS, In_File, "sinfo.ads"); + + -- Get Sinfo rev and write header to output file + + loop + Line := Get_Line (InS); + exit when Line = ""; + + if Match (Line, Get_Vsn) then + Put_Line + (Ofile, "/* Generated by xsinfo revision " + & XSinforev & " using */"); + Put_Line + (Ofile, "/* sinfo.ads revision " + & Sinforev & " */"); + + else + Match + (Line, + "-- S p e c ", + "-- C Header File "); + + Match (Line, "--", "/*"); + Match (Line, Rtab (2) * A & "--", M); + Replace (M, A & "*/"); + Put_Line (Ofile, Line); + end if; + end loop; + + -- Skip to package line + + loop + Getline; + exit when Match (Line, "package"); + end loop; + + -- Skip to first node kind line + + loop + Getline; + exit when Match (Line, Typ_Nod); + Put_Line (Ofile, Line); + end loop; + + Put_Line (Ofile, ""); + NKV := 0; + + -- Loop through node kind codes + + loop + Getline; + + if Match (Line, Get_Nam) then + Put_Line (Ofile, A & "#define N_" & Nam & ' ' & NKV); + NKV := NKV + 1; + exit when not Match (Term, ","); + + else + Put_Line (Ofile, Line); + end if; + end loop; + + Put_Line (Ofile, ""); + Put_Line (Ofile, A & "#define Number_Node_Kinds " & NKV); + + -- Loop through subtype declarations + + loop + Getline; + + if not Match (Line, Sub_Typ) then + exit when Match (Line, " function"); + Put_Line (Ofile, Line); + + else + Put_Line (Ofile, A & "SUBTYPE (" & N & ", Node_Kind, "); + Getline; + + -- Normal case + + if Match (Line, No_Cont) then + Put_Line (Ofile, A & " " & N1 & ", " & N2 & ')'); + + -- Continuation case + + else + if not Match (Line, Cont_N1) then + raise Err; + end if; + + Getline; + + if not Match (Line, Cont_N2) then + raise Err; + end if; + + Put_Line (Ofile, A & " " & N1 & ','); + Put_Line (Ofile, A & " " & N2 & ')'); + end if; + end if; + end loop; + + -- Loop through functions. Note that this loop is terminated by + -- the call to Getfile encountering the end of functions sentinel + + loop + if Match (Line, Is_Func) then + Getline; + if not Match (Line, Get_Arg) then + raise Err; + end if; + Put_Line + (Ofile, + A & "INLINE " & Rpad (Rtn, 9) + & ' ' & Rpad (Nam, 30) & " (" & Arg & " N)"); + + Put_Line (Ofile, A & " { return " & Comment & " (N); }"); + + else + Put_Line (Ofile, Line); + end if; + + Getline; + end loop; + +exception + when Done => + Put_Line (Ofile, ""); + Set_Exit_Status (0); + +end XSinfo; diff --git a/gcc/ada/xtreeprs.adb b/gcc/ada/xtreeprs.adb new file mode 100644 index 00000000000..995401e4984 --- /dev/null +++ b/gcc/ada/xtreeprs.adb @@ -0,0 +1,383 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT SYSTEM UTILITIES -- +-- -- +-- X T R E E P R S -- +-- -- +-- B o d y -- +-- -- +-- $Revision: 1.33 $ +-- -- +-- Copyright (C) 1992-2001 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 2, 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 COPYING. If not, write -- +-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- +-- MA 02111-1307, USA. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). -- +-- -- +------------------------------------------------------------------------------ + +-- Program to construct the spec of the Treeprs package + +-- Input files: + +-- sinfo.ads Spec of Sinfo package +-- treeprs.adt Template for Treeprs package + +-- Output files: + +-- treeprs.ads Spec of Treeprs package + +-- Note: this program assumes that sinfo.ads has passed the error checks which +-- are carried out by the CSinfo utility so it does not duplicate these checks + +-- An optional argument allows the specification of an output file name to +-- override the default treeprs.ads file name for the generated output file. + +with Ada.Command_Line; use Ada.Command_Line; +with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; +with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO; +with Ada.Text_IO; use Ada.Text_IO; + +with GNAT.Spitbol; use GNAT.Spitbol; +with GNAT.Spitbol.Patterns; use GNAT.Spitbol.Patterns; +with GNAT.Spitbol.Table_Boolean; use GNAT.Spitbol.Table_Boolean; +with GNAT.Spitbol.Table_VString; use GNAT.Spitbol.Table_VString; + +procedure XTreeprs is + + package TB renames GNAT.Spitbol.Table_Boolean; + package TV renames GNAT.Spitbol.Table_VString; + + Err : exception; + -- Raised on fatal error + + A : VString := Nul; + Ffield : VString := Nul; + Field : VString := Nul; + Fieldno : VString := Nul; + Flagno : VString := Nul; + Line : VString := Nul; + Name : VString := Nul; + Node : VString := Nul; + Outstring : VString := Nul; + Prefix : VString := Nul; + S : VString := Nul; + S1 : VString := Nul; + Sinforev : VString := Nul; + Syn : VString := Nul; + Synonym : VString := Nul; + Temprev : VString := Nul; + Term : VString := Nul; + Treeprsrev : VString := Nul; + + OutS : File_Type; + -- Output file + + InS : File_Type; + -- Read sinfo.ads + + InT : File_Type; + -- Read treeprs.adt + + Special : TB.Table (20); + -- Table of special fields. These fields are not included in the table + -- constructed by Xtreeprs, since they are specially handled in treeprs. + -- This means these field definitions are completely ignored. + + Names : array (1 .. 500) of VString; + -- Table of names of synonyms + + Positions : array (1 .. 500) of Natural; + -- Table of starting positions in Pchars string for synonyms + + Strings : TV.Table (300); + -- Contribution of each synonym to Pchars string, indexed by name + + Count : Natural := 0; + -- Number of synonyms processed so far + + Curpos : Natural := 1; + -- Number of characters generated in Pchars string so far + + Lineno : Natural := 0; + -- Line number in sinfo.ads + + Field_Base : constant := Character'Pos ('#'); + -- Fields 1-5 are represented by the characters #$%&' (i.e. by five + -- contiguous characters starting at # (16#23#)). + + Flag_Base : constant := Character'Pos ('('); + -- Flags 1-18 are represented by the characters ()*+,-./0123456789 + -- (i.e. by 18 contiguous characters starting at (16#28#)). + + Fieldch : Character; + -- Field character, as per above tables + + Sp : aliased Natural; + -- Space left on line for Pchars output + + wsp : Pattern := Span (' ' & ASCII.HT); + + Get_SRev : Pattern := BreakX ('$') & "$Rev" & "ision: " + & Break (' ') * Sinforev; + Get_TRev : Pattern := BreakX ('$') & "$Rev" & "ision: " + & Break (' ') * Temprev; + Is_Temp : Pattern := BreakX ('T') * A & "T e m p l a t e"; + Get_Node : Pattern := wsp & "-- N_" & Rest * Node; + Tst_Punc : Pattern := Break (" ,."); + Get_Syn : Pattern := Span (' ') & "-- " & Break (' ') * Synonym + & " (" & Break (')') * Field; + Brk_Min : Pattern := Break ('-') * Ffield; + Is_Flag : Pattern := "Flag" & Rest * Flagno; + Is_Field : Pattern := Rtab (1) & Len (1) * Fieldno; + Is_Syn : Pattern := wsp & "N_" & Break (",)") * Syn & Len (1) * Term; + Brk_Node : Pattern := Break (' ') * Node & ' '; + Chop_SP : Pattern := Len (Sp'Unrestricted_Access) * S1; + + M : Match_Result; + +begin + Anchored_Mode := True; + + Match ("$Revision: 1.33 $", "$Rev" & "ision: " & Break (' ') * Treeprsrev); + + if Argument_Count > 0 then + Create (OutS, Out_File, Argument (1)); + else + Create (OutS, Out_File, "treeprs.ads"); + end if; + + Open (InS, In_File, "sinfo.ads"); + Open (InT, In_File, "treeprs.adt"); + + -- Initialize special fields table + + Set (Special, "Analyzed", True); + Set (Special, "Cannot_Be_Constant", True); + Set (Special, "Chars", True); + Set (Special, "Comes_From_Source", True); + Set (Special, "Error_Posted", True); + Set (Special, "Etype", True); + Set (Special, "Has_No_Side_Effects", True); + Set (Special, "Is_Controlling_Actual", True); + Set (Special, "Is_Overloaded", True); + Set (Special, "Is_Static_Expression", True); + Set (Special, "Left_Opnd", True); + Set (Special, "Must_Check_Expr", True); + Set (Special, "No_Overflow_Expr", True); + Set (Special, "Paren_Count", True); + Set (Special, "Raises_Constraint_Error", True); + Set (Special, "Right_Opnd", True); + + -- Get sinfo revs and write header to output file + + loop + Line := Get_Line (InS); + Lineno := Lineno + 1; + + if Line = "" then + raise Err; + end if; + + exit when Match (Line, Get_SRev); + end loop; + + -- Read template header and generate new header + + loop + Line := Get_Line (InT); + + if Match (Line, Get_TRev) then + Put_Line + (OutS, + "-- Generated by xtreeprs revision " & + Treeprsrev & " using --"); + + Put_Line + (OutS, + "-- sinfo.ads revision " & + Sinforev & " --"); + + Put_Line + (OutS, + "-- treeprs.adt revision " + & Temprev & " --"); + + else + -- Skip lines describing the template + + if Match (Line, "-- This file is a template") then + loop + Line := Get_Line (InT); + exit when Line = ""; + end loop; + end if; + + exit when Match (Line, "package"); + + if Match (Line, Is_Temp, M) then + Replace (M, A & " S p e c "); + end if; + + Put_Line (OutS, Line); + end if; + end loop; + + Put_Line (OutS, Line); + + -- Copy rest of comments up to template insert point to spec + + loop + Line := Get_Line (InT); + exit when Match (Line, "!!TEMPLATE INSERTION POINT"); + Put_Line (OutS, Line); + end loop; + + -- Here we are doing the actual insertions + + Put_Line (OutS, " Pchars : constant String :="); + + -- Loop through comments describing nodes, picking up fields + + loop + Line := Get_Line (InS); + Lineno := Lineno + 1; + exit when Match (Line, " type Node_Kind"); + + if Match (Line, Get_Node) + and then not Match (Node, Tst_Punc) + then + Outstring := Node & ' '; + + loop + Line := Get_Line (InS); + exit when Line = ""; + + if Match (Line, Get_Syn) + and then not Match (Synonym, "plus") + and then not Present (Special, Synonym) + then + -- Convert this field into the character used to + -- represent the field according to the table: + + -- Field1 '#' + -- Field2 '$' + -- Field3 '%' + -- Field4 '&' + -- Field5 "'" + -- Flag1 "(" + -- Flag2 ")" + -- Flag3 '*' + -- Flag4 '+' + -- Flag5 ',' + -- Flag6 '-' + -- Flag7 '.' + -- Flag8 '/' + -- Flag9 '0' + -- Flag10 '1' + -- Flag11 '2' + -- Flag12 '3' + -- Flag13 '4' + -- Flag14 '5' + -- Flag15 '6' + -- Flag16 '7' + -- Flag17 '8' + -- Flag18 '9' + + if Match (Field, Brk_Min) then + Field := Ffield; + end if; + + if Match (Field, Is_Flag) then + Fieldch := Char (Flag_Base - 1 + N (Flagno)); + + elsif Match (Field, Is_Field) then + Fieldch := Char (Field_Base - 1 + N (Fieldno)); + + else + Put_Line + (Standard_Error, + "*** Line " & + Lineno & + " has unrecognized field name " & + Field); + raise Err; + end if; + + Append (Outstring, Fieldch & Synonym); + end if; + end loop; + + Set (Strings, Node, Outstring); + end if; + end loop; + + -- Loop through actual definitions of node kind enumeration literals + + loop + loop + Line := Get_Line (InS); + Lineno := Lineno + 1; + exit when Match (Line, Is_Syn); + end loop; + + S := Get (Strings, Syn); + Match (S, Brk_Node, ""); + Count := Count + 1; + Names (Count) := Syn; + Positions (Count) := Curpos; + Curpos := Curpos + Length (S); + Put_Line (OutS, " -- " & Node); + Prefix := V (" "); + exit when Term = ")"; + + -- Loop to output the string literal for Pchars + + loop + Sp := 79 - 4 - Length (Prefix); + exit when (Size (S) <= Sp); + Match (S, Chop_SP, ""); + Put_Line (OutS, Prefix & '"' & S1 & """ &"); + Prefix := V (" "); + end loop; + + Put_Line (OutS, Prefix & '"' & S & """ &"); + end loop; + + Put_Line (OutS, " """";"); + Put_Line (OutS, ""); + Put_Line + (OutS, " type Pchar_Pos_Array is array (Node_Kind) of Positive;"); + Put_Line + (OutS, + " Pchar_Pos : constant Pchar_Pos_Array := Pchar_Pos_Array'("); + + -- Output lines for Pchar_Pos_Array values + + for M in 1 .. Count - 1 loop + Name := Rpad ("N_" & Names (M), 40); + Put_Line (OutS, " " & Name & " => " & Positions (M) & ','); + end loop; + + Name := Rpad ("N_" & Names (Count), 40); + Put_Line (OutS, " " & Name & " => " & Positions (Count) & ");"); + + Put_Line (OutS, ""); + Put_Line (OutS, "end Treeprs;"); + +exception + when Err => + Put_Line (Standard_Error, "*** fatal error"); + Set_Exit_Status (1); + +end XTreeprs;