re PR target/21412 (ICE loading TLS address)
authorRichard Henderson <rth@redhat.com>
Wed, 11 May 2005 21:34:19 +0000 (14:34 -0700)
committerRichard Henderson <rth@gcc.gnu.org>
Wed, 11 May 2005 21:34:19 +0000 (14:34 -0700)
        PR target/21412
        * config/ia64/ia64.c (TARGET_CANNOT_FORCE_CONST_MEM): New.
        (ia64_cannot_force_const_mem): New.
        (tls_symbolic_operand_type): New.
        (ia64_legitimate_constant_p): New.
        (ia64_expand_load_address): Return true on success.  Improve
        checks for when we should not split.
        (ia64_expand_tls_address): New addend operand.  Distribute it
        as appropriate to the tls_kind.  Delay referencing gp.
        (ia64_expand_move): Split symbolic addend as necessary.  Handle
        tls symbols with addends.
        * config/ia64/ia64-protos.h: Update.
        * config/ia64/ia64.h (CALL_REALLY_USED_REGISTERS): False for r0,
        p0, f0, f1, and r13.
        (LEGITIMATE_CONSTANT_P): Move to ia64_legitimate_constant_p.
        * config/ia64/ia64.md (UNSPEC_DTPMOD): New.
        (symbolic_operand splitter): Pass everything through
        ia64_expand_load_address and FAIL or DONE as appropriate.
        (load_fptr): Only accept after reload.
        (load_fptr_internal1, gprel64_offset, load_gprel64, load_symptr_high,
        load_symptr_low, load_ltoff_dtpmod,
        (load_dtpmod): New.
        (load_dtprel): Only accept tls symbols.
        (load_dtprel64, load_dtprel22): Likewise.
        (load_tprel, load_tprel64, load_tprel22): Likewise.
        (load_dtprel_gd, load_ltoff_dtprel, load_tprel_ie): New.
        (add_dtprel): Only accept tls symbols.  Canonicalize PLUS.
        (add_dtprel14, add_dtprel22): Likewise.
        (add_tprel, add_tprel14, add_tprel22): Likewise.
        * config/ia64/predicates.md (small_addr_symbolic_operand): New.
        (any_offset_symbol_operand, aligned_offset_symbol_operand): New.
        (got_symbolic_operand): Check CONST offsets.
        (tls_symbolic_operand, ld_tls_symbolic_operand): New.
        (ie_tls_symbolic_operand, le_tls_symbolic_operand): New.
        (move_operand): Don't handle tls here.  Check CONST offsets.

From-SVN: r99596

gcc/ChangeLog
gcc/config/ia64/ia64-protos.h
gcc/config/ia64/ia64.c
gcc/config/ia64/ia64.h
gcc/config/ia64/ia64.md
gcc/config/ia64/predicates.md

index afbf5af7c1e5ccdf5415302f52dc9b2dec05cf1b..f92014e80770cc9a728b645dc51d718960438300 100644 (file)
@@ -1,3 +1,41 @@
+2005-05-11  Richard Henderson  <rth@redhat.com>
+
+       PR target/21412
+       * config/ia64/ia64.c (TARGET_CANNOT_FORCE_CONST_MEM): New.
+       (ia64_cannot_force_const_mem): New.
+       (tls_symbolic_operand_type): New.
+       (ia64_legitimate_constant_p): New.
+       (ia64_expand_load_address): Return true on success.  Improve 
+       checks for when we should not split.
+       (ia64_expand_tls_address): New addend operand.  Distribute it
+       as appropriate to the tls_kind.  Delay referencing gp.
+       (ia64_expand_move): Split symbolic addend as necessary.  Handle
+       tls symbols with addends.
+       * config/ia64/ia64-protos.h: Update.
+       * config/ia64/ia64.h (CALL_REALLY_USED_REGISTERS): False for r0,
+       p0, f0, f1, and r13.
+       (LEGITIMATE_CONSTANT_P): Move to ia64_legitimate_constant_p.
+       * config/ia64/ia64.md (UNSPEC_DTPMOD): New.
+       (symbolic_operand splitter): Pass everything through
+       ia64_expand_load_address and FAIL or DONE as appropriate.
+       (load_fptr): Only accept after reload.
+       (load_fptr_internal1, gprel64_offset, load_gprel64, load_symptr_high,
+       load_symptr_low, load_ltoff_dtpmod, 
+       (load_dtpmod): New.
+       (load_dtprel): Only accept tls symbols.
+       (load_dtprel64, load_dtprel22): Likewise.
+       (load_tprel, load_tprel64, load_tprel22): Likewise.
+       (load_dtprel_gd, load_ltoff_dtprel, load_tprel_ie): New.
+       (add_dtprel): Only accept tls symbols.  Canonicalize PLUS.
+       (add_dtprel14, add_dtprel22): Likewise.
+       (add_tprel, add_tprel14, add_tprel22): Likewise.
+       * config/ia64/predicates.md (small_addr_symbolic_operand): New.
+       (any_offset_symbol_operand, aligned_offset_symbol_operand): New.
+       (got_symbolic_operand): Check CONST offsets.
+       (tls_symbolic_operand, ld_tls_symbolic_operand): New.
+       (ie_tls_symbolic_operand, le_tls_symbolic_operand): New.
+       (move_operand): Don't handle tls here.  Check CONST offsets.
+
 2005-05-11  Richard Sandiford  <rsandifo@redhat.com>
 
        * config/mips/7000.md (rm7_impy_si_mult): Just match imul and imadd.
index 1eb458838607c656fba0b6982e6d2c04ec96cb5b..94fa176f7e7857f4c85838e0fe7257badb0d16aa 100644 (file)
@@ -37,6 +37,7 @@ extern int ia64_produce_address_p (rtx);
 extern bool ia64_const_ok_for_letter_p (HOST_WIDE_INT, char);
 extern bool ia64_const_double_ok_for_letter_p (rtx, char);
 extern bool ia64_extra_constraint (rtx, char);
+extern bool ia64_legitimate_constant_p (rtx);
 
 extern rtx ia64_expand_move (rtx, rtx);
 extern int ia64_move_ok (rtx, rtx);
@@ -58,7 +59,7 @@ extern void ia64_expand_prologue (void);
 extern void ia64_expand_epilogue (int);
 
 extern int ia64_direct_return (void);
-extern void ia64_expand_load_address (rtx, rtx);
+extern bool ia64_expand_load_address (rtx, rtx);
 extern int ia64_hard_regno_rename_ok (int, int);
 
 extern void ia64_initialize_trampoline (rtx, rtx, rtx);
index d75a74f0f1e6a0d34aaaf342e43631ff2b34bf50..a4d12c92cc34ead65c39cdc86cba9341bbcd5729 100644 (file)
@@ -163,7 +163,6 @@ static int ia64_first_cycle_multipass_dfa_lookahead_guard (rtx);
 static int ia64_dfa_new_cycle (FILE *, int, rtx, int, int, int *);
 static rtx gen_tls_get_addr (void);
 static rtx gen_thread_pointer (void);
-static rtx ia64_expand_tls_address (enum tls_model, rtx, rtx);
 static int find_gr_spill (int);
 static int next_scratch_gr_reg (void);
 static void mark_reg_gr_used_mask (rtx, void *);
@@ -264,7 +263,7 @@ static rtx ia64_struct_value_rtx (tree, int);
 static tree ia64_gimplify_va_arg (tree, tree, tree *, tree *);
 static bool ia64_scalar_mode_supported_p (enum machine_mode mode);
 static bool ia64_vector_mode_supported_p (enum machine_mode mode);
-
+static bool ia64_cannot_force_const_mem (rtx);
 \f
 /* Table of valid machine attributes.  */
 static const struct attribute_spec ia64_attribute_table[] =
@@ -424,6 +423,9 @@ static const struct attribute_spec ia64_attribute_table[] =
 #undef TARGET_HANDLE_OPTION
 #define TARGET_HANDLE_OPTION ia64_handle_option
 
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM ia64_cannot_force_const_mem
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 typedef enum
@@ -693,12 +695,64 @@ ia64_depz_field_mask (rtx rop, rtx rshift)
   return exact_log2 (op + 1);
 }
 
+/* Return the TLS model to use for ADDR.  */
+
+static enum tls_model
+tls_symbolic_operand_type (rtx addr)
+{
+  enum tls_model tls_kind = 0;
+
+  if (GET_CODE (addr) == CONST)
+    {
+      if (GET_CODE (XEXP (addr, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (addr, 0), 0)) == SYMBOL_REF)
+        tls_kind = SYMBOL_REF_TLS_MODEL (XEXP (XEXP (addr, 0), 0));
+    }
+  else if (GET_CODE (addr) == SYMBOL_REF)
+    tls_kind = SYMBOL_REF_TLS_MODEL (addr);
+
+  return tls_kind;
+}
+
+/* Return true if X is a constant that is valid for some immediate
+   field in an instruction.  */
+
+bool
+ia64_legitimate_constant_p (rtx x)
+{
+  switch (GET_CODE (x))
+    {
+    case CONST_INT:
+    case LABEL_REF:
+      return true;
+
+    case CONST_DOUBLE:
+      if (GET_MODE (x) == VOIDmode)
+       return true;
+      return CONST_DOUBLE_OK_FOR_G (x);
+
+    case CONST:
+    case SYMBOL_REF:
+      return tls_symbolic_operand_type (x) == 0;
+
+    default:
+      return false;
+    }
+}
+
+/* Don't allow TLS addresses to get spilled to memory.  */
+
+static bool
+ia64_cannot_force_const_mem (rtx x)
+{
+  return tls_symbolic_operand_type (x) != 0;
+}
+
 /* Expand a symbolic constant load.  */
 
-void
+bool
 ia64_expand_load_address (rtx dest, rtx src)
 {
-  gcc_assert (GET_CODE (src) != SYMBOL_REF || !SYMBOL_REF_TLS_MODEL (src));
   gcc_assert (GET_CODE (dest) == REG);
 
   /* ILP32 mode still loads 64-bits of data from the GOT.  This avoids
@@ -706,57 +760,59 @@ ia64_expand_load_address (rtx dest, rtx src)
      computation below are also more natural to compute as 64-bit quantities.
      If we've been given an SImode destination register, change it.  */
   if (GET_MODE (dest) != Pmode)
-    dest = gen_rtx_REG (Pmode, REGNO (dest));
+    dest = gen_rtx_REG_offset (dest, Pmode, REGNO (dest), 0);
 
-  if (GET_CODE (src) == SYMBOL_REF && SYMBOL_REF_SMALL_ADDR_P (src))
-    {
-      emit_insn (gen_rtx_SET (VOIDmode, dest, src));
-      return;
-    }
-  else if (TARGET_AUTO_PIC)
-    {
-      emit_insn (gen_load_gprel64 (dest, src));
-      return;
-    }
+  if (TARGET_NO_PIC)
+    return false;
+  if (small_addr_symbolic_operand (src, VOIDmode))
+    return false;
+
+  if (TARGET_AUTO_PIC)
+    emit_insn (gen_load_gprel64 (dest, src));
   else if (GET_CODE (src) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (src))
-    {
-      emit_insn (gen_load_fptr (dest, src));
-      return;
-    }
+    emit_insn (gen_load_fptr (dest, src));
   else if (sdata_symbolic_operand (src, VOIDmode))
+    emit_insn (gen_load_gprel (dest, src));
+  else
     {
-      emit_insn (gen_load_gprel (dest, src));
-      return;
-    }
+      HOST_WIDE_INT addend = 0;
+      rtx tmp;
 
-  if (GET_CODE (src) == CONST
-      && GET_CODE (XEXP (src, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (src, 0), 1)) == CONST_INT
-      && (INTVAL (XEXP (XEXP (src, 0), 1)) & 0x3fff) != 0)
-    {
-      rtx sym = XEXP (XEXP (src, 0), 0);
-      HOST_WIDE_INT ofs, hi, lo;
+      /* We did split constant offsets in ia64_expand_move, and we did try
+        to keep them split in move_operand, but we also allowed reload to
+        rematerialize arbitrary constants rather than spill the value to
+        the stack and reload it.  So we have to be prepared here to split
+        them apart again.  */
+      if (GET_CODE (src) == CONST)
+       {
+         HOST_WIDE_INT hi, lo;
 
-      /* Split the offset into a sign extended 14-bit low part
-        and a complementary high part.  */
-      ofs = INTVAL (XEXP (XEXP (src, 0), 1));
-      lo = ((ofs & 0x3fff) ^ 0x2000) - 0x2000;
-      hi = ofs - lo;
+         hi = INTVAL (XEXP (XEXP (src, 0), 1));
+         lo = ((hi & 0x3fff) ^ 0x2000) - 0x2000;
+         hi = hi - lo;
 
-      ia64_expand_load_address (dest, plus_constant (sym, hi));
-      emit_insn (gen_adddi3 (dest, dest, GEN_INT (lo)));
-    }
-  else
-    {
-      rtx tmp;
+         if (lo != 0)
+           {
+             addend = lo;
+             src = plus_constant (XEXP (XEXP (src, 0), 0), hi);
+           }
+       }
 
       tmp = gen_rtx_HIGH (Pmode, src);
       tmp = gen_rtx_PLUS (Pmode, tmp, pic_offset_table_rtx);
       emit_insn (gen_rtx_SET (VOIDmode, dest, tmp));
 
-      tmp = gen_rtx_LO_SUM (GET_MODE (dest), dest, src);
+      tmp = gen_rtx_LO_SUM (Pmode, dest, src);
       emit_insn (gen_rtx_SET (VOIDmode, dest, tmp));
+
+      if (addend)
+       {
+         tmp = gen_rtx_PLUS (Pmode, dest, GEN_INT (addend));
+         emit_insn (gen_rtx_SET (VOIDmode, dest, tmp));
+       }
     }
+
+  return true;
 }
 
 static GTY(()) rtx gen_tls_tga;
@@ -778,10 +834,15 @@ gen_thread_pointer (void)
 }
 
 static rtx
-ia64_expand_tls_address (enum tls_model tls_kind, rtx op0, rtx op1)
+ia64_expand_tls_address (enum tls_model tls_kind, rtx op0, rtx op1,
+                        HOST_WIDE_INT addend)
 {
   rtx tga_op1, tga_op2, tga_ret, tga_eqv, tmp, insns;
-  rtx orig_op0 = op0;
+  rtx orig_op0 = op0, orig_op1 = op1;
+  HOST_WIDE_INT addend_lo, addend_hi;
+
+  addend_lo = ((addend & 0x3fff) ^ 0x2000) - 0x2000;
+  addend_hi = addend - addend_lo;
 
   switch (tls_kind)
     {
@@ -789,12 +850,10 @@ ia64_expand_tls_address (enum tls_model tls_kind, rtx op0, rtx op1)
       start_sequence ();
 
       tga_op1 = gen_reg_rtx (Pmode);
-      emit_insn (gen_load_ltoff_dtpmod (tga_op1, op1));
-      tga_op1 = gen_const_mem (Pmode, tga_op1);
+      emit_insn (gen_load_dtpmod (tga_op1, op1));
 
       tga_op2 = gen_reg_rtx (Pmode);
-      emit_insn (gen_load_ltoff_dtprel (tga_op2, op1));
-      tga_op2 = gen_const_mem (Pmode, tga_op2);
+      emit_insn (gen_load_dtprel (tga_op2, op1));
 
       tga_ret = emit_library_call_value (gen_tls_get_addr (), NULL_RTX,
                                         LCT_CONST, Pmode, 2, tga_op1,
@@ -816,7 +875,7 @@ ia64_expand_tls_address (enum tls_model tls_kind, rtx op0, rtx op1)
       start_sequence ();
 
       tga_op1 = gen_reg_rtx (Pmode);
-      emit_insn (gen_load_ltoff_dtpmod (tga_op1, op1));
+      emit_insn (gen_load_dtpmod (tga_op1, op1));
       tga_op1 = gen_const_mem (Pmode, tga_op1);
 
       tga_op2 = const0_rtx;
@@ -841,14 +900,15 @@ ia64_expand_tls_address (enum tls_model tls_kind, rtx op0, rtx op1)
          emit_insn (gen_adddi3 (op0, tmp, op0));
        }
       else
-       emit_insn (gen_add_dtprel (op0, tmp, op1));
+       emit_insn (gen_add_dtprel (op0, op1, tmp));
       break;
 
     case TLS_MODEL_INITIAL_EXEC:
+      op1 = plus_constant (op1, addend_hi);
+      addend = addend_lo;
+
       tmp = gen_reg_rtx (Pmode);
-      emit_insn (gen_load_ltoff_tprel (tmp, op1));
-      tmp = gen_const_mem (Pmode, tmp);
-      tmp = force_reg (Pmode, tmp);
+      emit_insn (gen_load_tprel (tmp, op1));
 
       if (!register_operand (op0, Pmode))
        op0 = gen_reg_rtx (Pmode);
@@ -858,19 +918,25 @@ ia64_expand_tls_address (enum tls_model tls_kind, rtx op0, rtx op1)
     case TLS_MODEL_LOCAL_EXEC:
       if (!register_operand (op0, Pmode))
        op0 = gen_reg_rtx (Pmode);
+
+      op1 = orig_op1;
+      addend = 0;
       if (TARGET_TLS64)
        {
          emit_insn (gen_load_tprel (op0, op1));
-         emit_insn (gen_adddi3 (op0, gen_thread_pointer (), op0));
+         emit_insn (gen_adddi3 (op0, op0, gen_thread_pointer ()));
        }
       else
-       emit_insn (gen_add_tprel (op0, gen_thread_pointer (), op1));
+       emit_insn (gen_add_tprel (op0, op1, gen_thread_pointer ()));
       break;
 
     default:
       gcc_unreachable ();
     }
 
+  if (addend)
+    op0 = expand_simple_binop (Pmode, PLUS, op0, GEN_INT (addend),
+                              orig_op0, 1, OPTAB_DIRECT);
   if (orig_op0 == op0)
     return NULL_RTX;
   if (GET_MODE (orig_op0) == Pmode)
@@ -888,15 +954,58 @@ ia64_expand_move (rtx op0, rtx op1)
 
   if ((mode == Pmode || mode == ptr_mode) && symbolic_operand (op1, VOIDmode))
     {
+      HOST_WIDE_INT addend = 0;
       enum tls_model tls_kind;
-      if (GET_CODE (op1) == SYMBOL_REF
-         && (tls_kind = SYMBOL_REF_TLS_MODEL (op1)))
-       return ia64_expand_tls_address (tls_kind, op0, op1);
+      rtx sym = op1;
+
+      if (GET_CODE (op1) == CONST
+         && GET_CODE (XEXP (op1, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (op1, 0), 1)) == CONST_INT)
+       {
+         addend = INTVAL (XEXP (XEXP (op1, 0), 1));
+         sym = XEXP (XEXP (op1, 0), 0);
+       }
+
+      tls_kind = tls_symbolic_operand_type (sym);
+      if (tls_kind)
+       return ia64_expand_tls_address (tls_kind, op0, sym, addend);
+
+      if (any_offset_symbol_operand (sym, mode))
+       addend = 0;
+      else if (aligned_offset_symbol_operand (sym, mode))
+       {
+         HOST_WIDE_INT addend_lo, addend_hi;
+             
+         addend_lo = ((addend & 0x3fff) ^ 0x2000) - 0x2000;
+         addend_hi = addend - addend_lo;
+
+         if (addend_lo != 0)
+           {
+             op1 = plus_constant (sym, addend_hi);
+             addend = addend_lo;
+           }
+       }
+      else
+       op1 = sym;
 
-      if (!TARGET_NO_PIC && reload_completed)
+      if (reload_completed)
        {
-         ia64_expand_load_address (op0, op1);
-         return NULL_RTX;
+         /* We really should have taken care of this offset earlier.  */
+         gcc_assert (addend == 0);
+         if (ia64_expand_load_address (op0, op1))
+           return NULL_RTX;
+       }
+
+      if (addend)
+       {
+         rtx subtarget = no_new_pseudos ? op0 : gen_reg_rtx (mode);
+
+         emit_insn (gen_rtx_SET (VOIDmode, subtarget, op1));
+
+         op1 = expand_simple_binop (mode, PLUS, subtarget,
+                                    GEN_INT (addend), op0, 1, OPTAB_DIRECT);
+         if (op0 == op1)
+           return NULL_RTX;
        }
     }
 
index 4aca02471da1c0379e1fab092a949c9825de166d..bd32069b171cd2194da00455b0c750c78ac64da4 100644 (file)
@@ -454,7 +454,7 @@ while (0)
 
 #define CALL_REALLY_USED_REGISTERS \
 { /* General registers.  */                            \
-  1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1,      \
+  0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,      \
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      \
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      \
@@ -463,7 +463,7 @@ while (0)
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      \
   0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,      \
   /* Floating-point registers.  */                     \
-  1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
+  0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      \
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
@@ -472,7 +472,7 @@ while (0)
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
   /* Predicate registers.  */                          \
-  1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
+  0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,      \
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      \
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      \
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      \
@@ -1410,10 +1410,7 @@ do {                                                                     \
 /* A C expression that is nonzero if X is a legitimate constant for an
    immediate operand on the target machine.  */
 
-#define LEGITIMATE_CONSTANT_P(X) \
-  (GET_CODE (X) != CONST_DOUBLE || GET_MODE (X) == VOIDmode    \
-   || GET_MODE (X) == DImode || CONST_DOUBLE_OK_FOR_G (X))     \
-
+#define LEGITIMATE_CONSTANT_P(X) ia64_legitimate_constant_p (X)
 \f
 /* Condition Code Status */
 
index a1353f4e70a3860067f7ac1bb6d2b7d280490371..179a6e740049a7f3a0e2890a65014c96e7535987 100644 (file)
@@ -56,6 +56,7 @@
    (UNSPEC_DTPREL              2)
    (UNSPEC_LTOFF_TPREL         3)
    (UNSPEC_TPREL               4)
+   (UNSPEC_DTPMOD              5)
 
    (UNSPEC_LD_BASE             9)
    (UNSPEC_GR_SPILL            10)
 (define_split
   [(set (match_operand 0 "register_operand" "")
        (match_operand 1 "symbolic_operand" ""))]
-  "reload_completed && ! TARGET_NO_PIC"
+  "reload_completed"
   [(const_int 0)]
 {
-  ia64_expand_load_address (operands[0], operands[1]);
-  DONE;
+  if (ia64_expand_load_address (operands[0], operands[1]))
+    DONE;
+  else
+    FAIL;
 })
 
 (define_expand "load_fptr"
-  [(set (match_dup 2)
-       (plus:DI (reg:DI 1) (match_operand 1 "function_operand" "")))
-   (set (match_operand:DI 0 "register_operand" "") (match_dup 3))]
-  ""
+  [(set (match_operand:DI 0 "register_operand" "")
+       (plus:DI (match_dup 2) (match_operand 1 "function_operand" "")))
+   (set (match_dup 0) (match_dup 3))]
+  "reload_completed"
 {
-  operands[2] = no_new_pseudos ? operands[0] : gen_reg_rtx (DImode);
-  operands[3] = gen_const_mem (DImode, operands[2]);
+  operands[2] = pic_offset_table_rtx;
+  operands[3] = gen_const_mem (DImode, operands[0]);
 })
 
 (define_insn "*load_fptr_internal1"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI (reg:DI 1) (match_operand 1 "function_operand" "s")))]
-  ""
+  "reload_completed"
   "addl %0 = @ltoff(@fptr(%1)), gp"
   [(set_attr "itanium_class" "ialu")])
 
 (define_insn "load_gprel"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI (reg:DI 1) (match_operand 1 "sdata_symbolic_operand" "s")))]
-  ""
+  "reload_completed"
   "addl %0 = @gprel(%1), gp"
   [(set_attr "itanium_class" "ialu")])
 
-(define_insn "gprel64_offset"
+(define_insn "*gprel64_offset"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (minus:DI (match_operand:DI 1 "symbolic_operand" "") (reg:DI 1)))]
-  ""
+  "reload_completed"
   "movl %0 = @gprel(%1)"
   [(set_attr "itanium_class" "long_i")])
 
 (define_expand "load_gprel64"
-  [(set (match_dup 2)
-       (minus:DI (match_operand:DI 1 "symbolic_operand" "") (match_dup 3)))
-   (set (match_operand:DI 0 "register_operand" "")
-       (plus:DI (match_dup 3) (match_dup 2)))]
-  ""
+  [(set (match_operand:DI 0 "register_operand" "")
+       (minus:DI (match_operand:DI 1 "symbolic_operand" "") (match_dup 2)))
+   (set (match_dup 0)
+       (plus:DI (match_dup 2) (match_dup 0)))]
+  "reload_completed"
 {
-  operands[2] = no_new_pseudos ? operands[0] : gen_reg_rtx (DImode);
-  operands[3] = pic_offset_table_rtx;
+  operands[2] = pic_offset_table_rtx;
 })
 
 ;; This is used as a placeholder for the return address during early
   [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI (high:DI (match_operand 1 "got_symbolic_operand" "s"))
                 (match_operand:DI 2 "register_operand" "a")))]
-  ""
+  "reload_completed"
 {
   if (HAVE_AS_LTOFFX_LDXMOV_RELOCS)
     return "%,addl %0 = @ltoffx(%1), %2";
   [(set (match_operand:DI 0 "register_operand" "=r")
        (lo_sum:DI (match_operand:DI 1 "register_operand" "r")
                   (match_operand 2 "got_symbolic_operand" "s")))]
-  ""
+  "reload_completed"
 {
   if (HAVE_AS_LTOFFX_LDXMOV_RELOCS)
     return "%,ld8.mov %0 = [%1], %2";
 }
   [(set_attr "itanium_class" "ld")])
 
-(define_insn "load_ltoff_dtpmod"
+(define_insn_and_split "load_dtpmod"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (plus:DI (reg:DI 1)
-                (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
-                           UNSPEC_LTOFF_DTPMOD)))]
+       (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
+                  UNSPEC_DTPMOD))]
   ""
-  "addl %0 = @ltoff(@dtpmod(%1)), gp"
-  [(set_attr "itanium_class" "ialu")])
+  "#"
+  "reload_completed"
+  [(set (match_dup 0)
+       (plus:DI (unspec:DI [(match_dup 1)] UNSPEC_LTOFF_DTPMOD)
+                (match_dup 2)))
+   (set (match_dup 0) (match_dup 3))]
+{
+  operands[2] = pic_offset_table_rtx;
+  operands[3] = gen_const_mem (DImode, operands[0]);
+})
 
-(define_insn "load_ltoff_dtprel"
+(define_insn "*load_ltoff_dtpmod"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (plus:DI (reg:DI 1)
-                (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
-                           UNSPEC_LTOFF_DTPREL)))]
-  ""
-  "addl %0 = @ltoff(@dtprel(%1)), gp"
+       (plus:DI (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
+                           UNSPEC_LTOFF_DTPMOD)
+                (match_operand:DI 2 "register_operand" "a")))]
+  "reload_completed"
+  "addl %0 = @ltoff(@dtpmod(%1)), %2"
   [(set_attr "itanium_class" "ialu")])
 
 (define_expand "load_dtprel"
   [(set (match_operand:DI 0 "register_operand" "")
-       (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
+       (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
                   UNSPEC_DTPREL))]
   ""
   "")
 
 (define_insn "*load_dtprel64"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
+       (unspec:DI [(match_operand:DI 1 "ld_tls_symbolic_operand" "")]
                   UNSPEC_DTPREL))]
   "TARGET_TLS64"
   "movl %0 = @dtprel(%1)"
 
 (define_insn "*load_dtprel22"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
+       (unspec:DI [(match_operand:DI 1 "ld_tls_symbolic_operand" "")]
                   UNSPEC_DTPREL))]
   ""
   "addl %0 = @dtprel(%1), r0"
   [(set_attr "itanium_class" "ialu")])
 
+(define_insn_and_split "*load_dtprel_gd"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
+                  UNSPEC_DTPREL))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 0)
+       (plus:DI (unspec:DI [(match_dup 1)] UNSPEC_LTOFF_DTPREL)
+                (match_dup 2)))
+   (set (match_dup 0) (match_dup 3))]
+{
+  operands[2] = pic_offset_table_rtx;
+  operands[3] = gen_const_mem (DImode, operands[0]);
+})
+
+(define_insn "*load_ltoff_dtprel"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (plus:DI (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
+                           UNSPEC_LTOFF_DTPREL)
+                (match_operand:DI 2 "register_operand" "a")))]
+  ""
+  "addl %0 = @ltoff(@dtprel(%1)), %2"
+  [(set_attr "itanium_class" "ialu")])
+
 (define_expand "add_dtprel"
   [(set (match_operand:DI 0 "register_operand" "")
-       (plus:DI (match_operand:DI 1 "register_operand" "")
-                (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
-                           UNSPEC_DTPREL)))]
+       (plus:DI (unspec:DI [(match_operand:DI 1 "ld_tls_symbolic_operand" "")]
+                           UNSPEC_DTPREL)
+                (match_operand:DI 2 "register_operand" "")))]
   "!TARGET_TLS64"
   "")
 
 (define_insn "*add_dtprel14"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (plus:DI (match_operand:DI 1 "register_operand" "r")
-                (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
-                           UNSPEC_DTPREL)))]
+       (plus:DI (unspec:DI [(match_operand:DI 1 "ld_tls_symbolic_operand" "")]
+                           UNSPEC_DTPREL)
+                (match_operand:DI 2 "register_operand" "r")))]
   "TARGET_TLS14"
-  "adds %0 = @dtprel(%2), %1"
+  "adds %0 = @dtprel(%1), %2"
   [(set_attr "itanium_class" "ialu")])
 
 (define_insn "*add_dtprel22"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (plus:DI (match_operand:DI 1 "register_operand" "a")
-                (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
-                           UNSPEC_DTPREL)))]
+       (plus:DI (unspec:DI [(match_operand:DI 1 "ld_tls_symbolic_operand" "")]
+                           UNSPEC_DTPREL)
+                (match_operand:DI 2 "register_operand" "a")))]
   "TARGET_TLS22"
-  "addl %0 = @dtprel(%2), %1"
-  [(set_attr "itanium_class" "ialu")])
-
-(define_insn "load_ltoff_tprel"
-  [(set (match_operand:DI 0 "register_operand" "=r")
-       (plus:DI (reg:DI 1)
-                (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
-                           UNSPEC_LTOFF_TPREL)))]
-  ""
-  "addl %0 = @ltoff(@tprel(%1)), gp"
+  "addl %0 = @dtprel(%1), %2"
   [(set_attr "itanium_class" "ialu")])
 
 (define_expand "load_tprel"
   [(set (match_operand:DI 0 "register_operand" "")
-       (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
+       (unspec:DI [(match_operand:DI 1 "tls_symbolic_operand" "")]
                   UNSPEC_TPREL))]
   ""
   "")
 
 (define_insn "*load_tprel64"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
+       (unspec:DI [(match_operand:DI 1 "le_tls_symbolic_operand" "")]
                   UNSPEC_TPREL))]
   "TARGET_TLS64"
   "movl %0 = @tprel(%1)"
 
 (define_insn "*load_tprel22"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
+       (unspec:DI [(match_operand:DI 1 "le_tls_symbolic_operand" "")]
                   UNSPEC_TPREL))]
   ""
   "addl %0 = @tprel(%1), r0"
   [(set_attr "itanium_class" "ialu")])
 
+(define_insn_and_split "*load_tprel_ie"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (unspec:DI [(match_operand:DI 1 "ie_tls_symbolic_operand" "")]
+                  UNSPEC_TPREL))]
+  ""
+  "#"
+  "reload_completed"
+  [(set (match_dup 0)
+       (plus:DI (unspec:DI [(match_dup 1)] UNSPEC_LTOFF_TPREL)
+                (match_dup 2)))
+   (set (match_dup 0) (match_dup 3))]
+{
+  operands[2] = pic_offset_table_rtx;
+  operands[3] = gen_const_mem (DImode, operands[0]);
+})
+
+(define_insn "*load_ltoff_tprel"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (plus:DI (unspec:DI [(match_operand:DI 1 "ie_tls_symbolic_operand" "")]
+                           UNSPEC_LTOFF_TPREL)
+                (match_operand:DI 2 "register_operand" "a")))]
+  ""
+  "addl %0 = @ltoff(@tprel(%1)), %2"
+  [(set_attr "itanium_class" "ialu")])
+
 (define_expand "add_tprel"
   [(set (match_operand:DI 0 "register_operand" "")
-       (plus:DI (match_operand:DI 1 "register_operand" "")
-                (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
-                           UNSPEC_TPREL)))]
+       (plus:DI (unspec:DI [(match_operand:DI 1 "le_tls_symbolic_operand" "")]
+                           UNSPEC_TPREL)
+                (match_operand:DI 2 "register_operand" "")))]
   "!TARGET_TLS64"
   "")
 
 (define_insn "*add_tprel14"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (plus:DI (match_operand:DI 1 "register_operand" "r")
-                (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
-                           UNSPEC_TPREL)))]
+       (plus:DI (unspec:DI [(match_operand:DI 1 "le_tls_symbolic_operand" "")]
+                           UNSPEC_TPREL)
+                (match_operand:DI 2 "register_operand" "r")))]
   "TARGET_TLS14"
-  "adds %0 = @tprel(%2), %1"
+  "adds %0 = @tprel(%1), %2"
   [(set_attr "itanium_class" "ialu")])
 
 (define_insn "*add_tprel22"
   [(set (match_operand:DI 0 "register_operand" "=r")
-       (plus:DI (match_operand:DI 1 "register_operand" "a")
-                (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
-                           UNSPEC_TPREL)))]
+       (plus:DI (unspec:DI [(match_operand:DI 1 "le_tls_symbolic_operand" "")]
+                           UNSPEC_TPREL)
+                (match_operand:DI 2 "register_operand" "a")))]
   "TARGET_TLS22"
-  "addl %0 = @tprel(%2), %1"
+  "addl %0 = @tprel(%1), %2"
   [(set_attr "itanium_class" "ialu")])
 
 ;; With no offsettable memory references, we've got to have a scratch
index 554dc79c95f6ec64931d4537ca0ae2800b1d71a1..7ec32090b5d62ff8cd7b0e570abb09c8b6842c5d 100644 (file)
   (and (match_code "symbol_ref")
        (match_test "SYMBOL_REF_FUNCTION_P (op)")))
 
-;; True if OP refers to a symbol, and is appropriate for a GOT load.
-(define_predicate "got_symbolic_operand" 
-  (match_operand 0 "symbolic_operand" "")
-{
-  switch (GET_CODE (op))
-    {
-    case LABEL_REF:
-      return true;
-
-    case SYMBOL_REF:
-      /* This sort of load should not be used for things in sdata.  */
-      return !SYMBOL_REF_SMALL_ADDR_P (op);
-
-    case CONST:
-      /* Accept only (plus (symbol_ref) (const_int)).  */
-      op = XEXP (op, 0);
-      if (GET_CODE (op) != PLUS
-         || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
-          || GET_CODE (XEXP (op, 1)) != CONST_INT)
-        return false;
-
-      /* Ok if we're not using GOT entries at all.  */
-      if (TARGET_NO_PIC || TARGET_AUTO_PIC)
-        return true;
-
-      /* The low 14 bits of the constant have been forced to zero
-        by ia64_expand_load_address, so that we do not use up so
-        many GOT entries.  Prevent cse from undoing this.  */
-      op = XEXP (op, 1);
-      return (INTVAL (op) & 0x3fff) == 0;
-
-    default:
-      gcc_unreachable ();
-    }
-})
-
 ;; True if OP refers to a symbol in the sdata section.
 (define_predicate "sdata_symbolic_operand" 
   (match_code "symbol_ref,const")
     }
 })
 
+;; True if OP refers to a symbol in the small address area.
+(define_predicate "small_addr_symbolic_operand" 
+  (match_code "symbol_ref,const")
+{
+  switch (GET_CODE (op))
+    {
+    case CONST:
+      op = XEXP (op, 0);
+      if (GET_CODE (op) != PLUS
+         || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+         || GET_CODE (XEXP (op, 1)) != CONST_INT)
+       return false;
+      op = XEXP (op, 0);
+      /* FALLTHRU */
+
+    case SYMBOL_REF:
+      return SYMBOL_REF_SMALL_ADDR_P (op);
+
+    default:
+      gcc_unreachable ();
+    }
+})
+
+;; True if OP refers to a symbol with which we may use any offset.
+(define_predicate "any_offset_symbol_operand"
+  (match_code "symbol_ref")
+{
+  if (TARGET_NO_PIC || TARGET_AUTO_PIC)
+    return true;
+  if (SYMBOL_REF_SMALL_ADDR_P (op))
+    return true;
+  if (SYMBOL_REF_FUNCTION_P (op))
+    return false;
+  if (sdata_symbolic_operand (op, mode))
+    return true;
+  return false;
+})
+
+;; True if OP refers to a symbol with which we may use 14-bit aligned offsets.
+;; False if OP refers to a symbol with which we may not use any offset at any
+;; time.
+(define_predicate "aligned_offset_symbol_operand"
+  (and (match_code "symbol_ref")
+       (match_test "! SYMBOL_REF_FUNCTION_P (op)")))
+
+;; True if OP refers to a symbol, and is appropriate for a GOT load.
+(define_predicate "got_symbolic_operand" 
+  (match_operand 0 "symbolic_operand" "")
+{
+  HOST_WIDE_INT addend = 0;
+
+  switch (GET_CODE (op))
+    {
+    case LABEL_REF:
+      return true;
+
+    case CONST:
+      /* Accept only (plus (symbol_ref) (const_int)).  */
+      op = XEXP (op, 0);
+      if (GET_CODE (op) != PLUS
+         || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+          || GET_CODE (XEXP (op, 1)) != CONST_INT)
+        return false;
+
+      addend = INTVAL (XEXP (op, 1));
+      op = XEXP (op, 0);
+      /* FALLTHRU */
+
+    case SYMBOL_REF:
+      /* These symbols shouldn't be used with got loads.  */
+      if (SYMBOL_REF_SMALL_ADDR_P (op))
+       return false;
+      if (SYMBOL_REF_TLS_MODEL (op) != 0)
+       return false;
+
+      if (any_offset_symbol_operand (op, mode))
+       return true;
+
+      /* The low 14 bits of the constant have been forced to zero
+        so that we do not use up so many GOT entries.  Prevent cse
+        from undoing this.  */
+      if (aligned_offset_symbol_operand (op, mode))
+       return (addend & 0x3fff) == 0;
+
+      return addend == 0;
+
+    default:
+      gcc_unreachable ();
+    }
+})
+
+;; Return true if OP is a valid thread local storage symbolic operand.
+(define_predicate "tls_symbolic_operand"
+  (match_code "symbol_ref,const")
+{
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF:
+      return SYMBOL_REF_TLS_MODEL (op) != 0;
+
+    case CONST:
+      op = XEXP (op, 0);
+      if (GET_CODE (op) != PLUS
+         || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+         || GET_CODE (XEXP (op, 1)) != CONST_INT)
+       return false;
+
+      /* We only allow certain offsets for certain tls models.  */
+      switch (SYMBOL_REF_TLS_MODEL (XEXP (op, 0)))
+       {
+       case TLS_MODEL_GLOBAL_DYNAMIC:
+       case TLS_MODEL_LOCAL_DYNAMIC:
+         return false;
+
+       case TLS_MODEL_INITIAL_EXEC:
+         return (INTVAL (XEXP (op, 1)) & 0x3fff) == 0;
+
+       case TLS_MODEL_LOCAL_EXEC:
+         return true;
+
+       default:
+         return false;
+       }
+
+    default:
+      gcc_unreachable ();
+    }
+})
+
+;; Return true if OP is a local-dynamic thread local storage symbolic operand.
+(define_predicate "ld_tls_symbolic_operand"
+  (and (match_code "symbol_ref")
+       (match_test "SYMBOL_REF_TLS_MODEL (op) == TLS_MODEL_LOCAL_DYNAMIC")))
+
+;; Return true if OP is an initial-exec thread local storage symbolic operand.
+(define_predicate "ie_tls_symbolic_operand"
+  (match_code "symbol_ref,const")
+{
+  switch (GET_CODE (op))
+    {
+    case CONST:
+      op = XEXP (op, 0);
+      if (GET_CODE (op) != PLUS
+         || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+         || GET_CODE (XEXP (op, 1)) != CONST_INT
+         || (INTVAL (XEXP (op, 1)) & 0x3fff) != 0)
+       return false;
+      op = XEXP (op, 0);
+      /* FALLTHRU */
+
+    case SYMBOL_REF:
+      return SYMBOL_REF_TLS_MODEL (op) == TLS_MODEL_INITIAL_EXEC;
+
+    default:
+      gcc_unreachable ();
+    }
+})
+
+;; Return true if OP is a local-exec thread local storage symbolic operand.
+(define_predicate "le_tls_symbolic_operand"
+  (match_code "symbol_ref,const")
+{
+  switch (GET_CODE (op))
+    {
+    case CONST:
+      op = XEXP (op, 0);
+      if (GET_CODE (op) != PLUS
+          || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+          || GET_CODE (XEXP (op, 1)) != CONST_INT)
+        return false;
+      op = XEXP (op, 0);
+      /* FALLTHRU */
+
+    case SYMBOL_REF:
+      return SYMBOL_REF_TLS_MODEL (op) == TLS_MODEL_LOCAL_EXEC;
+
+    default:
+      gcc_unreachable ();
+    }
+})
+
 ;; Like nonimmediate_operand, but don't allow MEMs that try to use a
 ;; POST_MODIFY with a REG as displacement.
 (define_predicate "destination_operand"
   (and (match_operand 0 "memory_operand")
        (match_test "GET_RTX_CLASS (GET_CODE (XEXP (op, 0))) != RTX_AUTOINC")))
 
-;; True if OP is a general operand, excluding tls symbolic operands.
+;; True if OP is a general operand, with some restrictions on symbols.
 (define_predicate "move_operand"
-  (and (match_operand 0 "general_operand")
-       (not (match_test 
-            "GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (op)"))))
+  (match_operand 0 "general_operand")
+{
+  switch (GET_CODE (op))
+    {
+    case CONST:
+      {
+       HOST_WIDE_INT addend;
+
+       /* Accept only (plus (symbol_ref) (const_int)).  */
+       op = XEXP (op, 0);
+       if (GET_CODE (op) != PLUS
+           || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+            || GET_CODE (XEXP (op, 1)) != CONST_INT)
+         return false;
+
+       addend = INTVAL (XEXP (op, 1));
+       op = XEXP (op, 0);
+
+       /* After reload, we want to allow any offset whatsoever.  This
+          allows reload the opportunity to avoid spilling addresses to
+          the stack, and instead simply substitute in the value from a
+          REG_EQUIV.  We'll split this up again when splitting the insn.  */
+       if (reload_in_progress || reload_completed)
+         return true;
+
+       /* Some symbol types we allow to use with any offset.  */
+       if (any_offset_symbol_operand (op, mode))
+         return true;
+
+       /* Some symbol types we allow offsets with the low 14 bits of the
+          constant forced to zero so that we do not use up so many GOT
+          entries.  We want to prevent cse from undoing this.  */
+       if (aligned_offset_symbol_operand (op, mode))
+         return (addend & 0x3fff) == 0;
+
+       /* The remaining symbol types may never be used with an offset.  */
+       return false;
+      }
+
+    default:
+      return true;
+    }
+})
 
 ;; True if OP is a register operand that is (or could be) a GR reg.
 (define_predicate "gr_register_operand"