Skip to content

Commit 4b6c10b

Browse files
mborlanddaveedvdv-nvidia
authored andcommitted
When generating a setjmp, stop inlining and drop [[always_inline]] for the enclosing function. [GH edgcpp#176]
1 parent e24bb9a commit 4b6c10b

9 files changed

Lines changed: 104 additions & 0 deletions

File tree

‎src/Changes‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
Version 7.1
22

3+
10/7/26 [GH #176]
4+
C-generating back end: always_inline attribute and inlined setjmp calls
5+
6+
When lowering inserts a call of setjmp in a routine that has the GNU
7+
always_inline attribute, the attribute is removed, because a back end GCC
8+
compiler gives an error on an always_inline routine that calls setjmp (see
9+
the entry for EDGcpfe/19110 of 1/10/18). However, the front end's inliner
10+
(enabled when MINIMAL_INLINING is TRUE) could also introduce such a call when
11+
expanding a call of an inline function whose lowered code calls setjmp inside
12+
the always_inline routine. The generated C code was then rejected by GCC
13+
with an error indicating that the always_inline routine can never be inlined.
14+
For example, with --g++:
15+
16+
inline void *operator new(decltype(sizeof 0), void *p) noexcept {
17+
return p;
18+
}
19+
inline void operator delete(void *, void *) noexcept {}
20+
struct S { S(); };
21+
struct Alloc { void construct(S *p) { ::new ((void *)p) S(); } };
22+
[[gnu::always_inline]] static inline void make(Alloc &a, S *p) {
23+
a.construct(p); // Previously, GCC gave an error on the generated code.
24+
}
25+
void f(Alloc &a, S *p) { make(a, p); }
26+
27+
This is now fixed: When GCC is the back end compiler, such calls are now not
28+
inlined into a routine that has the always_inline attribute.
29+
30+
331
10/5/26 [GH #195]
432
C++-generating back end: dependent multi-dimensional subscripts
533

‎src/inline.c‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,22 @@ expression can't be inlined.
820820
an_expr_node_ptr operand = expr->variant.operation.operands;
821821
an_expr_node_ptr operand2 = operand->next;
822822
a_constant_ptr con2 = NULL;
823+
#if GNU_EXTENSIONS_ALLOWED && BACK_END_IS_C_GEN_BE
824+
if (op == eok_call &&
825+
gcc_is_generated_code_target &&
826+
innermost_function_scope != NULL &&
827+
innermost_function_scope->variant.routine.ptr->always_inline &&
828+
is_eh_setjmp_routine(routine_from_function_expr(operand))) {
829+
/* The code being inlined calls setjmp, and the routine it is being
830+
inlined into has the GNU always_inline attribute. gcc gives an
831+
error on an always_inline routine that calls setjmp
832+
(because such a routine can never be inlined), so do not inline the
833+
call. */
834+
*inlining_failed = TRUE;
835+
} /* if */
836+
#endif /* GNU_EXTENSIONS_ALLOWED && BACK_END_IS_C_GEN_BE */
837+
/* Look for operations that now have constant operands because of
838+
parameter variables remapped to constants. */
823839
if (is_constant_node(operand) &&
824840
(operand2 == NULL || is_constant_node(operand2))) {
825841
/* The operands are constant. */

‎src/lower_eh.c‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5380,6 +5380,17 @@ __suppress_optim_on_vars_in_try. NULL until created.
53805380
STATIC_THREAD a_routine_ptr
53815381
setjmp_routine,
53825382
suppress_optim_on_vars_in_try_routine;
5383+
5384+
5385+
a_boolean is_eh_setjmp_routine(a_routine_ptr routine)
5386+
/*
5387+
Return TRUE if routine is the runtime routine setjmp called by the lowered
5388+
code for try blocks.
5389+
*/
5390+
{
5391+
return routine != NULL && routine == setjmp_routine;
5392+
} /* is_eh_setjmp_routine */
5393+
53835394
#endif /* DO_FULL_PORTABLE_EH_LOWERING */
53845395

53855396

‎src/lower_eh.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ extern void lower_try_block(
111111
extern an_expr_node_ptr make_internal_try_expr(an_expr_node_ptr try_expr,
112112
an_expr_node_ptr catch_expr);
113113

114+
#if DO_FULL_PORTABLE_EH_LOWERING
115+
extern a_boolean is_eh_setjmp_routine(a_routine_ptr routine);
116+
#endif /* DO_FULL_PORTABLE_EH_LOWERING */
117+
114118
#if !DO_FULL_PORTABLE_EH_LOWERING
115119
extern an_expr_node_ptr make_thrown_object_address_node(void);
116120
#endif /* !DO_FULL_PORTABLE_EH_LOWERING */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
eccp -DTEST_NUMBER=1 --gnu_version=150000 --set_flag=no_checking_pragmas --g++ Test_name.c
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./a.out
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
eccp -DTEST_NUMBER=2 --gnu_version=150000 --set_flag=no_checking_pragmas --clang Test_name.c
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./a.out
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//type:rp
2+
//options_all:--gnu_version=150000 --set_flag=no_checking_pragmas
3+
//options:--g++:--clang
4+
inline void *operator new(decltype(sizeof 0), void *p) noexcept { return p; }
5+
int deletes;
6+
inline void operator delete(void *, void *) noexcept { ++deletes; }
7+
8+
struct A { A(); };
9+
// The code generated for Alloc::construct calls setjmp, to call the placement
10+
// operator delete if the constructor throws.
11+
struct Alloc { void construct(A *p) { ::new ((void *)p) A(); } };
12+
struct Alloc2 { Alloc a; void construct(A *p) { a.construct(p); } };
13+
14+
[[gnu::always_inline]] inline void tconstruct(Alloc &a, A *p) {
15+
a.construct(p);
16+
}
17+
18+
[[gnu::always_inline]] static inline void sconstruct(Alloc &a, A *p) {
19+
a.construct(p); // Previously gave a gcc error.
20+
}
21+
22+
[[gnu::always_inline]] static inline void s2construct(Alloc2 &a, A *p) {
23+
a.construct(p); // Previously gave a gcc error.
24+
}
25+
26+
void f(Alloc &a, A *p) { tconstruct(a, p); }
27+
void g(Alloc &a, A *p) { sconstruct(a, p); }
28+
void h(Alloc2 &a, A *p) { s2construct(a, p); }
29+
30+
A::A() { throw 1; }
31+
32+
int main() {
33+
alignas(A) unsigned char buf[sizeof(A)];
34+
A *p = (A *)buf;
35+
Alloc a;
36+
Alloc2 a2;
37+
try { f(a, p); } catch (int) {}
38+
try { g(a, p); } catch (int) {}
39+
try { h(a2, p); } catch (int) {}
40+
return deletes == 3 ? 0 : 1;
41+
}

0 commit comments

Comments
 (0)