[gnu.gcc] address of labels

rfg@MCC.COM (Ron Guilmette) (04/29/89)

It is apparent that C is being used more and more as a kind of high-level
assembly language.

GCC supports this kind of usage quite well in general.  The embedded
assembly language features of GCC are especially helpful in such
applications.

Of course it is the C language itself, and specifically its closeness
to typical hardware that is the real important thing in such situations.
I find that I can typically do better that 95% of even very low level
coding (e.g. diagnostics and boot code) in C.

One blatantly obvious feature seems to be missing however; i.e. the
ability to take the address of a label.  Is there some way of doing
this in GCC that I don't know about?  I'd like to be able to
construct things like jump tables mostly in C, but I can't for the
life of me see how to do it without being able to say something like:

int my_func ()
{
	static void* jump_table[] = { &label0, &label1, &label2 };

label0:
	...

label1:
	...

label2:
	...
}

Now I know that somebody will say that this is just a switch statement
and that I should use that, but I have other uses of label addresses in
mind also.  For instance, I'd like to be able to have a mechanism for
expressing things like Ada-style exception handling, e.g.:

void my_function ()
{
	declare_handler (&exception_handler);

	...

	deactivate_handler ();
	return;

exception_handler:
	deactivate_handler ();
	...
	return;
}

chase@Ozona.orc.olivetti.com (David Chase) (04/29/89)

rfg@MCC.COM (Ron Guilmette) writes:
>
>It is apparent that C is being used more and more as a kind of high-level
>assembly language.
> ...
>One blatantly obvious feature seems to be missing however; i.e. the
>ability to take the address of a label.  Is there some way of doing
>this in GCC that I don't know about?

There's nothing provided with the language, no, but it is possible to
hack it around to let you do this (I know; I did this very thing as an
experiment).  *However*, there's a bit of a gotcha or three:

1) I have no idea what interaction this would have with the rest of
the back-end.  What does this do for (to) optimization, for instance?

2) This interacts quite badly with some of the extensions made to GCC;
in particular, the builtin goodies for dynamically allocating arrays
on the stack within blocks.  (Already you should have figured out that
at a minimum a "label" is really a code address and a stack height.)
(This is the answer that I got from RMS when I proposed this
extension, by the way.)

3) If register allocation is at all sophisticated (I don't know what
GCC is doing today), then it gets really messy; a "label" is not just
an address, but a series of loads and stores (relative to where you
come from, too) to ensure that the various registers contain what they
are supposed to at that point.  Of course, if register allocation is
at all sophisticated you have to get pretty gung-ho with "volatile" to
ensure that setjmp/longjmp allow your program to have any defined
meaning at all, so (in an exception-handling context) this remains a
problem.

I use C as a "high-level assembly language" right now, and I can't say
I'd do it again.  It works great for a portability kit, but I wouldn't
trust the use of an optimizing C compiler (setjmp and longjmp, and not
enough volatile declarations yet) and the resulting code is dubiously
debuggable.  I don't get to use the implementation of choice for
exceptions, and I'm stuck with the C compiler's idea of how floating
point arithmetic "ought" to be done, and I can't even trust the
compiler to do the right thing with returned structure values.

The folks at Xerox PARC made it work for Cedar (debugging, that is),
but it doesn't sound easy.  One thing that they did differently (that
I would emulate if I did it over again) was to generate exceptionally
low-level C; it isn't portable, but it can be parameterized by machine
type, and this avoids some really annoying problems with the C type
system.

----------------

Another way to get the effect which you desire is to get a C compiler
which performs tail-call elimination.  In that way, you can get the
effect of label variables by transforming a single procedure into a
set of smaller procedures which pass around (say) a pointer to a
structure containing the local variables (the whole thing needs to be
wrapped up in something which allocates that structure, then passes it
along).  Instead of label variables, you use function variables.  Of
course, this might not provide the performance that you want, but,
then, you did decide to use C as an assembler.

David

mcgrath@paris.Berkeley.EDU (Roland McGrath) (04/29/89)

You can fake a C jump table with setjmp/longjmp.  It's not too pretty, but
it'll do it:

foo()
{
  jmp_buf env;

  switch (setjmp(env))
    {
    case 0:
      /* The initial setjmp call.  */
      break;
    case 1:
      /* Do stuff, or goto somewhere.  */
    case 2:
      /* ... */
      break;
    }

  /* ... */
  if (do_foo2)
    longjmp(env, 2);


  /* ... */
  if (do_foo98)
    longjmp(env, 98);
}
--
	Roland McGrath
	Free Software Foundation, Inc.
roland@wheaties.ai.mit.edu, mit-eddie!wheaties.ai.mit.edu!roland
Copyright 1989 Roland McGrath, under the GNU General Public License, version 1.