aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/sh/sh.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/sh/sh.c')
-rw-r--r--gcc/config/sh/sh.c243
1 files changed, 201 insertions, 42 deletions
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index 4385a0fa6e0..d466cd7eafc 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -27,7 +27,6 @@ Boston, MA 02111-1307, USA. */
#include "rtl.h"
#include "tree.h"
#include "flags.h"
-#include "insn-flags.h"
#include "expr.h"
#include "function.h"
#include "regs.h"
@@ -44,6 +43,9 @@ int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
+/* Set to 1 by expand_prologue() when the function is an interrupt handler. */
+int current_function_interrupt;
+
/* ??? The pragma interrupt support will not work for SH3. */
/* This is set by #pragma interrupt and #pragma trapa, and causes gcc to
output code for the next function appropriate for an interrupt handler. */
@@ -148,6 +150,7 @@ static void push_regs PARAMS ((int, int));
static int calc_live_regs PARAMS ((int *, int *));
static void mark_use PARAMS ((rtx, rtx *));
static HOST_WIDE_INT rounded_frame_size PARAMS ((int));
+static rtx mark_constant_pool_use PARAMS ((rtx));
/* Print the operand address in x to the stream. */
@@ -171,7 +174,7 @@ print_operand_address (stream, x)
switch (GET_CODE (index))
{
case CONST_INT:
- fprintf (stream, "@(%d,%s)", INTVAL (index),
+ fprintf (stream, "@(%d,%s)", (int) INTVAL (index),
reg_names[true_regnum (base)]);
break;
@@ -202,6 +205,7 @@ print_operand_address (stream, x)
break;
default:
+ x = mark_constant_pool_use (x);
output_addr_const (stream, x);
break;
}
@@ -262,6 +266,7 @@ print_operand (stream, x, code)
fprintf (stream, "\n\tnop");
break;
case 'O':
+ x = mark_constant_pool_use (x);
output_addr_const (stream, x);
break;
case 'R':
@@ -800,8 +805,12 @@ output_branch (logic, insn, operands)
rtx insn;
rtx *operands;
{
- switch (get_attr_length (insn))
+ int len = get_attr_length (insn);
+
+ switch (len)
{
+ case 16:
+ case 12:
case 6:
/* This can happen if filling the delay slot has caused a forward
branch to exceed its range (we could reverse it, but only
@@ -824,16 +833,24 @@ output_branch (logic, insn, operands)
if (final_sequence
&& ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))
{
- asm_fprintf (asm_out_file, "\tb%s%ss\t%LLF%d\n", logic ? "f" : "t",
+ asm_fprintf (asm_out_file, "\tb%s%ss\t%LLF%d\n",
+ logic ? "f" : "t",
ASSEMBLER_DIALECT ? "/" : ".", label);
print_slot (final_sequence);
}
else
- asm_fprintf (asm_out_file, "\tb%s\t%LLF%d\n", logic ? "f" : "t", label);
+ asm_fprintf (asm_out_file, "\tb%s\t%LLF%d\n", logic ? "f" : "t",
+ label);
- output_asm_insn ("bra\t%l0", &op0);
- fprintf (asm_out_file, "\tnop\n");
- ASM_OUTPUT_INTERNAL_LABEL(asm_out_file, "LF", label);
+ if (len == 6)
+ {
+ output_asm_insn ("bra\t%l0", &op0);
+ fprintf (asm_out_file, "\tnop\n");
+ }
+ else
+ output_far_jump (insn, op0);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LF", label);
return "";
}
@@ -905,8 +922,8 @@ output_file_start (file)
{
output_file_directive (file, main_input_filename);
- /* Switch to the data section so that the coffsem symbol and the
- gcc2_compiled. symbol aren't in the text section. */
+ /* Switch to the data section so that the coffsem symbol
+ isn't in the text section. */
data_section ();
if (TARGET_LITTLE_ENDIAN)
@@ -988,6 +1005,16 @@ shiftcosts (x)
{
int value;
+ if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+ {
+ if (GET_MODE (x) == DImode
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) == 1)
+ return 2;
+
+ /* Everything else is invalid, because there is no pattern for it. */
+ return 10000;
+ }
/* If shift by a non constant, then this will be expensive. */
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
return SH_DYNAMIC_SHIFT_COST;
@@ -1439,7 +1466,7 @@ shl_and_kind (left_rtx, mask_rtx, attrp)
}
}
/* Try to use a scratch register to hold the AND operand. */
- can_ext = ((mask << left) & 0xe0000000) == 0;
+ can_ext = ((mask << left) & ((unsigned HOST_WIDE_INT)3 << 30)) == 0;
for (i = 0; i <= 2; i++)
{
if (i > right)
@@ -1921,6 +1948,7 @@ typedef struct
{
rtx value; /* Value in table. */
rtx label; /* Label of value. */
+ rtx wend; /* End of window. */
enum machine_mode mode; /* Mode of value. */
} pool_node;
@@ -1931,6 +1959,8 @@ typedef struct
#define MAX_POOL_SIZE (1020/4)
static pool_node pool_vector[MAX_POOL_SIZE];
static int pool_size;
+static rtx pool_window_label;
+static int pool_window_last;
/* ??? If we need a constant in HImode which is the truncated value of a
constant we need in SImode, we could combine the two entries thus saving
@@ -1951,7 +1981,7 @@ add_constant (x, mode, last_value)
rtx last_value;
{
int i;
- rtx lab;
+ rtx lab, new, ref, newref;
/* First see if we've already got it. */
for (i = 0; i < pool_size; i++)
@@ -1966,15 +1996,25 @@ add_constant (x, mode, last_value)
}
if (rtx_equal_p (x, pool_vector[i].value))
{
- lab = 0;
+ lab = new = 0;
if (! last_value
|| ! i
|| ! rtx_equal_p (last_value, pool_vector[i-1].value))
{
- lab = pool_vector[i].label;
- if (! lab)
- pool_vector[i].label = lab = gen_label_rtx ();
+ new = gen_label_rtx ();
+ LABEL_REFS (new) = pool_vector[i].label;
+ pool_vector[i].label = lab = new;
+ }
+ if (lab && pool_window_label)
+ {
+ newref = gen_rtx_LABEL_REF (VOIDmode, pool_window_label);
+ ref = pool_vector[pool_window_last].wend;
+ LABEL_NEXTREF (newref) = ref;
+ pool_vector[pool_window_last].wend = newref;
}
+ if (new)
+ pool_window_label = new;
+ pool_window_last = i;
return lab;
}
}
@@ -1988,6 +2028,17 @@ add_constant (x, mode, last_value)
lab = gen_label_rtx ();
pool_vector[pool_size].mode = mode;
pool_vector[pool_size].label = lab;
+ pool_vector[pool_size].wend = NULL_RTX;
+ if (lab && pool_window_label)
+ {
+ newref = gen_rtx_LABEL_REF (VOIDmode, pool_window_label);
+ ref = pool_vector[pool_window_last].wend;
+ LABEL_NEXTREF (newref) = ref;
+ pool_vector[pool_window_last].wend = newref;
+ }
+ if (lab)
+ pool_window_label = lab;
+ pool_window_last = pool_size;
pool_size++;
return lab;
}
@@ -2000,6 +2051,7 @@ dump_table (scan)
{
int i;
int need_align = 1;
+ rtx lab, ref;
/* Do two passes, first time dump out the HI sized constants. */
@@ -2014,8 +2066,15 @@ dump_table (scan)
scan = emit_insn_after (gen_align_2 (), scan);
need_align = 0;
}
- scan = emit_label_after (p->label, scan);
- scan = emit_insn_after (gen_consttable_2 (p->value), scan);
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_2 (p->value, const0_rtx),
+ scan);
+ for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+ {
+ lab = XEXP (ref, 0);
+ scan = emit_insn_after (gen_consttable_window_end (lab), scan);
+ }
}
}
@@ -2037,9 +2096,10 @@ dump_table (scan)
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
}
- if (p->label)
- scan = emit_label_after (p->label, scan);
- scan = emit_insn_after (gen_consttable_4 (p->value), scan);
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_4 (p->value, const0_rtx),
+ scan);
break;
case DFmode:
case DImode:
@@ -2049,19 +2109,31 @@ dump_table (scan)
scan = emit_label_after (gen_label_rtx (), scan);
scan = emit_insn_after (gen_align_4 (), scan);
}
- if (p->label)
- scan = emit_label_after (p->label, scan);
- scan = emit_insn_after (gen_consttable_8 (p->value), scan);
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
+ scan);
break;
default:
abort ();
break;
}
+
+ if (p->mode != HImode)
+ {
+ for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+ {
+ lab = XEXP (ref, 0);
+ scan = emit_insn_after (gen_consttable_window_end (lab), scan);
+ }
+ }
}
scan = emit_insn_after (gen_consttable_end (), scan);
scan = emit_barrier_after (scan);
pool_size = 0;
+ pool_window_label = NULL_RTX;
+ pool_window_last = 0;
}
/* Return non-zero if constant would be an ok source for a
@@ -2720,7 +2792,7 @@ struct far_branch
static void gen_far_branch PARAMS ((struct far_branch *));
enum mdep_reorg_phase_e mdep_reorg_phase;
-void
+static void
gen_far_branch (bp)
struct far_branch *bp;
{
@@ -2824,7 +2896,7 @@ barrier_align (barrier_or_label)
the table to the minimum for proper code alignment. */
return ((TARGET_SMALLCODE
|| (XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
- <= 1 << (CACHE_LOG - 2)))
+ <= (unsigned)1 << (CACHE_LOG - 2)))
? 1 : CACHE_LOG);
}
@@ -2856,7 +2928,7 @@ barrier_align (barrier_or_label)
investigation. Skip to the insn before it. */
prev = prev_real_insn (prev);
- for (slot = 2, credit = 1 << (CACHE_LOG - 2) + 2;
+ for (slot = 2, credit = (1 << (CACHE_LOG - 2)) + 2;
credit >= 0 && prev && GET_CODE (prev) == INSN;
prev = prev_real_insn (prev))
{
@@ -3933,7 +4005,7 @@ rounded_frame_size (pushed)
HOST_WIDE_INT size = get_frame_size ();
HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
- return (size + pushed + align - 1 & -align) - pushed;
+ return ((size + pushed + align - 1) & -align) - pushed;
}
void
@@ -3944,6 +4016,11 @@ sh_expand_prologue ()
int live_regs_mask2;
int save_flags = target_flags;
+ current_function_interrupt
+ = lookup_attribute ("interrupt_handler",
+ DECL_MACHINE_ATTRIBUTES (current_function_decl))
+ != NULL_TREE;
+
/* We have pretend args if we had an object sent partially in registers
and partially on the stack, e.g. a large structure. */
output_stack_adjust (-current_function_pretend_args_size,
@@ -3987,7 +4064,23 @@ sh_expand_prologue ()
push_regs (live_regs_mask, live_regs_mask2);
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
- emit_insn (gen_GOTaddr2picreg ());
+ {
+ 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);
+ }
if (target_flags != save_flags)
{
@@ -4455,6 +4548,13 @@ initial_elimination_offset (from, to)
int save_flags = target_flags;
int live_regs_mask, live_regs_mask2;
+
+ if (from == RETURN_ADDRESS_POINTER_REGNUM)
+ /* Kludge: since we assume that the return address is on the stack,
+ make it so. N.B. We rely on RETURN_ADDRESS_POINTER_REGNUM being
+ processed before ARG_POINTER_REGNUM here. */
+ regs_ever_live[PR_REG] = 1;
+
live_regs_mask = calc_live_regs (&regs_saved, &live_regs_mask2);
total_auto_space = rounded_frame_size (regs_saved);
target_flags = save_flags;
@@ -4473,13 +4573,7 @@ initial_elimination_offset (from, to)
if (from == RETURN_ADDRESS_POINTER_REGNUM
&& (to == FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM))
- {
- int i, n = total_saved_regs_space;
- for (i = PR_REG-1; i >= 0; i--)
- if (live_regs_mask & (1 << i))
- n -= 4;
- return n + total_auto_space;
- }
+ return total_auto_space;
abort ();
}
@@ -5013,7 +5107,7 @@ reg_unused_after (reg, insn)
return 1;
}
-extern struct obstack permanent_obstack;
+#include "ggc.h"
rtx
get_fpscr_rtx ()
@@ -5181,8 +5275,6 @@ static rtx
get_free_reg (regs_live)
HARD_REG_SET regs_live;
{
- rtx reg;
-
if (! TEST_HARD_REG_BIT (regs_live, 1))
return gen_rtx_REG (Pmode, 1);
@@ -5204,8 +5296,10 @@ fpscr_set_from_mem (mode, regs_live)
enum attr_fp_mode fp_mode = mode;
rtx addr_reg = get_free_reg (regs_live);
- emit_insn ((fp_mode == (TARGET_FPU_SINGLE ? FP_MODE_SINGLE : FP_MODE_DOUBLE)
- ? gen_fpu_switch1 : gen_fpu_switch0) (addr_reg));
+ if (fp_mode == (enum attr_fp_mode) NORMAL_MODE (FP_MODE))
+ emit_insn (gen_fpu_switch1 (addr_reg));
+ else
+ emit_insn (gen_fpu_switch0 (addr_reg));
}
/* Is the given character a logical line separator for the assembler? */
@@ -5327,7 +5421,7 @@ nonpic_symbol_mentioned_p (x)
rtx
legitimize_pic_address (orig, mode, reg)
rtx orig;
- enum machine_mode mode;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
rtx reg;
{
if (GET_CODE (orig) == LABEL_REF
@@ -5352,3 +5446,68 @@ legitimize_pic_address (orig, mode, reg)
}
return orig;
}
+
+/* Mark the use of a constant in the literal table. If the constant
+ has multiple labels, make it unique. */
+static rtx mark_constant_pool_use (x)
+ rtx x;
+{
+ rtx insn, lab, pattern;
+
+ if (x == NULL)
+ return x;
+
+ switch (GET_CODE (x))
+ {
+ case LABEL_REF:
+ x = XEXP (x, 0);
+ case CODE_LABEL:
+ break;
+ default:
+ return x;
+ }
+
+ /* Get the first label in the list of labels for the same constant
+ and delete another labels in the list. */
+ lab = x;
+ for (insn = PREV_INSN (x); insn; insn = PREV_INSN (insn))
+ {
+ if (GET_CODE (insn) != CODE_LABEL
+ || LABEL_REFS (insn) != NEXT_INSN (insn))
+ break;
+ lab = insn;
+ }
+
+ for (insn = LABEL_REFS (lab); insn; insn = LABEL_REFS (insn))
+ INSN_DELETED_P (insn) = 1;
+
+ /* Mark constants in a window. */
+ for (insn = NEXT_INSN (x); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) != INSN)
+ continue;
+
+ pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != UNSPEC_VOLATILE)
+ continue;
+
+ switch (XINT (pattern, 1))
+ {
+ case UNSPECV_CONST2:
+ case UNSPECV_CONST4:
+ case UNSPECV_CONST8:
+ XVECEXP (pattern, 0, 1) = const1_rtx;
+ break;
+ case UNSPECV_WINDOW_END:
+ if (XVECEXP (pattern, 0, 0) == x)
+ return lab;
+ break;
+ case UNSPECV_CONST_END:
+ return lab;
+ default:
+ break;
+ }
+ }
+
+ return lab;
+}