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
29 changes: 29 additions & 0 deletions src/Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
Version 7.1

10/7/26 [GH #175]
C-generating back end: always_inline routines defined in several objects

When a routine placed in a COMDAT group also had the GNU "always_inline"
attribute, the C-generating back end suppressed the __weak__ attribute that
it otherwise uses to approximate the COMDAT group (see the entry for
EDGcpfe/21186 of 5/2/19). As a result, every object file that defined the
routine contained a conflicting external definition of it, and linking two
such object files failed. This affected, for example, std::move and
std::forward from libstdc++ 15. Such routines are now marked __weak__. When
GCC is the back end compiler, which gives an error on calls to a weak routine
marked __always_inline__, the __always_inline__ attribute is suppressed
instead. For example, with --g++:

// twice.h
[[gnu::always_inline]] inline int twice(int x) { return x + x; }

// test.cpp
#include "twice.h"
using fn = int (*)(int);
fn other();
int main() { return (&twice == other() && twice(1) == 2) ? 0 : 1; }

// other.cpp
#include "twice.h"
using fn = int (*)(int);
fn other() { return &twice; } // Linking with test.cpp previously failed.


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

Expand Down
20 changes: 13 additions & 7 deletions src/c_gen_be.c
Original file line number Diff line number Diff line change
Expand Up @@ -11086,20 +11086,26 @@ if this routine has a body (dump nothing if it has no body).
(a_storage_class)sc_unspecified,
"dump_routine_decl: rout without defn in comdat");
if (gcc_or_clang_is_generated_code_target
#if GNU_EXTENSIONS_ALLOWED
&& !rout->always_inline /* gcc gives an error on weak functions with
the always_inline attribute (because they
can change at link time). */
#if !LOWER_IFUNC
#if GNU_EXTENSIONS_ALLOWED && !LOWER_IFUNC
&& !rout->is_ifunc /* gcc doesn't allow ifunc to be weak, so
suppress the weak attribute (though this
may result in multiple-definition errors
in some cases). */
#endif /* !LOWER_IFUNC */
#endif /* GNU_EXTENSIONS_ALLOWED */
#endif /* GNU_EXTENSIONS_ALLOWED && !LOWER_IFUNC */
) {
/* GCC does not support COMDAT, but it does support weak, which
provides a sufficient approximation. */
#if GNU_EXTENSIONS_ALLOWED
if (rout->always_inline && gcc_is_generated_code_target) {
/* gcc gives an error on calls to a weak function with the
always_inline attribute (because the definition could be
replaced at link time). Without the weak attribute each
translation unit that defines the routine contains a conflicting
external definition of it, so suppress the always_inline
attribute instead (Clang accepts both). */
rout->always_inline = FALSE;
} /* if */
#endif /* GNU_EXTENSIONS_ALLOWED */
write_tok_str(" __attribute__((__weak__))");
is_marked_weak = TRUE;
} /* if */
Expand Down
3 changes: 3 additions & 0 deletions tests/tests/regressions/gh-175/.test.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
eccp -DTEST_NUMBER=1 --c++17 --g++ Test_name.c other.C
Test_name.c:
other.C:
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-175/.test.rto/default.1.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
3 changes: 3 additions & 0 deletions tests/tests/regressions/gh-175/.test.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
eccp -DTEST_NUMBER=2 --c++17 --g++ -tused Test_name.c other.C
Test_name.c:
other.C:
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-175/.test.rto/default.2.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
3 changes: 3 additions & 0 deletions tests/tests/regressions/gh-175/.test.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
eccp -DTEST_NUMBER=3 --c++17 --clang Test_name.c other.C
Test_name.c:
other.C:
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-175/.test.rto/default.3.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
5 changes: 5 additions & 0 deletions tests/tests/regressions/gh-175/other.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "twice.h"
using fn = int (*)(int);
using move_fn = int &&(*)(int &);
fn other() { return &twice; }
move_fn other_move() { return &move_it<int>; }
13 changes: 13 additions & 0 deletions tests/tests/regressions/gh-175/test.mft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//type:rp
//options:--c++17 --g++:--c++17 --g++ -tused:--c++17 --clang
//source_files:twice.h other.C
#include "twice.h"
using fn = int (*)(int);
using move_fn = int &&(*)(int &);
fn other();
move_fn other_move();
int main() {
int i = 1;
return (&twice == other() && twice(1) == 2 &&
&move_it<int> == other_move() && move_it(i) == 1) ? 0 : 1;
}
6 changes: 6 additions & 0 deletions tests/tests/regressions/gh-175/twice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[[gnu::always_inline]] inline int twice(int x) { return x + x; }

// Like std::move and std::forward in libstdc++ 15.
template<typename T> [[gnu::always_inline]] constexpr T &&move_it(T &t) {
return static_cast<T &&>(t);
}
Loading