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:
Hans-Peter Nilsson 2005-07-09 01:09:48 +00:00 committed by Hans-Peter Nilsson
parent 68ece23dbd
commit c00fc5cf8b
6 changed files with 695 additions and 455 deletions

View file

@ -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.

View file

@ -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);

View file

@ -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;

View file

@ -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)

View file

@ -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

View file

@ -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