From 771535dec733e4b85924f00a3a94c29683d614e5 Mon Sep 17 00:00:00 2001
From: hjl <hjl@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Mon, 26 Feb 2018 15:29:30 +0000
Subject: [PATCH 11/13] 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.

Revision 257992 removed the bool argument from ix86_output_indirect_jmp.
Update comments to reflect it.

Tested on i686 and x86-64.

gcc/

	Backport from mainline
	2018-02-26  H.J. Lu  <hongjiu.lu@intel.com>

	* config/i386/i386.c (ix86_output_indirect_jmp): Update comments.

	2018-02-26  H.J. Lu  <hongjiu.lu@intel.com>

	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/

	Backport from mainline
	2018-02-26  H.J. Lu  <hongjiu.lu@intel.com>

	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                       | 127 +++++++++++++++++++++++----
 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, 222 insertions(+), 20 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 620d70ef9f6..c7a0ccb58d3 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -311,8 +311,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,
 						enum machine_mode mode);
 
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 66502ee6da6..21c3c18bd3c 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -11080,6 +11080,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
@@ -11090,12 +11096,13 @@ static void
 indirect_thunk_name (char name[32], unsigned int regno,
 		     bool need_bnd_p, bool ret_p)
 {
-  if (regno != INVALID_REGNUM && ret_p)
+  if (regno != INVALID_REGNUM && regno != CX_REG && ret_p)
     gcc_unreachable ();
 
   if (USE_HIDDEN_LINKONCE)
     {
       const char *bnd = need_bnd_p ? "_bnd" : "";
+      const char *ret = ret_p ? "return" : "indirect";
       if (regno != INVALID_REGNUM)
 	{
 	  const char *reg_prefix;
@@ -11103,14 +11110,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",
-		   bnd, reg_prefix, reg_names[regno]);
+	  sprintf (name, "__x86_%s_thunk%s_%s%s",
+		   ret, bnd, reg_prefix, reg_names[regno]);
 	}
       else
-	{
-	  const char *ret = ret_p ? "return" : "indirect";
-	  sprintf (name, "__x86_%s_thunk%s", ret, bnd);
-	}
+	sprintf (name, "__x86_%s_thunk%s", ret, bnd);
     }
   else
     {
@@ -11274,9 +11278,23 @@ output_indirect_thunk_function (bool need_bnd_p, unsigned int regno)
 	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_bnd_p)
+	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_bnd_p, true);
@@ -28019,18 +28037,17 @@ 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.  */
+
+/* Output indirect jump.  CALL_OP is the jump target.  */
 
 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);
@@ -28081,6 +28098,86 @@ 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];
+      bool need_bnd_p = ix86_bnd_prefixed_insn_p (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_bnd_p, true);
+	  if (need_bnd_p)
+	    {
+	      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_bnd_p, 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 05a88fff356..857466a6361 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -11813,7 +11813,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)")
@@ -11868,7 +11868,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)")
@@ -12520,11 +12520,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")
@@ -12535,7 +12538,7 @@
   [(simple_return)
    (use (match_operand:SI 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;
+}
-- 
2.16.3

