Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
Version 7.1

10/7/26 [GH #176]
C-generating back end: always_inline attribute and inlined setjmp calls

When lowering inserts a call of setjmp in a routine that has the GNU
always_inline attribute, the attribute is removed, because a back end GCC
compiler gives an error on an always_inline routine that calls setjmp (see
the entry for EDGcpfe/19110 of 1/10/18). However, the front end's inlining
could also introduce such a call, by expanding a call of an inline routine
whose lowered code calls setjmp inside the always_inline routine. The
generated C code was then rejected by GCC with an error indicating that the
always_inline routine can never be inlined. When GCC is the back end
compiler, such calls are now not inlined into a routine that has the
always_inline attribute. For example, with --g++:

inline void *operator new(decltype(sizeof 0), void *p) noexcept {
return p;
}
inline void operator delete(void *, void *) noexcept {}
struct A { A(); };
struct Alloc { void construct(A *p) { ::new ((void *)p) A(); } };
[[gnu::always_inline]] static inline void tconstruct(Alloc &a, A *p) {
a.construct(p); // Previously, GCC gave an error on the generated code.
}
void f(Alloc &a, A *p) { tconstruct(a, p); }


10/5/26 [GH #195]
C++-generating back end: dependent multi-dimensional subscripts

Expand Down
16 changes: 16 additions & 0 deletions src/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,22 @@ expression can't be inlined.
an_expr_node_ptr operand = expr->variant.operation.operands;
an_expr_node_ptr operand2 = operand->next;
a_constant_ptr con2 = NULL;
#if GNU_EXTENSIONS_ALLOWED && BACK_END_IS_C_GEN_BE
if (op == eok_call &&
gcc_is_generated_code_target &&
innermost_function_scope != NULL &&
innermost_function_scope->variant.routine.ptr->always_inline &&
is_eh_setjmp_routine(routine_from_function_expr(operand))) {
/* The code being inlined calls setjmp, and the routine it is being
inlined into has the GNU always_inline attribute. gcc gives an
error on an always_inline routine that calls setjmp
(because such a routine can never be inlined), so do not inline the
call. */
*inlining_failed = TRUE;
} /* if */
#endif /* GNU_EXTENSIONS_ALLOWED && BACK_END_IS_C_GEN_BE */
/* Look for operations that now have constant operands because of
parameter variables remapped to constants. */
if (is_constant_node(operand) &&
(operand2 == NULL || is_constant_node(operand2))) {
/* The operands are constant. */
Expand Down
11 changes: 11 additions & 0 deletions src/lower_eh.c
Original file line number Diff line number Diff line change
Expand Up @@ -5380,6 +5380,17 @@ __suppress_optim_on_vars_in_try. NULL until created.
STATIC_THREAD a_routine_ptr
setjmp_routine,
suppress_optim_on_vars_in_try_routine;


a_boolean is_eh_setjmp_routine(a_routine_ptr routine)
/*
Return TRUE if routine is the runtime routine setjmp called by the lowered
code for try blocks.
*/
{
return routine != NULL && routine == setjmp_routine;
} /* is_eh_setjmp_routine */

#endif /* DO_FULL_PORTABLE_EH_LOWERING */


Expand Down
4 changes: 4 additions & 0 deletions src/lower_eh.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ extern void lower_try_block(
extern an_expr_node_ptr make_internal_try_expr(an_expr_node_ptr try_expr,
an_expr_node_ptr catch_expr);

#if DO_FULL_PORTABLE_EH_LOWERING
extern a_boolean is_eh_setjmp_routine(a_routine_ptr routine);
#endif /* DO_FULL_PORTABLE_EH_LOWERING */

#if !DO_FULL_PORTABLE_EH_LOWERING
extern an_expr_node_ptr make_thrown_object_address_node(void);
#endif /* !DO_FULL_PORTABLE_EH_LOWERING */
Expand Down
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-176.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eccp -DTEST_NUMBER=1 --gnu_version=150000 --set_flag=no_checking_pragmas --g++ Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-176.rto/default.1.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-176.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eccp -DTEST_NUMBER=2 --gnu_version=150000 --set_flag=no_checking_pragmas --clang Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-176.rto/default.2.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
41 changes: 41 additions & 0 deletions tests/tests/regressions/gh-176.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//type:rp
//options_all:--gnu_version=150000 --set_flag=no_checking_pragmas
//options:--g++:--clang
inline void *operator new(decltype(sizeof 0), void *p) noexcept { return p; }
int deletes;
inline void operator delete(void *, void *) noexcept { ++deletes; }

struct A { A(); };
// The code generated for Alloc::construct calls setjmp, to call the placement
// operator delete if the constructor throws.
struct Alloc { void construct(A *p) { ::new ((void *)p) A(); } };
struct Alloc2 { Alloc a; void construct(A *p) { a.construct(p); } };

[[gnu::always_inline]] inline void tconstruct(Alloc &a, A *p) {
a.construct(p);
}

[[gnu::always_inline]] static inline void sconstruct(Alloc &a, A *p) {
a.construct(p); // Previously gave a gcc error.
}

[[gnu::always_inline]] static inline void s2construct(Alloc2 &a, A *p) {
a.construct(p); // Previously gave a gcc error.
}

void f(Alloc &a, A *p) { tconstruct(a, p); }
void g(Alloc &a, A *p) { sconstruct(a, p); }
void h(Alloc2 &a, A *p) { s2construct(a, p); }

A::A() { throw 1; }

int main() {
alignas(A) unsigned char buf[sizeof(A)];
A *p = (A *)buf;
Alloc a;
Alloc2 a2;
try { f(a, p); } catch (int) {}
try { g(a, p); } catch (int) {}
try { h(a2, p); } catch (int) {}
return deletes == 3 ? 0 : 1;
}
Loading