svn commit: r279937 - in head/sys/powerpc: include powerpc

Bruce Evans brde at optusnet.com.au
Fri Mar 13 22:21:52 UTC 2015


On Fri, 13 Mar 2015, Konstantin Belousov wrote:

> On Fri, Mar 13, 2015 at 10:45:46AM -0700, Nathan Whitehorn wrote:
>> We'll need to hack the compiler in this case, since it assumes setjmp()
>> saves and restores the vector registers. I'm really not sure which
>> option is worse.
>
> Changing the compiler is arguably much worse than breaking ABI of the
> tier 2 platform, indeed.  We must maintain the situation where the stock
> build of the compilers work out of box.
>
> Still, how the compiler' assumptions are laid out ?  It could be argued
> that compilers on x86 also assume that FPU register file is restored by
> longjmp.

That would be a compiler bug.

> %st* and %xmm* are defined as not preserved across function
> calls, but I suspect that practical rule for setjmp() is that floating
> vars better not be used in the target frame.

Indeed.  They are not call-saved for ordinary functions, and it would be
stupid for the compiler to treat setjmp() differently, since setjmp()
wants to clobber the registers even more than an ordinary functions.

> Hm, indeed f14-f31 and v20-v31 are marked as non-volatile for 64bit ABI.

Powerpc has zillions of call-saved registers (I know little about powerpc,
but checked this in its gcc .md file).

There are complications for the FP environment.  setjmp() is specified
to save "its" environoment and longjmp() is specified to restore the
environment saved by setjmp().  Environments seem to be under-specified.
Floating point has an state that is actually called an environment, but
is this part of the execution environment?  Anyway, it is clear that
most call-saved parts of the FP environment should be saved and restored
(so that longjmp from signal handlers has a chance of working), but the
exception flags should not be restored (since restoring them would forget
exceptions).  x86 setjmp/longjump attempts to do this, but is still very
buggy:

- i386 _setjmp saves the i387 control word but not not the SSE control
   word (if any).  This is the setjmp that doesn't save the signal mask.
- i386 ___longjmp cannot restore the SSE control word since it was not
   saved.  (_longjmp is a weak alias for ___longjmp.  I don't know why
   _longjmp needs aliasing but _setjmp doesn't.  Similarly for the other
   5 *setjmp/*longjmp pairs in x86.)
- to preserve (that is, _not_ restore) the exception flags, someone
   removed the fninit that I wrote in all the i386 longjmps.  This broke:
   - cleaning up of the state.  Especially the tag word.  Cleaning is
     needed for at least longjmp() from FreeBSD-[1-4]-compat signal
     handlers which can be entered with any FP state
   - avoiding getting an exception when reloading the FP control word.
   The correct change is something like:
     fnstsw %ax
     fninit
     fstenv tmp(%esp)
     # set the pending-exception bit in %ax if there are any unmasked
     #   exceptions for the _new_ cw, else clear it
     # store %ax in tmp env
     # copy saved cw from jmp_buf to tmp env
     fldenv tmp(%esp)
     # do related stuff for SSE (simpler)
   The trip through the tmp env is needed since there is no instruction
   to load the new status word from a registers; anyway, it would be
   difficult to load the cw and status word separately without risking a
   trap for one (fninit only cleans enough for the next instruction).
- i386 setjmp and __longjmp are identically broken.
- i386 sigsetjmp and __siglongjmp are not even identically broken.  They
   used to be almost identical copies of setjmp and __longjmp (sigsetjmp
   == setjmp in BSD).  They shouldn't exist except as strong aliases,
   but still do despite extensive use of weak aliases and namespace
   messes like the triply underscored ___longjmp.  No one removed the
   fninit's from them.  So they are missing most of the above bugs,
   but the exception flags are not preserved as intended.

- amd64 _setjmp, setjmp, and sigsetjmp are OK, except for the existence
   of sigsetjmp as a non-alias.  They do preserve mxcsr.  The bug of the
   existence of the file containing sigsetjmp was copied perfectly from
   i386, including my comments in it saying why it should not exist.
- amd64 ___longjmp does preserve SSE exceptions.  This requires merging
   the current exceptions with the saved control word in mxcsr.  SSE
   exceptions are synchronous, and ldmxcsr cannot trap, so I think it
   is not necessary to adjust the pending-exception flag (or there is no
   such flag in mxcsr).  amd64 _longjmp then has the same bugs as i386
   _longjmp restoring the i387 cw, tag word and pending-exception flag
   without trapping.
- amd64 __longjmp is like amd64 ___longjmp.
- amd64 __siglongjmp is not even like amd64 ___longjmp.  No one removed
   the fninit there either.

These details are of course arcane and rarely matter.  I tested the old
versions using longjmp from signal handlers.  FreeBSD-[1-4] signal handlers
get the interrupted FP context.  One interesting case is when the compiler
temporarily changes the rounding mode.  This may be interrupted by even a
non-FP related signal like SIGINT.  For longjmp to work, it must switch
to the default rounding mode.  Now (at least on x86), signal handlers get
a clean state and longjmp doesn't need to do this particular cleaning
(except in when mixing with old libraries that use old signal handlers).
But fenv support for C99 makes things more complicated -- it isn't clear
if the state set by an fenv call should be cleaned.

Bruce


More information about the svn-src-head mailing list