]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
i386: Update -mfunction-return= for return with pop
authorH.J. Lu <hongjiu.lu@intel.com>
Mon, 16 Apr 2018 19:06:32 +0000 (19:06 +0000)
committerH.J. Lu <hjl@gcc.gnu.org>
Mon, 16 Apr 2018 19:06:32 +0000 (12:06 -0700)
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.

From-SVN: r259420

gcc/ChangeLog
gcc/config/i386/i386-protos.h
gcc/config/i386/i386.c
gcc/config/i386/i386.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/i386/ret-thunk-22.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/ret-thunk-23.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/ret-thunk-24.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/ret-thunk-25.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/ret-thunk-26.c [new file with mode: 0644]

index 61e22395998326f07ddd0feb4cc1e48b7360afb7..862ad5b915ddf0332257a6b8919540405ddabde9 100644 (file)
@@ -1,3 +1,35 @@
+2018-04-16  H.J. Lu  <hongjiu.lu@intel.com>
+
+       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.
+
 2018-04-16  H.J. Lu  <hongjiu.lu@intel.com>
 
        Backport from mainline
index 620d70ef9f6ba202c115880a8e5d388fa04f6d77..c7a0ccb58d3092d9e917b423557f419e53cfdf3c 100644 (file)
@@ -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);
 
index 66502ee6da6bcf1f0d94502ff5ebb0a39f993331..21c3c18bd3c3483d3f011e0e69ce41d1e042e7fe 100644 (file)
@@ -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 *
index 8dcb390b81ae2d758557fd541154ba714e0efde7..bddfa5925c12f69f3c1f69cfddf24df03c6e170c 100644 (file)
 (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)")
   [(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)")
    (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")
   [(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)")
index 300ce7d49a1cc356f274c304e8d87b18b4f06d58..2b8fde20aa5e93c15680ddb68970c759e8768467 100644 (file)
@@ -1,3 +1,15 @@
+2018-04-16  H.J. Lu  <hongjiu.lu@intel.com>
+
+       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.
+
 2018-04-16  H.J. Lu  <hongjiu.lu@intel.com>
 
        Backport from mainline
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 (file)
index 0000000..89e086d
--- /dev/null
@@ -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 (file)
index 0000000..43f0cca
--- /dev/null
@@ -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 (file)
index 0000000..8729e35
--- /dev/null
@@ -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 (file)
index 0000000..f73553c
--- /dev/null
@@ -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 (file)
index 0000000..9144e98
--- /dev/null
@@ -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;
+}