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
This commit is contained in:
parent
68ece23dbd
commit
c00fc5cf8b
6 changed files with 695 additions and 455 deletions
|
@ -1,3 +1,68 @@
|
|||
2005-07-08 Hans-Peter Nilsson <hp@axis.com>
|
||||
|
||||
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.
|
||||
|
||||
2005-07-08 Andrew Pinski <pinskia@physics.uc.edu>
|
||||
|
||||
* config/darwin.h (TARGET_C99_FUNCTIONS): Define to 1.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/* Return TRUE iff X is a CONST valid for e.g. indexing. */
|
||||
|
||||
bool
|
||||
cris_valid_pic_const (rtx x)
|
||||
{
|
||||
gcc_assert (flag_pic);
|
||||
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
case CONST_INT:
|
||||
case CONST_DOUBLE:
|
||||
return true;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Helper function to find the right PIC-type symbol to generate,
|
||||
given the original (non-PIC) representation. */
|
||||
|
||||
enum cris_pic_symbol_type
|
||||
cris_pic_symbol_type_of (rtx x)
|
||||
{
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
case SYMBOL_REF:
|
||||
return SYMBOL_REF_LOCAL_P (x)
|
||||
? cris_gotrel_symbol : cris_got_symbol;
|
||||
|
||||
case LABEL_REF:
|
||||
return cris_gotrel_symbol;
|
||||
|
||||
case CONST:
|
||||
return cris_pic_symbol_type_of (XEXP (x, 0));
|
||||
|
||||
case PLUS:
|
||||
case MINUS:
|
||||
{
|
||||
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 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);
|
||||
}
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* The LEGITIMATE_PIC_OPERAND_P worker. */
|
||||
|
||||
int
|
||||
cris_legitimate_pic_operand (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. */
|
||||
|
||||
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;
|
||||
|
||||
default:
|
||||
fatal_insn ("unrecognized supposed constant", x);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
int
|
||||
cris_gotless_symbol (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);
|
||||
|
||||
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;
|
||||
|
||||
case CONST:
|
||||
return cris_gotless_symbol (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)));
|
||||
}
|
||||
|
||||
case CONST_INT:
|
||||
case CONST_DOUBLE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
fatal_insn ("unrecognized supposed constant", x);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return nonzero if there's a SYMBOL_REF or LABEL_REF hiding inside this
|
||||
CONSTANT_P, and the symbol needs a GOT entry. */
|
||||
|
||||
int
|
||||
cris_got_symbol (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;
|
||||
const char *origstr = XSTR (x, 0);
|
||||
const char *str;
|
||||
str = (* targetm.strip_name_encoding) (origstr);
|
||||
assemble_name (file, 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");
|
||||
/* 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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
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]);
|
||||
|
||||
/* 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]);
|
||||
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);
|
||||
|
||||
CRIS_ASSERT (sym != NULL_RTX && offs != 0);
|
||||
/* 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]);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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,62 +2845,40 @@
|
|||
(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
|
||||
;; order of preference.
|
||||
|
||||
(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,27 +2898,32 @@
|
|||
;; than requiring getting rPIC + sym:PLT into a register.
|
||||
|
||||
(define_insn "*expanded_call_value"
|
||||
[(set (match_operand 0 "nonimmediate_operand" "=g,g,g,g")
|
||||
(call (mem:QI (match_operand:SI
|
||||
1 "cris_general_operand_or_plt_symbol" "r,Q>,g,S"))
|
||||
(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"
|
||||
[(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"
|
||||
[(set_attr "cc" "clobber")])
|
||||
|
||||
;; See similar call special-case.
|
||||
(define_insn "*expanded_call_value_side"
|
||||
[(set (match_operand 0 "nonimmediate_operand" "=g,g,g")
|
||||
(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
|
||||
;; delayed-branches are taken care of by other means.
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue