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?