gcc/ChangeLog:

        * config/rx/rx.cc (rx_is_local_readonly_data_p): New.
        (legitimize_pic_address): New.
        (rx_legitimize_address): Add PIC support.
        (rx_is_relocatable_operand_p): New.
        (rx_is_legitimate_address): Add PIC rules.
        (rx_is_pic_unspec_p): New.
        (nonpic_symbol_mentioned_p): New.
        (rx_print_operand_with_attrib): New.
        (rx_print_operand_address): Add PIC symbol support.
        (rx_assemble_integer): Add funcdesc output.
        (rx_print_operand): Add PC relative output.
        (rx_maybe_legitimize_pi_operand): Rename from rx_maybe_pidify_operand.
        Unified modification pic and pid mode.
        (rx_conditional_register_usage): mark used for pic reg.
        (rx_get_stack_layout): Add PIC support.
        (rx_expand_prologue): Add PIC prologue.
        (rx_expand_epilogue): PIC epilogue.
        (rx_elf_asm_cdtor): Add FDPIC output.
        (rx_assemble_integer): Add PIC case output.
        (rx_option_override): fdpic support.
        (rx_function_ok_for_sibcall): Use relative call in fdpic mode.
        (rx_is_legitimate_constant): Add PIC support.
        (rx_legitimate_pic_operand_p): New.
        (rx_get_fdpic_reg_initial_val) New.
        (rx_mov_pic_operands): New.
        (rx_asm_output_addr_const_extra) New.
        (rx_const_not_ok_for_debug_p): New.
        (rx_cannot_force_const_mem_p): New.
        (rx_is_unspec_offset): New.
        (rx_delegitimize_address): New.
        (+rx_expand_pcrel): New.
        (TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA): New.
        (TARGET_CONST_NOT_OK_FOR_DEBUG_P): New.
        (TARGET_CANNOT_FORCE_CONST_MEM): New.
        (TARGET_DELEGITIMIZE_ADDRESS): New.

Signed-off-by: Yoshinori Sato <[email protected]>
---
 gcc/config/rx/rx.cc | 574 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 555 insertions(+), 19 deletions(-)

diff --git a/gcc/config/rx/rx.cc b/gcc/config/rx/rx.cc
index 038f93431f4..9160d9ea2ee 100644
--- a/gcc/config/rx/rx.cc
+++ b/gcc/config/rx/rx.cc
@@ -143,11 +143,100 @@ rx_pid_data_operand (rtx op)
   return PID_NOT_PID;
 }
 
+/* Determine whether x is a local, read-only variable.  */
+static bool
+rx_is_local_readonly_data_p (rtx x)
+{
+  return (SYMBOL_REF_P (x)
+         && SYMBOL_REF_DECL (x)
+         && TREE_READONLY (SYMBOL_REF_DECL (x))
+         && !SYMBOL_REF_EXTERNAL_P (x)
+         && !SYMBOL_REF_FUNCTION_P (x));
+}
+
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+   @GOTOFF in `reg'.  */
+static rtx
+legitimize_pic_address (rtx x, rtx orig)
+{
+  rtx reg;
+  rtx inner = x;
+
+  if (GET_CODE (x) == CONST)
+    inner = XEXP (x, 0);
+
+  if ((GET_CODE (inner) == UNSPEC && XINT (inner, 1) == UNSPEC_PCREL)
+      && (!SYMBOL_REF_P (x) && !LABEL_REF_P (x) && GET_CODE (x) != CONST))
+    return x;
+
+  reg = gen_reg_rtx (Pmode);
+
+  if (flag_plt && (GET_CODE (orig) == LABEL_REF
+                  || (GET_CODE (orig) == SYMBOL_REF
+                      && !SYMBOL_REF_LOCAL_P (orig))))
+    {
+      if (TARGET_FDPIC)
+       {
+         if (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (orig))
+           {
+             if (SYMBOL_REF_LOCAL_P (orig))
+               emit_insn (gen_symGOTOFFFUNCDESC2reg (reg, orig));
+             else
+               emit_insn (gen_symGOTFUNCDESC2reg (reg, orig));
+           }
+         else
+           emit_insn (gen_symGOT2reg (reg, orig));
+       }
+      crtl->uses_pic_offset_table = 1;
+      return reg;
+    }
+  else if (GET_CODE (orig) == SYMBOL_REF)
+    {
+      if (TARGET_FDPIC && SYMBOL_REF_FUNCTION_P (orig))
+       {
+         if (SYMBOL_REF_LOCAL_P (orig))
+           emit_insn (gen_symGOTOFFFUNCDESC2reg (reg, orig));
+         else
+           emit_insn (gen_symGOTFUNCDESC2reg (reg, orig));
+       }
+      else
+       {
+         if (SYMBOL_REF_LOCAL_P (orig))
+           {
+             if (!rx_is_local_readonly_data_p (orig))
+               emit_insn (gen_symGOTOFF2reg (reg, orig));
+             else
+               emit_insn (gen_movsi_pcrel (reg, orig));
+           }
+         else
+           emit_insn (gen_symGOT2reg (reg, orig));
+       }
+      crtl->uses_pic_offset_table = 1;
+      return reg;
+    }
+  else if (flag_pic && rx_pcrel_label_operand (x, Pmode))
+    {
+      emit_insn (gen_movsi_pcrel (reg, orig));
+      return reg;
+    }
+  return x;
+}
+
 static rtx
 rx_legitimize_address (rtx x,
                       rtx oldx ATTRIBUTE_UNUSED,
                       machine_mode mode ATTRIBUTE_UNUSED)
 {
+  rtx op0 = XEXP (x, 0);
+  rtx op1 = XEXP (x, 1);
+
+  if (flag_pic)
+    {
+      rtx newx = legitimize_pic_address (x, oldx);
+      if (newx != oldx)
+       return newx;
+    }
+
   if (rx_pid_data_operand (x) == PID_UNENCODED)
     {
       rtx rv = gen_pid_addr (gen_rtx_REG (SImode, rx_pid_base_regnum ()), x);
@@ -156,9 +245,6 @@ rx_legitimize_address (rtx x,
 
   if (GET_CODE (x) == PLUS)
     {
-      rtx op0 = XEXP (x, 0);
-      rtx op1 = XEXP (x, 1);
-
       if (GET_CODE (op0) == PLUS
          && REG_P (XEXP (op0, 0))
          && CONST_INT_P (op1))
@@ -188,6 +274,15 @@ rx_small_data_operand (rtx op)
   return false;
 }
 
+/* Determine whether x is an operand that may involve relocation.  */
+static bool
+rx_is_relocatable_operand_p (rtx x)
+{
+  return (GET_CODE (x) == UNSPEC
+         || SYMBOL_REF_P (x)
+         || LABEL_REF_P (x));
+}
+
 static bool
 rx_is_legitimate_address (machine_mode mode, rtx x,
                          bool strict ATTRIBUTE_UNUSED,
@@ -212,6 +307,10 @@ rx_is_legitimate_address (machine_mode mode, rtx x,
       if (GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC)
        return false;
 
+      /* Cannot relocate the address of the high-order word.  */
+      if (flag_pic)
+       return false;
+
       if (GET_CODE (x) == PLUS)
        {
          rtx base = XEXP (x, 0);
@@ -249,6 +348,22 @@ rx_is_legitimate_address (machine_mode mode, rtx x,
       else
        return false;
 
+      if (flag_pic && GET_CODE (arg2) == CONST)
+       {
+         rtx inner = XEXP (arg2, 0);
+
+         /* Cannot relocate the address of the high-order word.  */
+         if (GET_MODE_SIZE (mode) == UNITS_PER_WORD * 2)
+           return false;
+
+         /* TODO:
+           Reject "mov.L symbol@GOTFUNCDESC[reg],reg"
+           because ld cannot relocate it.  */
+         if (GET_CODE (inner) == UNSPEC
+             && XINT (inner, 1) == UNSPEC_GOTFUNCDESC)
+           return false;
+       }
+
       switch (GET_CODE (index))
        {
        case CONST_INT:
@@ -294,16 +409,88 @@ rx_is_legitimate_address (machine_mode mode, rtx x,
              && CONST_INT_P (factor)
              && GET_MODE_SIZE (mode) == INTVAL (factor);
          }
-
+       case CONST:
+         if (!flag_pic)
+           return false;
+         else
+           {
+             rtx inner = XEXP (index, 0);
+             if (GET_CODE (inner) == PLUS)
+               {
+                 rtx op0 = XEXP (inner, 0);
+                 rtx op1 = XEXP (inner, 1);
+                 if (rx_is_relocatable_operand_p (op0)
+                     || rx_is_relocatable_operand_p (op1))
+                   return false;
+               }
+             return !rx_is_unspec_offset (index);
+           }
        default:
          return false;
        }
     }
+  if (flag_pic && GET_CODE (x) == CONST
+      && GET_CODE (XEXP (x, 0)) == UNSPEC
+      && XINT (XEXP (x, 0), 1) == UNSPEC_PCREL)
+    return true;
 
   /* Small data area accesses turn into register relative offsets.  */
   return rx_small_data_operand (x);
 }
 
+/* Check if the x is PIC.  */
+static bool
+rx_is_pic_unspec_p (rtx x)
+{
+  if (GET_CODE (x) != UNSPEC)
+    return false;
+  switch (XINT (x, 1))
+    {
+    case UNSPEC_PIC:
+    case UNSPEC_GOT:
+    case UNSPEC_GOTOFF:
+    case UNSPEC_PLT:
+    case UNSPEC_GOTFUNCDESC:
+    case UNSPEC_GOTOFFFUNCDESC:
+    case UNSPEC_PCREL:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
+   isn't protected by a PIC unspec.  */
+bool
+nonpic_symbol_mentioned_p (rtx x)
+{
+  if (GET_CODE (x) == PC)
+    return true;
+
+  /* We don't want to look into the possible MEM location of a
+     CONST_DOUBLE, since we're not going to use it, in general.  */
+  if (GET_CODE (x) == CONST_DOUBLE)
+    return false;
+
+  if (rx_is_pic_unspec_p (x))
+    return false;
+
+  const char* fmt = GET_RTX_FORMAT (GET_CODE (x));
+  for (int i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+       {
+         for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
+           if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
+             return true;
+       }
+      else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
+       return true;
+    }
+
+  return false;
+}
+
 /* Returns TRUE for simple memory addresses, ie ones
    that do not involve register indirect addressing
    or pre/post increment/decrement.  */
@@ -419,6 +606,21 @@ rx_mode_dependent_address_p (const_rtx addr, addr_space_t 
as ATTRIBUTE_UNUSED)
    assembler syntax for an instruction operand that is a memory
    reference whose address is ADDR.  */
 
+static void
+rx_print_operand_with_attrib (FILE * file, const char *attrib,
+                             rtx addr, bool immediate)
+{
+  if (SYMBOL_REF_P (addr))
+    {
+      if (immediate)
+       fprintf (file, "#");
+      output_addr_const (file, addr);
+      fprintf (file, "@%s", attrib);
+    }
+  else
+    rx_print_operand (file, addr, 0);
+}
+
 static void
 rx_print_operand_address (FILE * file, machine_mode /*mode*/, rtx addr)
 {
@@ -453,7 +655,7 @@ rx_print_operand_address (FILE * file, machine_mode 
/*mode*/, rtx addr)
        else if (REG_P (arg2) && RTX_OK_FOR_BASE (arg2, true))
          base = arg2, index = arg1;
        else
-         {
+         { /* symbol + offset[Rn] */
            rx_print_operand (file, arg1, 0);
            fprintf (file, " + ");
            rx_print_operand (file, arg2, 0);
@@ -461,13 +663,19 @@ rx_print_operand_address (FILE * file, machine_mode 
/*mode*/, rtx addr)
          }
 
        if (REG_P (index) || GET_CODE (index) == MULT)
-         {
+         { /* [Ri, Rb] */
            fprintf (file, "[");
            rx_print_operand (file, index, 'A');
            fprintf (file, ",");
          }
-       else /* GET_CODE (index) == CONST_INT  */
-         {
+       else
+         { /* dsp[Rn] */
+           if (GET_CODE (index) == CONST
+               && GET_CODE (XEXP (index, 0)) == UNSPEC
+               && (XINT (XEXP (index, 0), 1) == UNSPEC_GOTOFF
+                   || XINT (XEXP (index, 0), 1) == UNSPEC_GOTOFFFUNCDESC))
+             /* Only simple offsets are accepted here.  */
+             gcc_unreachable ();
            rx_print_operand (file, index, 'A');
            fprintf (file, "[");
          }
@@ -493,8 +701,33 @@ rx_print_operand_address (FILE * file, machine_mode 
/*mode*/, rtx addr)
       break;
 
     case UNSPEC:
-      addr = XVECEXP (addr, 0, 0);
-      /* Fall through.  */
+      {
+       int post = XINT (addr, 1);
+       addr = XVECEXP (addr, 0, 0);
+
+       switch (post)
+         {
+         case UNSPEC_GOT:
+           rx_print_operand_with_attrib (file, "GOT", addr, false);
+           break;
+         case UNSPEC_GOTOFF:
+           rx_print_operand_with_attrib (file, "GOTOFF", addr, true);
+           break;
+         case UNSPEC_GOTFUNCDESC:
+           rx_print_operand_with_attrib (file, "GOTFUNCDESC", addr, true);
+           break;
+         case UNSPEC_GOTOFFFUNCDESC:
+           rx_print_operand_with_attrib (file, "GOTOFFFUNCDESC", addr, true);
+           break;
+         case UNSPEC_PLT:
+           rx_print_operand_with_attrib (file, "PLT", addr, false);
+           break;
+         case UNSPEC_PCREL:
+           output_addr_const (file, addr);
+           break;
+         }
+       break;
+      }
     case LABEL_REF:
     case SYMBOL_REF:
       fprintf (file, "#");
@@ -522,6 +755,15 @@ rx_assemble_integer (rtx x, unsigned int size, int 
is_aligned)
 {
   const char *  op = integer_asm_op (size, is_aligned);
 
+    if (TARGET_FDPIC && size == UNITS_PER_WORD
+      && GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x))
+    {
+      fputs (op, asm_out_file);
+      output_addr_const (asm_out_file, x);
+      fputs ("@FUNCDESC\n", asm_out_file);
+      return true;
+    }
+
   if (! CONST_INT_P (x))
     return default_assemble_integer (x, size, is_aligned);
 
@@ -949,6 +1191,11 @@ rx_print_operand (FILE * file, rtx op, int letter)
                fprintf (file, ")");
                return;
              }
+           case UNSPEC_PCREL:
+             if (print_hash)
+               fprintf (file, "#");
+             output_addr_const (file, XVECEXP (op, 0, 0));
+             return;
            }
          /* Fall through */
 
@@ -966,10 +1213,10 @@ rx_print_operand (FILE * file, rtx op, int letter)
     }
 }
 
-/* Maybe convert an operand into its PID format.  */
+/* Maybe convert an operand into its PI (PIC/PID) format.  */
 
 rtx
-rx_maybe_pidify_operand (rtx op, int copy_to_reg)
+rx_maybe_legitimize_pi_operand (rtx op, int copy_to_reg)
 {
   if (rx_pid_data_operand (op) == PID_UNENCODED)
     {
@@ -986,6 +1233,105 @@ rx_maybe_pidify_operand (rtx op, int copy_to_reg)
       if (copy_to_reg)
        op = copy_to_mode_reg (GET_MODE (op), op);
     }
+  else if (flag_pic)
+    {
+      rtx picreg = gen_rtx_REG (Pmode, PIC_REG);
+      rtx t;
+
+      switch (GET_CODE (op))
+       {
+       case CONST:
+         {
+           rtx base, offset;
+           split_const (op, &base, &offset);
+
+           if (SYMBOL_REF_P (base) || LABEL_REF_P (base))
+             {
+               tree decl = SYMBOL_REF_P (base)
+                 ? SYMBOL_REF_DECL (base) : NULL_TREE;
+               bool is_local_var = (decl
+                                    && TREE_CODE (decl) == VAR_DECL
+                                    && !TREE_PUBLIC (decl));
+
+               t = gen_reg_rtx (GET_MODE (op));
+
+               if (is_local_var || LABEL_REF_P (base))
+                 {
+                   /* PC relative with offset.  */
+                   rtx label = gen_sym2pcrel (base);
+                   emit_insn (gen_movsi_pcrel (t, label));
+                 }
+               else
+                 {
+                   crtl->uses_pic_offset_table = true;
+                   if (SYMBOL_REF_LOCAL_P (base)
+                       && !rx_is_local_readonly_data_p (base))
+                     {
+                       rtx gotoffsym = gen_sym2GOTOFF (base);
+                       rtx src = gen_rtx_PLUS (Pmode, picreg, gotoffsym);
+                       emit_move_insn (t, src);
+                     }
+                   else
+                     {
+                       rtx gotsym = gen_sym2GOT (base);
+                       rtx got_addr = gen_rtx_PLUS (Pmode, picreg, gotsym);
+                       rtx src = gen_rtx_MEM (GET_MODE (op), got_addr);
+                       emit_move_insn (t, src);
+                   }
+                 }
+
+               if (offset != const0_rtx)
+                 emit_insn (gen_addsi3 (t, t, offset));
+
+               return t;
+             }
+           break;
+         }
+       case SYMBOL_REF:
+       case LABEL_REF:
+         {
+           /* Local read-only symbols are accessed
+             using PC-relative addresses.  */
+           if (GET_CODE (op) == LABEL_REF || rx_is_local_readonly_data_p (op))
+             return gen_sym2pcrel (op);
+
+           /* funcdesc case.  */
+           if (TARGET_FDPIC
+               && SYMBOL_REF_FUNCTION_P (op)
+               && !RTX_FLAG (op, frame_related))
+             {
+               rtx t = gen_reg_rtx (Pmode);
+               rtx funcsym;
+               funcsym = SYMBOL_REF_LOCAL_P (op)
+                 ? gen_sym2GOTOFFFUNCDESC (op) : gen_sym2GOTFUNCDESC (op);
+               emit_insn (gen_addsi3 (t, picreg, funcsym));
+               return t;
+             }
+           /* variable case.  */
+           if (SYMBOL_REF_LOCAL_P (op))
+             {
+               if (rx_is_local_readonly_data_p (op))
+                 return op;
+
+               rtx gotoffsym = gen_sym2GOTOFF (op);
+               return gen_rtx_PLUS (Pmode, picreg, gotoffsym);
+             }
+           else
+             {
+               crtl->uses_pic_offset_table = true;
+               rtx gotsym = gen_sym2GOT (op);
+               machine_mode mode = GET_MODE (op) != VOIDmode
+                 ? GET_MODE (op) : Pmode;
+               return gen_rtx_MEM (mode,
+                                   gen_rtx_PLUS (Pmode, picreg, gotsym));
+             }
+         }
+         break;
+       default:
+         /* Nothing do.  */
+         break;
+       }
+    }
   return op;
 }
 
@@ -1010,7 +1356,7 @@ rx_gen_move_template (rtx * operands, bool is_movu)
   else
     {
       /* Otherwise, use the smaller size.  */
-      if (GET_MODE (src) == SIGN_EXTEND || GET_MODE (src) == ZERO_EXTEND)
+      if (GET_CODE (src) == SIGN_EXTEND || GET_CODE (src) == ZERO_EXTEND)
        /* When expanding, the original size will be used. */
        src_mode = GET_MODE (XEXP (src, 0));
       else
@@ -1336,6 +1682,9 @@ rx_conditional_register_usage (void)
 
       using_fixed_regs = use_fixed_regs;
     }
+
+  if (flag_pic)
+    call_used_regs[PIC_REG] = 1;
 }
 
 struct decl_chain
@@ -1531,7 +1880,8 @@ rx_get_stack_layout (unsigned int * lowest,
                 to call-used by rx_conditional_register_usage.  If so then
                 they can be used in the fast interrupt handler without
                 saving them on the stack.  */
-             || (is_fast_interrupt_func (NULL_TREE)
+             || is_fast_interrupt_func (NULL_TREE)
+             || ((flag_pic && reg == PIC_REG)
                  && ! IN_RANGE (reg, 10, 13))))
        {
          if (low == 0)
@@ -1864,6 +2214,14 @@ rx_expand_prologue (void)
                    GEN_INT ((HOST_WIDE_INT) stack_size),
                    true);
     }
+  if (crtl->uses_pic_offset_table && !TARGET_FDPIC)
+    {
+      rtx got_base = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+      rtx picreg = gen_rtx_REG (Pmode, PIC_REG);
+
+      got_base = gen_sym2pcrel (got_base);
+      emit_insn (gen_movsi_pcrel (picreg, got_base));
+    }
 }
 
 static void
@@ -2231,6 +2589,12 @@ rx_expand_epilogue (bool is_sibcall)
                    GEN_INT (total_size), false);
     }
 
+  if (flag_pic)
+    {
+      rtx picreg = gen_rtx_REG (Pmode, PIC_REG);
+      emit_use (picreg);
+    }
+
   if (low)
     emit_jump_insn (gen_pop_and_return (GEN_INT (regs_size),
                                        gen_rx_rtsd_vector (regs_size,
@@ -2714,7 +3078,14 @@ rx_elf_asm_cdtor (rtx symbol, int priority, bool is_ctor)
 
   switch_to_section (s);
   assemble_align (POINTER_SIZE);
-  assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
+  if (!TARGET_FDPIC)
+    assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
+  else
+    {
+      fputs ("\t.long\t", asm_out_file);
+      output_addr_const (asm_out_file, symbol);
+      fputs ("\n", asm_out_file);
+    }
 }
 
 static void
@@ -2865,6 +3236,13 @@ rx_option_override (void)
   if (flag_strict_volatile_bitfields < 0 && abi_version_at_least(2))
     flag_strict_volatile_bitfields = 1;
 
+  /* FDPIC code is a special form of PIC, and the vast majority of code
+     generation constraints that apply to PIC also apply to FDPIC, so we
+     set flag_pic to avoid the need to check TARGET_FDPIC everywhere
+     flag_pic is checked.  */
+  if (TARGET_FDPIC && !flag_pic)
+    flag_pic = 2;
+
   rx_override_options_after_change ();
 
   /* These values are bytes, not log.  */
@@ -2912,7 +3290,7 @@ rx_warn_func_return (tree decl)
 static bool
 rx_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
 {
-  if (TARGET_JSR)
+  if (TARGET_JSR || TARGET_FDPIC)
     return false;
 
   /* Do not allow indirect tailcalls.  The
@@ -2949,6 +3327,25 @@ rx_is_ms_bitfield_layout (const_tree record_type 
ATTRIBUTE_UNUSED)
 bool
 rx_is_legitimate_constant (machine_mode mode ATTRIBUTE_UNUSED, rtx x)
 {
+  if (flag_pic)
+    {
+      if (SYMBOL_REF_P (x) || LABEL_REF_P (x))
+       return false;
+      if (GET_CODE (x) == CONST
+         && GET_CODE (XEXP (x, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (x, 0), 0)) == UNSPEC)
+       {
+         rtx x0 = XVECEXP (XEXP (XEXP (x, 0), 0), 0, 0);
+         if (SYMBOL_REF_P (x0) || LABEL_REF_P (x0))
+           return false;
+       }
+
+      if (GET_CODE (x) == CONST
+         && GET_CODE (XEXP (x, 0)) == UNSPEC
+         && XINT (XEXP (x, 0), 1) == UNSPEC_PCREL)
+       return false;
+    }
+
   switch (GET_CODE (x))
     {
     case CONST:
@@ -2969,10 +3366,28 @@ rx_is_legitimate_constant (machine_mode mode 
ATTRIBUTE_UNUSED, rtx x)
        {
        case LABEL_REF:
        case SYMBOL_REF:
-         return true;
+         return !flag_pic;
 
        case UNSPEC:
-         return XINT (x, 1) == UNSPEC_CONST || XINT (x, 1) == UNSPEC_PID_ADDR;
+         switch (XINT (x, 1))
+           {
+           case UNSPEC_PCREL:
+             {
+               rtx xx = XVECEXP (x, 0, 0);
+               return !(GET_CODE (xx) == CONST || GET_CODE (xx) == UNSPEC);
+             }
+           case UNSPEC_CONST:
+           case UNSPEC_PID_ADDR:
+           case UNSPEC_GOT:
+           case UNSPEC_GOTOFF:
+           case UNSPEC_GOTFUNCDESC:
+           case UNSPEC_GOTOFFFUNCDESC:
+           case UNSPEC_PLT:
+             return true;
+           default:
+             return false;
+           }
+         break;
 
        default:
          /* FIXME: Can this ever happen ?  */
@@ -3663,7 +4078,9 @@ rx_modes_tieable_p (machine_mode mode1, machine_mode 
mode2)
   return ((GET_MODE_CLASS (mode1) == MODE_FLOAT
           || GET_MODE_CLASS (mode1) == MODE_COMPLEX_FLOAT)
          == (GET_MODE_CLASS (mode2) == MODE_FLOAT
-             || GET_MODE_CLASS (mode2) == MODE_COMPLEX_FLOAT));
+             || GET_MODE_CLASS (mode2) == MODE_COMPLEX_FLOAT))
+         || (GET_MODE_CLASS (mode1) == MODE_INT
+             && GET_MODE_CLASS (mode2) == MODE_INT);
 }
 
 /* Implement TARGET_C_MODE_FOR_FLOATING_TYPE.  Return SFmode or DFmode
@@ -3730,6 +4147,113 @@ rx_relax_double_operands(rtx * operands, machine_mode 
mode)
   if (MEM_P (operands[0]) && !REG_P (operands[1]))
     operands[1] = force_reg (mode, operands[1]);
 }
+
+/* Implement LEGITIMATE_PIC_OPERAND_P.  */
+bool
+rx_legitimate_pic_operand_p (rtx x)
+{
+  return (! nonpic_symbol_mentioned_p (x)
+         && GET_CODE (x) != SYMBOL_REF);
+}
+
+/* Return an rtx holding the initial value of the FDPIC register (the
+   FDPIC pointer passed in from the caller).  */
+rtx
+rx_get_fdpic_reg_initial_val (void)
+{
+  return get_hard_reg_initial_val (Pmode, PIC_REG);
+}
+
+/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA.  */
+static bool
+rx_asm_output_addr_const_extra (FILE *file ATTRIBUTE_UNUSED,
+                               rtx x ATTRIBUTE_UNUSED)
+{
+  return false;
+}
+
+/* Implement TARGET_CONST_NOT_OK_FOR_DEBUG_P.  */
+static bool
+rx_const_not_ok_for_debug_p (rtx p)
+{
+  return (GET_CODE (p) == SYMBOL_REF);
+}
+
+/* Implement TARGET_CANNOT_FORCE_CONST_MEM.  */
+bool
+rx_cannot_force_const_mem_p (machine_mode mode ATTRIBUTE_UNUSED,
+                            rtx x ATTRIBUTE_UNUSED)
+{
+  return flag_pic;
+}
+
+/* Determine whether x is a valid UNSPEC as an offset.  */
+bool
+rx_is_unspec_offset (rtx x)
+{
+  if (!flag_pic
+      || GET_CODE (x) != CONST
+      || GET_CODE (XEXP (x, 0)) != UNSPEC)
+    return false;
+
+  switch (XINT (XEXP (x, 0), 1))
+    {
+    case UNSPEC_PID_ADDR:
+    case UNSPEC_GOTOFF:
+    case UNSPEC_GOTOFFFUNCDESC:
+    case UNSPEC_PCREL:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Implement TARGET_DELEGITIMIZE_ADDRESS.  */
+static rtx
+rx_delegitimize_address (rtx x)
+{
+  x = delegitimize_mem_from_attrs (x);
+  if (rx_pcrel_label_operand (x, Pmode))
+    return XVECEXP (XEXP (x, 0), 0, 0);
+
+  return x;
+}
+
+/* expand for pc relative address load.  */
+bool
+rx_expand_pcrel (rtx operands[], int sidx)
+{
+  rtx src = operands[sidx];
+  rtx dest = operands[0];
+  rtx pcrel_src = NULL_RTX;
+
+  if (!flag_pic)
+    return false;
+
+  if (rx_pcrel_label_operand (src, Pmode))
+    src = gen_rtx_CONST (Pmode,
+                        gen_rtx_UNSPEC (Pmode, gen_rtvec (1, src),
+                                        UNSPEC_PCREL));
+  if (CONSTANT_P (src))
+    {
+      rtx inner = (GET_CODE (src) == CONST)
+       ? XEXP (src, 0) : src;
+      if (GET_CODE (inner) == UNSPEC && XINT (inner, 1) == UNSPEC_PCREL)
+       pcrel_src = XVECEXP (inner, 0, 0);
+    }
+
+  if (pcrel_src != NULL_RTX)
+    {
+      if (!REG_P (dest))
+       dest = gen_reg_rtx (Pmode);
+      emit_insn (gen_movsi_pcrel (dest, pcrel_src));
+      if (dest != operands[0])
+       emit_move_insn (operands[0], dest);
+      return true;
+    }
+  return false;
+}
+
 
 
 #undef  TARGET_NARROW_VOLATILE_BITFIELD
@@ -3896,6 +4420,18 @@ rx_relax_double_operands(rtx * operands, machine_mode 
mode)
 #undef TARGET_DOCUMENTATION_NAME
 #define TARGET_DOCUMENTATION_NAME "RX"
 
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA rx_asm_output_addr_const_extra
+
+#undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
+#define TARGET_CONST_NOT_OK_FOR_DEBUG_P        rx_const_not_ok_for_debug_p
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM rx_cannot_force_const_mem_p
+
+#undef  TARGET_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS rx_delegitimize_address
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-rx.h"
-- 
2.47.3

Reply via email to