#include "cfglayout.h"
#include "intl.h"
#include "sched-int.h"
+#include "params.h"
#include "ggc.h"
#include "gimple.h"
#include "cfgloop.h"
static int calc_live_regs (HARD_REG_SET *);
static HOST_WIDE_INT rounded_frame_size (int);
static rtx mark_constant_pool_use (rtx);
-const struct attribute_spec sh_attribute_table[];
static tree sh_handle_interrupt_handler_attribute (tree *, tree, tree, int, bool *);
static tree sh_handle_resbank_handler_attribute (tree *, tree,
tree, int, bool *);
static tree sh_build_builtin_va_list (void);
static void sh_va_start (tree, rtx);
static tree sh_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
+static enum machine_mode sh_promote_function_mode (const_tree type,
+ enum machine_mode,
+ int *punsignedp,
+ const_tree funtype,
+ int for_return);
static bool sh_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
const_tree, bool);
static bool sh_callee_copies (CUMULATIVE_ARGS *, enum machine_mode,
static int sh_dwarf_calling_convention (const_tree);
static void sh_encode_section_info (tree, rtx, int);
static int sh2a_function_vector_p (tree);
+\f
+static const struct attribute_spec sh_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "interrupt_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
+ { "sp_switch", 1, 1, true, false, false, sh_handle_sp_switch_attribute },
+ { "trap_exit", 1, 1, true, false, false, sh_handle_trap_exit_attribute },
+ { "renesas", 0, 0, false, true, false, sh_handle_renesas_attribute },
+ { "trapa_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
+ { "nosave_low_regs", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
+ { "resbank", 0, 0, true, false, false, sh_handle_resbank_handler_attribute },
+ { "function_vector", 1, 1, true, false, false, sh2a_handle_function_vector_handler_attribute },
+#ifdef SYMBIAN
+ /* Symbian support adds three new attributes:
+ dllexport - for exporting a function/variable that will live in a dll
+ dllimport - for importing a function/variable from a dll
+ Microsoft allows multiple declspecs in one __declspec, separating
+ them with spaces. We do NOT support this. Instead, use __declspec
+ multiple times. */
+ { "dllimport", 0, 0, true, false, false, sh_symbian_handle_dll_attribute },
+ { "dllexport", 0, 0, true, false, false, sh_symbian_handle_dll_attribute },
+#endif
+ { NULL, 0, 0, false, false, false, NULL }
+};
\f
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES sh_promote_prototypes
-#undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS sh_promote_prototypes
-#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN sh_promote_prototypes
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE sh_promote_function_mode
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX sh_struct_value_rtx
}
}
\f
+/* Set default optimization options. */
+void
+sh_optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
+{
+ if (level)
+ {
+ flag_omit_frame_pointer = 2;
+ if (!size)
+ sh_div_str = "inv:minlat";
+ }
+ if (size)
+ {
+ target_flags |= MASK_SMALLCODE;
+ sh_div_str = SH_DIV_STR_FOR_SIZE ;
+ }
+ else
+ TARGET_CBRANCHDI4 = 1;
+ /* We can't meaningfully test TARGET_SHMEDIA here, because -m options
+ haven't been parsed yet, hence we'd read only the default.
+ sh_target_reg_class will return NO_REGS if this is not SHMEDIA, so
+ it's OK to always set flag_branch_target_load_optimize. */
+ if (level > 1)
+ {
+ flag_branch_target_load_optimize = 1;
+ if (!size)
+ target_flags |= MASK_SAVE_ALL_TARGET_REGS;
+ }
+ /* Likewise, we can't meaningfully test TARGET_SH2E / TARGET_IEEE
+ here, so leave it to OVERRIDE_OPTIONS to set
+ flag_finite_math_only. We set it to 2 here so we know if the user
+ explicitly requested this to be on or off. */
+ flag_finite_math_only = 2;
+ /* If flag_schedule_insns is 1, we set it to 2 here so we know if
+ the user explicitly requested this to be on or off. */
+ if (flag_schedule_insns > 0)
+ flag_schedule_insns = 2;
+
+ set_param_value ("simultaneous-prefetches", 2);
+}
+
+/* Implement OVERRIDE_OPTIONS macro. Validate and override various
+ options, and do some machine dependent initialization. */
+void
+sh_override_options (void)
+{
+ int regno;
+
+ SUBTARGET_OVERRIDE_OPTIONS;
+ if (flag_finite_math_only == 2)
+ flag_finite_math_only
+ = !flag_signaling_nans && TARGET_SH2E && ! TARGET_IEEE;
+ if (TARGET_SH2E && !flag_finite_math_only)
+ target_flags |= MASK_IEEE;
+ sh_cpu = PROCESSOR_SH1;
+ assembler_dialect = 0;
+ if (TARGET_SH2)
+ sh_cpu = PROCESSOR_SH2;
+ if (TARGET_SH2E)
+ sh_cpu = PROCESSOR_SH2E;
+ if (TARGET_SH2A)
+ sh_cpu = PROCESSOR_SH2A;
+ if (TARGET_SH3)
+ sh_cpu = PROCESSOR_SH3;
+ if (TARGET_SH3E)
+ sh_cpu = PROCESSOR_SH3E;
+ if (TARGET_SH4)
+ {
+ assembler_dialect = 1;
+ sh_cpu = PROCESSOR_SH4;
+ }
+ if (TARGET_SH4A_ARCH)
+ {
+ assembler_dialect = 1;
+ sh_cpu = PROCESSOR_SH4A;
+ }
+ if (TARGET_SH5)
+ {
+ sh_cpu = PROCESSOR_SH5;
+ target_flags |= MASK_ALIGN_DOUBLE;
+ if (TARGET_SHMEDIA_FPU)
+ target_flags |= MASK_FMOVD;
+ if (TARGET_SHMEDIA)
+ {
+ /* There are no delay slots on SHmedia. */
+ flag_delayed_branch = 0;
+ /* Relaxation isn't yet supported for SHmedia */
+ target_flags &= ~MASK_RELAX;
+ /* After reload, if conversion does little good but can cause
+ ICEs:
+ - find_if_block doesn't do anything for SH because we don't
+ have conditional execution patterns. (We use conditional
+ move patterns, which are handled differently, and only
+ before reload).
+ - find_cond_trap doesn't do anything for the SH because we
+ don't have conditional traps.
+ - find_if_case_1 uses redirect_edge_and_branch_force in
+ the only path that does an optimization, and this causes
+ an ICE when branch targets are in registers.
+ - find_if_case_2 doesn't do anything for the SHmedia after
+ reload except when it can redirect a tablejump - and
+ that's rather rare. */
+ flag_if_conversion2 = 0;
+ if (! strcmp (sh_div_str, "call"))
+ sh_div_strategy = SH_DIV_CALL;
+ else if (! strcmp (sh_div_str, "call2"))
+ sh_div_strategy = SH_DIV_CALL2;
+ if (! strcmp (sh_div_str, "fp") && TARGET_FPU_ANY)
+ sh_div_strategy = SH_DIV_FP;
+ else if (! strcmp (sh_div_str, "inv"))
+ sh_div_strategy = SH_DIV_INV;
+ else if (! strcmp (sh_div_str, "inv:minlat"))
+ sh_div_strategy = SH_DIV_INV_MINLAT;
+ else if (! strcmp (sh_div_str, "inv20u"))
+ sh_div_strategy = SH_DIV_INV20U;
+ else if (! strcmp (sh_div_str, "inv20l"))
+ sh_div_strategy = SH_DIV_INV20L;
+ else if (! strcmp (sh_div_str, "inv:call2"))
+ sh_div_strategy = SH_DIV_INV_CALL2;
+ else if (! strcmp (sh_div_str, "inv:call"))
+ sh_div_strategy = SH_DIV_INV_CALL;
+ else if (! strcmp (sh_div_str, "inv:fp"))
+ {
+ if (TARGET_FPU_ANY)
+ sh_div_strategy = SH_DIV_INV_FP;
+ else
+ sh_div_strategy = SH_DIV_INV;
+ }
+ TARGET_CBRANCHDI4 = 0;
+ /* Assembler CFI isn't yet fully supported for SHmedia. */
+ flag_dwarf2_cfi_asm = 0;
+ }
+ }
+ else
+ {
+ /* Only the sh64-elf assembler fully supports .quad properly. */
+ targetm.asm_out.aligned_op.di = NULL;
+ targetm.asm_out.unaligned_op.di = NULL;
+ }
+ if (TARGET_SH1)
+ {
+ if (! strcmp (sh_div_str, "call-div1"))
+ sh_div_strategy = SH_DIV_CALL_DIV1;
+ else if (! strcmp (sh_div_str, "call-fp")
+ && (TARGET_FPU_DOUBLE
+ || (TARGET_HARD_SH4 && TARGET_SH2E)
+ || (TARGET_SHCOMPACT && TARGET_FPU_ANY)))
+ sh_div_strategy = SH_DIV_CALL_FP;
+ else if (! strcmp (sh_div_str, "call-table") && TARGET_SH2)
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ else
+ /* Pick one that makes most sense for the target in general.
+ It is not much good to use different functions depending
+ on -Os, since then we'll end up with two different functions
+ when some of the code is compiled for size, and some for
+ speed. */
+
+ /* SH4 tends to emphasize speed. */
+ if (TARGET_HARD_SH4)
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ /* These have their own way of doing things. */
+ else if (TARGET_SH2A)
+ sh_div_strategy = SH_DIV_INTRINSIC;
+ /* ??? Should we use the integer SHmedia function instead? */
+ else if (TARGET_SHCOMPACT && TARGET_FPU_ANY)
+ sh_div_strategy = SH_DIV_CALL_FP;
+ /* SH1 .. SH3 cores often go into small-footprint systems, so
+ default to the smallest implementation available. */
+ else if (TARGET_SH2) /* ??? EXPERIMENTAL */
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ else
+ sh_div_strategy = SH_DIV_CALL_DIV1;
+ }
+ if (!TARGET_SH1)
+ TARGET_PRETEND_CMOVE = 0;
+ if (sh_divsi3_libfunc[0])
+ ; /* User supplied - leave it alone. */
+ else if (TARGET_DIVIDE_CALL_FP)
+ sh_divsi3_libfunc = "__sdivsi3_i4";
+ else if (TARGET_DIVIDE_CALL_TABLE)
+ sh_divsi3_libfunc = "__sdivsi3_i4i";
+ else if (TARGET_SH5)
+ sh_divsi3_libfunc = "__sdivsi3_1";
+ else
+ sh_divsi3_libfunc = "__sdivsi3";
+ if (sh_branch_cost == -1)
+ sh_branch_cost
+ = TARGET_SH5 ? 1 : ! TARGET_SH2 || TARGET_HARD_SH4 ? 2 : 1;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (! VALID_REGISTER_P (regno))
+ sh_register_names[regno][0] = '\0';
+
+ for (regno = 0; regno < ADDREGNAMES_SIZE; regno++)
+ if (! VALID_REGISTER_P (ADDREGNAMES_REGNO (regno)))
+ sh_additional_register_names[regno][0] = '\0';
+
+ if (flag_omit_frame_pointer == 2)
+ {
+ /* The debugging information is sufficient,
+ but gdb doesn't implement this yet */
+ if (0)
+ flag_omit_frame_pointer
+ = (PREFERRED_DEBUGGING_TYPE == DWARF2_DEBUG);
+ else
+ flag_omit_frame_pointer = 0;
+ }
+
+ if ((flag_pic && ! TARGET_PREFERGOT)
+ || (TARGET_SHMEDIA && !TARGET_PT_FIXED))
+ flag_no_function_cse = 1;
+
+ if (SMALL_REGISTER_CLASSES)
+ {
+ /* Never run scheduling before reload, since that can
+ break global alloc, and generates slower code anyway due
+ to the pressure on R0. */
+ /* Enable sched1 for SH4 if the user explicitly requests.
+ When sched1 is enabled, the ready queue will be reordered by
+ the target hooks if pressure is high. We can not do this for
+ PIC, SH3 and lower as they give spill failures for R0. */
+ if (!TARGET_HARD_SH4 || flag_pic)
+ flag_schedule_insns = 0;
+ /* ??? Current exception handling places basic block boundaries
+ after call_insns. It causes the high pressure on R0 and gives
+ spill failures for R0 in reload. See PR 22553 and the thread
+ on gcc-patches
+ <http://gcc.gnu.org/ml/gcc-patches/2005-10/msg00816.html>. */
+ else if (flag_exceptions)
+ {
+ if (flag_schedule_insns == 1)
+ warning (0, "ignoring -fschedule-insns because of exception handling bug");
+ flag_schedule_insns = 0;
+ }
+ else if (flag_schedule_insns == 2)
+ flag_schedule_insns = 0;
+ }
+
+ /* Unwinding with -freorder-blocks-and-partition does not work on this
+ architecture, because it requires far jumps to label crossing between
+ hot/cold sections which are rejected on this architecture. */
+ if (flag_reorder_blocks_and_partition)
+ {
+ if (flag_exceptions)
+ {
+ inform (input_location,
+ "-freorder-blocks-and-partition does not work with "
+ "exceptions on this architecture");
+ flag_reorder_blocks_and_partition = 0;
+ flag_reorder_blocks = 1;
+ }
+ else if (flag_unwind_tables)
+ {
+ inform (input_location,
+ "-freorder-blocks-and-partition does not support unwind "
+ "info on this architecture");
+ flag_reorder_blocks_and_partition = 0;
+ flag_reorder_blocks = 1;
+ }
+ }
+
+ if (align_loops == 0)
+ align_loops = 1 << (TARGET_SH5 ? 3 : 2);
+ if (align_jumps == 0)
+ align_jumps = 1 << CACHE_LOG;
+ else if (align_jumps < (TARGET_SHMEDIA ? 4 : 2))
+ align_jumps = TARGET_SHMEDIA ? 4 : 2;
+
+ /* Allocation boundary (in *bytes*) for the code of a function.
+ SH1: 32 bit alignment is faster, because instructions are always
+ fetched as a pair from a longword boundary.
+ SH2 .. SH5 : align to cache line start. */
+ if (align_functions == 0)
+ align_functions
+ = TARGET_SMALLCODE ? FUNCTION_BOUNDARY/8 : (1 << CACHE_LOG);
+ /* The linker relaxation code breaks when a function contains
+ alignments that are larger than that at the start of a
+ compilation unit. */
+ if (TARGET_RELAX)
+ {
+ int min_align
+ = align_loops > align_jumps ? align_loops : align_jumps;
+
+ /* Also take possible .long constants / mova tables int account. */
+ if (min_align < 4)
+ min_align = 4;
+ if (align_functions < min_align)
+ align_functions = min_align;
+ }
+
+ if (sh_fixed_range_str)
+ sh_fix_range (sh_fixed_range_str);
+}
+\f
/* Print the operand address in x to the stream. */
void
&& GET_CODE (SET_SRC (pat)) == CONST_DOUBLE
&& (fp_zero_operand (SET_SRC (pat))
|| fp_one_operand (SET_SRC (pat)))
- /* ??? If this is a -m4 or -m4-single compilation, in general
- we don't know the current setting of fpscr, so disable fldi.
+ /* In general we don't know the current setting of fpscr, so disable fldi.
There is an exception if this was a register-register move
before reload - and hence it was ascertained that we have
single precision setting - and in a post-reload optimization
we changed this to do a constant load. In that case
we don't have an r0 clobber, hence we must use fldi. */
- && (! TARGET_SH4 || TARGET_FMOVD
+ && (TARGET_FMOVD
|| (GET_CODE (XEXP (XVECEXP (PATTERN (insn), 0, 2), 0))
== SCRATCH))
&& REG_P (SET_DEST (pat))
/* If we're supposed to switch stacks at function entry, do so now. */
if (sp_switch_attr)
{
+ rtx lab, newsrc;
/* The argument specifies a variable holding the address of the
stack the interrupt function should switch to/from at entry/exit. */
+ tree arg = TREE_VALUE ( TREE_VALUE (sp_switch_attr));
const char *s
- = ggc_strdup (TREE_STRING_POINTER (TREE_VALUE (sp_switch_attr)));
+ = ggc_strdup (TREE_STRING_POINTER (arg));
rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s);
- emit_insn (gen_sp_switch_1 (sp_switch));
+ lab = add_constant (sp_switch, SImode, 0);
+ newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
+ newsrc = gen_const_mem (SImode, newsrc);
+
+ emit_insn (gen_sp_switch_1 (newsrc));
}
d = calc_live_regs (&live_regs_mask);
record = (*lang_hooks.types.make_type) (RECORD_TYPE);
- f_next_o = build_decl (FIELD_DECL, get_identifier ("__va_next_o"),
+ f_next_o = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_o"),
ptr_type_node);
- f_next_o_limit = build_decl (FIELD_DECL,
+ f_next_o_limit = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL,
get_identifier ("__va_next_o_limit"),
ptr_type_node);
- f_next_fp = build_decl (FIELD_DECL, get_identifier ("__va_next_fp"),
+ f_next_fp = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_fp"),
ptr_type_node);
- f_next_fp_limit = build_decl (FIELD_DECL,
+ f_next_fp_limit = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL,
get_identifier ("__va_next_fp_limit"),
ptr_type_node);
- f_next_stack = build_decl (FIELD_DECL, get_identifier ("__va_next_stack"),
+ f_next_stack = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_stack"),
ptr_type_node);
DECL_FIELD_CONTEXT (f_next_o) = record;
}
addr = create_tmp_var (pptr_type_node, NULL);
- lab_false = create_artificial_label ();
- lab_over = create_artificial_label ();
+ lab_false = create_artificial_label (UNKNOWN_LOCATION);
+ lab_over = create_artificial_label (UNKNOWN_LOCATION);
valist = build1 (INDIRECT_REF, ptr_type_node, addr);
if (result)
{
gimplify_assign (result, tmp, pre_p);
-
+ result = build1 (NOP_EXPR, TREE_TYPE (result), result);
tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
gimplify_and_add (tmp, pre_p);
}
DBX_REGISTER_NUMBER (regno))));
}
+static enum machine_mode
+sh_promote_function_mode (const_tree type, enum machine_mode mode,
+ int *punsignedp, const_tree funtype,
+ int for_return ATTRIBUTE_UNUSED)
+{
+ if (sh_promote_prototypes (funtype))
+ return promote_mode (type, mode, punsignedp);
+ else
+ return mode;
+}
+
bool
sh_promote_prototypes (const_tree type)
{
R0-R14, MACH, MACL, GBR and PR. This is useful only on SH2A targets.
*/
-const struct attribute_spec sh_attribute_table[] =
-{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
- { "interrupt_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
- { "sp_switch", 1, 1, true, false, false, sh_handle_sp_switch_attribute },
- { "trap_exit", 1, 1, true, false, false, sh_handle_trap_exit_attribute },
- { "renesas", 0, 0, false, true, false, sh_handle_renesas_attribute },
- { "trapa_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
- { "nosave_low_regs", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
- { "resbank", 0, 0, true, false, false, sh_handle_resbank_handler_attribute },
- { "function_vector", 1, 1, true, false, false, sh2a_handle_function_vector_handler_attribute },
-#ifdef SYMBIAN
- /* Symbian support adds three new attributes:
- dllexport - for exporting a function/variable that will live in a dll
- dllimport - for importing a function/variable from a dll
-
- Microsoft allows multiple declspecs in one __declspec, separating
- them with spaces. We do NOT support this. Instead, use __declspec
- multiple times. */
- { "dllimport", 0, 0, true, false, false, sh_symbian_handle_dll_attribute },
- { "dllexport", 0, 0, true, false, false, sh_symbian_handle_dll_attribute },
-#endif
- { NULL, 0, 0, false, false, false, NULL }
-};
-
/* Handle a 'resbank' attribute. */
static tree
sh_handle_resbank_handler_attribute (tree * node, tree name,
return REAL_VALUES_EQUAL (r, dconst1);
}
-/* For -m4 and -m4-single-only, mode switching is used. If we are
+/* In general mode switching is used. If we are
compiling without -mfmovd, movsf_ie isn't taken into account for
mode switching. We could check in machine_dependent_reorg for
cases where we know we are in single precision mode, but there is
int
fldi_ok (void)
{
- return ! TARGET_SH4 || TARGET_FMOVD || reload_completed;
+ return 1;
}
int
t = build_index_type (integer_one_node);
t = build_array_type (integer_type_node, t);
- t = build_decl (VAR_DECL, get_identifier ("__fpscr_values"), t);
+ t = build_decl (BUILTINS_LOCATION,
+ VAR_DECL, get_identifier ("__fpscr_values"), t);
DECL_ARTIFICIAL (t) = 1;
DECL_IGNORED_P (t) = 1;
DECL_EXTERNAL (t) = 1;