aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/cris
diff options
context:
space:
mode:
authorHans-Peter Nilsson <hp@axis.com>2005-07-09 01:09:48 +0000
committerHans-Peter Nilsson <hp@gcc.gnu.org>2005-07-09 01:09:48 +0000
commitc00fc5cf8b1979cd4a9200ec05593f2dba9f1eb0 (patch)
treed8c11bf50d6e74488663bde1ef740cd1675b38b9 /gcc/config/cris
parent68ece23dbd7a5f58f5066b7ffcac73ceca1dbeae (diff)
downloadgcc-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.h6
-rw-r--r--gcc/config/cris/cris.c471
-rw-r--r--gcc/config/cris/cris.h33
-rw-r--r--gcc/config/cris/cris.md498
-rw-r--r--gcc/config/cris/predicates.md17
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