]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
re PR target/34981 (Lazily-bound function called twice)
authorRichard Sandiford <rsandifo@nildram.co.uk>
Sat, 2 Feb 2008 09:55:42 +0000 (09:55 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Sat, 2 Feb 2008 09:55:42 +0000 (09:55 +0000)
gcc/
PR target/34981
* config/mips/mips-protos.h (mips_expand_call): Return an rtx.
(build_mips16_call_stub): Likewise.
* config/mips/mips.h (FIRST_PSEUDO_REGISTER): Rename FAKE_CALL_REGNO
to GOT_VERSION_REGNUM.
(CALL_REALLY_USED_REGISTERS): Set the GOT_VERSION_REGNUM entry to 0.
(EPILOGUE_USES): Include GOT_VERSION_REGNUM if TARGET_ABICALLS.
* config/mips/mips.c (mips_emit_call_insn): New function.
(mips_call_tls_get_addr): Call mips_expand_call directly.
(mips_expand_call): Update the call to build_mips16_call_stub
and remove a redundant condition.  Assert that MIPS16 stubs do not
use lazy binding.  Use mips_emit_call_insn and return the call insn.
(override_options): Allow SImode for GOT_VERSION_REGNUM.
(build_mips16_call_stub): Use mips_emit_call_insn rather than
emit_call_insn.  Return the call insn or null.
(mips_avoid_hazard): Remove hazard_set handling.
(mips_extra_live_on_entry): Include GOT_VERSION_REGNUM if
TARGET_ABICALLS.
* config/mips/mips.md (UNSPEC_EH_RECEIVER): Rename to...
(UNSPEC_RESTORE_GP): ...this.
(UNSPEC_SET_GOT_VERSION, UNSPEC_UPDATE_GOT_VERSION): New constants.
(FAKE_CALL_REGNO): Rename to...
(GOT_VERSION_REGNUM): ...this.
(type): Add "ghost" value.  Add an associated insn reservation.
(hazard_set): Remove.
(exception_receiver): Rename to...
(restore_gp): ...this and update the unspec identifier accordingly.
(exception_receiver, nonlocal_got_receiver): New expanders.
(load_call<mode>): Use GOT_VERSION_REGNUM.  Don't set
FAKE_CALL_REGNO.  Remove hazard_set attribute.
(set_got_version, update_got_version): New patterns.

gcc/testsuite/
PR target/34981
* gcc.target/mips/lazy-binding-1.c: New test.
* gcc.target/mips/mips.exp (setup_mips_tests): Set mips_abi,
mips_forced_gp, mips_forced_no_abicalls, mips_forced_no_shared
and mips_forced_no_er.
(dg-mips-options): Avoid using -mips16 -mhard-float for ABIs
other than o32 and o64.  Avoid using -mabicalls with an implicit
-mabi=eabi.  Avoid using small data with -mabicalls.  Skip
-mabi=*, -G*, -mabicalls, -mshared and -mexplicit-relocs tests
if the multilib forces the an incompatible option.

From-SVN: r132067

gcc/ChangeLog
gcc/config/mips/mips-protos.h
gcc/config/mips/mips.c
gcc/config/mips/mips.h
gcc/config/mips/mips.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/mips/lazy-binding-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/mips.exp

index 51ac67dc50323c07d74e00dda56ce5c23630a1a5..97346e60e6b42f33697367a0030ea1d71373bcc5 100644 (file)
@@ -1,3 +1,37 @@
+2008-02-02  Richard Sandiford  <rsandifo@nildram.co.uk>
+
+       PR target/34981
+       * config/mips/mips-protos.h (mips_expand_call): Return an rtx.
+       (build_mips16_call_stub): Likewise.
+       * config/mips/mips.h (FIRST_PSEUDO_REGISTER): Rename FAKE_CALL_REGNO
+       to GOT_VERSION_REGNUM.
+       (CALL_REALLY_USED_REGISTERS): Set the GOT_VERSION_REGNUM entry to 0.
+       (EPILOGUE_USES): Include GOT_VERSION_REGNUM if TARGET_ABICALLS.
+       * config/mips/mips.c (mips_emit_call_insn): New function.
+       (mips_call_tls_get_addr): Call mips_expand_call directly.
+       (mips_expand_call): Update the call to build_mips16_call_stub
+       and remove a redundant condition.  Assert that MIPS16 stubs do not
+       use lazy binding.  Use mips_emit_call_insn and return the call insn.
+       (override_options): Allow SImode for GOT_VERSION_REGNUM.
+       (build_mips16_call_stub): Use mips_emit_call_insn rather than
+       emit_call_insn.  Return the call insn or null.
+       (mips_avoid_hazard): Remove hazard_set handling.
+       (mips_extra_live_on_entry): Include GOT_VERSION_REGNUM if
+       TARGET_ABICALLS.
+       * config/mips/mips.md (UNSPEC_EH_RECEIVER): Rename to...
+       (UNSPEC_RESTORE_GP): ...this.
+       (UNSPEC_SET_GOT_VERSION, UNSPEC_UPDATE_GOT_VERSION): New constants.
+       (FAKE_CALL_REGNO): Rename to...
+       (GOT_VERSION_REGNUM): ...this.
+       (type): Add "ghost" value.  Add an associated insn reservation.
+       (hazard_set): Remove.
+       (exception_receiver): Rename to...
+       (restore_gp): ...this and update the unspec identifier accordingly.
+       (exception_receiver, nonlocal_got_receiver): New expanders.
+       (load_call<mode>): Use GOT_VERSION_REGNUM.  Don't set
+       FAKE_CALL_REGNO.  Remove hazard_set attribute.
+       (set_got_version, update_got_version): New patterns.
+
 2008-02-02  Richard Sandiford  <rsandifo@nildram.co.uk>
 
        PR target/31388
index 0cfe76756d55591591bb1eff87b4acfe698cea5e..737cc176a6af03ada1eb45ecb74481aa9e481436 100644 (file)
@@ -171,7 +171,7 @@ extern void mips_expand_vcondv2sf (rtx, rtx, rtx, enum rtx_code, rtx, rtx);
 #endif
 extern void gen_conditional_move (rtx *);
 extern void mips_gen_conditional_trap (rtx *);
-extern void mips_expand_call (rtx, rtx, rtx, rtx, int);
+extern rtx mips_expand_call (rtx, rtx, rtx, rtx, int);
 extern void mips_emit_fcc_reload (rtx, rtx, rtx);
 extern void mips_set_return_address (rtx, rtx);
 extern bool mips_expand_block_move (rtx, rtx, rtx);
@@ -231,7 +231,7 @@ extern enum reg_class mips_secondary_reload_class (enum reg_class,
                                                   enum machine_mode,
                                                   rtx, int);
 extern int mips_class_max_nregs (enum reg_class, enum machine_mode);
-extern int build_mips16_call_stub (rtx, rtx, rtx, int);
+extern rtx build_mips16_call_stub (rtx, rtx, rtx, int);
 extern int mips_register_move_cost (enum machine_mode, enum reg_class,
                                    enum reg_class);
 
index da3ddf836d0e0f8991ef53b27813e6b606d70ee9..1abbe49355b3a1e8e3ca68314319e9f62bbbd0f4 100644 (file)
@@ -1904,6 +1904,31 @@ mips_idiv_insns (void)
     count++;
   return count;
 }
+
+/* Emit a call sequence with call pattern PATTERN and return the call
+   instruction itself (which is not necessarily the last instruction
+   emitted).  LAZY_P is true if the call address is lazily-bound.  */
+
+static rtx
+mips_emit_call_insn (rtx pattern, bool lazy_p)
+{
+  rtx insn;
+
+  insn = emit_call_insn (pattern);
+
+  /* Lazy-binding stubs require $gp to be valid on entry.  */
+  if (lazy_p)
+    use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
+
+  if (TARGET_ABICALLS)
+    {
+      /* See the comment above load_call<mode> for details.  */
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
+              gen_rtx_REG (Pmode, GOT_VERSION_REGNUM));
+      emit_insn (gen_update_got_version ());
+    }
+  return insn;
+}
 \f
 /* This function is used to implement GO_IF_LEGITIMATE_ADDRESS.  It
    returns a nonzero value if X is a legitimate address for a memory
@@ -2044,7 +2069,7 @@ static GTY(()) rtx mips_tls_symbol;
 static rtx
 mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
 {
-  rtx insn, loc, tga, a0;
+  rtx insn, loc, a0;
 
   a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
 
@@ -2057,8 +2082,7 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
 
   emit_insn (gen_rtx_SET (Pmode, a0,
                          gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
-  tga = gen_rtx_MEM (Pmode, mips_tls_symbol);
-  insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx));
+  insn = mips_expand_call (v0, mips_tls_symbol, const0_rtx, const0_rtx, false);
   CONST_OR_PURE_CALL_P (insn) = 1;
   use_reg (&CALL_INSN_FUNCTION_USAGE (insn), v0);
   use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
@@ -3433,9 +3457,11 @@ mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
    function, ARGS_SIZE is the size of the arguments and AUX is
    the value passed to us by mips_function_arg.  SIBCALL_P is true
    if we are expanding a sibling call, false if we're expanding
-   a normal call.  */
+   a normal call.
 
-void
+   Return the call itself.  */
+
+rtx
 mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
 {
   rtx orig_addr, pattern, insn;
@@ -3449,11 +3475,13 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
       lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p);
     }
 
-  if (TARGET_MIPS16
-      && mips16_hard_float
-      && build_mips16_call_stub (result, addr, args_size,
-                                aux == 0 ? 0 : (int) GET_MODE (aux)))
-    return;
+  insn = build_mips16_call_stub (result, addr, args_size,
+                                aux == 0 ? 0 : (int) GET_MODE (aux));
+  if (insn)
+    {
+      gcc_assert (!sibcall_p && !lazy_p);
+      return insn;
+    }
 
   if (result == 0)
     pattern = (sibcall_p
@@ -3475,17 +3503,7 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
               ? gen_sibcall_value_internal (result, addr, args_size)
               : gen_call_value_internal (result, addr, args_size));
 
-  insn = emit_call_insn (pattern);
-
-  /* Lazy-binding stubs require $gp to be valid on entry.  We also pretend
-     that they use FAKE_CALL_REGNO; see the load_call<mode> patterns for
-     details.  */
-  if (lazy_p)
-    {
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
-              gen_rtx_REG (Pmode, FAKE_CALL_REGNO));
-    }
+  return mips_emit_call_insn (pattern, lazy_p);
 }
 
 
@@ -5095,6 +5113,10 @@ override_options (void)
 
          else if (ALL_COP_REG_P (regno))
            temp = (class == MODE_INT && size <= UNITS_PER_WORD);
+
+         else if (regno == GOT_VERSION_REGNUM)
+           temp = (mode == SImode);
+
          else
            temp = 0;
 
@@ -8093,10 +8115,12 @@ static struct mips16_stub *mips16_stubs;
    RETVAL is the location of the return value, or null if this is
    a call rather than a call_value.  FN is the address of the
    function and ARG_SIZE is the size of the arguments.  FP_CODE
-   is the code built by function_arg.  This function returns a nonzero
-   value if it builds the call instruction itself.  */
+   is the code built by function_arg.
 
-int
+   If a stub was needed, emit the call and return the call insn itself.
+   Return null otherwise.  */
+
+rtx
 build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 {
   int fpret;
@@ -8110,7 +8134,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
   /* We don't need to do anything if we aren't in mips16 mode, or if
      we were invoked with the -msoft-float option.  */
   if (! TARGET_MIPS16 || ! mips16_hard_float)
-    return 0;
+    return NULL_RTX;
 
   /* Figure out whether the value might come back in a floating point
      register.  */
@@ -8122,13 +8146,13 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
      arguments and the value will not be returned in a floating point
      register.  */
   if (fp_code == 0 && ! fpret)
-    return 0;
+    return NULL_RTX;
 
   /* We don't need to do anything if this is a call to a special
      mips16 support function.  */
   if (GET_CODE (fn) == SYMBOL_REF
       && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
-    return 0;
+    return NULL_RTX;
 
   /* This code will only work for o32 and o64 abis.  The other ABI's
      require more sophisticated support.  */
@@ -8167,7 +8191,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
        insn = gen_call_internal (stub_fn, arg_size);
       else
        insn = gen_call_value_internal (retval, stub_fn, arg_size);
-      insn = emit_call_insn (insn);
+      insn = mips_emit_call_insn (insn, false);
 
       /* Put the register usage information on the CALL.  */
       CALL_INSN_FUNCTION_USAGE (insn) =
@@ -8189,7 +8213,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 
       /* Return 1 to tell the caller that we've generated the call
          insn.  */
-      return 1;
+      return insn;
     }
 
   /* We know the function we are going to call.  If we have already
@@ -8368,7 +8392,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
        insn = gen_call_internal (fn, arg_size);
       else
        insn = gen_call_value_internal (retval, fn, arg_size);
-      insn = emit_call_insn (insn);
+      insn = mips_emit_call_insn (insn, false);
 
       CALL_INSN_FUNCTION_USAGE (insn) =
        gen_rtx_EXPR_LIST (VOIDmode,
@@ -8377,11 +8401,11 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 
       /* Return 1 to tell the caller that we've generated the call
          insn.  */
-      return 1;
+      return insn;
     }
 
   /* Return 0 to let the caller generate the call insn.  */
-  return 0;
+  return NULL_RTX;
 }
 
 /* An entry in the mips16 constant pool.  VALUE is the pool constant,
@@ -9112,7 +9136,7 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
                   rtx *delayed_reg, rtx lo_reg)
 {
   rtx pattern, set;
-  int nops, ninsns, hazard_set;
+  int nops, ninsns;
 
   pattern = PATTERN (insn);
 
@@ -9158,15 +9182,8 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
        break;
 
       case HAZARD_DELAY:
-       hazard_set = (int) get_attr_hazard_set (insn);
-       if (hazard_set == 0)
-         set = single_set (insn);
-       else
-         {
-           gcc_assert (GET_CODE (PATTERN (insn)) == PARALLEL);
-           set = XVECEXP (PATTERN (insn), 0, hazard_set - 1);
-         }
-       gcc_assert (set && GET_CODE (set) == SET);
+       set = single_set (insn);
+       gcc_assert (set);
        *delayed_reg = SET_DEST (set);
        break;
       }
@@ -11024,14 +11041,21 @@ mips_encode_section_info (tree decl, rtx rtl, int first)
     }
 }
 
-/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  PIC_FUNCTION_ADDR_REGNUM is live
-   on entry to a function when generating -mshared abicalls code.  */
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  */
 
 static void
 mips_extra_live_on_entry (bitmap regs)
 {
-  if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
-    bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+  if (TARGET_ABICALLS)
+    {
+      /* PIC_FUNCTION_ADDR_REGNUM is live if we need it to set up
+        the global pointer.   */
+      if (!TARGET_ABSOLUTE_ABICALLS)
+       bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+
+      /* See the comment above load_call<mode> for details.  */
+      bitmap_set_bit (regs, GOT_VERSION_REGNUM);
+    }
 }
 
 /* SImode values are represented as sign-extended to DImode.  */
index d34cad46006b986564c33b65445cda581d9c8531..e857c1d706dfdf766c6286cdf3fc5e9986042194 100644 (file)
@@ -1175,7 +1175,7 @@ extern const struct mips_rtx_cost_data *mips_cost;
    - 3 fake registers:
        - ARG_POINTER_REGNUM
        - FRAME_POINTER_REGNUM
-       - FAKE_CALL_REGNO (see the comment above load_callsi for details)
+       - GOT_VERSION_REGNUM (see the comment above load_call<mode> for details)
    - 3 dummy entries that were used at various times in the past.
    - 6 DSP accumulator registers (3 hi-lo pairs) for MIPS DSP ASE
    - 6 DSP control registers  */
@@ -1255,7 +1255,7 @@ extern const struct mips_rtx_cost_data *mips_cost;
   1, 1, 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,                      \
   /* Others.  */                                                        \
-  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, 0,                      \
   /* COP0 registers */                                                 \
   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,                      \
@@ -1966,8 +1966,12 @@ typedef struct mips_args {
 \f
 /* Say that the epilogue uses the return address register.  Note that
    in the case of sibcalls, the values "used by the epilogue" are
-   considered live at the start of the called function.  */
-#define EPILOGUE_USES(REGNO) ((REGNO) == 31)
+   considered live at the start of the called function.
+
+   If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
+   See the comment above load_call<mode> for details.  */
+#define EPILOGUE_USES(REGNO) \
+  ((REGNO) == 31 || (TARGET_ABICALLS && (REGNO) == GOT_VERSION_REGNUM))
 
 /* Treat LOC as a byte offset from the stack pointer and round it up
    to the next fully-aligned offset.  */
index af62a3399ac9b92c85aba4875e8ce27ca047fe7f..f22043416b4ab0ee1401fa4af7a617590c0733f0 100644 (file)
@@ -29,7 +29,7 @@
    (UNSPEC_GET_FNADDR           3)
    (UNSPEC_BLOCKAGE             4)
    (UNSPEC_CPRESTORE            5)
-   (UNSPEC_EH_RECEIVER          6)
+   (UNSPEC_RESTORE_GP           6)
    (UNSPEC_EH_RETURN            7)
    (UNSPEC_CONSTTABLE_INT       8)
    (UNSPEC_CONSTTABLE_FLOAT     9)
    (UNSPEC_MFHILO              26)
    (UNSPEC_TLS_LDM             27)
    (UNSPEC_TLS_GET_TP          28)
+   (UNSPEC_SET_GOT_VERSION     29)
+   (UNSPEC_UPDATE_GOT_VERSION  30)
 
    (UNSPEC_ADDRESS_FIRST       100)
 
-   (FAKE_CALL_REGNO            79)
+   (GOT_VERSION_REGNUM         79)
 
    ;; For MIPS Paired-Singled Floating Point Instructions.
 
 ;; frsqrt2      floating point reciprocal square root step2
 ;; multi       multiword sequence (or user asm statements)
 ;; nop         no operation
+;; ghost       an instruction that produces no real code
 (define_attr "type"
-  "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,xfer,mthilo,mfhilo,const,arith,shift,slt,clz,trap,imul,imul3,imadd,idiv,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop"
+  "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,xfer,mthilo,mfhilo,const,arith,shift,slt,clz,trap,imul,imul3,imadd,idiv,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop,ghost"
   (cond [(eq_attr "jal" "!unset") (const_string "call")
         (eq_attr "got" "load") (const_string "load")]
        (const_string "unknown")))
         (const_string "hilo")]
        (const_string "none")))
 
-;; Indicates which SET in an instruction pattern induces a hazard.
-;; Only meaningful when "hazard" is not "none".  SINGLE means that
-;; the pattern has only one set while the other values are indexes
-;; into a PARALLEL vector.
-;;
-;; Hazardous instructions with multiple sets should generally put the
-;; hazardous set first.  The only purpose of this attribute is to force
-;; each multi-set pattern to explicitly assert that this condition holds.
-(define_attr "hazard_set" "single,0"
-  (const_string "single"))
-
 ;; Is it a single instruction?
 (define_attr "single_insn" "no,yes"
   (symbol_ref "get_attr_length (insn) == (TARGET_MIPS16 ? 2 : 4)"))
 (define_cpu_unit "alu" "alu")
 (define_cpu_unit "imuldiv" "imuldiv")
 
+;; Ghost instructions produce no real code and introduce no hazards.
+;; They exist purely to express an effect on dataflow.
+(define_insn_reservation "ghost" 0
+  (eq_attr "type" "ghost")
+  "nothing")
+
 (include "4k.md")
 (include "5k.md")
 (include "24k.md")
   DONE;
 })
 
-(define_insn_and_split "exception_receiver"
+(define_expand "exception_receiver"
+  [(const_int 0)]
+  "TARGET_ABICALLS"
+{
+  /* See the comment above load_call<mode> for details.  */
+  emit_insn (gen_set_got_version ());
+
+  /* If we have a call-clobbered $gp, restore it from its save slot.  */
+  if (HAVE_restore_gp)
+    emit_insn (gen_restore_gp ());
+  DONE;
+})
+
+(define_expand "nonlocal_goto_receiver"
+  [(const_int 0)]
+  "TARGET_ABICALLS"
+{
+  /* See the comment above load_call<mode> for details.  */
+  emit_insn (gen_set_got_version ());
+  DONE;
+})
+
+;; Restore $gp from its .cprestore stack slot.  The instruction remains
+;; volatile until all uses of $28 are exposed.
+(define_insn_and_split "restore_gp"
   [(set (reg:SI 28)
-       (unspec_volatile:SI [(const_int 0)] UNSPEC_EH_RECEIVER))]
+       (unspec_volatile:SI [(const_int 0)] UNSPEC_RESTORE_GP))]
   "TARGET_ABICALLS && TARGET_OLDABI"
   "#"
   "&& reload_completed"
 ;; potentially modify the GOT entry.  And once a stub has been called,
 ;; we must not call it again.
 ;;
-;; We represent this restriction using an imaginary fixed register that
-;; is set by the GOT load and used by the call.  By making this register
-;; call-clobbered, and by making the GOT load the only way of setting
-;; the register, we ensure that the load cannot be moved past a call.
+;; We represent this restriction using an imaginary, fixed, call-saved
+;; register called GOT_VERSION_REGNUM.  The idea is to make the register
+;; live throughout the function and to change its value after every
+;; potential call site.  This stops any rtx value that uses the register
+;; from being computed before an earlier call.  To do this, we:
+;;
+;;    - Ensure that the register is live on entry to the function,
+;;     so that it is never thought to be used uninitalized.
+;;
+;;    - Ensure that the register is live on exit from the function,
+;;     so that it is live throughout.
+;;
+;;    - Make each call (lazily-bound or not) use the current value
+;;     of GOT_VERSION_REGNUM, so that updates of the register are
+;;     not moved across call boundaries.
+;;
+;;    - Add "ghost" definitions of the register to the beginning of
+;;     blocks reached by EH and ABNORMAL_CALL edges, because those
+;;     edges may involve calls that normal paths don't.  (E.g. the
+;;     unwinding code that handles a non-call exception may change
+;;     lazily-bound GOT entries.)  We do this by making the
+;;     exception_receiver and nonlocal_goto_receiver expanders emit
+;;     a set_got_version instruction.
+;;
+;;    - After each call (lazily-bound or not), use a "ghost"
+;;     update_got_version instruction to change the register's value.
+;;     This instruction mimics the _possible_ effect of the dynamic
+;;     resolver during the call and it remains live even if the call
+;;     itself becomes dead.
+;;
+;;    - Leave GOT_VERSION_REGNUM out of all register classes.
+;;     The register is therefore not a valid register_operand
+;;     and cannot be moved to or from other registers.
 (define_insn "load_call<mode>"
   [(set (match_operand:P 0 "register_operand" "=c")
        (unspec:P [(match_operand:P 1 "register_operand" "r")
-                  (match_operand:P 2 "immediate_operand" "")]
-                 UNSPEC_LOAD_CALL))
-   (set (reg:P FAKE_CALL_REGNO)
-       (unspec:P [(match_dup 2)] UNSPEC_LOAD_CALL))]
+                  (match_operand:P 2 "immediate_operand" "")
+                  (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))]
   "TARGET_ABICALLS"
   "<load>\t%0,%R2(%1)"
   [(set_attr "type" "load")
    (set_attr "mode" "<MODE>")
-   (set_attr "hazard_set" "0")
    (set_attr "length" "4")])
 
+(define_insn "set_got_version"
+  [(set (reg:SI GOT_VERSION_REGNUM)
+       (unspec_volatile:SI [(const_int 0)] UNSPEC_SET_GOT_VERSION))]
+  "TARGET_ABICALLS"
+  ""
+  [(set_attr "length" "0")
+   (set_attr "type" "ghost")])
+
+(define_insn "update_got_version"
+  [(set (reg:SI GOT_VERSION_REGNUM)
+       (unspec:SI [(reg:SI GOT_VERSION_REGNUM)] UNSPEC_UPDATE_GOT_VERSION))]
+  "TARGET_ABICALLS"
+  ""
+  [(set_attr "length" "0")
+   (set_attr "type" "ghost")])
+
 ;; Sibling calls.  All these patterns use jump instructions.
 
 ;; If TARGET_SIBCALLS, call_insn_operand will only accept constant
index 480c223bb9f10da50e8ecc8a8bf6e02ba1992a24..8c0804b7ea8586f3100ffc0051471c7a5eaf3130 100644 (file)
@@ -1,3 +1,16 @@
+2008-02-02  Richard Sandiford  <rsandifo@nildram.co.uk>
+
+       PR target/34981
+       * gcc.target/mips/lazy-binding-1.c: New test.
+       * gcc.target/mips/mips.exp (setup_mips_tests): Set mips_abi,
+       mips_forced_gp, mips_forced_no_abicalls, mips_forced_no_shared
+       and mips_forced_no_er.
+       (dg-mips-options): Avoid using -mips16 -mhard-float for ABIs
+       other than o32 and o64.  Avoid using -mabicalls with an implicit
+       -mabi=eabi.  Avoid using small data with -mabicalls.  Skip
+       -mabi=*, -G*, -mabicalls, -mshared and -mexplicit-relocs tests
+       if the multilib forces the an incompatible option.
+
 2008-02-01  Release Manager
 
        * GCC 4.2.3 released.
diff --git a/gcc/testsuite/gcc.target/mips/lazy-binding-1.c b/gcc/testsuite/gcc.target/mips/lazy-binding-1.c
new file mode 100644 (file)
index 0000000..eb4f808
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-mips-options "-mabicalls -mshared -mexplicit-relocs -O2 -fno-delayed-branch" } */
+
+void bar (void);
+
+void
+foo (int n)
+{
+  while (n--)
+    {
+      bar ();
+      bar ();
+    }
+}
+
+/* There should be exactly five uses of $25: one to set up $gp, two to
+   load the address of bar (), and two to call it.  */
+/* { dg-final { scan-assembler-times "\tl.\t\\\$25,%call16\\\(bar\\\)" 2 } } */
+/* { dg-final { scan-assembler-times "\tjalr\t\\\$25" 2 } } */
+/* { dg-final { scan-assembler "(\\\$28,|\t.cpload\t)\\\$25" } } */
+/* { dg-final { scan-assembler-times "\\\$25" 5 } } */
index 889fac9952abc1fe3b6ccf1d70211b87d2e97a16..f7a9137f9d25b03b1431b2926c9d317b61af2fd8 100644 (file)
@@ -33,22 +33,34 @@ load_lib gcc-dg.exp
 #    $mips_mips16:      true if MIPS16 mode is selected
 #    $mips_mips64:      true if 64-bit output is selected
 #    $mips_float:       "hard" or "soft"
+#    $mips_abi:                 the ABI specified by _MIPS_SIM
 #
 #    $mips_forced_isa:  true if the command line uses -march=* or -mips*
 #    $mips_forced_abi:  true if the command line uses -mabi=* or -mgp*
 #    $mips_forced_float: true if the command line uses -mhard/soft-float
 #    $mips_forced_le    true if the command line uses -EL or -mel
+#    $mips_forced_gp    true if the command line forces a particular GP mode
+#    $mips_forced_no_abicalls
+#                       true if the command line contains -mno-abicalls
+#    $mips_forced_no_shared
+#                       true if the command line contains -mno-shared
+#    $mips_forced_no_er  true if the command line contains -mno-explicit-relocs
 proc setup_mips_tests {} {
     global mips_isa
     global mips_arch
     global mips_mips16
     global mips_mips64
     global mips_float
+    global mips_abi
 
     global mips_forced_isa
     global mips_forced_abi
     global mips_forced_float
     global mips_forced_le
+    global mips_forced_gp
+    global mips_forced_no_abicalls
+    global mips_forced_no_shared
+    global mips_forced_no_er
 
     global compiler_flags
     global tool
@@ -69,6 +81,17 @@ proc setup_mips_tests {} {
        #else
        const char *float = "soft";
        #endif
+       #if !defined _MIPS_SIM
+       const char *abi = "unknown";
+       #elif _MIPS_SIM==_ABIO32
+       const char *abi = "32";
+       #elif _MIPS_SIM==_ABIO64
+       const char *abi = "o64";
+       #elif _MIPS_SIM==_ABIN32
+       const char *abi = "n32";
+       #else
+       const char *abi = "64";
+       #endif
     }
     close $f
     set output [${tool}_target_compile $src "" preprocess ""]
@@ -79,11 +102,16 @@ proc setup_mips_tests {} {
     set mips_mips16 [regexp {mips16 = 1} $output]
     set mips_mips64 [regexp {mips64 = 1} $output]
     regexp {float = "([^"]*)} $output dummy mips_float
+    regexp {abi = "([^"]*)} $output dummy mips_abi
 
     set mips_forced_isa [regexp -- {(-mips|-march)} $compiler_flags]
     set mips_forced_abi [regexp -- {(-mgp|-mabi)} $compiler_flags]
     set mips_forced_float [regexp -- {-m(hard|soft)-float} $compiler_flags]
     set mips_forced_le [regexp -- {-(EL|mel)[[:>:]]} $compiler_flags]
+    set mips_forced_gp [regexp -- {-(G|mabicalls)} $compiler_flags]
+    set mips_forced_no_abicalls [regexp -- {-mno-abicalls} $compiler_flags]
+    set mips_forced_no_shared [regexp -- {-mno-shared} $compiler_flags]
+    set mips_forced_no_er [regexp -- {-mno-explicit-relocs} $compiler_flags]
 }
 
 # Return true if command-line option FLAG forces 32-bit code.
@@ -110,6 +138,12 @@ proc is_gp32_flag {flag} {
 #    -mno-mips16
 #      Skip the test for MIPS16 targets.
 #
+#    -mabi=*
+#      Force a particular ABI.  Skip the test if the multilib flags
+#      force a specific ABI or a different register size.  If testing
+#      MIPS16 multilibs, try to force -msoft-float for ABIs other than
+#      o32 and o64, and skip the test if this is not possible.
+#
 #    -march=*
 #    -mips*
 #      Select the target architecture.  Skip the test for MIPS16 targets
@@ -123,6 +157,20 @@ proc is_gp32_flag {flag} {
 #    -EB
 #      Select big-endian code.  Skip the test if the multilib flags
 #      force a little-endian target.
+#
+#    -G*
+#      Select the small-data mode, and -mno-abcialls.  Skip the test if
+#      the multilib flags already contain such an option, or specify
+#      something that might be incompatible with them.
+#
+#    -mabicalls
+#    -mshared
+#      Select the form of SVR4 PIC.  Skip the test if the multilib flags
+#      conflict with the required setting.
+#
+#    -mexplicit-relocs
+#      Select explicit relocations.  Skip the test if the multilib flags
+#      force -mno-explicit-relocs.
 proc dg-mips-options {args} {
     upvar dg-extra-tool-flags extra_tool_flags
     upvar dg-do-what do_what
@@ -132,16 +180,34 @@ proc dg-mips-options {args} {
     global mips_mips16
     global mips_mips64
     global mips_float
+    global mips_abi
 
     global mips_forced_isa
     global mips_forced_abi
     global mips_forced_float
     global mips_forced_le
+    global mips_forced_gp
+    global mips_forced_no_abicalls
+    global mips_forced_no_shared
+    global mips_forced_no_er
 
     set flags [lindex $args 1]
     set matches 1
 
-    # First handle the -mgp* options.  Add an architecture option if necessary.
+    foreach flag $flags {
+       if {[regexp -- {^-mabi=(.*)} $flag dummy abi]
+           && $mips_mips16
+           && $abi != "32"
+           && $abi != "o64"} {
+           if {[lsearch $flags -mhard-float] >= 0} {
+               set matches 0
+           } else {
+               append flags " -msoft-float"
+           }
+       }
+    }
+
+    # Handle the -mgp* options.  Add an architecture option if necessary.
     foreach flag $flags {
        if {[is_gp32_flag $flag] && $mips_mips64} {
            if {$mips_forced_abi} {
@@ -160,12 +226,35 @@ proc dg-mips-options {args} {
            }
        }
     }
+
+    foreach flag $flags {
+       if {[string match -mabicalls $flag]} {
+           # EABI has no SVR4-style PIC mode, so if we don't know for
+           # sure what the default ABI is, try to force an appropriate one.
+           if {$mips_abi == "unknown" && [lsearch $flags "-mabi=*"] < 0} {
+               if {!$mips_mips64} {
+                   append flags " -mabi=32"
+               } else {
+                   append flags " -mabi=n32"
+               }
+           }
+           # Turn off small data, if on by default.  Also turn off
+           # MIPS16 code generation.  (We don't support -mabicalls
+           # with either.)
+           append flags " -G0 -mno-mips16"
+       }
+    }
+
     # Handle the other options.
     foreach flag $flags {
        if {$flag == "-mno-mips16"} {
            if {$mips_mips16} {
                set matches 0
            }
+       } elseif {[regexp -- {^-mabi=(.*)} $flag dummy abi]} {
+           if {$abi != $mips_abi && $mips_forced_abi} {
+               set matches 0
+           }
        } elseif {[regexp -- {^-march=(.*)} $flag dummy arch]} {
            if {$mips_mips16 || ($arch != $mips_arch && $mips_forced_isa)} {
                set matches 0
@@ -182,6 +271,25 @@ proc dg-mips-options {args} {
            if {$mips_forced_le} {
                set matches 0
            }
+       } elseif {[regexp -- {^-G} $flag]} {
+           if {$flag != "-G0"} {
+               append flags " -mno-abicalls"
+           }
+           if {$mips_forced_gp} {
+               set matches 0
+           }
+       } elseif {[regexp -- {^-mabicalls$} $flag]} {
+           if {$mips_forced_no_abicalls} {
+               set matches 0
+           }
+       } elseif {[regexp -- {^-mshared$} $flag]} {
+           if {$mips_forced_no_shared} {
+               set matches 0
+           }
+       } elseif {[regexp -- {^-mexplicit-relocs$} $flag]} {
+           if {$mips_forced_no_er} {
+               set matches 0
+           }
        }
     }
     if {$matches} {