Bug 60580 - aarch64 generates wrong code for __attribute__ ((optimize("no-omit-frame-pointer")))
Summary: aarch64 generates wrong code for __attribute__ ((optimize("no-omit-frame-poin...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 4.8.0
: P4 minor
Target Milestone: 7.2
Assignee: Jiong Wang
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-03-19 08:37 UTC by Stefan Becker
Modified: 2017-11-03 15:40 UTC (History)
5 users (show)

See Also:
Host:
Target: aarch64*-*-*
Build:
Known to work:
Known to fail:
Last reconfirmed: 2014-03-20 00:00:00


Attachments
Example C code used i the command lines (251 bytes, text/plain)
2014-03-19 08:37 UTC, Stefan Becker
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Stefan Becker 2014-03-19 08:37:42 UTC
Created attachment 32388 [details]
Example C code used i the command lines

We have a piece of code that we compile with -fomit-frame-pointer. The exception is one low-level function that *MUST NOT* rely on the SP register, i.e. that one function must be compiled with -fno-omit-frame-pointer. This works fine with the GCC ARM backend, i.e. we have in the C code the following function attribute:

 /*
  * This function calls XXX(), which modifies SP. This is incompatible to
  * -fomit-frame-pointer generated code as SP is used to access the frame.
  */
 __attribute__ ((optimize("no-omit-frame-pointer")))
 void some_low_level_function(....)


We are now trying to compile that piece of code with the aarch64 backend and get incorrect code that uses SP to access the frame. I tried with the following pre-compiled aarch64 toolchains:


Linaro release 2014.02:
COLLECT_GCC=gcc-linaro-aarch64-linux-gnu-4.8-2014.02_linux/bin/aarch64-linux-gnu-gcc
COLLECT_LTO_WRAPPER=/workarea/stefanb/playground/aarch64/gcc-linaro-aarch64-linux-gnu-4.8-2014.02_linux/bin/../libexec/gcc/aarch64-linux-gnu/4.8.3/lto-wrapper
Target: aarch64-linux-gnu
Configured with: /cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/.build/src/gcc-linaro-4.8-2014.02/configure --build=i686-build_pc-linux-gnu --host=i686-build_pc-linux-gnu --target=aarch64-linux-gnu --prefix=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/install --with-sysroot=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/install/aarch64-linux-gnu/libc --enable-languages=c,c++,fortran --disable-multilib --enable-multiarch --with-arch=armv8-a --with-pkgversion='crosstool-NG linaro-1.13.1-4.8-2014.02 - Linaro GCC 2014.02' --with-bugurl=https://bugs.launchpad.net/gcc-linaro --enable-__cxa_atexit --disable-libmudflap --enable-libgomp --disable-libssp --with-gmp=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/.build/aarch64-linux-gnu/build/static --with-mpfr=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/.build/aarch64-linux-gnu/build/static --with-mpc=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/.build/aarch64-linux-gnu/build/static --with-isl=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/.build/aarch64-linux-gnu/build/static --with-cloog=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/.build/aarch64-linux-gnu/build/static --with-libelf=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/.build/aarch64-linux-gnu/build/static --enable-threads=posix --disable-libstdcxx-pch --enable-linker-build-id --enable-plugin --with-local-prefix=/cbuild/slaves/oorts/crosstool-ng/builds/aarch64-linux-gnu-linux/install/aarch64-linux-gnu/libc --enable-c99 --enable-long-long
Thread model: posix
gcc version 4.8.3 20140203 (prerelease) (crosstool-NG linaro-1.13.1-4.8-2014.02 - Linaro GCC 2014.02)


Android AOSP:

COLLECT_GCC=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8/bin/aarch64-linux-android-gcc 
COLLECT_LTO_WRAPPER=/workarea/stefanb/repos/tmake-only/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8/bin/../libexec/gcc/aarch64-linux-android/4.8/lto-wrapper                               
Target: aarch64-linux-android                                                                       
Configured with: /tmp/AOSP-toolchain/build/../gcc/gcc-4.8/configure --prefix=/tmp/toolchain-build-aarch64-linux-4.8/prefix --target=aarch64-linux-android --host=x86_64-linux-gnu --build=x86_64-linux-gnu --with-gnu-as --with-gnu-ld --enable-languages=c,c++ --with-gmp=/tmp/toolchain-build-aarch64-linux-4.8/temp-install --with-mpfr=/tmp/toolchain-build-aarch64-linux-4.8/temp-install --with-mpc=/tmp/toolchain-build-aarch64-linux-4.8/temp-install --with-cloog=/tmp/toolchain-build-aarch64-linux-4.8/temp-install --with-isl=/tmp/toolchain-build-aarch64-linux-4.8/temp-install --with-ppl=/tmp/toolchain-build-aarch64-linux-4.8/temp-install --disable-ppl-version-check --disable-cloog-version-check --disable-isl-version-check --enable-cloog-backend=isl --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --disable-libssp --enable-threads --disable-nls --disable-libmudflap --disable-libgomp --disable-libstdc__-v3 --disable-sjlj-exceptions --disable-shared --disable-tls --disable-libitm --enable-initfini-array --disable-nls --prefix=/tmp/toolchain-build-aarch64-linux-4.8/prefix --with-sysroot=/tmp/toolchain-build-aarch64-linux-4.8/prefix/sysroot --with-binutils-version=2.23 --with-mpfr-version=3.1.1 --with-mpc-version=1.0.1 --with-gmp-version=5.0.5 --with-gcc-version=4.8 --with-gdb-version=7.6 --with-gxx-include-dir=/tmp/toolchain-build-aarch64-linux-4.8/prefix/include/c++/4.8 --with-bugurl=http://source.android.com/source/report-bugs.html --disable-bootstrap --disable-libquadmath --enable-plugins --enable-libgomp --disable-libsanitizer --enable-gold --enable-graphite=yes --with-cloog-version=0.18.0 --with-isl-version=0.11.1 --enable-eh-frame-hdr-for-static --disable-gold --disable-libgomp --program-transform-name='s&^&aarch64-linux-android-&'
Thread model: posix
gcc version 4.8 (GCC)


With the attached test.c I get the following assembler code for func2():

Case 1:
aarch64-linux-android-gcc -O0 -march=armv8-a -fno-omit-frame-pointer -S test.c -o -

 func2:
        stp     x29, x30, [sp, -32]!
        add     x29, sp, 0
        str     wzr, [x29,16]
        bl      func_no_leaf
        ldp     x29, x30, [sp], 32
        ret

-> Correct code

Case 2:
aarch64-linux-android-gcc -O0 -march=armv8-a -fno-omit-frame-pointer -DINCLUDE_ATTRIBUTE -S test.c -o -

 func2:
        sub     sp, sp, #32
        str     x30, [sp]
        str     wzr, [sp,16]
        bl      func_no_leaf
        ldr     x30, [sp]
        add     sp, sp, 32
        ret

-> WRONG BEHAVIOUR: adding the function attribute to the source code forces the whole module code to be compiled with -fomit-frame-pointer!

Case 3:
aarch64-linux-android-gcc -O0 -march=armv8-a -fomit-frame-pointer -DINCLUDE_ATTRIBUTE -S test.c -o -

 func2:
        sub     sp, sp, #32
        str     x30, [sp]
        str     wzr, [sp,16]
        bl      func_no_leaf
        ldr     x30, [sp]
        add     sp, sp, 32
        ret

-> WRONG BEHAVIOUR: although there is a function attribute for func2() the code looks like case 2, instead of the correct code in case 1

Case 4:
aarch64-linux-android-gcc -O0 -march=armv8-a -fomit-frame-pointer -mno-omit-leaf-frame-pointer -DINCLUDE_ATTRIBUTE -S test.c -o -

 func2:
        stp     x29, x30, [sp, -32]!
        add     x29, sp, 0
        str     wzr, [x29,16]
        bl      func_no_leaf
        ldp     x29, x30, [sp], 32
        ret

-> CORRECT: func2() uses frame pointer, all other code omits the frame pointer. This is the workaround we currently have to use to get our code to work.
Comment 1 Jakub Jelinek 2014-03-19 12:41:27 UTC
This is a complete mess, what aarch64 backend does is completely incompatible with optimize attribute.  I've tried to fix this up:
--- gcc/config/aarch64/aarch64.opt.jj	2014-01-20 14:50:22.000000000 +0100
+++ gcc/config/aarch64/aarch64.opt	2014-03-19 11:52:29.963772607 +0100
@@ -59,6 +59,11 @@ const char *aarch64_cpu_string
 Variable
 const char *aarch64_tune_string
 
+; Did we set flag_omit_frame_pointer just so aarch64_frame_pointer_required
+; would be called?
+Variable
+bool faked_omit_frame_pointer
+
 mbig-endian
 Target Report RejectNegative Mask(BIG_END)
 Assume target CPU is configured as big endian
--- gcc/config/aarch64/aarch64.c.jj	2014-03-18 12:26:13.000000000 +0100
+++ gcc/config/aarch64/aarch64.c	2014-03-19 12:20:19.471504562 +0100
@@ -315,10 +315,6 @@ static GTY(()) int gty_dummy;
 #define AARCH64_NUM_BITMASKS  5334
 static unsigned HOST_WIDE_INT aarch64_bitmasks[AARCH64_NUM_BITMASKS];
 
-/* Did we set flag_omit_frame_pointer just so
-   aarch64_frame_pointer_required would be called? */
-static bool faked_omit_frame_pointer;
-
 typedef enum aarch64_cond_code
 {
   AARCH64_EQ = 0, AARCH64_NE, AARCH64_CS, AARCH64_CC, AARCH64_MI, AARCH64_PL,
@@ -5278,7 +5274,11 @@ aarch64_override_options (void)
 static void
 aarch64_override_options_after_change (void)
 {
-  faked_omit_frame_pointer = false;
+  if (global_options_set.x_flag_omit_frame_pointer)
+    {
+      faked_omit_frame_pointer = false;
+      global_options_set.x_faked_omit_frame_pointer = false;
+    }
 
   /* To omit leaf frame pointers, we need to turn flag_omit_frame_pointer on so
      that aarch64_frame_pointer_required will be called.  We need to remember
@@ -5288,6 +5288,9 @@ aarch64_override_options_after_change (v
     {
       flag_omit_frame_pointer = true;
       faked_omit_frame_pointer = true;
+      global_options_set.x_faked_omit_frame_pointer
+	= global_options_set.x_flag_omit_frame_pointer;
+      global_options_set.x_flag_omit_frame_pointer = false;
     }
 }
 
but while that fixes some of the cases, it doesn't fix everything, the problem is that the non-TargetVariable Variables aren't actually Saved.

Now, i386 backend seems to handle it differently, instead of adding a faked_omit_frame_pointer variable (which, as this PR suggests would need to be handled as an Optimization Saved variable, but supposedly the opts framework doesn't allow it yet), it clears omit_leaf_frame_pointer flag if flag_omit_frame_pointer is true:
  /* Keep nonleaf frame pointers.  */
  if (opts->x_flag_omit_frame_pointer)
    opts->x_target_flags &= ~MASK_OMIT_LEAF_FRAME_POINTER;
  else if (TARGET_OMIT_LEAF_FRAME_POINTER_P (opts->x_target_flags))
    opts->x_flag_omit_frame_pointer = 1;
but does that only in in override_options hook.  That means that
say for -fomit-frame-pointer -momit-leaf-frame-pointer on the command line
supposedly that turns off TARGET_OMIT_LEAF_FRAME_POINTER and when some function
has __attribute__((optimize ("no-omit-frame-pointer"))) and is leaf, frame pointer will not be omitted there.  But for -fno-omit-frame-pointer -momit-leaf-frame-pointer on the command line and __attribute__((optimize ("no-omit-frame-pointer"))) supposedly it will not have frame pointer if it is leaf.
The primary issue is that flag_omit_frame_pointer is Optimization Save flag,
while omit-leaf-frame-pointer is typcially Target Save, the former optimize attribute thing, the latter target attribute thing.

Not sure what is the best way out of this, but I guess doing in aarch64 what i386 does would be significant progress.
Comment 2 jsm-csl@polyomino.org.uk 2014-03-19 22:21:21 UTC
I think following what i386 does is reasonable for now.

I'm a bit dubious of the whole Save design - logically it would be cleaner 
if the gcc_options structures were saved just before finish_options, and 
then use of option-changing attributes were to process options with a copy 
of that structure (if this hadn't already been done for that particular 
attribute value), with finish_options and subsequent processing then being 
done for the new copy.  But that would be quite a lot of work, to complete 
a clean separation of option processing working on such structures from 
options processing that actually needs global effects, and then you'd need 
to work out how to represent things for LTO.
Comment 3 Jakub Jelinek 2014-03-20 13:40:51 UTC
I'm not going to hack on this any time soon, so if aarch64 maintainers could take it over, it would be appreciated.
Comment 4 Ramana Radhakrishnan 2014-03-20 23:55:51 UTC
Confirmed.
Comment 5 mshawcroft 2014-03-27 10:20:22 UTC
Patch applied to trunk r208862
Comment 6 ktkachov 2014-07-02 13:57:14 UTC
Marcus, can this be closed off?
Comment 7 Jiong Wang 2014-11-14 11:39:04 UTC
I'd take this for long term, hopefully could find a acceptable solution.

X86 is default with -fomit-frame-pointer which makes the logic a little bit simpler, and thus X86 could survive in more normal case, but still fail in some corner cases.

the fundamental problem is aarch64 and also x86 want to implement a finer control of frame pointer which let leaf function be possible without setting up frame record even under -fno-omit-frame-pointer. While gcc generic code is not aware of that.
Comment 8 Wilco 2014-11-20 20:32:12 UTC
(In reply to Jiong Wang from comment #7)
> I'd take this for long term, hopefully could find a acceptable solution.
> 
> X86 is default with -fomit-frame-pointer which makes the logic a little bit
> simpler, and thus X86 could survive in more normal case, but still fail in
> some corner cases.
> 
> the fundamental problem is aarch64 and also x86 want to implement a finer
> control of frame pointer which let leaf function be possible without setting
> up frame record even under -fno-omit-frame-pointer. While gcc generic code
> is not aware of that.

Besides the solutions we discussed on the list to fix the underlying issues in the mid-end, a workaround may be possible in the backend. If we can guarantee a callback is made to the backend at the end of compilation of each function, then we can restore the original value of the omit-frame-pointer variable. This means the options code will never see the fake value, and attributes will work as expected.

In principle any callback would work as long as the frame pointer variable is not used for that function again, and it happens before entering the options code again.

Obviously this is a nasty hack, but at least it works.
Comment 9 Wilco 2017-10-24 16:58:33 UTC
Author: wilco
Date: Tue Oct 24 16:58:02 2017
New Revision: 254052

URL: https://gcc.gnu.org/viewcvs?rev=254052&root=gcc&view=rev
Log:
PR60580: Fix frame pointer option magic

To fix PR60580 simplify the logic in aarch64_override_options_after_change_1 (). 
If the frame pointer is enabled, set it to a special value that behaves similar
to frame pointer omission.  If we don't do this all leaf functions will get a
frame pointer even if flag_omit_leaf_frame_pointer is set.

If flag_omit_frame_pointer has this special value, we must force the frame
pointer if not in a leaf function.  We also need to force it in a leaf function
if flag_omit_frame_pointer is not set or if LR is used.

Doing this allows both -fomit-frame-pointer and -fomit-leaf-frame-pointer to be
independently set and changed in each function with the expected behaviour.

    gcc/
	PR middle-end/60580
	* config/aarch64/aarch64.c (aarch64_frame_pointer_required)
	Check special value of flag_omit_frame_pointer.
	(aarch64_can_eliminate): Likewise.
	(aarch64_override_options_after_change_1): Simplify handling of
	-fomit-frame-pointer and -fomit-leaf-frame-pointer.

Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/config/aarch64/aarch64.c
Comment 10 Wilco 2017-11-03 15:01:42 UTC
Author: wilco
Date: Fri Nov  3 15:01:10 2017
New Revision: 254377

URL: https://gcc.gnu.org/viewcvs?rev=254377&root=gcc&view=rev
Log:
PR60580: Fix frame pointer option magic

To fix PR60580 simplify the logic in aarch64_override_options_after_change_1 (). 
If the frame pointer is enabled, set it to a special value that behaves similar
to frame pointer omission.  If we don't do this all leaf functions will get a
frame pointer even if flag_omit_leaf_frame_pointer is set.

If flag_omit_frame_pointer has this special value, we must force the frame
pointer if not in a leaf function.  We also need to force it in a leaf function
if flag_omit_frame_pointer is not set or if LR is used.

Doing this allows both -fomit-frame-pointer and -fomit-leaf-frame-pointer to be
independently set and changed in each function with the expected behaviour.

    gcc/
	PR middle-end/60580
	* config/aarch64/aarch64.c (aarch64_frame_pointer_required)
	Check special value of flag_omit_frame_pointer.
	(aarch64_can_eliminate): Likewise.
	(aarch64_override_options_after_change_1): Simplify handling of
	-fomit-frame-pointer and -fomit-leaf-frame-pointer.

Modified:
    branches/gcc-7-branch/gcc/ChangeLog
    branches/gcc-7-branch/gcc/config/aarch64/aarch64.c
Comment 11 Wilco 2017-11-03 15:40:48 UTC
Fixed from 7.2 onwards - I've verified the failing cases now work. Backporting to earlier versions seems too risky so close as fixed (generated code is correct in all cases).