r230255 - Only lower __builtin_setjmp / __builtin_longjmp to

Joerg Sonnenberger joerg at britannica.bec.de
Tue Mar 3 12:51:59 PST 2015


On Tue, Mar 03, 2015 at 01:00:30PM -0600, Hal Finkel wrote:
> > Huh? How do you know that the intermediate functions haven't
> > clobbered a
> > register? Without unwinding, which we explicitly do not want to do
> > here,
> > you can't. As such you *can't* avoid the spilling.
> 
> You can because, for a caller-saved register, the caller saved them (if
> it, indeed, needed to do so). That's the nice thing about the builtins:
> they're call-site specific. So when you call setjmp, you don't need to
> save them if you don't need them afterward. When the caller of setjmp
> returns, its caller will restore those registers as needed (as it would
> have anyway). You do need to save callee-saved registers (along with
> any other registers the function is actually using).

Again, the intermediate functions are interrupted. They can not restore
whatever they spilled on the stack. You need to save them all unless you
can use CFG or unwind data for doing it more selective. The latter is
not really sj/lj anymore and tends to perform horrible on many platforms
due to the overhead associated with stack unwinding. The former is only
possible in a limited set and I don't think we do that. For all other
registers, they are free game. Good libc implementations exploit that.
I'm ignoring the mess with signal mask saving, which is irrelevant here.
The only difference for the place using setjmp is if you can merge the
the register saving with the function prologue spilling. As I said,
functions using setjmp tend to be simple, the requirement for using
volatile for local variables is a good enough incentive to avoid more
complex things. So unless your architecture is extremely register
starved, it would likely not touch any callee-safe registers at all.
So you can't really gain much on the setjmp side. Of course, there are
old setjmp implementations that are brain dead and will save all
registers. But that's like comparing qsort to bogosort and not to
heapsort.

> I think it is also worth noting that, as we implement them, the job of
> restoring registers is shifted compared to the library calls. So, when
> using library setjmp/longjmp, setjmp saves all of the necessary state
> into the jump buffer, and longjmp restores all necessary state and
> jumps to the designated location. With the builtins, __builtin_setjmp
> itself saves very little state into the jump buffer (only the address
> and some reserved registers), but causes the function calling it to
> spill and restore only necessary state around it. __builtin_longjmp
> restores only the reserved registers necessary to make the jump,
> nothing more (the spill/restore code around the __builtin_setjmp takes
> care of the rest).
> 
> So, for example, if we have some register, v1, which is caller saved...
> 
> void bar(jmp_buf &jb) {
>   // v1 is not used in this function, and the caller saved it if necessary
>   // so v1 is not spilled here
>   __builtin_setjmp(&jb);
>   // and v1 is not restored here (the caller saved it if necessary, and will restore it if necessary, when we return)
> }

If v1 is not callee-safe, a setjmp/longjmp pair doesn't have to
preserve. There is nothing to be gained.

> 
> foo() {
>   jmp_buf jb;
>   // v1 is saved here if necessary
>   bar(jb);
>   ...
>   // the value of v1 saved above is loaded here somewhere
>   ...
>   __builtin_longjmp() // this does not explicitly restore v1 here, although a library call would need to
> }

See above, that's wrong. There is one constellation where
__builtin_setjmp can be effectively free: if it called more than once in
the same function. I would be surprised if that happens regulary outside
EH...

Joerg



More information about the cfe-commits mailing list