blob: e6dc4199ae6f3323143eb4e76d86aa004585ab95 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- 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;