Hello,

On Mon, 16 Mar 2020, Richard Sandiford wrote:

> Segher Boessenkool <seg...@kernel.crashing.org> writes:
> > On Mon, Mar 16, 2020 at 05:47:03PM +0000, Richard Sandiford wrote:
> >> Segher Boessenkool <seg...@kernel.crashing.org> writes:
> >> >> we do delete "x = 1" for f1.   I think that's the expected behaviour.
> >> >> We don't yet delete the initialisation in f2, but I think in principle
> >> >> we could.
> >> >
> >> > Right.  And this is incorrect if the asm may throw.
> >> 
> >> Well...
> >> 
> >> >> So the kind of contract I was advocating was:
> >> >> 
> >> >> - the compiler can't make any assumptions about what the asm does
> >> >>   or doesn't do to output operands when an exception is raised
> >> >> 
> >> >> - the source code can't make any assumption about the values bound
> >> >>   to output operands when an exception is raised
> >> 
> >> ...with this interpretation, the deletions above would be correct even
> >> if the asm throws.
> >
> > The write to "x" *before the asm* is deleted.  I cannot think of any
> > interpretation where that is correct (this does not involve inline asm
> > at all: it is deleting an observable side effect before the exception).
> 
> It's correct under the contract above :-)
> 
> >> > And the easiest (and only feasible?) way to do this is for the compiler
> >> > to automatically make an input for every output as well, imo.
> >> 
> >> Modifying the asm like that feels a bit dangerous,
> >
> > Yes, obviously.  The other option is to accept that almost all existing
> > inline asm will have UB, with -fnon-call-exceptions.  I think that is
> > an even less desirable option.
> >
> >> And the other problem
> >> still exists: he compiler might assume that the output isn't modified
> >> unless the asm completes normally.
> >
> > I don't understand what this means?  As far as the compiler is concerned
> > any asm is just one instruction?  And it all executes completely always.
> > You need to do things with the constraints to tell the compiler it does
> > not know some of the values around.  If you have both an input and an
> > output for a variable, the compiler does not know what value is written
> > to it, and it might just be the one that was the input already (which is
> > the same effect as not writing it at all).
> 
> Normally, for SSA names in something like:
> 
>   _1 = foo ()
> 
> the definition of _1 does not take place when foo throws.

Mostly, but maybe we need to lift this somewhen.  E.g. when we support 
SSA form for non-registers; the actual return migth then be via invisible 
reference, and hence the result might be changed even if foo throws.  That 
also could happen right now for some return types depending on the 
architecture (think large float types).  Our workaround for some of these 
cases (where it's obvious that the result will lie in memory) is to put 
the real copy-out into an extra gimple insn and make the LHS be a 
temporary; but of course we don't want that with too large types.

> Similarly for non-call exceptions on other statements.  It sounds like 
> what you're describing requires the corresponding definition to happen 
> for memory outputs regardless of whether the asm throws or not, so that 
> the memory appears to change on both excecution paths.  Otherwise, the 
> compiler would be able to assume that the memory operand still has its 
> original value in the exception handler.

Well, it's both: on the exception path the compiler has to assume that the 
the value wasn't changed (so that former defines are regarded as dead) or 
that it already has changed (so that the effects the throwing 
"instruction" had on the result (if any) aren't lost).  The easiest for 
this is to regard the result place as also being an input.

(If broadened to all instructions under -fnon-call-exceptions, and not 
just to asms will have quite a bad effect on optimization capabilities, 
but I believe with enough force it's already possible now to construct 
miscompiling testcases with the right mixtures of return types and ABIs)


Ciao,
Michael.

Reply via email to