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]

Doing mips relocs the alpha way


Since my patch to add LO-SUM-related target macros was rejected,
I thought I'd have a go at doing mips relocs in a more alpha-like way.
Here's the result.  It turned out to be much easier than I'd expected. ;)

Even so, I'm afraid the patch is quite invasive.  Since it's confined
to the mips port, I hope it's still a candidate for stage 3.

Summmary
--------

To recap -- and ignoring mips16 for now -- the current explicit reloc
code works as follows:

    All symbolic constants are lowered at the outset.  The lowered
    form uses constant UNSPECs to represent values like "%got_disp(foo)".
    All these unspecs are 16-bit values and can be used either as
    arith_operands or as offsets in memory addresses.

    For example, assuming -mabicalls -mabi=n32 -mno-xgot, the value of
    a global symbol foo would be loaded with an expression such as:

	(set (reg dest)
	     (mem (plus (reg $gp)
			(const (unspec [(symbol_ref "foo")]
				       RELOC_GOT_DISP)))))

    which represents:

	lw dest,%got_disp(foo)($gp)

The intent of the patch below is that:

    All symbolic constants are initially kept in their original form or
    split into a HIGH/LO_SUM pair.  After reload, a set of define_splits
    will replace implicit uses of $gp with explicit uses.

    For example, the insn above would at first be represented as:

	(set (reg dest)
	     (symbol_ref "foo"))

    and, after reload, would be lowered into:

	(set (reg dest)
	     (mem (lo_sum (reg $gp)
		          (const (unspec [(symbol_ref "foo")]
				         UNSPEC_ADDRESS_FIRST
					 + SYMBOL_GOTOFF_GLOBAL)))))

    (More about this representation below.)

Splitting
---------

In the old code, mips_splittable_symbol_p decided whether we should
rewrite a symbolic constant and mips_legitimize_symbol actually did
the rewriting.  There were several different ways of legitimising the
constant, depending on the type of symbol.

In the new code, there's only one sort of rewrite we have to worry
about before reload, namely splitting things into a HIGH/LO_SUM pair.
The patch uses a new array, mips_split_p[], to decide whether this
should be done for a particular type of symbol.

Taking each of the affected symbol types in turn:

    SYMBOL_SMALL_DATA:
	mips_split_p[] is false.  We keep the original constant until
	after reload and then generate a LO_SUM with $gp.

    SYMBOL_GOT_GLOBAL (-mno-xgot)
	Again, mips_split_p[] is false, and we keep the original constant
	until after reload.  New patterns match the symbolic form and
	"split" it into a GOT access after reload.

    SYMBOL_GOT_GLOBAL (-mxgot)
	mips_split_p[] is true.  New patterns match the HIGH and LO_SUM
	parts and lower them after reload.  The HIGH part is lowered into
	an lui and addition, the LO_SUM into a GOT load.

    SYMBOL_GOT_LOCAL:
	Both the old and new code would split these into two, but the
	high part of the symbol is now provided by a HIGH rather than
	a load from the GOT.  Again, there are new patterns to match
	the HIGH part and lower it into a GOT access after reload.

Small data addresses are legitimate inside MEMs and can therefore appear
in lots of different instructions.  As with alpha, there's a special
define_split to handle this:

    (define_split
      [(match_operand 0 "small_data_pattern" "")]
      "reload_completed"
      [(match_dup 0)]
      { operands[0] = mips_rewrite_small_data (operands[0]); })

I've put this at the end of mips.md so that it doesn't override
anything else.

Note that any post-reload splitter which handles memory operands should pass
them through mips_rewrite_small_data.  At the moment, only the 64-bit move
splitters are affected.  A one-line patch to mips_subword() deals with that.

Lowered form
------------

The next question is how to represent the lowered form.  One alternative
is to keep the current set-up, i.e. use constant UNSPECs to represent
16-bit relocation values.  But we can reuse more of the new HIGH/LO_SUM
infrastructure if we use UNSPEC wrappers for offsets rather than for
individual relocation values.  This also seems more in keeping with
what other ports do.

Specifically, the patch uses UNSPEC addresses of the form:

    (unspec [(symbol)] UNSPEC_ADDRESS_FIRST + X)

where SYMBOL is either a SYMBOL_REF or LABEL_REF and X is a member
of enum mips_symbol_type.  There are new symbol types for GOT offsets:

    SYMBOL_GOTOFF_PAGE
	The offset from _gp of a GOT page entry.

    SYMBOL_GOTOFF_GLOBAL
	The offset from _gp of a global symbol's GOT entry.

    SYMBOL_GOTOFF_CALL
	Like SYMBOL_GOTOFF_GLOBAL, but used when calling a function.
	The GOT entry is allowed to point to a stub rather than to the
	function itself.

    SYMBOL_GOTOFF_LOADGP
	The offset of _gp from the start of a function.  Used when
	setting up $gp in n32 and n64 code.

Sometimes these offsets are 16-bit values and can be used in a LO_SUM
with $gp.  Sometimes they are 32-bit values and must be used with
a HIGH part:

	(set (reg base) (high OFFSET))
	(set (reg base) (plus (reg base) (reg $gp)))
	... (lo_sum (reg base) OFFSET) ...

This is similar to what alpha does with UNSPEC_DTPREL, for example.

As with normal symbols, mips_split_p[] decides whether a particular
type of offset needs a HIGH part or not.

To avoid having to add too many extra patterns, and for consistency with
calls (described below), the patch uses real MEMs for lowered GOT accesses.
For example, if "foo" is a global symbol, the splitter for:

    (set (reg dest)
	 (symbol_ref "foo"))

would generate:

    (set (reg dest)
	 (mem (lo_sum (reg $gp)
		      (const (unspec [(symbol_ref "foo")]
				     UNSPEC_ADDRESS_FIRST
				     + SYMBOL_GOTOFF_GLOBAL)))))

Call addresses
--------------

One slightly awkward problem is how to deal with PIC calls to global
functions.

At the moment, the call patterns require the function address to be a
register.  If the call expanders are given a global symbol as the target
address, they load it in pretty much the same way as any other global
symbol, except that they use UNSPECs for %call* instead of UNSPECs for
%got_disp(), etc.

Advantages of this are:

   - Sibcall addresses are loaded before the epilogue, thus allowing
     sibcalls to any function.
   - It produces more accurate pre-reload schedules.
   - It allows the HIGH part of xgot accesses to be hoisted.

So the patch continues to lower this case before reload, but using a
SYMBOL_GOTOFF_CALL offset instead of separate relocation constants.

For -mno-xgot, a call address would be loaded with:

    (set (reg dest) (mem (lo_sum (reg $gp) OFFSET)))

    where OFFSET == (const (unspec [(symbol_ref "func")]
				   UNSPEC_ADDRESS_FIRST
				   + SYMBOL_GOTOFF_CALL)))))

while the -mxgot sequence would look like:

    (set (reg r1) (high OFFSET))
    (set (reg r2) (plus (reg r1) (reg $gp)))
    (set (reg dest) (mem (lo_sum (reg r2) OFFSET)))

As before, the MEM is not marked constant since it might at first point
to a stub.

Printing relocations
--------------------

If a HIGH or LO_SUM isn't handled by a special mips.md pattern, the
arrays mips_hi_relocs[] and mips_lo_relocs[] say which relocations
should be used.  The point of using arrays is that mips_split_p[],
mips_lo_relocs[] and mips_hi_relocs[] can be set up in one place.
It should also make the compiler (very) slightly faster.

MIPS16
------

...is pretty much unchanged.  mips_split_p[] is true for small data
addresses and we generate the LO_SUM of a gp pseudo before reload.

Other related changes
---------------------

  - The current code uses mips_classify_constant to classify CONST rtxes.
    Having a single function seemed like a good idea because the code for
    handling relocation offsets was very similar to the code for handling
    symbol offsets.

    However, since the patch is dumping CONST relocs, there's very little
    point in keeping mips_classify_constant, at least not in its current
    form.  The patch replaces it with:

	CONST_GP_P
	    A macro that tests for mips16's (const $gp).

	mips_symbolic_constant_p
	    A function that tests specifically for symbolic constants.
	    Unlike the old mips_classify_constant, it checks whether the
	    offset is valid for the underlying relocations.

	    (An offset check wasn't really necessary in the old code
	    since we only kept symbolic constants if -mno-explicit-relocs.
	    Instead we checked whether the offset applied to a reloc
	    was valid.)

	mips_split_const
	    Splits a CONST into a base and offset.

  - Somewhat cosmetic, but I also changed mips_classify_address so that
    it has a similar interface to mips_symbolic_constant_p.  I.e., it
    returns true if the address is legitimate and stores the type of
    address in the mips_address_info structure.  This gets rid of the
    awkward ADDRESS_INVALID enum value.

  - Since small data addresses are preserved until after reload,
    mips_symbolic_address_p has to be more lenient than before.

  - The n32/n64 sequence to set up $gp must be treated as a black box
    until all uses of $gp are exposed.


Tested by bootstrapping & regression testing on mips64{,el}-linux-gnu.
I tested -mxgot on the little-endian system and -mno-xgot on the
big-endian system.  Also tested on mips-elf, mips64-elf, mips64vrel-elf
and mipsisa64-elf.

The irix machine I normally use is down at the moment so I couldn't test
that.  But it uses the same three ABIs as mips64-linux-gnu.

OK to install?

Richard

PS. diff seems to have got a bit confused here.  E.g. there's actually
    no change to mips_add_offset.  I tried various flags but this was
    the best I could do.


	* config/mips/mips-protos.h (mips_global_pic_constant_p): Delete.
	(mips_delegitimize_address): Delete.
	(mips_gotoff_global, mips_load_got_page): Declare.
	(mips_load_got_global, mips_rewrite_small_data): Declare.

	* config/mips/mips.h (FIND_BASE_TERM): Remove definition.
	(DANGEROUS_FOR_LA25_P): Use global_got_operand.
	(PREDICATE_CODES): Add global_got_operand, local_got_operand and
	small_data_pattern.  Remove CONST from const_arith_operand's entry.

	* config/mips/mips.c (UNSPEC_ADDRESS_P, CONST_GP_P): New macros.
	(UNSPEC_ADDRESS, UNSPEC_ADDRESS_TYPE): Likewise.
	(mips_constant_type): Delete.
	(mips_symbol_type): Add SYMBOL_GOTOFF_PAGE, SYMBOL_GOTOFF_GLOBAL,
	SYMBOL_GOTOFF_CALL and SYMBOL_GOTOFF_LOADGP.
	(NUM_SYMBOL_TYPES): New macro.
	(mips_address_type): Remove ADDRESS_INVALID.
	(machine_function): Add has_gp_insn_p.
	(mips_constant_info): Delete.
	(mips_address_info): Add the address type as an extra field.  Replace
	the c field with symbol_type.
	(mips_split_p, mips_lo_relocs, mips_hi_relocs): New arrays.
	(TARGET_DELEGITIMIZE_ADDRESS): Remove definition.
	(mips_reloc_offset_ok_p, mips_classify_constant): Delete.
	(mips_split_const, mips_symbolic_constant_p): New functions.
	(mips_symbolic_address_p): Take the symbol type and mode as arguments.
	(mips_classify_address): Return true if the address is valid, storing
	its type in INFO.  Use mips_symbol_constant_p.	Use mips_lo_relocs[]
	to test whether a LO_SUM address is allowed.
	(mips_symbol_insns): Return 0 for general mips16 symbols.
	Reorder SYMBOL_GOT_GLOBAL case to match mips_symbol_type definition.
	Handle the new SYMBOL_GOTOFF_*s.
	(mips_address_insns): Update call to mips_classify_address.
	(mips_const_insns): Be more fussy about HIGH constants.  Remove use
	of mips_classify_constant.  Be more accurate about CONSTs.
	(mips_global_pic_constant_p): Delete.
	(const_arith_operand): Only accept CONST_INTs.
	(call_insn_operand): Remove call to mips_classify_constant.
	Let mips_symbolic_constant_p check for invalid offsets.
	(move_operand): Check for general_operands first.  Only accept symbolic
	constants if they satisfy mips_symbolic_constant_p and cannot be split.
	(symbolic_constant): Use mips_symbolic_constant_p.
	(global_got_operand, local_got_operand): New predicates.
	(stack_operand): Update call to mips_classify_address.
	(mips_legitimate_address_p): Likewise.
	(mips_reloc, mips_lui_reloc): Delete.
	(mips_force_temporary): Only use the given temporary if no_new_pseudos.
	Use emit_move_insn.
	(mips_split_symbol, mips_unspec_address): New functions.
	(mips_unspec_offset_high): New function.
	(mips_load_got): Replace reloc argument with a symbol_type.
	Use mips_unspec_address to create the address and put it in a
	LO_SUM with the base register.
	(mips_load_got16, mips_load_got32): Delete.
	(mips_emit_high, mips_legitimize_symbol): Delete.
	(mips_gotoff_global): New function.
	(mips_load_got_page, mips_load_got_global): New functions.
	(mips_legitimize_symbol): Inline handling of LO_SUM splits.
	(mips_legitimize_const_move): Likewise.  Remove HIGH handling.
	Inline code to handle constants plus invalid offsets.  Use
	mips_split_symbol to legitimize constant pool addresses.
	(mips_delegitimize_address): Delete.
	(mips_rtx_costs): Give legitimate symbolic constants and CONST_DOUBLEs
	a cost of 1 insn.  Give the rest a cost of CONSTANT_POOL_ADDRESS.
	(mips_subword): Pass memrefs through mips_rewrite_small_data.
	(mips_output_move): Remove use of mips_classify_constant.
	(mips_expand_call): Use mips_unspec_offset_high to calculate the
	high part of the GOT address for calls to global functions.
	(override_options): Initialize mips_split_p[], mips_lo_relocs[]
	and mips_hi_relocs[].
	(print_operand): Use print_operand_reloc to handle '%h' and '%R'.
	Remove use of mips_classify_constant.
	(mips_reloc_string): Delete.
	(print_operand_reloc): New function.
	(print_operand_address): Update call to mips_classify_address.
	(mips_rewrite_small_data_p, small_data_pattern_1): New functions.
	(small_data_pattern): New predicate.
	(mips_rewrite_small_data_1, mips_rewrite_small_data): New functions.
	(mips_function_has_gp_insn): New function.
	(mips_global_pointer): Use it.
	(mips_gp_insn): Delete.
	(mips_expand_prologue): When compiling for n32/n64 abicalls, use a
	single loadgp pattern to initialize $gp.  Pass it the offset of _gp
	from the start of the current function.
	(mips16_gp_pseudo_reg): Revert last patch.

	* config/mips/mips.md: (RELOC_*): Delete.
	(UNSPEC_LOADGP, UNSPEC_FIRST_ADDRESS): New constants.
	(got): New insn attribute.
	(type): Set to "load" if got == load.
	(length): Set to 4 if got == load, 8 if got == xgot_high.
	(lui[sd]i): Delete.
	(*xgot_hi[sd]i, *xgot_lo[sd]i): New patterns.
	(*got_disp[sd]i, *got_page[sd]i): Likewise.
	(*low[sd]i): Change constraints to "d".  Add a new define_split to
	rewrite small data constants into LO_SUMs.
	(loadgp): New insns.

diff -cp config/mips.cvs/mips-protos.h config/mips/mips-protos.h
*** config/mips.cvs/mips-protos.h	Thu Oct 30 13:52:56 2003
--- config/mips/mips-protos.h	Thu Oct 30 22:26:48 2003
*************** extern int mips_reg_mode_ok_for_base_p (
*** 30,40 ****
  extern int mips_address_insns (rtx, enum machine_mode);
  extern int mips_const_insns (rtx);
  extern int mips_fetch_insns (rtx);
- extern bool mips_global_pic_constant_p (rtx);
  extern bool mips_legitimate_address_p (enum machine_mode, rtx, int);
  extern bool mips_legitimize_address (rtx *, enum machine_mode);
  extern bool mips_legitimize_move (enum machine_mode, rtx, rtx);
- extern rtx mips_delegitimize_address (rtx);
  
  extern int m16_uimm3_b (rtx, enum machine_mode);
  extern int m16_simm4_1 (rtx, enum machine_mode);
--- 30,41 ----
  extern int mips_address_insns (rtx, enum machine_mode);
  extern int mips_const_insns (rtx);
  extern int mips_fetch_insns (rtx);
  extern bool mips_legitimate_address_p (enum machine_mode, rtx, int);
  extern bool mips_legitimize_address (rtx *, enum machine_mode);
+ extern rtx mips_gotoff_global (rtx);
+ extern rtx mips_load_got_page (rtx);
+ extern rtx mips_load_got_global (rtx, rtx);
  extern bool mips_legitimize_move (enum machine_mode, rtx, rtx);
  
  extern int m16_uimm3_b (rtx, enum machine_mode);
  extern int m16_simm4_1 (rtx, enum machine_mode);
*************** extern void mips_declare_object (FILE *,
*** 110,115 ****
--- 111,117 ----
  extern void mips_declare_object_name (FILE *, const char *, tree);
  extern void mips_finish_declare_object (FILE *, tree, int, int);
  
+ extern rtx mips_rewrite_small_data (rtx);
  extern HOST_WIDE_INT compute_frame_size (HOST_WIDE_INT);
  extern int mips_initial_elimination_offset (int, int);
  extern rtx mips_return_addr (int, rtx);
diff -cp config/mips.cvs/mips.c config/mips/mips.c
*** config/mips.cvs/mips.c	Thu Oct 30 18:06:55 2003
--- config/mips/mips.c	Sat Nov  1 09:54:34 2003
*************** enum internal_test {
*** 80,85 ****
--- 80,104 ----
  #define INTERNAL_SYMBOL_P(SYM) \
    (XSTR (SYM, 0)[0] == '*' && XSTR (SYM, 0)[1] == LOCAL_LABEL_PREFIX[0])
  
+ /* True if X is an unspec wrapper around a SYMBOL_REF or LABEL_REF.  */
+ #define UNSPEC_ADDRESS_P(X)					\
+   (GET_CODE (X) == UNSPEC					\
+    && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST			\
+    && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES)
+ 
+ /* Extract the symbol or label from UNSPEC wrapper X.  */
+ #define UNSPEC_ADDRESS(X) \
+   XVECEXP (X, 0, 0)
+ 
+ /* Extract the symbol type from UNSPEC wrapper X.  */
+ #define UNSPEC_ADDRESS_TYPE(X) \
+   ((enum mips_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST))
+ 
+ /* True if X is (const $gp).  This is used to initialize the mips16
+    gp pseudo register.  */
+ #define CONST_GP_P(X) \
+   (GET_CODE (X) == CONST && XEXP (X, 0) == pic_offset_table_rtx)
+ 
  /* The maximum distance between the top of the stack frame and the
     value $sp has when we save & restore registers.
  
*************** enum internal_test {
*** 93,132 ****
     multi-instruction addu sequence.  Use 0x7fe0 to work around this.  */
  #define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7fe0)
  
! /* Classifies a non-literal integer constant.
! 
!    CONSTANT_NONE
!        Not one of the constants below.
! 
!    CONSTANT_GP
!        The global pointer, treated as a constant when TARGET_MIPS16.
!        The rtx has the form:
! 
! 	   (const (reg $gp)).
! 
!    CONSTANT_RELOC
!        A signed 16-bit relocation against either a symbol
!        or a symbol plus an offset.  The relocation has the form:
! 
! 	   (unspec [(SYMBOL) ...] RELOC)
! 
!        Any offset is added outside the unspec, such as:
! 
! 	   (plus (unspec [(SYMBOL) ...] RELOC) (const_int OFFSET))
! 
!        In either case, the whole expression is wrapped in a (const ...).
! 
!    CONSTANT_SYMBOLIC
!        A reference to a symbol, possibly with an offset.  */
! enum mips_constant_type {
!   CONSTANT_NONE,
!   CONSTANT_GP,
!   CONSTANT_RELOC,
!   CONSTANT_SYMBOLIC
! };
! 
! 
! /* Classifies a SYMBOL_REF or LABEL_REF.
  
     SYMBOL_GENERAL
         Used when none of the below apply.
--- 112,118 ----
     multi-instruction addu sequence.  Use 0x7fe0 to work around this.  */
  #define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7fe0)
  
! /* Classifies a SYMBOL_REF, LABEL_REF or UNSPEC address.
  
     SYMBOL_GENERAL
         Used when none of the below apply.
*************** enum mips_constant_type {
*** 142,162 ****
         the global offset table.
  
     SYMBOL_GOT_GLOBAL
!        Likewise non-local data.  */
  enum mips_symbol_type {
    SYMBOL_GENERAL,
    SYMBOL_SMALL_DATA,
    SYMBOL_CONSTANT_POOL,
    SYMBOL_GOT_LOCAL,
!   SYMBOL_GOT_GLOBAL
  };
  
  
  /* Classifies an address.
  
-    ADDRESS_INVALID
-        The address should be rejected as invalid.
- 
     ADDRESS_REG
         A natural register + offset address.  The register satisfies
         mips_valid_base_register_p and the offset is a const_arith_operand.
--- 128,167 ----
         the global offset table.
  
     SYMBOL_GOT_GLOBAL
!        Likewise non-local data.
! 
!    SYMBOL_GOTOFF_PAGE
!        An UNSPEC wrapper around a SYMBOL_GOT_LOCAL.  It represents the
!        offset from _gp of a GOT page entry.
! 
!    SYMBOL_GOTOFF_GLOBAL
!        An UNSPEC wrapper around a SYMBOL_GOT_GLOBAL.  It represents the
!        the offset from _gp of the symbol's GOT entry.
! 
!    SYMBOL_GOTOFF_CALL
!        Like SYMBOL_GOTOFF_GLOBAL, but used when calling a global function.
!        The GOT entry is allowed to point to a stub rather than to the
!        function itself.
! 
!    SYMBOL_GOTOFF_LOADGP
!        An UNSPEC wrapper around a function's address.  It represents the
!        offset of _gp from the start of the function.  */
  enum mips_symbol_type {
    SYMBOL_GENERAL,
    SYMBOL_SMALL_DATA,
    SYMBOL_CONSTANT_POOL,
    SYMBOL_GOT_LOCAL,
!   SYMBOL_GOT_GLOBAL,
!   SYMBOL_GOTOFF_PAGE,
!   SYMBOL_GOTOFF_GLOBAL,
!   SYMBOL_GOTOFF_CALL,
!   SYMBOL_GOTOFF_LOADGP
  };
+ #define NUM_SYMBOL_TYPES (SYMBOL_GOTOFF_LOADGP + 1)
  
  
  /* Classifies an address.
  
     ADDRESS_REG
         A natural register + offset address.  The register satisfies
         mips_valid_base_register_p and the offset is a const_arith_operand.
*************** enum mips_symbol_type {
*** 171,177 ****
     ADDRESS_SYMBOLIC:
         A constant symbolic address (equivalent to CONSTANT_SYMBOLIC).  */
  enum mips_address_type {
-   ADDRESS_INVALID,
    ADDRESS_REG,
    ADDRESS_LO_SUM,
    ADDRESS_CONST_INT,
--- 176,181 ----
*************** typedef void (*mips_save_restore_fn) (rt
*** 184,215 ****
  
  struct constant;
  struct mips_arg_info;
- struct mips_constant_info;
  struct mips_address_info;
  struct mips_integer_op;
  
- static bool mips_reloc_offset_ok_p (int, HOST_WIDE_INT);
- static enum mips_constant_type
-   mips_classify_constant (struct mips_constant_info *, rtx);
  static enum mips_symbol_type mips_classify_symbol (rtx);
  static bool mips_valid_base_register_p (rtx, enum machine_mode, int);
! static bool mips_symbolic_address_p (rtx, HOST_WIDE_INT,
! 				     enum machine_mode, int);
! static enum mips_address_type
!   mips_classify_address (struct mips_address_info *, rtx,
! 			 enum machine_mode, int, int);
! static bool mips_splittable_symbol_p (enum mips_symbol_type, HOST_WIDE_INT);
  static int mips_symbol_insns (enum mips_symbol_type);
  static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
- static rtx mips_reloc (rtx, int);
- static rtx mips_lui_reloc (rtx, int);
  static rtx mips_force_temporary (rtx, rtx);
  static rtx mips_add_offset (rtx, HOST_WIDE_INT);
- static rtx mips_load_got (rtx, rtx, int);
- static rtx mips_load_got16 (rtx, int);
- static rtx mips_load_got32 (rtx, rtx, int, int);
- static rtx mips_emit_high (rtx, rtx);
- static bool mips_legitimize_symbol (rtx, rtx *, int);
  static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT);
  static unsigned int mips_build_lower (struct mips_integer_op *,
  				      unsigned HOST_WIDE_INT);
--- 188,211 ----
  
  struct constant;
  struct mips_arg_info;
  struct mips_address_info;
  struct mips_integer_op;
  
  static enum mips_symbol_type mips_classify_symbol (rtx);
+ static void mips_split_const (rtx, rtx *, HOST_WIDE_INT *);
+ static bool mips_symbolic_constant_p (rtx, enum mips_symbol_type *);
  static bool mips_valid_base_register_p (rtx, enum machine_mode, int);
! static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode);
! static bool mips_classify_address (struct mips_address_info *, rtx,
! 				   enum machine_mode, int);
  static int mips_symbol_insns (enum mips_symbol_type);
  static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
  static rtx mips_force_temporary (rtx, rtx);
+ static rtx mips_split_symbol (rtx, rtx);
+ static rtx mips_unspec_address (rtx, enum mips_symbol_type);
+ static rtx mips_unspec_offset_high (rtx, rtx, enum mips_symbol_type);
+ static rtx mips_load_got (rtx, rtx, enum mips_symbol_type);
  static rtx mips_add_offset (rtx, HOST_WIDE_INT);
  static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT);
  static unsigned int mips_build_lower (struct mips_integer_op *,
  				      unsigned HOST_WIDE_INT);
*************** static bool mips_get_unaligned_mem (rtx 
*** 233,242 ****
  static void mips_set_architecture (const struct mips_cpu_info *);
  static void mips_set_tune (const struct mips_cpu_info *);
  static struct machine_function *mips_init_machine_status (void);
! static const char *mips_reloc_string (int);
  static bool mips_assemble_integer (rtx, unsigned int, int);
  static void mips_file_start (void);
  static void mips_file_end (void);
  static unsigned int mips_global_pointer	(void);
  static bool mips_save_reg_p (unsigned int);
  static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT,
--- 229,242 ----
  static void mips_set_architecture (const struct mips_cpu_info *);
  static void mips_set_tune (const struct mips_cpu_info *);
  static struct machine_function *mips_init_machine_status (void);
! static void print_operand_reloc (FILE *, rtx, const char **);
  static bool mips_assemble_integer (rtx, unsigned int, int);
  static void mips_file_start (void);
  static void mips_file_end (void);
+ static bool mips_rewrite_small_data_p (rtx);
+ static int small_data_pattern_1 (rtx *, void *);
+ static int mips_rewrite_small_data_1 (rtx *, void *);
+ static bool mips_function_has_gp_insn (void);
  static unsigned int mips_global_pointer	(void);
  static bool mips_save_reg_p (unsigned int);
  static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT,
*************** static void mips_output_function_prologu
*** 246,252 ****
  static void mips_set_frame_expr (rtx);
  static rtx mips_frame_set (rtx, rtx);
  static void mips_save_reg (rtx, rtx);
- static void mips_gp_insn (rtx, rtx);
  static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT);
  static void mips_restore_reg (rtx, rtx);
  static int symbolic_expression_p (rtx);
--- 246,251 ----
*************** static void mips_select_section (tree, i
*** 256,262 ****
  				  ATTRIBUTE_UNUSED;
  static bool mips_in_small_data_p (tree);
  static void mips_encode_section_info (tree, rtx, int);
! static rtx mips_sdata_pointer (void);
  static void mips16_fp_args (FILE *, int, int);
  static void build_mips16_function_stub (FILE *);
  static void mips16_optimize_gp (void);
--- 255,261 ----
  				  ATTRIBUTE_UNUSED;
  static bool mips_in_small_data_p (tree);
  static void mips_encode_section_info (tree, rtx, int);
! static rtx mips16_gp_pseudo_reg (void);
  static void mips16_fp_args (FILE *, int, int);
  static void build_mips16_function_stub (FILE *);
  static void mips16_optimize_gp (void);
*************** struct machine_function GTY(()) {
*** 337,342 ****
--- 336,344 ----
    /* True if the whole function is suitable for .set noreorder and
       .set nomacro.  */
    bool all_noreorder_p;
+ 
+   /* True if the function is known to have an instruction that needs $gp.  */
+   bool has_gp_insn_p;
  };
  
  /* Information about a single argument.  */
*************** struct mips_arg_info
*** 366,395 ****
  };
  
  
- /* Struct for recording constants.  The meaning of the fields depends
-    on a mips_constant_type:
- 
-    CONSTANT_NONE
-    CONSTANT_GP
-        No fields are valid.
- 
-    CONSTANT_SYMBOLIC
-        SYMBOL is the referenced symbol and OFFSET is the constant offset.
- 
-    CONSTANT_RELOC
-        SYMBOL and OFFSET are the same as for CONSTANT_SYMBOLIC.  RELOC is
-        the relocation number.  */
- struct mips_constant_info
- {
-   int reloc;
-   rtx symbol;
-   HOST_WIDE_INT offset;
- };
- 
- 
  /* Information about an address described by mips_address_type.
  
-    ADDRESS_INVALID
     ADDRESS_CONST_INT
         No fields are used.
  
--- 368,375 ----
*************** struct mips_constant_info
*** 398,413 ****
  
     ADDRESS_LO_SUM
         REG is the register that contains the high part of the address,
!        OFFSET is the symbolic address being referenced, and C contains
!        the individual components of the symbolic address.
  
     ADDRESS_SYMBOLIC
!        C contains the symbol and offset.  */
  struct mips_address_info
  {
    rtx reg;
    rtx offset;
!   struct mips_constant_info c;
  };
  
  
--- 378,395 ----
  
     ADDRESS_LO_SUM
         REG is the register that contains the high part of the address,
!        OFFSET is the symbolic address being referenced and SYMBOL_TYPE
!        is the type of OFFSET's symbol.
  
     ADDRESS_SYMBOLIC
!        SYMBOL_TYPE is the type of symbol being referenced.  */
! 
  struct mips_address_info
  {
+   enum mips_address_type type;
    rtx reg;
    rtx offset;
!   enum mips_symbol_type symbol_type;
  };
  
  
*************** static int mips_flag_delayed_branch;
*** 556,561 ****
--- 538,555 ----
  
  static GTY (()) int mips_output_filename_first_time = 1;
  
+ /* mips_split_p[X] is true if symbols of type X can be split by
+    mips_split_symbol().  */
+ static bool mips_split_p[NUM_SYMBOL_TYPES];
+ 
+ /* mips_lo_relocs[X] is the relocation to use when a symbol of type X
+    appears in a LO_SUM.  It can be null if such LO_SUMs aren't valid or
+    if they are matched by a special .md file pattern.  */
+ static const char *mips_lo_relocs[NUM_SYMBOL_TYPES];
+ 
+ /* Likewise for HIGHs.  */
+ static const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
+ 
  /* Hardware names for the registers.  If -mrnames is used, this
     will be overwritten with mips_sw_reg_names.  */
  
*************** const struct mips_cpu_info mips_cpu_info
*** 762,769 ****
  #define TARGET_RTX_COSTS mips_rtx_costs
  #undef TARGET_ADDRESS_COST
  #define TARGET_ADDRESS_COST mips_address_cost
- #undef TARGET_DELEGITIMIZE_ADDRESS
- #define TARGET_DELEGITIMIZE_ADDRESS mips_delegitimize_address
  
  #undef TARGET_ENCODE_SECTION_INFO
  #define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
--- 756,761 ----
*************** const struct mips_cpu_info mips_cpu_info
*** 798,880 ****
  
  struct gcc_target targetm = TARGET_INITIALIZER;
  
- /* Return true if RELOC is a valid relocation number and OFFSET can be
-    added to the relocation symbol.
- 
-    Note that OFFSET might not refer to part of the object.   For example,
-    in an expression like x[i - 0x12345], we might try to take the address
-    of "x - 0x12345".  */
- 
- static bool
- mips_reloc_offset_ok_p (int reloc, HOST_WIDE_INT offset)
- {
-   switch (reloc)
-     {
-     case RELOC_GOT_PAGE:
-       /* The linker should provide enough page entries to cope with
- 	 16-bit offsets from a valid segment address.  */
-       return SMALL_OPERAND (offset);
- 
-     case RELOC_GOT_HI:
-     case RELOC_GOT_LO:
-     case RELOC_GOT_DISP:
-     case RELOC_CALL16:
-     case RELOC_CALL_HI:
-     case RELOC_CALL_LO:
-     case RELOC_LOADGP_HI:
-     case RELOC_LOADGP_LO:
-       /* These relocations should be applied to bare symbols only.  */
-       return offset == 0;
- 
-     default:
-       return false;
-     }
- }
- 
- 
- /* If X is one of the constants described by mips_constant_type,
-    store its components in INFO and return its type.  */
- 
- static enum mips_constant_type
- mips_classify_constant (struct mips_constant_info *info, rtx x)
- {
-   enum mips_constant_type type;
- 
-   type = CONSTANT_SYMBOLIC;
-   info->offset = 0;
- 
-   if (GET_CODE (x) == CONST)
-     {
-       x = XEXP (x, 0);
- 
-       if (x == pic_offset_table_rtx)
- 	return CONSTANT_GP;
- 
-       while (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
- 	{
- 	  info->offset += INTVAL (XEXP (x, 1));
- 	  x = XEXP (x, 0);
- 	}
- 
-       if (TARGET_EXPLICIT_RELOCS
- 	  && GET_CODE (x) == UNSPEC
- 	  && mips_reloc_offset_ok_p (XINT (x, 1), info->offset))
- 	{
- 	  info->reloc = XINT (x, 1);
- 	  x = XVECEXP (x, 0, 0);
- 	  type = CONSTANT_RELOC;
- 	}
-     }
- 
-   if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
-     {
-       info->symbol = x;
-       return type;
-     }
-   return CONSTANT_NONE;
- }
- 
- 
  /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
  
  static enum mips_symbol_type
--- 790,795 ----
*************** mips_classify_symbol (rtx x)
*** 922,927 ****
--- 837,919 ----
  }
  
  
+ /* Split X into a base and a constant offset, storing them in *BASE
+    and *OFFSET respectively.  */
+ 
+ static void
+ mips_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset)
+ {
+   *offset = 0;
+ 
+   if (GET_CODE (x) == CONST)
+     x = XEXP (x, 0);
+ 
+   if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+     {
+       *offset += INTVAL (XEXP (x, 1));
+       x = XEXP (x, 0);
+     }
+   *base = x;
+ }
+ 
+ 
+ /* Return true if X is a symbolic constant that can be calculated in
+    the same way as a bare symbol.  If it is, store the type of the
+    symbol in *SYMBOL_TYPE.  */
+ 
+ static bool
+ mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
+ {
+   HOST_WIDE_INT offset;
+ 
+   mips_split_const (x, &x, &offset);
+   if (UNSPEC_ADDRESS_P (x))
+     *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+   else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+     *symbol_type = mips_classify_symbol (x);
+   else
+     return false;
+ 
+   if (offset == 0)
+     return true;
+ 
+   /* Check whether a nonzero offset is valid for the underlying
+      relocations.  */
+   switch (*symbol_type)
+     {
+     case SYMBOL_GENERAL:
+       /* %hi() and %lo() can handle anything.  */
+       return true;
+ 
+     case SYMBOL_SMALL_DATA:
+       /* Make sure that the offset refers to something within the
+ 	 -G limit.  If the offset is allowed to grow too much,
+ 	 it could overflow the range of %gp_rel().  */
+       return (offset > 0 && offset < mips_section_threshold);
+ 
+     case SYMBOL_CONSTANT_POOL:
+       /* Similarly check the range of offsets for mips16 constant
+ 	 pool entries.  */
+       return (CONSTANT_POOL_ADDRESS_P (x)
+ 	      && offset > 0
+ 	      && offset < (int) GET_MODE_SIZE (get_pool_mode (x)));
+ 
+     case SYMBOL_GOT_LOCAL:
+     case SYMBOL_GOTOFF_PAGE:
+       /* The linker should provide enough local GOT entries for a
+ 	 16-bit offset.  Larger offsets may lead to GOT overflow.  */
+       return SMALL_OPERAND (offset);
+ 
+     case SYMBOL_GOT_GLOBAL:
+     case SYMBOL_GOTOFF_GLOBAL:
+     case SYMBOL_GOTOFF_CALL:
+     case SYMBOL_GOTOFF_LOADGP:
+       return false;
+     }
+   abort ();
+ }
+ 
+ 
  /* This function is used to implement REG_MODE_OK_FOR_BASE_P.  */
  
  int
*************** mips_valid_base_register_p (rtx x, enum 
*** 947,1101 ****
  }
  
  
! /* Return true if SYMBOL + OFFSET should be considered a legitimate
!    address.  LEA_P is true and MODE is word_mode if the address
!    will be used in an LA or DLA macro.  Otherwise MODE is the
!    mode of the value being accessed.
! 
!    Some guiding principles:
! 
!    - Allow a nonzero offset when it takes no additional instructions.
!      Ask for other offsets to be added separately.
! 
!    - Only allow multi-instruction load or store macros when MODE is
!      word-sized or smaller.  For other modes (including BLKmode)
!      it is better to move the address into a register first.  */
  
  static bool
! mips_symbolic_address_p (rtx symbol, HOST_WIDE_INT offset,
! 			 enum machine_mode mode, int lea_p)
  {
!   if (TARGET_EXPLICIT_RELOCS)
!     return false;
! 
!   switch (mips_classify_symbol (symbol))
      {
      case SYMBOL_GENERAL:
!       /* General symbols aren't valid addresses in mips16 code:
! 	 they have to go into the constant pool.  */
!       return (!TARGET_MIPS16
! 	      && !mips_split_addresses
! 	      && SINGLE_WORD_MODE_P (mode));
  
      case SYMBOL_SMALL_DATA:
!       /* Small data references are normally OK for any address.
! 	 But for mips16 code, we need to use a pseudo register
! 	 instead of $gp as the base register.  */
!       return !TARGET_MIPS16;
  
      case SYMBOL_CONSTANT_POOL:
!       /* PC-relative addressing is only available for lw, sw, ld and sd.
! 	 There's also a PC-relative add instruction.  */
!       return lea_p || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
  
      case SYMBOL_GOT_GLOBAL:
!       /* The address of the symbol is stored in the GOT.  We can load
! 	 it using an LA or DLA instruction, but any offset is added
! 	 afterwards.  */
!       return lea_p && offset == 0;
  
!     case SYMBOL_GOT_LOCAL:
!       /* The symbol is part of a block of local memory.  We fetch the
! 	 address of the local memory from the GOT and then add the
! 	 offset for this symbol.  This addition can take the form of an
! 	 offset(base) address, so the symbol is a legitimate address.  */
!       return SINGLE_WORD_MODE_P (mode);
      }
    abort ();
  }
  
  
! /* If X is a valid address, describe it in INFO and return its type.
!    STRICT says to only allow hard registers.  MODE and LEA_P are
!    the same as for mips_symbolic_address_p.  */
  
! static enum mips_address_type
  mips_classify_address (struct mips_address_info *info, rtx x,
! 		       enum machine_mode mode, int strict, int lea_p)
  {
    switch (GET_CODE (x))
      {
      case REG:
      case SUBREG:
!       if (mips_valid_base_register_p (x, mode, strict))
! 	{
! 	  info->reg = x;
! 	  info->offset = const0_rtx;
! 	  return ADDRESS_REG;
! 	}
!       return ADDRESS_INVALID;
  
      case PLUS:
!       if (mips_valid_base_register_p (XEXP (x, 0), mode, strict)
! 	  && const_arith_operand (XEXP (x, 1), VOIDmode))
! 	{
! 	  info->reg = XEXP (x, 0);
! 	  info->offset = XEXP (x, 1);
! 	  return ADDRESS_REG;
! 	}
!       return ADDRESS_INVALID;
  
      case LO_SUM:
!       if (SINGLE_WORD_MODE_P (mode)
! 	  && mips_valid_base_register_p (XEXP (x, 0), mode, strict)
! 	  && (mips_classify_constant (&info->c, XEXP (x, 1))
! 	      == CONSTANT_SYMBOLIC)
! 	  && mips_splittable_symbol_p (mips_classify_symbol (info->c.symbol),
! 				       info->c.offset))
! 	{
! 	  info->reg = XEXP (x, 0);
! 	  info->offset = XEXP (x, 1);
! 	  return ADDRESS_LO_SUM;
! 	}
!       return ADDRESS_INVALID;
  
      case CONST_INT:
        /* Small-integer addresses don't occur very often, but they
  	 are legitimate if $0 is a valid base register.  */
!       if (!TARGET_MIPS16 && SMALL_INT (x))
! 	return ADDRESS_CONST_INT;
!       return ADDRESS_INVALID;
  
      case CONST:
      case LABEL_REF:
      case SYMBOL_REF:
!       if (mips_classify_constant (&info->c, x) == CONSTANT_SYMBOLIC
! 	  && mips_symbolic_address_p (info->c.symbol, info->c.offset,
! 				      mode, lea_p))
! 	return ADDRESS_SYMBOLIC;
!       return ADDRESS_INVALID;
! 
!     default:
!       return ADDRESS_INVALID;
!     }
! }
! 
! /* Return true if symbols of the given type can be split into a
!    high part and a LO_SUM.  In the case of small data symbols,
!    the high part will be $gp.  */
! 
! static bool
! mips_splittable_symbol_p (enum mips_symbol_type type, HOST_WIDE_INT offset)
! {
!   switch (type)
!     {
!     case SYMBOL_GENERAL:
!       return TARGET_EXPLICIT_RELOCS || mips_split_addresses;
! 
!     case SYMBOL_GOT_LOCAL:
!       return TARGET_EXPLICIT_RELOCS && SMALL_OPERAND (offset);
! 
!     case SYMBOL_SMALL_DATA:
!       return ((TARGET_EXPLICIT_RELOCS || TARGET_MIPS16)
! 	      && (offset == 0
! 		  || (offset > 0 && offset <= mips_section_threshold)));
  
      default:
        return false;
      }
  }
! 
! 
  /* Return the number of instructions needed to load a symbol of the
     given type into a register.  If valid in an address, the same number
     of instructions are needed for loads and stores.  Treat extended
--- 939,1032 ----
  }
  
  
! /* Return true if symbols of type SYMBOL_TYPE can directly address a value
!    with mode MODE.  This is used for both symbolic and LO_SUM addresses.  */
  
  static bool
! mips_symbolic_address_p (enum mips_symbol_type symbol_type,
! 			 enum machine_mode mode)
  {
!   switch (symbol_type)
      {
      case SYMBOL_GENERAL:
!       return !TARGET_MIPS16;
  
      case SYMBOL_SMALL_DATA:
!       return true;
  
      case SYMBOL_CONSTANT_POOL:
!       /* PC-relative addressing is only available for lw, sw, ld and sd.  */
!       return GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
! 
!     case SYMBOL_GOT_LOCAL:
!       return true;
  
      case SYMBOL_GOT_GLOBAL:
!       /* The address will have to be loaded from the GOT first.  */
!       return false;
  
!     case SYMBOL_GOTOFF_PAGE:
!     case SYMBOL_GOTOFF_GLOBAL:
!     case SYMBOL_GOTOFF_CALL:
!     case SYMBOL_GOTOFF_LOADGP:
!       return true;
      }
    abort ();
  }
  
  
! /* Return true if X is a valid address for machine mode MODE.  If it is,
!    fill in INFO appropriately.  STRICT is true if we should only accept
!    hard base registers.  */
  
! static bool
  mips_classify_address (struct mips_address_info *info, rtx x,
! 		       enum machine_mode mode, int strict)
  {
    switch (GET_CODE (x))
      {
      case REG:
      case SUBREG:
!       info->type = ADDRESS_REG;
!       info->reg = x;
!       info->offset = const0_rtx;
!       return mips_valid_base_register_p (info->reg, mode, strict);
  
      case PLUS:
!       info->type = ADDRESS_REG;
!       info->reg = XEXP (x, 0);
!       info->offset = XEXP (x, 1);
!       return (mips_valid_base_register_p (info->reg, mode, strict)
! 	      && const_arith_operand (info->offset, VOIDmode));
  
      case LO_SUM:
!       info->type = ADDRESS_LO_SUM;
!       info->reg = XEXP (x, 0);
!       info->offset = XEXP (x, 1);
!       return (mips_valid_base_register_p (info->reg, mode, strict)
! 	      && mips_symbolic_constant_p (info->offset, &info->symbol_type)
! 	      && mips_symbolic_address_p (info->symbol_type, mode)
! 	      && mips_lo_relocs[info->symbol_type] != 0);
  
      case CONST_INT:
        /* Small-integer addresses don't occur very often, but they
  	 are legitimate if $0 is a valid base register.  */
!       info->type = ADDRESS_CONST_INT;
!       return !TARGET_MIPS16 && SMALL_INT (x);
  
      case CONST:
      case LABEL_REF:
      case SYMBOL_REF:
!       info->type = ADDRESS_SYMBOLIC;
!       return (mips_symbolic_constant_p (x, &info->symbol_type)
! 	      && mips_symbolic_address_p (info->symbol_type, mode)
! 	      && !mips_split_p[info->symbol_type]);
  
      default:
        return false;
      }
  }
! 
  /* Return the number of instructions needed to load a symbol of the
     given type into a register.  If valid in an address, the same number
     of instructions are needed for loads and stores.  Treat extended
*************** mips_symbol_insns (enum mips_symbol_type
*** 1107,1112 ****
--- 1038,1048 ----
    switch (type)
      {
      case SYMBOL_GENERAL:
+       /* In mips16 code, general symbols must be fetched from the
+ 	 constant pool.  */
+       if (TARGET_MIPS16)
+ 	return 0;
+ 
        /* When using 64-bit symbols, we need 5 preparatory instructions,
  	 such as:
  
*************** mips_symbol_insns (enum mips_symbol_type
*** 1128,1144 ****
  	 extended instruction.  */
        return 2;
  
-     case SYMBOL_GOT_GLOBAL:
-       /* When using a small GOT, we just fetch the address using
- 	 a gp-relative load.   For a big GOT, we need a sequence
- 	 such as:
- 
- 	      lui     $at,%got_hi(symbol)
- 	      daddu   $at,$at,$gp
- 
- 	 and the final address is $at + %got_lo(symbol).  */
-       return (TARGET_XGOT ? 3 : 1);
- 
      case SYMBOL_GOT_LOCAL:
        /* For o32 and o64, the sequence is:
  
--- 1064,1069 ----
*************** mips_symbol_insns (enum mips_symbol_type
*** 1150,1155 ****
--- 1075,1098 ----
  	 of GAS insert a nop in the n32/n64 sequences too so, for
  	 simplicity, use the worst case of 3 instructions.  */
        return 3;
+ 
+     case SYMBOL_GOT_GLOBAL:
+       /* When using a small GOT, we just fetch the address using
+ 	 a gp-relative load.   For a big GOT, we need a sequence
+ 	 such as:
+ 
+ 	      lui     $at,%got_hi(symbol)
+ 	      daddu   $at,$at,$gp
+ 
+ 	 and the final address is $at + %got_lo(symbol).  */
+       return (TARGET_XGOT ? 3 : 1);
+ 
+     case SYMBOL_GOTOFF_PAGE:
+     case SYMBOL_GOTOFF_GLOBAL:
+     case SYMBOL_GOTOFF_CALL:
+     case SYMBOL_GOTOFF_LOADGP:
+       /* Check whether the offset is a 16- or 32-bit value.  */
+       return mips_split_p[type] ? 2 : 1;
      }
    abort ();
  }
*************** mips_address_insns (rtx x, enum machine_
*** 1193,1219 ****
  
    /* Each word of a multi-word value will be accessed individually.  */
    factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
!   switch (mips_classify_address (&addr, x, mode, 0, 0))
!     {
!     case ADDRESS_INVALID:
!       return 0;
! 
!     case ADDRESS_REG:
!       if (TARGET_MIPS16
! 	  && !mips16_unextended_reference_p (mode, addr.reg, addr.offset))
! 	return factor * 2;
!       return factor;
  
!     case ADDRESS_LO_SUM:
!       return (TARGET_MIPS16 ? factor * 2 : factor);
  
!     case ADDRESS_CONST_INT:
!       return factor;
  
!     case ADDRESS_SYMBOLIC:
!       return factor * mips_symbol_insns (mips_classify_symbol (addr.c.symbol));
!     }
!   abort ();
  }
  
  
--- 1136,1160 ----
  
    /* Each word of a multi-word value will be accessed individually.  */
    factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
!   if (mips_classify_address (&addr, x, mode, false))
!     switch (addr.type)
!       {
!       case ADDRESS_REG:
! 	if (TARGET_MIPS16
! 	    && !mips16_unextended_reference_p (mode, addr.reg, addr.offset))
! 	  return factor * 2;
! 	return factor;
  
!       case ADDRESS_LO_SUM:
! 	return (TARGET_MIPS16 ? factor * 2 : factor);
  
!       case ADDRESS_CONST_INT:
! 	return factor;
  
!       case ADDRESS_SYMBOLIC:
! 	return factor * mips_symbol_insns (addr.symbol_type);
!       }
!   return 0;
  }
  
  
*************** mips_address_insns (rtx x, enum machine_
*** 1222,1234 ****
  int
  mips_const_insns (rtx x)
  {
-   struct mips_constant_info c;
    struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
  
    switch (GET_CODE (x))
      {
      case CONSTANT_P_RTX:
      case HIGH:
        return 1;
  
      case CONST_INT:
--- 1163,1183 ----
  int
  mips_const_insns (rtx x)
  {
    struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+   enum mips_symbol_type symbol_type;
+   HOST_WIDE_INT offset;
  
    switch (GET_CODE (x))
      {
      case CONSTANT_P_RTX:
+       return 1;
+ 
      case HIGH:
+       if (TARGET_MIPS16
+ 	  || !mips_symbolic_constant_p (XEXP (x, 0), &symbol_type)
+ 	  || !mips_split_p[symbol_type])
+ 	return 0;
+ 
        return 1;
  
      case CONST_INT:
*************** mips_const_insns (rtx x)
*** 1248,1267 ****
      case CONST_DOUBLE:
        return (!TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0);
  
!     default:
!       switch (mips_classify_constant (&c, x))
  	{
! 	case CONSTANT_NONE:
! 	  return 0;
  
! 	case CONSTANT_GP:
! 	case CONSTANT_RELOC:
! 	  return 1;
  
! 	case CONSTANT_SYMBOLIC:
! 	  return mips_symbol_insns (mips_classify_symbol (c.symbol));
! 	}
!       abort ();
      }
  }
  
--- 1197,1233 ----
      case CONST_DOUBLE:
        return (!TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0);
  
!     case CONST:
!       if (CONST_GP_P (x))
! 	return 1;
! 
!       /* See if we can refer to X directly.  */
!       if (mips_symbolic_constant_p (x, &symbol_type))
! 	return mips_symbol_insns (symbol_type);
! 
!       /* Otherwise try splitting the constant into a base and offset.
! 	 16-bit offsets can be added using an extra addiu.  Larger offsets
! 	 must be calculated separately and then added to the base.  */
!       mips_split_const (x, &x, &offset);
!       if (offset != 0)
  	{
! 	  int n = mips_const_insns (x);
! 	  if (n != 0)
! 	    {
! 	      if (SMALL_OPERAND (offset))
! 		return n + 1;
! 	      else
! 		return n + 1 + mips_build_integer (codes, offset);
! 	    }
! 	}
!       return 0;
  
!     case SYMBOL_REF:
!     case LABEL_REF:
!       return mips_symbol_insns (mips_classify_symbol (x));
  
!     default:
!       return 0;
      }
  }
  
*************** mips_fetch_insns (rtx x)
*** 1279,1297 ****
  }
  
  
- /* Return true if OP is a symbolic constant that refers to a
-    global PIC symbol.  */
- 
- bool
- mips_global_pic_constant_p (rtx op)
- {
-   struct mips_constant_info c;
- 
-   return (mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC
- 	  && mips_classify_symbol (c.symbol) == SYMBOL_GOT_GLOBAL);
- }
- 
- 
  /* Return truth value of whether OP can be used as an operands
     where a register or 16 bit unsigned integer is needed.  */
  
--- 1245,1250 ----
*************** uns_arith_operand (rtx op, enum machine_
*** 1310,1319 ****
  int
  const_arith_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
  {
!   struct mips_constant_info c;
! 
!   return ((GET_CODE (op) == CONST_INT && SMALL_INT (op))
! 	  || mips_classify_constant (&c, op) == CONSTANT_RELOC);
  }
  
  
--- 1263,1269 ----
  int
  const_arith_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
  {
!   return GET_CODE (op) == CONST_INT && SMALL_INT (op);
  }
  
  
*************** pc_or_label_operand (rtx op, enum machin
*** 1481,1490 ****
  int
  call_insn_operand (rtx op, enum machine_mode mode)
  {
!   struct mips_constant_info c;
  
!   if (mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC)
!     switch (mips_classify_symbol (c.symbol))
        {
        case SYMBOL_GENERAL:
  	/* If -mlong-calls, force all calls to use register addressing.  */
--- 1431,1440 ----
  int
  call_insn_operand (rtx op, enum machine_mode mode)
  {
!   enum mips_symbol_type symbol_type;
  
!   if (mips_symbolic_constant_p (op, &symbol_type))
!     switch (symbol_type)
        {
        case SYMBOL_GENERAL:
  	/* If -mlong-calls, force all calls to use register addressing.  */
*************** call_insn_operand (rtx op, enum machine_
*** 1496,1502 ****
  	   Using "la $25,foo; jal $25" would prevent the lazy binding
  	   of "foo", so keep the address of global symbols with the
  	   jal macro.  */
! 	return c.offset == 0 && !TARGET_EXPLICIT_RELOCS;
  
        default:
  	return false;
--- 1446,1452 ----
  	   Using "la $25,foo; jal $25" would prevent the lazy binding
  	   of "foo", so keep the address of global symbols with the
  	   jal macro.  */
! 	return !TARGET_EXPLICIT_RELOCS;
  
        default:
  	return false;
*************** call_insn_operand (rtx op, enum machine_
*** 1511,1525 ****
  int
  move_operand (rtx op, enum machine_mode mode)
  {
!   struct mips_constant_info c;
  
!   if (GET_CODE (op) == HIGH && TARGET_ABICALLS)
      return false;
!   if (GET_CODE (op) == CONST_INT && !TARGET_MIPS16)
!     return (SMALL_INT (op) || SMALL_INT_UNSIGNED (op) || LUI_INT (op));
!   if (mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC)
!     return mips_symbolic_address_p (c.symbol, c.offset, word_mode, 1);
!   return general_operand (op, mode);
  }
  
  
--- 1461,1495 ----
  int
  move_operand (rtx op, enum machine_mode mode)
  {
!   enum mips_symbol_type symbol_type;
  
!   if (!general_operand (op, mode))
      return false;
! 
!   switch (GET_CODE (op))
!     {
!     case CONST_INT:
!       /* When generating mips16 code, LEGITIMATE_CONSTANT_P rejects
! 	 CONST_INTs that can't be loaded using simple insns.  */
!       if (TARGET_MIPS16)
! 	return true;
! 
!       /* Otherwise check whether the constant can be loaded in a single
! 	 instruction.  */
!       return LUI_INT (op) || SMALL_INT (op) || SMALL_INT_UNSIGNED (op);
! 
!     case CONST:
!     case SYMBOL_REF:
!     case LABEL_REF:
!       if (CONST_GP_P (op))
! 	return true;
! 
!       return (mips_symbolic_constant_p (op, &symbol_type)
! 	      && !mips_split_p[symbol_type]);
! 
!     default:
!       return true;
!     }
  }
  
  
*************** consttable_operand (rtx op, enum machine
*** 1540,1548 ****
  int
  symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
  {
!   struct mips_constant_info c;
  
!   return mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC;
  }
  
  
--- 1510,1544 ----
  int
  symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
  {
!   enum mips_symbol_type symbol_type;
! 
!   return mips_symbolic_constant_p (op, &symbol_type);
! }
! 
! 
! /* Return true if we're generating PIC and OP is a global symbol.  */
! 
! int
! global_got_operand (rtx op, enum machine_mode mode)
! {
!   enum mips_symbol_type symbol_type;
  
!   return ((mode == VOIDmode || mode == GET_MODE (op))
! 	  && mips_symbolic_constant_p (op, &symbol_type)
! 	  && symbol_type == SYMBOL_GOT_GLOBAL);
! }
! 
! 
! /* Likewise for local symbols.  */
! 
! int
! local_got_operand (rtx op, enum machine_mode mode)
! {
!   enum mips_symbol_type symbol_type;
! 
!   return ((mode == VOIDmode || mode == GET_MODE (op))
! 	  && mips_symbolic_constant_p (op, &symbol_type)
! 	  && symbol_type == SYMBOL_GOT_LOCAL);
  }
  
  
*************** stack_operand (rtx op, enum machine_mode
*** 1556,1563 ****
  
    return ((mode == VOIDmode || mode == GET_MODE (op))
  	  && GET_CODE (op) == MEM
! 	  && mips_classify_address (&addr, XEXP (op, 0),
! 				    GET_MODE (op), false, true) == ADDRESS_REG
  	  && addr.reg == stack_pointer_rtx);
  }
  
--- 1552,1559 ----
  
    return ((mode == VOIDmode || mode == GET_MODE (op))
  	  && GET_CODE (op) == MEM
! 	  && mips_classify_address (&addr, XEXP (op, 0), GET_MODE (op), false)
! 	  && addr.type == ADDRESS_REG
  	  && addr.reg == stack_pointer_rtx);
  }
  
*************** mips_legitimate_address_p (enum machine_
*** 1572,1655 ****
  {
    struct mips_address_info addr;
  
!   return mips_classify_address (&addr, x, mode, strict, 0) != ADDRESS_INVALID;
  }
  
  
! /* Return an rtx that represents the effect of applying relocation
!    RELOC to symbolic address ADDR.  */
  
  static rtx
! mips_reloc (rtx addr, int reloc)
  {
!   struct mips_constant_info c;
!   rtx x;
! 
!   if (mips_classify_constant (&c, addr) != CONSTANT_SYMBOLIC)
!     abort ();
! 
!   x = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, c.symbol), reloc);
!   return plus_constant (gen_rtx_CONST (VOIDmode, x), c.offset);
  }
  
  
! /* Likewise, but shift the result left 16 bits.  The expression can be
!    used as the right hand side of an LUISI or LUIDI pattern.  */
  
  static rtx
! mips_lui_reloc (rtx addr, int reloc)
  {
!   return gen_rtx_UNSPEC (Pmode,
! 			 gen_rtvec (1, mips_reloc (addr, reloc)),
! 			 UNSPEC_HIGH);
  }
  
- /* Copy VALUE to a register and return that register.  Use DEST as the
-    register if non-null, otherwise create a new one.
  
!    VALUE must be valid on the right hand side of a simple SET pattern.
!    The operation happens in Pmode.  */
  
  static rtx
! mips_force_temporary (rtx dest, rtx value)
  {
!   if (dest == 0)
!     return force_reg (Pmode, value);
!   else
!     {
!       if (!rtx_equal_p (dest, value))
! 	emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (dest), value));
!       return dest;
!     }
  }
  
  
! /* Return a legitimate address for REG + OFFSET.  This function will
!    create a temporary register if OFFSET is not a SMALL_OPERAND.  */
  
  static rtx
! mips_add_offset (rtx reg, HOST_WIDE_INT offset)
  {
!   if (!SMALL_OPERAND (offset))
!     reg = expand_simple_binop (GET_MODE (reg), PLUS,
! 			       GEN_INT (CONST_HIGH_PART (offset)),
! 			       reg, NULL, 0, OPTAB_WIDEN);
! 
!   return plus_constant (reg, CONST_LOW_PART (offset));
  }
  
  
! /* Return the GOT entry whose address is given by %RELOC(ADDR)(BASE).
!    BASE is a base register (such as $gp), ADDR is addresses being
!    sought and RELOC is the relocation that should be used.  */
  
  static rtx
! mips_load_got (rtx base, rtx addr, int reloc)
  {
!   rtx mem;
  
!   mem = gen_rtx_MEM (ptr_mode,
! 		     gen_rtx_PLUS (Pmode, base, mips_reloc (addr, reloc)));
    set_mem_alias_set (mem, mips_got_alias_set);
  
    /* GOT references can't trap.  */
--- 1568,1654 ----
  {
    struct mips_address_info addr;
  
!   return mips_classify_address (&addr, x, mode, strict);
  }
  
  
! /* Copy VALUE to a register and return that register.  If new psuedos
!    are allowed, copy it into a new register, otherwise use DEST.  */
  
  static rtx
! mips_force_temporary (rtx dest, rtx value)
  {
!   if (!no_new_pseudos)
!     return force_reg (Pmode, value);
!   else
!     {
!       emit_move_insn (copy_rtx (dest), value);
!       return dest;
!     }
  }
  
  
! /* Return a LO_SUM expression for ADDR.  TEMP is as for mips_force_temporary
!    and is used to load the high part into a register.  */
  
  static rtx
! mips_split_symbol (rtx temp, rtx addr)
  {
!   rtx high;
! 
!   if (TARGET_MIPS16)
!     high = mips16_gp_pseudo_reg ();
!   else
!     high = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr)));
!   return gen_rtx_LO_SUM (Pmode, high, addr);
  }
  
  
! /* Return an UNSPEC address with underlying address ADDRESS and symbol
!    type SYMBOL_TYPE.  */
  
  static rtx
! mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
  {
!   rtx base;
!   HOST_WIDE_INT offset;
! 
!   mips_split_const (address, &base, &offset);
!   base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
! 			 UNSPEC_ADDRESS_FIRST + symbol_type);
!   return plus_constant (gen_rtx_CONST (Pmode, base), offset);
  }
  
  
! /* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the
!    high part to BASE and return the result.  Just return BASE otherwise.
! 
!    The returned expression can be used as the first operand to a LO_SUM.  */
  
  static rtx
! mips_unspec_offset_high (rtx base, rtx addr, enum mips_symbol_type symbol_type)
  {
!   if (mips_split_p[symbol_type])
!     {
!       addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type));
!       base = force_reg (Pmode, expand_simple_binop (Pmode, PLUS, base, addr,
! 						    NULL, 0, OPTAB_WIDEN));
!     }
!   return base;
  }
  
  
! /* Return a memory reference for the GOT slot whose offset is given by
!    mips_unspec_address (ADDR, SYMBOL_TYPE).  Register BASE contains the
!    high part of the offset plus $gp.  */
  
  static rtx
! mips_load_got (rtx base, rtx addr, enum mips_symbol_type symbol_type)
  {
!   rtx mem, offset;
  
!   offset = mips_unspec_address (addr, symbol_type);
!   mem = gen_rtx_MEM (ptr_mode, gen_rtx_LO_SUM (Pmode, base, offset));
    set_mem_alias_set (mem, mips_got_alias_set);
  
    /* GOT references can't trap.  */
*************** mips_load_got (rtx base, rtx addr, int r
*** 1657,1770 ****
  
    /* If we allow a function's address to be lazily bound, its entry
       may change after the first call.  Other entries are constant.  */
!   if (reloc != RELOC_CALL16 && reloc != RELOC_CALL_LO)
      RTX_UNCHANGING_P (mem) = 1;
  
    return mem;
  }
  
  
! /* Obtain the address of ADDR from the GOT using relocation RELOC.
!    The returned address may be used on the right hand side of a SET.  */
  
! static rtx
! mips_load_got16 (rtx addr, int reloc)
  {
!   return mips_load_got (pic_offset_table_rtx, addr, reloc);
  }
  
  
! /* Like mips_load_got16, but for 32-bit offsets.  HIGH_RELOC is the
!    relocation that gives the high 16 bits of the offset and LOW_RELOC is
!    the relocation that gives the low 16 bits.  TEMP is a Pmode register
!    to use a temporary, or null if new registers can be created at will.  */
  
! static rtx
! mips_load_got32 (rtx temp, rtx addr, int high_reloc, int low_reloc)
  {
!   rtx x;
! 
!   x = mips_force_temporary (temp, mips_lui_reloc (addr, high_reloc));
!   x = mips_force_temporary (temp,
! 			    gen_rtx_PLUS (Pmode, pic_offset_table_rtx, x));
!   return mips_load_got (x, addr, low_reloc);
  }
  
  
! /* Copy the high part of ADDR into a register and return the register.
!    Use DEST as the register if non-null.  */
  
! static rtx
! mips_emit_high (rtx dest, rtx addr)
  {
!   rtx high, x;
! 
!   high = gen_rtx_HIGH (Pmode, addr);
!   if (TARGET_ABICALLS)
!     {
!       x = mips_load_got16 (copy_rtx (addr), RELOC_GOT_PAGE);
!       x = mips_force_temporary (dest, x);
!       set_unique_reg_note (get_last_insn (), REG_EQUAL, high);
!     }
!   else
!     x = mips_force_temporary (dest, high);
! 
!   return x;
  }
  
  
! /* See if *XLOC is a symbolic constant that can be reduced in some way.
!    If it is, set *XLOC to the reduced expression and return true.
!    The new expression will be both a legitimate address and a legitimate
!    source operand for a mips.md SET pattern.  If OFFSETABLE_P, the
!    address will be offsetable.
! 
!    DEST is a register to use a temporary, or null if new registers
!    can be created at will.  */
  
! static bool
! mips_legitimize_symbol (rtx dest, rtx *xloc, int offsetable_p)
  {
!   struct mips_constant_info c;
!   enum mips_symbol_type symbol_type;
!   rtx x;
! 
!   if (mips_classify_constant (&c, *xloc) != CONSTANT_SYMBOLIC)
!     return false;
! 
!   symbol_type = mips_classify_symbol (c.symbol);
! 
!   /* If a non-offsetable address is OK, try splitting it into a
!      high part and a LO_SUM.  */
!   if (!offsetable_p && mips_splittable_symbol_p (symbol_type, c.offset))
!     {
!       if (symbol_type == SYMBOL_SMALL_DATA)
! 	x = mips_sdata_pointer ();
!       else
! 	x = mips_emit_high (dest, *xloc);
!       if (x != 0)
! 	{
! 	  *xloc = gen_rtx_LO_SUM (Pmode, x, copy_rtx (*xloc));
! 	  return true;
! 	}
!     }
! 
!   /* If the offset is nonzero, move the symbol into a register (always valid)
!      and add the constant in afterwards.  This requires an extra temporary if
!      the offset isn't a signed 16-bit number.
! 
!      For mips16, it's better to force the constant into memory instead.  */
!   if (!TARGET_MIPS16
!       && c.offset != 0
!       && (SMALL_OPERAND (c.offset) || dest == 0))
!     {
!       x = (dest == 0 ? gen_reg_rtx (Pmode) : dest);
!       emit_move_insn (copy_rtx (x), c.symbol);
!       *xloc = mips_add_offset (x, c.offset);
!       return true;
!     }
  
!   return false;
  }
  
  
--- 1656,1709 ----
  
    /* If we allow a function's address to be lazily bound, its entry
       may change after the first call.  Other entries are constant.  */
!   if (symbol_type != SYMBOL_GOTOFF_CALL)
      RTX_UNCHANGING_P (mem) = 1;
  
    return mem;
  }
  
  
! /* Return the offset of ADDR's GOT entry from _gp.  ADDR is a
!    global_got_operand.  */
  
! rtx
! mips_gotoff_global (rtx addr)
  {
!   return mips_unspec_address (addr, SYMBOL_GOTOFF_GLOBAL);
  }
  
  
! /* Fetch the high part of local_got_operand ADDR from the GOT.  */
  
! rtx
! mips_load_got_page (rtx addr)
  {
!   return mips_load_got (pic_offset_table_rtx, addr, SYMBOL_GOTOFF_PAGE);
  }
  
  
! /* Fetch the address of global_got_operand ADDR from the GOT.  BASE is a
!    register that holds the address _gp + %got_hi(ADDR).  */
  
! rtx
! mips_load_got_global (rtx base, rtx addr)
  {
!   return mips_load_got (base, addr, SYMBOL_GOTOFF_GLOBAL);
  }
  
  
! /* Return a legitimate address for REG + OFFSET.  This function will
!    create a temporary register if OFFSET is not a SMALL_OPERAND.  */
  
! static rtx
! mips_add_offset (rtx reg, HOST_WIDE_INT offset)
  {
!   if (!SMALL_OPERAND (offset))
!     reg = expand_simple_binop (GET_MODE (reg), PLUS,
! 			       GEN_INT (CONST_HIGH_PART (offset)),
! 			       reg, NULL, 0, OPTAB_WIDEN);
  
!   return plus_constant (reg, CONST_LOW_PART (offset));
  }
  
  
*************** mips_legitimize_symbol (rtx dest, rtx *x
*** 1776,1783 ****
  bool
  mips_legitimize_address (rtx *xloc, enum machine_mode mode)
  {
!   if (mips_legitimize_symbol (0, xloc, !SINGLE_WORD_MODE_P (mode)))
!     return true;
  
    if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
      {
--- 1715,1730 ----
  bool
  mips_legitimize_address (rtx *xloc, enum machine_mode mode)
  {
!   enum mips_symbol_type symbol_type;
! 
!   /* See if the address can split into a high part and a LO_SUM.  */
!   if (mips_symbolic_constant_p (*xloc, &symbol_type)
!       && mips_symbolic_address_p (symbol_type, mode)
!       && mips_split_p[symbol_type])
!     {
!       *xloc = mips_split_symbol (0, *xloc);
!       return true;
!     }
  
    if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
      {
*************** mips_move_integer (rtx dest, unsigned HO
*** 1933,1986 ****
  static void
  mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
  {
!   rtx temp;
! 
!   temp = no_new_pseudos ? dest : 0;
! 
!   /* If generating PIC, the high part of an address is loaded from the GOT.  */
!   if (GET_CODE (src) == HIGH)
!     {
!       mips_emit_high (dest, XEXP (src, 0));
!       return;
!     }
  
    if (GET_CODE (src) == CONST_INT && !TARGET_MIPS16)
      {
        mips_move_integer (dest, INTVAL (src));
        return;
      }
  
!   /* Fetch global symbols from the GOT.  */
!   if (TARGET_EXPLICIT_RELOCS
!       && GET_CODE (src) == SYMBOL_REF
!       && mips_classify_symbol (src) == SYMBOL_GOT_GLOBAL)
      {
!       if (TARGET_XGOT)
! 	src = mips_load_got32 (temp, src, RELOC_GOT_HI, RELOC_GOT_LO);
!       else
! 	src = mips_load_got16 (src, RELOC_GOT_DISP);
!       emit_insn (gen_rtx_SET (VOIDmode, dest, src));
        return;
      }
  
!   /* Try handling the source operand as a symbolic address.  */
!   if (mips_legitimize_symbol (temp, &src, false))
      {
!       emit_insn (gen_rtx_SET (VOIDmode, dest, src));
        return;
      }
  
    src = force_const_mem (mode, src);
  
    /* When using explicit relocs, constant pool references are sometimes
!      not legitimate addresses.  mips_legitimize_symbol must be able to
!      deal with all such cases.  */
!   if (GET_CODE (src) == MEM && !memory_operand (src, VOIDmode))
!     {
!       src = copy_rtx (src);
!       if (!mips_legitimize_symbol (temp, &XEXP (src, 0), false))
! 	abort ();
!     }
    emit_move_insn (dest, src);
  }
  
--- 1880,1925 ----
  static void
  mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
  {
!   rtx base;
!   HOST_WIDE_INT offset;
!   enum mips_symbol_type symbol_type;
  
+   /* Split moves of big integers into smaller pieces.  In mips16 code,
+      it's better to force the constant into memory instead.  */
    if (GET_CODE (src) == CONST_INT && !TARGET_MIPS16)
      {
        mips_move_integer (dest, INTVAL (src));
        return;
      }
  
!   /* See if the symbol can be split.  For mips16, this is often worse than
!      forcing it in the constant pool since it needs the single-register form
!      of addiu or daddiu.  */
!   if (!TARGET_MIPS16
!       && mips_symbolic_constant_p (src, &symbol_type)
!       && mips_split_p[symbol_type])
      {
!       emit_move_insn (dest, mips_split_symbol (dest, src));
        return;
      }
  
!   /* If we have (const (plus symbol offset)), load the symbol first
!      and then add in the offset.  This is usually better than forcing
!      the constant into memory, at least in non-mips16 code.  */
!   mips_split_const (src, &base, &offset);
!   if (!TARGET_MIPS16 && offset != 0)
      {
!       base = mips_force_temporary (dest, base);
!       emit_move_insn (dest, mips_add_offset (base, offset));
        return;
      }
  
    src = force_const_mem (mode, src);
  
    /* When using explicit relocs, constant pool references are sometimes
!      not legitimate addresses.  */
!   if (!memory_operand (src, VOIDmode))
!     src = replace_equiv_address (src, mips_split_symbol (dest, XEXP (src, 0)));
    emit_move_insn (dest, src);
  }
  
*************** mips_legitimize_move (enum machine_mode 
*** 2011,2040 ****
      }
    return false;
  }
- 
- 
- /* Convert GOT and GP-relative accesses back into their original form.
-    Used by both TARGET_DELEGITIMIZE_ADDRESS and FIND_BASE_TERM.  */
- 
- rtx
- mips_delegitimize_address (rtx x)
- {
-   struct mips_constant_info c;
- 
-   if (GET_CODE (x) == MEM
-       && GET_CODE (XEXP (x, 0)) == PLUS
-       && mips_classify_constant (&c, XEXP (XEXP (x, 0), 1)) == CONSTANT_RELOC
-       && mips_classify_symbol (c.symbol) == SYMBOL_GOT_GLOBAL)
-     return c.symbol;
- 
-   if (GET_CODE (x) == LO_SUM
-       && XEXP (x, 0) == (TARGET_MIPS16
- 			 ? cfun->machine->mips16_gp_pseudo_rtx
- 			 : pic_offset_table_rtx))
-     return XEXP (x, 1);
- 
-   return x;
- }
  
  /* We need a lot of little routines to check constant values on the
     mips16.  These are used to figure out how long the instruction will
--- 1950,1955 ----
*************** mips_rtx_costs (rtx x, int code, int out
*** 2261,2277 ****
      case SYMBOL_REF:
      case LABEL_REF:
      case CONST_DOUBLE:
!       if (((outer_code) == PLUS || (outer_code) == MINUS)
!           && const_arith_operand (x, VOIDmode))
!         {
!           *total = 0;
!           return true;
!         }
        else
!         {
!           int n = mips_const_insns (x);
!           return (n == 0 ? CONSTANT_POOL_COST : COSTS_N_INSNS (n));
!         }
  
      case MEM:
        {
--- 2176,2192 ----
      case SYMBOL_REF:
      case LABEL_REF:
      case CONST_DOUBLE:
!       if (LEGITIMATE_CONSTANT_P (x))
! 	{
! 	  *total = COSTS_N_INSNS (1);
! 	  return true;
! 	}
        else
! 	{
! 	  /* The value will need to be fetched from the constant pool.  */
! 	  *total = CONSTANT_POOL_COST;
! 	  return true;
! 	}
  
      case MEM:
        {
*************** mips_subword (rtx op, int high_p)
*** 2558,2564 ****
      }
  
    if (GET_CODE (op) == MEM)
!     return adjust_address (op, word_mode, byte);
  
    return simplify_gen_subreg (word_mode, op, mode, byte);
  }
--- 2473,2479 ----
      }
  
    if (GET_CODE (op) == MEM)
!     return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
  
    return simplify_gen_subreg (word_mode, op, mode, byte);
  }
*************** const char *
*** 2648,2654 ****
  mips_output_move (rtx dest, rtx src)
  {
    enum rtx_code dest_code, src_code;
-   struct mips_constant_info c;
    bool dbl_p;
  
    dest_code = GET_CODE (dest);
--- 2563,2568 ----
*************** mips_output_move (rtx dest, rtx src)
*** 2725,2744 ****
        if (src_code == HIGH)
  	return "lui\t%0,%h1";
  
!       switch (mips_classify_constant (&c, src))
! 	{
! 	case CONSTANT_NONE:
! 	  break;
! 
! 	case CONSTANT_GP:
! 	  return "move\t%0,%1";
  
! 	case CONSTANT_RELOC:
! 	  return "li\t%0,%1";
! 
! 	case CONSTANT_SYMBOLIC:
! 	  return (dbl_p ? "dla\t%0,%a1" : "la\t%0,%a1");
! 	}
      }
    if (src_code == REG && FP_REG_P (REGNO (src)))
      {
--- 2639,2649 ----
        if (src_code == HIGH)
  	return "lui\t%0,%h1";
  
!       if (CONST_GP_P (src))
! 	return "move\t%0,%1";
  
!       if (symbolic_operand (src, VOIDmode))
! 	return (dbl_p ? "dla\t%0,%1" : "la\t%0,%1");
      }
    if (src_code == REG && FP_REG_P (REGNO (src)))
      {
*************** mips_expand_call (rtx result, rtx addr, 
*** 3277,3292 ****
  {
    if (!call_insn_operand (addr, VOIDmode))
      {
!       /* When generating PIC, try to allow global functions to be
! 	 lazily bound.  */
!       if (TARGET_EXPLICIT_RELOCS
! 	  && GET_CODE (addr) == SYMBOL_REF
! 	  && mips_classify_symbol (addr) == SYMBOL_GOT_GLOBAL)
  	{
! 	  if (TARGET_XGOT)
! 	    addr = mips_load_got32 (0, addr, RELOC_CALL_HI, RELOC_CALL_LO);
! 	  else
! 	    addr = mips_load_got16 (addr, RELOC_CALL16);
  	}
        addr = force_reg (Pmode, addr);
      }
--- 3182,3192 ----
  {
    if (!call_insn_operand (addr, VOIDmode))
      {
!       if (TARGET_EXPLICIT_RELOCS && global_got_operand (addr, VOIDmode))
  	{
! 	  rtx high = mips_unspec_offset_high (pic_offset_table_rtx,
! 					      addr, SYMBOL_GOTOFF_CALL);
! 	  addr = mips_load_got (high, addr, SYMBOL_GOTOFF_CALL);
  	}
        addr = force_reg (Pmode, addr);
      }
*************** override_options (void)
*** 5035,5040 ****
--- 4935,5002 ----
  
    /* Create a unique alias set for GOT references.  */
    mips_got_alias_set = new_alias_set ();
+ 
+   if (TARGET_EXPLICIT_RELOCS || mips_split_addresses)
+     {
+       mips_split_p[SYMBOL_GENERAL] = true;
+       mips_hi_relocs[SYMBOL_GENERAL] = "%hi(";
+       mips_lo_relocs[SYMBOL_GENERAL] = "%lo(";
+     }
+ 
+   if (TARGET_MIPS16)
+     {
+       /* The high part is provided by a pseudo copy of $gp.  */
+       mips_split_p[SYMBOL_SMALL_DATA] = true;
+       mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gprel(";
+     }
+ 
+   if (TARGET_EXPLICIT_RELOCS)
+     {
+       /* Small data constants are kept whole until after reload,
+ 	 then lowered by mips_rewrite_small_data.  */
+       mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gp_rel(";
+ 
+       mips_split_p[SYMBOL_GOT_LOCAL] = true;
+       if (TARGET_NEWABI)
+ 	{
+ 	  mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
+ 	  mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%got_ofst(";
+ 	}
+       else
+ 	{
+ 	  mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
+ 	  mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%lo(";
+ 	}
+ 
+       if (TARGET_XGOT)
+ 	{
+ 	  /* The HIGH and LO_SUM are matched by special .md patterns.  */
+ 	  mips_split_p[SYMBOL_GOT_GLOBAL] = true;
+ 
+ 	  mips_split_p[SYMBOL_GOTOFF_GLOBAL] = true;
+ 	  mips_hi_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_hi(";
+ 	  mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_lo(";
+ 
+ 	  mips_split_p[SYMBOL_GOTOFF_CALL] = true;
+ 	  mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
+ 	  mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
+ 	}
+       else
+ 	{
+ 	  if (TARGET_NEWABI)
+ 	    mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_disp(";
+ 	  else
+ 	    mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got(";
+ 	  mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
+ 	}
+     }
+ 
+   if (TARGET_NEWABI)
+     {
+       mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
+       mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
+       mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
+     }
  }
  
  /* Implement CONDITIONAL_REGISTER_USAGE.  */
*************** void
*** 5215,5222 ****
  print_operand (FILE *file, rtx op, int letter)
  {
    register enum rtx_code code;
-   struct mips_constant_info c;
-   const char *reloc;
  
    if (PRINT_OPERAND_PUNCT_VALID_P (letter))
      {
--- 5177,5182 ----
*************** print_operand (FILE *file, rtx op, int l
*** 5357,5372 ****
  
    code = GET_CODE (op);
  
!   if (letter == 'h')
!     {
!       if (GET_CODE (op) != HIGH)
! 	abort ();
!       fputs ("%hi(", file);
!       output_addr_const (file, XEXP (op, 0));
!       fputc (')', file);
!     }
! 
!   else if (letter == 'C')
      switch (code)
        {
        case EQ:	fputs ("eq",  file); break;
--- 5317,5323 ----
  
    code = GET_CODE (op);
  
!   if (letter == 'C')
      switch (code)
        {
        case EQ:	fputs ("eq",  file); break;
*************** print_operand (FILE *file, rtx op, int l
*** 5418,5423 ****
--- 5369,5385 ----
  	fatal_insn ("PRINT_OPERAND, invalid insn for %%W", op);
        }
  
+   else if (letter == 'h')
+     {
+       if (GET_CODE (op) == HIGH)
+ 	op = XEXP (op, 0);
+ 
+       print_operand_reloc (file, op, mips_hi_relocs);
+     }
+ 
+   else if (letter == 'R')
+     print_operand_reloc (file, op, mips_lo_relocs);
+ 
    else if (letter == 'S')
      {
        char buffer[100];
*************** print_operand (FILE *file, rtx op, int l
*** 5490,5551 ****
    else if (letter == 't')
      fputs (code == EQ ? "t" : "f", file);
  
!   else
!     switch (mips_classify_constant (&c, op))
!       {
!       case CONSTANT_SYMBOLIC:
! 	if (letter == 'R')
! 	  {
! 	    if (mips_classify_symbol (c.symbol) == SYMBOL_SMALL_DATA)
! 	      fputs (TARGET_MIPS16 ? "%gprel(" : "%gp_rel(", file);
! 	    else if (TARGET_ABICALLS && TARGET_NEWABI)
! 	      fputs ("%got_ofst(", file);
! 	    else
! 	      fputs ("%lo(", file);
! 	    output_addr_const (file, op);
! 	    fputc (')', file);
! 	    break;
! 	  }
! 	/* ... fall through ... */
  
!       case CONSTANT_NONE:
! 	output_addr_const (file, op);
! 	break;
  
-       case CONSTANT_GP:
- 	fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
- 	break;
  
!       case CONSTANT_RELOC:
! 	reloc = mips_reloc_string (c.reloc);
! 	fputs (reloc, file);
! 	output_addr_const (file, plus_constant (c.symbol, c.offset));
! 	while (*reloc != 0)
! 	  if (*reloc++ == '(')
! 	    fputc (')', file);
!       }
! }
! 
! /* Return the assembly operator used for the given type of relocation.  */
  
! static const char *
! mips_reloc_string (int reloc)
  {
!   switch (reloc)
!     {
!     case RELOC_GOT_HI:	  return "%got_hi(";
!     case RELOC_GOT_LO:	  return "%got_lo(";
!     case RELOC_GOT_PAGE:  return (TARGET_NEWABI ? "%got_page(" : "%got(");
!     case RELOC_GOT_DISP:  return (TARGET_NEWABI ? "%got_disp(" : "%got(");
!     case RELOC_CALL16:	  return "%call16(";
!     case RELOC_CALL_HI:	  return "%call_hi(";
!     case RELOC_CALL_LO:	  return "%call_lo(";
!     case RELOC_LOADGP_HI: return "%hi(%neg(%gp_rel(";
!     case RELOC_LOADGP_LO: return "%lo(%neg(%gp_rel(";
!     }
!   abort ();
! }
  
  /* Output address operand X to FILE.   */
  
  void
--- 5452,5491 ----
    else if (letter == 't')
      fputs (code == EQ ? "t" : "f", file);
  
!   else if (CONST_GP_P (op))
!     print_operand (file, XEXP (op, 0), letter);
  
!   else
!     output_addr_const (file, op);
! }
  
  
! /* Print symbolic operand OP, which is part of a HIGH or LO_SUM.
!    RELOCS is the array of relocations to use.  */
  
! static void
! print_operand_reloc (FILE *file, rtx op, const char **relocs)
  {
!   enum mips_symbol_type symbol_type;
!   const char *p;
!   rtx base;
!   HOST_WIDE_INT offset;
! 
!   if (!mips_symbolic_constant_p (op, &symbol_type) || relocs[symbol_type] == 0)
!     fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op);
  
+   /* If OP uses an UNSPEC address, we want to print the inner symbol.  */
+   mips_split_const (op, &base, &offset);
+   if (UNSPEC_ADDRESS_P (base))
+     op = plus_constant (UNSPEC_ADDRESS (base), offset);
+ 
+   fputs (relocs[symbol_type], file);
+   output_addr_const (file, op);
+   for (p = relocs[symbol_type]; *p != 0; p++)
+     if (*p == '(')
+       fputc (')', file);
+ }
+ 
  /* Output address operand X to FILE.   */
  
  void
*************** print_operand_address (FILE *file, rtx x
*** 5553,5578 ****
  {
    struct mips_address_info addr;
  
!   switch (mips_classify_address (&addr, x, word_mode, 1, 1))
!     {
!     case ADDRESS_INVALID:
!       abort ();
! 
!     case ADDRESS_REG:
!       print_operand (file, addr.offset, 0);
!       fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
!       return;
! 
!     case ADDRESS_LO_SUM:
!       print_operand (file, addr.offset, 'R');
!       fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
!       return;
! 
!     case ADDRESS_CONST_INT:
!     case ADDRESS_SYMBOLIC:
!       output_addr_const (file, x);
!       return;
!     }
    abort ();
  }
  
--- 5493,5516 ----
  {
    struct mips_address_info addr;
  
!   if (mips_classify_address (&addr, x, word_mode, true))
!     switch (addr.type)
!       {
!       case ADDRESS_REG:
! 	print_operand (file, addr.offset, 0);
! 	fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
! 	return;
! 
!       case ADDRESS_LO_SUM:
! 	print_operand (file, addr.offset, 'R');
! 	fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
! 	return;
! 
!       case ADDRESS_CONST_INT:
!       case ADDRESS_SYMBOLIC:
! 	output_addr_const (file, x);
! 	return;
!       }
    abort ();
  }
  
*************** mips_finish_declare_object (FILE *stream
*** 5994,5999 ****
--- 5932,6024 ----
  }
  #endif
  
+ /* Return true if X is a small data address that can be rewritten
+    as a LO_SUM.  */
+ 
+ static bool
+ mips_rewrite_small_data_p (rtx x)
+ {
+   enum mips_symbol_type symbol_type;
+ 
+   return (TARGET_EXPLICIT_RELOCS
+ 	  && mips_symbolic_constant_p (x, &symbol_type)
+ 	  && symbol_type == SYMBOL_SMALL_DATA);
+ }
+ 
+ 
+ /* A for_each_rtx callback for small_data_pattern.  */
+ 
+ static int
+ small_data_pattern_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+ {
+   if (GET_CODE (*loc) == LO_SUM)
+     return -1;
+ 
+   return mips_rewrite_small_data_p (*loc);
+ }
+ 
+ /* Return true if OP refers to small data symbols directly, not through
+    a LO_SUM.  */
+ 
+ int
+ small_data_pattern (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+ {
+   return (GET_CODE (op) != SEQUENCE
+ 	  && for_each_rtx (&op, small_data_pattern_1, 0));
+ }
+ 
+ /* A for_each_rtx callback, used by mips_rewrite_small_data.  */
+ 
+ static int
+ mips_rewrite_small_data_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+ {
+   if (mips_rewrite_small_data_p (*loc))
+     *loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc);
+ 
+   if (GET_CODE (*loc) == LO_SUM)
+     return -1;
+ 
+   return 0;
+ }
+ 
+ /* If possible, rewrite OP so that it refers to small data using
+    explicit relocations.  */
+ 
+ rtx
+ mips_rewrite_small_data (rtx op)
+ {
+   op = copy_insn (op);
+   for_each_rtx (&op, mips_rewrite_small_data_1, 0);
+   return op;
+ }
+ 
+ /* Return true if the current function has an insn that implicitly
+    refers to $gp.  */
+ 
+ static bool
+ mips_function_has_gp_insn (void)
+ {
+   /* Don't bother rechecking if we found one last time.  */
+   if (!cfun->machine->has_gp_insn_p)
+     {
+       rtx insn;
+ 
+       push_topmost_sequence ();
+       for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ 	if (INSN_P (insn)
+ 	    && GET_CODE (PATTERN (insn)) != USE
+ 	    && GET_CODE (PATTERN (insn)) != CLOBBER
+ 	    && (get_attr_got (insn) != GOT_UNSET
+ 		|| small_data_pattern (PATTERN (insn), VOIDmode)))
+ 	  break;
+       pop_topmost_sequence ();
+ 
+       cfun->machine->has_gp_insn_p = (insn != 0);
+     }
+   return cfun->machine->has_gp_insn_p;
+ }
+ 
+ 
  /* Return the register that should be used as the global pointer
     within this function.  Return 0 if the function doesn't need
     a global pointer.  */
*************** mips_global_pointer (void)
*** 6030,6036 ****
       In cases like these, reload will have added the constant to the pool
       but no instruction will yet refer to it.  */
    if (!regs_ever_live[GLOBAL_POINTER_REGNUM]
!       && !current_function_uses_const_pool)
      return 0;
  
    /* We need a global pointer, but perhaps we can use a call-clobbered
--- 6055,6062 ----
       In cases like these, reload will have added the constant to the pool
       but no instruction will yet refer to it.  */
    if (!regs_ever_live[GLOBAL_POINTER_REGNUM]
!       && !current_function_uses_const_pool
!       && !mips_function_has_gp_insn ())
      return 0;
  
    /* We need a global pointer, but perhaps we can use a call-clobbered
*************** mips_save_reg (rtx reg, rtx mem)
*** 6530,6555 ****
  }
  
  
- /* Emit an instruction to move SRC into DEST.  When generating
-    explicit reloc code, mark the instruction as potentially dead.  */
- 
- static void
- mips_gp_insn (rtx dest, rtx src)
- {
-   rtx insn;
- 
-   insn = emit_insn (gen_rtx_SET (VOIDmode, dest, src));
-   if (TARGET_EXPLICIT_RELOCS)
-     {
-       /* compute_frame_size assumes that any function which uses the
- 	 constant pool will need a gp.  However, all constant
- 	 pool references could be eliminated, in which case
- 	 it is OK for flow to delete the gp load as well.  */
-       REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
- 					    REG_NOTES (insn));
-     }
- }
- 
  /* Expand the prologue into a bunch of separate insns.  */
  
  void
--- 6556,6561 ----
*************** mips_expand_prologue (void)
*** 6640,6658 ****
    /* If generating n32/n64 abicalls, emit the instructions to load $gp.  */
    if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
      {
!       rtx fnsymbol, fnaddr;
! 
!       fnsymbol = XEXP (DECL_RTL (current_function_decl), 0);
!       fnaddr = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
! 
!       mips_gp_insn (MIPS_PROLOGUE_TEMP (Pmode),
! 		    mips_lui_reloc (fnsymbol, RELOC_LOADGP_HI));
!       mips_gp_insn (MIPS_PROLOGUE_TEMP (Pmode),
! 		    gen_rtx_PLUS (Pmode, MIPS_PROLOGUE_TEMP (Pmode), fnaddr));
!       mips_gp_insn (pic_offset_table_rtx,
! 		    gen_rtx_PLUS (Pmode, MIPS_PROLOGUE_TEMP (Pmode),
! 				  mips_reloc (fnsymbol, RELOC_LOADGP_LO)));
  
        if (!TARGET_EXPLICIT_RELOCS)
  	emit_insn (gen_loadgp_blockage ());
      }
--- 6646,6656 ----
    /* If generating n32/n64 abicalls, emit the instructions to load $gp.  */
    if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
      {
!       rtx addr, offset;
  
+       addr = XEXP (DECL_RTL (current_function_decl), 0);
+       offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
+       emit_insn (gen_loadgp (offset));
        if (!TARGET_EXPLICIT_RELOCS)
  	emit_insn (gen_loadgp_blockage ());
      }
*************** mips_valid_pointer_mode (enum machine_mo
*** 7433,7446 ****
     hold the $gp value.  */
  
  static rtx
! mips_sdata_pointer (void)
  {
-   if (TARGET_EXPLICIT_RELOCS)
-     return pic_offset_table_rtx;
- 
-   if (!TARGET_MIPS16 || no_new_pseudos)
-     return 0;
- 
    if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
      {
        rtx const_gp;
--- 7431,7438 ----
     hold the $gp value.  */
  
  static rtx
! mips16_gp_pseudo_reg (void)
  {
    if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
      {
        rtx const_gp;
diff -cp config/mips.cvs/mips.h config/mips/mips.h
*** config/mips.cvs/mips.h	Thu Oct 30 13:52:56 2003
--- config/mips/mips.h	Sat Nov  1 10:12:50 2003
*************** extern const struct mips_cpu_info *mips_
*** 1244,1251 ****
     SFmode register saves.  */
  #define DWARF_CIE_DATA_ALIGNMENT 4
  
- #define FIND_BASE_TERM(X) mips_delegitimize_address (X)
- 
  /* Correct the offset of automatic variables and arguments.  Note that
     the MIPS debug format wants all automatic variables and arguments
     to be in terms of the virtual frame pointer (stack pointer before
--- 1244,1249 ----
*************** extern enum reg_class mips_char_to_class
*** 2028,2036 ****
     part of a call sequence and allow a global 'foo' to be lazily bound.  */
  
  #define DANGEROUS_FOR_LA25_P(OP)					\
!   (TARGET_ABICALLS							\
!    && !TARGET_EXPLICIT_RELOCS						\
!    && mips_global_pic_constant_p (OP))
  
  /* Letters in the range `Q' through `U' may be defined in a
     machine-dependent fashion to stand for arbitrary operand types.
--- 2026,2032 ----
     part of a call sequence and allow a global 'foo' to be lazily bound.  */
  
  #define DANGEROUS_FOR_LA25_P(OP)					\
!   (!TARGET_EXPLICIT_RELOCS && global_got_operand (OP, VOIDmode))
  
  /* Letters in the range `Q' through `U' may be defined in a
     machine-dependent fashion to stand for arbitrary operand types.
*************** typedef struct mips_args {
*** 2798,2804 ****
  #define PREDICATE_CODES							\
    {"uns_arith_operand",		{ REG, CONST_INT, SUBREG, ADDRESSOF }},	\
    {"symbolic_operand",		{ CONST, SYMBOL_REF, LABEL_REF }},	\
!   {"const_arith_operand",	{ CONST, CONST_INT }},			\
    {"arith_operand",		{ REG, CONST_INT, CONST, SUBREG, ADDRESSOF }},	\
    {"reg_or_0_operand",		{ REG, CONST_INT, CONST_DOUBLE, SUBREG, ADDRESSOF }}, \
    {"small_int",			{ CONST_INT }},				\
--- 2794,2804 ----
  #define PREDICATE_CODES							\
    {"uns_arith_operand",		{ REG, CONST_INT, SUBREG, ADDRESSOF }},	\
    {"symbolic_operand",		{ CONST, SYMBOL_REF, LABEL_REF }},	\
!   {"global_got_operand",	{ CONST, SYMBOL_REF, LABEL_REF }},	\
!   {"local_got_operand",		{ CONST, SYMBOL_REF, LABEL_REF }},	\
!   {"const_arith_operand",	{ CONST_INT }},				\
!   {"small_data_pattern",	{ SET, PARALLEL, UNSPEC,		\
! 				  UNSPEC_VOLATILE }},			\
    {"arith_operand",		{ REG, CONST_INT, CONST, SUBREG, ADDRESSOF }},	\
    {"reg_or_0_operand",		{ REG, CONST_INT, CONST_DOUBLE, SUBREG, ADDRESSOF }}, \
    {"small_int",			{ CONST_INT }},				\
diff -cp config/mips.cvs/mips.md config/mips/mips.md
*** config/mips.cvs/mips.md	Thu Oct 30 18:12:08 2003
--- config/mips/mips.md	Sat Nov  1 10:08:20 2003
***************
*** 53,72 ****
     (UNSPEC_LDR			23)
     (UNSPEC_SDL			24)
     (UNSPEC_SDR			25)
  
!    ;; Constants used in relocation unspecs.  RELOC_GOT_PAGE and RELOC_GOT_DISP
!    ;; are really only available for n32 and n64.  However, it is convenient
!    ;; to reuse them for SVR4 PIC, where they represent the local and global
!    ;; forms of R_MIPS_GOT16.
!    (RELOC_GOT_HI		100)
!    (RELOC_GOT_LO		101)
!    (RELOC_GOT_PAGE		102)
!    (RELOC_GOT_DISP		103)
!    (RELOC_CALL16		104)
!    (RELOC_CALL_HI		105)
!    (RELOC_CALL_LO		106)
!    (RELOC_LOADGP_HI		107)
!    (RELOC_LOADGP_LO		108)])
  
  ;; ....................
  ;;
--- 53,61 ----
     (UNSPEC_LDR			23)
     (UNSPEC_SDL			24)
     (UNSPEC_SDR			25)
+    (UNSPEC_LOADGP		26)
  
!    (UNSPEC_ADDRESS_FIRST	100)])
  
  ;; ....................
  ;;
***************
*** 74,79 ****
--- 63,71 ----
  ;;
  ;; ....................
  
+ (define_attr "got" "unset,xgot_high,load"
+   (const_string "unset"))
+ 
  ;; For jal instructions, this attribute is DIRECT when the target address
  ;; is symbolic and INDIRECT when it is a register.
  (define_attr "jal" "unset,direct,indirect"
***************
*** 125,132 ****
  ;; nop		no operation
  (define_attr "type"
    "unknown,branch,jump,call,load,store,prefetch,prefetchx,move,condmove,xfer,hilo,const,arith,darith,imul,imadd,idiv,icmp,fadd,fmul,fmadd,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,multi,nop"
!   (cond [(eq_attr "jal" "!unset")
! 	 (const_string "call")]
  	(const_string "unknown")))
  
  ;; Main data type used by the insn
--- 117,124 ----
  ;; nop		no operation
  (define_attr "type"
    "unknown,branch,jump,call,load,store,prefetch,prefetchx,move,condmove,xfer,hilo,const,arith,darith,imul,imadd,idiv,icmp,fadd,fmul,fmadd,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,multi,nop"
!   (cond [(eq_attr "jal" "!unset") (const_string "call")
! 	 (eq_attr "got" "load") (const_string "load")]
  	(const_string "unknown")))
  
  ;; Main data type used by the insn
***************
*** 179,184 ****
--- 171,181 ----
  		 (const_int 24)
  		 ] (const_int 12))
  
+ 	  (eq_attr "got" "load")
+ 	  (const_int 4)
+ 	  (eq_attr "got" "xgot_high")
+ 	  (const_int 8)
+ 
  	  (eq_attr "type" "const")
  	  (symbol_ref "mips_const_insns (operands[1]) * 4")
  	  (eq_attr "type" "load")
*************** dsrl\t%3,%3,1\n\
*** 4147,4177 ****
    [(set_attr "type" "store")
     (set_attr "mode" "DI")])
  
  
! ;; Instructions for loading a relocation expression using "lui".
  
! (define_insn "luisi"
!   [(set (match_operand:SI 0 "register_operand" "=r")
! 	(unspec:SI [(match_operand 1 "const_arith_operand" "")] UNSPEC_HIGH))]
!   ""
!   "lui\t%0,%1"
!   [(set_attr "type" "arith")])
  
! (define_insn "luidi"
!   [(set (match_operand:DI 0 "register_operand" "=r")
! 	(unspec:DI [(match_operand 1 "const_arith_operand" "")] UNSPEC_HIGH))]
!   "TARGET_64BIT"
!   "lui\t%0,%1"
!   [(set_attr "type" "arith")])
  
  
  ;; Instructions for adding the low 16 bits of an address to a register.
  ;; Operand 2 is the address: print_operand works out which relocation
  ;; should be applied.
  
  (define_insn "*lowsi"
!   [(set (match_operand:SI 0 "register_operand" "=r")
! 	(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
  		   (match_operand:SI 2 "immediate_operand" "")))]
    "!TARGET_MIPS16"
    "addiu\t%0,%1,%R2"
--- 4144,4252 ----
    [(set_attr "type" "store")
     (set_attr "mode" "DI")])
  
+ ;; Insns to fetch a global symbol from a big GOT.
  
! (define_insn_and_split "*xgot_hisi"
!   [(set (match_operand:SI 0 "register_operand" "=d")
! 	(high:SI (match_operand:SI 1 "global_got_operand" "")))]
!   "TARGET_EXPLICIT_RELOCS && TARGET_XGOT"
!   "#"
!   "&& reload_completed"
!   [(set (match_dup 0) (high:SI (match_dup 2)))
!    (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 3)))]
! {
!   operands[2] = mips_gotoff_global (operands[1]);
!   operands[3] = pic_offset_table_rtx;
! }
!   [(set_attr "got" "xgot_high")])
  
! (define_insn_and_split "*xgot_losi"
!   [(set (match_operand:SI 0 "register_operand" "=d")
! 	(lo_sum:SI (match_operand:SI 1 "register_operand" "d")
! 		   (match_operand:SI 2 "global_got_operand" "")))]
!   "TARGET_EXPLICIT_RELOCS && TARGET_XGOT"
!   "#"
!   "&& reload_completed"
!   [(set (match_dup 0) (match_dup 3))]
!   { operands[3] = mips_load_got_global (operands[1], operands[2]); }
!   [(set_attr "got" "load")])
  
! (define_insn_and_split "*xgot_hidi"
!   [(set (match_operand:DI 0 "register_operand" "=d")
! 	(high:DI (match_operand:DI 1 "global_got_operand" "")))]
!   "TARGET_EXPLICIT_RELOCS && TARGET_XGOT"
!   "#"
!   "&& reload_completed"
!   [(set (match_dup 0) (high:DI (match_dup 2)))
!    (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 3)))]
! {
!   operands[2] = mips_gotoff_global (operands[1]);
!   operands[3] = pic_offset_table_rtx;
! }
!   [(set_attr "got" "xgot_high")])
! 
! (define_insn_and_split "*xgot_lodi"
!   [(set (match_operand:DI 0 "register_operand" "=d")
! 	(lo_sum:DI (match_operand:DI 1 "register_operand" "d")
! 		   (match_operand:DI 2 "global_got_operand" "")))]
!   "TARGET_EXPLICIT_RELOCS && TARGET_XGOT"
!   "#"
!   "&& reload_completed"
!   [(set (match_dup 0) (match_dup 3))]
!   { operands[3] = mips_load_got_global (operands[1], operands[2]); }
!   [(set_attr "got" "load")])
  
+ ;; Insns to fetch a global symbol from a normal GOT.
+ 
+ (define_insn_and_split "*got_dispsi"
+   [(set (match_operand:SI 0 "register_operand" "=d")
+ 	(match_operand:SI 1 "global_got_operand" ""))]
+   "TARGET_EXPLICIT_RELOCS && !TARGET_XGOT"
+   "#"
+   "&& reload_completed"
+   [(set (match_dup 0) (match_dup 2))]
+   { operands[2] = mips_load_got_global (pic_offset_table_rtx, operands[1]); }
+   [(set_attr "got" "load")])
+ 
+ (define_insn_and_split "*got_dispdi"
+   [(set (match_operand:DI 0 "register_operand" "=d")
+ 	(match_operand:DI 1 "global_got_operand" ""))]
+   "TARGET_EXPLICIT_RELOCS && !TARGET_XGOT"
+   "#"
+   "&& reload_completed"
+   [(set (match_dup 0) (match_dup 2))]
+   { operands[2] = mips_load_got_global (pic_offset_table_rtx, operands[1]); }
+   [(set_attr "got" "load")])
+ 
+ ;; Insns for loading the high part of a local symbol.
+ 
+ (define_insn_and_split "*got_pagesi"
+   [(set (match_operand:SI 0 "register_operand" "=d")
+ 	(high:SI (match_operand:SI 1 "local_got_operand" "")))]
+   "TARGET_EXPLICIT_RELOCS"
+   "#"
+   "&& reload_completed"
+   [(set (match_dup 0) (match_dup 2))]
+   { operands[2] = mips_load_got_page (operands[1]); }
+   [(set_attr "got" "load")])
+ 
+ (define_insn_and_split "*got_pagedi"
+   [(set (match_operand:DI 0 "register_operand" "=d")
+ 	(high:DI (match_operand:DI 1 "local_got_operand" "")))]
+   "TARGET_EXPLICIT_RELOCS"
+   "#"
+   "&& reload_completed"
+   [(set (match_dup 0) (match_dup 2))]
+   { operands[2] = mips_load_got_page (operands[1]); }
+   [(set_attr "got" "load")])
  
  ;; Instructions for adding the low 16 bits of an address to a register.
  ;; Operand 2 is the address: print_operand works out which relocation
  ;; should be applied.
  
  (define_insn "*lowsi"
!   [(set (match_operand:SI 0 "register_operand" "=d")
! 	(lo_sum:SI (match_operand:SI 1 "register_operand" "d")
  		   (match_operand:SI 2 "immediate_operand" "")))]
    "!TARGET_MIPS16"
    "addiu\t%0,%1,%R2"
*************** dsrl\t%3,%3,1\n\
*** 4179,4186 ****
     (set_attr "mode"	"SI")])
  
  (define_insn "*lowdi"
!   [(set (match_operand:DI 0 "register_operand" "=r")
! 	(lo_sum:DI (match_operand:DI 1 "register_operand" "r")
  		   (match_operand:DI 2 "immediate_operand" "")))]
    "!TARGET_MIPS16 && TARGET_64BIT"
    "daddiu\t%0,%1,%R2"
--- 4254,4261 ----
     (set_attr "mode"	"SI")])
  
  (define_insn "*lowdi"
!   [(set (match_operand:DI 0 "register_operand" "=d")
! 	(lo_sum:DI (match_operand:DI 1 "register_operand" "d")
  		   (match_operand:DI 2 "immediate_operand" "")))]
    "!TARGET_MIPS16 && TARGET_64BIT"
    "daddiu\t%0,%1,%R2"
*************** dsrl\t%3,%3,1\n\
*** 5048,5053 ****
--- 5123,5145 ----
     (set_attr "mode"	"SF")
     (set_attr "length"	"4")])
  
+ (define_insn_and_split "loadgp"
+   [(unspec_volatile [(match_operand 0 "" "")] UNSPEC_LOADGP)]
+   "TARGET_ABICALLS && TARGET_NEWABI"
+   "#"
+   ""
+   [(set (match_dup 1) (match_dup 2))
+    (set (match_dup 1) (match_dup 3))
+    (set (match_dup 1) (match_dup 4))]
+ {
+   operands[1] = pic_offset_table_rtx;
+   operands[2] = gen_rtx_HIGH (Pmode, operands[0]);
+   operands[3] = gen_rtx_PLUS (Pmode, operands[1],
+ 			      gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM));
+   operands[4] = gen_rtx_LO_SUM (Pmode, operands[1], operands[0]);
+ }
+   [(set_attr "length" "12")])
+ 
  ;; The use of gp is hidden when not using explicit relocations.
  ;; This blockage instruction prevents the gp load from being
  ;; scheduled after an implicit use of gp.  It also prevents
*************** ld\t%2,%1-%S1(%2)\;daddu\t%2,%2,$31\;%*j
*** 9038,9040 ****
--- 9130,9138 ----
    [(set_attr "type"	"branch")
     (set_attr "mode"	"none")
     (set_attr "length"	"8")])
+ 
+ (define_split
+   [(match_operand 0 "small_data_pattern" "")]
+   "reload_completed"
+   [(match_dup 0)]
+   { operands[0] = mips_rewrite_small_data (operands[0]); })


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]