aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/mn10300/mn10300.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/mn10300/mn10300.c')
-rw-r--r--gcc/config/mn10300/mn10300.c675
1 files changed, 669 insertions, 6 deletions
diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c
index df5d35fb770..3e14de37643 100644
--- a/gcc/config/mn10300/mn10300.c
+++ b/gcc/config/mn10300/mn10300.c
@@ -44,6 +44,16 @@ Boston, MA 02111-1307, USA. */
#include "target.h"
#include "target-def.h"
+/* This is used by GOTaddr2picreg to uniquely identify
+ UNSPEC_INT_LABELs. */
+int mn10300_unspec_int_label_counter;
+
+/* This is used in the am33_2.0-linux-gnu port, in which global symbol
+ names are not prefixed by underscores, to tell whether to prefix a
+ label with a plus sign or not, so that the assembler can tell
+ symbol names from register names. */
+int mn10300_protect_label;
+
/* The size of the callee register save area. Right now we save everything
on entry since it costs us nothing in code size. It does cost us from a
speed standpoint, so we want to optimize this sooner or later. */
@@ -75,6 +85,10 @@ static void mn10300_file_start PARAMS ((void));
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info
+
+static void mn10300_encode_section_info (tree, rtx, int);
struct gcc_target targetm = TARGET_INITIALIZER;
static void
@@ -82,7 +96,9 @@ mn10300_file_start ()
{
default_file_start ();
- if (TARGET_AM33)
+ if (TARGET_AM33_2)
+ fprintf (asm_out_file, "\t.am33_2\n");
+ else if (TARGET_AM33)
fprintf (asm_out_file, "\t.am33\n");
}
@@ -100,6 +116,58 @@ print_operand (file, x, code)
{
case 'b':
case 'B':
+ if (cc_status.mdep.fpCC)
+ {
+ switch (code == 'b' ? GET_CODE (x)
+ : reverse_condition_maybe_unordered (GET_CODE (x)))
+ {
+ case NE:
+ fprintf (file, "ne");
+ break;
+ case EQ:
+ fprintf (file, "eq");
+ break;
+ case GE:
+ fprintf (file, "ge");
+ break;
+ case GT:
+ fprintf (file, "gt");
+ break;
+ case LE:
+ fprintf (file, "le");
+ break;
+ case LT:
+ fprintf (file, "lt");
+ break;
+ case ORDERED:
+ fprintf (file, "lge");
+ break;
+ case UNORDERED:
+ fprintf (file, "uo");
+ break;
+ case LTGT:
+ fprintf (file, "lg");
+ break;
+ case UNEQ:
+ fprintf (file, "ue");
+ break;
+ case UNGE:
+ fprintf (file, "uge");
+ break;
+ case UNGT:
+ fprintf (file, "ug");
+ break;
+ case UNLE:
+ fprintf (file, "ule");
+ break;
+ case UNLT:
+ fprintf (file, "ul");
+ break;
+ default:
+ abort ();
+ }
+ break;
+ }
/* These are normal and reversed branches. */
switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
{
@@ -151,6 +219,24 @@ print_operand (file, x, code)
print_operand (file, x, 0);
break;
+ case 'D':
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ fputc ('(', file);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case REG:
+ fprintf (file, "fd%d", REGNO (x) - 18);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
/* These are the least significant word in a 64bit value. */
case 'L':
switch (GET_CODE (x))
@@ -338,6 +424,7 @@ print_operand (file, x, code)
case CONST:
case LABEL_REF:
case CODE_LABEL:
+ case UNSPEC:
print_operand_address (file, x);
break;
default:
@@ -388,6 +475,22 @@ print_operand_address (file, addr)
}
}
+/* Count the number of FP registers that have to be saved. */
+static int
+fp_regs_to_save ()
+{
+ int i, n = 0;
+
+ if (! TARGET_AM33_2)
+ return 0;
+
+ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
+ if (regs_ever_live[i] && ! call_used_regs[i])
+ ++n;
+
+ return n;
+}
+
/* Print a set of registers in the format required by "movm" and "ret".
Register K is saved if bit K of MASK is set. The data and address
registers can be stored individually, but the extended registers cannot.
@@ -446,6 +549,7 @@ can_use_return_insn ()
&& !regs_ever_live[15]
&& !regs_ever_live[16]
&& !regs_ever_live[17]
+ && fp_regs_to_save () == 0
&& !frame_pointer_needed);
}
@@ -460,7 +564,7 @@ mn10300_get_live_callee_saved_regs ()
int i;
mask = 0;
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
if (regs_ever_live[i] && ! call_used_regs[i])
mask |= (1 << i);
if ((mask & 0x3c000) != 0)
@@ -501,7 +605,7 @@ mn10300_gen_multiple_store (mask)
/* Count how many registers need to be saved. */
count = 0;
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
if ((mask & (1 << i)) != 0)
count += 1;
@@ -519,7 +623,7 @@ mn10300_gen_multiple_store (mask)
/* Create each store. */
pari = 1;
- for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ for (i = LAST_EXTENDED_REGNUM; i >= 0; i--)
if ((mask & (1 << i)) != 0)
{
rtx address = gen_rtx_PLUS (SImode,
@@ -549,6 +653,240 @@ expand_prologue ()
/* If we use any of the callee-saved registers, save them now. */
mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
+ if (TARGET_AM33_2 && fp_regs_to_save ())
+ {
+ int num_regs_to_save = fp_regs_to_save (), i;
+ HOST_WIDE_INT xsize;
+ enum { save_sp_merge,
+ save_sp_no_merge,
+ save_sp_partial_merge,
+ save_a0_merge,
+ save_a0_no_merge } strategy;
+ unsigned int strategy_size = (unsigned)-1, this_strategy_size;
+ rtx reg;
+ rtx insn;
+
+ /* We have several different strategies to save FP registers.
+ We can store them using SP offsets, which is beneficial if
+ there are just a few registers to save, or we can use `a0' in
+ post-increment mode (`a0' is the only call-clobbered address
+ register that is never used to pass information to a
+ function). Furthermore, if we don't need a frame pointer, we
+ can merge the two SP adds into a single one, but this isn't
+ always beneficial; sometimes we can just split the two adds
+ so that we don't exceed a 16-bit constant size. The code
+ below will select which strategy to use, so as to generate
+ smallest code. Ties are broken in favor or shorter sequences
+ (in terms of number of instructions). */
+
+#define SIZE_ADD_AX(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
+ : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 2)
+#define SIZE_ADD_SP(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
+ : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 3)
+#define SIZE_FMOV_LIMIT(S,N,L,SIZE1,SIZE2,ELSE) \
+ (((S) >= (L)) ? (SIZE1) * (N) \
+ : ((S) + 4 * (N) >= (L)) ? (((L) - (S)) / 4 * (SIZE2) \
+ + ((S) + 4 * (N) - (L)) / 4 * (SIZE1)) \
+ : (ELSE))
+#define SIZE_FMOV_SP_(S,N) \
+ (SIZE_FMOV_LIMIT ((S), (N), (1 << 24), 7, 6, \
+ SIZE_FMOV_LIMIT ((S), (N), (1 << 8), 6, 4, \
+ (S) ? 4 * (N) : 3 + 4 * ((N) - 1))))
+#define SIZE_FMOV_SP(S,N) (SIZE_FMOV_SP_ ((unsigned HOST_WIDE_INT)(S), (N)))
+
+ /* Consider alternative save_sp_merge only if we don't need the
+ frame pointer and size is nonzero. */
+ if (! frame_pointer_needed && size)
+ {
+ /* Insn: add -(size + 4 * num_regs_to_save), sp. */
+ this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
+ /* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
+ this_strategy_size += SIZE_FMOV_SP (size, num_regs_to_save);
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_sp_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Consider alternative save_sp_no_merge unconditionally. */
+ /* Insn: add -4 * num_regs_to_save, sp. */
+ this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
+ /* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
+ this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
+ if (size)
+ {
+ /* Insn: add -size, sp. */
+ this_strategy_size += SIZE_ADD_SP (-size);
+ }
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_sp_no_merge;
+ strategy_size = this_strategy_size;
+ }
+
+ /* Consider alternative save_sp_partial_merge only if we don't
+ need a frame pointer and size is reasonably large. */
+ if (! frame_pointer_needed && size + 4 * num_regs_to_save > 128)
+ {
+ /* Insn: add -128, sp. */
+ this_strategy_size = SIZE_ADD_SP (-128);
+ /* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
+ this_strategy_size += SIZE_FMOV_SP (128 - 4 * num_regs_to_save,
+ num_regs_to_save);
+ if (size)
+ {
+ /* Insn: add 128-size, sp. */
+ this_strategy_size += SIZE_ADD_SP (128 - size);
+ }
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_sp_partial_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Consider alternative save_a0_merge only if we don't need a
+ frame pointer, size is nonzero and the user hasn't
+ changed the calling conventions of a0. */
+ if (! frame_pointer_needed && size
+ && call_used_regs[FIRST_ADDRESS_REGNUM]
+ && ! fixed_regs[FIRST_ADDRESS_REGNUM])
+ {
+ /* Insn: add -(size + 4 * num_regs_to_save), sp. */
+ this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
+ /* Insn: mov sp, a0. */
+ this_strategy_size++;
+ if (size)
+ {
+ /* Insn: add size, a0. */
+ this_strategy_size += SIZE_ADD_AX (size);
+ }
+ /* Insn: fmov fs#, (a0+), for each fs# to be saved. */
+ this_strategy_size += 3 * num_regs_to_save;
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_a0_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Consider alternative save_a0_no_merge if the user hasn't
+ changed the calling conventions of a0. */
+ if (call_used_regs[FIRST_ADDRESS_REGNUM]
+ && ! fixed_regs[FIRST_ADDRESS_REGNUM])
+ {
+ /* Insn: add -4 * num_regs_to_save, sp. */
+ this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
+ /* Insn: mov sp, a0. */
+ this_strategy_size++;
+ /* Insn: fmov fs#, (a0+), for each fs# to be saved. */
+ this_strategy_size += 3 * num_regs_to_save;
+ if (size)
+ {
+ /* Insn: add -size, sp. */
+ this_strategy_size += SIZE_ADD_SP (-size);
+ }
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_a0_no_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Emit the initial SP add, common to all strategies. */
+ switch (strategy)
+ {
+ case save_sp_no_merge:
+ case save_a0_no_merge:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-4 * num_regs_to_save)));
+ xsize = 0;
+ break;
+
+ case save_sp_partial_merge:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-128)));
+ xsize = 128 - 4 * num_regs_to_save;
+ size -= xsize;
+ break;
+
+ case save_sp_merge:
+ case save_a0_merge:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-(size + 4 * num_regs_to_save))));
+ /* We'll have to adjust FP register saves according to the
+ frame size. */
+ xsize = size;
+ /* Since we've already created the stack frame, don't do it
+ again at the end of the function. */
+ size = 0;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Now prepare register a0, if we have decided to use it. */
+ switch (strategy)
+ {
+ case save_sp_merge:
+ case save_sp_no_merge:
+ case save_sp_partial_merge:
+ reg = 0;
+ break;
+
+ case save_a0_merge:
+ case save_a0_no_merge:
+ reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM);
+ emit_insn (gen_movsi (reg, stack_pointer_rtx));
+ if (xsize)
+ emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize)));
+ reg = gen_rtx_POST_INC (SImode, reg);
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Now actually save the FP registers. */
+ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
+ if (regs_ever_live[i] && ! call_used_regs[i])
+ {
+ rtx addr;
+
+ if (reg)
+ addr = reg;
+ else
+ {
+ /* If we aren't using `a0', use an SP offset. */
+ if (xsize)
+ {
+ addr = gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (xsize));
+ }
+ else
+ addr = stack_pointer_rtx;
+
+ xsize += 4;
+ }
+
+ insn = emit_insn (gen_movsi (gen_rtx_MEM (SImode, addr),
+ gen_rtx_REG (SImode, i)));
+
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ }
+
/* Now put the frame pointer into the frame pointer register. */
if (frame_pointer_needed)
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
@@ -558,6 +896,24 @@ expand_prologue ()
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-size)));
+ if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
+ {
+ rtx insn = get_last_insn ();
+ rtx last = emit_insn (gen_GOTaddr2picreg ());
+
+ /* Mark these insns as possibly dead. Sometimes, flow2 may
+ delete all uses of the PIC register. In this case, let it
+ delete the initialization too. */
+ do
+ {
+ insn = NEXT_INSN (insn);
+
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
+ const0_rtx,
+ REG_NOTES (insn));
+ }
+ while (insn != last);
+ }
}
void
@@ -569,6 +925,193 @@ expand_epilogue ()
size = get_frame_size () + current_function_outgoing_args_size;
size += (current_function_outgoing_args_size ? 4 : 0);
+ if (TARGET_AM33_2 && fp_regs_to_save ())
+ {
+ int num_regs_to_save = fp_regs_to_save (), i;
+ rtx reg = 0;
+
+ /* We have several options to restore FP registers. We could
+ load them from SP offsets, but, if there are enough FP
+ registers to restore, we win if we use a post-increment
+ addressing mode. */
+
+ /* If we have a frame pointer, it's the best option, because we
+ already know it has the value we want. */
+ if (frame_pointer_needed)
+ reg = gen_rtx_REG (SImode, FRAME_POINTER_REGNUM);
+ /* Otherwise, we may use `a1', since it's call-clobbered and
+ it's never used for return values. But only do so if it's
+ smaller than using SP offsets. */
+ else
+ {
+ enum { restore_sp_post_adjust,
+ restore_sp_pre_adjust,
+ restore_sp_partial_adjust,
+ restore_a1 } strategy;
+ unsigned int this_strategy_size, strategy_size = (unsigned)-1;
+
+ /* Consider using sp offsets before adjusting sp. */
+ /* Insn: fmov (##,sp),fs#, for each fs# to be restored. */
+ this_strategy_size = SIZE_FMOV_SP (size, num_regs_to_save);
+ /* If size is too large, we'll have to adjust SP with an
+ add. */
+ if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
+ {
+ /* Insn: add size + 4 * num_regs_to_save, sp. */
+ this_strategy_size += SIZE_ADD_SP (size + 4 * num_regs_to_save);
+ }
+ /* If we don't have to restore any non-FP registers,
+ we'll be able to save one byte by using rets. */
+ if (! REG_SAVE_BYTES)
+ this_strategy_size--;
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_sp_post_adjust;
+ strategy_size = this_strategy_size;
+ }
+
+ /* Consider using sp offsets after adjusting sp. */
+ /* Insn: add size, sp. */
+ this_strategy_size = SIZE_ADD_SP (size);
+ /* Insn: fmov (##,sp),fs#, for each fs# to be restored. */
+ this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
+ /* We're going to use ret to release the FP registers
+ save area, so, no savings. */
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_sp_pre_adjust;
+ strategy_size = this_strategy_size;
+ }
+
+ /* Consider using sp offsets after partially adjusting sp.
+ When size is close to 32Kb, we may be able to adjust SP
+ with an imm16 add instruction while still using fmov
+ (d8,sp). */
+ if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
+ {
+ /* Insn: add size + 4 * num_regs_to_save
+ + REG_SAVE_BYTES - 252,sp. */
+ this_strategy_size = SIZE_ADD_SP (size + 4 * num_regs_to_save
+ + REG_SAVE_BYTES - 252);
+ /* Insn: fmov (##,sp),fs#, fo each fs# to be restored. */
+ this_strategy_size += SIZE_FMOV_SP (252 - REG_SAVE_BYTES
+ - 4 * num_regs_to_save,
+ num_regs_to_save);
+ /* We're going to use ret to release the FP registers
+ save area, so, no savings. */
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_sp_partial_adjust;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Consider using a1 in post-increment mode, as long as the
+ user hasn't changed the calling conventions of a1. */
+ if (call_used_regs[FIRST_ADDRESS_REGNUM+1]
+ && ! fixed_regs[FIRST_ADDRESS_REGNUM+1])
+ {
+ /* Insn: mov sp,a1. */
+ this_strategy_size = 1;
+ if (size)
+ {
+ /* Insn: add size,a1. */
+ this_strategy_size += SIZE_ADD_AX (size);
+ }
+ /* Insn: fmov (a1+),fs#, for each fs# to be restored. */
+ this_strategy_size += 3 * num_regs_to_save;
+ /* If size is large enough, we may be able to save a
+ couple of bytes. */
+ if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
+ {
+ /* Insn: mov a1,sp. */
+ this_strategy_size += 2;
+ }
+ /* If we don't have to restore any non-FP registers,
+ we'll be able to save one byte by using rets. */
+ if (! REG_SAVE_BYTES)
+ this_strategy_size--;
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_a1;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ switch (strategy)
+ {
+ case restore_sp_post_adjust:
+ break;
+
+ case restore_sp_pre_adjust:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (size)));
+ size = 0;
+ break;
+
+ case restore_sp_partial_adjust:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (size + 4 * num_regs_to_save
+ + REG_SAVE_BYTES - 252)));
+ size = 252 - REG_SAVE_BYTES - 4 * num_regs_to_save;
+ break;
+
+ case restore_a1:
+ reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM + 1);
+ emit_insn (gen_movsi (reg, stack_pointer_rtx));
+ if (size)
+ emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ /* Adjust the selected register, if any, for post-increment. */
+ if (reg)
+ reg = gen_rtx_POST_INC (SImode, reg);
+
+ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
+ if (regs_ever_live[i] && ! call_used_regs[i])
+ {
+ rtx addr;
+
+ if (reg)
+ addr = reg;
+ else if (size)
+ {
+ /* If we aren't using a post-increment register, use an
+ SP offset. */
+ addr = gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (size));
+ }
+ else
+ addr = stack_pointer_rtx;
+
+ size += 4;
+
+ emit_insn (gen_movsi (gen_rtx_REG (SImode, i),
+ gen_rtx_MEM (SImode, addr)));
+ }
+
+ /* If we were using the restore_a1 strategy and the number of
+ bytes to be released won't fit in the `ret' byte, copy `a1'
+ to `sp', to avoid having to use `add' to adjust it. */
+ if (! frame_pointer_needed && reg && size + REG_SAVE_BYTES > 255)
+ {
+ emit_move_insn (stack_pointer_rtx, XEXP (reg, 0));
+ size = 0;
+ }
+ }
+
/* Maybe cut back the stack, except for the register save area.
If the frame pointer exists, then use the frame pointer to
@@ -649,6 +1192,9 @@ notice_update_cc (body, insn)
/* The insn is a compare instruction. */
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (body);
+ if (GET_CODE (cc_status.value1) == COMPARE
+ && GET_MODE (XEXP (cc_status.value1, 0)) == SFmode)
+ cc_status.mdep.fpCC = 1;
break;
case CC_INVERT:
@@ -714,7 +1260,7 @@ store_multiple_operation (op, mode)
LAST keeps track of the smallest-numbered register stored so far.
MASK is the set of stored registers. */
- last = FIRST_PSEUDO_REGISTER;
+ last = LAST_EXTENDED_REGNUM + 1;
mask = 0;
for (i = 1; i < count; i++)
{
@@ -756,6 +1302,9 @@ call_address_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
+ if (flag_pic)
+ return (EXTRA_CONSTRAINT (op, 'S') || GET_CODE (op) == REG);
+
return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG);
}
@@ -810,6 +1359,14 @@ secondary_reload_class (class, mode, in)
return DATA_REGS;
}
+ if (TARGET_AM33_2 && class == FP_REGS
+ && GET_CODE (in) == MEM && ! OK_FOR_Q (in))
+ {
+ if (TARGET_AM33)
+ return DATA_OR_EXTENDED_REGS;
+ return DATA_REGS;
+ }
+
/* Otherwise assume no secondary reloads are needed. */
return NO_REGS;
}
@@ -826,8 +1383,10 @@ initial_offset (from, to)
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
+ || fp_regs_to_save ()
|| frame_pointer_needed)
- return REG_SAVE_BYTES;
+ return REG_SAVE_BYTES
+ + 4 * fp_regs_to_save ();
else
return 0;
}
@@ -841,8 +1400,10 @@ initial_offset (from, to)
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
+ || fp_regs_to_save ()
|| frame_pointer_needed)
return (get_frame_size () + REG_SAVE_BYTES
+ + 4 * fp_regs_to_save ()
+ (current_function_outgoing_args_size
? current_function_outgoing_args_size + 4 : 0));
else
@@ -1155,6 +1716,15 @@ const_8bit_operand (op, mode)
&& INTVAL (op) < 256);
}
+/* Return true if the operand is the 1.0f constant. */
+int
+const_1f_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return (op == CONST1_RTX (SFmode));
+}
+
/* Similarly, but when using a zero_extract pattern for a btst where
the source operand might end up in memory. */
int
@@ -1222,6 +1792,9 @@ legitimize_address (x, oldx, mode)
rtx oldx ATTRIBUTE_UNUSED;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
+ if (flag_pic && ! legitimate_pic_operand_p (x))
+ x = legitimize_pic_address (oldx, NULL_RTX);
+
/* Uh-oh. We might have an address for x[n-100000]. This needs
special handling to avoid creating an indexed memory address
with x-100000 as the base. */
@@ -1252,6 +1825,75 @@ legitimize_address (x, oldx, mode)
return x;
}
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+ @GOTOFF in `reg'. */
+rtx
+legitimize_pic_address (orig, reg)
+ rtx orig;
+ rtx reg;
+{
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF
+ && (CONSTANT_POOL_ADDRESS_P (orig)
+ || ! MN10300_GLOBAL_P (orig))))
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTOFF2reg (reg, orig));
+ return reg;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, orig));
+ return reg;
+ }
+ return orig;
+}
+
+/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec; nonzero otherwise. */
+int
+legitimate_pic_operand_p (x)
+ rtx x;
+{
+ register const char *fmt;
+ register int i;
+
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ return 0;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_PIC
+ || XINT (x, 1) == UNSPEC_GOT
+ || XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_PLT))
+ return 1;
+
+ if (GET_CODE (x) == QUEUED)
+ return legitimate_pic_operand_p (QUEUED_VAR (x));
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (! legitimate_pic_operand_p (XVECEXP (x, i, j)))
+ return 0;
+ }
+ else if (fmt[i] == 'e' && ! legitimate_pic_operand_p (XEXP (x, i)))
+ return 0;
+ }
+
+ return 1;
+}
+
static int
mn10300_address_cost_1 (x, unsig)
rtx x;
@@ -1271,6 +1913,7 @@ mn10300_address_cost_1 (x, unsig)
case DATA_REGS:
case EXTENDED_REGS:
+ case FP_REGS:
return 3;
case NO_REGS:
@@ -1438,3 +2081,23 @@ mn10300_wide_const_load_uses_clr (operands)
return val[0] == 0 || val[1] == 0;
}
+/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
+ may access it using GOTOFF instead of GOT. */
+
+static void
+mn10300_encode_section_info (decl, rtl, first)
+ tree decl;
+ rtx rtl;
+ int first;
+{
+ rtx symbol;
+
+ if (GET_CODE (rtl) != MEM)
+ return;
+ symbol = XEXP (rtl, 0);
+ if (GET_CODE (symbol) != SYMBOL_REF)
+ return;
+
+ if (flag_pic)
+ SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
+}