diff options
Diffstat (limited to 'gcc/config/sh/sh.c')
-rw-r--r-- | gcc/config/sh/sh.c | 243 |
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 (®s_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; +} |