The direct cause of this PR is the fact that tls_gdld_nomark didn't
handle indirect calls.  Adding the missing support revealed that most
indirect calls were being optimised back to direct calls anyway, due
to tls_gdld_nomark not checking any of the parallel elements except
the first (plus the extra element that distinguishes this call from
normal calls).  Just checking the number of elements is enough to
separate the indirect calls from direct for ABI_ELFv2 and ABI_AIX,
while checking for the LONG_CALL bit in the cookie works for ABI_V4.
Direct calls being substituted for indirect calls is not the only
unwanted substitution.  See the tls_nomark_call comment.  I also saw a
_GLOBAL_OFFSET_TABLE_ symbol_ref being substituted for the GOT reg,
hence the unspec_tls change.

Bootstrap and regression testing on powerpc64le-linux and
powerpc64-linux in progress.  Note that the patch requires
https://gcc.gnu.org/ml/gcc-patches/2019-01/msg00252.html or the
earlier version for the attribute support.

        PR 88614
        * config/rs6000/predicates.md (unspec_tls): Ensure GOT reg
        stays a reg.
        (tls_nomark_call): New.
        * config/rs6000/rs6000.c (rs6000_call_sysv): Generate sysv4 secure
        plt call pattern here..
        * config/rs6000/rs6000.md (call_nonlocal_sysv): ..rather than here,
        delete split..
        (call_value_nonlocal_sysv): ..or here, delete split.
        (tls_gdld_nomark): Use tls_nomark_call predicate.  Set up operands
        for indirect calls and correct length attr.

diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index 21791c51f2f..246452879a8 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -988,7 +988,12 @@ (define_predicate "rs6000_tls_symbol_ref"
 (define_predicate "unspec_tls"
   (match_code "unspec")
 {
-  return XINT (op, 1) == UNSPEC_TLSGD || XINT (op, 1) == UNSPEC_TLSLD;
+  if (XINT (op, 1) == UNSPEC_TLSGD)
+    return REG_P (XVECEXP (op, 0, 1));
+  else if (XINT (op, 1) == UNSPEC_TLSLD)
+    return REG_P (XVECEXP (op, 0, 0));
+  else
+    return 0;
 })
 
 ;; Return 1 if the operand, used inside a MEM, is a valid first argument
@@ -1018,6 +1023,86 @@ (define_predicate "indirect_call_operand"
   return false;
 })
 
+;; Verify that elements of the tls_gdld_nomark call insn parallel past the
+;; second element (added to distinguish this call from normal calls) match
+;; the normal contours of a call insn.  This is necessary to prevent
+;; substitutions we don't want, for example, an indirect call being
+;; optimised to a direct call, or (set (reg:r2) (unspec [] UNSPEC_TOCSLOT))
+;; being cleverly optimised to (set (reg:r2) (reg:r2)) because gcc
+;; "knows" that r2 hasn't changed from a previous call.
+(define_predicate "tls_nomark_call"
+  (match_code "parallel")
+{
+  int n = XVECLEN (op, 0);
+  rtvec v = XVEC (op, 0);
+  rtx set = RTVEC_ELT (v, 0);
+  if (GET_CODE (set) != SET)
+    return 0;
+  rtx call = XEXP (set, 1);
+  if (GET_CODE (call) != CALL)
+    return 0;
+  rtx mem = XEXP (call, 0);
+  if (GET_CODE (mem) != MEM)
+    return 0;
+  rtx addr = XEXP (mem, 0);
+  if (GET_CODE (addr) == SYMBOL_REF)
+    {
+      if (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_AIX)
+       return (n == 3 && GET_CODE (RTVEC_ELT (v, 2)) == CLOBBER
+               && REG_P (XEXP (RTVEC_ELT (v, 2), 0))
+               && REGNO (XEXP (RTVEC_ELT (v, 2), 0)) == LR_REGNO);
+      else if (DEFAULT_ABI == ABI_V4)
+       return (n >= 4 && n <= 5 && GET_CODE (RTVEC_ELT (v, 2)) == USE
+               && CONST_INT_P (XEXP (RTVEC_ELT (v, 2), 0))
+               && (INTVAL (XEXP (RTVEC_ELT (v, 2), 0)) & CALL_LONG) == 0
+               && (n == 4
+                   || (GET_CODE (RTVEC_ELT (v, 3)) == USE
+                       && REG_P (XEXP (RTVEC_ELT (v, 3), 0))))
+               && GET_CODE (RTVEC_ELT (v, n - 1)) == CLOBBER
+               && REG_P (XEXP (RTVEC_ELT (v, n - 1), 0))
+               && REGNO (XEXP (RTVEC_ELT (v, n - 1), 0)) == LR_REGNO);
+      else
+       gcc_unreachable ();
+    }
+  else if (indirect_call_operand (addr, mode))
+    {
+      if (DEFAULT_ABI == ABI_ELFv2)
+       return (n == 4 && GET_CODE (RTVEC_ELT (v, 2)) == SET
+               && REG_P (XEXP (RTVEC_ELT (v, 2), 0))
+               && REGNO (XEXP (RTVEC_ELT (v, 2), 0)) == TOC_REGNUM
+               && GET_CODE (XEXP (RTVEC_ELT (v, 2), 1)) == UNSPEC
+               && XINT (XEXP (RTVEC_ELT (v, 2), 1), 1) == UNSPEC_TOCSLOT
+               && XVECLEN (XEXP (RTVEC_ELT (v, 2), 1), 0) == 1
+               && CONST_INT_P (XVECEXP (XEXP (RTVEC_ELT (v, 2), 1), 0, 0))
+               && GET_CODE (RTVEC_ELT (v, 3)) == CLOBBER
+               && REG_P (XEXP (RTVEC_ELT (v, 3), 0))
+               && REGNO (XEXP (RTVEC_ELT (v, 3), 0)) == LR_REGNO);
+      else if (DEFAULT_ABI == ABI_AIX)
+       return (n == 5 && GET_CODE (RTVEC_ELT (v, 2)) == USE
+               && GET_CODE (XEXP (RTVEC_ELT (v, 2), 0)) == MEM
+               && GET_CODE (RTVEC_ELT (v, 3)) == SET
+               && REG_P (XEXP (RTVEC_ELT (v, 3), 0))
+               && REGNO (XEXP (RTVEC_ELT (v, 3), 0)) == TOC_REGNUM
+               && GET_CODE (XEXP (RTVEC_ELT (v, 3), 1)) == UNSPEC
+               && XINT (XEXP (RTVEC_ELT (v, 3), 1), 1) == UNSPEC_TOCSLOT
+               && XVECLEN (XEXP (RTVEC_ELT (v, 3), 1), 0) == 1
+               && CONST_INT_P (XVECEXP (XEXP (RTVEC_ELT (v, 3), 1), 0, 0))
+               && GET_CODE (RTVEC_ELT (v, 4)) == CLOBBER
+               && REG_P (XEXP (RTVEC_ELT (v, 4), 0))
+               && REGNO (XEXP (RTVEC_ELT (v, 4), 0)) == LR_REGNO);
+      else if (DEFAULT_ABI == ABI_V4)
+       return (n == 4 && GET_CODE (RTVEC_ELT (v, 2)) == USE
+               && CONST_INT_P (XEXP (RTVEC_ELT (v, 2), 0))
+               && GET_CODE (RTVEC_ELT (v, 3)) == CLOBBER
+               && REG_P (XEXP (RTVEC_ELT (v, 3), 0))
+               && REGNO (XEXP (RTVEC_ELT (v, 3), 0)) == LR_REGNO);
+      else
+       gcc_unreachable ();
+    }
+  else
+    return 0;
+})
+
 ;; Return 1 if the operand is a SYMBOL_REF for a function known to be in
 ;; this file.
 (define_predicate "current_file_function_operand"
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index a25755418ea..4e3c5fc135f 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -37918,9 +37918,10 @@ rs6000_call_sysv (rtx value, rtx func_desc, rtx 
tlsarg, rtx cookie)
 {
   rtx func = func_desc;
   rtx func_addr;
-  rtx call[3];
+  rtx call[4];
   rtx insn;
   rtx abi_reg = NULL_RTX;
+  int n;
 
   if (global_tlsarg)
     tlsarg = global_tlsarg;
@@ -37968,9 +37969,16 @@ rs6000_call_sysv (rtx value, rtx func_desc, rtx 
tlsarg, rtx cookie)
     call[0] = gen_rtx_SET (value, call[0]);
 
   call[1] = gen_rtx_USE (VOIDmode, cookie);
-  call[2] = gen_hard_reg_clobber (Pmode, LR_REGNO);
+  n = 2;
+  if (TARGET_SECURE_PLT
+      && flag_pic
+      && GET_CODE (func_addr) == SYMBOL_REF
+      && !SYMBOL_REF_LOCAL_P (func_addr))
+    call[n++] = gen_rtx_USE (VOIDmode, pic_offset_table_rtx);
 
-  insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
+  call[n++] = gen_hard_reg_clobber (Pmode, LR_REGNO);
+
+  insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (n, call));
   insn = emit_call_insn (insn);
   if (abi_reg)
     use_reg (&CALL_INSN_FUNCTION_USAGE (insn), abi_reg);
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 56364e0e43b..0d5ef31f9f2 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -9443,7 +9443,7 @@ (define_peephole2
 ;; TLS support.
 
 (define_insn "*tls_gdld_nomark<bits>"
-  [(match_parallel 3 ""
+  [(match_parallel 3 "tls_nomark_call"
     [(set (match_operand:P 0 "gpc_reg_operand" "=b")
          (call (mem:SI (match_operand:P 1))
                (match_operand:P 2 "unspec_tls")))
@@ -9470,15 +9470,43 @@ (define_insn "*tls_gdld_nomark<bits>"
       else
        output_asm_insn ("addi %0,%1,%&@got@tlsld", op);
     }
-  return rs6000_call_template (operands, 1);
+  if (GET_CODE (operands[1]) == SYMBOL_REF)
+    return rs6000_call_template (operands, 1);
+
+  /* Indirect calls need to recog a few more operands.  See the various
+     call_value_indirect patterns, and note that edit_tls_call_insn
+     added an extra element to the parallel.  */
+  rtx par = operands[3];
+  rtvec v = XVEC (par, 0);
+  if (DEFAULT_ABI == ABI_ELFv2)
+    operands[3] = XVECEXP (XEXP (RTVEC_ELT (v, 2), 1), 0, 0);
+  else if (DEFAULT_ABI == ABI_AIX)
+    {
+      operands[3] = XEXP (RTVEC_ELT (v, 2), 0);
+      operands[4] = XVECEXP (XEXP (RTVEC_ELT (v, 3), 1), 0, 0);
+    }
+  else if (DEFAULT_ABI == ABI_V4)
+    operands[3] = XEXP (RTVEC_ELT (v, 2), 0);
+  else
+    gcc_unreachable ();
+  return rs6000_indirect_call_template (operands, 1);
 }
   [(set_attr "type" "two")
    (set (attr "length")
-     (cond [(match_test "TARGET_CMODEL != CMODEL_SMALL")
-               (const_int 16)
-           (match_test "DEFAULT_ABI != ABI_V4")
-               (const_int 12)]
-       (const_int 8)))])
+     (plus (if_then_else (match_test "TARGET_CMODEL != CMODEL_SMALL")
+                        (const_int 8)
+                        (const_int 4))
+          (plus (if_then_else (match_test "GET_CODE (operands[1]) != 
SYMBOL_REF")
+                              (plus (if_then_else (match_test 
"!rs6000_speculate_indirect_jumps")
+                                                  (const_int 4)
+                                                  (const_int 0))
+                                    (if_then_else (match_test "DEFAULT_ABI == 
ABI_AIX")
+                                                  (const_int 4)
+                                                  (const_int 0)))
+                              (const_int 0))
+                (if_then_else (match_test "DEFAULT_ABI != ABI_V4")
+                              (const_int 8)
+                              (const_int 4)))))])
 
 (define_insn_and_split "*tls_gd<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
@@ -10440,7 +10468,7 @@ (define_insn "*call_indirect_nonlocal_sysv<mode>"
                  (const_string "8")]
              (const_string "4")))])
 
-(define_insn_and_split "*call_nonlocal_sysv<mode>"
+(define_insn "*call_nonlocal_sysv<mode>"
   [(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s,s"))
         (match_operand 1))
    (use (match_operand:SI 2 "immediate_operand" "O,n"))
@@ -10456,17 +10484,6 @@ (define_insn_and_split "*call_nonlocal_sysv<mode>"
     output_asm_insn ("creqv 6,6,6", operands);
 
   return rs6000_call_template (operands, 0);
-}
-  "DEFAULT_ABI == ABI_V4
-   && TARGET_SECURE_PLT && flag_pic && !SYMBOL_REF_LOCAL_P (operands[0])
-   && (INTVAL (operands[2]) & CALL_LONG) == 0"
-  [(parallel [(call (mem:SI (match_dup 0))
-                   (match_dup 1))
-             (use (match_dup 2))
-             (use (match_dup 3))
-             (clobber (reg:SI LR_REGNO))])]
-{
-  operands[3] = pic_offset_table_rtx;
 }
   [(set_attr "type" "branch,branch")
    (set_attr "length" "4,8")])
@@ -10521,7 +10538,7 @@ (define_insn "*call_value_indirect_nonlocal_sysv<mode>"
                  (const_string "8")]
              (const_string "4")))])
 
-(define_insn_and_split "*call_value_nonlocal_sysv<mode>"
+(define_insn "*call_value_nonlocal_sysv<mode>"
   [(set (match_operand 0 "" "")
        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s,s"))
              (match_operand 2)))
@@ -10538,18 +10555,6 @@ (define_insn_and_split 
"*call_value_nonlocal_sysv<mode>"
     output_asm_insn ("creqv 6,6,6", operands);
 
   return rs6000_call_template (operands, 1);
-}
-  "DEFAULT_ABI == ABI_V4
-   && TARGET_SECURE_PLT && flag_pic && !SYMBOL_REF_LOCAL_P (operands[1])
-   && (INTVAL (operands[3]) & CALL_LONG) == 0"
-  [(parallel [(set (match_dup 0)
-                  (call (mem:SI (match_dup 1))
-                        (match_dup 2)))
-             (use (match_dup 3))
-             (use (match_dup 4))
-             (clobber (reg:SI LR_REGNO))])]
-{
-  operands[4] = pic_offset_table_rtx;
 }
   [(set_attr "type" "branch,branch")
    (set_attr "length" "4,8")])

-- 
Alan Modra
Australia Development Lab, IBM

Reply via email to