[comp.lang.c] Argument Passing in C

dmg@ssc-vax.UUCP (David Geary) (09/18/88)

Sorry if this is a repost; I thought that I'd posted it once, but
apparently it didn't get posted:



  I'm trying to get a grasp on what actually goes on under the covers
when passing arguments in C.  Here's how I understand the process, please
feel free to correct me if I'm wrong:

Let's say we have the following:

DoIt(x,y,z)
  int x,y,z;
{
  /* Does something... */
}

main()
{
   int a=2, b=3, c=4;

   DoIt(a,b,c);
}

Simple enough? ;-)  Anyway, here goes...

When main() calls DoIt(), the values of a,b, and c are loaded on
a stack somewhere in memory.  However, as I understand it, the
top of the stack is at a higher address than the bottom.  Here's what
I mean:

1)  Load a on the stack (args are loaded left to right, correct?)

  	ADDRESS		VALUE

	106000		2

2)  Then b is pushed on the stack:

	ADDRESS		VALUE

	105996		2
	106000		3

3)  Then c is pushed on the stack:

	ADDRESS		VALUE

	105992		2
	105996		3
	106000		4

Notice that the stack's "top" is at location 106000.  In other words
the stack grows toward the top of memory.  Also, of course, the ADDRESS
locations were made up off the top of my head. I am also assuming, for
the sake of example, that int's take up 4 bytes in memory. 

Then, I guess, the address of the calling routine is loaded onto the
stack, so that we know where to return when DoIt() is done.  So, 
the stack looks like so when everything is pushed on:

	ADDRESS		VALUE

	105988		Address of main()   (assuming 4 bytes for pointers, too)
	105992		2
	105996		3
	106000		4

So, this should be the condition the stack is in when DoIt() takes over.


When DoIt() takes over, it only knows where the stack starts in memory,
and then uses that starting address and offsets to access the x,y, and z
in DoIt().



Am I thinking correctly?  I've never written a compiler, so I am not real
sure about this.  Also, I'm a bit confused about what is left up to the
compiler as far as HOW to implement the passing of variable values.  Does
the compiler HAVE TO do it this way.  Does the compiler have the freedom
to, say, grow the stack towards the end of memory instead of the beginning?
Where is all this kind of stuff spelled out?

I am teaching an Advanced C course, and am showing my students how
to write variadic functions.  I want to make sure that the above is
correct, and also be prepared to answer the questions in the above
paragraph.

Thanx for the help,
-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ David Geary, Boeing Aerospace,               ~ 
~ #define    Seattle     RAIN                  ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

davidsen@steinmetz.ge.com (William E. Davidsen Jr) (09/20/88)

In article <2235@ssc-vax.UUCP> dmg@ssc-vax.UUCP (David Geary) writes:
	[ discussion about how one C implementation works ]

| Am I thinking correctly?  I've never written a compiler, so I am not real
| sure about this.  Also, I'm a bit confused about what is left up to the
| compiler as far as HOW to implement the passing of variable values.  Does
| the compiler HAVE TO do it this way. Does the compiler have the freedom
| to, say, grow the stack towards the end of memory instead of the beginning?
| Where is all this kind of stuff spelled out?

There is no reason why the compiler even has to use a stack at all. It's
the common way to do it, but some machines have features which make it
inpractical. The VAX has hardware support for other argument passing
mechanisms by pointing the argument pointer to a data block. The
Honeywell machines don't have a hardware stack, and can have any number
of software stacks.

| I am teaching an Advanced C course, and am showing my students how
| to write variadic functions.  I want to make sure that the above is
| correct, and also be prepared to answer the questions in the above
| paragraph.

I hope you're not teaching anything other than varargs... unless they're
writing the varargs package for your C implementation you should be
doing things the portable way.

I've been teaching C since V7, and although I use the stack model as a
conceptual model, I repeat many times that it doesn't have to work that
way, and that you can't assume it does.
-- 
	bill davidsen		(wedu@ge-crd.arpa)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me

swilson%thetone@Sun.COM (Scott Wilson) (09/21/88)

>The
>Honeywell machines don't have a hardware stack, and can have any number
>of software stacks.

Can someone explain to me what the difference is between a hardware stack
and a software stack?  I worked on a C compiler project once where
one person said the target machine "doesn't even have a hardware stack."
I asked what the difference was between the target machine and, say, a 68000.
The answer was the 68000 had a stack because it supported addressing modes
like "movl d7,@sp-" whereas the target machine required you to use two
instructions, first a move then an explicit decrement of the stack pointer.
So what?, I thought.  So which machines have a hardware stack and which
don't and how do the differences appear to, say, an assembly language
programmer.

--
Scott Wilson		arpa: swilson@sun.com
Sun Microsystems	uucp: ...!sun!swilson
Mt. View, CA

barmar@think.COM (Barry Margolin) (09/22/88)

In article <69210@sun.uucp> swilson@sun.UUCP (Scott Wilson) writes:
>Can someone explain to me what the difference is between a hardware stack
>and a software stack?

A hardware stack is one that has explicit machine instructions and/or
addressing modes for manipulating and using a register (or registers)
as a stack pointer.  Frequently there is a dedicated register for this
purpose, and it is used implicitly by some of these instructions.

The example you gave was good.  If you can push or pop a value on a
stack in one instruction, rather than having to increment the stack
pointer in a separate instruction, you have the rudiments of a
hardware stack (of course, autoincrementing is also useful for
stepping through an array).

Other operations sometimes supplied with a hardware stack are special
call and return instructions that automatically manipulate the stack.
For example, if the return instruction knows that the saved PC is at a
particular offset from the stack pointer register and automatically
pops it into the PC and unwinds the stack, that is a hardware stack.

The Honeywell hardware that Multics runs on doesn't have a hardware
stack, but there are conventions that a particular register was
always used as the stack pointer.  The assembler has a RETURN
pseudo-op that is actually a built-in macro that does all the stack
manipulation for returning (actually, it transfers to a standard
shared routine that does the work).

Barry Margolin
Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

will.summers@p6.f18.n114.z1.fidonet.org (will summers) (09/22/88)

In message <69210@sun.uucp> swilson%thetone@Sun.COM (Scott Wilson) writes:
 > 
 > Can someone explain to me what the difference is between a hardware stack
 > and a software stack?  I worked on a C compiler project once where
 > one person said the target machine "doesn't even have a hardware stack."
 > I asked what the difference was between the target machine and, say, a
 > 68000.
 > The answer was the 68000 had a stack because it supported addressing modes
 > like "movl d7,@sp-" whereas the target machine required you to use two
 > instructions, first a move then an explicit decrement of the stack pointer.

Interesting point.  Methinks a "proper" distinction would place any stack 
implemented in the store in the software category. Some kind of on-chip or 
auxilliary-chip stack would seem to me to be necessary to qualify as a 
"hardware" stack.                                 

A possible exception would be an implementation where the stack storage 
had a completely separate address space.  It's semantics, but seems to me 
that for a distinction there should be a difference, and one vs. two 
instructions seems too narrow a difference...

    \/\/ill 


--  
St. Joseph's Hospital/Medical Center - Usenet <=> FidoNet Gateway
Uucp: ...{gatech,ames,rutgers}!ncar!noao!asuvax!stjhmc!18.6!will.summers

cramer@optilink.UUCP (Clayton Cramer) (09/28/88)

In article <69210@sun.uucp>, swilson%thetone@Sun.COM (Scott Wilson) writes:
> Can someone explain to me what the difference is between a hardware stack
> and a software stack?  I worked on a C compiler project once where
> one person said the target machine "doesn't even have a hardware stack."
> I asked what the difference was between the target machine and, say, a 68000.
> The answer was the 68000 had a stack because it supported addressing modes
> like "movl d7,@sp-" whereas the target machine required you to use two
> instructions, first a move then an explicit decrement of the stack pointer.
> So what?, I thought.  So which machines have a hardware stack and which
> don't and how do the differences appear to, say, an assembly language
> programmer.
> 
> --
> Scott Wilson		arpa: swilson@sun.com

This may be going further back into prehistory than you intended, but I
worked on a machine called the Univac 1219 that had no concept of a
"stack pointer".  Subroutine calls were implemented with an instruction
called "B7IJ", which took the current return address and stuffed it into
the word at the beginning of the subroutine you were calling.  When your
subroutine was complete, you executed a return by doing an indirect
jump through the address in the first word of the subroutine.  Needless
to say, there was no way to do recursion without tremendous effort.

An example of a machine with a nearly useless hardware stack is the 6502.
The stack pointer is an 8-bit register, and the first eight bits of the
hardware stack pointer address are optionally definable by a jumper.
As a consequence, the hardware stack is only 256 bytes long.  If you
want to do anything more complicated than a simple video game, or perhaps
a simple process control system, plan on building your own stack 
manipulation subroutines.

And you thought the 8086 family was primitive!
-- 
Clayton E. Cramer
..!ames!pyramid!kontron!optilin!cramer

pjh@mccc.UUCP (Pete Holsberg) (09/30/88)

In article <510@optilink.UUCP> cramer@optilink.UUCP (Clayton Cramer) writes:
...An example of a machine with a nearly useless hardware stack is the 6502.
...The stack pointer is an 8-bit register, and the first eight bits of the
...hardware stack pointer address are optionally definable by a jumper.
...As a consequence, the hardware stack is only 256 bytes long.  If you
...want to do anything more complicated than a simple video game, or perhaps
...a simple process control system, plan on building your own stack 
...manipulation subroutines.

Yas, and for Gawd's sakes, don't even think about designing a personal
computer around it!!  ;-)

Pete Holsberg                   UUCP: {...!rutgers!}princeton!mccc!pjh
Technology Division                   ...!att!jonlab!mccc!pjh
Mercer College			CompuServe: 70240,334
1200 Old Trenton Road           GEnie: PJHOLSBERG
Trenton, NJ 08690               Voice: 1-609-586-4800

dharvey@wsccs.UUCP (David Harvey) (10/03/88)

In article <705.2339B3D8@stjhmc.fidonet.org>, will.summers@p6.f18.n114.z1.fidonet.org (will summers) writes:

	<body of his message removed>

> In message <69210@sun.uucp> swilson%thetone@Sun.COM (Scott Wilson) writes:
>  > 
>  > Can someone explain to me what the difference is between a hardware stack
>  > and a software stack?  I worked on a C compiler project once where
>  > one person said the target machine "doesn't even have a hardware stack."
>  > I asked what the difference was between the target machine and, say, a
>  > 68000.
>  > The answer was the 68000 had a stack because it supported addressing modes
>  > like "movl d7,@sp-" whereas the target machine required you to use two
>  > instructions, first a move then an explicit decrement of the stack pointer.

It seems to me that any register based hardware with register indirect
mode and, post-increment and pre-decrement instructions will do the
trick for you.  Using the 68000 as an example:

MOVE.L		#$6000,-(A7)	; to push it on the stack
MOVE.L		(A7)+,D5	; to pop it back off.

This of course creates a normal (goes down in memory as stack grows)
stack, but YOU are responsible for where to put the stack.  Also, it
is highly doubtful that some of the compiler vendors are going to
implement it as a stack (they might if that is all they have).  That
of course means that you will very likely be using Assembler.  My bet
for saying that a machine has a hardware stack is that it has a register
so designated with all the trimmings and trappings as described above.
It is the addressing mode that is most important here however, not a
designated hardware register as a stack pointer per-se.

dharvey@wsccs

I am responsible for Nobody
and Nobody is responsible for me.

The only thing you can know for sure
is that you can't know anything for sure.

wes@obie.UUCP (Barnacle Wes) (10/06/88)

In message <69210@sun.uucp> swilson%thetone@Sun.COM (Scott Wilson) writes:
| Can someone explain to me what the difference is between a hardware stack
| and a software stack?  I worked on a C compiler project once where
| one person said the target machine "doesn't even have a hardware stack."
| I asked what the difference was between the target machine and, say, a
| 68000.
| The answer was the 68000 had a stack because it supported addressing modes
| like "movl d7,@sp-" whereas the target machine required you to use two
| instructions, first a move then an explicit decrement of the stack pointer.

In article <699@wsccs.UUCP>, dharvey@wsccs.UUCP (David Harvey) replies:
> It seems to me that any register based hardware with register indirect
> mode and, post-increment and pre-decrement instructions will do the
> trick for you.  [...]

But what about hardware without pre-decrement and post-increment
addressing?  The Univac 1100 and many derivatives of it, for example.
Or the IBM 360 family?  With the 360, you usually end up using a
register for the stack pointer, and using macros like (forgive me if
the syntax is wrong, it has been quite a few years since I wrote a
BAL program :-)

POP	MACRO	a
	MOV	(R5),a
	ADD	#4,R5
	ENDM

PUSH	MACRO	a
	SUB	#4,R5
	MOV	a,(R5)
	ENDM

The problem with these macros is that they are not atomic.  I can't
think of any straightforward reasons why this is a problem, but I'm sure
it would cause problems somewhere, somehow.

-- 
                     {hpda, uwmcsd1}!sp7040!obie!wes

         "How do you make the boat go when there's no wind?"
                                 -- Me --

henry@utzoo.uucp (Henry Spencer) (10/10/88)

In article <202@obie.UUCP> wes@obie.UUCP (Barnacle Wes) writes:
>But what about hardware without pre-decrement and post-increment
>addressing?  The Univac 1100 and many derivatives of it, for example.
>Or the IBM 360 family?  With the 360, you usually end up using a
>register for the stack pointer, and using macros [for pop and push.]
>The problem with these macros is that they are not atomic...

What you can do, to avoid this, is to allocate some anonymous temporary
variables in the stack frame and use them instead of pushing and popping
temporary values.  Actually, even the original C compiler, with the pdp11's
hardware stack support, used this approach to a small extent:  the topmost
stack location was always a temporary.  (The significance of this on the
11 was that after a function call, one less parameter had to be popped off
the stack, which was a major win since most functions have few parameters
and the 11 was particularly good at popping two or one (or zero!).)  By
doing this, you can generally avoid stack-pointer movement except as part
of the calling sequence.  It does mean that accessing small offsets from
the stack pointer or frame pointer needs to be efficient, which can
impose some constraints.  (For example, if you're not using a separate
frame pointer, on the 360 the stack must grow downward, since the 360
has positive offsets only.)
-- 
The meek can have the Earth;    |    Henry Spencer at U of Toronto Zoology
the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu

diamond@csl.sony.JUNET (Norman Diamond) (10/12/88)

In <202@obie.UUCP>, wes@obie.UUCP (Barnacle Wes) writes (almost):

> POP	MACRO	a
> 	L	R5,a
> 	LA	R5,4(R5)
> 	MEND
 
> PUSH	MACRO	a
> 	S	R5,=F'4'
> 	ST	R5,a
> 	MEND

> The problem with these macros is that they are not atomic.  I can't
> think of any straightforward reasons why this is a problem, but I'm
> sure it would cause problems somewhere, somehow.

No problems should be caused by not being atomic.  Real problems are
caused when programmers carelessly use machine instructions such as
POP a   or   PUSH a    and think that they are atomic.  In fact,
such instructions are also non-atomic (nearly always if not always)
and careless programmers create obscure bugs in multiprocessing
systems.
-- 
-------------------------------------------------------------------------------
  The above opinions are my own.   |   Norman Diamond
  If they're also your opinions,   |   Sony Computer Science Laboratory, Inc.
  you're infringing my copyright.  |   diamond%csl.sony.jp@relay.cs.net