i386: Don't use frame pointer without stack access

Message ID CAMe9rOqqLu+SP-36X=G9N3CoEysjE0G4s=u68FKzpJgARy_V0A@mail.gmail.com
State New
Headers show

Commit Message

H.J. Lu Aug. 9, 2017, 6:18 p.m.
On Wed, Aug 9, 2017 at 10:28 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Aug 9, 2017 at 8:26 AM, Andi Kleen <andi@firstfloor.org> wrote:
>>> This must be much more specific.  How does it impact:
>>>
>>> 1. LTO
>>> 2. Function inlining.
>>> 3. Partial function inlining.
>>> 4. Shrink-wrapping.
>>>
>>> Any of them can impact function stack frame.
>>
>> It doesn't. It's just to get back to the previous state.
>>
>> Also these others already have explicit options to disable them.
>>
>
> How about
>
> item -fkeep-frame-pointer
> @opindex fkeep-frame-pointer
> Keep the frame pointer in a register for functions.  This option always
> forces a new stack frame for all functions regardless of whether
> @option{-fomit-frame-pointer} is enabled or not.  Disabled by default.
>

Here is the updated patch with -fkeep-frame-pointer.

OK for trunk?

Comments

Richard Sandiford Aug. 10, 2017, 7:09 a.m. | #1
"H.J. Lu" <hjl.tools@gmail.com> writes:
> On Wed, Aug 9, 2017 at 10:28 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Wed, Aug 9, 2017 at 8:26 AM, Andi Kleen <andi@firstfloor.org> wrote:
>>>> This must be much more specific.  How does it impact:
>>>>
>>>> 1. LTO
>>>> 2. Function inlining.
>>>> 3. Partial function inlining.
>>>> 4. Shrink-wrapping.
>>>>
>>>> Any of them can impact function stack frame.
>>>
>>> It doesn't. It's just to get back to the previous state.
>>>
>>> Also these others already have explicit options to disable them.
>>>
>>
>> How about
>>
>> item -fkeep-frame-pointer
>> @opindex fkeep-frame-pointer
>> Keep the frame pointer in a register for functions.  This option always
>> forces a new stack frame for all functions regardless of whether
>> @option{-fomit-frame-pointer} is enabled or not.  Disabled by default.
>>
>
> Here is the updated patch with -fkeep-frame-pointer.

It sounds like there's still some disagreement about what this option
should mean; Andi's and Arjan's replies didn't seem to be asking for
the same thing.

I think as implemented the patch just retains the GCC 7 x86 behaviour of
-fno-omit-frame-pointer, i.e. forces a frame to be created *somewhere*
between the start and end addresses of the function, but makes no
guarantee where.  It could be a bundle of three instructions somewhere
in the middle of a basic block, and the code might not be executed for
all paths through the function (e.g. it might only be executed on
error paths).

I think even if we think that's useful, it should be documented clearly.
Otherwise people might assume that a function f is guaranteed to set up
a frame every time it's called.

Thanks,
Richard
Uros Bizjak Aug. 10, 2017, 7:39 a.m. | #2
On Thu, Aug 10, 2017 at 9:09 AM, Richard Sandiford
<richard.sandiford@linaro.org> wrote:
> "H.J. Lu" <hjl.tools@gmail.com> writes:
>> On Wed, Aug 9, 2017 at 10:28 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> On Wed, Aug 9, 2017 at 8:26 AM, Andi Kleen <andi@firstfloor.org> wrote:
>>>>> This must be much more specific.  How does it impact:
>>>>>
>>>>> 1. LTO
>>>>> 2. Function inlining.
>>>>> 3. Partial function inlining.
>>>>> 4. Shrink-wrapping.
>>>>>
>>>>> Any of them can impact function stack frame.
>>>>
>>>> It doesn't. It's just to get back to the previous state.
>>>>
>>>> Also these others already have explicit options to disable them.
>>>>
>>>
>>> How about
>>>
>>> item -fkeep-frame-pointer
>>> @opindex fkeep-frame-pointer
>>> Keep the frame pointer in a register for functions.  This option always
>>> forces a new stack frame for all functions regardless of whether
>>> @option{-fomit-frame-pointer} is enabled or not.  Disabled by default.
>>>
>>
>> Here is the updated patch with -fkeep-frame-pointer.
>
> It sounds like there's still some disagreement about what this option
> should mean; Andi's and Arjan's replies didn't seem to be asking for
> the same thing.
>
> I think as implemented the patch just retains the GCC 7 x86 behaviour of
> -fno-omit-frame-pointer, i.e. forces a frame to be created *somewhere*
> between the start and end addresses of the function, but makes no
> guarantee where.  It could be a bundle of three instructions somewhere
> in the middle of a basic block, and the code might not be executed for
> all paths through the function (e.g. it might only be executed on
> error paths).
>
> I think even if we think that's useful, it should be documented clearly.
> Otherwise people might assume that a function f is guaranteed to set up
> a frame every time it's called.

As said earlier, I think we should proceed with the previous patch
(that documents -fno-omit-frame-pointer limitations), It was
demonstrated that the patch does not make current situation worse.

-fkeep-frame-pointer is an orthogonal issue, and this option should
guarantee frame formation in *all* situations.This means that the
option should disable (partial) inlining, shrink-wrapping, etc.
Actually, it would disable so much optimizations, that its usefullnes
is questioned. OTOH, nobody ever complained about limited FP debugging
"experience" when mentioned optimizations were activated.

BTW: The option should be called -fforce-frame-pointer, but I really
doubt about its usefullnes.

Uros.
H.J. Lu Aug. 10, 2017, 1:42 p.m. | #3
On Thu, Aug 10, 2017 at 12:39 AM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Thu, Aug 10, 2017 at 9:09 AM, Richard Sandiford
> <richard.sandiford@linaro.org> wrote:
>> "H.J. Lu" <hjl.tools@gmail.com> writes:
>>> On Wed, Aug 9, 2017 at 10:28 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> On Wed, Aug 9, 2017 at 8:26 AM, Andi Kleen <andi@firstfloor.org> wrote:
>>>>>> This must be much more specific.  How does it impact:
>>>>>>
>>>>>> 1. LTO
>>>>>> 2. Function inlining.
>>>>>> 3. Partial function inlining.
>>>>>> 4. Shrink-wrapping.
>>>>>>
>>>>>> Any of them can impact function stack frame.
>>>>>
>>>>> It doesn't. It's just to get back to the previous state.
>>>>>
>>>>> Also these others already have explicit options to disable them.
>>>>>
>>>>
>>>> How about
>>>>
>>>> item -fkeep-frame-pointer
>>>> @opindex fkeep-frame-pointer
>>>> Keep the frame pointer in a register for functions.  This option always
>>>> forces a new stack frame for all functions regardless of whether
>>>> @option{-fomit-frame-pointer} is enabled or not.  Disabled by default.
>>>>
>>>
>>> Here is the updated patch with -fkeep-frame-pointer.
>>
>> It sounds like there's still some disagreement about what this option
>> should mean; Andi's and Arjan's replies didn't seem to be asking for
>> the same thing.
>>
>> I think as implemented the patch just retains the GCC 7 x86 behaviour of
>> -fno-omit-frame-pointer, i.e. forces a frame to be created *somewhere*
>> between the start and end addresses of the function, but makes no
>> guarantee where.  It could be a bundle of three instructions somewhere
>> in the middle of a basic block, and the code might not be executed for
>> all paths through the function (e.g. it might only be executed on
>> error paths).
>>
>> I think even if we think that's useful, it should be documented clearly.
>> Otherwise people might assume that a function f is guaranteed to set up
>> a frame every time it's called.
>
> As said earlier, I think we should proceed with the previous patch
> (that documents -fno-omit-frame-pointer limitations), It was
> demonstrated that the patch does not make current situation worse.
>

I will check in my previous patch today.

Thanks.

Patch

From 6f5b42bbe2baa7977dcbf388219dabcb4a6b546d Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sun, 6 Aug 2017 06:24:36 -0700
Subject: [PATCH] i386: Don't use frame pointer without stack access

When there is no stack access, there is no need to use frame pointer
even if -fno-omit-frame-pointer is used and caller's frame pointer is
unchanged.  A command-line option, -fkeep-frame-pointer, is added to
always force a new stack frame.

gcc/

	PR target/81736
	* common.opt (-fomit-frame-pointer): Override
	-fkeep-frame-pointer.
	(-fkeep-frame-pointer): New command-line option.
	* config/i386/i386.c (ix86_finalize_stack_realign_flags): Renamed
	to ...
	(ix86_finalize_stack_frame_flags): This.  Also clear
	frame_pointer_needed if -fkeep-frame-pointer isn't used and
	,-fno-omit-frame-pointer is used without stack access.
	(ix86_expand_prologue): Replace ix86_finalize_stack_realign_flags
	with ix86_finalize_stack_frame_flags.
	(ix86_expand_epilogue): Likewise.
	(ix86_expand_split_stack_prologue): Likewise.
	* doc/invoke.texi: Document -fkeep-frame-pointer.  Update
	-fomit-frame-pointer.  Add a note for -fno-omit-frame-pointer.
	* toplev.c (process_options): Disable -fomit-frame-pointer if
	-fkeep-frame-pointer is used.

gcc/testsuite/

	PR target/81736
	* gcc.target/i386/pr81736-1a.c: New test.
	* gcc.target/i386/pr81736-1b.c: Likewise.
	* gcc.target/i386/pr81736-1c.c: Likewise.
	* gcc.target/i386/pr81736-1d.c: Likewise.
	* gcc.target/i386/pr81736-1e.c: Likewise.
	* gcc.target/i386/pr81736-1f.c: Likewise.
	* gcc.target/i386/pr81736-1g.c: Likewise.
	* gcc.target/i386/pr81736-2.c: Likewise.
	* gcc.target/i386/pr81736-3.c: Likewise.
	* gcc.target/i386/pr81736-4.c: Likewise.
	* gcc.target/i386/pr81736-5.c: Likewise.
	* gcc.target/i386/pr81736-6.c: Likewise.
	* gcc.target/i386/pr81736-7.c: Likewise.
---
 gcc/common.opt                             |  6 +++++-
 gcc/config/i386/i386.c                     | 24 +++++++++++++-----------
 gcc/doc/invoke.texi                        | 13 ++++++++++++-
 gcc/testsuite/gcc.target/i386/pr81736-1a.c | 13 +++++++++++++
 gcc/testsuite/gcc.target/i386/pr81736-1b.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr81736-1c.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr81736-1d.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr81736-1e.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr81736-1f.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr81736-1g.c |  7 +++++++
 gcc/testsuite/gcc.target/i386/pr81736-2.c  | 14 ++++++++++++++
 gcc/testsuite/gcc.target/i386/pr81736-3.c  | 11 +++++++++++
 gcc/testsuite/gcc.target/i386/pr81736-4.c  | 11 +++++++++++
 gcc/testsuite/gcc.target/i386/pr81736-5.c  | 20 ++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr81736-6.c  | 16 ++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr81736-7.c  | 13 +++++++++++++
 gcc/toplev.c                               |  4 ++++
 17 files changed, 174 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-1a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-1b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-1c.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-1d.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-1e.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-1f.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-1g.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr81736-7.c

diff --git a/gcc/common.opt b/gcc/common.opt
index 1cb1c83d306..b73564d7145 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1881,9 +1881,13 @@  EnumValue
 Enum(offload_abi) String(lp64) Value(OFFLOAD_ABI_LP64)
 
 fomit-frame-pointer
-Common Report Var(flag_omit_frame_pointer) Optimization
+Common Report Var(flag_omit_frame_pointer) Negative(fkeep-frame-pointer) Optimization
 When possible do not generate stack frames.
 
+fkeep-frame-pointer
+Common Report Var(flag_keep_frame_pointer) Negative(fomit-frame-pointer)
+Always force a new stack frame
+
 fopt-info
 Common Report Var(flag_opt_info) Optimization
 Enable all optimization info dumps on stderr.
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 3f8519777f7..9c37e960dc8 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -14179,10 +14179,11 @@  output_probe_stack_range (rtx reg, rtx end)
   return "";
 }
 
-/* Finalize stack_realign_needed flag, which will guide prologue/epilogue
-   to be generated in correct form.  */
+/* Finalize stack_realign_needed and frame_pointer_needed flags, which
+   will guide prologue/epilogue to be generated in correct form.  */
+
 static void
-ix86_finalize_stack_realign_flags (void)
+ix86_finalize_stack_frame_flags (void)
 {
   /* Check if stack realign is really needed after reload, and
      stores result in cfun */
@@ -14205,13 +14206,14 @@  ix86_finalize_stack_realign_flags (void)
     }
 
   /* If the only reason for frame_pointer_needed is that we conservatively
-     assumed stack realignment might be needed, but in the end nothing that
-     needed the stack alignment had been spilled, clear frame_pointer_needed
-     and say we don't need stack realignment.  */
-  if (stack_realign
+     assumed stack realignment might be needed or -fno-omit-frame-pointer
+     is used, but in the end nothing that needed the stack alignment had
+     been spilled nor stack access, clear frame_pointer_needed and say we
+     don't need stack realignment.  */
+  if ((stack_realign || !flag_omit_frame_pointer)
+      && !flag_keep_frame_pointer
       && frame_pointer_needed
       && crtl->is_leaf
-      && flag_omit_frame_pointer
       && crtl->sp_is_unchanging
       && !ix86_current_function_calls_tls_descriptor
       && !crtl->accesses_prior_frames
@@ -14402,7 +14404,7 @@  ix86_expand_prologue (void)
   if (ix86_function_naked (current_function_decl))
     return;
 
-  ix86_finalize_stack_realign_flags ();
+  ix86_finalize_stack_frame_flags ();
 
   /* DRAP should not coexist with stack_realign_fp */
   gcc_assert (!(crtl->drap_reg && stack_realign_fp));
@@ -15266,7 +15268,7 @@  ix86_expand_epilogue (int style)
       return;
     }
 
-  ix86_finalize_stack_realign_flags ();
+  ix86_finalize_stack_frame_flags ();
   frame = m->frame;
 
   m->fs.sp_realigned = stack_realign_fp;
@@ -15801,7 +15803,7 @@  ix86_expand_split_stack_prologue (void)
 
   gcc_assert (flag_split_stack && reload_completed);
 
-  ix86_finalize_stack_realign_flags ();
+  ix86_finalize_stack_frame_flags ();
   frame = cfun->machine->frame;
   allocate = frame.stack_pointer_offset - INCOMING_FRAME_SP_OFFSET;
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index cc0f5a00a4f..7a3b3d53f17 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -397,7 +397,7 @@  Objective-C and Objective-C++ Dialects}.
 -fno-peephole2  -fno-printf-return-value  -fno-sched-interblock @gol
 -fno-sched-spec  -fno-signed-zeros @gol
 -fno-toplevel-reorder  -fno-trapping-math  -fno-zero-initialized-in-bss @gol
--fomit-frame-pointer  -foptimize-sibling-calls @gol
+-fomit-frame-pointer -fkeep-frame-pointer -foptimize-sibling-calls @gol
 -fpartial-inlining  -fpeel-loops  -fpredictive-commoning @gol
 -fprefetch-loop-arrays @gol
 -fprofile-correction @gol
@@ -7365,9 +7365,20 @@  The default setting (when not optimizing for
 size) for 32-bit GNU/Linux x86 and 32-bit Darwin x86 targets is
 @option{-fomit-frame-pointer}.  You can configure GCC with the
 @option{--enable-frame-pointer} configure option to change the default.
+This option overrides @option{-fkeep-frame-pointer}.
+
+Note that @option{-fno-omit-frame-pointer} doesn't force a new stack
+frame for all functions if it isn't otherwise needed, and hence doesn't
+guarantee a new frame pointer for all functions.
 
 Enabled at levels @option{-O}, @option{-O2}, @option{-O3}, @option{-Os}.
 
+@item -fkeep-frame-pointer
+@opindex fkeep-frame-pointer
+Keep the frame pointer in a register for functions.  This option always
+forces a new stack frame for all functions.  This option overrides
+@option{-fomit-frame-pointer}.  Disabled by default.
+
 @item -foptimize-sibling-calls
 @opindex foptimize-sibling-calls
 Optimize sibling and tail recursive calls.
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-1a.c b/gcc/testsuite/gcc.target/i386/pr81736-1a.c
new file mode 100644
index 00000000000..92c7bc97a0d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-1a.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer" } */
+
+extern int i;
+
+int
+foo (void)
+{
+  return i;
+}
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-1b.c b/gcc/testsuite/gcc.target/i386/pr81736-1b.c
new file mode 100644
index 00000000000..692ca9b7a30
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-1b.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fkeep-frame-pointer -fno-omit-frame-pointer" } */
+
+#include "pr81736-1a.c"
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-1c.c b/gcc/testsuite/gcc.target/i386/pr81736-1c.c
new file mode 100644
index 00000000000..36ee6bc63f0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-1c.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fomit-frame-pointer -fno-omit-frame-pointer" } */
+
+#include "pr81736-1a.c"
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-1d.c b/gcc/testsuite/gcc.target/i386/pr81736-1d.c
new file mode 100644
index 00000000000..143985852f7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-1d.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fkeep-frame-pointer -fomit-frame-pointer" } */
+
+#include "pr81736-1a.c"
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-1e.c b/gcc/testsuite/gcc.target/i386/pr81736-1e.c
new file mode 100644
index 00000000000..fe98e96074c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-1e.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fomit-frame-pointer -fkeep-frame-pointer" } */
+
+#include "pr81736-1a.c"
+
+/* Need to use a frame pointer.  */
+/* { dg-final { scan-assembler "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-1f.c b/gcc/testsuite/gcc.target/i386/pr81736-1f.c
new file mode 100644
index 00000000000..682cb3b4030
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-1f.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fkeep-frame-pointer" } */
+
+#include "pr81736-1a.c"
+
+/* Need to use a frame pointer.  */
+/* { dg-final { scan-assembler "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-1g.c b/gcc/testsuite/gcc.target/i386/pr81736-1g.c
new file mode 100644
index 00000000000..b6d19985c8c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-1g.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer -fkeep-frame-pointer" } */
+
+#include "pr81736-1a.c"
+
+/* Need to use a frame pointer.  */
+/* { dg-final { scan-assembler "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-2.c b/gcc/testsuite/gcc.target/i386/pr81736-2.c
new file mode 100644
index 00000000000..a3720879937
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-2.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer" } */
+
+int
+#ifndef __x86_64__
+__attribute__((regparm(3)))
+#endif
+foo (int i)
+{
+  return i;
+}
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-3.c b/gcc/testsuite/gcc.target/i386/pr81736-3.c
new file mode 100644
index 00000000000..c3bde7dd933
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-3.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer" } */
+
+void
+foo (void)
+{
+  asm ("# " : : : "ebx");
+}
+
+/* Need to use a frame pointer.  */
+/* { dg-final { scan-assembler "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-4.c b/gcc/testsuite/gcc.target/i386/pr81736-4.c
new file mode 100644
index 00000000000..25f50016a64
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-4.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer" } */
+
+int
+foo (int i1, int i2, int i3, int i4, int i5, int i6, int i7)
+{
+  return i7;
+}
+
+/* Need to use a frame pointer.  */
+/* { dg-final { scan-assembler "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-5.c b/gcc/testsuite/gcc.target/i386/pr81736-5.c
new file mode 100644
index 00000000000..e1602cf25ba
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-5.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer -mavx" } */
+
+typedef int v8si __attribute__ ((vector_size (32)));
+
+void
+#ifndef __x86_64__
+__attribute__((regparm(3)))
+#endif
+foo (v8si *out_start, v8si *out_end, v8si *regions)
+{
+  v8si base = regions[3];
+  *out_start = base;
+  *out_end = base;
+}
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
+/* Verify no dynamic realignment is performed.  */
+/* { dg-final { scan-assembler-not "and\[^\n\r]*sp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-6.c b/gcc/testsuite/gcc.target/i386/pr81736-6.c
new file mode 100644
index 00000000000..6198574c8cc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-6.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer" } */
+
+struct foo
+{
+  int head;
+} a;
+
+int
+bar (void)
+{
+  return a.head != 0;
+}
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr81736-7.c b/gcc/testsuite/gcc.target/i386/pr81736-7.c
new file mode 100644
index 00000000000..f947886e642
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr81736-7.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer" } */
+
+extern int foo (void);
+
+int
+bar (void)
+{
+  return foo ();
+}
+
+/* No need to use a frame pointer.  */
+/* { dg-final { scan-assembler-not "%\[re\]bp" } } */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d23714c4773..55069784bdd 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1297,6 +1297,10 @@  process_options (void)
 	   "-floop-parallelize-all)");
 #endif
 
+  /* Don't omit frame pointer if -fkeep-frame-pointer is used.  */
+  if (flag_keep_frame_pointer)
+    flag_omit_frame_pointer = 0;
+
   if (flag_check_pointer_bounds)
     {
       if (targetm.chkp_bound_mode () == VOIDmode)
-- 
2.13.4