Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83; site umcp-cs.UUCP Path: utzoo!watmath!clyde!burl!ulysses!mhuxj!mhuxn!houxm!ihnp4!zehntel!hplabs!hao!seismo!umcp-cs!chris From: chris@umcp-cs.UUCP (Chris Torek) Newsgroups: net.lang.c Subject: Re: C stack frame sizes Message-ID: <1545@umcp-cs.UUCP> Date: Mon, 3-Dec-84 19:18:03 EST Article-I.D.: umcp-cs.1545 Posted: Mon Dec 3 19:18:03 1984 Date-Received: Thu, 6-Dec-84 05:35:53 EST References: <18092@arizona.UUCP> <9000033@uiucdcsb.UUCP> Organization: U of Maryland, Computer Science Dept., College Park, MD Lines: 132 [Before I begin, I have to add a disclaimer that the information given here was obtained almost entirely by examining the output of the C compiler on one particular Pyramid 90-X. Nothing stated herein should be taken as official. End disclaimer.] The Pyramid is a wonderful machine for exercising ``portable'' C code! Not only are unions and structs kept only on the stack, the evaluation order for subroutine calls is different. First, here's a little bit of information about the hardware architecture. There are always 64 registers avaliable at any point in code. They are divided into four groups of sixteen registers. Four registers in each group are used for special things, so you really wind up with 12 registers in each group. ----------------------------------------------------------------- proc A ------- | pr0 | ------- | ... | ------- | pr15| ------- | lr0 | ------- | ... | ------- | lr15| proc B ------- ------- | tr0 | | pr0 | ------- ------- | ... | | ... | ------- ------- | tr15| | pr15| ------- ------- | ... | | ... | ------- | tr0 | ------- | ... | ------- | tr15| ------- Register mapping: proc A calls proc B Figure 1. ----------------------------------------------------------------- The registers are named by a group description and a number. The four groups are "global", "parameter", "local", and "transfer". The 16 (really 12) global registers, gr0 through gr15 (gr11), are the same in every procedure; the others are windowed, with some hardware and software taking care of overflows when calling and returning. Transfer registers are used to pass parameters (where they become parameter registers); parameter registers are used for local parameters and return values; and local registers are used as in conventional architectures. See Figure 1 for a pictorial representation of parameter, local, and transfer registers. As shown in Figure 1, a called procedure's registers (the parameter registers for proc B) and the calling procedure's registers (the transfer registers for proc A) overlap, allowing the "transfer" of "parameters" through registers (clever, no?). Now, of course, only fixed-size units can be passed via registers; when arrays or other "large" parameters are passed, conventional mechanisms are used. (There are no special cases for "small" structures in the present implementation of the C compiler.) Now, this presents another problem: what if more than 12 parameters are passed? Again, the Pyramid compiler uses conventional mechanisms -- but only when the first 12 registers are used up. This means that if you compile the code f() { g(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); } the last four arguments (12 through 15) are passed on the stack, with the first 12 (0 through 11) being in the corresponding transfer/parameter register. Since the transfer registers are also used as temporary registers when evaluating expressions, the compiler naturally evaluates parameters which will be placed on the stack before those that will go into registers. As with the Vax compilers, these are evaluated in reverse order (right to left), so that each can be pushed onto the stack as soon as the actual value is known. However, the compiler evaluates the first 12 parameters left to right! In other words, the Pyramid compiler evaluates arguments from the outside in. ---------- By the way, this reminds me of a bug we discovered: if you have a C function that returns a value which is an expression involving the first parameter, the compiler accidently clobbers the parameter before completing the evaluation. That is, since pr0 (the caller's tr0) is used hold the return value, someone decided to make it available for temporary storage, resulting in code like get pr0 mask with 0xff shift left 8 bits store in pr0 # oops! get pr0 mask with 0xff00 shift right 8 bits or into pr0 return for return ((x & 0xff) << 8) | ((x & 0xff00) >> 8); This is not good, to say the least. ---------- [In case you've read this far: we (Maryland) have an info-pyramid mailing list to discuss anything to do with Pyramid machines; if you'd like to be added to the list, send mail to info-pyramid-request@MARYLAND.ARPA, info-pyramid-request@umcp-cs.CSNet, or seismo!umcp-cs!info-pyramid-request.] -- (This line accidently left nonblank.) In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (301) 454-7690 UUCP: {seismo,allegra,brl-bmd}!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@maryland