This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFA/C: Lazily-bound GOT entries


This follows on from a discussion on #gcc yesterday.

While looking at the char_pointer_func.f90 failure on MIPS, I noticed
that we were (still) loading lazily-bound GOT entries before earlier
calls, and could therefore call the same lazy-binding stub twice.
This is a 4.3 regression.  (It's also a 4.2 regression if it
happens there.)

My last attempt at a fix was:

    http://gcc.gnu.org/ml/gcc-patches/2007-10/msg01062.html

explained here:

    http://gcc.gnu.org/ml/gcc-patches/2007-10/msg00980.html

That patch stopped GCC from moving an existing GOT load across
a call, but the optimisers were still smart enough to realise
that the loaded value was modelled as a constant.  Thus if we had:

        lw $reg1,%call16(alloc)($28)
        jalr $reg1
        lw $reg2,%call16(alloc)($28)
        jalr $reg2

we might change the last call to "jalr $reg1" while still keeping
the second load.  We might then call alloc's lazy-binding stub twice.

Stepping back a bit, the MIPS port uses "load_call<mode>" instructions
to load the contents of a (potentially) lazily-bound GOT entry.
In general, each call might change the GOT entry from a lazy-binding
stub to the real address, so we want to prevent load_call<mode>s from
being computed before earlier calls.

The fundamental problem is that we have a real dataflow dependence
between calls and load_call<mode>s, but this dependence is not in
itself a reason to keep the call around.  For example, if we have a
libcall that is otherwise dead, we should not keep it simply because
of the later load_call<mode>s.

In 4.1 and earlier, the load_call<mode>s used an imaginary
call-clobbered register, FAKE_CALL_REGNO.  At that time, we would not
move any use of a call-clobbered register across a call, and all was
well.  However, now that we have a proper df framework, we can only
expect this to work if there is a real D-U chain between the calls and
load_code<mode>s; if there isn't, the use simply looks like an invariant.

There are various ways we could fix this, including (but not limited to!):

  1. Modelling the load as a memory access and relying on the usual
     assumption that calls modify memory.  This has two problems:

       - even const and pure calls can change lazily-bound GOT entries
       - the accesses would unnecessarily alias accesses to set 0

     (So, one correctness problem and one optimisation problem.)

     As far as the correctness problem goes, we would need to say
     that const and pure calls do in fact clobber _some_ memory,
     but do not clobber anything in alias set 0.  That seems like
     a contradiction in our current infrastructure.

     One way of fixing this would be to allow several independent
     alias trees, rather than one single tree rooted at alias set 0.
     However, that's not stage 3 material, and every piece of code
     that handles calls' memory clobbers would potentially need to
     know about this special case (especially those that check for
     constness and pureness).

  2. Having a special kind of D-U chain that is weaker than normal:
     to paraphrase Kenny, it would serialise without being a real
     data dependency.

     I floated this as a strawman, but no-one on #gcc (including me)
     liked the idea.  It would push the onus down to every DF consumer
     that cares about such things.

  3. Giving up on the idea of allowing libcalls to be deleted.  I don't
     think this is acceptable.

  4. Sticking to the idea of using a "GOT version" register.  We could
     make the register call-saved and:

       - 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 the GOT version register, so that updates of the register
         are not moved across call boundaries.

       - Add artificial 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.)

       - After each call (lazily-bound of not), use a "ghost"
         instruction to update the GOT version (i.e. use an instruction
         that doesn't emit any code).  This instruction mimics the
         _possible_ effect of the dynamic resolver during the call and
         remains live even if the call itself becomes dead.  It is not
         part of a libcall block.

       - Leave the GOT version out of all register classes.  The
         register is therefore not a valid register_operand and
         (with appropriately-defined move patterns) cannot be moved
         to or from other registers.

     In summary, the register is live throughout the function and,
     because of the register_operand restriction, the optimizers cannot
     temporarily use it for anything else (not even by moving the old
     value to a temporary register and then moving it back).  This means
     that an instruction that uses the result of an update_got_version
     cannot be computed before an earlier call.

As you can probably tell by the length, (4) is my "preferred" option,
for a very loose definition of "preferred".  The idea of having a "GOT
version number" is (and always has been) a hack, but if you accept that
it's OK to have such a thing in principle, I think (4) is actually a
fairly accurate way of modelling it.  The necessary code is conditional
on the target using such a register, so it should be safe for stage 3.

Using a dataflow-only insn is similar in spirit to things like stack ties
and scheduling barriers.

Dan said that it would be nice to have some way of detecting when a
function has definitely been called once, and where we therefore know
that the GOT entry must point at the real address.  I agree, but the
only ways I can think of doing this are invasive.  (On the upside,
if we had a proper rtl representation for lazily-bound values,
we might be able to get rid of the fake register.)

There was also a sentiment that the infrastructure shouldn't be
MIPS-specific.  That seems reasonable, so if we do (4), we should
probably move it outside the MIPS backend.

The patch below does this.  It replaces MIPS's FAKE_CALL_REGNO with
a target-independent GOT_VERSION_REGNUM and adds a new named pattern
(update_got_version) for modelling the effect of the dynamic resolver.
The implementation follows fairly directly from the description above.

The only change not guarded by GOT_VERSION_REGNUM != INVALID_REGNUM
is the one to gcse, which currently doesn't model DF's artificial
definitions.  While we could add special code specifically for
GOT_VERSION_REGNUM, I think it is safer to honour _all_ the
artificial definitions.  This is one step towards the eventual
goal of having entirely df-based dataflow.

Bootstrapped & regression-tested on x86_64-linux-gnu.  Also
regression-tested on mips64-linux-gnu.  I compiled gcc.c-torture,
gcc.dg and g++.dg with these options:

    -O2
    -O3
    -O2 -march=r9000
    -O3 -march=sb1
    -O2 -march=4kc -mabi=32
    -O3 -march=74kf -mabi=32
    -O2 -march=r7000 -mabi=64
    -O3 -march=5kf -mabi=64

and could find no remaining examples of code where we reused
load_call<mode>s across calls.

What do you think?  I'm going away for a few days, so please feel
free to rip the patch to shreds in my absence.

Richard

PS. I have a follow-on patch that uses the new MIPS "ghost" type
    for blockages too.


gcc/
	* doc/tm.texi: Document GOT_VERSION_REGNUM.
	* doc/md.texi: Document update_got_version.
	* defaults.h (GOT_VERSION_REGNUM): Define.
	* expr.h (emit_update_got_version): Declare.
	* builtins.c (expand_builtin_apply): Call emit_update_got_version.
	* calls.c (expand_call, expand_library_call_value_1): Likewise.
	(emit_update_got_version): New function.
	* df-scan.c (df_bb_refs_collect): Only compute bb_has_eh_pred once.
	Add artificial definitions of GOT_VERSION_REGNUM for exception
	handlers and nonlocal-goto receivers.
	(df_get_entry_block_def_set): Include GOT_VERSION_REGNUM.
	(df_get_exit_block_use_set): Likewise.
	* gcse.c (compute_hash_table_work): Record artificial definitions
	at the top of the block.

	* config/mips/mips.h (FIRST_PSEUDO_REGISTER): Update comment
	after renaming FAKE_CALL_REGNO to C_GOT_VERSION_REGNUM.
	(CALL_REALLY_USED_REGISTERS): Set the C_GOT_VERSION_REGNUM entry to 0.
	(GOT_VERSION_REGNUM): Define.
	* config/mips/mips.c (mips_expand_call): Don't use FAKE_CALL_REGNO.
	(mips_hard_regno_mode_ok_p): Allow SImode for C_GOT_VERSION_REGNUM.
	(mips_avoid_hazard): Remove hazard_set handling.
	* config/mips/mips.md (UNSPEC_UPDATE_GOT_VERSION): New constant.
	(FAKE_CALL_REGNO): Rename to...
	(C_GOT_VERSION_REGNUM): ...this.
	(type): Add "ghost".  Add an insn_reservation for it.
	(hazard_set): Delete.
	(load_call<mode>): Don't set FAKE_CALL_REGNUM; use
	C_GOT_VERSION_REGNUM instead.  Remove hazard_set attribute.
	(update_got_version): New instruction.

Index: gcc/doc/tm.texi
===================================================================
--- gcc/doc/tm.texi	2008-01-10 09:09:53.000000000 +0000
+++ gcc/doc/tm.texi	2008-01-10 12:56:48.000000000 +0000
@@ -45,6 +45,7 @@ through the macros defined in the @file{
 * Scheduling::          Adjusting the behavior of the instruction scheduler.
 * Sections::            Dividing storage into text, data, and other sections.
 * PIC::			Macros for position independent code.
+* Lazy Binding::        Macros relating to lazily-bound functions.
 * Assembler Format::    Defining how to write insns and pseudo-ops to output.
 * Debugging Info::      Defining the format of debugging output.
 * Floating Point::      Handling floating point for cross-compilers.
@@ -4728,7 +4729,8 @@ function.  This hook only needs to be de
 cannot be found by examination of FUNCTION_ARG_REGNO_P, the callee saved
 registers, STATIC_CHAIN_INCOMING_REGNUM, STATIC_CHAIN_REGNUM,
 TARGET_STRUCT_VALUE_RTX, FRAME_POINTER_REGNUM, EH_USES,
-FRAME_POINTER_REGNUM, ARG_POINTER_REGNUM, and the PIC_OFFSET_TABLE_REGNUM.
+FRAME_POINTER_REGNUM, ARG_POINTER_REGNUM, GOT_VERSION_REGNUM
+and the PIC_OFFSET_TABLE_REGNUM.
 @end deftypefn
 
 @node Stack Smashing Protection
@@ -6717,6 +6719,75 @@ check it either.  You need not define th
 position independent code.
 @end defmac
 
+@node Lazy Binding
+@section Lazy Binding
+@cindex @code{update_got_version} instruction pattern
+@cindex lazy binding
+@cindex GOT
+@cindex PIC
+
+Some ports need to model the effect of the dynamic resolver on
+the global offset table (GOT).  For example, rather than go through
+a procedure linkage table, some ports may load the address of a
+lazily-bound function directly from a GOT entry.  Depending on the
+static and dynamic linking options, this GOT entry may initially
+point either at the target function or at a lazy-binding stub;
+in the latter case, the first call to the stub will replace the
+GOT entry with the function's real address.
+
+Without information to the contrary, GCC must assume that every call
+could change the value of a lazily-bound GOT entry.  Note that this
+applies even to const and pure calls, which do not otherwise modify
+visible memory.  For this reason, it is usually not a good idea to
+model the GOT access as a @code{MEM} rtx.
+
+Code should not call the same lazy-binding stub twice, so in general,
+GCC should not move an instruction that loads a lazily-bound GOT entry
+before an earlier call.  A port can model this restriction by setting
+aside an imaginary hard register, @code{GOT_VERSION_REGNUM}, to hold the
+current ``GOT version''.  GCC updates this register after any sequence
+that @emph{might} call the dynamic resolver; this update takes the form
+of either an artificial definition at the beginning of a basic block,
+or an @code{update_got_version} instruction.  Instructions that load
+lazily-bound GOT entries should then @code{USE} this register to
+ensure proper placement with respect to earlier calls.
+
+For example, the MIPS port uses an instruction like this:
+
+@smallexample
+(define_insn "load_call<mode>"
+  [(set (match_operand:P 0 "register_operand" "=d")
+        (unspec:P [(match_operand:P 1 "register_operand" "d")
+                   (match_operand:P 2 "immediate_operand")
+                   (reg:SI C_GOT_VERSION_REGNUM)]
+                  UNSPEC_LOAD_CALL))]
+  "TARGET_USE_GOT"
+  "<load>\t%0,%R2(%1)"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")
+   (set_attr "length" "4")])
+@end smallexample
+
+Here, @code{C_GOT_VERSION_REGNUM} is an .md constant that
+holds @code{GOT_VERSION_REGNUM}.  Operand 1 is the PIC register
+and operand 2 is the target function.
+
+@defmac GOT_VERSION_REGNUM
+The imaginary hard register that should be used as a GOT version register,
+or @code{INVALID_REGNUM} if none.  The default is @code{INVALID_REGNUM}.
+
+When @code{GOT_VERSION_REGNUM} is not @code{INVALID_REGNUM},
+the register's entry in @code{FIXED_REGISTERS} and @code{CALL_USED_REGISTERS}
+must be 1.  Its entry in @code{CALL_REALLY_USED_REGISTERS} must be 0.
+It must not belong to any register class but must allow a mode that
+is consistent with its uses and definitions in the @file{.md} file.
+For example, the MIPS case above requires:
+@smallexample
+HARD_REGNO_MODE_OK (GOT_VERSION_REGNUM, SImode)
+@end smallexample
+to be true.
+@end defmac
+
 @node Assembler Format
 @section Defining the Output Assembler Language
 
Index: gcc/doc/md.texi
===================================================================
--- gcc/doc/md.texi	2008-01-10 10:02:30.000000000 +0000
+++ gcc/doc/md.texi	2008-01-10 10:07:20.000000000 +0000
@@ -5176,6 +5176,29 @@ inclusive and operand 1 exclusive.
 If this pattern is not defined, a call to the library function
 @code{__clear_cache} is used.
 
+@cindex @code{update_got_version} instruction pattern
+@cindex lazy binding
+@cindex GOT
+@cindex PIC
+@item @samp{update_got_version}
+
+If the target has a @code{GOT_VERSION_REGNUM}, this pattern should
+mimic the effects of the dynamic resolver by using and setting
+that register.  A typical pattern is:
+
+@smallexample
+(define_insn "update_got_version"
+  [(set (reg:SI GOT_VERSION_REGNUM)
+        (unspec:SI [(reg:SI GOT_VERSION_REGNUM)]
+                   UNSPEC_UPDATE_GOT_VERSION))]
+  ""
+  ""
+  [(set_attr "length" "0")])
+@end smallexample
+
+Do not provide this pattern if your port does not use
+@code{GOT_VERSION_REGNUM}.
+
 @end table
 
 @end ifset
Index: gcc/defaults.h
===================================================================
--- gcc/defaults.h	2008-01-09 18:03:39.000000000 +0000
+++ gcc/defaults.h	2008-01-09 18:06:49.000000000 +0000
@@ -496,6 +496,10 @@ #define POINTER_SIZE BITS_PER_WORD
 #define PIC_OFFSET_TABLE_REGNUM INVALID_REGNUM
 #endif
 
+#ifndef GOT_VERSION_REGNUM
+#define GOT_VERSION_REGNUM INVALID_REGNUM
+#endif
+
 #ifndef TARGET_DLLIMPORT_DECL_ATTRIBUTES
 #define TARGET_DLLIMPORT_DECL_ATTRIBUTES 0
 #endif
Index: gcc/expr.h
===================================================================
--- gcc/expr.h	2008-01-09 17:33:50.000000000 +0000
+++ gcc/expr.h	2008-01-09 18:24:29.000000000 +0000
@@ -586,6 +586,8 @@ extern bool shift_return_value (enum mac
 
 extern rtx expand_call (tree, rtx, int);
 
+extern void emit_update_got_version (void);
+
 extern void fixup_tail_calls (void);
 
 #ifdef TREE_CODE
Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	2008-01-09 17:53:58.000000000 +0000
+++ gcc/builtins.c	2008-01-09 18:00:58.000000000 +0000
@@ -1546,6 +1546,8 @@ expand_builtin_apply (rtx function, rtx 
 #endif
     gcc_unreachable ();
 
+  emit_update_got_version ();
+
   /* Find the CALL insn we just emitted, and attach the register usage
      information.  */
   call_insn = last_call_insn ();
Index: gcc/calls.c
===================================================================
--- gcc/calls.c	2008-01-09 17:34:25.000000000 +0000
+++ gcc/calls.c	2008-01-10 11:37:54.000000000 +0000
@@ -3060,6 +3060,8 @@ expand_call (tree exp, rtx target, int i
 	    }
 	}
 
+      emit_update_got_version ();
+
       /* If size of args is variable or this was a constructor call for a stack
 	 argument, restore saved stack-pointer value.  */
 
@@ -3185,6 +3187,24 @@ expand_call (tree exp, rtx target, int i
   return target;
 }
 
+/* If the target uses update_got_version instructions, emit one after
+   the last call.  */
+
+void
+emit_update_got_version (void)
+{
+#ifdef HAVE_update_got_version
+  if (GOT_VERSION_REGNUM != INVALID_REGNUM
+      && HAVE_update_got_version)
+    {
+      rtx call = last_call_insn ();
+      use_reg (&CALL_INSN_FUNCTION_USAGE (call),
+	       regno_reg_rtx[GOT_VERSION_REGNUM]);
+      emit_insn (gen_update_got_version ());
+    }
+#endif
+}
+
 /* A sibling call sequence invalidates any REG_EQUIV notes made for
    this function's incoming arguments.
 
@@ -3967,6 +3987,8 @@ emit_library_call_value_1 (int retval, r
 	}
     }
 
+  emit_update_got_version ();
+
   if (ACCUMULATE_OUTGOING_ARGS)
     {
 #ifdef REG_PARM_STACK_SPACE
Index: gcc/df-scan.c
===================================================================
--- gcc/df-scan.c	2008-01-09 18:03:03.000000000 +0000
+++ gcc/df-scan.c	2008-01-10 09:07:31.000000000 +0000
@@ -3248,6 +3248,8 @@ df_need_static_chain_reg (struct functio
 static void
 df_bb_refs_collect (struct df_collection_rec *collection_rec, basic_block bb)
 {
+  bool has_eh_pred_p;
+
   collection_rec->next_def = 0;
   collection_rec->next_use = 0;
   collection_rec->next_eq_use = 0;
@@ -3264,8 +3266,10 @@ df_bb_refs_collect (struct df_collection
       return;
     }
 
+  has_eh_pred_p = bb_has_eh_pred (bb);
+
 #ifdef EH_RETURN_DATA_REGNO
-  if (bb_has_eh_pred (bb))
+  if (has_eh_pred_p)
     {
       unsigned int i;
       /* Mark the registers that will contain data for the handler.  */
@@ -3282,7 +3286,7 @@ df_bb_refs_collect (struct df_collection
 
 
 #ifdef EH_USES
-  if (bb_has_eh_pred (bb))
+  if (has_eh_pred_p)
     {
       unsigned int i;
       /* This code is putting in an artificial ref for the use at the
@@ -3303,6 +3307,11 @@ df_bb_refs_collect (struct df_collection
     }
 #endif
 
+  if (GOT_VERSION_REGNUM != INVALID_REGNUM
+      && (has_eh_pred_p || (bb->flags & BB_NON_LOCAL_GOTO_TARGET)))
+    df_ref_record (collection_rec, regno_reg_rtx[GOT_VERSION_REGNUM], NULL,
+		   bb, NULL, DF_REF_REG_DEF, DF_REF_AT_TOP);
+
   /* Add the hard_frame_pointer if this block is the target of a
      non-local goto.  */
   if (bb->flags & BB_NON_LOCAL_GOTO_TARGET)
@@ -3314,9 +3323,9 @@ df_bb_refs_collect (struct df_collection
     {
       bitmap_iterator bi;
       unsigned int regno;
-      bitmap au = bb_has_eh_pred (bb) 
-	? df->eh_block_artificial_uses 
-	: df->regular_block_artificial_uses;
+      bitmap au = (has_eh_pred_p
+		   ? df->eh_block_artificial_uses
+		   : df->regular_block_artificial_uses);
 
       EXECUTE_IF_SET_IN_BITMAP (au, 0, regno, bi)
 	{
@@ -3504,7 +3513,10 @@ df_get_entry_block_def_set (bitmap entry
 	bitmap_set_bit (entry_block_defs, i);
 #endif
     }
-      
+
+  if (GOT_VERSION_REGNUM != INVALID_REGNUM)
+    bitmap_set_bit (entry_block_defs, GOT_VERSION_REGNUM);
+
   /* Once the prologue has been generated, all of these registers
      should just show up in the first regular block.  */
   if (HAVE_prologue && epilogue_completed)
@@ -3718,6 +3730,9 @@ df_get_exit_block_use_set (bitmap exit_b
     if (global_regs[i] || EPILOGUE_USES (i))
       bitmap_set_bit (exit_block_uses, i);
   
+  if (GOT_VERSION_REGNUM != INVALID_REGNUM)
+    bitmap_set_bit (exit_block_uses, GOT_VERSION_REGNUM);
+
   if (HAVE_epilogue && epilogue_completed)
     {
       /* Mark all call-saved registers that we actually used.  */
Index: gcc/gcse.c
===================================================================
--- gcc/gcse.c	2008-01-09 20:15:06.000000000 +0000
+++ gcc/gcse.c	2008-01-09 20:23:02.000000000 +0000
@@ -2041,6 +2041,7 @@ compute_hash_table_work (struct hash_tab
 
   FOR_EACH_BB (current_bb)
     {
+      struct df_ref **def;
       rtx insn;
       unsigned int regno;
       int in_libcall_block;
@@ -2050,6 +2051,10 @@ compute_hash_table_work (struct hash_tab
 	 ??? hard-reg reg_set_in_block computation
 	 could be moved to compute_sets since they currently don't change.  */
 
+      for (def = df_get_artificial_defs (current_bb->index); *def; def++)
+	if (DF_REF_FLAGS_IS_SET (*def, DF_REF_AT_TOP))
+	  record_last_set_info (DF_REF_REG (*def), NULL, BB_HEAD (current_bb));
+
       FOR_BB_INSNS (current_bb, insn)
 	{
 	  if (! INSN_P (insn))
Index: gcc/config/mips/mips.h
===================================================================
--- gcc/config/mips/mips.h	2008-01-08 23:26:24.000000000 +0000
+++ gcc/config/mips/mips.h	2008-01-09 19:42:28.000000000 +0000
@@ -1372,7 +1372,7 @@ #define CLZ_DEFINED_VALUE_AT_ZERO(MODE, 
    - 3 fake registers:
 	- ARG_POINTER_REGNUM
 	- FRAME_POINTER_REGNUM
-	- FAKE_CALL_REGNO (see the comment above load_callsi for details)
+	- C_GOT_VERSION_REGNUM
    - 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  */
@@ -1452,7 +1452,7 @@ #define CALL_REALLY_USED_REGISTERS      
   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,			\
@@ -1623,6 +1623,8 @@ #define PIC_OFFSET_TABLE_REGNUM \
   (reload_completed ? REGNO (pic_offset_table_rtx) : GLOBAL_POINTER_REGNUM)
 
 #define PIC_FUNCTION_ADDR_REGNUM (GP_REG_FIRST + 25)
+
+#define GOT_VERSION_REGNUM (TARGET_USE_GOT ? C_GOT_VERSION_REGNUM : 0)
 
 /* Define the classes of registers for register constraints in the
    machine description.  Also define ranges of constants.
Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	2008-01-08 23:26:23.000000000 +0000
+++ gcc/config/mips/mips.c	2008-01-09 19:42:02.000000000 +0000
@@ -5584,15 +5584,9 @@ mips_expand_call (rtx result, rtx addr, 
 
   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.  */
+  /* Lazy-binding stubs require $gp to be valid on entry.  */
   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));
-    }
+    use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
 }
 
 /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
@@ -8720,6 +8714,9 @@ mips_hard_regno_mode_ok_p (unsigned int 
   if (ALL_COP_REG_P (regno))
     return class == MODE_INT && size <= UNITS_PER_WORD;
 
+  if (regno == C_GOT_VERSION_REGNUM)
+    return mode == SImode;
+
   return false;
 }
 
@@ -11360,7 +11357,7 @@ mips_avoid_hazard (rtx after, rtx insn, 
 		   rtx *delayed_reg, rtx lo_reg)
 {
   rtx pattern, set;
-  int nops, ninsns, hazard_set;
+  int nops, ninsns;
 
   pattern = PATTERN (insn);
 
@@ -11406,15 +11403,8 @@ mips_avoid_hazard (rtx after, rtx insn, 
 	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;
       }
Index: gcc/config/mips/mips.md
===================================================================
--- gcc/config/mips/mips.md	2008-01-08 23:26:23.000000000 +0000
+++ gcc/config/mips/mips.md	2008-01-10 11:35:34.000000000 +0000
@@ -58,10 +58,11 @@ (define_constants
    (UNSPEC_SYNC_NEW_OP		39)
    (UNSPEC_SYNC_EXCHANGE	40)
    (UNSPEC_MEMORY_BARRIER	41)
+   (UNSPEC_UPDATE_GOT_VERSION	42)
    
    (UNSPEC_ADDRESS_FIRST	100)
 
-   (FAKE_CALL_REGNO		79)
+   (C_GOT_VERSION_REGNUM	79)
 
    ;; For MIPS Paired-Singled Floating Point Instructions.
 
@@ -290,8 +291,9 @@ (define_attr "jal_macro" "no,yes"
 ;; 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,mfc,mtc,mthilo,mfhilo,const,arith,logical,shift,slt,signext,clz,trap,imul,imul3,imadd,idiv,move,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,mfc,mtc,mthilo,mfhilo,const,arith,logical,shift,slt,signext,clz,trap,imul,imul3,imadd,idiv,move,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")))
@@ -438,17 +440,6 @@ (define_attr "hazard" "none,delay,hilo"
 	 (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)"))
@@ -703,6 +694,12 @@ (define_automaton "alu,imuldiv")
 (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 "20kc.md")
@@ -5623,30 +5620,26 @@ (define_insn_and_split "exception_receiv
 ;; point to a function or to a lazy binding stub.  In the latter case,
 ;; the stub will use the dynamic linker to resolve the function, which
 ;; in turn will change the GOT entry to point to the function's real
-;; address.
-;;
-;; This means that every call, even pure and constant ones, can
-;; 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.
+;; address.  We model these changes using GOT_VERSION_REGNUM.
 (define_insn "load_call<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
-	(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))]
+	(unspec:P [(match_operand:P 1 "register_operand" "d")
+		   (match_operand:P 2 "immediate_operand")
+		   (reg:SI C_GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))]
   "TARGET_USE_GOT"
   "<load>\t%0,%R2(%1)"
   [(set_attr "type" "load")
    (set_attr "mode" "<MODE>")
-   (set_attr "hazard_set" "0")
    (set_attr "length" "4")])
 
+(define_insn "update_got_version"
+  [(set (reg:SI C_GOT_VERSION_REGNUM)
+	(unspec:SI [(reg:SI C_GOT_VERSION_REGNUM)] UNSPEC_UPDATE_GOT_VERSION))]
+  ""
+  ""
+  [(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 Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]