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.