SPARC registers


The SPARC specification defines a few special-purpose registers ( generally accessible only to the kernel, if at all) and some working space.

  • Processor Status Register ( PSR ) ” Contains state (kernel/ user mode), condition codes, and the CPU priority level.

  • Program Counter ( PC ) ” Address of the instruction currently being executed.

  • next Program Counter ( nPC ) ” Address of the next instruction. Used for prefetch of an instruction code.

  • Partial Arithmetic Result ( Y ) ” Primarily used for integer multiplication.

  • 32 working registers ( %r0 through %r31 ) ” General-purpose 32-bit registers.

The CPU itself may have a large number of general-purpose registers available (from 40 up to 512), but an individual piece of code can access only 32 of these at any one time. This restriction is partly due to a design issue related to passing parameters.

Passing parameters when calling routines

Function calls are a common occurrence, especially with modular or recursive programs, so passing parameters needs to be fairly efficient. Since we've seen that access to memory doesn't fall in this category, it would be best if parameters were put in registers rather than on a stack in memory. There are several ways to do this.

First, we might have a lot of registers and dedicate a certain subset to be used for parameters. Thus, any routine that calls another would have to clear out that register set and put parameters there, which means lots of moving data from one register to another. This may be more efficient than putting data on the stack all the time, but it's still not very elegant.

A better method would be to give a subset of the registers to a function and make sure that any function that it calls would have a different subset with which to work. This approach is the one that was taken and leads us to the first interesting and potentially difficult concept to understand when dealing with SPARC systems.

Register windows

Regardless of the total number of general-purpose registers that the CPU contains, a function can see only 32 of them. There is a subset of the register set, a "window" into it, available for any given function. The kernel keeps a pointer to the current register subset in the processor status word.

Now, if each function uses a different set of registers, how does this help with passing parameters or results back and forth? The answer is to have these windows overlap , so that there are some registers (a subset of the window, if you will) in common. These shared registers can be used to pass parameters to a called function or to return a value to a caller. The registers available to a given function are shown below.

Figure 16-1. Registers Available to a Given Function

graphics/16fig01.gif

As you can see from the diagram, there are four sets of 8 registers, or 32 in all. The global registers are exactly that ” accessible to all functions (all windows) as if they were really global variables . Everybody can see them.

The "window" that moves is composed of the 24 remaining registers. These are divided into groups of 8 and named more or less according to their function. The first 8, the input registers, are those into which the calling function will put the parameters that it wants to pass. In our current function, then, these registers contain the incoming data: the input to this subroutine. These 8 registers are the ones that overlap with the caller's window. The last 8, the output registers, will contain any data that this function passes on to someone else: outgoing parameters. These, then, overlap with the window of the next function, the one(s) called by this current bit of code. The local registers, the piece in the middle, don't overlap with any other registers and are purely local to this function.

These registers act like a "circular buffer," so it looks like an infinite set of windows are available. However, at some point you're going to run out, so there is a trap to the kernel when the next window would conflict with one already in use. This trap, known as a window overflow , will cause the system to save the conflicting window (the old data) on the stack. The reverse case, when a window underflow is caused by trying to back up to a window that has been put on the stack already, will result in another trap to the kernel to get the data and load it back into the desired register window.

Special instructions ( save and restore ) move to the next or previous register window, which may cause these special window underflow and overflow traps. Moving to a new window gets a new set of 24 registers, but since they overlap by 8, a save or restore will only move the window pointer by 16 registers. This gives a new function 16 registers plus 8 from the caller (or, when returning from a function, restores 16 registers and leaves the 8 output registers with the values placed in them by the old called function).

These registers are commonly named g (global), i (input), l (local), and o (output) in assembly language. The notation used starts a register name with a percent sign to distinguish it from a variable name , has one letter to indicate the type of register, and has one number from 0 to 7 to identify it. Thus, register % i0 is the first input register, and %g7 is the last global.

Register usage

Some conventions and some hardware requirements govern how these registers are used. From the software standpoint, a function assumes that the input and local registers belong to that function. Contents can be modified as the function sees fit. This means that the input registers can be used for parameters, local storage, temporary values, or all three. When a function returns back to the caller, then, that caller cannot depend on the values in its output registers (the overlap to the called function) still being there when the caller resumes.

The 8 output registers belong to the called function; their values cannot be assumed to be consistent across function calls. You will often see output registers used to hold temporary values in a function in between calls to other functions. Thus, a window overflow, when it has to save the old register contents to make way for a new function, will only save 16 registers (the inputs and locals) on the stack.

Now, parameters passed to a function must go in the output registers. This requirement obviously restricts you to passing eight parameters, right? Well, not quite. The excess has to be put somewhere else, that's all. (They go on the stack.) So, we have space for the first eight parameters in registers.

Unfortunately, that's not quite true, since there is additional information that would be nice to pass on to a subroutine. One vital bit, which that function will need in order to get back, is the return address , that is, where the call came from. That has to go in a register. In fact, one instruction ( call) uses output register #7 ( %o7) to hold the address of the call itself. This is done by the hardware; you have no choice in the matter. So one register is used for something other than a parameter, which bounces you down to seven, max.

There is, however, still more that needs to be passed on to the new function: the stack pointer. Since there is no dedicated stack pointer registers, one of the general-purpose registers has to be used instead. Output register #6 ( %o6 , also known as %sp) is used for this purpose, although it really could be any one of them.

Fortunately, this is it; the remaining six registers ( %o0 through %o5 ) are available for parameters. They are normally used in a left-to-right fashion: the first parameter goes in the first register ( %o0 ), the next in the second ( %o1 ), and so on. Thus, the new function sees its incoming parameters in %i0 (the first), %i1 (the second), and so on up to %i5 (the sixth ).

When more than six calling parameters are passed, the additional ones can be found on the stack ” and we do have the stack pointer, since that's in the caller's %o6 ” which is our %i6 . Our %i7, of course, is the caller's %o7, which contains essentially the return address.

The debugger cannot tell which of these six registers, if any, are really used for passing parameters, so usually a stack traceback will always display six parameters. The only way you can tell which of those six are used for incoming data versus temporary storage is to examine the source code (or, if you're really dedicated, by looking at the assembly code to see what they're used for.)

Global register zero

One other hardware feature concerns register contents. The first global register, %g0, is the assembly language equivalent of /dev/null . This register always contains zero. It doesn't matter what you try to put into it, the value will always remain zero. This is actually amazingly useful; you see %g0 used quite often. We'll see some examples later.



PANIC. UNIX System Crash Dump Analysis Handbook
PANIC! UNIX System Crash Dump Analysis Handbook (Bk/CD-ROM)
ISBN: 0131493868
EAN: 2147483647
Year: 1994
Pages: 289
Authors: Chris Drake

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net