[TUHS] "The programmer must have been carried away ...

Angelo Papenhoff aap at papnet.eu
Thu Jun 24 02:01:02 AEST 2021


... when he declared all the parameters for this procedure"

I'm sure some of you remember this quote expressing John Lions'
puzzlement over the declaration of printf in the UNIX kernel:

printf(fmt,x1,x2,x3,x4,x5,x6,x7,x8,x9,xa,xb,xc)

Note that parameters x2 and following are never referenced by name
in the function body, only by pointer arithmetic from &x1.

The historical reason for this long list of parameters is probably
not well known, so I want to explain it and hope that you will find
it as enjoyable as I did when I came across it some time ago while
studying leftover files of B.

To call a function in B, there's a little dance you have to do:
first push the function address onto the stack, remember the stack
pointer, push all the arguments, then restore the stack pointer and
do the actual call.

In diagrams, this is what happens to the B stack:

push func addr:

+------------+
|            |  <- sp
+------------+
|  func addr |
+------------+


mark (n2):

+------------+
|            |  <- sp
+------------+
|            |  <- old sp (saved on native stack)
+------------+
|  func addr |
+------------+


push arguments:

+------------+
|            |  <- sp
+------------+
|    arg n   |
+------------+
|    ...     |
+------------+
|    arg 0   |
+------------+
|            |  <- old sp (saved on native stack)
+------------+
|  func addr |
+------------+


call (n3):

pop old sp from stack
jump to func addr on stack
set ret addr and old fp
+------------+
|    arg n   |
+------------+
|    ...     |
+------------+
|    arg 0   |
+------------+
|  ret addr  |
+------------+
|   old fp   |  <- sp, fp
+------------+


The callee then sets sp, so has to know how many args and automatics!

+------------+
|            |  <- sp
+------------+
|   auto n   |
+------------+
|    ...     |
+------------+
|   auto 0   |
+------------+
|    arg n   |
+------------+
|    ...     |
+------------+
|    arg 0   |
+------------+
|  ret addr  |
+------------+
|   old fp   |  <- fp
+------------+


So because the arguments to a functions were grouped together with
the automatics and not below the ret addr and saved fp, there has
to be space on the stack lest they clash. This of course means that
this printf in B would probably break if called with more arguments
than it expects.
So there you have it. Just a line of B code that wasn't updated to C.


Cheers,
aap


More information about the TUHS mailing list