PA-RISC uses a stack to keep track of local data and procedure call return information for each procedure call. The PA-RISC architecture does not provide explicit stack manipulation instructions. To manage the stack, the runtime architecture specifies that general register GR30 will be used as a stack pointer. This register is commonly referred to as the sp register. The stack in PA-RISC grows upward in memory. That is, as data is added to the stack, it goes at increasing addresses and the stack pointer increases. When data is removed from the stack, the stack pointer decreases. Each called procedure begins by increasing the stack pointer, thus allocating itself memory for local data storage and linkage information. This area is called a stack frame. The amount of space allocated for a stack frame depends on how much local data storage the procedure needs. Stack usage differs between narrow mode and wide mode. We discuss the narrow mode stack usage first, then look at the differences when running in wide mode. Stack Usage in Narrow Mode Figure 2-1 shows the stack frame for a narrow-mode call. By convention, we draw the stack with lower addresses higher on the page, and the stack grows down to higher addresses. The first thing on the stack is a frame marker, which is always eight words long. The frame marker for the current process is located at SP-4 through SP-0x20. The space in the frame marker is reserved for a variety of uses, but the only one we need be concerned with is at SP-0x14. SP-0x14 contains the Return Pointer (RP) of a called procedure. In other words, when a procedure is called, it saves the return pointer at SP-0x14 in the calling procedure's stack frame. Figure 2-1. Stack Usage in Narrow Mode The area at SP-0x24 through SP-0x30 is reserved for arguments zero through three. This space is always allocated even if there are fewer than four arguments. Beyond that is a variable-sized area for storing any arguments beyond four. This area is used only if the called procedure takes more than four arguments. Finally, beyond the variable arguments area, the called procedure can allocate additional space to store callee save registers and for local data storage. Listing 2.1 is an example of C code and the corresponding assembly showing the linkage in the procedure call. Listing 2.1. Narrow Mode Procedure Call Example #include <stdio.h> int proc(int x); main() { int i; int j; i = 10; j = proc(i); printf("j is %d\n", j); } int proc(int x) { int r; r = 2*x; return r; } main starting at 0x28d8: +0x0 NOP +0x4 STW rp,-0x14(sp) ; save the return pointer +0x8 LDO 0x40(sp),sp ; grow the stack +0xc LDI 0xa,r1 ; i = 10 +0x10 STW r1,-0x38(sp) ; save I on the stack +0x14 LDW -0x38(sp),arg0 ; arg0 = i +0x18 LDIL 0x2800,r31 ; calculate address for proc +0x1c BE,L 0x124(sr4,r31) ; call proc +0x20 COPY r31,rp +0x24 STW ret0,-0x34(sp) ; save the return value in j +0x28 LDIL 0x2800,r31 ; load address of constant +0x2c LDO 0x768(r31),arg0 ; string into arg0 +0x30 LDW -0x34(sp),arg1 ; load j into arg1 +0x34 LDIL 0x2800,r31 ; calculate address of printf +0x38 BE,L 0xc0(sr4,r31) ; call printf +0x3c COPY r31,rp +0x40 LDW -0x54(sp),rp ; get the return pointer from +0x44 BV (rp) ; our caller and branch there +0x48 LDO -0x40(sp),sp ; restore the stack pointer proc starting at 0x2924: +0x0 NOP +0x4 LDO 0x40(sp),sp ; Grow stack +0x8 STW arg0,-0x64(sp) ; Store arg0 +0xc LDW -0x64(sp),r19 ; r19 = arg0 +0x10 SHLADD r19,1,0,r20 ; r20 = 2 * r19 +0x14 STW r20,-0x28(sp) ; save r20 +0x18 LDW -0x28(sp),ret0 ; ret0 = r20 +0x1c COPY ret0,ret0 +0x20 BV (rp) ; Branch to rp +0x24 LDO -0x40(sp),sp ; Shrink stack Stack Usage in Wide Mode Figure 2-2 shows the stack usage for a wide-mode procedure call. Recall that in wide mode all our registers are 64 bits wide. Each entry on the stack is also treated as a 64-bit doubleword. Figure 2-2. Stack Usage in Wide Mode The frame marker for wide mode is only two doublewords long to allow for storing two values. The return pointer is stored at SP-0x10. SP-0x08 is reserved for storing the previous stack pointer if needed. After the frame marker, eight doublewords are allocated to store eight arguments. Additional space may be allocated for arguments if there are more than eight. Unlike in narrow mode, these are stored at increasing addresses, not decreasing addresses. In addition, arguments are no longer addressed relative to the stack pointer. Instead, GR29 is set to point to the address of the ninth argument (even if there are fewer than nine arguments, the pointer is set to point to the doubleword after the location reserved for the eighth argument). After the area for arguments are optional areas to accommodate stack growth for allocated memory, local storage, and callee save registers. Listing 2.2 is the assembly language for the same code as above, this time compiled in wide mode. Note that gr9 is decoded as ret1 by this tool because of its alternate use as a return value. Listing 2.2. Wide Mode Procedure Call Example main starting at 0x4000000000001e08: +0x0 STD rp,-0x10(sp) +0x4 STD,MA r3,0x90(sp) ; save r3, grow stack +0x8 STD r4,-0x88(sp) ; save r4 +0xc COPY ret1,r3 +0x10 STD dp,-0x80(sp) +0x14 MFIA r1 +0x18 ADDIL -0x800,r1 +0x1c LDO 0x7ec(r1),r31 +0x20 LDI 0xa,r19 ; i = 10 +0x24 STW r19,-0x78(sp) ; store i +0x28 LDW -0x78(sp),arg0 ; arg0 = i +0x2c BLL 0x1e88,rp ; call proc() +0x30 LDO -0x30(sp),ret1 ; ap = SP-0x30 +0x34 LDD -0x80(sp),dp +0x38 STW ret0,-0x74(sp) +0x3c ADDIL -0x800,dp +0x40 COPY r1,ret0 +0x44 LDD 0x760(ret0),r19 +0x48 LDW -0x74(sp),r20 +0x4c COPY r19,arg0 +0x50 COPY r20,arg1 +0x54 BLL 0x1e78,rp +0x58 LDO -0x30(sp),ret1 +0x5c LDD -0x80(sp),dp +0x60 LDD -0xa0(sp),rp +0x64 LDD -0x88(sp),r4 +0x68 BVE (rp) +0x6c LDD,MB -0x90(sp),r3 proc starting at 0x4000000000001e88: +0x0 NOP +0x4 STD,MA r3,0x50(sp) ; save r3, ; grow stack +0x8 STD r4,-0x48(sp) ; save r4 +0xc COPY ret1,r3 ; r3 = ap +0x10 NOP +0x14 STW arg0,-0x3c(ret1) ; save arg0 at ; ap-0x3c +0x18 LDW -0x3c(ret1),r19 ; r19 = arg0 +0x1c SHLADD r19,1,0,r19 ; r19 *= 2 +0x20 STW r19,-0x38(sp) ; save r19 +0x24 LDW -0x38(sp),ret0 ; ret0 = r19 +0x28 COPY ret0,ret0 +0x2c LDD -0x48(sp),r4 ; restore r4 +0x30 BVE (rp) ; branch to rp +0x34 LDD,MB -0x50(sp),r3 ; shrink stack, ; restore r3 Figure 2-3 shows the stack usage for the call to proc(). You can see that the stack is first grown by 0x50; then GR3, GR4, and G26 (arg0) are stored on the stack. GR3 and GR4 are callee saves, so proc() has to store them in its own stack to be restored later. GR26 gets stored into the caller's argument area. Figure 2-3. Stack Usage for the Call to proc() |