[comp.lang.c] asm statements & the stack

andrew@teletron.UUCP (Andrew Scott) (10/06/87)

Many C compilers have one variation or another on the asm() "function".  It
allows one to insert inline assembly code in the middle of C code.  I have
been playing around with it a bit, and have a question.  (The code I'm writing
is targeted for a specific hardware system, so no flames for unportability
please).

What can I do in an asm() statement?  The question arises because I was
bitten by the compiler's code generation method.  I was writing a function
(for the 68000, if you don't recognize the assembly code) which must be
non-interruptable.  I have to disable all interrupts before executing the code,
and re-enable them to the previous level when I'm done.  My function looked
something like:

	#define disable	asm("mov.w %sr,-(%sp)"); asm("mov.w &0x2700,%sr")
	/* push current status register on stack, mask out interrupts */

	#define restore	asm("mov.w (%sp)+,%sr")
	/* restore status register from stack save location */

	foo(a, b)
	int a, b;
	{
		int x, y;

		disable;
		bar(a, b);
		/* other stuff */
		restore;
	}

Was I correct in assuming the stack would be available to save the status reg.?
The compiler I use (stock AT&T /bin/cc) does a wierd thing upon the call to foo.
It allocates one more longword than necessary when creating foo's stack frame.
The actual assembly output of the above function (obtained via cc -S) is:

	global	foo
foo:
	link	%fp,&-12		/* allocate space for x, y & ? */
	mov.w	%sr,-(%sp)		/* disable */
	mov.w	&0x2700,%sr		/*   ""    */
	mov.l	12(%fp),(%sp)		/* note this is not a push of b */
	mov.l	8(%fp),-(%sp)		/* push a on stack */
	jsr	bar
	add.w	&4,%sp			/* clean up stack */
	mov.w	(%sp)+,%sr		/* restore */
	unlk	%fp			/* dealloocate stack frame */
	rts

Note that 12 bytes were allocated for local variables within the stack frame.
Only 8 bytes (four each for x & y) are needed.  Therefore, the stack pointer
points to an unused longword location.  Notice that the call to bar only
pushes one argument.  The argument b is simply moved to the unused location.
This really messes up the intended action of disable, because the saved
status register is overwritten.

To conclude a long story, should I really be angry at whoever wrote the compiler
for doing such a strange thing, or should I avoid asm statements altogether
because I can't count on anything?  The alternative is rather barfy:

	foo(a, b)
	int a, b;
	{
		int x, y;
		short ps;

		ps = disable();		/* actual function call */
		bar(a, b);
		/* other stuff */
		restore(ps);
	}

Here, disable & restore are actual function calls to external assembly coded
subroutines.  There is much more overhead involved, and the functions which
require the use of disable & restore are all quite time critical.

Thanks for enduring a rather long posting and question.

	Andrew

eric@ms.uky.edu (Eric Herrin) (10/07/87)

My general philosophy when doing this sort of thing is to use static data
structures instead of the stack.  This at least allows the compiler to 
do any funny business it likes with the stack.

The method of keeping the stack pointer pointing at an unused location is
quite common, as I have used many unix compilers which do this.  I believe
if you are going to the trouble of hacking in assembly code anyway, you
should simply look at what is being generated before running the routines.
Then you could, in your case, use the unused location yourself and then
decrement the stack pointer to keep the compiled code happy.


				eric



-- 
|    Eric Herrin II				     	cbosgd!ukma!eric      |
|    "'tis better to be silent                         	eric@UKMA.BITNET      |
|     and be THOUGHT a fool, than to open              	eric@ms.uky.csnet     |
|     one's mouth and remove all doubt."                eric@ms.uky.edu       |

karl@haddock.ISC.COM (Karl Heuer) (10/08/87)

In article <110@teletron.UUCP> andrew@teletron.UUCP (Andrew Scott) writes:
>[Tried to use the stack from an asm() instruction, and got bitten]

Well, in my opinion, the compiler is free to do whatever is easiest or most
efficient as long as it has the appropriate semantics.  If an asm() escape is
provided, it's the user's responsibility to know enough about the compiler to
avoid colliding with the generated code.

>The compiler I use ... allocates one more longword than necessary when
>creating foo's stack frame.

If this is a consistent feature, you could save %sr in that slot, and then
decrement the stack to make a new slot for the function to use.  Or, you could
avoid the stack entirely and store %sr in a static location, if you don't need
to nest the disable/enable calls.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

chip@ateng.UUCP (Chip Salzenberg) (10/09/87)

In article <110@teletron.UUCP> andrew@teletron.UUCP (Andrew Scott) writes:
>
>	#define disable	asm("mov.w %sr,-(%sp)"); asm("mov.w &0x2700,%sr")
>	/* push current status register on stack, mask out interrupts */
>
>	#define restore	asm("mov.w (%sp)+,%sr")
>	/* restore status register from stack save location */
>
>The compiler I use (stock AT&T /bin/cc) does a wierd thing upon the call to foo.
>It allocates one more longword than necessary when creating foo's stack frame.

This is a feature!  Your C code runs faster as a result.

Try the following:

#define disable { asm("mov.w %sr,(%sp)"); \
		  asm("subq.l &4,%sp"); \
		  asm("mov.w &0x2700,%sr"); }

#define enable  { asm("addq.l &4,%sp"); \
		  asm("mov.w (%sp),%sr"); }

>	Andrew

-- 
Chip Salzenberg         "chip@ateng.UUCP"  or  "{uunet,usfvax2}!ateng!chip"
A.T. Engineering        My employer's opinions are not mine, but these are.
   "Gentlemen, your work today has been outstanding.  I intend to recommend
   you all for promotion -- in whatever fleet we end up serving."   - JTK

jholbach@wright.EDU (Jim Holbach) (10/10/87)

in article <110@teletron.UUCP>, andrew@teletron.UUCP (Andrew Scott) says:
> 
> The compiler I use (stock AT&T /bin/cc) does a wierd thing upon the call to foo.
> It allocates one more longword than necessary when creating foo's stack frame.

	The logic I've read for this is that it reduces the amount
of pushing and popping required for function calls (from within functions)
because at least one longword is always sitting on the stack. Only when
more than one parameter is used, is it necessary to change the stack
pointer. (This was from the manual for C with CPM-68K but it seems that
the same logic is involved here. )

> This really messes up the intended action of disable, because the saved
> status register is overwritten.
> 
> To conclude a long story, should I really be angry at whoever wrote the compiler
> for doing such a strange thing, or should I avoid asm statements altogether
> because I can't count on anything?  The alternative is rather barfy:
> require the use of disable & restore are all quite time critical.

	Why not just adapt your definitions of enable/disable to
the observed behavior of your compiler? Since you know and accept that
the definitions are machine/compiler specific, how about something like 
the following:

 	#define disable	asm("mov.w %sr,(%sp)"); \
 			asm("mov.w &0x2700,%sr"); \
			asm("sub.w &4,%sp")

 	#define restore	asm("add.w &4,%sp"); \
			asm("mov.w (%sp)+,%sr")

	Jim Holbach
	Wright State University
	Dayton, Ohio

henry@utzoo.UUCP (Henry Spencer) (10/11/87)

> What can I do in an asm() statement? ...

The answer is utterly, totally, irremediably compiler-dependent.  If your
compiler documentation does not discuss the issue, then the only answer is
"try it and see", remembering that you will have to re-try it with every
new release of the compiler!
-- 
"Mir" means "peace", as in           |  Henry Spencer @ U of Toronto Zoology
"the war is over; we've won".        | {allegra,ihnp4,decvax,utai}!utzoo!henry

henry@utzoo.UUCP (Henry Spencer) (10/11/87)

> The method of keeping the stack pointer pointing at an unused location is
> quite common, as I have used many unix compilers which do this...

Yes, people trying to work in assembler around C compilers should be aware
that this is a standard tactic.  There are three reasons for it.  First,
moving a value to an already-vacant stack-top location is quicker than
pushing it onto the stack in many machines.  Second, it becomes unnecessary
to remove the argument from the stack after calling a one-argument function.
And third, on some machines it is easier to remove one or two words from
the stack than to remove a more general number, and these efficient removal
sequences then apply to a larger fraction of all functions.

The first and second reasons also apply to using the stacktop location as
an expression-evaluation temporary.

This tactic does require careful attention to situations like function calls
within function argument lists, but it is a cheap and effective optimization
if done right.
-- 
"Mir" means "peace", as in           |  Henry Spencer @ U of Toronto Zoology
"the war is over; we've won".        | {allegra,ihnp4,decvax,utai}!utzoo!henry

john@frog.UUCP (John Woods, Software) (10/15/87)

In article <110@teletron.UUCP>, andrew@teletron.UUCP (Andrew Scott) writes:
> 
> Many C compilers have one variation or another on the asm() "function"...
> What can I do in an asm() statement?

"Nothing."

> The question arises because I was
> bitten by the compiler's code generation method.

That's where the answer comes from.

> I was writing a function which must be non-interruptable. ...
> Should I be angry with the compiler writer...?
No.

The only solution you can believe in is to use function calls to assembly
modules.  This way, you can (A) know that it really works for real, not by
accident, (B) change the compiler at will (next release of the same one, or
a new compiler altogether -- which would basically chuck your old asm()s out
the window, most likely, and SOME compilers do not permit asm statements
at all (some don't even have an assembly language intermediate stage)),
and (C) change to a new processor type with less effort (the assembly
code is localized in one module).  If your time-critical routine cannot deal
with wasting 72 clock cycles on subroutine linkage (5.76 microseconds at
12.5Mc, plus wait states), then you probably ought to write the whole routine
in assembly, because the C compiler is probably wasting more than that on
other little bobbles.

"Pluck the millisecond-wasting bad algorithms from thine own mind before
complaining about the micro-second wasting nature of high-level languages."

Or something like that.

--
John Woods, Charles River Data Systems, Framingham MA, (617) 626-1101
...!decvax!frog!john, ...!mit-eddie!jfw, jfw@eddie.mit.edu

"Cutting the space budget really restores my faith in humanity.  It
eliminates dreams, goals, and ideals and lets us get straight to the
business of hate, debauchery, and self-annihilation."
		-- Johnny Hart