[net.lang.c] Stack Frames

dave@ur-helheim.UUCP (Sinse Raver Dave) (02/12/86)

In c one can declare variables within any {} block.  Does a
{} pair *imply* new context, with stack frame shifts, etc.
I would have to assume so if local variables are declared
within the {} but if no variables are declared? ...

The reason for asking is for ease of expansion and 
consistancy of formatting I often put {} blocks where
they are not necessary (clauses that are only one
statement).  Am I paying a penalty for these one-statement
clauses or will my trusted compiler note the context has
not changed and inhibit the subsequent costly stack manipulations?

Mail or to the net any helpful ganders.  (Not male geese.)

dave

-- 
"The Faster I Go the Behinder I Get"
--Lewis Carroll

Dave Carlson

{allegra,seismo,decvax}!rochester!ur-valhalla!dave

ark@alice.UucP (Andrew Koenig) (02/14/86)

> In c one can declare variables within any {} block.  Does a
> {} pair *imply* new context, with stack frame shifts, etc.
> I would have to assume so if local variables are declared
> within the {} but if no variables are declared? ...

The semantics are as if the {} allocated a new stack frame,
but what actually happens is up to the compiler.  Every compiler
I have seen actually allocates stack memory only at entry to
a procedure, both for greater speed and in order to do the
right thing in case someone jumps into a "block".

mauney@ncsu.UUCP (Jon Mauney) (02/14/86)

> In c one can declare variables within any {} block.
> ...
> Am I paying a penalty for these one-statement clauses

Block structure is static with respect to the enclosing procedure
and can be taken care of by the compiler, with no run-time
stack manipulations required.  Any compiler that tries to be
efficient should do this.
Any compiler that has a run-time penalty for use of {} without
local declarations should be shot.
-- 
Jon Mauney,    mcnc!ncsu!mauney
North Carolina State University

gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/16/86)

In article <463@ur-helheim.UUCP> dave@ur-helheim.UUCP (Raver Dave) writes:
>
>In c one can declare variables within any {} block.  Does a
>{} pair *imply* new context, with stack frame shifts, etc.
>I would have to assume so if local variables are declared
>within the {} but if no variables are declared? ...
>
>The reason for asking is for ease of expansion and 
>consistancy of formatting I often put {} blocks where
>they are not necessary (clauses that are only one
>statement).  Am I paying a penalty for these one-statement
>clauses or will my trusted compiler note the context has
>not changed and inhibit the subsequent costly stack manipulations?

If your C compiler generates additional overhead for local
blocks, it is not very good.  Most reasonable implementations
reserve enough stack at function entry for the deepest local
block nesting within the function, so that there is no
run-time action required upon entering a local block.

robison@uiucdcsb.CS.UIUC.EDU (02/17/86)

The output from C compilers I have seen indicates there is no penalty for
{} blocks with declarations.  All allocation can be done at the function entry.
I.e., the source code:

     int f(x,y)
        int x,y;
	{
           int a,b;
	   ...
	   {
	      int m,n;
	      ...
	   } 
	   ...
	}

generates assembly code which allocates a,b,m, and n on the stack upon entry.
The only difference between a,b and m,n is that m,n is visible
only within the inner block.

- Arch Robison

cottrell@NBS-VMS.ARPA (COTTRELL, JAMES) (02/19/86)

> In article <463@ur-helheim.UUCP> dave@ur-helheim.UUCP (Raver Dave) writes:
> >
> >In c one can declare variables within any {} block.  Does a
> >{} pair *imply* new context, with stack frame shifts, etc.
> >I would have to assume so if local variables are declared
> >within the {} but if no variables are declared? ...
> >
> >The reason for asking is for ease of expansion and 
> >consistancy of formatting I often put {} blocks where
> >they are not necessary (clauses that are only one
> >statement).  Am I paying a penalty for these one-statement
> >clauses or will my trusted compiler note the context has
> >not changed and inhibit the subsequent costly stack manipulations?

To which Dagwynn responds:

> If your C compiler generates additional overhead for local
> blocks, it is not very good.  Most reasonable implementations
> reserve enough stack at function entry for the deepest local
> block nesting within the function, so that there is no
> run-time action required upon entering a local block.

Not only `not very good' but seemingly impossible. If a separate
stack frame was created, access to arguments would be different
within the new block. Still doable, but now try jumping in or
out of the block. Getting Hairy!

My experience is that the variable is allocated as if it were
declared local to the funxion, but with it's scope restricted
to the new block. Doug is correct. Fear not, it's cool.

	jim		cottrell@nbs
*/
------

ka@hropus.UUCP (Kenneth Almquist) (02/20/86)

>> If your C compiler generates additional overhead for local
>> blocks, it is not very good.  Most reasonable implementations
>> reserve enough stack at function entry for the deepest local
>> block nesting within the function, so that there is no
>> run-time action required upon entering a local block. [DAGWYNN]
>
> Not only `not very good' but seemingly impossible. If a separate
> stack frame [were] created, access to arguments would be different
> within the new block. Still doable, but now try jumping in or
> out of the block. Getting Hairy!		[COTTRELL]

There is at least one compiler out there for which does this.  I know
because I got a bug report on vnews concerning a piece of code where
I jumped into the middle of a block.  The rule is that the effect of
jumping into a block containing declarations is machine dependent.  All
other jumps, such as jumping into a block that contains no variable
declarations or jumping out of a block that does, are all legal, just
as in PL/1.

Cottrell overestimates the difficulty of allocating variables declared
in a block when that block is entered.  On a stack based machine, when
you enter a block with declarations, the compiler merely adjusts the
stack pointer to make room for the variables.  When you exit the block,
either by reaching the bottom or by doing a goto out of it, the stack
pointer must be adjusted back.  This is also fairly simple if the com-
piler is prepared to read the entire routine before it starts generating
code (otherwise it wouldn't know which goto's required adjustment to the
stack pointer).  Ideally, the compiler should also adjust the stack
pointer when compiling a goto into a block that contains declarations,
but C does not require this.

The advantage of this approach is that stack space may be saved if
subroutines are called outside the block.  In most code the space
savings won't make much difference.  The disadvantage is the extra time
spent adjusting the stack pointer.  Again this cost is likely to be small,
but my guess is that is the cost of adjusting the stack pointer will
usually outweigh the space savings that it brings, and most compiler
writers appear to agree.  Still, you cannot count on this if you want
to write portable code.
				Kenneth Almquist
				ihnp4!houxm!hropus!ka	(official name)
				ihnp4!opus!ka		(shorter path)

brooks@lll-crg.ARpA (Eugene D. Brooks III) (02/20/86)

The idea of creating a new context with each entry to a {} block,
or at least for some very special ones, has some merit worth
thinking about.  Suppose you have extended C for parallel programming.

main()
{
	int i;
	int shared;

	forall(i from 0 to 9) {
		int private;
		/* lines of code */
	}
}

if the forall loop is looked at as a fork of 10 lines of control
which join up at the closing brace then the opening brace must do
something special to the stack frame so that 10 seperate copies of
'private' are generated.  The int 'shared' is on the stack of the parent
and can be read or written by all of the lines of control but the
stack splits into 10 segments at the opening brace of the forall loop.

rb@ccivax.UUCP (rex ballard) (02/20/86)

In article <139200021@uiucdcsb> robison@uiucdcsb.CS.UIUC.EDU writes:
>
>The output from C compilers I have seen indicates there is no penalty for
>{} blocks with declarations.  All allocation can be done at the function entry.
>I.e., the source code:
>
>     int f(x,y)
>        int x,y;
>	{
>           int a,b;
>	   ...
>	   {
>	      int m,n;
>	      ...
>	   } 
>	   ...
>	}
>
>generates assembly code which allocates a,b,m, and n on the stack upon entry.
>The only difference between a,b and m,n is that m,n is visible
>only within the inner block.

I know of two compilers, one for RT-11, that will allocate the
additional stack space separately.  A 'goto' in or out of an inner
block will cause the stack space to be adjusted before the loop.  This
makes the parser simpler, but requires extra code.  If no
variables are declared, the block is transparent.  Conditional blocks
are better defined inside "allocation local blocks" in this case.

brett@wjvax.UUCP (Brett Galloway) (02/21/86)

This posting may be slightly inappropriate in net.lang.c, but I am interested
in the matter of how variables get declared in routine subblocks (i.e.
within {} within a routine; for example:
routine()
{ int	a;
	{ int b; }
}
I would assume that they are declared in routine()'s stack frame,
and that the only effect of declaring them in a subblock is to restrict
their scope to that subblock.

HOWEVER, in the Mt Xinu port of BSD 4.2 (for a VAX 750), dbx does not seem to
treat them this way; once dbx has moved into a subblock in which variables
are declared, any variables higher up in the stack are marked as
"not active".  This behaviour of dbx appears to be wrong.  My question is
whether this behaviour is simply a shortcoming of dbx's implementation of
subblock scoping, or whether it reflects some peculiarity in the way
variables declared in subblocks are treated by the c compiler.

My apologies if this question is obvious or inappropriate.  Please send me
mail if the answer is not of general interest.

-------------
Brett Galloway
{pesnta,twg,ios,qubix,turtlevax,tymix,vecpyr,certes,isi}!wjvax!brett

davidsen@steinmetz.UUCP (Davidsen) (02/27/86)

In article <289@hropus.UUCP> ka@hropus.UUCP (Kenneth Almquist) writes:
>>> If your C compiler generates additional overhead for local
>>> blocks, it is not very good.  Most reasonable implementations
>>> reserve enough stack at function entry for the deepest local
>>> block nesting within the function, so that there is no
>>> run-time action required upon entering a local block. [DAGWYNN]
>>
>> Not only `not very good' but seemingly impossible. If a separate
>> stack frame [were] created, access to arguments would be different
>> within the new block. Still doable, but now try jumping in or
>> out of the block. Getting Hairy!		[COTTRELL]
>
>There is at least one compiler out there for which does this.  I know
>because I got a bug report on vnews concerning a piece of code where
>I jumped into the middle of a block. ...

On machines which do not have stack hardware, C may be implemented by using
offsets to a register, since all offsets can be calculated at compile time. I
first saw this in the "B" compiler for GE600 (Honeywell 6000/DPS) systems in
about 1970.

On entry to a procedure a register pointed to the return, arguments were
offsets in one direction, and local variables were offsets in the other. The
allocation consisted of assuming a larger number for "bytes in use" when
entering the inner block. There is nothing faster at runtime than an
assumption made at compile time... no code, no overhead.

Point of allocation becomes important when variables are initialized, since
in C it happens when the space is allocated (by this I mean that static
variables are allocated at link time and initialized then, once, while auto
variables are allocated and initialized on entry to a block.

If the variables are allocated on entry to the procedure (which means that all
inner block variables are taking stack space at once), the code to initialize
them still has to be at the head of the inner block. Allocating inner block
variables at procedure entry will save space/time on many machines, while
allocating them at block entry will save stack space.
-- 
	-bill davidsen

	seismo!rochester!steinmetz!--\
       /                               \
ihnp4!              unirot ------------->---> crdos1!davidsen
       \                               /
        chinet! ---------------------/        (davidsen@ge-crd.ARPA)

"It seemed like a good idea at the time..."

cottrell@NBS-VMS.ARPA (COTTRELL, JAMES) (02/28/86)

/*
> >> If your C compiler generates additional overhead for local
> >> blocks, it is not very good.  Most reasonable implementations
> >> reserve enough stack at function entry for the deepest local
> >> block nesting within the function, so that there is no
> >> run-time action required upon entering a local block. [DAGWYNN]
> >
> > Not only `not very good' but seemingly impossible. If a separate
> > stack frame [were] created, access to arguments would be different
> > within the new block. Still doable, but now try jumping in or
> > out of the block. Getting Hairy!		[COTTRELL]
> 
> There is at least one compiler out there for which does this.  I know
> because I got a bug report on vnews concerning a piece of code where
> I jumped into the middle of a block.  The rule is that the effect of
> jumping into a block containing declarations is machine dependent.  All
> other jumps, such as jumping into a block that contains no variable
> declarations or jumping out of a block that does, are all legal, just
> as in PL/1.

While I rarely do this (I did for the Rand editor for the arrow keys,
they had `ESC [ A', & my terminal produced both that & `ESC O A' as well. I
jumped from one switch into the other) I would not expect any penalty
by doing this. I consider the idea of blocks stupid, merely syntactic
sugar. `{' is `then'. `}' is `end'. Functions should be small enuf so
that nested blocks are not needed. But I'm rambling...
 
> Cottrell overestimates the difficulty of allocating variables declared
> in a block when that block is entered.  On a stack based machine, when
> you enter a block with declarations, the compiler merely adjusts the
> stack pointer to make room for the variables.  

And I think you underestimate the difficulty. You are not creating a new
stack frame, which is what the original discussion mentioned. Also, are
you referencing your variables relative to the stack pointer, or the
frame pointer? The stack pointer moves around, especially in expressions
like: `{ int q = 3; printf("%d%d%d%d\n",q,q*q,q*q*q,q*q*q*q); }'.

>When you exit the block,
> either by reaching the bottom or by doing a goto out of it, the stack
> pointer must be adjusted back.  This is also fairly simple if the com-
> piler is prepared to read the entire routine before it starts generating
> code (otherwise it wouldn't know which goto's required adjustment to the
> stack pointer).  Ideally, the compiler should also adjust the stack
> pointer when compiling a goto into a block that contains declarations,
> but C does not require this.

While all this is possible, the way Doug described is so much simpler,
faster, easier, & offers less surprises. Your compiler must have been
written by a nested block freak.

> The advantage of this approach is that stack space may be saved if
> subroutines are called outside the block.  In most code the space
> savings won't make much difference.  The disadvantage is the extra time
> spent adjusting the stack pointer.  Again this cost is likely to be small,
> but my guess is that is the cost of adjusting the stack pointer will
> usually outweigh the space savings that it brings, and most compiler
> writers appear to agree.  Still, you cannot count on this if you want
> to write portable code.

I agree totally. If it's small, who cares. If it's big, use malloc.
The time overhead is the same to adjust the stack pointer by one as
it is to adjust it by one thousand. Unless you have an `add quick'.

> 				Kenneth Almquist
> 				ihnp4!houxm!hropus!ka	(official name)
> 				ihnp4!opus!ka		(shorter path)

	jim		cottrell@nbs
*/
------