On 2020-03-09 13:13, Richard Sandiford wrote:
> Thanks for doing this.

Hi Richard, thanks for your response.

> "J.W. Jagersma" <jwjager...@gmail.com> writes:
>> On 2020-03-07 20:20, Segher Boessenkool wrote:
>>> Some comments:
>>>
>>>> +When non-call exceptions (@option{-fnon-call-exceptions}) are enabled, a
>>>> +@code{volatile asm} statement is also allowed to throw exceptions.  If it
>>>> +does, then the compiler assumes that its output operands have not been 
>>>> written
>>>> +yet.
>>>
>>> That reads as if the compiler assumes the outputs retain their original
>>> value, but that isn't true (I hope!)  The compiler assumes the output
>>> are clobbered, but it doesn't assume they are assigned any definite
>>> value?
>>
>> Register outputs are assumed to be clobbered, yes.  For memory outputs
>> this is not the case, if the asm writes it before throwing then the
>> memory operand retains this value.  It should be the user's
>> responsibility to ensure that an asm has no side-effects if it throws.
> 
> I guess one problem is that something like "=&m" explicitly says that
> the operand is clobbered "early", and I think it would be fair for
> "early" to include clobbers before the exception.  So IMO we should
> allow at least early-clobbered memory outputs to be clobbered by the
> exception.

Is "=&m" not equivalent to "=m" in every case?  As I understand it, the
earlyclobber modifier is only relevant for register outputs.  It does
not specify anything about clobbering the output itself, it only says
that an input could be clobbered if it is allocated in the same
register (or if a memory input uses this register as index).

> And if we do that, then I'm not sure there's much benefit in trying to
> treat the non-earlyclobber memory case specially.
> 
> It would be good to have testcases for the output cases.  E.g. for:
> 
>         int foo;
>         int bar = 0;
>         try
>           {
>             foo = 1;
>             asm volatile ("..." : "=m" (foo));
>           }
>         catch (...whatever...)
>           {
>             bar = foo;
>           }
>         ...use bar...
> 
> What does "bar = foo" read?  Is it always undefined behaviour if executed?
> Or does it always read "foo" from memory?  Can it be optimised to "bar = 1"?
> Is "foo = 1" dead code?

These are very good points.  But I am not sure how to test for all of
these.  My test case now looks as follows:

    // PR inline-asm/93981
    // { dg-do run }
    // { dg-options "-fnon-call-exceptions -O3" }
    // { dg-xfail-run-if "" { ! *-linux-gnu } }

    #include <csignal>

    struct illegal_opcode { };

    extern "C" void
    sigill (int)
    {
      throw illegal_opcode ( );
    }

    int
    test_mem ()
    {
      int i = 2;
      try
        {
          asm volatile ("mov%z0 $1, %0; ud2" : "=m" (i));
        }
      catch (const illegal_opcode&)
        {
          if (i == 1) return 0;
        }
      return i;
    }

    int
    test_reg ()
    {
      int i = 2;
      try
        {
          asm volatile ("mov%z0 $1, %0; ud2" : "=r" (i));
        }
      catch (const illegal_opcode&)
        {
          if (i == 2) return 0;
        }
      return i;
    }

    int
    main ()
    {
      std::signal (SIGILL, sigill);
      return test_reg () + test_mem ();
    }

I think that should cover most of it.  Am I missing anything?

Reply via email to