On 2020-03-12 20:26, Segher Boessenkool wrote:
> On Thu, Mar 12, 2020 at 07:42:30PM +0100, J.W. Jagersma wrote:
>> On 2020-03-12 16:32, Segher Boessenkool wrote:
>>> On Thu, Mar 12, 2020 at 02:08:18PM +0100, Richard Biener wrote:
>>>>> It wasn't clear from my message above, but: I was mostly worried about
>>>>> requiring the asm to treat memory operands in a certain way when the
>>>>> exception is thrown.  IMO it would be better to say that the values of
>>>>> memory operands are undefined on the exception edge.
>>>>
>>>> Rather unspecified.  So IMHO on the exception edge the asm() should
>>>> still appear as a def for all outputs so the compiler cannot derive any
>>>> actual values for them.  That of course also means that they must not
>>>> appear to be must-defs since we may not DSE earlier stores for example.
>>>
>>> So make all outputs of an asm that may throw (w/ -fnon-call-exceptions)
>>> inout operands instead?  That works for registers exactly the same, too?
>>
>> Should gcc do this or would it be the user's responsibility?  For
>> memory operands I don't think anything changes if you replace "=m" by
>> "+m".  However changing each "=r" to "+r" would certainly generate less
>> optimal code.
> 
> [  "+m"(x)  means exactly the same as  "=m"(x) : "m"(x)  ]
> 
> Yes, but this is required for all operands that could keep their old
> value on exceptions.  Since this will only happen with
> -fnon-call-exceptions, which already has a significant performance cost,
> I don't think adding just a tiny bit more is so bad?  The benefit is
> that all asm will work, the user cannot do this wrong anymore.

I don't want to unnecessarily pessimize register outputs if it can be
avoided.  And even if you do change register outputs to in+out, they
are still more likely to contain some intermediate value that you would
want to discard on throwing.

If you think of it in terms of a function call, the closest equivalent
of register vs memory outputs would be:
    reg = f ();
vs.
    f (&mem);
If f() throws, then in the first case, 'reg' is never assigned to. For
the second case, you can't expect 'mem' to remain unmodified.

There is also the case of "=rm" and similar which have not been
discussed so far, but are (in my experience) the most common operand
constraint.  I think these can be handled as "=r" if they end up in
memory since they are never offsettable, and are unlikely (impossible?)
to have side-effects if written to.  So I think the following behavior
is the most sensible:

If the output *may* reside in a register (this covers any constraint
combination that includes a register constraint, so also "=rm", etc),
then it is assigned via a temporary and its value discarded after a
throw.
Only pure memory operands, strictly "=m", "=o", etc, are converted to
in+out and assumed to be valid after throwing.

Does that sound reasonable?

Reply via email to