[PATCH v2] generate EH info for volatile asm statements (PR93981)
Richard Sandiford
richard.sandiford@arm.com
Fri Mar 13 13:31:19 GMT 2020
"J.W. Jagersma" <jwjagersma@gmail.com> writes:
> On 2020-03-12 10:59, Richard Sandiford wrote:
>> The other case I mentioned was equivalent to:
>>
>> int
>> test_mem2 ()
>> {
>> int i = 2;
>> try
>> {
>> asm volatile ("ud2; mov%z0 $1, %0" : "=m" (i));
>> }
>> catch (const illegal_opcode&)
>> {
>> if (i == 2) return 0;
>> }
>> return i;
>> }
>>
>> Is this test expected to pass?
>
> Good point. Yes, this should pass, and I thought it did, but it seems
> I was mistaken. To fix that would require transforming "=m" into "+m"
> as Segher suggested.
>
>> However, these "mem" tests are testing gimple register types, so they'll
>> still be SSA names outside of the asm. It would also be good to test what
>> happens for non-register types, e.g. something like:
>>
>> int
>> test_mem3 ()
>> {
>> int i[5] = { 1, 2, 3, 4, 5 };
>> try
>> {
>> asm volatile ("ud2; <insert asm here>" : "=m" (i));
>> }
>> catch (const illegal_opcode&)
>> {
>> if (i[0] == 1 && ...) return 0;
>> }
>> return ...;
>> }
>>
>> and similarly for ud2 after the store.
>
> I think I see what you mean. Would such a test not also cover what the
> current test_mem() function does? If so then that could be removed.
I think it's better to have tests for both the is_gimple_reg_type case
and the !is_gimple_reg_type case, so keeping test_mem sounds better.
> Also in my current patch I used: (tree-eh.c:2104)
>
> if (tree_could_throw_p (opval)
> || !is_gimple_reg_type (TREE_TYPE (opval))
> || !is_gimple_reg (get_base_address (opval)))
>
> to determine the difference between a register and memory type. Could
> there potentially be a case where that identifies an operand as gimple
> register type, but ends up compiled as a memory operand to an asm?
The constraints can support both registers and memory, e.g. via "rm"
or "g". I'm not sure off-hand what we do with those for gimple.
>> 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.
>
> I'm not sure about the semantic difference between undefined and
> unspecified. But gcc should not write to any memory after a throw,
> because that write operation itself may have side effects. Likewise
> asm memory operands should not be redirected to a temporary for the
> same reason, and also because gcc can't possibly know which parts of
> an (offsettable) memory operand are written to.
This might not be what you mean, but for:
int
f1 (void)
{
int x = 1;
asm volatile ("": "=m" (x));
return x;
}
struct s { int i[5]; };
struct s
f2 (void)
{
struct s x = { 1, 2, 3, 4, 5 };
asm volatile ("": "=m" (x));
return x;
}
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.
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
Thanks,
Richard
More information about the Gcc-patches
mailing list