| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- A T R E E -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Aspects; use Aspects; |
| with Debug; use Debug; |
| with Namet; use Namet; |
| with Nlists; use Nlists; |
| with Opt; use Opt; |
| with Output; use Output; |
| with Sinfo.Utils; use Sinfo.Utils; |
| with System.Storage_Elements; |
| |
| package body Atree is |
| |
| --------------- |
| -- Debugging -- |
| --------------- |
| |
| -- Suppose you find that node 12345 is messed up. You might want to find |
| -- the code that created that node. See sinfo-utils.adb for how to do that. |
| |
| Ignored_Ghost_Recording_Proc : Ignored_Ghost_Record_Proc := null; |
| -- This soft link captures the procedure invoked during the creation of an |
| -- ignored Ghost node or entity. |
| |
| Locked : Boolean := False; |
| -- Compiling with assertions enabled, node contents modifications are |
| -- permitted only when this switch is set to False; compiling without |
| -- assertions this lock has no effect. |
| |
| Reporting_Proc : Report_Proc := null; |
| -- Set_Reporting_Proc sets this. Set_Reporting_Proc must be called only |
| -- once. |
| |
| Rewriting_Proc : Rewrite_Proc := null; |
| -- This soft link captures the procedure invoked during a node rewrite |
| |
| ----------------------------- |
| -- Local Objects and Types -- |
| ----------------------------- |
| |
| Comes_From_Source_Default : Boolean := False; |
| |
| use Atree_Private_Part; |
| -- We are also allowed to see our private data structures |
| |
| -------------------------------------------------- |
| -- Implementation of Tree Substitution Routines -- |
| -------------------------------------------------- |
| |
| -- A separate table keeps track of the mapping between rewritten nodes and |
| -- their corresponding original tree nodes. Rewrite makes an entry in this |
| -- table for use by Original_Node. By default the entry in this table |
| -- points to the original unwritten node. Note that if a node is rewritten |
| -- more than once, there is no easy way to get to the intermediate |
| -- rewrites; the node itself is the latest version, and the entry in this |
| -- table is the original. |
| |
| -- Note: This could be a node field. |
| |
| package Orig_Nodes is new Table.Table ( |
| Table_Component_Type => Node_Id, |
| Table_Index_Type => Node_Id'Base, |
| Table_Low_Bound => First_Node_Id, |
| Table_Initial => Alloc.Node_Offsets_Initial, |
| Table_Increment => Alloc.Node_Offsets_Increment, |
| Table_Name => "Orig_Nodes"); |
| |
| ------------------ |
| -- Parent Stack -- |
| ------------------ |
| |
| -- A separate table is used to traverse trees. It passes the parent field |
| -- of each node to the called process subprogram. It is defined global to |
| -- avoid adding performance overhead if allocated each time the traversal |
| -- functions are invoked. |
| |
| package Parents_Stack is new Table.Table |
| (Table_Component_Type => Node_Id, |
| Table_Index_Type => Nat, |
| Table_Low_Bound => 1, |
| Table_Initial => 256, |
| Table_Increment => 100, |
| Table_Name => "Parents_Stack"); |
| |
| -------------------------- |
| -- Paren_Count Handling -- |
| -------------------------- |
| |
| -- The Small_Paren_Count field has range 0 .. 3. If the Paren_Count is |
| -- in the range 0 .. 2, then it is stored as Small_Paren_Count. Otherwise, |
| -- Small_Paren_Count = 3, and the actual Paren_Count is stored in the |
| -- Paren_Counts table. |
| -- |
| -- We use linear search on the Paren_Counts table, which is plenty |
| -- efficient because only pathological programs will use it. Nobody |
| -- writes (((X + Y))). |
| |
| type Paren_Count_Entry is record |
| Nod : Node_Id; |
| -- The node to which this count applies |
| |
| Count : Nat range 3 .. Nat'Last; |
| -- The count of parentheses, which will be in the indicated range |
| end record; |
| |
| package Paren_Counts is new Table.Table ( |
| Table_Component_Type => Paren_Count_Entry, |
| Table_Index_Type => Int, |
| Table_Low_Bound => 0, |
| Table_Initial => 10, |
| Table_Increment => 200, |
| Table_Name => "Paren_Counts"); |
| |
| procedure Set_Paren_Count_Of_Copy (Target, Source : Node_Id); |
| pragma Inline (Set_Paren_Count_Of_Copy); |
| -- Called when copying a node. Makes sure the Paren_Count of the copy is |
| -- correct. |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| function Allocate_New_Node (Kind : Node_Kind) return Node_Id; |
| pragma Inline (Allocate_New_Node); |
| -- Allocate a new node or first part of a node extension. Initialize the |
| -- Nodes.Table entry, Flags, Orig_Nodes, and List tables. |
| |
| procedure Fix_Parents (Ref_Node, Fix_Node : Node_Id); |
| -- Fix up parent pointers for the children of Fix_Node after a copy, |
| -- setting them to Fix_Node when they pointed to Ref_Node. |
| |
| generic |
| with function Process |
| (Parent_Node : Node_Id; |
| Node : Node_Id) return Traverse_Result is <>; |
| function Internal_Traverse_With_Parent |
| (Node : Node_Id) return Traverse_Final_Result; |
| pragma Inline (Internal_Traverse_With_Parent); |
| -- Internal function that provides a functionality similar to Traverse_Func |
| -- but extended to pass the Parent node to the called Process subprogram; |
| -- delegates to Traverse_Func_With_Parent the initialization of the stack |
| -- data structure which stores the parent nodes (cf. Parents_Stack). |
| -- ??? Could we factorize the common code of Internal_Traverse_Func and |
| -- Traverse_Func? |
| |
| procedure Mark_New_Ghost_Node (N : Node_Or_Entity_Id); |
| -- Mark arbitrary node or entity N as Ghost when it is created within a |
| -- Ghost region. |
| |
| procedure Report (Target, Source : Node_Id); |
| pragma Inline (Report); |
| -- Invoke the reporting procedure if available |
| |
| function Size_In_Slots (N : Node_Or_Entity_Id) return Slot_Count; |
| -- Number of slots belonging to N. This can be less than |
| -- Size_In_Slots_To_Alloc for entities. Includes both header |
| -- and dynamic slots. |
| |
| function Size_In_Slots_Dynamic (N : Node_Or_Entity_Id) return Slot_Count; |
| -- Just counts the number of dynamic slots |
| |
| function Size_In_Slots_To_Alloc (N : Node_Or_Entity_Id) return Slot_Count; |
| function Size_In_Slots_To_Alloc (Kind : Node_Kind) return Slot_Count; |
| -- Number of slots to allocate for a node or entity. For entities, we have |
| -- to allocate the max, because we don't know the Ekind when this is |
| -- called. |
| |
| function Off_F (N : Node_Id) return Node_Offset with Inline; |
| -- Offset of the first dynamic slot of N in Slots.Table. |
| -- The actual offset of this slot from the start of the node |
| -- is not 0; this is logically the first slot after the header |
| -- slots. |
| |
| function Off_0 (N : Node_Id) return Node_Offset'Base with Inline; |
| -- This is for zero-origin addressing of the dynamic slots. |
| -- It points to slot 0 of N in Slots.Table, which does not exist, |
| -- because the first few slots are stored in the header. |
| |
| function Off_L (N : Node_Id) return Node_Offset with Inline; |
| -- Offset of the last slot of N in Slots.Table |
| |
| procedure Zero_Dynamic_Slots (First, Last : Node_Offset'Base) with Inline; |
| -- Set dynamic slots in the range First..Last to zero |
| |
| procedure Zero_Header_Slots (N : Node_Or_Entity_Id) with Inline; |
| -- Zero the header slots belonging to N |
| |
| procedure Zero_Slots (N : Node_Or_Entity_Id) with Inline; |
| -- Zero the slots belonging to N (both header and dynamic) |
| |
| procedure Copy_Dynamic_Slots |
| (From, To : Node_Offset; Num_Slots : Slot_Count) |
| with Inline; |
| -- Copy Num_Slots slots from From to To. Caller is responsible for ensuring |
| -- that the Num_Slots at To are a reasonable place to copy to. |
| |
| procedure Copy_Slots (Source, Destination : Node_Id) with Inline; |
| -- Copies the slots (both header and dynamic) of Source to Destination; |
| -- uses the node kind to determine the Num_Slots. |
| |
| function Get_Field_Value |
| (N : Node_Id; Field : Node_Or_Entity_Field) return Field_Size_32_Bit; |
| -- Get any field value as a Field_Size_32_Bit. If the field is smaller than |
| -- 32 bits, convert it to Field_Size_32_Bit. The Field must be present in |
| -- the Nkind of N. |
| |
| procedure Set_Field_Value |
| (N : Node_Id; Field : Node_Or_Entity_Field; Val : Field_Size_32_Bit); |
| -- Set any field value as a Field_Size_32_Bit. If the field is smaller than |
| -- 32 bits, convert it from Field_Size_32_Bit, and Val had better be small |
| -- enough. The Field must be present in the Nkind of N. |
| |
| procedure Check_Vanishing_Fields |
| (Old_N : Node_Id; New_Kind : Node_Kind); |
| -- Called whenever Nkind is modified. Raises an exception if not all |
| -- vanishing fields are in their initial zero state. |
| |
| procedure Check_Vanishing_Fields |
| (Old_N : Entity_Id; New_Kind : Entity_Kind); |
| -- Above are the same as the ones for nodes, but for entities |
| |
| procedure Init_Nkind (N : Node_Id; Val : Node_Kind); |
| -- Initialize the Nkind field, which must not have been set already. This |
| -- cannot be used to modify an already-initialized Nkind field. See also |
| -- Mutate_Nkind. |
| |
| procedure Mutate_Nkind |
| (N : Node_Id; Val : Node_Kind; Old_Size : Slot_Count); |
| -- Called by the other Mutate_Nkind to do all the work. This is needed |
| -- because the call in Change_Node, which calls this one directly, happens |
| -- after zeroing N's slots, which destroys its Nkind, which prevents us |
| -- from properly computing Old_Size. |
| |
| package Field_Checking is |
| -- Functions for checking field access, used only in assertions |
| |
| function Field_Present |
| (Kind : Node_Kind; Field : Node_Field) return Boolean; |
| function Field_Present |
| (Kind : Entity_Kind; Field : Entity_Field) return Boolean; |
| -- True if a node/entity of the given Kind has the given Field. |
| -- Always True if assertions are disabled. |
| |
| end Field_Checking; |
| |
| package body Field_Checking is |
| |
| -- Tables used by Field_Present |
| |
| type Node_Field_Sets is array (Node_Kind) of Node_Field_Set; |
| type Node_Field_Sets_Ptr is access all Node_Field_Sets; |
| Node_Fields_Present : Node_Field_Sets_Ptr; |
| |
| type Entity_Field_Sets is array (Entity_Kind) of Entity_Field_Set; |
| type Entity_Field_Sets_Ptr is access all Entity_Field_Sets; |
| Entity_Fields_Present : Entity_Field_Sets_Ptr; |
| |
| procedure Init_Tables; |
| |
| function Create_Node_Fields_Present |
| (Kind : Node_Kind) return Node_Field_Set; |
| function Create_Entity_Fields_Present |
| (Kind : Entity_Kind) return Entity_Field_Set; |
| -- Computes the set of fields present in each Node/Entity Kind. Used to |
| -- initialize the above tables. |
| |
| -------------------------------- |
| -- Create_Node_Fields_Present -- |
| -------------------------------- |
| |
| function Create_Node_Fields_Present |
| (Kind : Node_Kind) return Node_Field_Set |
| is |
| Result : Node_Field_Set := (others => False); |
| begin |
| for J in Node_Field_Table (Kind)'Range loop |
| Result (Node_Field_Table (Kind) (J)) := True; |
| end loop; |
| |
| return Result; |
| end Create_Node_Fields_Present; |
| |
| -------------------------------- |
| -- Create_Entity_Fields_Present -- |
| -------------------------------- |
| |
| function Create_Entity_Fields_Present |
| (Kind : Entity_Kind) return Entity_Field_Set |
| is |
| Result : Entity_Field_Set := (others => False); |
| begin |
| for J in Entity_Field_Table (Kind)'Range loop |
| Result (Entity_Field_Table (Kind) (J)) := True; |
| end loop; |
| |
| return Result; |
| end Create_Entity_Fields_Present; |
| |
| ----------------- |
| -- Init_Tables -- |
| ----------------- |
| |
| procedure Init_Tables is |
| begin |
| Node_Fields_Present := new Node_Field_Sets; |
| |
| for Kind in Node_Kind loop |
| Node_Fields_Present (Kind) := Create_Node_Fields_Present (Kind); |
| end loop; |
| |
| Entity_Fields_Present := new Entity_Field_Sets; |
| |
| for Kind in Entity_Kind loop |
| Entity_Fields_Present (Kind) := |
| Create_Entity_Fields_Present (Kind); |
| end loop; |
| end Init_Tables; |
| |
| -- In production mode, we leave Node_Fields_Present and |
| -- Entity_Fields_Present null. Field_Present is only for |
| -- use in assertions. |
| |
| pragma Debug (Init_Tables); |
| |
| function Field_Present |
| (Kind : Node_Kind; Field : Node_Field) return Boolean is |
| begin |
| if Node_Fields_Present = null then |
| return True; |
| end if; |
| |
| return Node_Fields_Present (Kind) (Field); |
| end Field_Present; |
| |
| function Field_Present |
| (Kind : Entity_Kind; Field : Entity_Field) return Boolean is |
| begin |
| if Entity_Fields_Present = null then |
| return True; |
| end if; |
| |
| return Entity_Fields_Present (Kind) (Field); |
| end Field_Present; |
| |
| end Field_Checking; |
| |
| ------------------------ |
| -- Atree_Private_Part -- |
| ------------------------ |
| |
| package body Atree_Private_Part is |
| |
| -- The following validators are disabled in production builds, by being |
| -- called in pragma Debug. They are also disabled by default in debug |
| -- builds, by setting the flags below, because they make the compiler |
| -- very slow (10 to 20 times slower). Validate can be set True to debug |
| -- the low-level accessors. |
| -- |
| -- Even if Validate is True, validation is disabled during |
| -- Validate_... calls to prevent infinite recursion |
| -- (Validate_... procedures call field getters, which call |
| -- Validate_... procedures). That's what the Enable_Validate_... |
| -- flags are for; they are toggled so that when we're inside one |
| -- of them, and enter it again, the inner call doesn't do anything. |
| -- These flags are irrelevant when Validate is False. |
| |
| Validate : constant Boolean := False; |
| |
| Enable_Validate_Node, |
| Enable_Validate_Node_Write, |
| Enable_Validate_Node_And_Offset, |
| Enable_Validate_Node_And_Offset_Write : |
| Boolean := Validate; |
| |
| procedure Validate_Node_And_Offset |
| (N : Node_Or_Entity_Id; Offset : Field_Offset); |
| procedure Validate_Node_And_Offset_Write |
| (N : Node_Or_Entity_Id; Offset : Field_Offset); |
| -- Asserts N is OK, and the Offset in slots is within N. Note that this |
| -- does not guarantee that the offset is valid, just that it's not past |
| -- the last slot. It could be pointing at unused bits within the node, |
| -- or unused padding at the end. The "_Write" version is used when we're |
| -- about to modify the node. |
| |
| procedure Validate_Node_And_Offset |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) is |
| begin |
| if Enable_Validate_Node_And_Offset then |
| Enable_Validate_Node_And_Offset := False; |
| |
| pragma Debug (Validate_Node (N)); |
| pragma Assert (Offset'Valid); |
| pragma Assert (Offset < Size_In_Slots (N)); |
| |
| Enable_Validate_Node_And_Offset := True; |
| end if; |
| end Validate_Node_And_Offset; |
| |
| procedure Validate_Node_And_Offset_Write |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) is |
| begin |
| if Enable_Validate_Node_And_Offset_Write then |
| Enable_Validate_Node_And_Offset_Write := False; |
| |
| pragma Debug (Validate_Node_Write (N)); |
| pragma Assert (Offset'Valid); |
| pragma Assert (Offset < Size_In_Slots (N)); |
| |
| Enable_Validate_Node_And_Offset_Write := True; |
| end if; |
| end Validate_Node_And_Offset_Write; |
| |
| procedure Validate_Node (N : Node_Or_Entity_Id) is |
| begin |
| if Enable_Validate_Node then |
| Enable_Validate_Node := False; |
| |
| pragma Assert (N'Valid); |
| pragma Assert (N <= Node_Offsets.Last); |
| pragma Assert (Off_L (N) >= Off_0 (N)); |
| pragma Assert (Off_L (N) >= Off_F (N) - 1); |
| pragma Assert (Off_L (N) <= Slots.Last); |
| pragma Assert (Nkind (N)'Valid); |
| pragma Assert (Nkind (N) /= N_Unused_At_End); |
| |
| if Nkind (N) in N_Entity then |
| pragma Assert (Ekind (N)'Valid); |
| end if; |
| |
| if Nkind (N) in |
| N_Aggregate |
| | N_Attribute_Definition_Clause |
| | N_Aspect_Specification |
| | N_Extension_Aggregate |
| | N_Freeze_Entity |
| | N_Freeze_Generic_Entity |
| | N_Has_Entity |
| | N_Selected_Component |
| | N_Use_Package_Clause |
| then |
| pragma Assert (Entity_Or_Associated_Node (N)'Valid); |
| end if; |
| |
| Enable_Validate_Node := True; |
| end if; |
| end Validate_Node; |
| |
| procedure Validate_Node_Write (N : Node_Or_Entity_Id) is |
| begin |
| if Enable_Validate_Node_Write then |
| Enable_Validate_Node_Write := False; |
| |
| pragma Debug (Validate_Node (N)); |
| pragma Assert (not Locked); |
| |
| Enable_Validate_Node_Write := True; |
| end if; |
| end Validate_Node_Write; |
| |
| function Is_Valid_Node (U : Union_Id) return Boolean is |
| begin |
| return Node_Id'Base (U) <= Node_Offsets.Last; |
| end Is_Valid_Node; |
| |
| function Alloc_Node_Id return Node_Id is |
| begin |
| Node_Offsets.Increment_Last; |
| return Node_Offsets.Last; |
| end Alloc_Node_Id; |
| |
| function Alloc_Slots (Num_Slots : Slot_Count) return Node_Offset is |
| begin |
| return Result : constant Node_Offset := Slots.Last + 1 do |
| Slots.Set_Last (Slots.Last + Num_Slots); |
| end return; |
| end Alloc_Slots; |
| |
| function Get_1_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Type |
| is |
| pragma Assert (Field_Type'Size = 1); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Size_1_Bit, Field_Type); |
| Val : constant Field_Size_1_Bit := Get_1_Bit_Val (N, Offset); |
| begin |
| return Cast (Val); |
| end Get_1_Bit_Field; |
| |
| function Get_2_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Type |
| is |
| pragma Assert (Field_Type'Size = 2); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Size_2_Bit, Field_Type); |
| Val : constant Field_Size_2_Bit := Get_2_Bit_Val (N, Offset); |
| begin |
| return Cast (Val); |
| end Get_2_Bit_Field; |
| |
| function Get_4_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Type |
| is |
| pragma Assert (Field_Type'Size = 4); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Size_4_Bit, Field_Type); |
| Val : constant Field_Size_4_Bit := Get_4_Bit_Val (N, Offset); |
| begin |
| return Cast (Val); |
| end Get_4_Bit_Field; |
| |
| function Get_8_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Type |
| is |
| pragma Assert (Field_Type'Size = 8); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Size_8_Bit, Field_Type); |
| Val : constant Field_Size_8_Bit := Get_8_Bit_Val (N, Offset); |
| begin |
| return Cast (Val); |
| end Get_8_Bit_Field; |
| |
| function Get_32_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Type |
| is |
| pragma Assert (Field_Type'Size = 32); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Size_32_Bit, Field_Type); |
| |
| Val : constant Field_Size_32_Bit := Get_32_Bit_Val (N, Offset); |
| Result : constant Field_Type := Cast (Val); |
| -- Note: declaring Result here instead of directly returning |
| -- Cast (...) helps CodePeer understand that there are no issues |
| -- around uninitialized variables. |
| begin |
| return Result; |
| end Get_32_Bit_Field; |
| |
| function Get_32_Bit_Field_With_Default |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Type |
| is |
| function Get_Field is new Get_32_Bit_Field (Field_Type) with Inline; |
| Result : Field_Type; |
| begin |
| -- If the field has not yet been set, it will be equal to zero. |
| -- That is of the "wrong" type, so we fetch it as a |
| -- Field_Size_32_Bit. |
| |
| if Get_32_Bit_Val (N, Offset) = 0 then |
| Result := Default_Val; |
| |
| else |
| Result := Get_Field (N, Offset); |
| end if; |
| |
| return Result; |
| end Get_32_Bit_Field_With_Default; |
| |
| function Get_Valid_32_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Type |
| is |
| pragma Assert (Get_32_Bit_Val (N, Offset) /= 0); |
| -- If the field has not yet been set, it will be equal to zero. |
| -- This asserts that we don't call Get_ before Set_. Note that |
| -- the predicate on the Val parameter of Set_ checks for the No_... |
| -- value, so it can't possibly be (for example) No_Uint here. |
| |
| function Get_Field is new Get_32_Bit_Field (Field_Type) with Inline; |
| Result : constant Field_Type := Get_Field (N, Offset); |
| begin |
| return Result; |
| end Get_Valid_32_Bit_Field; |
| |
| procedure Set_1_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Type) |
| is |
| pragma Assert (Field_Type'Size = 1); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Type, Field_Size_1_Bit); |
| begin |
| Set_1_Bit_Val (N, Offset, Cast (Val)); |
| end Set_1_Bit_Field; |
| |
| procedure Set_2_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Type) |
| is |
| pragma Assert (Field_Type'Size = 2); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Type, Field_Size_2_Bit); |
| begin |
| Set_2_Bit_Val (N, Offset, Cast (Val)); |
| end Set_2_Bit_Field; |
| |
| procedure Set_4_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Type) |
| is |
| pragma Assert (Field_Type'Size = 4); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Type, Field_Size_4_Bit); |
| begin |
| Set_4_Bit_Val (N, Offset, Cast (Val)); |
| end Set_4_Bit_Field; |
| |
| procedure Set_8_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Type) |
| is |
| pragma Assert (Field_Type'Size = 8); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Type, Field_Size_8_Bit); |
| begin |
| Set_8_Bit_Val (N, Offset, Cast (Val)); |
| end Set_8_Bit_Field; |
| |
| procedure Set_32_Bit_Field |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Type) |
| is |
| pragma Assert (Field_Type'Size = 32); |
| |
| function Cast is new |
| Unchecked_Conversion (Field_Type, Field_Size_32_Bit); |
| begin |
| Set_32_Bit_Val (N, Offset, Cast (Val)); |
| end Set_32_Bit_Field; |
| |
| pragma Style_Checks ("M90"); |
| |
| ----------------------------------- |
| -- Low-level getters and setters -- |
| ----------------------------------- |
| |
| -- In the getters and setters below, we use shifting and masking to |
| -- simulate packed arrays. F_Size is the field size in bits. Mask is |
| -- that number of 1 bits in the low-order bits. F_Per_Slot is the number |
| -- of fields per slot. Slot_Off is the offset of the slot of interest. |
| -- S is the slot at that offset. V is the amount to shift by. |
| |
| function In_NH (Slot_Off : Field_Offset) return Boolean is |
| (Slot_Off < N_Head); |
| -- In_NH stands for "in Node_Header", not "in New Hampshire" |
| |
| function Get_Slot |
| (N : Node_Or_Entity_Id; Slot_Off : Field_Offset) |
| return Slot is |
| (if In_NH (Slot_Off) then |
| Node_Offsets.Table (N).Slots (Slot_Off) |
| else Slots.Table (Node_Offsets.Table (N).Offset + Slot_Off)); |
| -- Get the slot value, either directly from the node header, or |
| -- indirectly from the Slots table. |
| |
| procedure Set_Slot |
| (N : Node_Or_Entity_Id; Slot_Off : Field_Offset; S : Slot); |
| -- Set the slot value, either directly from the node header, or |
| -- indirectly from the Slots table, to S. |
| |
| function Get_1_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Size_1_Bit |
| is |
| F_Size : constant := 1; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset (N, Slot_Off)); |
| Raw : constant Field_Size_1_Bit := |
| Field_Size_1_Bit (Shift_Right (S, V) and Mask); |
| begin |
| return Raw; |
| end Get_1_Bit_Val; |
| |
| function Get_2_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Size_2_Bit |
| is |
| F_Size : constant := 2; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset (N, Slot_Off)); |
| Raw : constant Field_Size_2_Bit := |
| Field_Size_2_Bit (Shift_Right (S, V) and Mask); |
| begin |
| return Raw; |
| end Get_2_Bit_Val; |
| |
| function Get_4_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Size_4_Bit |
| is |
| F_Size : constant := 4; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset (N, Slot_Off)); |
| Raw : constant Field_Size_4_Bit := |
| Field_Size_4_Bit (Shift_Right (S, V) and Mask); |
| begin |
| return Raw; |
| end Get_4_Bit_Val; |
| |
| function Get_8_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Size_8_Bit |
| is |
| F_Size : constant := 8; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset (N, Slot_Off)); |
| Raw : constant Field_Size_8_Bit := |
| Field_Size_8_Bit (Shift_Right (S, V) and Mask); |
| begin |
| return Raw; |
| end Get_8_Bit_Val; |
| |
| function Get_32_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset) return Field_Size_32_Bit |
| is |
| F_Size : constant := 32; |
| -- No Mask needed |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| pragma Debug (Validate_Node_And_Offset (N, Slot_Off)); |
| Raw : constant Field_Size_32_Bit := |
| Field_Size_32_Bit (S); |
| begin |
| return Raw; |
| end Get_32_Bit_Val; |
| |
| procedure Set_Slot |
| (N : Node_Or_Entity_Id; Slot_Off : Field_Offset; S : Slot) is |
| begin |
| if In_NH (Slot_Off) then |
| Node_Offsets.Table (N).Slots (Slot_Off) := S; |
| else |
| Slots.Table (Node_Offsets.Table (N).Offset + Slot_Off) := S; |
| end if; |
| end Set_Slot; |
| |
| procedure Set_1_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Size_1_Bit) |
| is |
| F_Size : constant := 1; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset_Write (N, Slot_Off)); |
| begin |
| Set_Slot |
| (N, Slot_Off, |
| (S and not Shift_Left (Mask, V)) or Shift_Left (Slot (Val), V)); |
| end Set_1_Bit_Val; |
| |
| procedure Set_2_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Size_2_Bit) |
| is |
| F_Size : constant := 2; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset_Write (N, Slot_Off)); |
| begin |
| Set_Slot |
| (N, Slot_Off, |
| (S and not Shift_Left (Mask, V)) or Shift_Left (Slot (Val), V)); |
| end Set_2_Bit_Val; |
| |
| procedure Set_4_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Size_4_Bit) |
| is |
| F_Size : constant := 4; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset_Write (N, Slot_Off)); |
| begin |
| Set_Slot |
| (N, Slot_Off, |
| (S and not Shift_Left (Mask, V)) or Shift_Left (Slot (Val), V)); |
| end Set_4_Bit_Val; |
| |
| procedure Set_8_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Size_8_Bit) |
| is |
| F_Size : constant := 8; |
| Mask : constant := 2**F_Size - 1; |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| S : constant Slot := Get_Slot (N, Slot_Off); |
| V : constant Natural := Natural ((Offset mod F_Per_Slot) * F_Size); |
| pragma Debug (Validate_Node_And_Offset_Write (N, Slot_Off)); |
| begin |
| Set_Slot |
| (N, Slot_Off, |
| (S and not Shift_Left (Mask, V)) or Shift_Left (Slot (Val), V)); |
| end Set_8_Bit_Val; |
| |
| procedure Set_32_Bit_Val |
| (N : Node_Or_Entity_Id; Offset : Field_Offset; Val : Field_Size_32_Bit) |
| is |
| F_Size : constant := 32; |
| -- No Mask needed; this one doesn't do read-modify-write |
| F_Per_Slot : constant Field_Offset := Slot_Size / F_Size; |
| Slot_Off : constant Field_Offset := Offset / F_Per_Slot; |
| pragma Debug (Validate_Node_And_Offset_Write (N, Slot_Off)); |
| begin |
| Set_Slot (N, Slot_Off, Slot (Val)); |
| end Set_32_Bit_Val; |
| |
| ---------------------- |
| -- Print_Atree_Info -- |
| ---------------------- |
| |
| procedure Print_Atree_Info (N : Node_Or_Entity_Id) is |
| function Cast is new Unchecked_Conversion (Slot, Int); |
| begin |
| Write_Int (Int (Size_In_Slots (N))); |
| Write_Str (" slots ("); |
| Write_Int (Int (Off_0 (N))); |
| Write_Str (" .. "); |
| Write_Int (Int (Off_L (N))); |
| Write_Str ("):"); |
| |
| for Off in Off_0 (N) .. Off_L (N) loop |
| Write_Str (" "); |
| Write_Int (Cast (Get_Slot (N, Off))); |
| end loop; |
| |
| Write_Eol; |
| end Print_Atree_Info; |
| |
| end Atree_Private_Part; |
| |
| --------------------- |
| -- Get_Field_Value -- |
| --------------------- |
| |
| function Get_Node_Field_Union is new Get_32_Bit_Field (Union_Id) |
| with Inline; |
| -- Called when we don't know whether a field is a Node_Id or a List_Id, |
| -- etc. |
| |
| function Get_Field_Value |
| (N : Node_Id; Field : Node_Or_Entity_Field) return Field_Size_32_Bit |
| is |
| Desc : Field_Descriptor renames Field_Descriptors (Field); |
| NN : constant Node_Or_Entity_Id := Node_To_Fetch_From (N, Field); |
| |
| begin |
| case Field_Size (Desc.Kind) is |
| when 1 => return Field_Size_32_Bit (Get_1_Bit_Val (NN, Desc.Offset)); |
| when 2 => return Field_Size_32_Bit (Get_2_Bit_Val (NN, Desc.Offset)); |
| when 4 => return Field_Size_32_Bit (Get_4_Bit_Val (NN, Desc.Offset)); |
| when 8 => return Field_Size_32_Bit (Get_8_Bit_Val (NN, Desc.Offset)); |
| when others => return Get_32_Bit_Val (NN, Desc.Offset); -- 32 |
| end case; |
| end Get_Field_Value; |
| |
| --------------------- |
| -- Set_Field_Value -- |
| --------------------- |
| |
| procedure Set_Field_Value |
| (N : Node_Id; Field : Node_Or_Entity_Field; Val : Field_Size_32_Bit) |
| is |
| Desc : Field_Descriptor renames Field_Descriptors (Field); |
| |
| begin |
| case Field_Size (Desc.Kind) is |
| when 1 => Set_1_Bit_Val (N, Desc.Offset, Field_Size_1_Bit (Val)); |
| when 2 => Set_2_Bit_Val (N, Desc.Offset, Field_Size_2_Bit (Val)); |
| when 4 => Set_4_Bit_Val (N, Desc.Offset, Field_Size_4_Bit (Val)); |
| when 8 => Set_8_Bit_Val (N, Desc.Offset, Field_Size_8_Bit (Val)); |
| when others => Set_32_Bit_Val (N, Desc.Offset, Val); -- 32 |
| end case; |
| end Set_Field_Value; |
| |
| procedure Reinit_Field_To_Zero |
| (N : Node_Id; Field : Node_Or_Entity_Field) |
| is |
| begin |
| Set_Field_Value (N, Field, 0); |
| end Reinit_Field_To_Zero; |
| |
| function Field_Is_Initial_Zero |
| (N : Node_Id; Field : Node_Or_Entity_Field) return Boolean is |
| begin |
| return Get_Field_Value (N, Field) = 0; |
| end Field_Is_Initial_Zero; |
| |
| procedure Reinit_Field_To_Zero |
| (N : Node_Id; Field : Entity_Field; Old_Ekind : Entity_Kind_Set) is |
| begin |
| pragma Assert (Old_Ekind (Ekind (N)), "Reinit: " & Ekind (N)'Img); |
| Reinit_Field_To_Zero (N, Field); |
| end Reinit_Field_To_Zero; |
| |
| procedure Reinit_Field_To_Zero |
| (N : Node_Id; Field : Entity_Field; Old_Ekind : Entity_Kind) is |
| Old_Ekind_Set : Entity_Kind_Set := (others => False); |
| begin |
| Old_Ekind_Set (Old_Ekind) := True; |
| Reinit_Field_To_Zero (N, Field, Old_Ekind => Old_Ekind_Set); |
| end Reinit_Field_To_Zero; |
| |
| procedure Check_Vanishing_Fields |
| (Old_N : Node_Id; New_Kind : Node_Kind) |
| is |
| Old_Kind : constant Node_Kind := Nkind (Old_N); |
| |
| -- If this fails, it means you need to call Reinit_Field_To_Zero before |
| -- calling Mutate_Nkind. |
| |
| begin |
| for J in Node_Field_Table (Old_Kind)'Range loop |
| declare |
| F : constant Node_Field := Node_Field_Table (Old_Kind) (J); |
| begin |
| if not Field_Checking.Field_Present (New_Kind, F) then |
| if not Field_Is_Initial_Zero (Old_N, F) then |
| Write_Str (Old_Kind'Img); |
| Write_Str (" --> "); |
| Write_Str (New_Kind'Img); |
| Write_Str (" Nonzero field "); |
| Write_Str (F'Img); |
| Write_Str (" is vanishing for node "); |
| Write_Int (Nat (Old_N)); |
| Write_Eol; |
| |
| raise Program_Error; |
| end if; |
| end if; |
| end; |
| end loop; |
| end Check_Vanishing_Fields; |
| |
| procedure Check_Vanishing_Fields |
| (Old_N : Entity_Id; New_Kind : Entity_Kind) |
| is |
| Old_Kind : constant Entity_Kind := Ekind (Old_N); |
| |
| -- If this fails, it means you need to call Reinit_Field_To_Zero before |
| -- calling Mutate_Ekind. But we have many cases where vanishing fields |
| -- are expected to reappear after converting to/from E_Void. Other cases |
| -- are more problematic; set a breakpoint on "(non-E_Void case)" below. |
| |
| begin |
| for J in Entity_Field_Table (Old_Kind)'Range loop |
| declare |
| F : constant Entity_Field := Entity_Field_Table (Old_Kind) (J); |
| begin |
| if not Field_Checking.Field_Present (New_Kind, F) then |
| if not Field_Is_Initial_Zero (Old_N, F) then |
| Write_Str (Old_Kind'Img); |
| Write_Str (" --> "); |
| Write_Str (New_Kind'Img); |
| Write_Str (" Nonzero field "); |
| Write_Str (F'Img); |
| Write_Str (" is vanishing for node "); |
| Write_Int (Nat (Old_N)); |
| Write_Eol; |
| |
| if New_Kind = E_Void or else Old_Kind = E_Void then |
| Write_Line (" (E_Void case)"); |
| else |
| Write_Line (" (non-E_Void case)"); |
| end if; |
| end if; |
| end if; |
| end; |
| end loop; |
| end Check_Vanishing_Fields; |
| |
| Nkind_Offset : constant Field_Offset := |
| Field_Descriptors (F_Nkind).Offset; |
| |
| procedure Set_Node_Kind_Type is new Set_8_Bit_Field (Node_Kind) with Inline; |
| |
| procedure Init_Nkind (N : Node_Id; Val : Node_Kind) is |
| pragma Assert (Field_Is_Initial_Zero (N, F_Nkind)); |
| begin |
| if Atree_Statistics_Enabled then |
| Set_Count (F_Nkind) := Set_Count (F_Nkind) + 1; |
| end if; |
| |
| Set_Node_Kind_Type (N, Nkind_Offset, Val); |
| end Init_Nkind; |
| |
| procedure Mutate_Nkind |
| (N : Node_Id; Val : Node_Kind; Old_Size : Slot_Count) |
| is |
| New_Size : constant Slot_Count := Size_In_Slots_To_Alloc (Val); |
| |
| All_Node_Offsets : Node_Offsets.Table_Type renames |
| Node_Offsets.Table (Node_Offsets.First .. Node_Offsets.Last); |
| begin |
| pragma Debug (Check_Vanishing_Fields (N, Val)); |
| |
| -- Grow the slots if necessary |
| |
| if Old_Size < New_Size then |
| declare |
| Old_Last_Slot : constant Node_Offset := Slots.Last; |
| Old_Off_F : constant Node_Offset := Off_F (N); |
| begin |
| if Old_Last_Slot = Old_Off_F + Old_Size - 1 then |
| -- In this case, the slots are at the end of Slots.Table, so we |
| -- don't need to move them. |
| Slots.Set_Last (Old_Last_Slot + New_Size - Old_Size); |
| |
| else |
| -- Move the slots |
| |
| declare |
| New_Off_F : constant Node_Offset := Alloc_Slots (New_Size); |
| begin |
| All_Node_Offsets (N).Offset := New_Off_F - N_Head; |
| Copy_Dynamic_Slots (Old_Off_F, New_Off_F, Old_Size); |
| pragma Debug |
| (Zero_Dynamic_Slots (Old_Off_F, Old_Off_F + Old_Size - 1)); |
| end; |
| end if; |
| end; |
| |
| Zero_Dynamic_Slots (Off_F (N) + Old_Size, Slots.Last); |
| end if; |
| |
| if Atree_Statistics_Enabled then |
| Set_Count (F_Nkind) := Set_Count (F_Nkind) + 1; |
| end if; |
| |
| Set_Node_Kind_Type (N, Nkind_Offset, Val); |
| pragma Debug (Validate_Node_Write (N)); |
| |
| New_Node_Debugging_Output (N); |
| end Mutate_Nkind; |
| |
| procedure Mutate_Nkind (N : Node_Id; Val : Node_Kind) is |
| begin |
| Mutate_Nkind (N, Val, Old_Size => Size_In_Slots_Dynamic (N)); |
| end Mutate_Nkind; |
| |
| Ekind_Offset : constant Field_Offset := |
| Field_Descriptors (F_Ekind).Offset; |
| |
| procedure Set_Entity_Kind_Type is new Set_8_Bit_Field (Entity_Kind) |
| with Inline; |
| |
| procedure Mutate_Ekind |
| (N : Entity_Id; Val : Entity_Kind) |
| is |
| begin |
| if Ekind (N) = Val then |
| return; |
| end if; |
| |
| if Debug_Flag_Underscore_V then |
| pragma Debug (Check_Vanishing_Fields (N, Val)); |
| end if; |
| |
| -- For now, we are allocating all entities with the same size, so we |
| -- don't need to reallocate slots here. |
| |
| if Atree_Statistics_Enabled then |
| Set_Count (F_Nkind) := Set_Count (F_Ekind) + 1; |
| end if; |
| |
| Set_Entity_Kind_Type (N, Ekind_Offset, Val); |
| pragma Debug (Validate_Node_Write (N)); |
| |
| New_Node_Debugging_Output (N); |
| end Mutate_Ekind; |
| |
| ----------------------- |
| -- Allocate_New_Node -- |
| ----------------------- |
| |
| function Allocate_New_Node (Kind : Node_Kind) return Node_Id is |
| begin |
| return Result : constant Node_Id := Alloc_Node_Id do |
| declare |
| Sz : constant Slot_Count := Size_In_Slots_To_Alloc (Kind); |
| Sl : constant Node_Offset := Alloc_Slots (Sz); |
| begin |
| Node_Offsets.Table (Result).Offset := Sl - N_Head; |
| Zero_Dynamic_Slots (Sl, Sl + Sz - 1); |
| Zero_Header_Slots (Result); |
| end; |
| |
| Init_Nkind (Result, Kind); |
| |
| Orig_Nodes.Append (Result); |
| Set_Comes_From_Source (Result, Comes_From_Source_Default); |
| Allocate_List_Tables (Result); |
| Report (Target => Result, Source => Empty); |
| end return; |
| end Allocate_New_Node; |
| |
| -------------------------- |
| -- Check_Error_Detected -- |
| -------------------------- |
| |
| procedure Check_Error_Detected is |
| begin |
| -- An anomaly has been detected which is assumed to be a consequence of |
| -- a previous serious error or configurable run time violation. Raise |
| -- an exception if no such error has been detected. |
| |
| if Serious_Errors_Detected = 0 |
| and then Configurable_Run_Time_Violations = 0 |
| then |
| raise Program_Error; |
| end if; |
| end Check_Error_Detected; |
| |
| ----------------- |
| -- Change_Node -- |
| ----------------- |
| |
| procedure Change_Node (N : Node_Id; New_Kind : Node_Kind) is |
| pragma Debug (Validate_Node_Write (N)); |
| pragma Assert (Nkind (N) not in N_Entity); |
| pragma Assert (New_Kind not in N_Entity); |
| |
| Old_Size : constant Slot_Count := Size_In_Slots_Dynamic (N); |
| New_Size : constant Slot_Count := Size_In_Slots_To_Alloc (New_Kind); |
| |
| Save_Sloc : constant Source_Ptr := Sloc (N); |
| Save_In_List : constant Boolean := In_List (N); |
| Save_CFS : constant Boolean := Comes_From_Source (N); |
| Save_Posted : constant Boolean := Error_Posted (N); |
| Save_CA : constant Boolean := Check_Actuals (N); |
| Save_Is_IGN : constant Boolean := Is_Ignored_Ghost_Node (N); |
| Save_Link : constant Union_Id := Link (N); |
| |
| Par_Count : Nat := 0; |
| |
| begin |
| if Nkind (N) in N_Subexpr then |
| Par_Count := Paren_Count (N); |
| end if; |
| |
| if New_Size > Old_Size then |
| declare |
| New_Offset : constant Field_Offset := Alloc_Slots (New_Size); |
| begin |
| pragma Debug (Zero_Slots (N)); |
| Node_Offsets.Table (N).Offset := New_Offset - N_Head; |
| Zero_Dynamic_Slots (New_Offset, New_Offset + New_Size - 1); |
| Zero_Header_Slots (N); |
| end; |
| |
| else |
| Zero_Slots (N); |
| end if; |
| |
| Init_Nkind (N, New_Kind); -- Not Mutate, because of Zero_Slots above |
| |
| Set_Sloc (N, Save_Sloc); |
| Set_In_List (N, Save_In_List); |
| Set_Comes_From_Source (N, Save_CFS); |
| Set_Error_Posted (N, Save_Posted); |
| Set_Check_Actuals (N, Save_CA); |
| Set_Is_Ignored_Ghost_Node (N, Save_Is_IGN); |
| Set_Link (N, Save_Link); |
| |
| if New_Kind in N_Subexpr then |
| Set_Paren_Count (N, Par_Count); |
| end if; |
| end Change_Node; |
| |
| ---------------- |
| -- Copy_Slots -- |
| ---------------- |
| |
| procedure Copy_Dynamic_Slots |
| (From, To : Node_Offset; Num_Slots : Slot_Count) |
| is |
| pragma Assert (if Num_Slots /= 0 then From /= To); |
| |
| All_Slots : Slots.Table_Type renames |
| Slots.Table (Slots.First .. Slots.Last); |
| |
| Source_Slots : Slots.Table_Type renames |
| All_Slots (From .. From + Num_Slots - 1); |
| |
| Destination_Slots : Slots.Table_Type renames |
| All_Slots (To .. To + Num_Slots - 1); |
| |
| begin |
| Destination_Slots := Source_Slots; |
| end Copy_Dynamic_Slots; |
| |
| procedure Copy_Slots (Source, Destination : Node_Id) is |
| pragma Debug (Validate_Node (Source)); |
| pragma Assert (Source /= Destination); |
| |
| S_Size : constant Slot_Count := Size_In_Slots_Dynamic (Source); |
| |
| All_Node_Offsets : Node_Offsets.Table_Type renames |
| Node_Offsets.Table (Node_Offsets.First .. Node_Offsets.Last); |
| |
| begin |
| Copy_Dynamic_Slots |
| (Off_F (Source), Off_F (Destination), S_Size); |
| All_Node_Offsets (Destination).Slots := All_Node_Offsets (Source).Slots; |
| end Copy_Slots; |
| |
| --------------- |
| -- Copy_Node -- |
| --------------- |
| |
| procedure Copy_Node (Source, Destination : Node_Or_Entity_Id) is |
| pragma Assert (Source /= Destination); |
| |
| Save_In_List : constant Boolean := In_List (Destination); |
| Save_Link : constant Union_Id := Link (Destination); |
| |
| S_Size : constant Slot_Count := Size_In_Slots_To_Alloc (Source); |
| D_Size : constant Slot_Count := Size_In_Slots_To_Alloc (Destination); |
| |
| begin |
| New_Node_Debugging_Output (Source); |
| New_Node_Debugging_Output (Destination); |
| |
| -- Currently all entities are allocated the same number of slots. |
| -- Hopefully that won't always be the case, but if it is, the following |
| -- is suboptimal if D_Size < S_Size, because in fact the Destination was |
| -- allocated the max. |
| |
| -- If Source doesn't fit in Destination, we need to allocate |
| |
| if D_Size < S_Size then |
| pragma Debug (Zero_Slots (Destination)); -- destroy old slots |
| Node_Offsets.Table (Destination).Offset := |
| Alloc_Slots (S_Size) - N_Head; |
| end if; |
| |
| Copy_Slots (Source, Destination); |
| |
| Set_In_List (Destination, Save_In_List); |
| Set_Link (Destination, Save_Link); |
| Set_Paren_Count_Of_Copy (Target => Destination, Source => Source); |
| end Copy_Node; |
| |
| ------------------------ |
| -- Copy_Separate_List -- |
| ------------------------ |
| |
| function Copy_Separate_List (Source : List_Id) return List_Id is |
| Result : constant List_Id := New_List; |
| Nod : Node_Id := First (Source); |
| |
| begin |
| while Present (Nod) loop |
| Append (Copy_Separate_Tree (Nod), Result); |
| Next (Nod); |
| end loop; |
| |
| return Result; |
| end Copy_Separate_List; |
| |
| ------------------------ |
| -- Copy_Separate_Tree -- |
| ------------------------ |
| |
| function Copy_Separate_Tree (Source : Node_Id) return Node_Id is |
| |
| pragma Debug (Validate_Node (Source)); |
| |
| New_Id : Node_Id; |
| |
| function Copy_Entity (E : Entity_Id) return Entity_Id; |
| -- Copy Entity, copying only Chars field |
| |
| function Copy_List (List : List_Id) return List_Id; |
| -- Copy list |
| |
| function Possible_Copy (Field : Union_Id) return Union_Id; |
| -- Given a field, returns a copy of the node or list if its parent is |
| -- the current source node, and otherwise returns the input. |
| |
| ----------------- |
| -- Copy_Entity -- |
| ----------------- |
| |
| function Copy_Entity (E : Entity_Id) return Entity_Id is |
| begin |
| pragma Assert (Nkind (E) in N_Entity); |
| |
| return Result : constant Entity_Id := New_Entity (Nkind (E), Sloc (E)) |
| do |
| Set_Chars (Result, Chars (E)); |
| end return; |
| end Copy_Entity; |
| |
| --------------- |
| -- Copy_List -- |
| --------------- |
| |
| function Copy_List (List : List_Id) return List_Id is |
| NL : List_Id; |
| E : Node_Id; |
| |
| begin |
| if List = No_List then |
| return No_List; |
| |
| else |
| NL := New_List; |
| |
| E := First (List); |
| while Present (E) loop |
| if Is_Entity (E) then |
| Append (Copy_Entity (E), NL); |
| else |
| Append (Copy_Separate_Tree (E), NL); |
| end if; |
| |
| Next (E); |
| end loop; |
| |
| return NL; |
| end if; |
| end Copy_List; |
| |
| ------------------- |
| -- Possible_Copy -- |
| ------------------- |
| |
| function Possible_Copy (Field : Union_Id) return Union_Id is |
| New_N : Union_Id; |
| |
| begin |
| if Field in Node_Range then |
| New_N := Union_Id (Copy_Separate_Tree (Node_Id (Field))); |
| |
| if Present (Node_Id (Field)) |
| and then Parent (Node_Id (Field)) = Source |
| then |
| Set_Parent (Node_Id (New_N), New_Id); |
| end if; |
| |
| return New_N; |
| |
| elsif Field in List_Range then |
| New_N := Union_Id (Copy_List (List_Id (Field))); |
| |
| if Parent (List_Id (Field)) = Source then |
| Set_Parent (List_Id (New_N), New_Id); |
| end if; |
| |
| return New_N; |
| |
| else |
| return Field; |
| end if; |
| end Possible_Copy; |
| |
| procedure Walk is new Walk_Sinfo_Fields_Pairwise (Possible_Copy); |
| |
| -- Start of processing for Copy_Separate_Tree |
| |
| begin |
| if Source <= Empty_Or_Error then |
| return Source; |
| |
| elsif Is_Entity (Source) then |
| return Copy_Entity (Source); |
| |
| else |
| New_Id := New_Copy (Source); |
| |
| Walk (New_Id, Source); |
| |
| -- Explicitly copy the aspect specifications as those do not reside |
| -- in a node field. |
| |
| if Permits_Aspect_Specifications (Source) |
| and then Has_Aspects (Source) |
| then |
| Set_Aspect_Specifications |
| (New_Id, Copy_List (Aspect_Specifications (Source))); |
| end if; |
| |
| -- Set Entity field to Empty to ensure that no entity references |
| -- are shared between the two, if the source is already analyzed. |
| |
| if Nkind (New_Id) in N_Has_Entity |
| or else Nkind (New_Id) = N_Freeze_Entity |
| then |
| Set_Entity (New_Id, Empty); |
| end if; |
| |
| -- Reset all Etype fields and Analyzed flags, because input tree may |
| -- have been fully or partially analyzed. |
| |
| if Nkind (New_Id) in N_Has_Etype then |
| Set_Etype (New_Id, Empty); |
| end if; |
| |
| Set_Analyzed (New_Id, False); |
| |
| -- Rather special case, if we have an expanded name, then change |
| -- it back into a selected component, so that the tree looks the |
| -- way it did coming out of the parser. This will change back |
| -- when we analyze the selected component node. |
| |
| if Nkind (New_Id) = N_Expanded_Name then |
| |
| -- The following code is a bit kludgy. It would be cleaner to |
| -- Add an entry Change_Expanded_Name_To_Selected_Component to |
| -- Sinfo.CN, but that's delicate because Atree is used in the |
| -- binder, so we don't want to add that dependency. |
| -- ??? Revisit now that ASIS is no longer using this unit. |
| |
| -- Consequently we have no choice but to hold our noses and do the |
| -- change manually. At least we are Atree, so this is at least all |
| -- in the family. |
| |
| -- Clear the Chars field which is not present in a selected |
| -- component node, so we don't want a junk value around. Note that |
| -- we can't just call Set_Chars, because Empty is of the wrong |
| -- type, and is outside the range of Name_Id. |
| |
| Reinit_Field_To_Zero (New_Id, F_Chars); |
| Reinit_Field_To_Zero (New_Id, F_Has_Private_View); |
| Reinit_Field_To_Zero (New_Id, F_Is_Elaboration_Checks_OK_Node); |
| Reinit_Field_To_Zero (New_Id, F_Is_Elaboration_Warnings_OK_Node); |
| Reinit_Field_To_Zero (New_Id, F_Is_SPARK_Mode_On_Node); |
| |
| -- Change the node type |
| |
| Mutate_Nkind (New_Id, N_Selected_Component); |
| end if; |
| |
| -- All done, return copied node |
| |
| return New_Id; |
| end if; |
| end Copy_Separate_Tree; |
| |
| ----------------------- |
| -- Exchange_Entities -- |
| ----------------------- |
| |
| procedure Exchange_Entities (E1 : Entity_Id; E2 : Entity_Id) is |
| pragma Debug (Validate_Node_Write (E1)); |
| pragma Debug (Validate_Node_Write (E2)); |
| pragma Assert |
| (Is_Entity (E1) and then Is_Entity (E2) |
| and then not In_List (E1) and then not In_List (E2)); |
| |
| Old_E1 : constant Node_Header := Node_Offsets.Table (E1); |
| |
| begin |
| Node_Offsets.Table (E1) := Node_Offsets.Table (E2); |
| Node_Offsets.Table (E2) := Old_E1; |
| |
| -- That exchange exchanged the parent pointers as well, which is what |
| -- we want, but we need to patch up the defining identifier pointers |
| -- in the parent nodes (the child pointers) to match this switch |
| -- unless for Implicit types entities which have no parent, in which |
| -- case we don't do anything otherwise we won't be able to revert back |
| -- to the original situation. |
| |
| -- Shouldn't this use Is_Itype instead of the Parent test??? |
| |
| if Present (Parent (E1)) and then Present (Parent (E2)) then |
| Set_Defining_Identifier (Parent (E1), E1); |
| Set_Defining_Identifier (Parent (E2), E2); |
| end if; |
| |
| New_Node_Debugging_Output (E1); |
| New_Node_Debugging_Output (E2); |
| end Exchange_Entities; |
| |
| ----------------- |
| -- Extend_Node -- |
| ----------------- |
| |
| procedure Extend_Node (Source : Node_Id) is |
| pragma Assert (Present (Source)); |
| pragma Assert (not Is_Entity (Source)); |
| |
| Old_Kind : constant Node_Kind := Nkind (Source); |
| pragma Assert (Old_Kind in N_Direct_Name); |
| New_Kind : constant Node_Kind := |
| (case Old_Kind is |
| when N_Character_Literal => N_Defining_Character_Literal, |
| when N_Identifier => N_Defining_Identifier, |
| when N_Operator_Symbol => N_Defining_Operator_Symbol, |
| when others => N_Unused_At_Start); -- can't happen |
| -- The new NKind, which is the appropriate value of N_Entity based on |
| -- the old Nkind. N_xxx is mapped to N_Defining_xxx. |
| pragma Assert (New_Kind in N_Entity); |
| |
| -- Start of processing for Extend_Node |
| |
| begin |
| Set_Check_Actuals (Source, False); |
| Mutate_Nkind (Source, New_Kind); |
| Report (Target => Source, Source => Source); |
| end Extend_Node; |
| |
| ----------------- |
| -- Fix_Parents -- |
| ----------------- |
| |
| procedure Fix_Parents (Ref_Node, Fix_Node : Node_Id) is |
| pragma Assert (Nkind (Ref_Node) = Nkind (Fix_Node)); |
| |
| procedure Fix_Parent (Field : Union_Id); |
| -- Fix up one parent pointer. Field is checked to see if it points to |
| -- a node, list, or element list that has a parent that points to |
| -- Ref_Node. If so, the parent is reset to point to Fix_Node. |
| |
| ---------------- |
| -- Fix_Parent -- |
| ---------------- |
| |
| procedure Fix_Parent (Field : Union_Id) is |
| begin |
| -- Fix parent of node that is referenced by Field. Note that we must |
| -- exclude the case where the node is a member of a list, because in |
| -- this case the parent is the parent of the list. |
| |
| if Field in Node_Range |
| and then Present (Node_Id (Field)) |
| and then not In_List (Node_Id (Field)) |
| and then Parent (Node_Id (Field)) = Ref_Node |
| then |
| Set_Parent (Node_Id (Field), Fix_Node); |
| |
| -- Fix parent of list that is referenced by Field |
| |
| elsif Field in List_Range |
| and then Present (List_Id (Field)) |
| and then Parent (List_Id (Field)) = Ref_Node |
| then |
| Set_Parent (List_Id (Field), Fix_Node); |
| end if; |
| end Fix_Parent; |
| |
| Fields : Node_Field_Array renames |
| Node_Field_Table (Nkind (Fix_Node)).all; |
| |
| -- Start of processing for Fix_Parents |
| |
| begin |
| for J in Fields'Range loop |
| declare |
| Desc : Field_Descriptor renames Field_Descriptors (Fields (J)); |
| begin |
| if Desc.Kind in Node_Id_Field | List_Id_Field then |
| Fix_Parent (Get_Node_Field_Union (Fix_Node, Desc.Offset)); |
| end if; |
| end; |
| end loop; |
| end Fix_Parents; |
| |
| ----------------------------------- |
| -- Get_Comes_From_Source_Default -- |
| ----------------------------------- |
| |
| function Get_Comes_From_Source_Default return Boolean is |
| begin |
| return Comes_From_Source_Default; |
| end Get_Comes_From_Source_Default; |
| |
| --------------- |
| -- Is_Entity -- |
| --------------- |
| |
| function Is_Entity (N : Node_Or_Entity_Id) return Boolean is |
| begin |
| return Nkind (N) in N_Entity; |
| end Is_Entity; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize is |
| Dummy : Node_Id; |
| pragma Warnings (Off, Dummy); |
| |
| begin |
| -- Allocate Empty node |
| |
| Dummy := New_Node (N_Empty, No_Location); |
| Set_Chars (Empty, No_Name); |
| pragma Assert (Dummy = Empty); |
| |
| -- Allocate Error node, and set Error_Posted, since we certainly |
| -- only generate an Error node if we do post some kind of error. |
| |
| Dummy := New_Node (N_Error, No_Location); |
| Set_Chars (Error, Error_Name); |
| Set_Error_Posted (Error, True); |
| pragma Assert (Dummy = Error); |
| end Initialize; |
| |
| -------------------------- |
| -- Is_Rewrite_Insertion -- |
| -------------------------- |
| |
| function Is_Rewrite_Insertion (Node : Node_Id) return Boolean is |
| begin |
| return Rewrite_Ins (Node); |
| end Is_Rewrite_Insertion; |
| |
| ----------------------------- |
| -- Is_Rewrite_Substitution -- |
| ----------------------------- |
| |
| function Is_Rewrite_Substitution (Node : Node_Id) return Boolean is |
| begin |
| return Orig_Nodes.Table (Node) /= Node; |
| end Is_Rewrite_Substitution; |
| |
| ------------------ |
| -- Last_Node_Id -- |
| ------------------ |
| |
| function Last_Node_Id return Node_Id is |
| begin |
| return Node_Offsets.Last; |
| end Last_Node_Id; |
| |
| ---------- |
| -- Lock -- |
| ---------- |
| |
| procedure Lock is |
| begin |
| Orig_Nodes.Locked := True; |
| end Lock; |
| |
| ---------------- |
| -- Lock_Nodes -- |
| ---------------- |
| |
| procedure Lock_Nodes is |
| begin |
| pragma Assert (not Locked); |
| Locked := True; |
| end Lock_Nodes; |
| |
| ------------------------- |
| -- Mark_New_Ghost_Node -- |
| ------------------------- |
| |
| procedure Mark_New_Ghost_Node (N : Node_Or_Entity_Id) is |
| begin |
| pragma Debug (Validate_Node_Write (N)); |
| |
| -- The Ghost node is created within a Ghost region |
| |
| if Ghost_Mode = Check then |
| if Nkind (N) in N_Entity then |
| Set_Is_Checked_Ghost_Entity (N); |
| end if; |
| |
| elsif Ghost_Mode = Ignore then |
| if Nkind (N) in N_Entity then |
| Set_Is_Ignored_Ghost_Entity (N); |
| end if; |
| |
| Set_Is_Ignored_Ghost_Node (N); |
| |
| -- Record the ignored Ghost node or entity in order to eliminate it |
| -- from the tree later. |
| |
| if Ignored_Ghost_Recording_Proc /= null then |
| Ignored_Ghost_Recording_Proc.all (N); |
| end if; |
| end if; |
| end Mark_New_Ghost_Node; |
| |
| ---------------------------- |
| -- Mark_Rewrite_Insertion -- |
| ---------------------------- |
| |
| procedure Mark_Rewrite_Insertion (New_Node : Node_Id) is |
| begin |
| Set_Rewrite_Ins (New_Node); |
| end Mark_Rewrite_Insertion; |
| |
| -------------- |
| -- New_Copy -- |
| -------------- |
| |
| function New_Copy (Source : Node_Id) return Node_Id is |
| pragma Debug (Validate_Node (Source)); |
| S_Size : constant Slot_Count := Size_In_Slots_To_Alloc (Source); |
| begin |
| if Source <= Empty_Or_Error then |
| return Source; |
| end if; |
| |
| return New_Id : constant Node_Id := Alloc_Node_Id do |
| Node_Offsets.Table (New_Id).Offset := |
| Alloc_Slots (S_Size) - N_Head; |
| Orig_Nodes.Append (New_Id); |
| Copy_Slots (Source, New_Id); |
| |
| Set_Check_Actuals (New_Id, False); |
| Set_Paren_Count_Of_Copy (Target => New_Id, Source => Source); |
| |
| Allocate_List_Tables (New_Id); |
| Report (Target => New_Id, Source => Source); |
| |
| Set_In_List (New_Id, False); |
| Set_Link (New_Id, Empty_List_Or_Node); |
| |
| -- If the original is marked as a rewrite insertion, then unmark the |
| -- copy, since we inserted the original, not the copy. |
| |
| Set_Rewrite_Ins (New_Id, False); |
| |
| -- Clear Is_Overloaded since we cannot have semantic interpretations |
| -- of this new node. |
| |
| if Nkind (Source) in N_Subexpr then |
| Set_Is_Overloaded (New_Id, False); |
| end if; |
| |
| -- Always clear Has_Aspects, the caller must take care of copying |
| -- aspects if this is required for the particular situation. |
| |
| Set_Has_Aspects (New_Id, False); |
| |
| -- Mark the copy as Ghost depending on the current Ghost region |
| |
| Mark_New_Ghost_Node (New_Id); |
| |
| New_Node_Debugging_Output (New_Id); |
| |
| pragma Assert (New_Id /= Source); |
| end return; |
| end New_Copy; |
| |
| ---------------- |
| -- New_Entity -- |
| ---------------- |
| |
| function New_Entity |
| (New_Node_Kind : Node_Kind; |
| New_Sloc : Source_Ptr) return Entity_Id |
| is |
| pragma Assert (New_Node_Kind in N_Entity); |
| New_Id : constant Entity_Id := Allocate_New_Node (New_Node_Kind); |
| pragma Assert (Original_Node (Node_Offsets.Last) = Node_Offsets.Last); |
| begin |
| -- If this is a node with a real location and we are generating |
| -- source nodes, then reset Current_Error_Node. This is useful |
| -- if we bomb during parsing to get a error location for the bomb. |
| |
| if New_Sloc > No_Location and then Comes_From_Source_Default then |
| Current_Error_Node := New_Id; |
| end if; |
| |
| Set_Sloc (New_Id, New_Sloc); |
| |
| -- Mark the new entity as Ghost depending on the current Ghost region |
| |
| Mark_New_Ghost_Node (New_Id); |
| |
| New_Node_Debugging_Output (New_Id); |
| |
| return New_Id; |
| end New_Entity; |
| |
| -------------- |
| -- New_Node -- |
| -------------- |
| |
| function New_Node |
| (New_Node_Kind : Node_Kind; |
| New_Sloc : Source_Ptr) return Node_Id |
| is |
| pragma Assert (New_Node_Kind not in N_Entity); |
| New_Id : constant Node_Id := Allocate_New_Node (New_Node_Kind); |
| pragma Assert (Original_Node (Node_Offsets.Last) = Node_Offsets.Last); |
| begin |
| Set_Sloc (New_Id, New_Sloc); |
| |
| -- If this is a node with a real location and we are generating source |
| -- nodes, then reset Current_Error_Node. This is useful if we bomb |
| -- during parsing to get an error location for the bomb. |
| |
| if Comes_From_Source_Default and then New_Sloc > No_Location then |
| Current_Error_Node := New_Id; |
| end if; |
| |
| -- Mark the new node as Ghost depending on the current Ghost region |
| |
| Mark_New_Ghost_Node (New_Id); |
| |
| New_Node_Debugging_Output (New_Id); |
| |
| return New_Id; |
| end New_Node; |
| |
| -------- |
| -- No -- |
| -------- |
| |
| function No (N : Node_Id) return Boolean is |
| begin |
| return N = Empty; |
| end No; |
| |
| ------------------- |
| -- Nodes_Address -- |
| ------------------- |
| |
| function Node_Offsets_Address return System.Address is |
| begin |
| return Node_Offsets.Table (First_Node_Id)'Address; |
| end Node_Offsets_Address; |
| |
| function Slots_Address return System.Address is |
| Slot_Byte_Size : constant := 4; |
| pragma Assert (Slot_Byte_Size * 8 = Slot'Size); |
| Extra : constant := Slots_Low_Bound * Slot_Byte_Size; |
| -- Slots does not start at 0, so we need to subtract off the extra |
| -- amount. We are returning Slots.Table (0)'Address, except that |
| -- that component does not exist. |
| use System.Storage_Elements; |
| begin |
| return Slots.Table (Slots_Low_Bound)'Address - Extra; |
| end Slots_Address; |
| |
| ----------------------------------- |
| -- Approx_Num_Nodes_And_Entities -- |
| ----------------------------------- |
| |
| function Approx_Num_Nodes_And_Entities return Nat is |
| begin |
| return Nat (Node_Offsets.Last - First_Node_Id); |
| end Approx_Num_Nodes_And_Entities; |
| |
| ----------- |
| -- Off_0 -- |
| ----------- |
| |
| function Off_0 (N : Node_Id) return Node_Offset'Base is |
| pragma Debug (Validate_Node (N)); |
| |
| All_Node_Offsets : Node_Offsets.Table_Type renames |
| Node_Offsets.Table (Node_Offsets.First .. Node_Offsets.Last); |
| begin |
| return All_Node_Offsets (N).Offset; |
| end Off_0; |
| |
| ----------- |
| -- Off_F -- |
| ----------- |
| |
| function Off_F (N : Node_Id) return Node_Offset is |
| begin |
| return Off_0 (N) + N_Head; |
| end Off_F; |
| |
| ----------- |
| -- Off_L -- |
| ----------- |
| |
| function Off_L (N : Node_Id) return Node_Offset is |
| pragma Debug (Validate_Node (N)); |
| |
| All_Node_Offsets : Node_Offsets.Table_Type renames |
| Node_Offsets.Table (Node_Offsets.First .. Node_Offsets.Last); |
| begin |
| return All_Node_Offsets (N).Offset + Size_In_Slots (N) - 1; |
| end Off_L; |
| |
| ------------------- |
| -- Original_Node -- |
| ------------------- |
| |
| function Original_Node (Node : Node_Id) return Node_Id is |
| begin |
| pragma Debug (Validate_Node (Node)); |
| if Atree_Statistics_Enabled then |
| Get_Original_Node_Count := Get_Original_Node_Count + 1; |
| end if; |
| |
| return Orig_Nodes.Table (Node); |
| end Original_Node; |
| |
| ----------------- |
| -- Paren_Count -- |
| ----------------- |
| |
| function Paren_Count (N : Node_Id) return Nat is |
| pragma Debug (Validate_Node (N)); |
| |
| C : constant Small_Paren_Count_Type := Small_Paren_Count (N); |
| |
| begin |
| -- Value of 0,1,2 returned as is |
| |
| if C <= 2 then |
| return C; |
| |
| -- Value of 3 means we search the table, and we must find an entry |
| |
| else |
| for J in Paren_Counts.First .. Paren_Counts.Last loop |
| if N = Paren_Counts.Table (J).Nod then |
| return Paren_Counts.Table (J).Count; |
| end if; |
| end loop; |
| |
| raise Program_Error; |
| end if; |
| end Paren_Count; |
| |
| function Parent (N : Node_Or_Entity_Id) return Node_Or_Entity_Id is |
| begin |
| pragma Assert (Present (N)); |
| |
| if Is_List_Member (N) then |
| return Parent (List_Containing (N)); |
| else |
| return Node_Or_Entity_Id (Link (N)); |
| end if; |
| end Parent; |
| |
| ------------- |
| -- Present -- |
| ------------- |
| |
| function Present (N : Node_Id) return Boolean is |
| begin |
| return N /= Empty; |
| end Present; |
| |
| -------------------------------- |
| -- Preserve_Comes_From_Source -- |
| -------------------------------- |
| |
| procedure Preserve_Comes_From_Source (NewN, OldN : Node_Id) is |
| begin |
| Set_Comes_From_Source (NewN, Comes_From_Source (OldN)); |
| end Preserve_Comes_From_Source; |
| |
| ------------------- |
| -- Relocate_Node -- |
| ------------------- |
| |
| function Relocate_Node (Source : Node_Id) return Node_Id is |
| New_Node : Node_Id; |
| |
| begin |
| if No (Source) then |
| return Empty; |
| end if; |
| |
| New_Node := New_Copy (Source); |
| Fix_Parents (Ref_Node => Source, Fix_Node => New_Node); |
| |
| -- We now set the parent of the new node to be the same as the parent of |
| -- the source. Almost always this parent will be replaced by a new value |
| -- when the relocated node is reattached to the tree, but by doing it |
| -- now, we ensure that this node is not even temporarily disconnected |
| -- from the tree. Note that this does not happen free, because in the |
| -- list case, the parent does not get set. |
| |
| Set_Parent (New_Node, Parent (Source)); |
| |
| -- If the node being relocated was a rewriting of some original node, |
| -- then the relocated node has the same original node. |
| |
| if Is_Rewrite_Substitution (Source) then |
| Set_Original_Node (New_Node, Original_Node (Source)); |
| end if; |
| |
| -- If we're relocating a subprogram call and we're doing |
| -- unnesting, be sure we make a new copy of any parameter associations |
| -- so that we don't share them. |
| |
| if Nkind (Source) in N_Subprogram_Call |
| and then Opt.Unnest_Subprogram_Mode |
| and then Present (Parameter_Associations (Source)) |
| then |
| declare |
| New_Assoc : constant List_Id := Parameter_Associations (Source); |
| begin |
| Set_Parent (New_Assoc, New_Node); |
| Set_Parameter_Associations (New_Node, New_Assoc); |
| end; |
| end if; |
| |
| return New_Node; |
| end Relocate_Node; |
| |
| ------------- |
| -- Replace -- |
| ------------- |
| |
| procedure Replace (Old_Node, New_Node : Node_Id) is |
| Old_Post : constant Boolean := Error_Posted (Old_Node); |
| Old_HasA : constant Boolean := Has_Aspects (Old_Node); |
| Old_CFS : constant Boolean := Comes_From_Source (Old_Node); |
| |
| procedure Destroy_New_Node; |
| -- Overwrite New_Node data with junk, for debugging purposes |
| |
| procedure Destroy_New_Node is |
| begin |
| Zero_Slots (New_Node); |
| Node_Offsets.Table (New_Node).Offset := Field_Offset'Base'Last; |
| end Destroy_New_Node; |
| |
| begin |
| New_Node_Debugging_Output (Old_Node); |
| New_Node_Debugging_Output (New_Node); |
| |
| pragma Assert |
| (not Is_Entity (Old_Node) |
| and not Is_Entity (New_Node) |
| and not In_List (New_Node) |
| and Old_Node /= New_Node); |
| |
| -- Do copy, preserving link and in list status and required flags |
| |
| Copy_Node (Source => New_Node, Destination => Old_Node); |
| Set_Comes_From_Source (Old_Node, Old_CFS); |
| Set_Error_Posted (Old_Node, Old_Post); |
| Set_Has_Aspects (Old_Node, Old_HasA); |
| |
| -- Fix parents of substituted node, since it has changed identity |
| |
| Fix_Parents (Ref_Node => New_Node, Fix_Node => Old_Node); |
| |
| pragma Debug (Destroy_New_Node); |
| |
| -- Since we are doing a replace, we assume that the original node |
| -- is intended to become the new replaced node. The call would be |
| -- to Rewrite if there were an intention to save the original node. |
| |
| Set_Original_Node (Old_Node, Old_Node); |
| |
| -- Invoke the reporting procedure (if available) |
| |
| if Reporting_Proc /= null then |
| Reporting_Proc.all (Target => Old_Node, Source => New_Node); |
| end if; |
| end Replace; |
| |
| ------------ |
| -- Report -- |
| ------------ |
| |
| procedure Report (Target, Source : Node_Id) is |
| begin |
| if Reporting_Proc /= null then |
| Reporting_Proc.all (Target, Source); |
| end if; |
| end Report; |
| |
| ------------- |
| -- Rewrite -- |
| ------------- |
| |
| procedure Rewrite (Old_Node, New_Node : Node_Id) is |
| Old_CA : constant Boolean := Check_Actuals (Old_Node); |
| Old_Is_IGN : constant Boolean := Is_Ignored_Ghost_Node (Old_Node); |
| Old_Error_Posted : constant Boolean := |
| Error_Posted (Old_Node); |
| Old_Has_Aspects : constant Boolean := |
| Has_Aspects (Old_Node); |
| |
| Old_Must_Not_Freeze : constant Boolean := |
| (if Nkind (Old_Node) in N_Subexpr then Must_Not_Freeze (Old_Node) |
| else False); |
| Old_Paren_Count : constant Nat := |
| (if Nkind (Old_Node) in N_Subexpr then Paren_Count (Old_Node) else 0); |
| -- These fields are preserved in the new node only if the new node and |
| -- the old node are both subexpression nodes. We might be changing Nkind |
| -- (Old_Node) from not N_Subexpr to N_Subexpr, so we need a value |
| -- (False/0) even if Old_Noed is not a N_Subexpr. |
| |
| -- Note: it is a violation of abstraction levels for Must_Not_Freeze |
| -- to be referenced like this. ??? |
| |
| Sav_Node : Node_Id; |
| |
| begin |
| New_Node_Debugging_Output (Old_Node); |
| New_Node_Debugging_Output (New_Node); |
| |
| pragma Assert |
| (not Is_Entity (Old_Node) |
| and not Is_Entity (New_Node) |
| and not In_List (New_Node)); |
| |
| -- Allocate a new node, to be used to preserve the original contents |
| -- of the Old_Node, for possible later retrival by Original_Node and |
| -- make an entry in the Orig_Nodes table. This is only done if we have |
| -- not already rewritten the node, as indicated by an Orig_Nodes entry |
| -- that does not reference the Old_Node. |
| |
| if Original_Node (Old_Node) = Old_Node then |
| Sav_Node := New_Copy (Old_Node); |
| Set_Original_Node (Sav_Node, Sav_Node); |
| Set_Original_Node (Old_Node, Sav_Node); |
| |
| -- Both the old and new copies of the node will share the same list |
| -- of aspect specifications if aspect specifications are present. |
| -- Restore the parent link of the aspect list to the old node, which |
| -- is the one linked in the tree. |
| |
| if Old_Has_Aspects then |
| declare |
| Aspects : constant List_Id := Aspect_Specifications (Old_Node); |
| begin |
| Set_Aspect_Specifications (Sav_Node, Aspects); |
| Set_Parent (Aspects, Old_Node); |
| end; |
| end if; |
| end if; |
| |
| -- Copy substitute node into place, preserving old fields as required |
| |
| Copy_Node (Source => New_Node, Destination => Old_Node); |
| Set_Error_Posted (Old_Node, Old_Error_Posted); |
| Set_Has_Aspects (Old_Node, Old_Has_Aspects); |
| |
| Set_Check_Actuals (Old_Node, Old_CA); |
| Set_Is_Ignored_Ghost_Node (Old_Node, Old_Is_IGN); |
| |
| if Nkind (New_Node) in N_Subexpr then |
| Set_Paren_Count (Old_Node, Old_Paren_Count); |
| Set_Must_Not_Freeze (Old_Node, Old_Must_Not_Freeze); |
| end if; |
| |
| Fix_Parents (Ref_Node => New_Node, Fix_Node => Old_Node); |
| |
| -- Invoke the reporting procedure (if available) |
| |
| if Reporting_Proc /= null then |
| Reporting_Proc.all (Target => Old_Node, Source => New_Node); |
| end if; |
| |
| -- Invoke the rewriting procedure (if available) |
| |
| if Rewriting_Proc /= null then |
| Rewriting_Proc.all (Target => Old_Node, Source => New_Node); |
| end if; |
| end Rewrite; |
| |
| ----------------------------------- |
| -- Set_Comes_From_Source_Default -- |
| ----------------------------------- |
| |
| procedure Set_Comes_From_Source_Default (Default : Boolean) is |
| begin |
| Comes_From_Source_Default := Default; |
| end Set_Comes_From_Source_Default; |
| |
| -------------------------------------- |
| -- Set_Ignored_Ghost_Recording_Proc -- |
| -------------------------------------- |
| |
| procedure Set_Ignored_Ghost_Recording_Proc |
| (Proc : Ignored_Ghost_Record_Proc) |
| is |
| begin |
| pragma Assert (Ignored_Ghost_Recording_Proc = null); |
| Ignored_Ghost_Recording_Proc := Proc; |
| end Set_Ignored_Ghost_Recording_Proc; |
| |
| ----------------------- |
| -- Set_Original_Node -- |
| ----------------------- |
| |
| procedure Set_Original_Node (N : Node_Id; Val : Node_Id) is |
| begin |
| pragma Debug (Validate_Node_Write (N)); |
| if Atree_Statistics_Enabled then |
| Set_Original_Node_Count := Set_Original_Node_Count + 1; |
| end if; |
| |
| Orig_Nodes.Table (N) := Val; |
| end Set_Original_Node; |
| |
| --------------------- |
| -- Set_Paren_Count -- |
| --------------------- |
| |
| procedure Set_Paren_Count (N : Node_Id; Val : Nat) is |
| begin |
| pragma Debug (Validate_Node_Write (N)); |
| pragma Assert (Nkind (N) in N_Subexpr); |
| |
| -- Value of 0,1,2 stored as is |
| |
| if Val <= 2 then |
| Set_Small_Paren_Count (N, Val); |
| |
| -- Value of 3 or greater stores 3 in node and makes table entry |
| |
| else |
| Set_Small_Paren_Count (N, 3); |
| |
| -- Search for existing table entry |
| |
| for J in Paren_Counts.First .. Paren_Counts.Last loop |
| if N = Paren_Counts.Table (J).Nod then |
| Paren_Counts.Table (J).Count := Val; |
| return; |
| end if; |
| end loop; |
| |
| -- No existing table entry; make a new one |
| |
| Paren_Counts.Append ((Nod => N, Count => Val)); |
| end if; |
| end Set_Paren_Count; |
| |
| ----------------------------- |
| -- Set_Paren_Count_Of_Copy -- |
| ----------------------------- |
| |
| procedure Set_Paren_Count_Of_Copy (Target, Source : Node_Id) is |
| begin |
| -- We already copied the Small_Paren_Count. We need to update the |
| -- Paren_Counts table only if greater than 2. |
| |
| if Nkind (Source) in N_Subexpr |
| and then Small_Paren_Count (Source) = 3 |
| then |
| Set_Paren_Count (Target, Paren_Count (Source)); |
| end if; |
| |
| pragma Assert (Paren_Count (Target) = Paren_Count (Source)); |
| end Set_Paren_Count_Of_Copy; |
| |
| ---------------- |
| -- Set_Parent -- |
| ---------------- |
| |
| procedure Set_Parent (N : Node_Or_Entity_Id; Val : Node_Or_Entity_Id) is |
| begin |
| pragma Assert (Present (N)); |
| pragma Assert (not In_List (N)); |
| Set_Link (N, Union_Id (Val)); |
| end Set_Parent; |
| |
| ------------------------ |
| -- Set_Reporting_Proc -- |
| ------------------------ |
| |
| procedure Set_Reporting_Proc (Proc : Report_Proc) is |
| begin |
| pragma Assert (Reporting_Proc = null); |
| Reporting_Proc := Proc; |
| end Set_Reporting_Proc; |
| |
| ------------------------ |
| -- Set_Rewriting_Proc -- |
| ------------------------ |
| |
| procedure Set_Rewriting_Proc (Proc : Rewrite_Proc) is |
| begin |
| pragma Assert (Rewriting_Proc = null); |
| Rewriting_Proc := Proc; |
| end Set_Rewriting_Proc; |
| |
| ---------------------------- |
| -- Size_In_Slots_To_Alloc -- |
| ---------------------------- |
| |
| function Size_In_Slots_To_Alloc (Kind : Node_Kind) return Slot_Count is |
| begin |
| return |
| (if Kind in N_Entity then Einfo.Entities.Max_Entity_Size |
| else Sinfo.Nodes.Size (Kind)) - N_Head; |
| -- Unfortunately, we don't know the Entity_Kind, so we have to use the |
| -- max. |
| end Size_In_Slots_To_Alloc; |
| |
| function Size_In_Slots_To_Alloc |
| (N : Node_Or_Entity_Id) return Slot_Count is |
| begin |
| return Size_In_Slots_To_Alloc (Nkind (N)); |
| end Size_In_Slots_To_Alloc; |
| |
| ------------------- |
| -- Size_In_Slots -- |
| ------------------- |
| |
| function Size_In_Slots (N : Node_Or_Entity_Id) return Slot_Count is |
| begin |
| pragma Assert (Nkind (N) /= N_Unused_At_Start); |
| return |
| (if Nkind (N) in N_Entity then Einfo.Entities.Max_Entity_Size |
| else Sinfo.Nodes.Size (Nkind (N))); |
| end Size_In_Slots; |
| |
| --------------------------- |
| -- Size_In_Slots_Dynamic -- |
| --------------------------- |
| |
| function Size_In_Slots_Dynamic (N : Node_Or_Entity_Id) return Slot_Count is |
| begin |
| return Size_In_Slots (N) - N_Head; |
| end Size_In_Slots_Dynamic; |
| |
| ----------------------------------- |
| -- Internal_Traverse_With_Parent -- |
| ----------------------------------- |
| |
| function Internal_Traverse_With_Parent |
| (Node : Node_Id) return Traverse_Final_Result |
| is |
| Tail_Recursion_Counter : Natural := 0; |
| |
| procedure Pop_Parents; |
| -- Pop enclosing nodes of tail recursion plus the current parent. |
| |
| function Traverse_Field (Fld : Union_Id) return Traverse_Final_Result; |
| -- Fld is one of the Traversed fields of Nod, which is necessarily a |
| -- Node_Id or List_Id. It is traversed, and the result is the result of |
| -- this traversal. |
| |
| ----------------- |
| -- Pop_Parents -- |
| ----------------- |
| |
| procedure Pop_Parents is |
| begin |
| -- Pop the enclosing nodes of the tail recursion |
| |
| for J in 1 .. Tail_Recursion_Counter loop |
| Parents_Stack.Decrement_Last; |
| end loop; |
| |
| -- Pop the current node |
| |
| pragma Assert (Parents_Stack.Table (Parents_Stack.Last) = Node); |
| Parents_Stack.Decrement_Last; |
| end Pop_Parents; |
| |
| -------------------- |
| -- Traverse_Field -- |
| -------------------- |
| |
| function Traverse_Field (Fld : Union_Id) return Traverse_Final_Result is |
| begin |
| if Fld /= Union_Id (Empty) then |
| |
| -- Descendant is a node |
| |
| if Fld in Node_Range then |
| return Internal_Traverse_With_Parent (Node_Id (Fld)); |
| |
| -- Descendant is a list |
| |
| elsif Fld in List_Range then |
| declare |
| Elmt : Node_Id := First (List_Id (Fld)); |
| begin |
| while Present (Elmt) loop |
| if Internal_Traverse_With_Parent (Elmt) = Abandon then |
| return Abandon; |
| end if; |
| |
| Next (Elmt); |
| end loop; |
| end; |
| |
| else |
| raise Program_Error; |
| end if; |
| end if; |
| |
| return OK; |
| end Traverse_Field; |
| |
| -- Local variables |
| |
| Parent_Node : Node_Id := Parents_Stack.Table (Parents_Stack.Last); |
| Cur_Node : Node_Id := Node; |
| |
| -- Start of processing for Internal_Traverse_With_Parent |
| |
| begin |
| -- If the last field is a node, we eliminate the tail recursion by |
| -- jumping back to this label. This is because concatenations are |
| -- sometimes deeply nested, as in X1&X2&...&Xn. Gen_IL ensures that the |
| -- Left_Opnd field of N_Op_Concat comes last in Traversed_Fields, so the |
| -- tail recursion is eliminated in that case. This trick prevents us |
| -- from running out of stack memory in that case. We don't bother |
| -- eliminating the tail recursion if the last field is a list. |
| |
| <<Tail_Recurse>> |
| |
| Parents_Stack.Append (Cur_Node); |
| |
| case Process (Parent_Node, Cur_Node) is |
| when Abandon => |
| Pop_Parents; |
| return Abandon; |
| |
| when Skip => |
| Pop_Parents; |
| return OK; |
| |
| when OK => |
| null; |
| |
| when OK_Orig => |
| Cur_Node := Original_Node (Cur_Node); |
| end case; |
| |
| -- Check for empty Traversed_Fields before entering loop below, so the |
| -- tail recursive step won't go past the end. |
| |
| declare |
| Cur_Field : Offset_Array_Index := Traversed_Offset_Array'First; |
| Offsets : Traversed_Offset_Array renames |
| Traversed_Fields (Nkind (Cur_Node)); |
| |
| begin |
| if Offsets (Traversed_Offset_Array'First) /= No_Field_Offset then |
| while Offsets (Cur_Field + 1) /= No_Field_Offset loop |
| declare |
| F : constant Union_Id := |
| Get_Node_Field_Union (Cur_Node, Offsets (Cur_Field)); |
| |
| begin |
| if Traverse_Field (F) = Abandon then |
| Pop_Parents; |
| return Abandon; |
| end if; |
| end; |
| |
| Cur_Field := Cur_Field + 1; |
| end loop; |
| |
| declare |
| F : constant Union_Id := |
| Get_Node_Field_Union (Cur_Node, Offsets (Cur_Field)); |
| |
| begin |
| if F not in Node_Range then |
| if Traverse_Field (F) = Abandon then |
| Pop_Parents; |
| return Abandon; |
| end if; |
| |
| elsif F /= Empty_List_Or_Node then |
| -- Here is the tail recursion step, we reset Cur_Node and |
| -- jump back to the start of the procedure, which has the |
| -- same semantic effect as a call. |
| |
| Tail_Recursion_Counter := Tail_Recursion_Counter + 1; |
| Parent_Node := Cur_Node; |
| Cur_Node := Node_Id (F); |
| goto Tail_Recurse; |
| end if; |
| end; |
| end if; |
| end; |
| |
| Pop_Parents; |
| return OK; |
| end Internal_Traverse_With_Parent; |
| |
| ------------------- |
| -- Traverse_Func -- |
| ------------------- |
| |
| function Traverse_Func (Node : Node_Id) return Traverse_Final_Result is |
| pragma Debug (Validate_Node (Node)); |
| |
| function Traverse_Field (Fld : Union_Id) return Traverse_Final_Result; |
| -- Fld is one of the Traversed fields of Nod, which is necessarily a |
| -- Node_Id or List_Id. It is traversed, and the result is the result of |
| -- this traversal. |
| |
| -------------------- |
| -- Traverse_Field -- |
| -------------------- |
| |
| function Traverse_Field (Fld : Union_Id) return Traverse_Final_Result is |
| begin |
| if Fld /= Union_Id (Empty) then |
| |
| -- Descendant is a node |
| |
| if Fld in Node_Range then |
| return Traverse_Func (Node_Id (Fld)); |
| |
| -- Descendant is a list |
| |
| elsif Fld in List_Range then |
| declare |
| Elmt : Node_Id := First (List_Id (Fld)); |
| begin |
| while Present (Elmt) loop |
| if Traverse_Func (Elmt) = Abandon then |
| return Abandon; |
| end if; |
| |
| Next (Elmt); |
| end loop; |
| end; |
| |
| else |
| raise Program_Error; |
| end if; |
| end if; |
| |
| return OK; |
| end Traverse_Field; |
| |
| Cur_Node : Node_Id := Node; |
| |
| -- Start of processing for Traverse_Func |
| |
| begin |
| -- If the last field is a node, we eliminate the tail recursion by |
| -- jumping back to this label. This is because concatenations are |
| -- sometimes deeply nested, as in X1&X2&...&Xn. Gen_IL ensures that the |
| -- Left_Opnd field of N_Op_Concat comes last in Traversed_Fields, so the |
| -- tail recursion is eliminated in that case. This trick prevents us |
| -- from running out of stack memory in that case. We don't bother |
| -- eliminating the tail recursion if the last field is a list. |
| -- |
| -- (To check, look in the body of Sinfo.Nodes, search for the Left_Opnd |
| -- getter, and note the offset of Left_Opnd. Then look in the spec of |
| -- Sinfo.Nodes, look at the Traversed_Fields table, search for the |
| -- N_Op_Concat component. The offset of Left_Opnd should be the last |
| -- component before the No_Field_Offset sentinels.) |
| |
| <<Tail_Recurse>> |
| |
| case Process (Cur_Node) is |
| when Abandon => |
| return Abandon; |
| |
| when Skip => |
| return OK; |
| |
| when OK => |
| null; |
| |
| when OK_Orig => |
| Cur_Node := Original_Node (Cur_Node); |
| end case; |
| |
| -- Check for empty Traversed_Fields before entering loop below, so the |
| -- tail recursive step won't go past the end. |
| |
| declare |
| Cur_Field : Offset_Array_Index := Traversed_Offset_Array'First; |
| Offsets : Traversed_Offset_Array renames |
| Traversed_Fields (Nkind (Cur_Node)); |
| |
| begin |
| if Offsets (Traversed_Offset_Array'First) /= No_Field_Offset then |
| while Offsets (Cur_Field + 1) /= No_Field_Offset loop |
| declare |
| F : constant Union_Id := |
| Get_Node_Field_Union (Cur_Node, Offsets (Cur_Field)); |
| |
| begin |
| if Traverse_Field (F) = Abandon then |
| return Abandon; |
| end if; |
| end; |
| |
| Cur_Field := Cur_Field + 1; |
| end loop; |
| |
| declare |
| F : constant Union_Id := |
| Get_Node_Field_Union (Cur_Node, Offsets (Cur_Field)); |
| |
| begin |
| if F not in Node_Range then |
| if Traverse_Field (F) = Abandon then |
| return Abandon; |
| end if; |
| |
| elsif F /= Empty_List_Or_Node then |
| -- Here is the tail recursion step, we reset Cur_Node and |
| -- jump back to the start of the procedure, which has the |
| -- same semantic effect as a call. |
| |
| Cur_Node := Node_Id (F); |
| goto Tail_Recurse; |
| end if; |
| end; |
| end if; |
| end; |
| |
| return OK; |
| end Traverse_Func; |
| |
| ------------------------------- |
| -- Traverse_Func_With_Parent -- |
| ------------------------------- |
| |
| function Traverse_Func_With_Parent |
| (Node : Node_Id) return Traverse_Final_Result |
| is |
| function Traverse is new Internal_Traverse_With_Parent (Process); |
| Result : Traverse_Final_Result; |
| begin |
| -- Ensure that the Parents stack is not currently in use; required since |
| -- it is global and hence a tree traversal with parents must be finished |
| -- before the next tree traversal with parents starts. |
| |
| pragma Assert (Parents_Stack.Last = 0); |
| Parents_Stack.Set_Last (0); |
| |
| Parents_Stack.Append (Parent (Node)); |
| Result := Traverse (Node); |
| Parents_Stack.Decrement_Last; |
| |
| pragma Assert (Parents_Stack.Last = 0); |
| |
| return Result; |
| end Traverse_Func_With_Parent; |
| |
| ------------------- |
| -- Traverse_Proc -- |
| ------------------- |
| |
| procedure Traverse_Proc (Node : Node_Id) is |
| function Traverse is new Traverse_Func (Process); |
| Discard : Traverse_Final_Result; |
| pragma Warnings (Off, Discard); |
| begin |
| Discard := Traverse (Node); |
| end Traverse_Proc; |
| |
| ------------------------------- |
| -- Traverse_Proc_With_Parent -- |
| ------------------------------- |
| |
| procedure Traverse_Proc_With_Parent (Node : Node_Id) is |
| function Traverse is new Traverse_Func_With_Parent (Process); |
| Discard : Traverse_Final_Result; |
| pragma Warnings (Off, Discard); |
| begin |
| Discard := Traverse (Node); |
| end Traverse_Proc_With_Parent; |
| |
| ------------ |
| -- Unlock -- |
| ------------ |
| |
| procedure Unlock is |
| begin |
| Orig_Nodes.Locked := False; |
| end Unlock; |
| |
| ------------------ |
| -- Unlock_Nodes -- |
| ------------------ |
| |
| procedure Unlock_Nodes is |
| begin |
| pragma Assert (Locked); |
| Locked := False; |
| end Unlock_Nodes; |
| |
| ---------------- |
| -- Zero_Slots -- |
| ---------------- |
| |
| procedure Zero_Dynamic_Slots (First, Last : Node_Offset'Base) is |
| begin |
| Slots.Table (First .. Last) := (others => 0); |
| end Zero_Dynamic_Slots; |
| |
| procedure Zero_Header_Slots (N : Node_Or_Entity_Id) is |
| All_Node_Offsets : Node_Offsets.Table_Type renames |
| Node_Offsets.Table (Node_Offsets.First .. Node_Offsets.Last); |
| begin |
| All_Node_Offsets (N).Slots := (others => 0); |
| end Zero_Header_Slots; |
| |
| procedure Zero_Slots (N : Node_Or_Entity_Id) is |
| begin |
| Zero_Dynamic_Slots (Off_F (N), Off_L (N)); |
| Zero_Header_Slots (N); |
| end Zero_Slots; |
| |
| ---------------------- |
| -- Print_Statistics -- |
| ---------------------- |
| |
| procedure Print_Node_Statistics; |
| procedure Print_Field_Statistics; |
| -- Helpers for Print_Statistics |
| |
| procedure Write_Ratio (X : Nat_64; Y : Pos_64); |
| -- Write the value of (X/Y) without using 'Image (approximately) |
| |
| procedure Write_Ratio (X : Nat_64; Y : Pos_64) is |
| pragma Assert (X <= Y); |
| Ratio : constant Nat := Nat ((Long_Float (X) / Long_Float (Y)) * 1000.0); |
| begin |
| Write_Str (" ("); |
| |
| if Ratio = 0 then |
| Write_Str ("0.000"); |
| elsif Ratio in 1 .. 9 then |
| Write_Str ("0.00"); |
| Write_Int (Ratio); |
| elsif Ratio in 10 .. 99 then |
| Write_Str ("0.0"); |
| Write_Int (Ratio); |
| elsif Ratio in 100 .. 999 then |
| Write_Str ("0."); |
| Write_Int (Ratio); |
| else |
| Write_Int (Ratio / 1000); |
| end if; |
| |
| Write_Str (")"); |
| end Write_Ratio; |
| |
| procedure Print_Node_Statistics is |
| subtype Count is Nat_64; |
| Node_Counts : array (Node_Kind) of Count := (others => 0); |
| Entity_Counts : array (Entity_Kind) of Count := (others => 0); |
| |
| All_Node_Offsets : Node_Offsets.Table_Type renames |
| Node_Offsets.Table (Node_Offsets.First .. Node_Offsets.Last); |
| begin |
| Write_Int (Int (Node_Offsets.Last)); |
| Write_Line (" nodes (including entities)"); |
| Write_Int (Int (Slots.Last)); |
| Write_Line (" non-header slots"); |
| |
| for N in All_Node_Offsets'Range loop |
| declare |
| K : constant Node_Kind := Nkind (N); |
| |
| begin |
| Node_Counts (K) := Node_Counts (K) + 1; |
| |
| if K in N_Entity then |
| Entity_Counts (Ekind (N)) := Entity_Counts (Ekind (N)) + 1; |
| end if; |
| end; |
| end loop; |
| |
| for K in Node_Kind loop |
| declare |
| Count : constant Nat_64 := Node_Counts (K); |
| begin |
| Write_Int_64 (Count); |
| Write_Ratio (Count, Int_64 (Node_Offsets.Last)); |
| Write_Str (" "); |
| Write_Str (Node_Kind'Image (K)); |
| Write_Str (" "); |
| Write_Int (Int (Sinfo.Nodes.Size (K))); |
| Write_Str (" slots"); |
| Write_Eol; |
| end; |
| end loop; |
| |
| for K in Entity_Kind loop |
| declare |
| Count : constant Nat_64 := Entity_Counts (K); |
| begin |
| Write_Int_64 (Count); |
| Write_Ratio (Count, Int_64 (Node_Offsets.Last)); |
| Write_Str (" "); |
| Write_Str (Entity_Kind'Image (K)); |
| Write_Str (" "); |
| Write_Int (Int (Einfo.Entities.Size (K))); |
| Write_Str (" slots"); |
| Write_Eol; |
| end; |
| end loop; |
| end Print_Node_Statistics; |
| |
| procedure Print_Field_Statistics is |
| Total, G_Total, S_Total : Call_Count := 0; |
| begin |
| Write_Int_64 (Get_Original_Node_Count); |
| Write_Str (" + "); |
| Write_Int_64 (Set_Original_Node_Count); |
| Write_Eol; |
| Write_Line (" Original_Node_Count getter and setter calls"); |
| Write_Eol; |
| |
| Write_Line ("Frequency of field getter and setter calls:"); |
| |
| for Field in Node_Or_Entity_Field loop |
| G_Total := G_Total + Get_Count (Field); |
| S_Total := S_Total + Set_Count (Field); |
| Total := G_Total + S_Total; |
| end loop; |
| |
| -- This assertion helps CodePeer understand that Total cannot be 0 (this |
| -- is true because GNAT does not attempt to compile empty files). |
| pragma Assert (Total > 0); |
| |
| Write_Int_64 (Total); |
| Write_Str (" (100%) = "); |
| Write_Int_64 (G_Total); |
| Write_Str (" + "); |
| Write_Int_64 (S_Total); |
| Write_Line (" total getter and setter calls"); |
| |
| for Field in Node_Or_Entity_Field loop |
| declare |
| G : constant Call_Count := Get_Count (Field); |
| S : constant Call_Count := Set_Count (Field); |
| GS : constant Call_Count := G + S; |
| |
| Desc : Field_Descriptor renames Field_Descriptors (Field); |
| Slot : constant Field_Offset := |
| (Field_Size (Desc.Kind) * Desc.Offset) / Slot_Size; |
| |
| begin |
| Write_Int_64 (GS); |
| Write_Ratio (GS, Total); |
| Write_Str (" = "); |
| Write_Int_64 (G); |
| Write_Str (" + "); |
| Write_Int_64 (S); |
| Write_Str (" "); |
| Write_Str (Node_Or_Entity_Field'Image (Field)); |
| Write_Str (" in slot "); |
| Write_Int (Int (Slot)); |
| Write_Str (" size "); |
| Write_Int (Int (Field_Size (Desc.Kind))); |
| Write_Eol; |
| end; |
| end loop; |
| end Print_Field_Statistics; |
| |
| procedure Print_Statistics is |
| begin |
| Write_Eol; |
| Write_Eol; |
| Print_Node_Statistics; |
| Print_Field_Statistics; |
| end Print_Statistics; |
| |
| end Atree; |