The following patch extends the generation of exception handling information, so that it is possible to catch exceptions thrown from volatile asm statements, when -fnon-call-exceptions is enabled. Parts of the gcc code already suggested this should be possible, but it was never fully implemented.
Two new test cases are added. The target-dependent test should pass on platforms where throwing from a signal handler is allowed. The only platform I am aware of where that is the case is *-linux-gnu, so it is set to XFAIL on all others. gcc/ 2020-03-11 Jan W. Jagersma <jwjager...@gmail.com> PR inline-asm/93981 * tree-cfg.c (make_edges_bb): Make EH edges for GIMPLE_ASM. * tree-eh.c (lower_eh_constructs_2): Add case for GIMPLE_ASM. Assign register output operands to temporaries. * doc/extend.texi: Document that volatile asms can now throw. gcc/testsuite/ 2020-03-11 Jan W. Jagersma <jwjager...@gmail.com> PR inline-asm/93981 * g++.target/i386/pr93981.C: New test. * g++.dg/eh/pr93981.C: New test. --- gcc/doc/extend.texi | 5 +++ gcc/testsuite/g++.dg/eh/pr93981.C | 18 ++++++++ gcc/testsuite/g++.target/i386/pr93981.C | 55 +++++++++++++++++++++++++ gcc/tree-cfg.c | 2 + gcc/tree-eh.c | 32 ++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 gcc/testsuite/g++.dg/eh/pr93981.C create mode 100644 gcc/testsuite/g++.target/i386/pr93981.C diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index e0e7f540c21..b51e34c617a 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -9577,6 +9577,11 @@ errors during compilation if your @code{asm} code defines symbols or labels. Using @samp{%=} (@pxref{AssemblerTemplate}) may help resolve this problem. +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 its register output operands are assumed to be clobbered and will not be +used. Memory operands, however, are always considered valid. + @anchor{AssemblerTemplate} @subsubsection Assembler Template @cindex @code{asm} assembler template diff --git a/gcc/testsuite/g++.dg/eh/pr93981.C b/gcc/testsuite/g++.dg/eh/pr93981.C new file mode 100644 index 00000000000..a9adb5c069e --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/pr93981.C @@ -0,0 +1,18 @@ +// PR inline-asm/93981 +// { dg-do compile } +// { dg-options "-fnon-call-exceptions" } + +void +f () +{ + try + { + asm ("#try"); + } + catch (...) + { + asm ("#catch"); + } +} + +// { dg-final { scan-assembler "#catch" } } diff --git a/gcc/testsuite/g++.target/i386/pr93981.C b/gcc/testsuite/g++.target/i386/pr93981.C new file mode 100644 index 00000000000..7a3117901f9 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr93981.C @@ -0,0 +1,55 @@ +// PR inline-asm/93981 +// { dg-do run } +// { dg-options "-fnon-call-exceptions -O3" } +// { dg-xfail-if "" { ! *-linux-gnu } } +// { dg-xfail-run-if "" { ! *-linux-gnu } } + +#include <signal.h> + +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 = 8; + try + { + asm volatile ("mov%z0 $4, %0; ud2" : "=r" (i)); + } + catch (const illegal_opcode&) + { + if (i == 8) return 0; + } + return i; +} + +int +main () +{ + struct sigaction sa = { }; + sa.sa_handler = sigill; + sa.sa_flags = SA_NODEFER; + sigaction (SIGILL, &sa, 0); + return test_mem () | test_reg (); +} diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index f7b817d94e6..c21a7978493 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -913,6 +913,8 @@ make_edges_bb (basic_block bb, struct omp_region **pcur_region, int *pomp_index) break; case GIMPLE_ASM: + if (stmt_can_throw_internal (cfun, last)) + make_eh_edges (last); make_gimple_asm_edges (bb); fallthru = true; break; diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index 2a409dcaffe..58b16aa763a 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -2077,6 +2077,8 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi) DECL_GIMPLE_REG_P (tmp) = 1; gsi_insert_after (gsi, s, GSI_SAME_STMT); } + +record_throwing_stmt: /* Look for things that can throw exceptions, and record them. */ if (state->cur_region && stmt_could_throw_p (cfun, stmt)) { @@ -2085,6 +2087,36 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi) } break; + case GIMPLE_ASM: + { + /* As above with GIMPLE_ASSIGN. Change each register output operand + to a temporary and insert a new stmt to assign this to the original + operand. */ + gasm *asm_stmt = as_a <gasm *> (stmt); + if (stmt_could_throw_p (cfun, stmt) + && gimple_asm_noutputs (asm_stmt) > 0 + && gimple_stmt_may_fallthru (stmt)) + { + for (unsigned i = 0; i < gimple_asm_noutputs (asm_stmt); ++i) + { + tree op = gimple_asm_output_op (asm_stmt, i); + tree opval = TREE_VALUE (op); + if (tree_could_throw_p (opval) + || !is_gimple_reg_type (TREE_TYPE (opval)) + || !is_gimple_reg (get_base_address (opval))) + continue; + + tree tmp = create_tmp_reg (TREE_TYPE (opval)); + gimple *s = gimple_build_assign (opval, tmp); + gimple_set_location (s, gimple_location (stmt)); + gimple_set_block (s, gimple_block (stmt)); + TREE_VALUE (op) = tmp; + gsi_insert_after (gsi, s, GSI_SAME_STMT); + } + } + } + goto record_throwing_stmt; + case GIMPLE_COND: case GIMPLE_GOTO: case GIMPLE_RETURN: -- 2.25.1