1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- S Y S T E M . S E C O N D A R Y _ S T A C K --
9 -- Copyright (C) 1992-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with System
.Parameters
;
33 with System
.Storage_Elements
;
35 package System
.Secondary_Stack
is
38 package SP
renames System
.Parameters
;
39 package SSE
renames System
.Storage_Elements
;
41 use type SP
.Size_Type
;
43 type SS_Stack
(Default_Chunk_Size
: SP
.Size_Type
) is private;
44 -- An abstraction for a heap structure maintained in a stack-like fashion.
45 -- The structure is comprised of chunks which accommodate allocations of
46 -- varying sizes. See the private part of the package for further details.
47 -- Default_Chunk_Size indicates the size of the static chunk, and provides
48 -- a minimum size for all dynamic chunks.
50 type SS_Stack_Ptr
is access all SS_Stack
;
51 -- A reference to a secondary stack
53 type Mark_Id
is private;
54 -- An abstraction for tracking the state of the secondary stack
57 (Stack
: in out SS_Stack_Ptr
;
58 Size
: SP
.Size_Type
:= SP
.Unspecified_Size
);
59 -- Initialize or reuse a secondary stack denoted by reference Stack. If
60 -- Stack is null, create a new stack of size Size in the following manner:
62 -- * If Size denotes Unspecified_Size, allocate the stack from the binder
63 -- generated pool as long as the pool has not been exhausted.
65 -- * Otherwise allocate the stack from the heap.
67 -- If Stack is not null, reset the state of the stack. No existing chunks
68 -- are freed because they may be reused again.
72 Storage_Size
: SSE
.Storage_Count
;
73 Alignment
: SSE
.Storage_Count
:= Standard
'Maximum_Alignment);
74 -- Allocate enough space on the secondary stack of the invoking task to
75 -- accommodate an allocation of size Storage_Size. Return the address of
76 -- the first byte of the allocation in Addr, which is a multiple of
77 -- Alignment. The routine may carry out one or more of the following
80 -- * Reuse an existing chunk that is big enough to accommodate the
81 -- requested Storage_Size.
83 -- * Free an existing chunk that is too small to accommodate the
84 -- requested Storage_Size.
86 -- * Create a new chunk that fits the requested Storage_Size.
88 procedure SS_Free
(Stack
: in out SS_Stack_Ptr
);
89 -- Free all dynamic chunks of secondary stack Stack. If possible, free the
92 function SS_Mark
return Mark_Id
;
93 -- Capture and return the state of the invoking task's secondary stack
95 procedure SS_Release
(M
: Mark_Id
);
96 -- Restore the state of the invoking task's secondary stack to mark M
98 function SS_Get_Max
return Long_Long_Integer;
99 -- Return the high water mark of the invoking task's secondary stack, in
103 with procedure Put_Line
(S
: String);
105 -- Debugging procedure for outputting the internals of the invoking task's
106 -- secondary stack. This procedure is generic in order to avoid a direct
107 -- dependence on a particular IO package. Instantiate with Text_IO.Put_Line
112 -- Unused entity that is just present to ease the sharing of the pool
113 -- mechanism for specific allocation/deallocation in the compiler.
119 -- The secondary stack is a runtime data structure managed in a stack-like
120 -- fashion. It is part of the runtime support for functions that return
121 -- results of caller-unknown size.
123 -- The secondary stack is utilized as follows:
125 -- * The compiler pushes the caller-unknown size result on the secondary
126 -- stack as part of return statement or build-in-place semantics.
128 -- * The caller receives a reference to the result.
130 -- * Using the reference, the caller may "offload" the result into its
131 -- primary stack, or use it in-place while still on the secondary
134 -- * Once the caller has utilized the result, the compiler reclaims the
135 -- memory occupied by the result by popping the secondary stack up to
144 -- The secondary stack is a linked structure which consist of "chunks".
145 -- A chunk is both a memory storage and a linked-list node. Addresses of
146 -- allocated objects refer to addresses within the memory storage of a
147 -- chunk. Chunks come in two variants - static and dynamic.
151 -- The secondary stack has exactly one static chunk that is created on the
152 -- primary stack. The static chunk allows secondary-stack usage on targets
153 -- where dynamic allocation is not available or desirable. The static chunk
154 -- is always the "first" chunk and precedes all dynamic chunks.
156 -- 1.2) Dynamic chunk
158 -- The secondary stack may have zero or more dynamic chunks, created on the
159 -- heap. Dynamic chunks allow the secondary stack to grow beyond the limits
160 -- of the initial static chunk. They provide a finer-grained management of
161 -- the memory by means of reuse and deallocation.
165 -- The secondary stack captures its state in a "mark". The mark is used by
166 -- the compiler to indicate how far the stack can be safely popped after a
167 -- sequence of pushes has taken place.
169 -- 3) Secondary stack
171 -- The secondary stack maintains a singly-linked list of chunks, starting
172 -- with the static chunk, along with a stack pointer.
176 -- The process of allocation equates to "pushing" on the secondary stack.
177 -- Depending on whether dynamic allocation is allowed or not, there are
178 -- two variants of allocation - static and dynamic.
180 -- 4.1) Static allocation
182 -- In this case the secondary stack has only the static chunk to work with.
183 -- The requested size is reserved on the static chunk and the stack pointer
184 -- is advanced. If the requested size will overflow the static chunk, then
185 -- Storage_Error is raised.
187 -- 4.2) Dynamic allocation
189 -- In this case the secondary stack may carry out several actions depending
190 -- on how much free memory is available in the chunk indicated by the stack
193 -- * If the indicated chunk is big enough, allocation is carried out on
196 -- * If the indicated chunk is too small, subsequent chunks (if any) are
197 -- examined. If a subsequent chunk is big enough, allocation is carried
198 -- out on it, otherwise the subsequent chunk is deallocated.
200 -- * If none of the chunks following and including the indicated chunk
201 -- are big enough, a new chunk is created and the allocation is carried
204 -- This model of operation has several desirable effects:
206 -- * Leftover chunks from prior allocations, followed by at least one pop
207 -- are either reused or deallocated. This compacts the memory footprint
208 -- of the secondary stack.
210 -- * When a new chunk is created, its size is exactly the requested size.
211 -- This keeps the memory usage of the secondary stack tight.
213 -- * Allocation is in general an expensive operation. Compaction is thus
214 -- added to this cost, rather than penalizing mark and pop operations.
218 -- The process of marking involves capturing the secondary-stack pointer
219 -- in a mark for later restore.
223 -- The process of releasing equates to "popping" the secondary stack. It
224 -- moves the stack pointer to a previously captured mark, causing chunks
225 -- to become reusable or deallocatable during the allocation process.
234 -- | Top.Byte ------------------------+
235 -- | Top.Chunk ------------------+ |
238 -- +------------+ +--------+ +-----|--+ +--------+
239 -- | Memory | | Memory | | Memo|y | | Memory |
240 -- | ######### | | ##### | | ####| | | ##### |
242 -- | Next ---> | Next ---> | Next ---> | Next ---> x
243 -- +------------+ +--------+ +--------+ +--------+
245 -- Static chunk Chunk 2 Chunk 3 Chunk 4
247 --------------------------
248 -- Memory-related types --
249 --------------------------
251 subtype Memory_Size_With_Invalid
is SP
.Size_Type
;
252 -- Memory storage size which also includes an invalid negative range
254 Invalid_Memory_Size
: constant Memory_Size_With_Invalid
:= -1;
256 subtype Memory_Size
is
257 Memory_Size_With_Invalid
range 0 .. SP
.Size_Type
'Last;
258 -- The memory storage size of a single chunk or the whole secondary stack.
259 -- A non-negative size is considered a "valid" size.
261 subtype Memory_Index
is Memory_Size
;
262 -- Index into the memory storage of a single chunk
264 type Chunk_Memory
is array (Memory_Size
range <>) of SSE
.Storage_Element
;
265 for Chunk_Memory
'Alignment use Standard
'Maximum_Alignment;
266 -- The memory storage of a single chunk
272 type SS_Chunk
(Size
: Memory_Size
);
273 -- Abstraction for a chunk. Size indicates the memory capacity of the
276 type SS_Chunk_Ptr
is access all SS_Chunk
;
277 -- Reference to the static or any dynamic chunk
279 type SS_Chunk
(Size
: Memory_Size
) is record
281 -- Pointer to the next chunk. The direction of the pointer is from the
282 -- static chunk to the first dynamic chunk, and so on.
284 Size_Up_To_Chunk
: Memory_Size
;
285 -- The size of the secondary stack up to, but excluding the current
286 -- chunk. This value aids in calculating the total amount of memory
287 -- the stack is consuming, for high-water-mark update purposes.
289 Memory
: Chunk_Memory
(1 .. Size
);
290 -- The memory storage of the chunk. The 1-indexing facilitates various
291 -- size and indexing calculations.
298 -- Abstraction for a secondary stack pointer
300 type Stack_Pointer
is record
302 -- The position of the first free byte within the memory storage of
303 -- Chunk.all. Byte - 1 denotes the last occupied byte within Chunk.all.
305 Chunk
: SS_Chunk_Ptr
;
306 -- Reference to the chunk that accommodated the most recent allocation.
307 -- This could be the static or any dynamic chunk.
314 type SS_Stack
(Default_Chunk_Size
: SP
.Size_Type
) is record
316 -- Indicates whether the secondary stack can be freed
318 High_Water_Mark
: Memory_Size
;
319 -- The maximum amount of memory in use throughout the lifetime of the
325 Static_Chunk
: aliased SS_Chunk
(Default_Chunk_Size
);
326 -- A special chunk with a default size. On targets that do not support
327 -- dynamic allocations, this chunk represents the capacity of the whole
335 type Mark_Id
is record
336 Stack
: SS_Stack_Ptr
;
337 -- The secondary stack whose mark was taken
340 -- The value of Stack.Top at the point in time when the mark was taken
347 -- The following section provides lightweight versions of all abstractions
348 -- used to implement a secondary stack. The contents of these versions may
349 -- look identical to the original abstractions, however there are several
350 -- important implications:
352 -- * The versions do not expose pointers.
354 -- * The types of the versions are all definite. In addition, there are
355 -- no per-object constrained components. As a result, the versions do
356 -- not involve the secondary stack or the heap in any way.
358 -- * The types of the versions do not contain potentially big components.
360 subtype Chunk_Id_With_Invalid
is Natural;
361 -- Numeric Id of a chunk with value zero
363 Invalid_Chunk_Id
: constant Chunk_Id_With_Invalid
:= 0;
366 Chunk_Id_With_Invalid
range 1 .. Chunk_Id_With_Invalid
'Last;
367 -- Numeric Id of a chunk. A positive Id is considered "valid" because a
368 -- secondary stack will have at least one chunk (the static chunk).
370 subtype Chunk_Count
is Natural;
371 -- Number of chunks in a secondary stack
373 -- Lightweight version of SS_Chunk
375 type Chunk_Info
is record
376 Size
: Memory_Size_With_Invalid
;
377 -- The memory capacity of the chunk
379 Size_Up_To_Chunk
: Memory_Size_With_Invalid
;
380 -- The size of the secondary stack up to, but excluding the current
384 Invalid_Chunk
: constant Chunk_Info
:=
385 (Size
=> Invalid_Memory_Size
,
386 Size_Up_To_Chunk
=> Invalid_Memory_Size
);
388 -- Lightweight version of Stack_Pointer
390 type Stack_Pointer_Info
is record
392 -- The position of the first free byte within the memory storage of
393 -- Chunk. Byte - 1 denotes the last occupied byte within Chunk.
395 Chunk
: Chunk_Id_With_Invalid
;
396 -- The Id of the chunk that accommodated the most recent allocation.
397 -- This could be the static or any dynamic chunk.
400 -- Lightweight version of SS_Stack
402 type Stack_Info
is record
403 Default_Chunk_Size
: Memory_Size
;
404 -- The default memory capacity of a chunk
407 -- Indicates whether the secondary stack can be freed
409 High_Water_Mark
: Memory_Size
;
410 -- The maximum amount of memory in use throughout the lifetime of the
413 Number_Of_Chunks
: Chunk_Count
;
414 -- The total number of static and dynamic chunks in the secondary stack
416 Top
: Stack_Pointer_Info
;
420 function Get_Chunk_Info
421 (Stack
: SS_Stack_Ptr
;
422 C_Id
: Chunk_Id
) return Chunk_Info
;
423 -- Obtain the information attributes of a chunk that belongs to secondary
424 -- stack Stack and is identified by Id C_Id.
426 function Get_Stack_Info
(Stack
: SS_Stack_Ptr
) return Stack_Info
;
427 -- Obtain the information attributes of secondary stack Stack
429 end System
.Secondary_Stack
;