From ea462dd06fa40bc09069b7d9f72951708a3700a2 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Thu, 16 Mar 2006 20:24:42 +0000 Subject: predicates.md (splittable_const_int_operand): New, split from move_operand. * config/mips/predicates.md (splittable_const_int_operand): New, split from move_operand. (splittable_symbolic_operand): New. (move_operand): Add commentary. Use splittable_const_int_operand. Inline mips_atomic_symbolic_constant_p. * config/mips/mips.md: Add combine splitters for handling moves of splittable_const_int_operands and splittable_symbolic_operands. * config/mips/mips-protos.h (mips_atomic_symbolic_constant_p): Delete. (mips_split_symbol): Declare. (mips_move_integer): Declare. * config/mips/mips.c (mips_split_p): Make global. (TARGET_MIN_ANCHOR_OFFSET): Override default. (TARGET_MAX_ANCHOR_OFFSET): Likewise. (TARGET_USE_BLOCKS_FOR_CONSTANT_P): Likewise. (TARGET_USE_ANCHORS_FOR_SYMBOL_P): Likewise. (mips_offset_within_object_p): Handle block symbols. (mips_atomic_symbolic_constant_p): Delete. (mips_cannot_force_const_mem): Return false for constants that mips_legitimize_move can handle. (mips_use_blocks_for_constant_p): New function. (mips_split_symbol): Make global. (mips_move_integer): Likewise. Add a temporary register argument. (mips_legitimize_const_move): Use splittable_const_int_operand and splittable_symbolic_operand. (mips_use_anchors_for_symbol_p): New function. * config/mips/mips.h: Protect externs with !USED_FOR_TARGET. (mips_split_p): Declare. From-SVN: r112150 --- gcc/ChangeLog | 30 ++++++++++ gcc/config/mips/mips-protos.h | 3 +- gcc/config/mips/mips.c | 127 ++++++++++++++++++++++++++++++------------ gcc/config/mips/mips.h | 3 + gcc/config/mips/mips.md | 22 ++++++++ gcc/config/mips/predicates.md | 83 ++++++++++++++++++++++----- 6 files changed, 216 insertions(+), 52 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6e388d8..6175a87 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,35 @@ 2006-03-16 Richard Sandiford + * config/mips/predicates.md (splittable_const_int_operand): New, + split from move_operand. + (splittable_symbolic_operand): New. + (move_operand): Add commentary. Use splittable_const_int_operand. + Inline mips_atomic_symbolic_constant_p. + * config/mips/mips.md: Add combine splitters for handling moves + of splittable_const_int_operands and splittable_symbolic_operands. + * config/mips/mips-protos.h (mips_atomic_symbolic_constant_p): Delete. + (mips_split_symbol): Declare. + (mips_move_integer): Declare. + * config/mips/mips.c (mips_split_p): Make global. + (TARGET_MIN_ANCHOR_OFFSET): Override default. + (TARGET_MAX_ANCHOR_OFFSET): Likewise. + (TARGET_USE_BLOCKS_FOR_CONSTANT_P): Likewise. + (TARGET_USE_ANCHORS_FOR_SYMBOL_P): Likewise. + (mips_offset_within_object_p): Handle block symbols. + (mips_atomic_symbolic_constant_p): Delete. + (mips_cannot_force_const_mem): Return false for constants that + mips_legitimize_move can handle. + (mips_use_blocks_for_constant_p): New function. + (mips_split_symbol): Make global. + (mips_move_integer): Likewise. Add a temporary register argument. + (mips_legitimize_const_move): Use splittable_const_int_operand and + splittable_symbolic_operand. + (mips_use_anchors_for_symbol_p): New function. + * config/mips/mips.h: Protect externs with !USED_FOR_TARGET. + (mips_split_p): Declare. + +2006-03-16 Richard Sandiford + * config.gcc (mips64*-*-linux*): Keep existing tm_defines. (mips*-*-linux*): Likewise. diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h index 1dc31f5..b737375 100644 --- a/gcc/config/mips/mips-protos.h +++ b/gcc/config/mips/mips-protos.h @@ -106,7 +106,6 @@ enum mips_symbol_type { #define NUM_SYMBOL_TYPES (SYMBOL_64_LOW + 1) extern bool mips_symbolic_constant_p (rtx, enum mips_symbol_type *); -extern bool mips_atomic_symbolic_constant_p (rtx); extern int mips_regno_mode_ok_for_base_p (int, enum machine_mode, int); extern bool mips_stack_address_p (rtx, enum machine_mode); extern int mips_address_insns (rtx, enum machine_mode); @@ -116,8 +115,10 @@ extern int mips_idiv_insns (void); extern int fp_register_operand (rtx, enum machine_mode); extern int lo_operand (rtx, enum machine_mode); extern bool mips_legitimate_address_p (enum machine_mode, rtx, int); +extern rtx mips_split_symbol (rtx, rtx); extern rtx mips_unspec_address (rtx, enum mips_symbol_type); extern bool mips_legitimize_address (rtx *, enum machine_mode); +extern void mips_move_integer (rtx, rtx, unsigned HOST_WIDE_INT); extern bool mips_legitimize_move (enum machine_mode, rtx, rtx); extern int m16_uimm3_b (rtx, enum machine_mode); diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index 12394ca..f907c01 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -274,10 +274,10 @@ 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 bool mips_cannot_force_const_mem (rtx); +static bool mips_use_blocks_for_constant_p (enum machine_mode, rtx); 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_offset_high (rtx, rtx, rtx, enum mips_symbol_type); static rtx mips_add_offset (rtx, rtx, HOST_WIDE_INT); static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT); @@ -285,7 +285,6 @@ static unsigned int mips_build_lower (struct mips_integer_op *, unsigned HOST_WIDE_INT); static unsigned int mips_build_integer (struct mips_integer_op *, unsigned HOST_WIDE_INT); -static void mips_move_integer (rtx, unsigned HOST_WIDE_INT); static void mips_legitimize_const_move (enum machine_mode, rtx, rtx); static int m16_check_op (rtx, int, int, int); static bool mips_rtx_costs (rtx, int, int, int *); @@ -333,6 +332,7 @@ static section *mips_select_rtx_section (enum machine_mode, rtx, unsigned HOST_WIDE_INT); static section *mips_function_rodata_section (tree); static bool mips_in_small_data_p (tree); +static bool mips_use_anchors_for_symbol_p (rtx); static int mips_fpr_return_fields (tree, tree *); static bool mips_return_in_msb (tree); static rtx mips_return_fpr_pair (enum machine_mode mode, @@ -628,7 +628,7 @@ 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]; +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 @@ -1162,6 +1162,15 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] = #undef TARGET_EXTRA_LIVE_ON_ENTRY #define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry +#undef TARGET_MIN_ANCHOR_OFFSET +#define TARGET_MIN_ANCHOR_OFFSET -32768 +#undef TARGET_MAX_ANCHOR_OFFSET +#define TARGET_MAX_ANCHOR_OFFSET 32767 +#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P +#define TARGET_USE_BLOCKS_FOR_CONSTANT_P mips_use_blocks_for_constant_p +#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P +#define TARGET_USE_ANCHORS_FOR_SYMBOL_P mips_use_anchors_for_symbol_p + struct gcc_target targetm = TARGET_INITIALIZER; /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */ @@ -1256,7 +1265,7 @@ mips_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset) /* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points - to the same object as SYMBOL. */ + to the same object as SYMBOL, or to the same object_block. */ static bool mips_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset) @@ -1274,6 +1283,13 @@ mips_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset) && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol)))) return true; + if (SYMBOL_REF_HAS_BLOCK_INFO_P (symbol) + && SYMBOL_REF_BLOCK (symbol) + && SYMBOL_REF_BLOCK_OFFSET (symbol) >= 0 + && ((unsigned HOST_WIDE_INT) offset + SYMBOL_REF_BLOCK_OFFSET (symbol) + < (unsigned HOST_WIDE_INT) SYMBOL_REF_BLOCK (symbol)->size)) + return true; + return false; } @@ -1357,17 +1373,6 @@ mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type) } -/* Return true if X is a symbolic constant whose value is not split - into separate relocations. */ - -bool -mips_atomic_symbolic_constant_p (rtx x) -{ - enum mips_symbol_type type; - return mips_symbolic_constant_p (x, &type) && !mips_split_p[type]; -} - - /* This function is used to implement REG_MODE_OK_FOR_BASE_P. */ int @@ -1542,10 +1547,42 @@ mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED) static bool mips_cannot_force_const_mem (rtx x) { - if (! TARGET_HAVE_TLS) - return false; + rtx base; + HOST_WIDE_INT offset; + + if (!TARGET_MIPS16) + { + /* As an optimization, reject constants that mips_legitimize_move + can expand inline. - return for_each_rtx (&x, &mips_tls_symbol_ref_1, 0); + Suppose we have a multi-instruction sequence that loads constant C + into register R. If R does not get allocated a hard register, and + R is used in an operand that allows both registers and memory + references, reload will consider forcing C into memory and using + one of the instruction's memory alternatives. Returning false + here will force it to use an input reload instead. */ + if (GET_CODE (x) == CONST_INT) + return true; + + mips_split_const (x, &base, &offset); + if (symbolic_operand (base, VOIDmode) && SMALL_OPERAND (offset)) + return true; + } + + if (TARGET_HAVE_TLS && for_each_rtx (&x, &mips_tls_symbol_ref_1, 0)) + return true; + + return false; +} + +/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. MIPS16 uses per-function + constant pools, but normal-mode code doesn't need to. */ + +static bool +mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, + rtx x ATTRIBUTE_UNUSED) +{ + return !TARGET_MIPS16; } /* Return the number of instructions needed to load a symbol of the @@ -1845,7 +1882,7 @@ mips_force_temporary (rtx dest, rtx value) /* 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 +rtx mips_split_symbol (rtx temp, rtx addr) { rtx high; @@ -2175,10 +2212,10 @@ mips_build_integer (struct mips_integer_op *codes, } -/* Move VALUE into register DEST. */ +/* Load VALUE into DEST, using TEMP as a temporary register if need be. */ -static void -mips_move_integer (rtx dest, unsigned HOST_WIDE_INT value) +void +mips_move_integer (rtx dest, rtx temp, unsigned HOST_WIDE_INT value) { struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS]; enum machine_mode mode; @@ -2194,7 +2231,10 @@ mips_move_integer (rtx dest, unsigned HOST_WIDE_INT value) for (i = 1; i < cost; i++) { if (no_new_pseudos) - emit_move_insn (dest, x), x = dest; + { + emit_insn (gen_rtx_SET (VOIDmode, temp, x)); + x = temp; + } else x = force_reg (mode, x); x = gen_rtx_fmt_ee (codes[i].code, mode, x, GEN_INT (codes[i].value)); @@ -2213,30 +2253,24 @@ 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) + /* Split moves of big integers into smaller pieces. */ + if (splittable_const_int_operand (src, mode)) { - mips_move_integer (dest, INTVAL (src)); + mips_move_integer (dest, dest, INTVAL (src)); return; } - if (mips_tls_operand_p (src)) + /* Split moves of symbolic constants into high/low pairs. */ + if (splittable_symbolic_operand (src, mode)) { - emit_move_insn (dest, mips_legitimize_tls_address (src)); + emit_insn (gen_rtx_SET (VOIDmode, dest, mips_split_symbol (dest, 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]) + if (mips_tls_operand_p (src)) { - emit_move_insn (dest, mips_split_symbol (dest, src)); + emit_move_insn (dest, mips_legitimize_tls_address (src)); return; } @@ -7205,6 +7239,25 @@ mips_in_small_data_p (tree decl) size = int_size_in_bytes (TREE_TYPE (decl)); return (size > 0 && size <= mips_section_threshold); } + +/* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P. We don't want to use + anchors for small data: the GP register acts as an anchor in that + case. We also don't want to use them for PC-relative accesses, + where the PC acts as an anchor. */ + +static bool +mips_use_anchors_for_symbol_p (rtx symbol) +{ + switch (mips_classify_symbol (symbol)) + { + case SYMBOL_CONSTANT_POOL: + case SYMBOL_SMALL_DATA: + return false; + + default: + return true; + } +} /* See whether VALTYPE is a record whose fields should be returned in floating-point registers. If so, return the number of fields and diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h index b0ae120..51d383c 100644 --- a/gcc/config/mips/mips.h +++ b/gcc/config/mips/mips.h @@ -106,6 +106,7 @@ struct mips_cpu_info { int isa; }; +#ifndef USED_FOR_TARGET extern char mips_print_operand_punct[256]; /* print_operand punctuation chars */ extern const char *current_function_file; /* filename current function is in */ extern int num_source_filenames; /* current .file # */ @@ -117,6 +118,7 @@ extern int set_noat; /* # of nested .set noat's */ extern int set_volatile; /* # of nested .set volatile's */ extern int mips_branch_likely; /* emit 'l' after br (branch likely) */ extern int mips_dbx_regno[]; /* Map register # to debug register # */ +extern bool mips_split_p[]; extern GTY(()) rtx cmp_operands[2]; extern enum processor_type mips_arch; /* which cpu to codegen for */ extern enum processor_type mips_tune; /* which cpu to schedule for */ @@ -127,6 +129,7 @@ extern const struct mips_cpu_info mips_cpu_info_table[]; extern const struct mips_cpu_info *mips_arch_info; extern const struct mips_cpu_info *mips_tune_info; extern const struct mips_rtx_cost_data *mips_cost; +#endif /* Macros to silence warnings about numbers being signed in traditional C and unsigned in ISO C when compiled on 32-bit hosts. */ diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index cc327f9..ce2cce6 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -3194,6 +3194,28 @@ (set_attr "mode" "") (set_attr "length" "8")]) +;; Allow combine to split complex const_int load sequences, using operand 2 +;; to store the intermediate results. See move_operand for details. +(define_split + [(set (match_operand:GPR 0 "register_operand") + (match_operand:GPR 1 "splittable_const_int_operand")) + (clobber (match_operand:GPR 2 "register_operand"))] + "" + [(const_int 0)] +{ + mips_move_integer (operands[0], operands[2], INTVAL (operands[1])); + DONE; +}) + +;; Likewise, for symbolic operands. +(define_split + [(set (match_operand:P 0 "register_operand") + (match_operand:P 1 "splittable_symbolic_operand")) + (clobber (match_operand:P 2 "register_operand"))] + "" + [(set (match_dup 0) (match_dup 1))] + { operands[1] = mips_split_symbol (operands[2], operands[1]); }) + ;; 64-bit integer moves ;; Unlike most other insns, the move insns can't be split with diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md index d93621d..23e85d8 100644 --- a/gcc/config/mips/predicates.md +++ b/gcc/config/mips/predicates.md @@ -125,30 +125,85 @@ (ior (match_operand 0 "const_call_insn_operand") (match_operand 0 "register_operand"))) +;; A legitimate CONST_INT operand that takes more than one instruction +;; to load. +(define_predicate "splittable_const_int_operand" + (match_code "const_int") +{ + /* When generating mips16 code, LEGITIMATE_CONSTANT_P rejects + CONST_INTs that can't be loaded using simple insns. */ + if (TARGET_MIPS16) + return false; + + /* Don't handle multi-word moves this way; we don't want to introduce + the individual word-mode moves until after reload. */ + if (GET_MODE_SIZE (mode) > UNITS_PER_WORD) + return false; + + /* Otherwise check whether the constant can be loaded in a single + instruction. */ + return !LUI_INT (op) && !SMALL_INT (op) && !SMALL_INT_UNSIGNED (op); +}) + +;; A legitimate symbolic operand that takes more than one instruction +;; to load. +(define_predicate "splittable_symbolic_operand" + (match_code "const,symbol_ref,label_ref") +{ + enum mips_symbol_type symbol_type; + return (mips_symbolic_constant_p (op, &symbol_type) + && mips_split_p[symbol_type]); +}) + (define_predicate "move_operand" (match_operand 0 "general_operand") { + enum mips_symbol_type symbol_type; + + /* The thinking here is as follows: + + (1) The move expanders should split complex load sequences into + individual instructions. Those individual instructions can + then be optimized by all rtl passes. + + (2) The target of pre-reload load sequences should not be used + to store temporary results. If the target register is only + assigned one value, reload can rematerialize that value + on demand, rather than spill it to the stack. + + (3) If we allowed pre-reload passes like combine and cse to recreate + complex load sequences, we would want to be able to split the + sequences before reload as well, so that the pre-reload scheduler + can see the individual instructions. This falls foul of (2); + the splitter would be forced to reuse the target register for + intermediate results. + + (4) We want to define complex load splitters for combine. These + splitters can request a temporary scratch register, which avoids + the problem in (2). They allow things like: + + (set (reg T1) (high SYM)) + (set (reg T2) (low (reg T1) SYM)) + (set (reg X) (plus (reg T2) (const_int OFFSET))) + + to be combined into: + + (set (reg T3) (high SYM+OFFSET)) + (set (reg X) (lo_sum (reg T3) SYM+OFFSET)) + + if T2 is only used this once. */ 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; - - /* When generating 32-bit code, allow DImode move_operands to - match arbitrary constants. We split them after reload. */ - if (!TARGET_64BIT && mode == DImode) - 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); + return !splittable_const_int_operand (op, mode); case CONST: case SYMBOL_REF: case LABEL_REF: - return CONST_GP_P (op) || mips_atomic_symbolic_constant_p (op); + if (CONST_GP_P (op)) + return true; + return (mips_symbolic_constant_p (op, &symbol_type) + && !mips_split_p[symbol_type]); default: return true; -- cgit v1.1