2.3 Memory Hierarchy
2.3.2 Hardware Stack
Stack architectures use one or more last-in-first-out (LIFO) stacks instead of random-access regis- ters to organise operands. If the stack overflows, elements must be spilled to main memory. If the stack underflows, stack elements must be restored from main memory. This is analogous to pushing and popping registers to and from memory during function calls on CISC-based architectures.
Koopman [21] monitored data stack spilling and restoring for benchmarks Life, Hanoi, Frac, Math and Queens. The spilling algorithm spilled one stack element each time an instruction attempted to push to a full stack and restored an element each time a pop operation was performed on an empty stack. The results showed that stack spilling and restoration tapered off at an exponential rate for these programs. As a practical matter, a stack size of 32 will eliminate stack spilling for almost all programs, Koopman [21]. According to Waldron and Harrison[37], programs including atom, fire, jas, jjt and jcc rarely have a data stack size of more than ten words.
2.3.2.1 Overflow and Underflow
There are a number of strategies for managing stack overflows and underflows. The following discusses some of these.
2.3.2.2 Size Stack for Worst Case Depth
An on-chip stack large enough for the worst case stack depth requirements of the program can be used. This approach completely removes the possibility of overflows and underflows. As control logic is not required for managing these scenarios, this is the simplest approach that can be taken. Sizing the stack for the worst case scenario also has a significant advantage for real time systems. Since stack spilling and restoration is input data dependent, it can be very challenging to predict at exactly which points during program execution this needs to be done. Since static analysis is not required this simplifies Worst Case Execution Time (WCET) analysis. Because spilling and restoration are not required, this greatly simplifies the processor design. However, static analysis is still required to determine the maximum stack depth for the program. Stack depth analysis is considered easier than spilling and restoration analysis, Koopman [21]. Given that a stack size of 32 will eliminate stack spilling for almost all programs, this may be the simplest and most cost effective strategy.
The major disadvantage to this approach is that it may not be possible to modify the size of the stack; this is only practical for FPGA-based processors (soft cores). A compromising strategy could be taken where offline analysis could be used to confirm where in a program stack spilling is likely to occur. At these points a software-based runtime library could be used to perform spilling and restoration at the appropriate boundaries. Where spilling and restoration are required, the programmer would modify the program to call the runtime library functions. Alternatively, the compiler could be modified so that the stack depth analysis and runtime support library functions are called automatically at the required program points.
2.3.2.3 Demand Fed
This approach spills and restores single elements at a time when required. A stack element is only transferred if necessary, hence minimising transfers between the on-chip stack and main memory. Very good use is made of the on-chip stack, making this approach suitable for use where chip space is at a premium. The disadvantages include the fact that complex control logic is required. The control logic is likely to be the most complex part of a stack-based processor. The points at
which spilling and restoration occur introduce temporal non-determinism and this is problematic for real- time systems. However, if the points in a program at which spilling and restoration will occur can be determined by static analysis then this can be accounted for during WCET analysis. Demand-fed single element spilling is a very common approach. A number of processors employing this approach are described in Koopman [21].
2.3.2.4 Paging
Instead of spilling and restoring single elements one at a time as and when required, a paging approach spills and restores fixed sized pages. The premise is that as soon as a single element is spilled or restored then more elements are likely to require spilling and restoring too. The work required to perform the spilling and restoration is typically done by the software runtime system. For example, the processor generates an interrupt when a stack overflows or underflows. A corresponding interrupt service routine (ISR) then performs the necessary data transfers for example, using direct memory access (DMA). This was the approach taken for Moon2 by Vulcan Machines (company now dissolved). The transfer of stack pages is done by the software runtime system, allowing the processor hardware to remain simple. Paging requires a larger on-chip stack than the demand-fed approach, to reduce the execution frequency of the relatively slow ISR and runtime software. The cost of paging is about twice that of the demand-fed approach for memory cycles spent copying stack elements, Koopman [21]. If a program can execute within the constraints of the stack size most of the time, then paging provides an inexpensive method for graceful program degradation.
2.3.2.5 Cache
A data cache can be used by mapping it to the memory space used for the data stack. This is the usual approach used by register-based architectures. It involves complex hardware control but does not provide any advantage over alternative approaches. Caches are used by mainstream processor designers since they improve average case execution times. They are advantageous when variable length data structures such as strings and C structures are accessed.