alpha_legitimize_address emits movdi_er_tlsgd and movdi_er_tlsldm together
with their paired call_value_osf_tlsgd/tlsldm at expand time, and both halves
already carry the sequence number that ties the pair together.  Those patterns
are marked with the cannot_copy attribute, but alpha_cannot_copy_insn_p
returned false whenever !reload_completed, so the hook had no effect on any
pass running before register allocation.

Unrolling a loop whose body contains such a pair therefore copies the sequence
number along with it.  With

  extern __thread int tv;
  extern int cond (int);
  int f (int n)
  {
    int s = 0;
    for (int i = 0; i < n; i++)
      if (cond (i))
        s += tv;
    return s;
  }

compiled with -O2 -funroll-loops -fno-move-loop-invariants -fPIC
-ftls-model=global-dynamic, the unroller produces seven copies of !tlsgd!1 and
the assembler rejects the result:

  Error: duplicate !tlsgd!1
  Error: too many lituse insns for !lituse_tlsgd!1

Drop the reload_completed test.  The gpdisp pairs are only created after
reload, so this does not change their handling; it only lets the hook protect
the TLS pairs that already exist before register allocation.  This is
independent of the register allocator: the failure reproduces identically with
both reload and LRA.

gcc/
        * config/alpha/alpha.cc (alpha_cannot_copy_insn_p): Do not return
        false before reload_completed.  Update comment.

gcc/testsuite/
        * gcc.target/alpha/tlsgd-dup-1.c: New test.
---
 gcc/config/alpha/alpha.cc                    | 17 +++++++++++----
 gcc/testsuite/gcc.target/alpha/tlsgd-dup-1.c | 22 ++++++++++++++++++++
 2 files changed, 35 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/alpha/tlsgd-dup-1.c

diff --git ./gcc/config/alpha/alpha.cc ./gcc/config/alpha/alpha.cc
index 9b8433c5bb0..90e4c21dd11 100644
--- ./gcc/config/alpha/alpha.cc
+++ ./gcc/config/alpha/alpha.cc
@@ -1242,8 +1242,8 @@ split_small_symbolic_operand (rtx x)
 }
 
 /* Indicate that INSN cannot be duplicated.  This is true for any insn
-   that we've marked with gpdisp relocs, since those have to stay in
-   1-1 correspondence with one another.
+   that we've marked with a relocation sequence number, since those have
+   to stay in 1-1 correspondence with one another.
 
    Technically we could copy them if we could set up a mapping from one
    sequence number to another, across the set of insns to be duplicated.
@@ -1253,12 +1253,21 @@ split_small_symbolic_operand (rtx x)
    Also cannot allow jsr insns to be duplicated.  If they throw exceptions,
    then they'll be in a different block from their ldgp.  Which could lead
    the bb reorder code to think that it would be ok to copy just the block
-   containing the call and branch to the block containing the ldgp.  */
+   containing the call and branch to the block containing the ldgp.
+
+   Note this must not be gated on reload_completed.  While the gpdisp pairs
+   are only created after reload, alpha_legitimize_address emits
+   movdi_er_tlsgd and movdi_er_tlsldm together with their paired
+   call_value_osf_tlsgd/tlsldm at expand time, and those already carry the
+   sequence number that ties each pair together.  Returning false before
+   reload lets the pre-RA duplicators reach them: unrolling a loop whose
+   body holds such a pair copies the sequence number along with it, and the
+   assembler then rejects the result with "duplicate !tlsgd!N".  */
 
 static bool
 alpha_cannot_copy_insn_p (rtx_insn *insn)
 {
-  if (!reload_completed || !TARGET_EXPLICIT_RELOCS)
+  if (!TARGET_EXPLICIT_RELOCS)
     return false;
   if (recog_memoized (insn) >= 0)
     return get_attr_cannot_copy (insn);
diff --git ./gcc/testsuite/gcc.target/alpha/tlsgd-dup-1.c 
./gcc/testsuite/gcc.target/alpha/tlsgd-dup-1.c
new file mode 100644
index 00000000000..33ffc4d5dea
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/tlsgd-dup-1.c
@@ -0,0 +1,22 @@
+/* Verify that a TLS sequence insn and its paired call are not duplicated by
+   the pre-RA passes.  Each movdi_er_tlsgd carries a sequence number that must
+   stay in 1-1 correspondence with the !lituse_tlsgd of its call; duplicating
+   the loop body used to copy both halves and make the assembler reject the
+   result with "duplicate !tlsgd!1".  */
+/* { dg-do assemble } */
+/* { dg-require-effective-target tls_native } */
+/* { dg-require-effective-target fpic } */
+/* { dg-options "-O2 -funroll-loops -fno-move-loop-invariants -fPIC 
-ftls-model=global-dynamic" } */
+
+extern __thread int tv;
+extern int cond (int);
+
+int
+f (int n)
+{
+  int s = 0;
+  for (int i = 0; i < n; i++)
+    if (cond (i))
+      s += tv;
+  return s;
+}
-- 
2.54.0

Reply via email to