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