i386: Update -mfunction-return= for return with pop

Message ID 20180225013732.GA24850@gmail.com
State New
Headers show
Series
  • i386: Update -mfunction-return= for return with pop
Related show

Commit Message

H.J. Lu Feb. 25, 2018, 1:37 a.m.
When -mfunction-return= is used, simple_return_pop_internal should pop
return address into ECX register, adjust stack by bytes to pop from stack
and jump to the return thunk via ECX register.

Tested on i686 and x86-64.

	PR target/r84530
	* config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove
	the bool argument.
	(ix86_output_indirect_function_return): New prototype.
	(ix86_split_simple_return_pop_internal): Likewise.
	* config/i386/i386.c (indirect_return_via_cx): New.
	(indirect_return_via_cx_bnd): Likewise.
	(indirect_thunk_name): Handle return va CX_REG.
	(output_indirect_thunk_function): Create alias for
	__x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.
	(ix86_output_indirect_jmp): Remove the bool argument.
	(ix86_output_indirect_function_return): New function.
	(ix86_split_simple_return_pop_internal): Likewise.
	* config/i386/i386.md (*indirect_jump): Don't pass false
	to ix86_output_indirect_jmp.
	(*tablejump_1): Likewise.
	(simple_return_pop_internal): Change it to define_insn_and_split.
	Call ix86_split_simple_return_pop_internal to split it for
	-mfunction-return=.
	(simple_return_indirect_internal): Call
	ix86_output_indirect_function_return instead of
	ix86_output_indirect_jmp.

gcc/testsuite/

	PR target/r84530
	* gcc.target/i386/ret-thunk-22.c: New test.
	* gcc.target/i386/ret-thunk-23.c: Likewise.
	* gcc.target/i386/ret-thunk-24.c: Likewise.
	* gcc.target/i386/ret-thunk-25.c: Likewise.
	* gcc.target/i386/ret-thunk-26.c: Likewise.
---
 gcc/config/i386/i386-protos.h                |   4 +-
 gcc/config/i386/i386.c                       | 126 ++++++++++++++++++++++++---
 gcc/config/i386/i386.md                      |  11 ++-
 gcc/testsuite/gcc.target/i386/ret-thunk-22.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-23.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-24.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-25.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-26.c |  40 +++++++++
 8 files changed, 223 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-22.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-23.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-24.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-25.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-26.c

-- 
2.14.3

Comments

Martin Liška Feb. 26, 2018, 10:05 a.m. | #1
On 02/25/2018 02:37 AM, H.J. Lu wrote:
> 	PR target/r84530


Just a nit I've noticed:

s/r84530/84530

Martin
H.J. Lu Feb. 26, 2018, 10:19 a.m. | #2
On Mon, Feb 26, 2018 at 2:05 AM, Martin Liška <mliska@suse.cz> wrote:
> On 02/25/2018 02:37 AM, H.J. Lu wrote:

>>       PR target/r84530

>

> Just a nit I've noticed:

>

> s/r84530/84530


Fixed.

-- 
H.J.
From f08b8721ed038cefcee5a0bb1329b90a2d322269 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 24 Feb 2018 05:05:49 -0800
Subject: [PATCH] i386: Update -mfunction-return= for return with pop

When -mfunction-return= is used, simple_return_pop_internal should pop
return address into ECX register, adjust stack by bytes to pop from stack
and jump to the return thunk via ECX register.

Tested on i686 and x86-64.

	PR target/84530
	* config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove
	the bool argument.
	(ix86_output_indirect_function_return): New prototype.
	(ix86_split_simple_return_pop_internal): Likewise.
	* config/i386/i386.c (indirect_return_via_cx): New.
	(indirect_return_via_cx_bnd): Likewise.
	(indirect_thunk_name): Handle return va CX_REG.
	(output_indirect_thunk_function): Create alias for
	__x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.
	(ix86_output_indirect_jmp): Remove the bool argument.
	(ix86_output_indirect_function_return): New function.
	(ix86_split_simple_return_pop_internal): Likewise.
	* config/i386/i386.md (*indirect_jump): Don't pass false
	to ix86_output_indirect_jmp.
	(*tablejump_1): Likewise.
	(simple_return_pop_internal): Change it to define_insn_and_split.
	Call ix86_split_simple_return_pop_internal to split it for
	-mfunction-return=.
	(simple_return_indirect_internal): Call
	ix86_output_indirect_function_return instead of
	ix86_output_indirect_jmp.

gcc/testsuite/

	PR target/84530
	* gcc.target/i386/ret-thunk-22.c: New test.
	* gcc.target/i386/ret-thunk-23.c: Likewise.
	* gcc.target/i386/ret-thunk-24.c: Likewise.
	* gcc.target/i386/ret-thunk-25.c: Likewise.
	* gcc.target/i386/ret-thunk-26.c: Likewise.
---
 gcc/config/i386/i386-protos.h                |   4 +-
 gcc/config/i386/i386.c                       | 126 ++++++++++++++++++++++++---
 gcc/config/i386/i386.md                      |  11 ++-
 gcc/testsuite/gcc.target/i386/ret-thunk-22.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-23.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-24.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-25.c |  15 ++++
 gcc/testsuite/gcc.target/i386/ret-thunk-26.c |  40 +++++++++
 8 files changed, 223 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-22.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-23.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-24.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-25.c
 create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-26.c

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index fb86f00b3a6..79b96c0f80b 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -305,8 +305,10 @@ extern enum attr_cpu ix86_schedule;
 #endif
 
 extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
-extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
+extern const char * ix86_output_indirect_jmp (rtx call_op);
 extern const char * ix86_output_function_return (bool long_p);
+extern const char * ix86_output_indirect_function_return (rtx ret_op);
+extern void ix86_split_simple_return_pop_internal (rtx);
 extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 						machine_mode mode);
 extern int ix86_min_insn_size (rtx_insn *);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 640c5921863..98075ad42c7 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10812,6 +10812,12 @@ static int indirect_thunks_used;
    by call and return thunks functions with the BND prefix.  */
 static int indirect_thunks_bnd_used;
 
+/* True if return thunk function via CX is needed.  */
+static bool indirect_return_via_cx;
+/* True if return thunk function via CX with the BND prefix is
+   needed.  */
+static bool indirect_return_via_cx_bnd;
+
 #ifndef INDIRECT_LABEL
 # define INDIRECT_LABEL "LIND"
 #endif
@@ -10852,7 +10858,7 @@ indirect_thunk_name (char name[32], unsigned int regno,
 		     enum indirect_thunk_prefix need_prefix,
 		     bool ret_p)
 {
-  if (regno != INVALID_REGNUM && ret_p)
+  if (regno != INVALID_REGNUM && regno != CX_REG && ret_p)
     gcc_unreachable ();
 
   if (USE_HIDDEN_LINKONCE)
@@ -10872,6 +10878,8 @@ indirect_thunk_name (char name[32], unsigned int regno,
       else
 	prefix = "";
 
+      const char *ret = ret_p ? "return" : "indirect";
+
       if (regno != INVALID_REGNUM)
 	{
 	  const char *reg_prefix;
@@ -10879,14 +10887,11 @@ indirect_thunk_name (char name[32], unsigned int regno,
 	    reg_prefix = TARGET_64BIT ? "r" : "e";
 	  else
 	    reg_prefix = "";
-	  sprintf (name, "__x86_indirect_thunk%s_%s%s",
-		   prefix, reg_prefix, reg_names[regno]);
+	  sprintf (name, "__x86_%s_thunk%s_%s%s",
+		   ret, prefix, reg_prefix, reg_names[regno]);
 	}
       else
-	{
-	  const char *ret = ret_p ? "return" : "indirect";
-	  sprintf (name, "__x86_%s_thunk%s", ret, prefix);
-	}
+	sprintf (name, "__x86_%s_thunk%s", ret, prefix);
     }
   else
     {
@@ -11055,9 +11060,23 @@ output_indirect_thunk_function (enum indirect_thunk_prefix need_prefix,
 	ASM_OUTPUT_LABEL (asm_out_file, name);
       }
 
+  /* Create alias for __x86_return_thunk/__x86_return_thunk_bnd or
+     __x86_return_thunk_ecx/__x86_return_thunk_ecx_bnd.  */
+  bool need_alias;
   if (regno == INVALID_REGNUM)
+    need_alias = true;
+  else if (regno == CX_REG)
+    {
+      if (need_prefix == indirect_thunk_prefix_bnd)
+	need_alias = indirect_return_via_cx_bnd;
+      else
+	need_alias = indirect_return_via_cx;
+    }
+  else
+    need_alias = false;
+
+  if (need_alias)
     {
-      /* Create alias for __x86.return_thunk/__x86.return_thunk_bnd.  */
       char alias[32];
 
       indirect_thunk_name (alias, regno, need_prefix, true);
@@ -28979,18 +28998,18 @@ ix86_output_indirect_branch (rtx call_op, const char *xasm,
   else
     ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
 }
+
 /* Output indirect jump.  CALL_OP is the jump target.  Jump is a
    function return if RET_P is true.  */
 
 const char *
-ix86_output_indirect_jmp (rtx call_op, bool ret_p)
+ix86_output_indirect_jmp (rtx call_op)
 {
   if (cfun->machine->indirect_branch_type != indirect_branch_keep)
     {
-      /* We can't have red-zone if this isn't a function return since
-	 "call" in the indirect thunk pushes the return address onto
-	 stack, destroying red-zone.  */
-      if (!ret_p && ix86_red_zone_size != 0)
+      /* We can't have red-zone since "call" in the indirect thunk
+         pushes the return address onto stack, destroying red-zone.  */
+      if (ix86_red_zone_size != 0)
 	gcc_unreachable ();
 
       ix86_output_indirect_branch (call_op, "%0", true);
@@ -29042,6 +29061,87 @@ ix86_output_function_return (bool long_p)
   return "rep%; ret";
 }
 
+/* Output indirect function return.  RET_OP is the function return
+   target.  */
+
+const char *
+ix86_output_indirect_function_return (rtx ret_op)
+{
+  if (cfun->machine->function_return_type != indirect_branch_keep)
+    {
+      char thunk_name[32];
+      enum indirect_thunk_prefix need_prefix
+	= indirect_thunk_need_prefix (current_output_insn);
+      unsigned int regno = REGNO (ret_op);
+      gcc_assert (regno == CX_REG);
+
+      if (cfun->machine->function_return_type
+	  != indirect_branch_thunk_inline)
+	{
+	  bool need_thunk = (cfun->machine->function_return_type
+			     == indirect_branch_thunk);
+	  indirect_thunk_name (thunk_name, regno, need_prefix, true);
+	  if (need_prefix == indirect_thunk_prefix_bnd)
+	    {
+	      if (need_thunk)
+		{
+		  indirect_return_via_cx_bnd = true;
+		  indirect_thunks_bnd_used |= 1 << CX_REG;
+		}
+	      fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+	    }
+	  else
+	    {
+	      if (need_thunk)
+		{
+		  indirect_return_via_cx = true;
+		  indirect_thunks_used |= 1 << CX_REG;
+		}
+	      fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+	    }
+	}
+      else
+	output_indirect_thunk (need_prefix, regno);
+
+      return "";
+    }
+  else
+    return "%!jmp\t%A0";
+}
+
+/* Split simple return with popping POPC bytes from stack to indirect
+   branch with stack adjustment .  */
+
+void
+ix86_split_simple_return_pop_internal (rtx popc)
+{
+  struct machine_function *m = cfun->machine;
+  rtx ecx = gen_rtx_REG (SImode, CX_REG);
+  rtx_insn *insn;
+
+  /* There is no "pascal" calling convention in any 64bit ABI.  */
+  gcc_assert (!TARGET_64BIT);
+
+  insn = emit_insn (gen_pop (ecx));
+  m->fs.cfa_offset -= UNITS_PER_WORD;
+  m->fs.sp_offset -= UNITS_PER_WORD;
+
+  rtx x = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD);
+  x = gen_rtx_SET (stack_pointer_rtx, x);
+  add_reg_note (insn, REG_CFA_ADJUST_CFA, x);
+  add_reg_note (insn, REG_CFA_REGISTER, gen_rtx_SET (ecx, pc_rtx));
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, popc);
+  x = gen_rtx_SET (stack_pointer_rtx, x);
+  insn = emit_insn (x);
+  add_reg_note (insn, REG_CFA_ADJUST_CFA, x);
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  /* Now return address is in ECX.  */
+  emit_jump_insn (gen_simple_return_indirect_internal (ecx));
+}
+
 /* Output the assembly for a call instruction.  */
 
 const char *
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 3998053a506..f271278ed58 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -12319,7 +12319,7 @@
 (define_insn "*indirect_jump"
   [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))]
   ""
-  "* return ix86_output_indirect_jmp (operands[0], false);"
+  "* return ix86_output_indirect_jmp (operands[0]);"
   [(set (attr "type")
      (if_then_else (match_test "(cfun->machine->indirect_branch_type
 				 != indirect_branch_keep)")
@@ -12374,7 +12374,7 @@
   [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))
    (use (label_ref (match_operand 1)))]
   ""
-  "* return ix86_output_indirect_jmp (operands[0], false);"
+  "* return ix86_output_indirect_jmp (operands[0]);"
   [(set (attr "type")
      (if_then_else (match_test "(cfun->machine->indirect_branch_type
 				 != indirect_branch_keep)")
@@ -13097,11 +13097,14 @@
    (set_attr "prefix_rep" "1")
    (set_attr "modrm" "0")])
 
-(define_insn "simple_return_pop_internal"
+(define_insn_and_split "simple_return_pop_internal"
   [(simple_return)
    (use (match_operand:SI 0 "const_int_operand"))]
   "reload_completed"
   "%!ret\t%0"
+  "&& cfun->machine->function_return_type != indirect_branch_keep"
+  [(const_int 0)]
+  "ix86_split_simple_return_pop_internal (operands[0]); DONE;"
   [(set_attr "length" "3")
    (set_attr "atom_unit" "jeu")
    (set_attr "length_immediate" "2")
@@ -13112,7 +13115,7 @@
   [(simple_return)
    (use (match_operand 0 "register_operand" "r"))]
   "reload_completed"
-  "* return ix86_output_indirect_jmp (operands[0], true);"
+  "* return ix86_output_indirect_function_return (operands[0]);"
   [(set (attr "type")
      (if_then_else (match_test "(cfun->machine->indirect_branch_type
 				 != indirect_branch_keep)")
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-22.c b/gcc/testsuite/gcc.target/i386/ret-thunk-22.c
new file mode 100644
index 00000000000..89e086de97b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-22.c
@@ -0,0 +1,15 @@
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_ecx" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-23.c b/gcc/testsuite/gcc.target/i386/ret-thunk-23.c
new file mode 100644
index 00000000000..43f0ccaa854
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-23.c
@@ -0,0 +1,15 @@
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_ecx" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not {\tpause} } } */
+/* { dg-final { scan-assembler-not {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-24.c b/gcc/testsuite/gcc.target/i386/ret-thunk-24.c
new file mode 100644
index 00000000000..8729e35147e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-24.c
@@ -0,0 +1,15 @@
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk_ecx" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-25.c b/gcc/testsuite/gcc.target/i386/ret-thunk-25.c
new file mode 100644
index 00000000000..f73553c9a9f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-25.c
@@ -0,0 +1,15 @@
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_bnd_ecx" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-26.c b/gcc/testsuite/gcc.target/i386/ret-thunk-26.c
new file mode 100644
index 00000000000..9144e988735
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-26.c
@@ -0,0 +1,40 @@
+/* PR target/r84530 */
+/* { dg-do run } */
+/* { dg-options "-Os -mfunction-return=thunk" } */
+
+struct S { int i; };
+__attribute__((const, noinline, noclone))
+struct S foo (int x)
+{
+  struct S s;
+  s.i = x;
+  return s;
+}
+
+int a[2048], b[2048], c[2048], d[2048];
+struct S e[2048];
+
+__attribute__((noinline, noclone)) void
+bar (void)
+{
+  int i;
+  for (i = 0; i < 1024; i++)
+    {
+      e[i] = foo (i);
+      a[i+2] = a[i] + a[i+1];
+      b[10] = b[10] + i;
+      c[i] = c[2047 - i];
+      d[i] = d[i + 1];
+    }
+}
+
+int
+main ()
+{
+  int i;
+  bar ();
+  for (i = 0; i < 1024; i++)
+    if (e[i].i != i)
+      __builtin_abort ();
+  return 0;
+}
Jan Hubicka Feb. 26, 2018, 10:58 a.m. | #3
> On Mon, Feb 26, 2018 at 2:05 AM, Martin Liška <mliska@suse.cz> wrote:

> > On 02/25/2018 02:37 AM, H.J. Lu wrote:

> >>       PR target/r84530

> >

> > Just a nit I've noticed:

> >

> > s/r84530/84530

> 

> Fixed.

> 

> -- 

> H.J.


> From f08b8721ed038cefcee5a0bb1329b90a2d322269 Mon Sep 17 00:00:00 2001

> From: "H.J. Lu" <hjl.tools@gmail.com>

> Date: Sat, 24 Feb 2018 05:05:49 -0800

> Subject: [PATCH] i386: Update -mfunction-return= for return with pop

> 

> When -mfunction-return= is used, simple_return_pop_internal should pop

> return address into ECX register, adjust stack by bytes to pop from stack

> and jump to the return thunk via ECX register.

> 

> Tested on i686 and x86-64.

> 

> 	PR target/84530

> 	* config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove

> 	the bool argument.

> 	(ix86_output_indirect_function_return): New prototype.

> 	(ix86_split_simple_return_pop_internal): Likewise.

> 	* config/i386/i386.c (indirect_return_via_cx): New.

> 	(indirect_return_via_cx_bnd): Likewise.

> 	(indirect_thunk_name): Handle return va CX_REG.

> 	(output_indirect_thunk_function): Create alias for

> 	__x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.

> 	(ix86_output_indirect_jmp): Remove the bool argument.

> 	(ix86_output_indirect_function_return): New function.

> 	(ix86_split_simple_return_pop_internal): Likewise.

> 	* config/i386/i386.md (*indirect_jump): Don't pass false

> 	to ix86_output_indirect_jmp.

> 	(*tablejump_1): Likewise.

> 	(simple_return_pop_internal): Change it to define_insn_and_split.

> 	Call ix86_split_simple_return_pop_internal to split it for

> 	-mfunction-return=.

> 	(simple_return_indirect_internal): Call

> 	ix86_output_indirect_function_return instead of

> 	ix86_output_indirect_jmp.


It seems to make sense. Since LLVM has picked up our thunk names now,
I wonder if they have same bug or someohow already assigned a name
to this thunk. In the second case we probably ought to match it. Do
you know what LLVM uses?

Honza
H.J. Lu Feb. 26, 2018, 11:46 a.m. | #4
On Mon, Feb 26, 2018 at 2:58 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> On Mon, Feb 26, 2018 at 2:05 AM, Martin Liška <mliska@suse.cz> wrote:

>> > On 02/25/2018 02:37 AM, H.J. Lu wrote:

>> >>       PR target/r84530

>> >

>> > Just a nit I've noticed:

>> >

>> > s/r84530/84530

>>

>> Fixed.

>>

>> --

>> H.J.

>

>> From f08b8721ed038cefcee5a0bb1329b90a2d322269 Mon Sep 17 00:00:00 2001

>> From: "H.J. Lu" <hjl.tools@gmail.com>

>> Date: Sat, 24 Feb 2018 05:05:49 -0800

>> Subject: [PATCH] i386: Update -mfunction-return= for return with pop

>>

>> When -mfunction-return= is used, simple_return_pop_internal should pop

>> return address into ECX register, adjust stack by bytes to pop from stack

>> and jump to the return thunk via ECX register.

>>

>> Tested on i686 and x86-64.

>>

>>       PR target/84530

>>       * config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove

>>       the bool argument.

>>       (ix86_output_indirect_function_return): New prototype.

>>       (ix86_split_simple_return_pop_internal): Likewise.

>>       * config/i386/i386.c (indirect_return_via_cx): New.

>>       (indirect_return_via_cx_bnd): Likewise.

>>       (indirect_thunk_name): Handle return va CX_REG.

>>       (output_indirect_thunk_function): Create alias for

>>       __x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.

>>       (ix86_output_indirect_jmp): Remove the bool argument.

>>       (ix86_output_indirect_function_return): New function.

>>       (ix86_split_simple_return_pop_internal): Likewise.

>>       * config/i386/i386.md (*indirect_jump): Don't pass false

>>       to ix86_output_indirect_jmp.

>>       (*tablejump_1): Likewise.

>>       (simple_return_pop_internal): Change it to define_insn_and_split.

>>       Call ix86_split_simple_return_pop_internal to split it for

>>       -mfunction-return=.

>>       (simple_return_indirect_internal): Call

>>       ix86_output_indirect_function_return instead of

>>       ix86_output_indirect_jmp.

>

> It seems to make sense. Since LLVM has picked up our thunk names now,

> I wonder if they have same bug or someohow already assigned a name

> to this thunk. In the second case we probably ought to match it. Do

> you know what LLVM uses?

>


I don't think LLVM has implemented -mfunction-return=.   I disclosed
my -mindirect-branch= approach to LLVM last year.  At the time, they
wanted freedom in their implementation and didn't want to be compatible
with mine.   Later they added GCC compatibility so that LLVM may be
used to compile Linux kernel.  I believe it will be the same case if this
option is used to compile Linux kernel.

BTW, is my patch:

https://gcc.gnu.org/ml/gcc-patches/2018-01/msg02233.html

OK for trunk?  The review died at

https://gcc.gnu.org/ml/gcc-patches/2018-02/msg01355.html

Thanks.

-- 
H.J.
Jan Hubicka Feb. 26, 2018, 3:06 p.m. | #5
> On Mon, Feb 26, 2018 at 2:58 AM, Jan Hubicka <hubicka@ucw.cz> wrote:

> >> On Mon, Feb 26, 2018 at 2:05 AM, Martin Liška <mliska@suse.cz> wrote:

> >> > On 02/25/2018 02:37 AM, H.J. Lu wrote:

> >> >>       PR target/r84530

> >> >

> >> > Just a nit I've noticed:

> >> >

> >> > s/r84530/84530

> >>

> >> Fixed.

> >>

> >> --

> >> H.J.

> >

> >> From f08b8721ed038cefcee5a0bb1329b90a2d322269 Mon Sep 17 00:00:00 2001

> >> From: "H.J. Lu" <hjl.tools@gmail.com>

> >> Date: Sat, 24 Feb 2018 05:05:49 -0800

> >> Subject: [PATCH] i386: Update -mfunction-return= for return with pop

> >>

> >> When -mfunction-return= is used, simple_return_pop_internal should pop

> >> return address into ECX register, adjust stack by bytes to pop from stack

> >> and jump to the return thunk via ECX register.

> >>

> >> Tested on i686 and x86-64.

> >>

> >>       PR target/84530

> >>       * config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove

> >>       the bool argument.

> >>       (ix86_output_indirect_function_return): New prototype.

> >>       (ix86_split_simple_return_pop_internal): Likewise.

> >>       * config/i386/i386.c (indirect_return_via_cx): New.

> >>       (indirect_return_via_cx_bnd): Likewise.

> >>       (indirect_thunk_name): Handle return va CX_REG.

> >>       (output_indirect_thunk_function): Create alias for

> >>       __x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.

> >>       (ix86_output_indirect_jmp): Remove the bool argument.

> >>       (ix86_output_indirect_function_return): New function.

> >>       (ix86_split_simple_return_pop_internal): Likewise.

> >>       * config/i386/i386.md (*indirect_jump): Don't pass false

> >>       to ix86_output_indirect_jmp.

> >>       (*tablejump_1): Likewise.

> >>       (simple_return_pop_internal): Change it to define_insn_and_split.

> >>       Call ix86_split_simple_return_pop_internal to split it for

> >>       -mfunction-return=.

> >>       (simple_return_indirect_internal): Call

> >>       ix86_output_indirect_function_return instead of

> >>       ix86_output_indirect_jmp.

> >

> > It seems to make sense. Since LLVM has picked up our thunk names now,

> > I wonder if they have same bug or someohow already assigned a name

> > to this thunk. In the second case we probably ought to match it. Do

> > you know what LLVM uses?

> >

> 

> I don't think LLVM has implemented -mfunction-return=.   I disclosed

> my -mindirect-branch= approach to LLVM last year.  At the time, they

> wanted freedom in their implementation and didn't want to be compatible

> with mine.   Later they added GCC compatibility so that LLVM may be

> used to compile Linux kernel.  I believe it will be the same case if this

> option is used to compile Linux kernel.


Patch is OK then, thanks!

Honza
> 

> BTW, is my patch:

> 

> https://gcc.gnu.org/ml/gcc-patches/2018-01/msg02233.html

> 

> OK for trunk?  The review died at

> 

> https://gcc.gnu.org/ml/gcc-patches/2018-02/msg01355.html

> 

> Thanks.

> 

> -- 

> H.J.
H.J. Lu Feb. 26, 2018, 3:34 p.m. | #6
On Mon, Feb 26, 2018 at 7:06 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> On Mon, Feb 26, 2018 at 2:58 AM, Jan Hubicka <hubicka@ucw.cz> wrote:

>> >> On Mon, Feb 26, 2018 at 2:05 AM, Martin Liška <mliska@suse.cz> wrote:

>> >> > On 02/25/2018 02:37 AM, H.J. Lu wrote:

>> >> >>       PR target/r84530

>> >> >

>> >> > Just a nit I've noticed:

>> >> >

>> >> > s/r84530/84530

>> >>

>> >> Fixed.

>> >>

>> >> --

>> >> H.J.

>> >

>> >> From f08b8721ed038cefcee5a0bb1329b90a2d322269 Mon Sep 17 00:00:00 2001

>> >> From: "H.J. Lu" <hjl.tools@gmail.com>

>> >> Date: Sat, 24 Feb 2018 05:05:49 -0800

>> >> Subject: [PATCH] i386: Update -mfunction-return= for return with pop

>> >>

>> >> When -mfunction-return= is used, simple_return_pop_internal should pop

>> >> return address into ECX register, adjust stack by bytes to pop from stack

>> >> and jump to the return thunk via ECX register.

>> >>

>> >> Tested on i686 and x86-64.

>> >>

>> >>       PR target/84530

>> >>       * config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove

>> >>       the bool argument.

>> >>       (ix86_output_indirect_function_return): New prototype.

>> >>       (ix86_split_simple_return_pop_internal): Likewise.

>> >>       * config/i386/i386.c (indirect_return_via_cx): New.

>> >>       (indirect_return_via_cx_bnd): Likewise.

>> >>       (indirect_thunk_name): Handle return va CX_REG.

>> >>       (output_indirect_thunk_function): Create alias for

>> >>       __x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.

>> >>       (ix86_output_indirect_jmp): Remove the bool argument.

>> >>       (ix86_output_indirect_function_return): New function.

>> >>       (ix86_split_simple_return_pop_internal): Likewise.

>> >>       * config/i386/i386.md (*indirect_jump): Don't pass false

>> >>       to ix86_output_indirect_jmp.

>> >>       (*tablejump_1): Likewise.

>> >>       (simple_return_pop_internal): Change it to define_insn_and_split.

>> >>       Call ix86_split_simple_return_pop_internal to split it for

>> >>       -mfunction-return=.

>> >>       (simple_return_indirect_internal): Call

>> >>       ix86_output_indirect_function_return instead of

>> >>       ix86_output_indirect_jmp.

>> >

>> > It seems to make sense. Since LLVM has picked up our thunk names now,

>> > I wonder if they have same bug or someohow already assigned a name

>> > to this thunk. In the second case we probably ought to match it. Do

>> > you know what LLVM uses?

>> >

>>

>> I don't think LLVM has implemented -mfunction-return=.   I disclosed

>> my -mindirect-branch= approach to LLVM last year.  At the time, they

>> wanted freedom in their implementation and didn't want to be compatible

>> with mine.   Later they added GCC compatibility so that LLVM may be

>> used to compile Linux kernel.  I believe it will be the same case if this

>> option is used to compile Linux kernel.

>

> Patch is OK then, thanks!

>


Checked into trunk.  OK for backport to GCC 7 branch after a few days?


-- 
H.J.
Jan Hubicka Feb. 26, 2018, 3:44 p.m. | #7
> On Mon, Feb 26, 2018 at 7:06 AM, Jan Hubicka <hubicka@ucw.cz> wrote:

> >> On Mon, Feb 26, 2018 at 2:58 AM, Jan Hubicka <hubicka@ucw.cz> wrote:

> >> >> On Mon, Feb 26, 2018 at 2:05 AM, Martin Liška <mliska@suse.cz> wrote:

> >> >> > On 02/25/2018 02:37 AM, H.J. Lu wrote:

> >> >> >>       PR target/r84530

> >> >> >

> >> >> > Just a nit I've noticed:

> >> >> >

> >> >> > s/r84530/84530

> >> >>

> >> >> Fixed.

> >> >>

> >> >> --

> >> >> H.J.

> >> >

> >> >> From f08b8721ed038cefcee5a0bb1329b90a2d322269 Mon Sep 17 00:00:00 2001

> >> >> From: "H.J. Lu" <hjl.tools@gmail.com>

> >> >> Date: Sat, 24 Feb 2018 05:05:49 -0800

> >> >> Subject: [PATCH] i386: Update -mfunction-return= for return with pop

> >> >>

> >> >> When -mfunction-return= is used, simple_return_pop_internal should pop

> >> >> return address into ECX register, adjust stack by bytes to pop from stack

> >> >> and jump to the return thunk via ECX register.

> >> >>

> >> >> Tested on i686 and x86-64.

> >> >>

> >> >>       PR target/84530

> >> >>       * config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove

> >> >>       the bool argument.

> >> >>       (ix86_output_indirect_function_return): New prototype.

> >> >>       (ix86_split_simple_return_pop_internal): Likewise.

> >> >>       * config/i386/i386.c (indirect_return_via_cx): New.

> >> >>       (indirect_return_via_cx_bnd): Likewise.

> >> >>       (indirect_thunk_name): Handle return va CX_REG.

> >> >>       (output_indirect_thunk_function): Create alias for

> >> >>       __x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.

> >> >>       (ix86_output_indirect_jmp): Remove the bool argument.

> >> >>       (ix86_output_indirect_function_return): New function.

> >> >>       (ix86_split_simple_return_pop_internal): Likewise.

> >> >>       * config/i386/i386.md (*indirect_jump): Don't pass false

> >> >>       to ix86_output_indirect_jmp.

> >> >>       (*tablejump_1): Likewise.

> >> >>       (simple_return_pop_internal): Change it to define_insn_and_split.

> >> >>       Call ix86_split_simple_return_pop_internal to split it for

> >> >>       -mfunction-return=.

> >> >>       (simple_return_indirect_internal): Call

> >> >>       ix86_output_indirect_function_return instead of

> >> >>       ix86_output_indirect_jmp.

> >> >

> >> > It seems to make sense. Since LLVM has picked up our thunk names now,

> >> > I wonder if they have same bug or someohow already assigned a name

> >> > to this thunk. In the second case we probably ought to match it. Do

> >> > you know what LLVM uses?

> >> >

> >>

> >> I don't think LLVM has implemented -mfunction-return=.   I disclosed

> >> my -mindirect-branch= approach to LLVM last year.  At the time, they

> >> wanted freedom in their implementation and didn't want to be compatible

> >> with mine.   Later they added GCC compatibility so that LLVM may be

> >> used to compile Linux kernel.  I believe it will be the same case if this

> >> option is used to compile Linux kernel.

> >

> > Patch is OK then, thanks!

> >

> 

> Checked into trunk.  OK for backport to GCC 7 branch after a few days?

OK,
thanks!
Honza
> 

> 

> -- 

> H.J.
H.J. Lu Feb. 26, 2018, 5:59 p.m. | #8
On Mon, Feb 26, 2018 at 7:44 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> On Mon, Feb 26, 2018 at 7:06 AM, Jan Hubicka <hubicka@ucw.cz> wrote:

>> >> On Mon, Feb 26, 2018 at 2:58 AM, Jan Hubicka <hubicka@ucw.cz> wrote:

>> >> >> On Mon, Feb 26, 2018 at 2:05 AM, Martin Liška <mliska@suse.cz> wrote:

>> >> >> > On 02/25/2018 02:37 AM, H.J. Lu wrote:

>> >> >> >>       PR target/r84530

>> >> >> >

>> >> >> > Just a nit I've noticed:

>> >> >> >

>> >> >> > s/r84530/84530

>> >> >>

>> >> >> Fixed.

>> >> >>

>> >> >> --

>> >> >> H.J.

>> >> >

>> >> >> From f08b8721ed038cefcee5a0bb1329b90a2d322269 Mon Sep 17 00:00:00 2001

>> >> >> From: "H.J. Lu" <hjl.tools@gmail.com>

>> >> >> Date: Sat, 24 Feb 2018 05:05:49 -0800

>> >> >> Subject: [PATCH] i386: Update -mfunction-return= for return with pop

>> >> >>

>> >> >> When -mfunction-return= is used, simple_return_pop_internal should pop

>> >> >> return address into ECX register, adjust stack by bytes to pop from stack

>> >> >> and jump to the return thunk via ECX register.

>> >> >>

>> >> >> Tested on i686 and x86-64.

>> >> >>

>> >> >>       PR target/84530

>> >> >>       * config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove

>> >> >>       the bool argument.

>> >> >>       (ix86_output_indirect_function_return): New prototype.

>> >> >>       (ix86_split_simple_return_pop_internal): Likewise.

>> >> >>       * config/i386/i386.c (indirect_return_via_cx): New.

>> >> >>       (indirect_return_via_cx_bnd): Likewise.

>> >> >>       (indirect_thunk_name): Handle return va CX_REG.

>> >> >>       (output_indirect_thunk_function): Create alias for

>> >> >>       __x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd.

>> >> >>       (ix86_output_indirect_jmp): Remove the bool argument.

>> >> >>       (ix86_output_indirect_function_return): New function.

>> >> >>       (ix86_split_simple_return_pop_internal): Likewise.

>> >> >>       * config/i386/i386.md (*indirect_jump): Don't pass false

>> >> >>       to ix86_output_indirect_jmp.

>> >> >>       (*tablejump_1): Likewise.

>> >> >>       (simple_return_pop_internal): Change it to define_insn_and_split.

>> >> >>       Call ix86_split_simple_return_pop_internal to split it for

>> >> >>       -mfunction-return=.

>> >> >>       (simple_return_indirect_internal): Call

>> >> >>       ix86_output_indirect_function_return instead of

>> >> >>       ix86_output_indirect_jmp.

>> >> >

>> >> > It seems to make sense. Since LLVM has picked up our thunk names now,

>> >> > I wonder if they have same bug or someohow already assigned a name

>> >> > to this thunk. In the second case we probably ought to match it. Do

>> >> > you know what LLVM uses?

>> >> >

>> >>

>> >> I don't think LLVM has implemented -mfunction-return=.   I disclosed

>> >> my -mindirect-branch= approach to LLVM last year.  At the time, they

>> >> wanted freedom in their implementation and didn't want to be compatible

>> >> with mine.   Later they added GCC compatibility so that LLVM may be

>> >> used to compile Linux kernel.  I believe it will be the same case if this

>> >> option is used to compile Linux kernel.

>> >

>> > Patch is OK then, thanks!

>> >

>>

>> Checked into trunk.  OK for backport to GCC 7 branch after a few days?

> OK,


I checked in this patch to update comments.  I will backport it together to
GCC 7 branch.

Index: config/i386/i386.c
===================================================================
--- config/i386/i386.c (revision 258004)
+++ config/i386/i386.c (working copy)
@@ -28999,8 +28999,7 @@ ix86_output_indirect_branch (rtx call_op
     ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
 }

-/* Output indirect jump.  CALL_OP is the jump target.  Jump is a
-   function return if RET_P is true.  */
+/* Output indirect jump.  CALL_OP is the jump target.  */

 const char *
 ix86_output_indirect_jmp (rtx call_op)


-- 
H.J.

Patch

diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index fb86f00b3a6..79b96c0f80b 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -305,8 +305,10 @@  extern enum attr_cpu ix86_schedule;
 #endif
 
 extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
-extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p);
+extern const char * ix86_output_indirect_jmp (rtx call_op);
 extern const char * ix86_output_function_return (bool long_p);
+extern const char * ix86_output_indirect_function_return (rtx ret_op);
+extern void ix86_split_simple_return_pop_internal (rtx);
 extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
 						machine_mode mode);
 extern int ix86_min_insn_size (rtx_insn *);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 640c5921863..98075ad42c7 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10812,6 +10812,12 @@  static int indirect_thunks_used;
    by call and return thunks functions with the BND prefix.  */
 static int indirect_thunks_bnd_used;
 
+/* True if return thunk function via CX is needed.  */
+static bool indirect_return_via_cx;
+/* True if return thunk function via CX with the BND prefix is
+   needed.  */
+static bool indirect_return_via_cx_bnd;
+
 #ifndef INDIRECT_LABEL
 # define INDIRECT_LABEL "LIND"
 #endif
@@ -10852,7 +10858,7 @@  indirect_thunk_name (char name[32], unsigned int regno,
 		     enum indirect_thunk_prefix need_prefix,
 		     bool ret_p)
 {
-  if (regno != INVALID_REGNUM && ret_p)
+  if (regno != INVALID_REGNUM && regno != CX_REG && ret_p)
     gcc_unreachable ();
 
   if (USE_HIDDEN_LINKONCE)
@@ -10872,6 +10878,8 @@  indirect_thunk_name (char name[32], unsigned int regno,
       else
 	prefix = "";
 
+      const char *ret = ret_p ? "return" : "indirect";
+
       if (regno != INVALID_REGNUM)
 	{
 	  const char *reg_prefix;
@@ -10879,14 +10887,11 @@  indirect_thunk_name (char name[32], unsigned int regno,
 	    reg_prefix = TARGET_64BIT ? "r" : "e";
 	  else
 	    reg_prefix = "";
-	  sprintf (name, "__x86_indirect_thunk%s_%s%s",
-		   prefix, reg_prefix, reg_names[regno]);
+	  sprintf (name, "__x86_%s_thunk%s_%s%s",
+		   ret, prefix, reg_prefix, reg_names[regno]);
 	}
       else
-	{
-	  const char *ret = ret_p ? "return" : "indirect";
-	  sprintf (name, "__x86_%s_thunk%s", ret, prefix);
-	}
+	sprintf (name, "__x86_%s_thunk%s", ret, prefix);
     }
   else
     {
@@ -11055,9 +11060,23 @@  output_indirect_thunk_function (enum indirect_thunk_prefix need_prefix,
 	ASM_OUTPUT_LABEL (asm_out_file, name);
       }
 
+  /* Create alias for __x86_return_thunk/__x86_return_thunk_bnd or
+     __x86_return_thunk_ecx/__x86_return_thunk_ecx_bnd.  */
+  bool need_alias;
   if (regno == INVALID_REGNUM)
+    need_alias = true;
+  else if (regno == CX_REG)
+    {
+      if (need_prefix == indirect_thunk_prefix_bnd)
+	need_alias = indirect_return_via_cx_bnd;
+      else
+	need_alias = indirect_return_via_cx;
+    }
+  else
+    need_alias = false;
+
+  if (need_alias)
     {
-      /* Create alias for __x86.return_thunk/__x86.return_thunk_bnd.  */
       char alias[32];
 
       indirect_thunk_name (alias, regno, need_prefix, true);
@@ -28979,18 +28998,18 @@  ix86_output_indirect_branch (rtx call_op, const char *xasm,
   else
     ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
 }
+
 /* Output indirect jump.  CALL_OP is the jump target.  Jump is a
    function return if RET_P is true.  */
 
 const char *
-ix86_output_indirect_jmp (rtx call_op, bool ret_p)
+ix86_output_indirect_jmp (rtx call_op)
 {
   if (cfun->machine->indirect_branch_type != indirect_branch_keep)
     {
-      /* We can't have red-zone if this isn't a function return since
-	 "call" in the indirect thunk pushes the return address onto
-	 stack, destroying red-zone.  */
-      if (!ret_p && ix86_red_zone_size != 0)
+      /* We can't have red-zone since "call" in the indirect thunk
+         pushes the return address onto stack, destroying red-zone.  */
+      if (ix86_red_zone_size != 0)
 	gcc_unreachable ();
 
       ix86_output_indirect_branch (call_op, "%0", true);
@@ -29042,6 +29061,87 @@  ix86_output_function_return (bool long_p)
   return "rep%; ret";
 }
 
+/* Output indirect function return.  RET_OP is the function return
+   target.  */
+
+const char *
+ix86_output_indirect_function_return (rtx ret_op)
+{
+  if (cfun->machine->function_return_type != indirect_branch_keep)
+    {
+      char thunk_name[32];
+      enum indirect_thunk_prefix need_prefix
+	= indirect_thunk_need_prefix (current_output_insn);
+      unsigned int regno = REGNO (ret_op);
+      gcc_assert (regno == CX_REG);
+
+      if (cfun->machine->function_return_type
+	  != indirect_branch_thunk_inline)
+	{
+	  bool need_thunk = (cfun->machine->function_return_type
+			     == indirect_branch_thunk);
+	  indirect_thunk_name (thunk_name, regno, need_prefix, true);
+	  if (need_prefix == indirect_thunk_prefix_bnd)
+	    {
+	      if (need_thunk)
+		{
+		  indirect_return_via_cx_bnd = true;
+		  indirect_thunks_bnd_used |= 1 << CX_REG;
+		}
+	      fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
+	    }
+	  else
+	    {
+	      if (need_thunk)
+		{
+		  indirect_return_via_cx = true;
+		  indirect_thunks_used |= 1 << CX_REG;
+		}
+	      fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
+	    }
+	}
+      else
+	output_indirect_thunk (need_prefix, regno);
+
+      return "";
+    }
+  else
+    return "%!jmp\t%A0";
+}
+
+/* Split simple return with popping POPC bytes from stack to indirect
+   branch with stack adjustment .  */
+
+void
+ix86_split_simple_return_pop_internal (rtx popc)
+{
+  struct machine_function *m = cfun->machine;
+  rtx ecx = gen_rtx_REG (SImode, CX_REG);
+  rtx_insn *insn;
+
+  /* There is no "pascal" calling convention in any 64bit ABI.  */
+  gcc_assert (!TARGET_64BIT);
+
+  insn = emit_insn (gen_pop (ecx));
+  m->fs.cfa_offset -= UNITS_PER_WORD;
+  m->fs.sp_offset -= UNITS_PER_WORD;
+
+  rtx x = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD);
+  x = gen_rtx_SET (stack_pointer_rtx, x);
+  add_reg_note (insn, REG_CFA_ADJUST_CFA, x);
+  add_reg_note (insn, REG_CFA_REGISTER, gen_rtx_SET (ecx, pc_rtx));
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, popc);
+  x = gen_rtx_SET (stack_pointer_rtx, x);
+  insn = emit_insn (x);
+  add_reg_note (insn, REG_CFA_ADJUST_CFA, x);
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  /* Now return address is in ECX.  */
+  emit_jump_insn (gen_simple_return_indirect_internal (ecx));
+}
+
 /* Output the assembly for a call instruction.  */
 
 const char *
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 3998053a506..f271278ed58 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -12319,7 +12319,7 @@ 
 (define_insn "*indirect_jump"
   [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))]
   ""
-  "* return ix86_output_indirect_jmp (operands[0], false);"
+  "* return ix86_output_indirect_jmp (operands[0]);"
   [(set (attr "type")
      (if_then_else (match_test "(cfun->machine->indirect_branch_type
 				 != indirect_branch_keep)")
@@ -12374,7 +12374,7 @@ 
   [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))
    (use (label_ref (match_operand 1)))]
   ""
-  "* return ix86_output_indirect_jmp (operands[0], false);"
+  "* return ix86_output_indirect_jmp (operands[0]);"
   [(set (attr "type")
      (if_then_else (match_test "(cfun->machine->indirect_branch_type
 				 != indirect_branch_keep)")
@@ -13097,11 +13097,14 @@ 
    (set_attr "prefix_rep" "1")
    (set_attr "modrm" "0")])
 
-(define_insn "simple_return_pop_internal"
+(define_insn_and_split "simple_return_pop_internal"
   [(simple_return)
    (use (match_operand:SI 0 "const_int_operand"))]
   "reload_completed"
   "%!ret\t%0"
+  "&& cfun->machine->function_return_type != indirect_branch_keep"
+  [(const_int 0)]
+  "ix86_split_simple_return_pop_internal (operands[0]); DONE;"
   [(set_attr "length" "3")
    (set_attr "atom_unit" "jeu")
    (set_attr "length_immediate" "2")
@@ -13112,7 +13115,7 @@ 
   [(simple_return)
    (use (match_operand 0 "register_operand" "r"))]
   "reload_completed"
-  "* return ix86_output_indirect_jmp (operands[0], true);"
+  "* return ix86_output_indirect_function_return (operands[0]);"
   [(set (attr "type")
      (if_then_else (match_test "(cfun->machine->indirect_branch_type
 				 != indirect_branch_keep)")
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-22.c b/gcc/testsuite/gcc.target/i386/ret-thunk-22.c
new file mode 100644
index 00000000000..89e086de97b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-22.c
@@ -0,0 +1,15 @@ 
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_ecx" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-23.c b/gcc/testsuite/gcc.target/i386/ret-thunk-23.c
new file mode 100644
index 00000000000..43f0ccaa854
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-23.c
@@ -0,0 +1,15 @@ 
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk-extern" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_ecx" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler-not {\tpause} } } */
+/* { dg-final { scan-assembler-not {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-24.c b/gcc/testsuite/gcc.target/i386/ret-thunk-24.c
new file mode 100644
index 00000000000..8729e35147e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-24.c
@@ -0,0 +1,15 @@ 
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk-inline" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk_ecx" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-25.c b/gcc/testsuite/gcc.target/i386/ret-thunk-25.c
new file mode 100644
index 00000000000..f73553c9a9f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-25.c
@@ -0,0 +1,15 @@ 
+/* PR target/r84530 */
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -mfunction-return=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */
+
+struct s { _Complex unsigned short x; };
+struct s gs = { 100 + 200i };
+struct s __attribute__((noinline)) foo (void) { return gs; }
+
+/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */
+/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_bnd_ecx" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */
+/* { dg-final { scan-assembler {\tpause} } } */
+/* { dg-final { scan-assembler {\tlfence} } } */
diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-26.c b/gcc/testsuite/gcc.target/i386/ret-thunk-26.c
new file mode 100644
index 00000000000..9144e988735
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ret-thunk-26.c
@@ -0,0 +1,40 @@ 
+/* PR target/r84530 */
+/* { dg-do run } */
+/* { dg-options "-Os -mfunction-return=thunk" } */
+
+struct S { int i; };
+__attribute__((const, noinline, noclone))
+struct S foo (int x)
+{
+  struct S s;
+  s.i = x;
+  return s;
+}
+
+int a[2048], b[2048], c[2048], d[2048];
+struct S e[2048];
+
+__attribute__((noinline, noclone)) void
+bar (void)
+{
+  int i;
+  for (i = 0; i < 1024; i++)
+    {
+      e[i] = foo (i);
+      a[i+2] = a[i] + a[i+1];
+      b[10] = b[10] + i;
+      c[i] = c[2047 - i];
+      d[i] = d[i + 1];
+    }
+}
+
+int
+main ()
+{
+  int i;
+  bar ();
+  for (i = 0; i < 1024; i++)
+    if (e[i].i != i)
+      __builtin_abort ();
+  return 0;
+}