https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94357

            Bug ID: 94357
           Summary: Inline assembly with memory operand is considered
                    nothrow with `-fnon-call-exceptions`
           Product: gcc
           Version: 9.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: sagebar at web dot de
  Target Milestone: ---

Created attachment 48130
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=48130&action=edit
Issue demonstration

The documentation of `-fnon-call-exceptions`
(https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fnon-call-exceptions)
states that any instruction with a "memory references" is allowed to throw a
c++ exception (or in other words: do what essentially boils down to performing
a CFI unwind, and calling `__gxx_personality_v0()` without that function
calling `std::terminate()` because the faulting PC isn't allowed to trap)

However, from what I can tell by looking at the GCC sources
(gcc/tree-eh.c:tree_could_trap_p(); case ASM_EXPR), GCC only considers an
`asm()` statement as being able to cause a non-call-exception (i.e. trap) when
written as `asm volatile()`.

Given the fact that "memory references" is listed as one of the possible
reasons for an instruction to be trapable, it stands reason that any inline
assembly statement that uses memory operands (i.e. `asm("..." : "=m" (...))` or
`asm("..." : : "m" (...))`), as well as any assembly statement that specifies
"memory" as part of its clobber list (i.e. `asm("..." : : : "memory")`) should
also be considered as potentially containing instructions that may trap.

Note also that during my investigation I've noticed that the catch(...) block
is fully optimized away in the following case ... (`gcc -S` does contain `/*
Hello */`, but doesn't contain `/* World! */`):
```c
void foo() {
        try {
                __asm__ __volatile__("/* Hello */");
        } catch (...) {
                __asm__("/* World! */");
        }
}
```

... but isn't optimized away when the first `__asm__ __volatile__` is placed in
an inline function (`gcc -S` does contains both `/* Hello */` and `/* World!
*/`):
```c
inline void bar() {
        __asm__ __volatile__("/* Hello */");
}
void foo() {
        try {
                bar();
        } catch (...) {
                __asm__("/* World! */");
        }
}
```


The attached file shows the different cases where `__asm__` appears in `try ...
catch`, and should be compiled as follows (note that -O* flags don't have any
affect on which catch-statements get deleted; -O0 has the same output as -O3):
$ g++ -S -x c++ -fnon-call-exceptions attaced-file.cc -o - | grep "reference"

The current output is:```
        call    must_reference_1
```

A consistent output (with the current rules concerning __volatile__) would look
like:```
        call    must_reference_1
        call    must_reference_2
        call    must_reference_3
```

And an output consistent with published documentation would look like:```
        call    must_reference_1
        call    must_reference_2
        call    must_reference_3
        call    must_reference_4
        call    must_reference_5
        call    must_reference_6
```

Reply via email to