diff mbox

i386: Update INCOMING_FRAME_SP_OFFSET for exception handler

Message ID 20170729142251.GA19499@gmail.com
State New
Headers show

Commit Message

H.J. Lu July 29, 2017, 2:22 p.m. UTC
Since there is an extra error code passed to the exception handler,
INCOMING_FRAME_SP_OFFSET is return address plus error code for the
exception handler.  This patch updates INCOMING_FRAME_SP_OFFSET to
the correct value for the exception handler.

This patch exposed a bug in DWARF stack frame CFI generation, which
assumes that INCOMING_FRAME_SP_OFFSET is the same for all functions:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81570

It sets and caches the incoming stack frame offset with the same
INCOMING_FRAME_SP_OFFSET for all functions.  When there are both
exception handler and normal function in the same input, the wrong
incoming stack frame offset is used for exception handler or normal
function, which leads to

FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 error == 0x12345670
FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->ip == 0x12345671
FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->cs == 0x12345672
FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->flags == 0x12345673
FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->sp == 0x12345674
FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->ss == 0x12345675

With the patch for PR 81570:

https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01851.html

applied, there are no regressions on i686 and x86-64.

OK for trunk?

H.J.
---
gcc/

	PR target/79793
	* config/i386/i386.c (ix86_function_arg): Update arguments for
	exception handler.
	(ix86_compute_frame_layout): Set the initial stack offset to
	INCOMING_FRAME_SP_OFFSET.  Update red-zone offset with
	INCOMING_FRAME_SP_OFFSET.
	(ix86_expand_epilogue): Don't pop the 'ERROR_CODE' off the
	stack before exception handler returns.
	* config/i386/i386.h (INCOMING_FRAME_SP_OFFSET): Add the
	the 'ERROR_CODE' for exception handler.

gcc/testsuite/

	PR target/79793
	* gcc.dg/guality/pr68037-1.c: Update gdb breakpoints.
	* gcc.target/i386/interrupt-5.c (interrupt_frame): New struct.
	(foo): Check the builtin return address against the return address
	in interrupt frame.
	* gcc.target/i386/pr79793-1.c: New test.
	* gcc.target/i386/pr79793-2.c: Likewise.
---
 gcc/config/i386/i386.c                      | 36 +++++++++--------------------
 gcc/config/i386/i386.h                      |  6 +++--
 gcc/testsuite/gcc.dg/guality/pr68037-1.c    | 12 +++++-----
 gcc/testsuite/gcc.target/i386/interrupt-5.c | 13 +++++++++--
 gcc/testsuite/gcc.target/i386/pr79793-1.c   | 14 +++++++++++
 gcc/testsuite/gcc.target/i386/pr79793-2.c   | 16 +++++++++++++
 6 files changed, 62 insertions(+), 35 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr79793-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr79793-2.c

Comments

Uros Bizjak July 30, 2017, 9:18 a.m. UTC | #1
On Sat, Jul 29, 2017 at 4:22 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> Since there is an extra error code passed to the exception handler,
> INCOMING_FRAME_SP_OFFSET is return address plus error code for the
> exception handler.  This patch updates INCOMING_FRAME_SP_OFFSET to
> the correct value for the exception handler.
>
> This patch exposed a bug in DWARF stack frame CFI generation, which
> assumes that INCOMING_FRAME_SP_OFFSET is the same for all functions:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81570
>
> It sets and caches the incoming stack frame offset with the same
> INCOMING_FRAME_SP_OFFSET for all functions.  When there are both
> exception handler and normal function in the same input, the wrong
> incoming stack frame offset is used for exception handler or normal
> function, which leads to
>
> FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 error == 0x12345670
> FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->ip == 0x12345671
> FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->cs == 0x12345672
> FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->flags == 0x12345673
> FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->sp == 0x12345674
> FAIL: gcc.dg/guality/pr68037-1.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  line 33 frame->ss == 0x12345675
>
> With the patch for PR 81570:
>
> https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01851.html
>
> applied, there are no regressions on i686 and x86-64.
>
> OK for trunk?

I have two nits below.

LGTM - you know this part better than I, so it is your call.

Uros.

> H.J.
> ---
> gcc/
>
>         PR target/79793
>         * config/i386/i386.c (ix86_function_arg): Update arguments for
>         exception handler.
>         (ix86_compute_frame_layout): Set the initial stack offset to
>         INCOMING_FRAME_SP_OFFSET.  Update red-zone offset with
>         INCOMING_FRAME_SP_OFFSET.
>         (ix86_expand_epilogue): Don't pop the 'ERROR_CODE' off the
>         stack before exception handler returns.
>         * config/i386/i386.h (INCOMING_FRAME_SP_OFFSET): Add the
>         the 'ERROR_CODE' for exception handler.
>
> gcc/testsuite/
>
>         PR target/79793
>         * gcc.dg/guality/pr68037-1.c: Update gdb breakpoints.
>         * gcc.target/i386/interrupt-5.c (interrupt_frame): New struct.
>         (foo): Check the builtin return address against the return address
>         in interrupt frame.
>         * gcc.target/i386/pr79793-1.c: New test.
>         * gcc.target/i386/pr79793-2.c: Likewise.
> ---
>  gcc/config/i386/i386.c                      | 36 +++++++++--------------------
>  gcc/config/i386/i386.h                      |  6 +++--
>  gcc/testsuite/gcc.dg/guality/pr68037-1.c    | 12 +++++-----
>  gcc/testsuite/gcc.target/i386/interrupt-5.c | 13 +++++++++--
>  gcc/testsuite/gcc.target/i386/pr79793-1.c   | 14 +++++++++++
>  gcc/testsuite/gcc.target/i386/pr79793-2.c   | 16 +++++++++++++
>  6 files changed, 62 insertions(+), 35 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr79793-1.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr79793-2.c
>
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index f1486ff3750..e1582f8c9a6 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -10422,25 +10422,21 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
>         {
>           /* This is the pointer argument.  */
>           gcc_assert (TYPE_MODE (type) == Pmode);
> -         if (cfun->machine->func_type == TYPE_INTERRUPT)
> -           /* -WORD(AP) in the current frame in interrupt handler.  */
> -           arg = plus_constant (Pmode, arg_pointer_rtx,
> -                                -UNITS_PER_WORD);
> -         else
> -           /* (AP) in the current frame in exception handler.  */
> -           arg = arg_pointer_rtx;
> +         /* It is at -WORD(AP) in the current frame in interrupt and
> +            exception handlers.  */
> +         arg = plus_constant (Pmode, arg_pointer_rtx, -UNITS_PER_WORD);
>         }
>        else
>         {
>           gcc_assert (cfun->machine->func_type == TYPE_EXCEPTION
>                       && TREE_CODE (type) == INTEGER_TYPE
>                       && TYPE_MODE (type) == word_mode);
> -         /* The integer argument is the error code at -WORD(AP) in
> +         /* The integer argument is the error code at -2 * WORD(AP) in
>              the current frame in exception handler.  */

The comment should probably say:

  /* The error code is the word-mode integer argument at -2 * WORD(AP)
in the current frame of the exception handler.  */

>           arg = gen_rtx_MEM (word_mode,
>                              plus_constant (Pmode,
>                                             arg_pointer_rtx,
> -                                           -UNITS_PER_WORD));
> +                                           -2 * UNITS_PER_WORD));
>         }
>        return arg;
>      }
> @@ -12915,8 +12911,8 @@ ix86_compute_frame_layout (void)
>           the registers need to be saved before allocating the frame.  */
>         && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
>
> -  /* Skip return address.  */
> -  offset = UNITS_PER_WORD;
> +  /* Skip return address and error code in exception handler.  */
> +  offset = INCOMING_FRAME_SP_OFFSET;
>
>    /* Skip pushed static chain.  */
>    if (ix86_static_chain_on_stack)
> @@ -15221,8 +15217,9 @@ ix86_expand_epilogue (int style)
>    m->fs.red_zone_offset = 0;
>    if (ix86_using_red_zone () && crtl->args.pops_args < 65536)
>      {
> -      /* The red-zone begins below the return address.  */
> -      m->fs.red_zone_offset = RED_ZONE_SIZE + UNITS_PER_WORD;
> +      /* The red-zone begins below return address and error code in
> +        exception handler.  */
> +      m->fs.red_zone_offset = RED_ZONE_SIZE + INCOMING_FRAME_SP_OFFSET;
>
>        /* When the register save area is in the aligned portion of
>           the stack, determine the maximum runtime displacement that
> @@ -15517,18 +15514,7 @@ ix86_expand_epilogue (int style)
>      }
>
>    if (cfun->machine->func_type != TYPE_NORMAL)
> -    {
> -      /* Return with the "IRET" instruction from interrupt handler.
> -        Pop the 'ERROR_CODE' off the stack before the 'IRET'
> -        instruction in exception handler.  */
> -      if (cfun->machine->func_type == TYPE_EXCEPTION)
> -       {
> -         rtx r = plus_constant (Pmode, stack_pointer_rtx,
> -                                UNITS_PER_WORD);
> -         emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
> -       }
> -      emit_jump_insn (gen_interrupt_return ());
> -    }
> +    emit_jump_insn (gen_interrupt_return ());
>    else if (crtl->args.pops_args && crtl->args.size)
>      {
>        rtx popc = GEN_INT (crtl->args.pops_args);
> diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
> index 682745ae06b..ddbd6759caf 100644
> --- a/gcc/config/i386/i386.h
> +++ b/gcc/config/i386/i386.h
> @@ -2177,8 +2177,10 @@ extern int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER];
>  /* PC is dbx register 8; let's use that column for RA.  */
>  #define DWARF_FRAME_RETURN_COLUMN      (TARGET_64BIT ? 16 : 8)
>
> -/* Before the prologue, the top of the frame is at 4(%esp).  */
> -#define INCOMING_FRAME_SP_OFFSET UNITS_PER_WORD
> +/* Before the prologue, there are return address and error code for
> +   exception handler on the top of the frame.  */
> +#define INCOMING_FRAME_SP_OFFSET \
> +  (UNITS_PER_WORD * (1 + (cfun->machine->func_type == TYPE_EXCEPTION)))

The above is unnecessarily complicated way to write:

+#define INCOMING_FRAME_SP_OFFSET \
+  (cfun->machine->func_type == TYPE_EXCEPTION \
+   ? 2 * UNITS_PER_WORD : UNITS_PER_WORD)

>  /* Describe how we implement __builtin_eh_return.  */
>  #define EH_RETURN_DATA_REGNO(N)        ((N) <= DX_REG ? (N) : INVALID_REGNUM)
> diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-1.c b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
> index 74f61ec5f96..44cab58659f 100644
> --- a/gcc/testsuite/gcc.dg/guality/pr68037-1.c
> +++ b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
> @@ -59,9 +59,9 @@ main ()
>    return 0;
>  }
>
> -/* { dg-final { gdb-test 31 "error" "0x12345670" } } */
> -/* { dg-final { gdb-test 31 "frame->ip" "0x12345671" } } */
> -/* { dg-final { gdb-test 31 "frame->cs" "0x12345672" } } */
> -/* { dg-final { gdb-test 31 "frame->flags" "0x12345673" } } */
> -/* { dg-final { gdb-test 31 "frame->sp" "0x12345674" } } */
> -/* { dg-final { gdb-test 31 "frame->ss" "0x12345675" } } */
> +/* { dg-final { gdb-test 33 "error" "0x12345670" } } */
> +/* { dg-final { gdb-test 33 "frame->ip" "0x12345671" } } */
> +/* { dg-final { gdb-test 33 "frame->cs" "0x12345672" } } */
> +/* { dg-final { gdb-test 33 "frame->flags" "0x12345673" } } */
> +/* { dg-final { gdb-test 33 "frame->sp" "0x12345674" } } */
> +/* { dg-final { gdb-test 33 "frame->ss" "0x12345675" } } */
> diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
> index 803c0636299..5742b6f4743 100644
> --- a/gcc/testsuite/gcc.target/i386/interrupt-5.c
> +++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
> @@ -7,12 +7,21 @@ extern void link_error (void);
>
>  typedef unsigned int uword_t __attribute__ ((mode (__word__)));
>
> +struct interrupt_frame
> +{
> +  uword_t ip;
> +  uword_t cs;
> +  uword_t flags;
> +  uword_t sp;
> +  uword_t ss;
> +};
> +
>  __attribute__ ((used, interrupt))
>  void
> -foo (void *frame, uword_t error)
> +foo (struct interrupt_frame *frame, uword_t error)
>  {
>    void *ra = __builtin_return_address (0);
> -  if ((uintptr_t) ra != (uintptr_t) error)
> +  if ((uintptr_t) ra != (uintptr_t) frame->ip)
>      link_error ();
>  }
>
> diff --git a/gcc/testsuite/gcc.target/i386/pr79793-1.c b/gcc/testsuite/gcc.target/i386/pr79793-1.c
> new file mode 100644
> index 00000000000..a382fe9c5e2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr79793-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
> +/* { dg-options "-O2 -mgeneral-regs-only" } */
> +
> +void
> + __attribute__ ((interrupt))
> +fn1 (void *frame)
> +{
> +  char fxsave_region [512] __attribute__((aligned(16)));
> +  __builtin_ia32_fxsave64 (fxsave_region);
> +}
> +
> +/* { dg-final { scan-assembler-times "sub\[lq\]\[\t \]*\\\$400,\[\t \]*%\[re\]sp" 1 } } */
> +/* { dg-final { scan-assembler-times "fxsave64\[\t \]*-120\\(%\[re\]sp\\)" 1 } } */
> +/* { dg-final { scan-assembler-times "add\[lq\]\[\t \]*\\\$400,\[\t \]*%\[re\]sp" 1 } } */
> diff --git a/gcc/testsuite/gcc.target/i386/pr79793-2.c b/gcc/testsuite/gcc.target/i386/pr79793-2.c
> new file mode 100644
> index 00000000000..f6ae5aed33a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr79793-2.c
> @@ -0,0 +1,16 @@
> +/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
> +/* { dg-options "-O2 -mgeneral-regs-only" } */
> +
> +typedef unsigned int uword_t __attribute__ ((mode (__word__)));
> +
> +void
> + __attribute__ ((interrupt))
> +fn1 (void *frame, uword_t error)
> +{
> +  char fxsave_region [512] __attribute__((aligned(16)));
> +  __builtin_ia32_fxsave64 (fxsave_region);
> +}
> +
> +/* { dg-final { scan-assembler-times "sub\[lq\]\[\t \]*\\\$392,\[\t \]*%\[re\]sp" 1 } } */
> +/* { dg-final { scan-assembler-times "fxsave64\[\t \]*-120\\(%\[re\]sp\\)" 1 } } */
> +/* { dg-final { scan-assembler-times "add\[lq\]\[\t \]*\\\$400,\[\t \]*%\[re\]sp" 1 } } */
> --
> 2.13.3
>
diff mbox

Patch

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index f1486ff3750..e1582f8c9a6 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10422,25 +10422,21 @@  ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
 	{
 	  /* This is the pointer argument.  */
 	  gcc_assert (TYPE_MODE (type) == Pmode);
-	  if (cfun->machine->func_type == TYPE_INTERRUPT)
-	    /* -WORD(AP) in the current frame in interrupt handler.  */
-	    arg = plus_constant (Pmode, arg_pointer_rtx,
-				 -UNITS_PER_WORD);
-	  else
-	    /* (AP) in the current frame in exception handler.  */
-	    arg = arg_pointer_rtx;
+	  /* It is at -WORD(AP) in the current frame in interrupt and
+	     exception handlers.  */
+	  arg = plus_constant (Pmode, arg_pointer_rtx, -UNITS_PER_WORD);
 	}
       else
 	{
 	  gcc_assert (cfun->machine->func_type == TYPE_EXCEPTION
 		      && TREE_CODE (type) == INTEGER_TYPE
 		      && TYPE_MODE (type) == word_mode);
-	  /* The integer argument is the error code at -WORD(AP) in
+	  /* The integer argument is the error code at -2 * WORD(AP) in
 	     the current frame in exception handler.  */
 	  arg = gen_rtx_MEM (word_mode,
 			     plus_constant (Pmode,
 					    arg_pointer_rtx,
-					    -UNITS_PER_WORD));
+					    -2 * UNITS_PER_WORD));
 	}
       return arg;
     }
@@ -12915,8 +12911,8 @@  ix86_compute_frame_layout (void)
 	  the registers need to be saved before allocating the frame.  */
        && flag_stack_check != STATIC_BUILTIN_STACK_CHECK);
 
-  /* Skip return address.  */
-  offset = UNITS_PER_WORD;
+  /* Skip return address and error code in exception handler.  */
+  offset = INCOMING_FRAME_SP_OFFSET;
 
   /* Skip pushed static chain.  */
   if (ix86_static_chain_on_stack)
@@ -15221,8 +15217,9 @@  ix86_expand_epilogue (int style)
   m->fs.red_zone_offset = 0;
   if (ix86_using_red_zone () && crtl->args.pops_args < 65536)
     {
-      /* The red-zone begins below the return address.  */
-      m->fs.red_zone_offset = RED_ZONE_SIZE + UNITS_PER_WORD;
+      /* The red-zone begins below return address and error code in
+	 exception handler.  */
+      m->fs.red_zone_offset = RED_ZONE_SIZE + INCOMING_FRAME_SP_OFFSET;
 
       /* When the register save area is in the aligned portion of
          the stack, determine the maximum runtime displacement that
@@ -15517,18 +15514,7 @@  ix86_expand_epilogue (int style)
     }
 
   if (cfun->machine->func_type != TYPE_NORMAL)
-    {
-      /* Return with the "IRET" instruction from interrupt handler.
-	 Pop the 'ERROR_CODE' off the stack before the 'IRET'
-	 instruction in exception handler.  */
-      if (cfun->machine->func_type == TYPE_EXCEPTION)
-	{
-	  rtx r = plus_constant (Pmode, stack_pointer_rtx,
-				 UNITS_PER_WORD);
-	  emit_insn (gen_rtx_SET (stack_pointer_rtx, r));
-	}
-      emit_jump_insn (gen_interrupt_return ());
-    }
+    emit_jump_insn (gen_interrupt_return ());
   else if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 682745ae06b..ddbd6759caf 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2177,8 +2177,10 @@  extern int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER];
 /* PC is dbx register 8; let's use that column for RA.  */
 #define DWARF_FRAME_RETURN_COLUMN 	(TARGET_64BIT ? 16 : 8)
 
-/* Before the prologue, the top of the frame is at 4(%esp).  */
-#define INCOMING_FRAME_SP_OFFSET UNITS_PER_WORD
+/* Before the prologue, there are return address and error code for
+   exception handler on the top of the frame.  */
+#define INCOMING_FRAME_SP_OFFSET \
+  (UNITS_PER_WORD * (1 + (cfun->machine->func_type == TYPE_EXCEPTION)))
 
 /* Describe how we implement __builtin_eh_return.  */
 #define EH_RETURN_DATA_REGNO(N)	((N) <= DX_REG ? (N) : INVALID_REGNUM)
diff --git a/gcc/testsuite/gcc.dg/guality/pr68037-1.c b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
index 74f61ec5f96..44cab58659f 100644
--- a/gcc/testsuite/gcc.dg/guality/pr68037-1.c
+++ b/gcc/testsuite/gcc.dg/guality/pr68037-1.c
@@ -59,9 +59,9 @@  main ()
   return 0;
 }
 
-/* { dg-final { gdb-test 31 "error" "0x12345670" } } */
-/* { dg-final { gdb-test 31 "frame->ip" "0x12345671" } } */
-/* { dg-final { gdb-test 31 "frame->cs" "0x12345672" } } */
-/* { dg-final { gdb-test 31 "frame->flags" "0x12345673" } } */
-/* { dg-final { gdb-test 31 "frame->sp" "0x12345674" } } */
-/* { dg-final { gdb-test 31 "frame->ss" "0x12345675" } } */
+/* { dg-final { gdb-test 33 "error" "0x12345670" } } */
+/* { dg-final { gdb-test 33 "frame->ip" "0x12345671" } } */
+/* { dg-final { gdb-test 33 "frame->cs" "0x12345672" } } */
+/* { dg-final { gdb-test 33 "frame->flags" "0x12345673" } } */
+/* { dg-final { gdb-test 33 "frame->sp" "0x12345674" } } */
+/* { dg-final { gdb-test 33 "frame->ss" "0x12345675" } } */
diff --git a/gcc/testsuite/gcc.target/i386/interrupt-5.c b/gcc/testsuite/gcc.target/i386/interrupt-5.c
index 803c0636299..5742b6f4743 100644
--- a/gcc/testsuite/gcc.target/i386/interrupt-5.c
+++ b/gcc/testsuite/gcc.target/i386/interrupt-5.c
@@ -7,12 +7,21 @@  extern void link_error (void);
 
 typedef unsigned int uword_t __attribute__ ((mode (__word__)));
 
+struct interrupt_frame
+{
+  uword_t ip;
+  uword_t cs;
+  uword_t flags;
+  uword_t sp;
+  uword_t ss;
+};
+
 __attribute__ ((used, interrupt))
 void
-foo (void *frame, uword_t error)
+foo (struct interrupt_frame *frame, uword_t error)
 {
   void *ra = __builtin_return_address (0);
-  if ((uintptr_t) ra != (uintptr_t) error)
+  if ((uintptr_t) ra != (uintptr_t) frame->ip)
     link_error ();
 }
 
diff --git a/gcc/testsuite/gcc.target/i386/pr79793-1.c b/gcc/testsuite/gcc.target/i386/pr79793-1.c
new file mode 100644
index 00000000000..a382fe9c5e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr79793-1.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -mgeneral-regs-only" } */
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame)
+{
+  char fxsave_region [512] __attribute__((aligned(16)));
+  __builtin_ia32_fxsave64 (fxsave_region);
+}
+
+/* { dg-final { scan-assembler-times "sub\[lq\]\[\t \]*\\\$400,\[\t \]*%\[re\]sp" 1 } } */
+/* { dg-final { scan-assembler-times "fxsave64\[\t \]*-120\\(%\[re\]sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "add\[lq\]\[\t \]*\\\$400,\[\t \]*%\[re\]sp" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr79793-2.c b/gcc/testsuite/gcc.target/i386/pr79793-2.c
new file mode 100644
index 00000000000..f6ae5aed33a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr79793-2.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -mgeneral-regs-only" } */
+
+typedef unsigned int uword_t __attribute__ ((mode (__word__)));
+
+void
+ __attribute__ ((interrupt))
+fn1 (void *frame, uword_t error)
+{
+  char fxsave_region [512] __attribute__((aligned(16)));
+  __builtin_ia32_fxsave64 (fxsave_region);
+}
+
+/* { dg-final { scan-assembler-times "sub\[lq\]\[\t \]*\\\$392,\[\t \]*%\[re\]sp" 1 } } */
+/* { dg-final { scan-assembler-times "fxsave64\[\t \]*-120\\(%\[re\]sp\\)" 1 } } */
+/* { dg-final { scan-assembler-times "add\[lq\]\[\t \]*\\\$400,\[\t \]*%\[re\]sp" 1 } } */