Bug 2223951

Summary: Possible regression since f37/GCC 12.3.1 in handling of __attribute__((always_inline, optimize("-O3")))
Product: [Fedora] Fedora Reporter: Martin Osvald 🛹 <mosvald>
Component: gccAssignee: Jakub Jelinek <jakub>
Status: CLOSED NOTABUG QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: medium Docs Contact:
Priority: unspecified    
Version: rawhideCC: dmalcolm, fweimer, jakub, jlaw, jwakely, mcermak, mpolacek, msebor, nickc, sipoyare
Target Milestone: ---   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2023-07-19 12:37:36 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
srpm
none
f36-build.log
none
f37-build.log
none
f39-build.log none

Description Martin Osvald 🛹 2023-07-19 11:52:35 UTC
I wasn't able to debug this myself further as not understanding it enough so it's vague, but I believe according to tests that some change between gcc 12.2.1 (f36) and 12.3.1 (f37) must have happened which prevents building of a new upstream cyrus-imapd although it is related to recent changes made by this upstream cyrus-imapd commit (previous 3.6.0 didn't FTBFS):

https://github.com/cyrusimap/cyrus-imapd/commit/3e1ea54e4975acdcc5daa6fee60518a571a7f642

Reproducible: Always

Steps to Reproduce:
1. Download the attached cyrus-imapd-3.8.0-1.fc39.src.rpm

2. Run the commands to get rpmbuild running on f37..f39/rawhide machine:

~~~
# cat ~/.rpmmacros 
%_topdir %(echo $HOME)/rpmbuild
#
# rpm -i cyrus-imapd-3.8.0-1.fc39.src.rpm
# dnf install rpm-build -y
# # Note on rawhide/f39 with dnf5 you have to install also dnf-utils to get builddep working
# # dnf install dnf-utils -y
# dnf builddep rpmbuild/SPECS/cyrus-imapd.spec -y
# rpmbuild -ba ~/rpmbuild/SPECS/cyrus-imapd.spec 2>&1 | tee f39-build.log
~~~

or use mock:

~~~
$ mock -r fedora-rawhide-x86_64 --resultdir=./cyrus-imapd-3.8.0-results cyrus-imapd-3.8.0-1.fc39.src.rpm
~~~
Actual Results:  
~~~
# rpmbuild -ba ~/rpmbuild/SPECS/cyrus-imapd.spec 2>&1 | tee f39-build.log
... snip ...

gcc   -lpthread -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,--build-id=sha1  -L/usr/local/lib -fstack-protector-strong  IMAP.o  -o blib/arch/auto/Cyrus/IMAP/IMAP.so ../../perl/.libs/libcyrus.a ../../perl/.libs/libcyrus_min.a \
   -lsasl2 -lssl -lcrypto -lssl -lcrypto -luuid -lz -lpcreposix -lperl   \
  
../../lib/util.c: In function 'XS_Cyrus__IMAP_toURL':
../../lib/util.c:1193:24: error: inlining failed in call to 'always_inline' 'buf_len.localalias': optimization level attribute mismatch
 1193 | EXPORTED inline size_t buf_len(const struct buf *buf)
      |                        ^
IMAP.xs:729:13: note: called from here
  729 |         if(!buf_len(&buf)) {
      |             ^
make[3]: *** [/tmp/ccO8zh2g.mk:2: /tmp/ccVNbDeQ.ltrans0.ltrans.o] Error 1
lto-wrapper: fatal error: make returned 2 exit status
compilation terminated.
/usr/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status
make[2]: *** [Makefile:493: blib/arch/auto/Cyrus/IMAP/IMAP.so] Error 1
make[2]: Leaving directory '/root/rpmbuild/BUILD/cyrus-imapd-3.8.0/perl/imap'
make[1]: *** [Makefile:7537: all-recursive] Error 1
make: *** [Makefile:3346: all] Error 2
error: Bad exit status from /var/tmp/rpm-tmp.sg5jw2 (%build)

RPM build errors:
    Bad exit status from /var/tmp/rpm-tmp.sg5jw2 (%build)
~~~

Expected Results:  
No such error

If you want to get quickly to (hopefully all the) related commands and rerun them, the below should be the ones:

~~~
# cd ~/rpmbuild/BUILD/cyrus-imapd-3.8.0

# gcc -DHAVE_CONFIG_H -I. -I. -I./lib -I. -I./lib -DLIBEXEC_DIR=\"/usr/libexec/cyrus-imapd\" -DSBIN_DIR=\"/usr/sbin\" -DSYSCONF_DIR=\"/etc\" -DHAVE_CONFIG_H -I/usr/include -I/usr/include/libxml2 -I/usr/include/mysql -I/usr/include/mysql/mysql -fPIC -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -c lib/util.c  -fPIC -DPIC -o lib/.libs/util.o

# ar cr perl/.libs/libcyrus_min.a lib/.libs/arrayu64.o lib/.libs/assert.o lib/.libs/bufarray.o lib/.libs/byteorder.o lib/.libs/dynarray.o lib/.libs/hash.o lib/.libs/hashset.o lib/.libs/hashu64.o lib/.libs/libconfig.o lib/.libs/mpool.o lib/.libs/retry.o lib/.libs/smallarrayu64.o lib/.libs/strarray.o lib/.libs/strhash.o lib/.libs/tok.o lib/.libs/util.o lib/.libs/vparse.o lib/.libs/xmalloc.o lib/.libs/xstrlcat.o lib/.libs/xstrlcpy.o lib/.libs/xstrnchr.o lib/.libs/lock_fcntl.o lib/.libs/map_shared.o lib/.libs/imapopts.o

# ranlib perl/.libs/libcyrus_min.a

# cd ~/rpmbuild/BUILD/cyrus-imapd-3.8.0/perl/imap/

# /usr/bin/perl /usr/share/perl5/vendor_perl/ExtUtils/xsubpp  -typemap '/usr/share/perl5/ExtUtils/typemap' -typemap '/root/rpmbuild/BUILD/cyrus-imapd-3.8.0/perl/imap/typemap'  IMAP.xs > IMAP.xsc && mv IMAP.xsc IMAP.c

# gcc -c  -I../.. -I../../com_err/et    -I../../perl/imap -D_REENTRANT -D_GNU_SOURCE -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fwrapv -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g   -DVERSION=\"1.00\" -DXS_VERSION=\"1.00\" -fPIC "-I/usr/lib64/perl5/CORE"  -DPERL_POLLUTE IMAP.c

# gcc   -lpthread -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,--build-id=sha1  -L/usr/local/lib -fstack-protector-strong  IMAP.o  -o blib/arch/auto/Cyrus/IMAP/IMAP.so ../../perl/.libs/libcyrus.a ../../perl/.libs/libcyrus_min.a    -lsasl2 -lssl -lcrypto -lssl -lcrypto -luuid -lz -lpcreposix -lperl
../../lib/util.c: In function ‘XS_Cyrus__IMAP_toURL’:
../../lib/util.c:1193:24: error: inlining failed in call to ‘always_inline’ ‘buf_len.localalias’: optimization level attribute mismatch
 1193 | EXPORTED inline size_t buf_len(const struct buf *buf)
      |                        ^
IMAP.xs:729:13: note: called from here
  729 |         if(!buf_len(&buf)) {
      |             ^
make: *** [/tmp/cc11iLW9.mk:2: /tmp/ccLhbEaR.ltrans0.ltrans.o] Error 1
lto-wrapper: fatal error: make returned 2 exit status
compilation terminated.
/usr/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status
#
~~~

Will be attaching build logs from f36, f37 and f39/rawhide soon

Comment 1 Martin Osvald 🛹 2023-07-19 11:54:32 UTC
Created attachment 1976494 [details]
srpm

Comment 2 Martin Osvald 🛹 2023-07-19 11:56:43 UTC
Created attachment 1976495 [details]
f36-build.log

this passes without errors

Comment 3 Martin Osvald 🛹 2023-07-19 11:58:05 UTC
Created attachment 1976496 [details]
f37-build.log

shows the error

Comment 4 Martin Osvald 🛹 2023-07-19 11:59:28 UTC
Created attachment 1976497 [details]
f39-build.log

shows the error

Comment 5 Martin Osvald 🛹 2023-07-19 12:04:07 UTC
buf_len code:

./cyrus-imapd-3.8.0/lib/util.c:
~~~
1189 #ifdef HAVE_DECLARE_OPTIMIZE
1190 EXPORTED inline size_t buf_len(const struct buf *buf)
1191     __attribute__((always_inline, optimize("-O3")));
1192 #endif
1193 EXPORTED inline size_t buf_len(const struct buf *buf)
1194 {
1195     return buf->len;
1196 }
~~~

Comment 6 Martin Osvald 🛹 2023-07-19 12:05:47 UTC
note that it fails even if I remove the optimize("-O3")

Comment 8 Jakub Jelinek 2023-07-19 12:20:30 UTC
With always_inline attribute you ask the compiler to error if it couldn't inline the function, which is exactly what you got.
__attribute__((always_inline, optimize("-O3"))) is in itself non-sense, either you want to always inline the function,
then what optimization level it has doesn't matter much, you get what optimization level has the function into which it is inlined.
Also, making an exported function always_inline inside of a *.c file (rather than putting the inline function definition and
the attribute into a header file) doesn't make much sense, without LTO it then isn't really inlined into anything but callers within
the same function.
GCC treats various option differences as preventing inlining, some can differ in one direction only and not in the other, some the other way around, etc.
E.g.

      /* There are some options that change IL semantics which means
         we cannot inline in these cases for correctness reason.
         Not even for always_inline declared functions.  */
     else if (check_match (flag_wrapv)
              || check_match (flag_trapv)
              || check_match (flag_pcc_struct_return)
              || check_maybe_down (optimize_debug)
              /* When caller or callee does FP math, be sure FP codegen flags
                 compatible.  */
              || ((caller_info->fp_expressions && callee_info->fp_expressions)
                  && (check_maybe_up (flag_rounding_math)
                      || check_maybe_up (flag_trapping_math)
                      || check_maybe_down (flag_unsafe_math_optimizations)
                      || check_maybe_down (flag_finite_math_only)
                      || check_maybe_up (flag_signaling_nans)
                      || check_maybe_down (flag_cx_limited_range)
                      || check_maybe_up (flag_signed_zeros)
                      || check_maybe_down (flag_associative_math)
                      || check_maybe_down (flag_reciprocal_math)
                      || check_maybe_down (flag_fp_int_builtin_inexact)
                      /* Strictly speaking only when the callee contains function
                         calls that may end up setting errno.  */
                      || check_maybe_up (flag_errno_math)))
              /* We do not want to make code compiled with exceptions to be
                 brought into a non-EH function unless we know that the callee
                 does not throw.
                 This is tracked by DECL_FUNCTION_PERSONALITY.  */
              || (check_maybe_up (flag_non_call_exceptions)
                  && DECL_FUNCTION_PERSONALITY (callee->decl))
              || (check_maybe_up (flag_exceptions)
                  && DECL_FUNCTION_PERSONALITY (callee->decl))
              /* When devirtualization is disabled for callee, it is not safe
                 to inline it as we possibly mangled the type info.
                 Allow early inlining of always inlines.  */
              || (!early && check_maybe_down (flag_devirtualize)))
Without trying it it isn't clear which of the cases this actually is.

Comment 10 Jakub Jelinek 2023-07-19 12:37:36 UTC
And the log quickly shows the reason.  IMAP.c (created from IMAP.xs) which calls buf_len is compiled with -fwrapv option, while lib/util.c is not.
As whether operations on signed integers are wrapping or have undefined behavior or trapping aren't in GCC encoded in the IL, but come from per-function flags,
it is really not possible to do such inlining.
You can either drop the bogus always_inline, it doesn't help much, such trivial function is inlined whenever possible anyway, or compile
IMAP.c without -flto=auto (or add -fno-lto), such that that TU which uses -fwrapv isn't LTO optimized together with the rest, etc.