#include "opts.h"
#include "dumpfile.h"
#include "gimple-expr.h"
+#include "target-globals.h"
#include "builtins.h"
#include "tm-constrs.h"
#include "rtl-iter.h"
static void arm_expand_builtin_va_start (tree, rtx);
static tree arm_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
static void arm_option_override (void);
+static void arm_set_current_function (tree);
+static bool arm_can_inline_p (tree, tree);
+static bool arm_valid_target_attribute_p (tree, tree, tree, int);
static unsigned HOST_WIDE_INT arm_shift_truncation_mask (machine_mode);
static bool arm_macro_fusion_p (void);
static bool arm_cannot_copy_insn_p (rtx_insn *);
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE arm_output_function_epilogue
+#undef TARGET_CAN_INLINE_P
+#define TARGET_CAN_INLINE_P arm_can_inline_p
+
#undef TARGET_OPTION_OVERRIDE
#define TARGET_OPTION_OVERRIDE arm_option_override
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST arm_adjust_cost
+#undef TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION arm_set_current_function
+
+#undef TARGET_OPTION_VALID_ATTRIBUTE_P
+#define TARGET_OPTION_VALID_ATTRIBUTE_P arm_valid_target_attribute_p
+
#undef TARGET_SCHED_REORDER
#define TARGET_SCHED_REORDER arm_sched_reorder
max_insns_skipped = current_tune->max_insns_skipped;
}
+/* Options after initial target override. */
+static GTY(()) tree init_optimize;
+
/* Reset options between modes that the user has specified. */
static void
arm_option_override_internal (struct gcc_options *opts,
if (TARGET_THUMB_P (opts->x_target_flags) && TARGET_CALLEE_INTERWORKING)
opts->x_target_flags |= MASK_INTERWORK;
+ /* need to remember initial values so combinaisons of options like
+ -mflip-thumb -mthumb -fno-schedule-insns work for any attribute. */
+ cl_optimization *to = TREE_OPTIMIZATION (init_optimize);
+
if (! opts_set->x_arm_restrict_it)
opts->x_arm_restrict_it = arm_arch8;
if (!TARGET_THUMB2_P (opts->x_target_flags))
opts->x_arm_restrict_it = 0;
+ /* Don't warn since it's on by default in -O2. */
if (TARGET_THUMB1_P (opts->x_target_flags))
- {
- /* Don't warn since it's on by default in -O2. */
- opts->x_flag_schedule_insns = 0;
- }
+ opts->x_flag_schedule_insns = 0;
+ else
+ opts->x_flag_schedule_insns = to->x_flag_schedule_insns;
/* Disable shrink-wrap when optimizing function for size, since it tends to
generate additional returns. */
if (optimize_function_for_size_p (cfun)
&& TARGET_THUMB2_P (opts->x_target_flags))
opts->x_flag_shrink_wrap = false;
+ else
+ opts->x_flag_shrink_wrap = to->x_flag_shrink_wrap;
/* In Thumb1 mode, we emit the epilogue in RTL, but the last insn
- epilogue_insns - does not accurately model the corresponding insns
fipa-ra. */
if (TARGET_THUMB1_P (opts->x_target_flags))
opts->x_flag_ipa_ra = 0;
+ else
+ opts->x_flag_ipa_ra = to->x_flag_ipa_ra;
/* Thumb2 inline assembly code should always use unified syntax.
This will apply to ARM and Thumb1 eventually. */
&& (!arm_arch7 || !current_tune->prefer_ldrd_strd))
flag_schedule_fusion = 0;
+ /* Need to remember initial options before they are overriden. */
+ init_optimize = build_optimization_node (&global_options);
+
arm_option_override_internal (&global_options, &global_options_set);
arm_option_check_internal (&global_options);
arm_option_params_internal (&global_options);
/* Register global variables with the garbage collector. */
arm_add_gc_roots ();
+
+ /* Save the initial options in case the user does function specific
+ options. */
+ target_option_default_node = target_option_current_node
+ = build_target_option_node (&global_options);
}
static void
static void
arm_asm_trampoline_template (FILE *f)
{
+ if (TARGET_UNIFIED_ASM)
+ fprintf (f, "\t.syntax unified\n");
+ else
+ fprintf (f, "\t.syntax divided\n");
+
if (TARGET_ARM)
{
+ fprintf (f, "\t.arm\n");
asm_fprintf (f, "\tldr\t%r, [%r, #0]\n", STATIC_CHAIN_REGNUM, PC_REGNUM);
asm_fprintf (f, "\tldr\t%r, [%r, #0]\n", PC_REGNUM, PC_REGNUM);
}
else if (TARGET_THUMB2)
{
+ fprintf (f, "\t.thumb\n");
/* The Thumb-2 trampoline is similar to the arm implementation.
Unlike 16-bit Thumb, we enter the stub in thumb mode. */
asm_fprintf (f, "\tldr.w\t%r, [%r, #4]\n",
mark_reg_pointer (arg_pointer_rtx, PARM_BOUNDARY);
}
+/* Check that FUNC is called with a different mode. */
+
+bool
+arm_change_mode_p (tree func)
+{
+ if (TREE_CODE (func) != FUNCTION_DECL)
+ return false;
+
+ tree callee_tree = DECL_FUNCTION_SPECIFIC_TARGET (func);
+
+ if (!callee_tree)
+ callee_tree = target_option_default_node;
+
+ struct cl_target_option *callee_opts = TREE_TARGET_OPTION (callee_tree);
+ int flags = callee_opts->x_target_flags;
+
+ return (TARGET_THUMB_P (flags) != TARGET_THUMB);
+}
/* Like arm_compute_initial_elimination offset. Simpler because there
isn't an ABI specified frame pointer for Thumb. Instead, we set it
{
int val;
- if (TARGET_UNIFIED_ASM)
- asm_fprintf (asm_out_file, "\t.syntax unified\n");
-
if (TARGET_BPABI)
{
const char *fpu_name;
&& CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)));
}
+/* Remember the last target of arm_set_current_function. */
+static GTY(()) tree arm_previous_fndecl;
+
+/* Invalidate arm_previous_fndecl. */
+void
+arm_reset_previous_fndecl (void)
+{
+ arm_previous_fndecl = NULL_TREE;
+}
+
+/* Establish appropriate back-end context for processing the function
+ FNDECL. The argument might be NULL to indicate processing at top
+ level, outside of any function scope. */
+static void
+arm_set_current_function (tree fndecl)
+{
+ if (!fndecl || fndecl == arm_previous_fndecl)
+ return;
+
+ tree old_tree = (arm_previous_fndecl
+ ? DECL_FUNCTION_SPECIFIC_TARGET (arm_previous_fndecl)
+ : NULL_TREE);
+
+ tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
+
+ arm_previous_fndecl = fndecl;
+ if (old_tree == new_tree)
+ ;
+
+ else if (new_tree)
+ {
+ cl_target_option_restore (&global_options,
+ TREE_TARGET_OPTION (new_tree));
+
+ if (TREE_TARGET_GLOBALS (new_tree))
+ restore_target_globals (TREE_TARGET_GLOBALS (new_tree));
+ else
+ TREE_TARGET_GLOBALS (new_tree)
+ = save_target_globals_default_opts ();
+ }
+
+ else if (old_tree)
+ {
+ new_tree = target_option_current_node;
+
+ cl_target_option_restore (&global_options,
+ TREE_TARGET_OPTION (new_tree));
+ if (TREE_TARGET_GLOBALS (new_tree))
+ restore_target_globals (TREE_TARGET_GLOBALS (new_tree));
+ else if (new_tree == target_option_default_node)
+ restore_target_globals (&default_target_globals);
+ else
+ TREE_TARGET_GLOBALS (new_tree)
+ = save_target_globals_default_opts ();
+ }
+
+ arm_option_params_internal (&global_options);
+}
+
+/* Hook to determine if one function can safely inline another. */
+
+static bool
+arm_can_inline_p (tree caller ATTRIBUTE_UNUSED, tree callee ATTRIBUTE_UNUSED)
+{
+ /* Overidde default hook: Always OK to inline between different modes.
+ Function with mode specific instructions, e.g using asm, must be explicitely
+ protected with noinline. */
+ return true;
+}
+
+/* Inner function to process the attribute((target(...))), take an argument and
+ set the current options from the argument. If we have a list, recursively
+ go over the list. */
+
+static bool
+arm_valid_target_attribute_rec (tree args, struct gcc_options *opts)
+{
+ if (TREE_CODE (args) == TREE_LIST)
+ {
+ bool ret = true;
+ for (; args; args = TREE_CHAIN (args))
+ if (TREE_VALUE (args)
+ && !arm_valid_target_attribute_rec (TREE_VALUE (args), opts))
+ ret = false;
+ return ret;
+ }
+
+ else if (TREE_CODE (args) != STRING_CST)
+ {
+ error ("attribute %<target%> argument not a string");
+ return false;
+ }
+
+ char *argstr = ASTRDUP (TREE_STRING_POINTER (args));
+ while (argstr && *argstr != '\0')
+ {
+ while (ISSPACE (*argstr))
+ argstr++;
+
+ if (!strcmp (argstr, "thumb"))
+ {
+ opts->x_target_flags |= MASK_THUMB;
+ arm_option_check_internal (opts);
+ return true;
+ }
+
+ if (!strcmp (argstr, "arm"))
+ {
+ opts->x_target_flags &= ~MASK_THUMB;
+ arm_option_check_internal (opts);
+ return true;
+ }
+
+ warning (0, "attribute(target(\"%s\")) is unknown", argstr);
+ return false;
+ }
+
+ return false;
+}
+
+/* Return a TARGET_OPTION_NODE tree of the target options listed or NULL. */
+
+tree
+arm_valid_target_attribute_tree (tree args, struct gcc_options *opts,
+ struct gcc_options *opts_set)
+{
+ if (!arm_valid_target_attribute_rec (args, opts))
+ return NULL_TREE;
+
+ /* Do any overrides, such as global options arch=xxx. */
+ arm_option_override_internal (opts, opts_set);
+
+ return build_target_option_node (opts);
+}
+
+/* Hook to validate attribute((target("string"))). */
+
+static bool
+arm_valid_target_attribute_p (tree fndecl, tree ARG_UNUSED (name),
+ tree args, int ARG_UNUSED (flags))
+{
+ bool ret = true;
+ struct gcc_options func_options;
+ tree cur_tree, new_optimize;
+ gcc_assert ((fndecl != NULL_TREE) && (args != NULL_TREE));
+
+ /* Get the optimization options of the current function. */
+ tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
+
+ /* If the function changed the optimization levels as well as setting target
+ options, start with the optimizations specified. */
+ if (!func_optimize)
+ func_optimize = optimization_default_node;
+
+ /* Init func_options. */
+ memset (&func_options, 0, sizeof (func_options));
+ init_options_struct (&func_options, NULL);
+ lang_hooks.init_options_struct (&func_options);
+
+ /* Initialize func_options to the defaults. */
+ cl_optimization_restore (&func_options,
+ TREE_OPTIMIZATION (func_optimize));
+
+ cl_target_option_restore (&func_options,
+ TREE_TARGET_OPTION (target_option_default_node));
+
+ /* Set func_options flags with new target mode. */
+ cur_tree = arm_valid_target_attribute_tree (args, &func_options,
+ &global_options_set);
+
+ if (cur_tree == NULL_TREE)
+ ret = false;
+
+ new_optimize = build_optimization_node (&func_options);
+
+ DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = cur_tree;
+
+ DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize;
+
+ return ret;
+}
+
void
arm_declare_function_name (FILE *stream, const char *name, tree decl)
{
+ if (TARGET_UNIFIED_ASM)
+ fprintf (stream, "\t.syntax unified\n");
+ else
+ fprintf (stream, "\t.syntax divided\n");
+
if (TARGET_THUMB)
{
if (is_called_in_ARM_mode (decl)
else
fprintf (stream, "\t.thumb\n\t.thumb_func\n");
}
+ else
+ fprintf (stream, "\t.arm\n");
if (TARGET_POKE_FUNCTION_NAME)
arm_poke_function_name (stream, (const char *) name);