OSDN Git Service

Rewrite PIC support to more closely model actual instructions.
authorhp <hp@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 9 Jul 2005 01:09:48 +0000 (01:09 +0000)
committerhp <hp@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 9 Jul 2005 01:09:48 +0000 (01:09 +0000)
* config/cris/cris-protos.h (cris_gotless_symbol, cris_got_symbol)
(cris_symbol): Remove prototypes for removed functions.
(cris_pic_symbol_type_of, cris_valid_pic_const)
(cris_expand_pic_call_address): Prototypes for new functions.
* config/cris/cris/cris.c (cris_pic_sympart_only): Remove unused
variable.
(cris_print_operand) <case 'v', 'P'>: Remove cases for unused
modifiers.
<case ':'>: Add case for new punctuation character.
<case 'd'>: Temporarily set flag_pic = 2 instead of incorrectly
emitting (extra) PIC modifier.
<case UNSPEC>: Do not assert for PLT.
(cris_initial_frame_pointer_offset, cris_simple_epilogue)
(cris_expand_prologue, cris_expand_epilogue): Check
for pic_offset_table_rtx usage instead of taking
current_function_uses_pic_offset_table as the final word.
(cris_rtx_costs, cris_address_cost, cris_side_effect_mode_ok):
Remove flag_pic difference.
(cris_valid_pic_const, cris_pic_symbol_type_of): New functions,
the moral equivalents of...
(cris_symbol, cris_gotless_symbol, cris_got_symbol): Remove
functions.
(cris_legitimate_pic_operand): Just call cris_valid_pic_const.
(cris_handle_option): Mark ARG as unused.
(cris_expand_pic_call_address): New worker function for "call",
"call_value".
(cris_asm_output_symbol_ref, cris_asm_output_label_ref): Do not
output PIC constructs here.
(cris_output_addr_const_extra): Changes for emitting PIC modifiers
as symbol-specific modifers, not whole or part of operands.
* config/cris/cris/cris.h (EXTRA_CONSTRAINT): Remove 'U' case.
(EXTRA_CONSTRAINT_S): Changed semantics: allow only CONST-wrapped
constants and flag_pic.
(CONSTANT_INDEX_P): Adjust for new functions.
(enum cris_pic_symbol_type): New helper type.
(PRINT_OPERAND_PUNCT_VALID_P): Add ':'.
* config/cris/cris/cris.md (CRIS_UNSPEC_GOTREL)
(CRIS_UNSPEC_GOTREAD, CRIS_UNSPEC_PLTGOTREAD): New
define_constants.
("movsi"): Emit actual instructions for GOT and relative access.
("*movsi_got_load"): New pattern to set up the register holding
the GOT pointer.
("*movsi_internal"): Operand 1 is not a plain general_operand.
Adjust FIXME for 'S'.
<output for 'S' alternative>: Sanity-check UNSPEC types for PIC.
Use "movs" for -fpic cases.
("addsi3"): Add alternative for 'S'; use adds.w when possible.
("uminsi3","*expanded_call_value"): Remove 'S' alternative.
("call", "call_value"): Just call cris_expand_pic_call_address for
PIC addresses.
("*expanded_call_no_gotplt", "*expanded_call_value_no_gotplt"):
Remove special pattern.
("*expanded_call_side", "*expanded_call_value_side"): New
patterns.
(gotplt-to-plt, gotplt-to-plt-side-call)
(gotplt-to-plt-side-call-value, gotplt-to-plt-side): New
peephole2:s.
* config/cris/cris/predicates.md
("cris_general_operand_or_gotless_symbol"): Remove unused
predicate.
("cris_general_operand_or_symbol"): Adjust for new functions.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@101812 138bc75d-0d04-0410-961f-82ee72b054a4

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

index 94b1f25..6c5e2bb 100644 (file)
@@ -1,3 +1,68 @@
+2005-07-08  Hans-Peter Nilsson  <hp@axis.com>
+
+       Rewrite PIC support to more closely model actual instructions.
+       * config/cris/cris-protos.h (cris_gotless_symbol, cris_got_symbol)
+       (cris_symbol): Remove prototypes for removed functions.
+       (cris_pic_symbol_type_of, cris_valid_pic_const)
+       (cris_expand_pic_call_address): Prototypes for new functions.
+       * config/cris/cris/cris.c (cris_pic_sympart_only): Remove unused
+       variable.
+       (cris_print_operand) <case 'v', 'P'>: Remove cases for unused
+       modifiers.
+       <case ':'>: Add case for new punctuation character.
+       <case 'd'>: Temporarily set flag_pic = 2 instead of incorrectly
+       emitting (extra) PIC modifier.
+       <case UNSPEC>: Do not assert for PLT.
+       (cris_initial_frame_pointer_offset, cris_simple_epilogue)
+       (cris_expand_prologue, cris_expand_epilogue): Check
+       for pic_offset_table_rtx usage instead of taking
+       current_function_uses_pic_offset_table as the final word.
+       (cris_rtx_costs, cris_address_cost, cris_side_effect_mode_ok):
+       Remove flag_pic difference.
+       (cris_valid_pic_const, cris_pic_symbol_type_of): New functions,
+       the moral equivalents of...
+       (cris_symbol, cris_gotless_symbol, cris_got_symbol): Remove
+       functions.
+       (cris_legitimate_pic_operand): Just call cris_valid_pic_const.
+       (cris_handle_option): Mark ARG as unused.
+       (cris_expand_pic_call_address): New worker function for "call",
+       "call_value".
+       (cris_asm_output_symbol_ref, cris_asm_output_label_ref): Do not
+       output PIC constructs here.
+       (cris_output_addr_const_extra): Changes for emitting PIC modifiers
+       as symbol-specific modifers, not whole or part of operands.
+       * config/cris/cris/cris.h (EXTRA_CONSTRAINT): Remove 'U' case.
+       (EXTRA_CONSTRAINT_S): Changed semantics: allow only CONST-wrapped
+       constants and flag_pic.
+       (CONSTANT_INDEX_P): Adjust for new functions.
+       (enum cris_pic_symbol_type): New helper type.
+       (PRINT_OPERAND_PUNCT_VALID_P): Add ':'.
+       * config/cris/cris/cris.md (CRIS_UNSPEC_GOTREL)
+       (CRIS_UNSPEC_GOTREAD, CRIS_UNSPEC_PLTGOTREAD): New
+       define_constants.
+       ("movsi"): Emit actual instructions for GOT and relative access.
+       ("*movsi_got_load"): New pattern to set up the register holding
+       the GOT pointer.
+       ("*movsi_internal"): Operand 1 is not a plain general_operand.
+       Adjust FIXME for 'S'.
+       <output for 'S' alternative>: Sanity-check UNSPEC types for PIC.
+       Use "movs" for -fpic cases.
+       ("addsi3"): Add alternative for 'S'; use adds.w when possible.
+       ("uminsi3","*expanded_call_value"): Remove 'S' alternative.
+       ("call", "call_value"): Just call cris_expand_pic_call_address for
+       PIC addresses.
+       ("*expanded_call_no_gotplt", "*expanded_call_value_no_gotplt"):
+       Remove special pattern.
+       ("*expanded_call_side", "*expanded_call_value_side"): New
+       patterns.
+       (gotplt-to-plt, gotplt-to-plt-side-call)
+       (gotplt-to-plt-side-call-value, gotplt-to-plt-side): New
+       peephole2:s.
+       * config/cris/cris/predicates.md
+       ("cris_general_operand_or_gotless_symbol"): Remove unused
+       predicate.
+       ("cris_general_operand_or_symbol"): Adjust for new functions.
+
 2005-07-08  Andrew Pinski  <pinskia@physics.uc.edu>
 
        * config/darwin.h (TARGET_C99_FUNCTIONS): Define to 1.
index 8a7335c..f95a5d1 100644 (file)
@@ -38,9 +38,8 @@ extern int cris_side_effect_mode_ok (enum rtx_code, rtx *, int, int,
 extern rtx cris_return_addr_rtx (int, rtx);
 extern rtx cris_split_movdx (rtx *);
 extern int cris_legitimate_pic_operand (rtx);
-extern int cris_gotless_symbol (rtx);
-extern int cris_got_symbol (rtx);
-extern int cris_symbol (rtx);
+extern enum cris_pic_symbol_type cris_pic_symbol_type_of (rtx);
+extern bool cris_valid_pic_const (rtx);
 extern bool cris_store_multiple_op_p (rtx);
 extern bool cris_movem_load_rest_p (rtx, int);
 extern void cris_asm_output_symbol_ref (FILE *, rtx);
@@ -48,6 +47,7 @@ extern bool cris_output_addr_const_extra (FILE *, rtx);
 extern int cris_cfun_uses_pic_table (void);
 extern rtx cris_gen_movem_load (rtx, rtx, int);
 extern rtx cris_emit_movem_store (rtx, rtx, int, bool);
+extern void cris_expand_pic_call_address (rtx *);
 #endif /* RTX_CODE */
 extern void cris_asm_output_label_ref (FILE *, char *);
 extern void cris_target_asm_named_section (const char *, unsigned int, tree);
index ac55717..4ce3c8b 100644 (file)
@@ -82,12 +82,6 @@ struct machine_function GTY(())
    pattern.  */
 static char cris_output_insn_is_bound = 0;
 
-/* This one suppresses printing out the "rPIC+" in
-   "rPIC+sym:GOTOFF+offset" when doing PIC.  For a PLT symbol, it
-   suppresses outputting it as [rPIC+sym:GOTPLT] and outputs similarly
-   just the "sym:GOTOFF" part.  */
-static int cris_pic_sympart_only = 0;
-
 /* In code for output macros, this is how we know whether e.g. constant
    goes in code or in a static initializer.  */
 static int in_code = 0;
@@ -686,15 +680,6 @@ cris_print_operand (FILE *file, rtx x, int code)
       fprintf (file, "%s", cris_op_str (operand));
       return;
 
-    case 'v':
-      /* Print the operand without the PIC register.  */
-      if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x))
-       LOSE_AND_RETURN ("invalid operand for 'v' modifier", x);
-      cris_pic_sympart_only++;
-      cris_output_addr_const (file, x);
-      cris_pic_sympart_only--;
-      return;
-
     case 'o':
       {
        /* A movem modifier working on a parallel; output the register
@@ -751,14 +736,6 @@ cris_print_operand (FILE *file, rtx x, int code)
       }
       return;
 
-    case 'P':
-      /* Print the PIC register.  Applied to a GOT-less PIC symbol for
-         sanity.  */
-      if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x))
-       LOSE_AND_RETURN ("invalid operand for 'P' modifier", x);
-      fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-      return;
-
     case 'p':
       /* Adjust a power of two to its log2.  */
       if (GET_CODE (x) != CONST_INT || exact_log2 (INTVAL (x)) < 0 )
@@ -829,6 +806,13 @@ cris_print_operand (FILE *file, rtx x, int code)
               : ".p2alignw 5,0x050f,2\n\t", file);
       return;
 
+    case ':':
+      /* The PIC register.  */
+      if (! flag_pic)
+       internal_error ("invalid use of ':' modifier");
+      fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]);
+      return;
+
     case 'H':
       /* Print high (most significant) part of something.  */
       switch (GET_CODE (operand))
@@ -939,11 +923,16 @@ cris_print_operand (FILE *file, rtx x, int code)
       return;
 
     case 'd':
-      /* If this is a GOT symbol, print it as :GOT regardless of -fpic.  */
-      if (flag_pic && CONSTANT_P (operand) && cris_got_symbol (operand))
+      /* If this is a GOT symbol, force it to be emitted as :GOT and
+        :GOTPLT regardless of -fpic (i.e. not as :GOT16, :GOTPLT16).
+        Avoid making this too much of a special case.  */
+      if (flag_pic == 1 && CONSTANT_P (operand))
        {
+         int flag_pic_save = flag_pic;
+
+         flag_pic = 2;
          cris_output_addr_const (file, operand);
-         fprintf (file, ":GOT");
+         flag_pic = flag_pic_save;
          return;
        }
       break;
@@ -1015,9 +1004,7 @@ cris_print_operand (FILE *file, rtx x, int code)
       return;
 
     case UNSPEC:
-      ASSERT_PLT_UNSPEC (operand);
       /* Fall through.  */
-
     case CONST:
       cris_output_addr_const (file, operand);
       return;
@@ -1153,7 +1140,16 @@ cris_initial_frame_pointer_offset (void)
 
   /* Initial offset is 0 if we don't have a frame pointer.  */
   int offs = 0;
-  bool got_really_used = current_function_uses_pic_offset_table;
+  bool got_really_used = false;
+
+  if (current_function_uses_pic_offset_table)
+    {
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (),
+                             NULL_RTX);
+      pop_topmost_sequence ();
+    }
 
   /* And 4 for each register pushed.  */
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
@@ -1485,7 +1481,7 @@ cris_simple_epilogue (void)
 {
   unsigned int regno;
   unsigned int reglimit = STACK_POINTER_REGNUM;
-  bool got_really_used = current_function_uses_pic_offset_table;
+  bool got_really_used = false;
 
   if (! reload_completed
       || frame_pointer_needed
@@ -1500,6 +1496,14 @@ cris_simple_epilogue (void)
       || !TARGET_PROLOGUE_EPILOGUE)
     return false;
 
+  if (current_function_uses_pic_offset_table)
+    {
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
+      pop_topmost_sequence ();
+    }
+
   /* No simple epilogue if there are saved registers.  */
   for (regno = 0; regno < reglimit; regno++)
     if (cris_reg_saved_in_regsave_area (regno, got_really_used))
@@ -1561,18 +1565,7 @@ cris_rtx_costs (rtx x, int code, int outer_code, int *total)
 
     case CONST:
     case SYMBOL_REF:
-      /* For PIC, we need a prefix (if it isn't already there),
-        and the PIC register.  For a global PIC symbol, we also
-        need a read of the GOT.  */
-      if (flag_pic)
-       {
-         if (cris_got_symbol (x))
-           *total = 2 + 4 + 6;
-         else
-           *total = 2 + 6;
-       }
-      else
-       *total = 6;
+      *total = 6;
       return true;
 
     case CONST_DOUBLE:
@@ -1657,12 +1650,9 @@ cris_address_cost (rtx x)
     return (2 + 4) / 2;
 
   /* Assume (2 + 4) / 2 for a single constant; a dword, since it needs
-     an extra DIP prefix and 4 bytes of constant in most cases.
-     For PIC and a symbol with a GOT entry, we double the cost since we
-     add a [rPIC+...] offset.  A GOT-less symbol uses a BDAP prefix
-     equivalent to the DIP prefix for non-PIC, hence the same cost.  */
+     an extra DIP prefix and 4 bytes of constant in most cases.  */
   if (CONSTANT_P (x))
-    return flag_pic && cris_got_symbol (x) ? 2 * (2 + 4) / 2 : (2 + 4) / 2;
+    return (2 + 4) / 2;
 
   /* Handle BIAP and BDAP prefixes.  */
   if (GET_CODE (x) == PLUS)
@@ -1789,10 +1779,9 @@ cris_side_effect_mode_ok (enum rtx_code code, rtx *ops,
          && (INTVAL (val_rtx) <= 63 && INTVAL (val_rtx) >= -63))
        return 0;
 
-      /* Check allowed cases, like [r(+)?].[bwd] and const.
-        A symbol is not allowed with PIC.  */
+      /* Check allowed cases, like [r(+)?].[bwd] and const.  */
       if (CONSTANT_P (val_rtx))
-       return flag_pic == 0 || cris_symbol (val_rtx) == 0;
+       return 1;
 
       if (GET_CODE (val_rtx) == MEM
          && BASE_OR_AUTOINCR_P (XEXP (val_rtx, 0)))
@@ -1861,162 +1850,104 @@ cris_target_asm_named_section (const char *name, unsigned int flags,
     default_elf_asm_named_section (name, flags, decl);
 }
 
-/* The LEGITIMATE_PIC_OPERAND_P worker.  */
+/* Return TRUE iff X is a CONST valid for e.g. indexing.  */
 
-int
-cris_legitimate_pic_operand (rtx x)
+bool
+cris_valid_pic_const (rtx x)
 {
-  /* The PIC representation of a symbol with a GOT entry will be (for
-     example; relocations differ):
-      sym => [rPIC+sym:GOT]
-     and for a GOT-less symbol it will be (for example, relocation differ):
-      sym => rPIC+sym:GOTOFF
-     so only a symbol with a GOT is by itself a valid operand, and it
-     can't be a sum of a symbol and an offset.  */
-  return ! cris_symbol (x) || cris_got_symbol (x);
-}
-
-/* Return nonzero if there's a SYMBOL_REF or LABEL_REF hiding inside this
-   CONSTANT_P.  */
+  gcc_assert (flag_pic);
 
-int
-cris_symbol (rtx x)
-{
   switch (GET_CODE (x))
     {
-    case SYMBOL_REF:
-    case LABEL_REF:
-      return 1;
-
-    case UNSPEC:
-      if (XINT (x, 1) == CRIS_UNSPEC_GOT || XINT (x, 1) != CRIS_UNSPEC_PLT)
-       return 0;
-      /* A PLT reference.  */
-      ASSERT_PLT_UNSPEC (x);
-      return 1;
-
-    case CONST:
-      return cris_symbol (XEXP (x, 0));
-
-    case PLUS:
-    case MINUS:
-      return cris_symbol (XEXP (x, 0)) || cris_symbol (XEXP (x, 1));
-
     case CONST_INT:
     case CONST_DOUBLE:
-      return 0;
-
+      return true;
     default:
-      fatal_insn ("unrecognized supposed constant", x);
+      ;
     }
 
-  return 1;
+  if (GET_CODE (x) != CONST)
+    return false;
+
+  x = XEXP (x, 0);
+
+  /* Handle (const (plus (unspec .. UNSPEC_GOTREL) (const_int ...))).  */
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 0)) == UNSPEC
+      && XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL
+      && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    x = XEXP (x, 0);
+
+  if (GET_CODE (x) == UNSPEC)
+    switch (XINT (x, 1))
+      {
+      case CRIS_UNSPEC_PLT:
+      case CRIS_UNSPEC_PLTGOTREAD:
+      case CRIS_UNSPEC_GOTREAD:
+      case CRIS_UNSPEC_GOTREL:
+       return true;
+      default:
+       gcc_unreachable ();
+      }
+
+  return cris_pic_symbol_type_of (x) == cris_no_symbol;
 }
 
-/* Return nonzero if there's a SYMBOL_REF or LABEL_REF hiding inside this
-   CONSTANT_P, and the symbol does not need a GOT entry.  Also set
-   current_function_uses_pic_offset_table if we're generating PIC and ever
-   see something that would need one.  */
+/* Helper function to find the right PIC-type symbol to generate,
+   given the original (non-PIC) representation.  */
 
-int
-cris_gotless_symbol (rtx x)
+enum cris_pic_symbol_type
+cris_pic_symbol_type_of (rtx x)
 {
-  CRIS_ASSERT (flag_pic);
-
   switch (GET_CODE (x))
     {
-    case UNSPEC:
-      if (XINT (x, 1) == CRIS_UNSPEC_GOT)
-       return 1;
-      if (XINT (x, 1) != CRIS_UNSPEC_PLT)
-       return 0;
-      ASSERT_PLT_UNSPEC (x);
-      return 1;
-
     case SYMBOL_REF:
-      if (cfun != NULL)
-       current_function_uses_pic_offset_table = 1;
-      return SYMBOL_REF_LOCAL_P (x);
+      return SYMBOL_REF_LOCAL_P (x)
+       ? cris_gotrel_symbol : cris_got_symbol;
 
     case LABEL_REF:
-      /* We don't set current_function_uses_pic_offset_table for
-        LABEL_REF:s in here, since they are almost always originating
-        from some branch.  The only time it does not come from a label is
-        when GCC does something like __builtin_setjmp.  Then we get the
-        LABEL_REF from the movsi expander, so we mark it there as a
-        special case.  */
-      return 1;
+      return cris_gotrel_symbol;
 
     case CONST:
-      return cris_gotless_symbol (XEXP (x, 0));
+      return cris_pic_symbol_type_of (XEXP (x, 0));
 
     case PLUS:
     case MINUS:
       {
-       int x0 = cris_gotless_symbol (XEXP (x, 0)) != 0;
-       int x1 = cris_gotless_symbol (XEXP (x, 1)) != 0;
-
-       /* One and only one of them must be a local symbol.  Neither must
-          be some other, more general kind of symbol.  */
-       return
-         (x0 ^ x1)
-         && ! (x0 == 0 && cris_symbol (XEXP (x, 0)))
-         && ! (x1 == 0 && cris_symbol (XEXP (x, 1)));
+       enum cris_pic_symbol_type t1 = cris_pic_symbol_type_of (XEXP (x, 0));
+       enum cris_pic_symbol_type t2 = cris_pic_symbol_type_of (XEXP (x, 1));
+
+       gcc_assert (t1 == cris_no_symbol || t2 == cris_no_symbol);
+
+       if (t1 == cris_got_symbol || t1 == cris_got_symbol)
+         return cris_got_symbol_needing_fixup;
+
+       return t1 != cris_no_symbol ? t1 : t2;
       }
 
     case CONST_INT:
     case CONST_DOUBLE:
-      return 0;
+      return cris_no_symbol;
+
+    case UNSPEC:
+      /* Likely an offsettability-test attempting to add a constant to
+        a GOTREAD symbol, which can't be handled.  */
+      return cris_invalid_pic_symbol;
 
     default:
       fatal_insn ("unrecognized supposed constant", x);
     }
 
-  return 1;
+  gcc_unreachable ();
 }
 
-/* Return nonzero if there's a SYMBOL_REF or LABEL_REF hiding inside this
-   CONSTANT_P, and the symbol needs a GOT entry.  */
+/* The LEGITIMATE_PIC_OPERAND_P worker.  */
 
 int
-cris_got_symbol (rtx x)
+cris_legitimate_pic_operand (rtx x)
 {
-  CRIS_ASSERT (flag_pic);
-
-  switch (GET_CODE (x))
-    {
-    case UNSPEC:
-      if (XINT (x, 1) == CRIS_UNSPEC_GOT)
-       return 0;
-      ASSERT_PLT_UNSPEC (x);
-      return 0;
-
-    case SYMBOL_REF:
-      if (cfun != NULL)
-       current_function_uses_pic_offset_table = 1;
-      return ! SYMBOL_REF_LOCAL_P (x);
-
-    case CONST:
-      return cris_got_symbol (XEXP (x, 0));
-
-    case LABEL_REF:
-      /* A LABEL_REF is never visible as a symbol outside the local
-         function.  */
-    case PLUS:
-    case MINUS:
-      /* Nope, can't access the GOT for "symbol + offset".  */
-      return 0;
-
-    case CONST_INT:
-    case CONST_DOUBLE:
-      return 0;
-
-    default:
-      fatal_insn ("unrecognized supposed constant in cris_global_pic_symbol",
-                 x);
-    }
-
-  return 1;
+  /* Symbols are not valid PIC operands as-is; just constants.  */
+  return cris_valid_pic_const (x);
 }
 
 /* TARGET_HANDLE_OPTION worker.  We just store the values into local
@@ -2024,7 +1955,8 @@ cris_got_symbol (rtx x)
    cris_override_options.  */
 
 static bool
-cris_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+cris_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
+                   int value ATTRIBUTE_UNUSED)
 {
   switch (code)
     {
@@ -2441,7 +2373,7 @@ cris_expand_prologue (void)
   int framesize = 0;
   rtx mem, insn;
   int return_address_on_stack = cris_return_address_on_stack ();
-  int got_really_used = current_function_uses_pic_offset_table;
+  int got_really_used = false;
   int n_movem_regs = 0;
   int pretend = current_function_pretend_args_size;
 
@@ -2451,6 +2383,17 @@ cris_expand_prologue (void)
 
   CRIS_ASSERT (size >= 0);
 
+  if (current_function_uses_pic_offset_table)
+    {
+      /* A reference may have been optimized out (like the abort () in
+        fde_split in unwind-dw2-fde.c, at least 3.2.1) so check that
+        it's still used.  */
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
+      pop_topmost_sequence ();
+    }
+
   /* Align the size to what's best for the CPU model.  */
   if (TARGET_STACK_ALIGN)
     size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
@@ -2713,12 +2656,23 @@ cris_expand_epilogue (void)
   /* A reference may have been optimized out
      (like the abort () in fde_split in unwind-dw2-fde.c, at least 3.2.1)
      so check that it's still used.  */
-  int got_really_used = current_function_uses_pic_offset_table;
+  int got_really_used = false;
   int n_movem_regs = 0;
 
   if (!TARGET_PROLOGUE_EPILOGUE)
     return;
 
+  if (current_function_uses_pic_offset_table)
+    {
+      /* A reference may have been optimized out (like the abort () in
+        fde_split in unwind-dw2-fde.c, at least 3.2.1) so check that
+        it's still used.  */
+      push_topmost_sequence ();
+      got_really_used
+       = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
+      pop_topmost_sequence ();
+    }
+
   /* Align byte count of stack frame.  */
   if (TARGET_STACK_ALIGN)
     size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
@@ -3059,6 +3013,93 @@ cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment,
   return insn;
 }
 
+/* Worker function for expanding the address for PIC function calls.  */
+
+void
+cris_expand_pic_call_address (rtx *opp)
+{
+  rtx op = *opp;
+
+  gcc_assert (MEM_P (op));
+  op = XEXP (op, 0);
+
+  /* It might be that code can be generated that jumps to 0 (or to a
+     specific address).  Don't die on that.  (There is a
+     testcase.)  */
+  if (CONSTANT_ADDRESS_P (op) && GET_CODE (op) != CONST_INT)
+    {
+      enum cris_pic_symbol_type t = cris_pic_symbol_type_of (op);
+
+      CRIS_ASSERT (!no_new_pseudos);
+
+      /* For local symbols (non-PLT), just get the plain symbol
+        reference into a register.  For symbols that can be PLT, make
+        them PLT.  */
+      if (t == cris_gotrel_symbol)
+       op = force_reg (Pmode, op);
+      else if (t == cris_got_symbol)
+       {
+         if (TARGET_AVOID_GOTPLT)
+           {
+             /* Change a "jsr sym" into (allocate register rM, rO)
+                "move.d (const (unspec [sym] CRIS_UNSPEC_PLT)),rM"
+                "add.d rPIC,rM,rO", "jsr rO".  */
+             rtx tem, rm, ro;
+             gcc_assert (! no_new_pseudos);
+             current_function_uses_pic_offset_table = 1;
+             tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), CRIS_UNSPEC_PLT);
+             rm = gen_reg_rtx (Pmode);
+             emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
+             ro = gen_reg_rtx (Pmode);
+             if (expand_binop (Pmode, add_optab, rm,
+                               pic_offset_table_rtx,
+                               ro, 0, OPTAB_LIB_WIDEN) != ro)
+               internal_error ("expand_binop failed in movsi got");
+             op = ro;
+           }
+         else
+           {
+             /* Change a "jsr sym" into (allocate register rM, rO)
+                "move.d (const (unspec [sym] CRIS_UNSPEC_PLTGOT)),rM"
+                "add.d rPIC,rM,rO" "jsr [rO]" with the memory access
+                marked as not trapping and not aliasing.  No "move.d
+                [rO],rP" as that would invite to re-use of a value
+                that should not be reused.  FIXME: Need a peephole2
+                for cases when this is cse:d from the call, to change
+                back to just get the PLT entry address, so we don't
+                resolve the same symbol over and over (the memory
+                access of the PLTGOT isn't constant).  */
+             rtx tem, mem, rm, ro;
+
+             gcc_assert (! no_new_pseudos);
+             current_function_uses_pic_offset_table = 1;
+             tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op),
+                                   CRIS_UNSPEC_PLTGOTREAD);
+             rm = gen_reg_rtx (Pmode);
+             emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
+             ro = gen_reg_rtx (Pmode);
+             if (expand_binop (Pmode, add_optab, rm,
+                               pic_offset_table_rtx,
+                               ro, 0, OPTAB_LIB_WIDEN) != ro)
+               internal_error ("expand_binop failed in movsi got");
+             mem = gen_rtx_MEM (Pmode, ro);
+
+             /* This MEM doesn't alias anything.  Whether it aliases
+                other same symbols is unimportant.  */
+             set_mem_alias_set (mem, new_alias_set ());
+             MEM_NOTRAP_P (mem) = 1;
+             op = mem;
+           }
+       }
+      else
+       /* Can't possibly get a GOT-needing-fixup for a function-call,
+          right?  */
+       fatal_insn ("Unidentifiable call op", op);
+
+      *opp = replace_equiv_address (*opp, op);
+    }
+}
+
 /* Use from within code, from e.g. PRINT_OPERAND and
    PRINT_OPERAND_ADDRESS.  Macros used in output_addr_const need to emit
    different things depending on whether code operand or constant is
@@ -3077,38 +3118,18 @@ cris_output_addr_const (FILE *file, rtx x)
 void
 cris_asm_output_symbol_ref (FILE *file, rtx x)
 {
+  gcc_assert (GET_CODE (x) == SYMBOL_REF);
+
   if (flag_pic && in_code > 0)
     {
-      const char *origstr = XSTR (x, 0);
-      const char *str;
-
-      str = (* targetm.strip_name_encoding) (origstr);
-
-      if (cris_gotless_symbol (x))
-       {
-         if (! cris_pic_sympart_only)
-           fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-         assemble_name (file, str);
-         fprintf (file, ":GOTOFF");
-       }
-      else if (cris_got_symbol (x))
-       {
-         CRIS_ASSERT (!cris_pic_sympart_only);
-
-         fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-         assemble_name (file, XSTR (x, 0));
-
-         if (flag_pic == 1)
-           fprintf (file, ":GOT16]");
-         else
-           fprintf (file, ":GOT]");
-       }
-      else
-       LOSE_AND_RETURN ("unexpected PIC symbol", x);
-
-      /* Sanity check.  */
-      if (! current_function_uses_pic_offset_table)
-       output_operand_lossage ("PIC register isn't set up");
+     const char *origstr = XSTR (x, 0);
+     const char *str;
+     str = (* targetm.strip_name_encoding) (origstr);
+     assemble_name (file, str);
+
+     /* Sanity check.  */
+     if (! current_function_uses_pic_offset_table)
+       output_operand_lossage ("PIC register isn't set up");
     }
   else
     assemble_name (file, XSTR (x, 0));
@@ -3121,12 +3142,8 @@ cris_asm_output_label_ref (FILE *file, char *buf)
 {
   if (flag_pic && in_code > 0)
     {
-      if (! cris_pic_sympart_only)
-       fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
       assemble_name (file, buf);
 
-      fprintf (file, ":GOTOFF");
-
       /* Sanity check.  */
       if (! current_function_uses_pic_offset_table)
        internal_error ("emitting PIC operand, but PIC register isn't set up");
@@ -3138,34 +3155,44 @@ cris_asm_output_label_ref (FILE *file, char *buf)
 /* Worker function for OUTPUT_ADDR_CONST_EXTRA.  */
 
 bool
-cris_output_addr_const_extra (FILE *file, rtx x)
+cris_output_addr_const_extra (FILE *file, rtx xconst)
 {
-  switch (GET_CODE (x))
+  switch (GET_CODE (xconst))
     {
-      const char *origstr;
-      const char *str;
+      rtx x;
 
     case UNSPEC:
-      ASSERT_PLT_UNSPEC (x);
-      x = XVECEXP (x, 0, 0);
-      origstr = XSTR (x, 0);
-      str = (* targetm.strip_name_encoding) (origstr);
-      if (cris_pic_sympart_only)
+      x = XVECEXP (xconst, 0, 0);
+      CRIS_ASSERT (GET_CODE (x) == SYMBOL_REF
+                  || GET_CODE (x) == LABEL_REF
+                  || GET_CODE (x) == CONST);
+      output_addr_const (file, x);
+      switch (XINT (xconst, 1))
        {
-         assemble_name (file, str);
+       case CRIS_UNSPEC_PLT:
          fprintf (file, ":PLTG");
-       }
-      else
-       {
-         CRIS_ASSERT (!TARGET_AVOID_GOTPLT);
+         break;
 
-         fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
-         assemble_name (file, XSTR (x, 0));
+       case CRIS_UNSPEC_GOTREL:
+         fprintf (file, ":GOTOFF");
+         break;
 
+       case CRIS_UNSPEC_GOTREAD:
          if (flag_pic == 1)
-           fprintf (file, ":GOTPLT16]");
+           fprintf (file, ":GOT16");
          else
-           fprintf (file, ":GOTPLT]");
+           fprintf (file, ":GOT");
+         break;
+
+       case CRIS_UNSPEC_PLTGOTREAD:
+         if (flag_pic == 1)
+           fprintf (file, CRIS_GOTPLT_SUFFIX "16");
+         else
+           fprintf (file, CRIS_GOTPLT_SUFFIX);
+         break;
+
+       default:
+         gcc_unreachable ();
        }
       return true;
 
index a7305dd..0ca52da 100644 (file)
@@ -634,8 +634,6 @@ enum reg_class
   (C) == 'S' ? EXTRA_CONSTRAINT_S (X) :                \
   /* A three-address addressing-mode?  */      \
   (C) == 'T' ? EXTRA_CONSTRAINT_T (X) :                \
-  /* A global PIC symbol?  */                  \
-  (C) == 'U' ? EXTRA_CONSTRAINT_U (X) :                \
   0)
 
 #define EXTRA_MEMORY_CONSTRAINT(X, STR) ((X) == 'Q')
@@ -685,16 +683,9 @@ enum reg_class
                  && BIAP_INDEX_P (XEXP (XEXP (X, 0), 0))))))           \
  )
 
-/* We're kind of out of constraints, so we use "S" for both gotless
-   symbols and the GOT-address load.  Both must go in a general register
-   only: for pre-V32, arithmetic is done on the destination.  */
+/* PIC-constructs for symbols.  */
 #define EXTRA_CONSTRAINT_S(X)                                          \
- (flag_pic                                                             \
-  && ((CONSTANT_P (X) && cris_gotless_symbol (X))                      \
-      || (GET_CODE (X) == UNSPEC && XINT ((X), 1) == CRIS_UNSPEC_GOT)))
-
-#define EXTRA_CONSTRAINT_U(X) \
- (flag_pic && CONSTANT_P (X) && cris_got_symbol (X))
+ (flag_pic && GET_CODE (X) == CONST && cris_valid_pic_const (X))
 
 
 /* Node: Frame Layout */
@@ -956,7 +947,7 @@ struct cum_args {int regs;};
 /* No symbol can be used as an index (or more correct, as a base) together
    with a register with PIC; the PIC register must be there.  */
 #define CONSTANT_INDEX_P(X) \
- (CONSTANT_P (X) && !(flag_pic && cris_symbol (X)))
+ (CONSTANT_P (X) && (!flag_pic || cris_valid_pic_const (X)))
 
 /* True if X is a valid base register.  */
 #define BASE_P(X) \
@@ -1003,10 +994,7 @@ struct cum_args {int regs;};
    rtx x1, x2;                                                 \
    if (SIMPLE_ADDRESS_P (X))                                   \
      goto ADDR;                                                        \
-   if (CONSTANT_P (X)                                          \
-       && (! flag_pic                                          \
-          || cris_gotless_symbol (X)                           \
-          || ! cris_symbol (X)))                               \
+   if (CONSTANT_INDEX_P (X))                                   \
      goto ADDR;                                                        \
    /* Indexed?  */                                             \
    if (GET_CODE (X) == PLUS)                                   \
@@ -1150,6 +1138,17 @@ struct cum_args {int regs;};
 
 /* Node: PIC */
 
+/* Helper type.  */
+
+enum cris_pic_symbol_type
+  {
+    cris_no_symbol = 0,
+    cris_got_symbol = 1,
+    cris_gotrel_symbol = 2,
+    cris_got_symbol_needing_fixup = 3,
+    cris_invalid_pic_symbol = 4
+  };
+
 #define PIC_OFFSET_TABLE_REGNUM (flag_pic ? CRIS_GOT_REGNUM : INVALID_REGNUM)
 
 #define LEGITIMATE_PIC_OPERAND_P(X) cris_legitimate_pic_operand (X)
@@ -1276,7 +1275,7 @@ struct cum_args {int regs;};
 
 /* For delay-slot handling.  */
 #define PRINT_OPERAND_PUNCT_VALID_P(CODE)      \
- ((CODE) == '#' || (CODE) == '!')
+ ((CODE) == '#' || (CODE) == '!' || (CODE) == ':')
 
 #define PRINT_OPERAND_ADDRESS(FILE, ADDR)      \
    cris_print_operand_address (FILE, ADDR)
index 2ecfa63..8d0ecaa 100644 (file)
 ;;   the mode is VOIDmode.  Always wrapped in CONST.
 ;; 1 Stack frame deallocation barrier.
 ;; 2 The address of the global offset table as a source operand.
+;; 3 The address of a global-offset-table-relative symbol + offset.
+;; 4 The offset within GOT of a symbol.
+;; 5 The offset within GOT of a symbol that has a PLT.
 
-(define_constants
+(define_constants ; FIXME: reorder sanely.
   [(CRIS_UNSPEC_PLT 0)
    (CRIS_UNSPEC_FRAME_DEALLOC 1)
-   (CRIS_UNSPEC_GOT 2)])
+   (CRIS_UNSPEC_GOT 2)
+   (CRIS_UNSPEC_GOTREL 3)
+   (CRIS_UNSPEC_GOTREAD 4)
+   (CRIS_UNSPEC_PLTGOTREAD 5)])
 
 ;; Register numbers.
 (define_constants
      FIXME: Do we *have* to recognize anything that would normally be a
      valid symbol?  Can we exclude global PIC addresses with an added
      offset?  */
-  if (flag_pic
-      && CONSTANT_ADDRESS_P (operands[1])
-      && cris_symbol (operands[1]))
-    {
-      /* We must have a register as destination for what we're about to
-        do, and for the patterns we generate.  */
-      if (! REG_S_P (operands[0]))
-       {
-         CRIS_ASSERT (!no_new_pseudos);
-         operands[1] = force_reg (SImode, operands[1]);
-       }
-      else
-       {
-         /* Mark a needed PIC setup for a LABEL_REF:s coming in here:
-            they are so rare not-being-branch-targets that we don't mark
-            a function as needing PIC setup just because we have
-            inspected LABEL_REF:s as operands.  It is only in
-            __builtin_setjmp and such that we can get a LABEL_REF
-            assigned to a register.  */
-         if (GET_CODE (operands[1]) == LABEL_REF)
+    if (flag_pic
+       && CONSTANT_ADDRESS_P (operands[1])
+       && !cris_valid_pic_const (operands[1]))
+      {
+       enum cris_pic_symbol_type t = cris_pic_symbol_type_of (operands[1]);
+
+       gcc_assert (t != cris_no_symbol);
+
+       if (! REG_S_P (operands[0]))
+         {
+           /* We must have a register as destination for what we're about to
+              do, and for the patterns we generate.  */
+           CRIS_ASSERT (!no_new_pseudos);
+           operands[1] = force_reg (SImode, operands[1]);
+         }
+       else
+         {
+           /* FIXME: add a REG_EQUAL (or is it REG_EQUIV) note to the
+              destination register for the symbol.  It might not be
+              worth it.  Measure.  */
            current_function_uses_pic_offset_table = 1;
-
-         /* We don't have to do anything for global PIC operands; they
-            look just like ``[rPIC+sym]''.  */
-         if (! cris_got_symbol (operands[1])
-             /* We don't do anything for local PIC operands; we match
-                that with a special alternative.  */
-             && ! cris_gotless_symbol (operands[1]))
-           {
-             /* We get here when we have to change something that would
-                be recognizable if it wasn't PIC.  A ``sym'' is ok for
-                PIC symbols both with and without a GOT entry.  And ``sym
-                + offset'' is ok for local symbols, so the only thing it
-                could be, is a global symbol with an offset.  Check and
-                abort if not.  */
-             rtx sym = get_related_value (operands[1]);
-             HOST_WIDE_INT offs = get_integer_term (operands[1]);
-
-             CRIS_ASSERT (sym != NULL_RTX && offs != 0);
-
-             emit_move_insn (operands[0], sym);
-             if (expand_binop (SImode, add_optab, operands[0],
-                               GEN_INT (offs), operands[0], 0,
-                               OPTAB_LIB_WIDEN) != operands[0])
-               internal_error ("expand_binop failed in movsi");
-             DONE;
-           }
-       }
-    }
+           if (t == cris_gotrel_symbol)
+             {
+               /* Change a "move.d sym(+offs),rN" into (allocate register rM)
+                  "move.d (const (plus (unspec [sym]
+                   CRIS_UNSPEC_GOTREL) offs)),rM" "add.d rPIC,rM,rN"  */
+               rtx tem, rm, rn = operands[0];
+               rtx sym = GET_CODE (operands[1]) != CONST
+                 ? operands[1] : get_related_value (operands[1]);
+               HOST_WIDE_INT offs = get_integer_term (operands[1]);
+
+               gcc_assert (! no_new_pseudos);
+               tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym),
+                                     CRIS_UNSPEC_GOTREL);
+               if (offs != 0)
+                 tem = plus_constant (tem, offs);
+               rm = gen_reg_rtx (Pmode);
+               emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
+               if (expand_binop (Pmode, add_optab, rm, pic_offset_table_rtx,
+                                 rn, 0, OPTAB_LIB_WIDEN) != rn)
+                 internal_error ("expand_binop failed in movsi gotrel");
+               DONE;
+             }
+           else if (t == cris_got_symbol)
+             {
+               /* Change a "move.d sym,rN" into (allocate register rM, rO)
+                  "move.d (const (unspec [sym] CRIS_UNSPEC_GOTREAD)),rM"
+                  "add.d rPIC,rM,rO", "move.d [rO],rN" with
+                  the memory access marked as read-only.  */
+               rtx tem, mem, rm, ro, rn = operands[0];
+               gcc_assert (! no_new_pseudos);
+               tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, operands[1]),
+                                     CRIS_UNSPEC_GOTREAD);
+               rm = gen_reg_rtx (Pmode);
+               emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
+               ro = gen_reg_rtx (Pmode);
+               if (expand_binop (Pmode, add_optab, rm, pic_offset_table_rtx,
+                                 ro, 0, OPTAB_LIB_WIDEN) != ro)
+                 internal_error ("expand_binop failed in movsi got");
+               mem = gen_rtx_MEM (Pmode, ro);
+
+               /* This MEM doesn't alias anything.  Whether it
+                  aliases other same symbols is unimportant.  */
+               set_mem_alias_set (mem, new_alias_set ());
+               MEM_NOTRAP_P (mem) = 1;
+               MEM_READONLY_P (mem) = 1;
+               emit_move_insn (rn, mem);
+               DONE;
+             }
+           else
+             {
+               /* We get here when we have to change something that would
+                  be recognizable if it wasn't PIC.  A ``sym'' is ok for
+                  PIC symbols both with and without a GOT entry.  And ``sym
+                  + offset'' is ok for local symbols, so the only thing it
+                  could be, is a global symbol with an offset.  Check and
+                  abort if not.  */
+               rtx reg = gen_reg_rtx (Pmode);
+               rtx sym = get_related_value (operands[1]);
+               HOST_WIDE_INT offs = get_integer_term (operands[1]);
+
+               gcc_assert (! no_new_pseudos
+                           && t == cris_got_symbol_needing_fixup
+                           && sym != NULL_RTX && offs != 0);
+
+               emit_move_insn (reg, sym);
+               if (expand_binop (SImode, add_optab, reg,
+                                 GEN_INT (offs), operands[0], 0,
+                                 OPTAB_LIB_WIDEN) != operands[0])
+                 internal_error ("expand_binop failed in movsi got+offs");
+               DONE;
+             }
+         }
+      }
 })
 
+(define_insn "*movsi_got_load"
+  [(set (reg:SI CRIS_GOT_REGNUM) (unspec:SI [(const_int 0)] CRIS_UNSPEC_GOT))]
+  "flag_pic"
+  "move.d $pc,%:\;sub.d .:GOTOFF,%:"
+  [(set_attr "cc" "clobber")])
+
 (define_insn "*movsi_internal"
   [(set
     (match_operand:SI 0 "nonimmediate_operand" "=r,r, r,Q>,r,Q>,g,r,r, r,g,rQ>,x,  m,x")
-    (match_operand:SI 1
-    ;; FIXME: We want to put S last, but apparently g matches S.
-    ;; It's a bug: an S is not a general_operand and shouldn't match g.
-     "cris_general_operand_or_gotless_symbol"   "r,Q>,M,M, I,r, M,n,!S,g,r,x,  rQ>,x,gi"))]
+    ;; Note that we prefer not to use the S alternative (if for some reason
+    ;; it competes with others), but g matches S.
+    (match_operand:SI 1 "general_operand"      "r,Q>,M,M, I,r, M,n,!S,g,r,x,  rQ>,x,gi"))]
   ""
 {
   /* Better to have c-switch here; it is worth it to optimize the size of
       return "move.d %1,%0";
 
     case 8:
-      /* FIXME: Try and split this into pieces GCC makes better code of,
-        than this multi-insn pattern.  Synopsis: wrap the GOT-relative
-        symbol into an unspec, and when PIC, recognize the unspec
-        everywhere a symbol is normally recognized.  (The PIC register
-        should be recognized by GCC as pic_offset_table_rtx when needed
-        and similar for PC.)  Each component can then be optimized with
-        the rest of the code; it should be possible to have a constant
-        term added on an unspec.  Don't forget to add a REG_EQUAL (or
-        is it REG_EQUIV) note to the destination.  It might not be
-        worth it.  Measure.
-
-        Note that the 'v' modifier makes PLT references be output as
-        sym:PLT rather than [rPIC+sym:GOTPLT].  */
-      if (GET_CODE (operands[1]) == UNSPEC
-         && XINT (operands[1], 1) == CRIS_UNSPEC_GOT)
-       {
-         /* We clobber cc0 rather than set it to GOT.  Should not
-             matter, though.  */
-         CC_STATUS_INIT;
-         CRIS_ASSERT (REGNO (operands[0]) == PIC_OFFSET_TABLE_REGNUM);
-
-         return "move.d $pc,%0\;sub.d .:GOTOFF,%0";
-       }
-
-      return "move.d %v1,%0\;add.d %P1,%0";
-
+      {
+       rtx tem = operands[1];
+       gcc_assert (GET_CODE (tem) == CONST);
+       tem = XEXP (tem, 0);
+       if (GET_CODE (tem) == PLUS
+           && GET_CODE (XEXP (tem, 0)) == UNSPEC
+           && XINT (XEXP (tem, 0), 1) == CRIS_UNSPEC_GOTREL
+           && GET_CODE (XEXP (tem, 1)) == CONST_INT)
+         tem = XEXP (tem, 0);
+       gcc_assert (GET_CODE (tem) == UNSPEC);
+       switch (XINT (tem, 1))
+         {
+         case CRIS_UNSPEC_GOTREAD:
+         case CRIS_UNSPEC_PLTGOTREAD:
+           /* Using sign-extend mostly to be consistent with the
+              indexed addressing mode.  */
+           if (flag_pic == 1)
+             return "movs.w %1,%0";
+         case CRIS_UNSPEC_GOTREL:
+         case CRIS_UNSPEC_PLT:
+           return "move.d %1,%0";
+
+         default:
+           gcc_unreachable ();
+         }
+      }
     default:
       return "BOGUS: %1 to %0";
     }
    add.d %M2,%M1,%M0\;ax\;add.d %H2,%H1,%H0")
 
 (define_insn "addsi3"
-  [(set (match_operand:SI 0 "register_operand"  "=r,r, r,r,r,r,r,  r")
+  [(set (match_operand:SI 0 "register_operand"  "=r,r, r,r,r,r, r,r,  r")
        (plus:SI
-        (match_operand:SI 1 "register_operand" "%0,0, 0,0,0,0,r,  r")
-        (match_operand:SI 2 "general_operand"   "r,Q>,J,N,n,g,!To,0")))]
+        (match_operand:SI 1 "register_operand" "%0,0, 0,0,0,0, 0,r,  r")
+        (match_operand:SI 2 "general_operand"   "r,Q>,J,N,n,!S,g,!To,0")))]
 
 ;; The last constraint is due to that after reload, the '%' is not
 ;; honored, and canonicalization doesn't care about keeping the same
            return "subu.w %n2,%0";
        }
       return "add.d %2,%0";
-    case 6:
-      return "add.d %2,%1,%0";
     case 5:
+      {
+       rtx tem = operands[2];
+       gcc_assert (GET_CODE (tem) == CONST);
+       tem = XEXP (tem, 0);
+       if (GET_CODE (tem) == PLUS
+           && GET_CODE (XEXP (tem, 0)) == UNSPEC
+           && XINT (XEXP (tem, 0), 1) == CRIS_UNSPEC_GOTREL
+           && GET_CODE (XEXP (tem, 1)) == CONST_INT)
+         tem = XEXP (tem, 0);
+       gcc_assert (GET_CODE (tem) == UNSPEC);
+       switch (XINT (tem, 1))
+         {
+         case CRIS_UNSPEC_GOTREAD:
+         case CRIS_UNSPEC_PLTGOTREAD:
+           /* Using sign-extend mostly to be consistent with the
+              indexed addressing mode.  */
+           if (flag_pic == 1)
+             return "adds.w %2,%0";
+           /* Fall through.  */
+         case CRIS_UNSPEC_PLT:
+         case CRIS_UNSPEC_GOTREL:
+           return "add.d %2,%0";
+         default:
+           gcc_unreachable ();
+         }
+      }
+    case 6:
       return "add.d %2,%0";
     case 7:
+      return "add.d %2,%1,%0";
+    case 8:
       return "add.d %1,%0";
     default:
       return "BOGUS addsi %2+%1 to %0";
     }
 }
- [(set_attr "slottable" "yes,yes,yes,yes,no,no,no,yes")])
+ [(set_attr "slottable" "yes,yes,yes,yes,no,no,no,no,yes")])
 \f
 (define_insn "addhi3"
   [(set (match_operand:HI 0 "register_operand"         "=r,r, r,r,r,r")
 (define_insn "uminsi3"
   [(set (match_operand:SI 0 "register_operand"          "=r,r, r,r")
        (umin:SI  (match_operand:SI 1 "register_operand" "%0,0, 0,r")
-                 (match_operand:SI 2 "general_operand"   "r,Q>,g,!STo")))]
+                 (match_operand:SI 2 "general_operand"   "r,Q>,g,!To")))]
   ""
 {
   if (GET_CODE (operands[2]) == CONST_INT)
              (clobber (reg:SI CRIS_SRP_REGNUM))])]
   ""
 {
-  rtx op0;
-
   gcc_assert (GET_CODE (operands[0]) == MEM);
-
   if (flag_pic)
-    {
-      op0 = XEXP (operands[0], 0);
-
-      /* It might be that code can be generated that jumps to 0 (or to a
-        specific address).  Don't die on that.  (There is a testcase.)  */
-      if (CONSTANT_ADDRESS_P (op0) && GET_CODE (op0) != CONST_INT)
-       {
-         CRIS_ASSERT (!no_new_pseudos);
-
-         /* For local symbols (non-PLT), get the plain symbol reference
-            into a register.  For symbols that can be PLT, make them PLT.  */
-         if (cris_gotless_symbol (op0) || GET_CODE (op0) != SYMBOL_REF)
-           op0 = force_reg (Pmode, op0);
-         else if (cris_symbol (op0))
-           /* FIXME: Would hanging a REG_EQUIV/EQUAL on that register
-              for the symbol cause bad recombinatorial effects?  */
-           op0 = force_reg (Pmode,
-                            gen_rtx_CONST
-                            (Pmode,
-                             gen_rtx_UNSPEC (VOIDmode,
-                                             gen_rtvec (1, op0),
-                                             CRIS_UNSPEC_PLT)));
-         else
-           internal_error ("Unidentifiable op0");
-
-         operands[0] = replace_equiv_address (operands[0], op0);
-       }
-    }
+    cris_expand_pic_call_address (&operands[0]);
 })
 
 ;; Accept *anything* as operand 1.  Accept operands for operand 0 in
 
 (define_insn "*expanded_call"
   [(call (mem:QI (match_operand:SI
-                 0 "cris_general_operand_or_plt_symbol" "r,Q>,g,S"))
-        (match_operand 1 "" ""))
-   (clobber (reg:SI CRIS_SRP_REGNUM))]
-  "! TARGET_AVOID_GOTPLT"
-  "jsr %0")
-
-;; Same as above, since can't afford wasting a constraint letter to mean
-;; "S unless TARGET_AVOID_GOTPLT".
-(define_insn "*expanded_call_no_gotplt"
-  [(call (mem:QI (match_operand:SI
                  0 "cris_general_operand_or_plt_symbol" "r,Q>,g"))
         (match_operand 1 "" ""))
    (clobber (reg:SI CRIS_SRP_REGNUM))]
-  "TARGET_AVOID_GOTPLT"
+  ""
   "jsr %0")
 
+;; Parallel when calculating and reusing address of indirect pointer
+;; with simple offset.  (Makes most sense with PIC.)  It looks a bit
+;; wrong not to have the clobber last, but that's the way combine
+;; generates it (except it doesn' look into the *inner* mem, so this
+;; just matches a peephole2).  FIXME: investigate that.
+(define_insn "*expanded_call_side"
+  [(call (mem:QI
+         (mem:SI
+          (plus:SI (match_operand:SI 0 "cris_bdap_operand" "%r,  r,r")
+                   (match_operand:SI 1 "cris_bdap_operand" "r>Rn,r,>Rn"))))
+        (match_operand 2 "" ""))
+   (clobber (reg:SI CRIS_SRP_REGNUM))
+   (set (match_operand:SI 3 "register_operand" "=*0,r,r")
+       (plus:SI (match_dup 0)
+                (match_dup 1)))]
+  "! TARGET_AVOID_GOTPLT"
+  "jsr [%3=%0%S1]")
+
 (define_expand "call_value"
   [(parallel [(set (match_operand 0 "" "")
                   (call (match_operand:QI 1 "cris_mem_call_operand" "")
              (clobber (reg:SI CRIS_SRP_REGNUM))])]
   ""
 {
-  rtx op1;
-
   gcc_assert (GET_CODE (operands[1]) == MEM);
-
   if (flag_pic)
-    {
-      op1 = XEXP (operands[1], 0);
-
-      /* It might be that code can be generated that jumps to 0 (or to a
-        specific address).  Don't die on that.  (There is a testcase.)  */
-      if (CONSTANT_ADDRESS_P (op1) && GET_CODE (op1) != CONST_INT)
-       {
-         CRIS_ASSERT (!no_new_pseudos);
-
-         if (cris_gotless_symbol (op1))
-           op1 = force_reg (Pmode, op1);
-         else if (cris_symbol (op1))
-           /* FIXME: Would hanging a REG_EQUIV/EQUAL on that register
-              for the symbol cause bad recombinatorial effects?  */
-           op1 = force_reg (Pmode,
-                            gen_rtx_CONST
-                            (Pmode,
-                             gen_rtx_UNSPEC (VOIDmode,
-                                             gen_rtvec (1, op1),
-                                             CRIS_UNSPEC_PLT)));
-         else
-           internal_error ("Unidentifiable op0");
-
-         operands[1] = replace_equiv_address (operands[1], op1);
-       }
-    }
+    cris_expand_pic_call_address (&operands[1]);
 })
 
 ;; Accept *anything* as operand 2.  The validity other than "general" of
 ;; than requiring getting rPIC + sym:PLT into a register.
 
 (define_insn "*expanded_call_value"
-  [(set (match_operand 0 "nonimmediate_operand" "=g,g,g,g")
+  [(set (match_operand 0 "nonimmediate_operand" "=g,g,g")
        (call (mem:QI (match_operand:SI
-                      1 "cris_general_operand_or_plt_symbol" "r,Q>,g,S"))
+                      1 "cris_general_operand_or_plt_symbol" "r,Q>,g"))
              (match_operand 2 "" "")))
    (clobber (reg:SI CRIS_SRP_REGNUM))]
-  "! TARGET_AVOID_GOTPLT"
+  ""
   "Jsr %1"
   [(set_attr "cc" "clobber")])
 
-;; Same as above, since can't afford wasting a constraint letter to mean
-;; "S unless TARGET_AVOID_GOTPLT".
-(define_insn "*expanded_call_value_no_gotplt"
+;; See similar call special-case.
+(define_insn "*expanded_call_value_side"
   [(set (match_operand 0 "nonimmediate_operand" "=g,g,g")
-       (call (mem:QI (match_operand:SI
-                      1 "cris_general_operand_or_plt_symbol" "r,Q>,g"))
-             (match_operand 2 "" "")))
-   (clobber (reg:SI CRIS_SRP_REGNUM))]
-  "TARGET_AVOID_GOTPLT"
-  "Jsr %1"
+       (call
+        (mem:QI
+         (mem:SI
+          (plus:SI (match_operand:SI 1 "cris_bdap_operand" "%r,  r,r")
+                   (match_operand:SI 2 "cris_bdap_operand" "r>Rn,r,>Rn"))))
+             (match_operand 3 "" "")))
+   (clobber (reg:SI CRIS_SRP_REGNUM))
+   (set (match_operand:SI 4 "register_operand" "=*1,r,r")
+       (plus:SI (match_dup 1)
+                (match_dup 2)))]
+  "! TARGET_AVOID_GOTPLT"
+  "Jsr [%4=%1%S2]"
   [(set_attr "cc" "clobber")])
 
 ;; Used in debugging.  No use for the direct pattern; unfilled
                                                amode == SImode
                                                ? QImode : amode)));
 })
+
+;; Try and avoid GOTPLT reads escaping a call: transform them into
+;; PLT.  Curiously (but thankfully), peepholes for instructions
+;; *without side-effects* that just feed a call (or call_value) are
+;; not matched neither in a build or test-suite, so those patterns are
+;; omitted.
+
+;; A "normal" move where we don't check the consumer.
+
+(define_peephole2 ; gotplt-to-plt
+  [(set
+    (match_operand:SI 0 "register_operand" "")
+    (match_operator:SI
+     1 "cris_mem_op"
+     [(plus:SI
+       (reg:SI CRIS_GOT_REGNUM)
+       (const:SI
+       (unspec:SI [(match_operand:SI 2 "cris_general_operand_or_symbol" "")]
+                  CRIS_UNSPEC_PLTGOTREAD)))]))]
+  "flag_pic
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && REGNO_REG_CLASS (REGNO (operands[0])) == REGNO_REG_CLASS (0)"
+  [(set (match_dup 0) (const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLT)))
+   (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI CRIS_GOT_REGNUM)))]
+  "")
+
+;; And one set with a side-effect getting the PLTGOT offset.
+;; First call and call_value variants.
+
+(define_peephole2 ; gotplt-to-plt-side-call
+  [(parallel
+    [(set
+      (match_operand:SI 0 "register_operand" "")
+      (match_operator:SI
+       1 "cris_mem_op"
+       [(plus:SI
+        (reg:SI CRIS_GOT_REGNUM)
+        (const:SI
+         (unspec:SI [(match_operand:SI
+                      2 "cris_general_operand_or_symbol" "")]
+                    CRIS_UNSPEC_PLTGOTREAD)))]))
+     (set (match_operand:SI 3 "register_operand" "")
+         (plus:SI (reg:SI CRIS_GOT_REGNUM)
+                  (const:SI
+                   (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLTGOTREAD))))])
+  (parallel [(call (mem:QI (match_dup 0))
+                   (match_operand 4 "" ""))
+             (clobber (reg:SI CRIS_SRP_REGNUM))])]
+  "flag_pic
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && peep2_reg_dead_p (2, operands[0])"
+  [(parallel [(call (mem:QI (match_dup 1))
+                   (match_dup 4))
+             (clobber (reg:SI CRIS_SRP_REGNUM))
+             (set (match_dup 3)
+                  (plus:SI (reg:SI CRIS_GOT_REGNUM)
+                           (const:SI
+                            (unspec:SI [(match_dup 2)]
+                                       CRIS_UNSPEC_PLTGOTREAD))))])]
+  "")
+
+(define_peephole2 ; gotplt-to-plt-side-call-value
+  [(parallel
+    [(set
+      (match_operand:SI 0 "register_operand" "")
+      (match_operator:SI
+       1 "cris_mem_op"
+       [(plus:SI
+        (reg:SI CRIS_GOT_REGNUM)
+        (const:SI
+         (unspec:SI [(match_operand:SI
+                      2 "cris_general_operand_or_symbol" "")]
+                    CRIS_UNSPEC_PLTGOTREAD)))]))
+     (set (match_operand:SI 3 "register_operand" "")
+         (plus:SI (reg:SI CRIS_GOT_REGNUM)
+                  (const:SI
+                   (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLTGOTREAD))))])
+   (parallel [(set (match_operand 5 "" "")
+                  (call (mem:QI (match_dup 0))
+                        (match_operand 4 "" "")))
+             (clobber (reg:SI CRIS_SRP_REGNUM))])]
+  "flag_pic
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && peep2_reg_dead_p (2, operands[0])"
+  [(parallel [(set (match_dup 5)
+                  (call (mem:QI (match_dup 1))
+                        (match_dup 4)))
+             (clobber (reg:SI CRIS_SRP_REGNUM))
+             (set (match_dup 3)
+                  (plus:SI (reg:SI CRIS_GOT_REGNUM)
+                           (const:SI
+                            (unspec:SI [(match_dup 2)]
+                                       CRIS_UNSPEC_PLTGOTREAD))))])]
+  "")
+
+(define_peephole2 ; gotplt-to-plt-side
+  [(parallel
+    [(set
+      (match_operand:SI 0 "register_operand" "")
+      (match_operator:SI
+       1 "cris_mem_op"
+       [(plus:SI
+        (reg:SI CRIS_GOT_REGNUM)
+        (const:SI
+         (unspec:SI [(match_operand:SI
+                      2 "cris_general_operand_or_symbol" "")]
+                    CRIS_UNSPEC_PLTGOTREAD)))]))
+     (set (match_operand:SI 3 "register_operand" "")
+         (plus:SI (reg:SI CRIS_GOT_REGNUM)
+                  (const:SI
+                   (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLTGOTREAD))))])]
+  "flag_pic
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && REGNO_REG_CLASS (REGNO (operands[0])) == REGNO_REG_CLASS (0)"
+  [(set (match_dup 3)
+       (const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLTGOTREAD)))
+   (set (match_dup 3) (plus:SI (match_dup 3) (reg:SI CRIS_GOT_REGNUM)))
+   (set (match_dup 0) (const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLT)))
+   (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI CRIS_GOT_REGNUM)))]
+  "")
 \f
 ;; Local variables:
 ;; mode:emacs-lisp
index c37247e..90fab8d 100644 (file)
@@ -63,8 +63,8 @@
 
 (define_predicate "cris_bdap_const_operand"
   (and (match_code "label_ref, symbol_ref, const_int, const_double, const")
-       (not (and (match_test "flag_pic")
-                (match_test "cris_symbol (op)")))))
+       (ior (not (match_test "flag_pic"))
+           (match_test "cris_valid_pic_const (op)"))))
 
 (define_predicate "cris_simple_address_operand"
   (ior (match_operand:SI 0 "register_operand")
   (ior (match_operand 0 "cris_bdap_operand")
        (match_operand 0 "cris_biap_mult_operand")))
 
-;; Since a PIC symbol without a GOT entry is not a general_operand, we
-;; have to have a predicate that matches it.  We use this in the expanded
-;; "movsi" anonymous pattern.
-;; FIXME: Can s/special_// when PR 20413 is fixed.
-
-(define_special_predicate "cris_general_operand_or_gotless_symbol"
-  (ior (match_operand 0 "general_operand")
-       (and (match_code "const, symbol_ref, label_ref, unspec")
-           (match_test "cris_gotless_symbol (op)"))))
-
 ;; Since with -fPIC, not all symbols are valid PIC symbols or indeed
 ;; general_operands, we have to have a predicate that matches it for the
 ;; "movsi" expander.
 (define_special_predicate "cris_general_operand_or_symbol"
   (ior (match_operand 0 "general_operand")
        (and (match_code "const, symbol_ref, label_ref")
-           (match_test "cris_symbol (op)"))))
+                   ; The following test is actually just an assertion.
+           (match_test "cris_pic_symbol_type_of (op) != cris_no_symbol"))))
 
 ;; Since a PLT symbol is not a general_operand, we have to have a
 ;; predicate that matches it when we need it.  We use this in the expanded