Hi Ramana, Richard, This patch implements the attribute target (and pragma) to allow function based interworking.
as in the updated documentation, the syntax is: __attribute__((target("thumb"))) int foo() Forces thumb mode for function foo only. If the file was compiled with -mthumb iit has no effect. Similarly __attribute__((target("arm"))) int foo() Forces arm mode for function foo. It has no effect if the file was not compiled with -mthumb. and regions can be grouped together with #pragma GCC target ("thumb") or #pragma GCC target ("arm") a few notes - Inlining is allowed between functions of the same mode (compilation switch, #pragma and attribute) - 'arm_option_override' is now reorganized around 'arm_option_override_internal' for thumb related macros - I kept TARGET_UNIFIED_ASM to minimize changes. Although removing it would avoid to switch between unified/divided asms and simplify arm_declare_function_name. Should be considered at some point. - It is only available for Thumb2 variants (for thumb1 lack of interest and a few complications I was unable to test, although this could be added easily if needed, I think) Tested for no regression for arm-none-eabi [,-with-arch=armv7-a] OK for trunk ? many thanks, Christian
2014-09-23 Christian Bruel <christian.br...@st.com> PR target/52144 * config/arm/arm.opt (THUMB): Sqve target option. * config/arm/arm-protos.h (arm_declare_function_name, arm_valid_target_attribute_tree arm_register_target_pragmas, arm_reset_previous_fndecl): Declare. * config/arm/arm.c (arm_declare_function_name): Move here. add attribute target support. (emit_thumb): New boolean. (arm_file_start): Set emit_thumb mode. (arm_pragma_target_parse): New function. (arm_valid_target_attribute_p, arm_valid_target_attribute_tree, arm_valid_target_attribute_rec): New functions. (arm_can_inline_p): New function. (arm_set_current_function, arm_reset_previous_fndecl): New functions. (arm_option_override): Split. (arm_option_override_internal): New function. (TARGET_CAN_INLINE_P, TARGET_SET_CURRENT_FUNCTION, TARGET_OPTION_VALID_ATTRIBUTE_P): Define. * config/arm/arm-c.c (arm_pragma_target_parse, arm_target_modify_macros, arm_pragma_target_parse, arm_register_target_pragmas): New functions. * config/arm/arm.h (SWITCHABLE_TARGET): Define. (ARM_DECLARE_FUNCTION_NAME): Call arm_declare_function_name. (REGISTER_TARGET_PRAGMAS): Call arm_register_target_pragma. (TREE_TARGET_THUMB): New macro. * doc/extend.texi (arm, thumb): Document target attributes. * doc/invoke.texi (arm, thumb): Mention target attributes. 2014-09-23 Christian Bruel <christian.br...@st.com> PR target/52144 * gcc.target/arm/attr_thumb.c: New test. Index: gcc/config/arm/arm-c.c =================================================================== --- gcc/config/arm/arm-c.c (revision 215680) +++ gcc/config/arm/arm-c.c (working copy) @@ -20,9 +20,12 @@ #include "system.h" #include "coretypes.h" #include "tm.h" -#include "tm_p.h" #include "tree.h" +#include "tm_p.h" #include "c-family/c-common.h" +#include "target.h" +#include "target-def.h" +#include "c-family/c-pragma.h" /* Output C specific EABI object attributes. These can not be done in arm.c because they require information from the C frontend. */ @@ -42,3 +45,109 @@ { arm_lang_output_object_attributes_hook = arm_output_c_attributes; } + + +/* Define or undefine macros based on the current target. If the user does + #pragma GCC target, we need to adjust the macros dynamically. */ + +static void +arm_target_modify_macros (bool thumb_p) +{ + if (thumb_p) + { + cpp_define (parse_in, "__thumb__"); + if (arm_arch_thumb2) + cpp_define (parse_in, "__thumb2__"); + if (TARGET_BIG_END) + cpp_define (parse_in, "__THUMBEB__"); + else + cpp_define (parse_in, "__THUMBEL__"); + } + else + { + cpp_undef (parse_in, "__thumb__"); + if (arm_arch_thumb2) + cpp_undef (parse_in, "__thumb2__"); + if (TARGET_BIG_END) + cpp_undef (parse_in, "__THUMBEB__"); + else + cpp_undef (parse_in, "__THUMBEL__"); + } + +} + +/* Hook to validate the current #pragma GCC target and set the FPU custom + code option state. If ARGS is NULL, then POP_TARGET is used to reset + the options. */ +static bool +arm_pragma_target_parse (tree args, tree pop_target) +{ + tree prev_tree = build_target_option_node (&global_options); + tree cur_tree; + struct cl_target_option *prev_opt; + struct cl_target_option *cur_opt; + bool cur_mode, prev_mode; + + if (! args) + { + cur_tree = ((pop_target) ? pop_target : target_option_default_node); + cl_target_option_restore (&global_options, + TREE_TARGET_OPTION (cur_tree)); + } + else + { + cur_tree = arm_valid_target_attribute_tree (args, &global_options); + if (cur_tree == NULL_TREE) + { + cl_target_option_restore (&global_options, + TREE_TARGET_OPTION (prev_tree)); + return false; + } + } + + target_option_current_node = cur_tree; + arm_reset_previous_fndecl (); + + /* Figure out the previous mode. */ + prev_opt = TREE_TARGET_OPTION (prev_tree); + cur_opt = TREE_TARGET_OPTION (cur_tree); + + gcc_assert (prev_opt); + gcc_assert (cur_opt); + + cur_mode = TREE_TARGET_THUMB (cur_opt); + prev_mode = TREE_TARGET_THUMB (prev_opt); + + if (prev_mode != cur_mode) + { + /* For the definitions, ensure all newly defined macros are considered + as used for -Wunused-macros. There is no point warning about the + compiler predefined macros. */ + cpp_options *cpp_opts = cpp_get_options (parse_in); + unsigned char saved_warn_unused_macros = cpp_opts->warn_unused_macros; + cpp_opts->warn_unused_macros = 0; + + /* Update macros. */ + arm_target_modify_macros (cur_mode); + + cpp_opts->warn_unused_macros = saved_warn_unused_macros; + } + + return true; +} + +/* Register target pragmas. We need to add the hook for parsing #pragma GCC + option here rather than in arm.c since it will pull in various preprocessor + functions, and those are not present in languages like fortran without a + preprocessor. */ + +void +arm_register_target_pragmas (void) +{ + /* Update pragma hook to allow parsing #pragma GCC target. */ + targetm.target_option.pragma_parse = arm_pragma_target_parse; + +#ifdef REGISTER_SUBTARGET_PRAGMAS + REGISTER_SUBTARGET_PRAGMAS (); +#endif +} Index: gcc/config/arm/arm-protos.h =================================================================== --- gcc/config/arm/arm-protos.h (revision 215680) +++ gcc/config/arm/arm-protos.h (working copy) @@ -30,6 +30,7 @@ extern int arm_volatile_func (void); extern void arm_expand_prologue (void); extern void arm_expand_epilogue (bool); +extern void arm_declare_function_name (FILE *, const char *, tree); extern void thumb2_expand_return (bool); extern const char *arm_strip_name_encoding (const char *); extern void arm_asm_output_labelref (FILE *, const char *); @@ -209,14 +210,13 @@ extern int arm_dllimport_p (tree); extern void arm_mark_dllexport (tree); extern void arm_mark_dllimport (tree); +extern tree arm_valid_target_attribute_tree (tree, struct gcc_options *); #endif extern void arm_pr_long_calls (struct cpp_reader *); extern void arm_pr_no_long_calls (struct cpp_reader *); extern void arm_pr_long_calls_off (struct cpp_reader *); -extern void arm_lang_object_attributes_init(void); - extern const char *arm_mangle_type (const_tree); extern void arm_order_regs_for_local_alloc (void); @@ -301,9 +301,15 @@ extern void arm_emit_eabi_attribute (const char *, int, int); +extern void arm_reset_previous_fndecl (void); + /* Defined in gcc/common/config/arm-common.c. */ extern const char *arm_rewrite_selected_cpu (const char *name); +/* Defined in gcc/common/config/arm-c.c. */ +extern void arm_lang_object_attributes_init(void); +extern void arm_register_target_pragmas (void); + extern bool arm_is_constant_pool_ref (rtx); #endif /* ! GCC_ARM_PROTOS_H */ Index: gcc/config/arm/arm.c =================================================================== --- gcc/config/arm/arm.c (revision 215680) +++ gcc/config/arm/arm.c (working copy) @@ -61,6 +61,7 @@ #include "opts.h" #include "dumpfile.h" #include "gimple-expr.h" +#include "target-globals.h" #include "builtins.h" #include "tm-constrs.h" @@ -238,6 +239,9 @@ 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 (enum machine_mode); static bool arm_cannot_copy_insn_p (rtx_insn *); static int arm_issue_rate (void); @@ -378,6 +382,9 @@ #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 @@ -390,6 +397,12 @@ #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 @@ -2552,7 +2565,104 @@ /* Fix up any incompatible options that the user has specified. */ static void -arm_option_override (void) +arm_option_override_internal (void) +{ + /* Make sure that the processor choice does not conflict with any of the + other command line choices. */ + if (TARGET_ARM && !(insn_flags & FL_NOTM)) + error ("target CPU does not support ARM mode"); + + /* BPABI targets use linker tricks to allow interworking on cores + without thumb support. */ + if (TARGET_INTERWORK && !((insn_flags & FL_THUMB) || TARGET_BPABI)) + { + warning (0, "target CPU does not support interworking" ); + target_flags &= ~MASK_INTERWORK; + } + + if (TARGET_THUMB && !(insn_flags & FL_THUMB)) + { + warning (0, "target CPU does not support THUMB instructions"); + target_flags &= ~MASK_THUMB; + } + + if (TARGET_APCS_FRAME && TARGET_THUMB) + { + /* warning (0, "ignoring -mapcs-frame because -mthumb was used"); */ + target_flags &= ~MASK_APCS_FRAME; + } + + /* Callee super interworking implies thumb interworking. Adding + this to the flags here simplifies the logic elsewhere. */ + if (TARGET_THUMB && TARGET_CALLEE_INTERWORKING) + target_flags |= MASK_INTERWORK; + + /* TARGET_BACKTRACE calls leaf_function_p, which causes a crash if done + from here where no function is being compiled currently. */ + if ((TARGET_TPCS_FRAME || TARGET_TPCS_LEAF_FRAME) && TARGET_ARM) + warning (0, "enabling backtrace support is only meaningful when compiling for the Thumb"); + + if (TARGET_ARM && TARGET_CALLEE_INTERWORKING) + warning (0, "enabling callee interworking support is only meaningful when compiling for the Thumb"); + + /* If this target is normally configured to use APCS frames, warn if they + are turned off and debugging is turned on. */ + if (TARGET_ARM + && write_symbols != NO_DEBUG + && !TARGET_APCS_FRAME + && (TARGET_DEFAULT & MASK_APCS_FRAME)) + warning (0, "-g with -mno-apcs-frame may not give sensible debugging"); + + thumb_code = TARGET_ARM == 0; + + if (arm_restrict_it == 2) + arm_restrict_it = arm_arch8 && TARGET_THUMB2; + + if (!TARGET_THUMB2) + arm_restrict_it = 0; + + /* If we are not using the default (ARM mode) section anchor offset + ranges, then set the correct ranges now. */ + if (TARGET_THUMB2) + { + /* The minimum is set such that the total size of the block + for a particular anchor is 248 + 1 + 4095 bytes, which is + divisible by eight, ensuring natural spacing of anchors. */ + targetm.min_anchor_offset = -248; + targetm.max_anchor_offset = 4095; + } + + /* iWMMXt unsupported under Thumb mode. */ + if (TARGET_THUMB && TARGET_IWMMXT) + error ("iWMMXt unsupported under Thumb mode"); + + if (!TARGET_ARM && TARGET_VXWORKS_RTP && flag_pic) + { + error ("RTP PIC is incompatible with Thumb"); + flag_pic = 0; + } + + if (optimize_size) + { + /* If optimizing for size, bump the number of instructions that we + are prepared to conditionally execute (even on a StrongARM). */ + max_insns_skipped = 6; + + /* For THUMB2, we limit the conditional sequence to one IT block. */ + if (TARGET_THUMB2) + max_insns_skipped = MAX_INSN_PER_IT_BLOCK; + } + else + max_insns_skipped = current_tune->max_insns_skipped; + + /* Disable shrink-wrap when optimizing function for size, since it tends to + generate additional returns. */ + if (optimize_function_for_size_p (cfun) && TARGET_THUMB2) + flag_shrink_wrap = false; +} + +static void +arm_option_override (void) { if (global_options_set.x_arm_arch_option) arm_selected_arch = &all_architectures[arm_arch_option]; @@ -2692,43 +2802,7 @@ tune_flags = arm_selected_tune->flags; current_tune = arm_selected_tune->tune; - /* Make sure that the processor choice does not conflict with any of the - other command line choices. */ - if (TARGET_ARM && !(insn_flags & FL_NOTM)) - error ("target CPU does not support ARM mode"); - - /* BPABI targets use linker tricks to allow interworking on cores - without thumb support. */ - if (TARGET_INTERWORK && !((insn_flags & FL_THUMB) || TARGET_BPABI)) - { - warning (0, "target CPU does not support interworking" ); - target_flags &= ~MASK_INTERWORK; - } - - if (TARGET_THUMB && !(insn_flags & FL_THUMB)) - { - warning (0, "target CPU does not support THUMB instructions"); - target_flags &= ~MASK_THUMB; - } - - if (TARGET_APCS_FRAME && TARGET_THUMB) - { - /* warning (0, "ignoring -mapcs-frame because -mthumb was used"); */ - target_flags &= ~MASK_APCS_FRAME; - } - - /* Callee super interworking implies thumb interworking. Adding - this to the flags here simplifies the logic elsewhere. */ - if (TARGET_THUMB && TARGET_CALLEE_INTERWORKING) - target_flags |= MASK_INTERWORK; - - /* TARGET_BACKTRACE calls leaf_function_p, which causes a crash if done - from here where no function is being compiled currently. */ - if ((TARGET_TPCS_FRAME || TARGET_TPCS_LEAF_FRAME) && TARGET_ARM) - warning (0, "enabling backtrace support is only meaningful when compiling for the Thumb"); - - if (TARGET_ARM && TARGET_CALLEE_INTERWORKING) - warning (0, "enabling callee interworking support is only meaningful when compiling for the Thumb"); + arm_option_override_internal (); if (TARGET_APCS_STACK && !TARGET_APCS_FRAME) { @@ -2745,14 +2819,6 @@ if (TARGET_APCS_REENT) warning (0, "APCS reentrant code not supported. Ignored"); - /* If this target is normally configured to use APCS frames, warn if they - are turned off and debugging is turned on. */ - if (TARGET_ARM - && write_symbols != NO_DEBUG - && !TARGET_APCS_FRAME - && (TARGET_DEFAULT & MASK_APCS_FRAME)) - warning (0, "-g with -mno-apcs-frame may not give sensible debugging"); - if (TARGET_APCS_FLOAT) warning (0, "passing floating point arguments in fp regs not yet supported"); @@ -2774,9 +2840,8 @@ arm_ld_sched = (tune_flags & FL_LDSCHED) != 0; arm_tune_strongarm = (tune_flags & FL_STRONG) != 0; - thumb_code = TARGET_ARM == 0; - thumb1_code = TARGET_THUMB1 != 0; arm_tune_wbuf = (tune_flags & FL_WBUF) != 0; + thumb1_code = TARGET_THUMB1 != 0; arm_tune_xscale = (tune_flags & FL_XSCALE) != 0; arm_arch_iwmmxt = (insn_flags & FL_IWMMXT) != 0; arm_arch_iwmmxt2 = (insn_flags & FL_IWMMXT2) != 0; @@ -2784,11 +2849,6 @@ arm_arch_arm_hwdiv = (insn_flags & FL_ARM_DIV) != 0; arm_tune_cortex_a9 = (arm_tune == cortexa9) != 0; arm_arch_crc = (insn_flags & FL_CRC32) != 0; - if (arm_restrict_it == 2) - arm_restrict_it = arm_arch8 && TARGET_THUMB2; - - if (!TARGET_THUMB2) - arm_restrict_it = 0; /* If we are not using the default (ARM mode) section anchor offset ranges, then set the correct ranges now. */ @@ -2802,14 +2862,6 @@ targetm.min_anchor_offset = 0; targetm.max_anchor_offset = 127; } - else if (TARGET_THUMB2) - { - /* The minimum is set such that the total size of the block - for a particular anchor is 248 + 1 + 4095 bytes, which is - divisible by eight, ensuring natural spacing of anchors. */ - targetm.min_anchor_offset = -248; - targetm.max_anchor_offset = 4095; - } /* V5 code we generate is completely interworking capable, so we turn off TARGET_INTERWORK here to avoid many tests later on. */ @@ -2872,10 +2924,6 @@ if (TARGET_IWMMXT && TARGET_NEON) error ("iWMMXt and NEON are incompatible"); - /* iWMMXt unsupported under Thumb mode. */ - if (TARGET_THUMB && TARGET_IWMMXT) - error ("iWMMXt unsupported under Thumb mode"); - /* __fp16 support currently assumes the core has ldrh. */ if (!arm_arch4 && arm_fp16_format != ARM_FP16_FORMAT_NONE) sorry ("__fp16 and no ldrh"); @@ -2945,12 +2993,6 @@ } } - if (!TARGET_ARM && TARGET_VXWORKS_RTP && flag_pic) - { - error ("RTP PIC is incompatible with Thumb"); - flag_pic = 0; - } - /* If stack checking is disabled, we can use r10 as the PIC register, which keeps r9 available. The EABI specifies r9 as the PIC register. */ if (flag_pic && TARGET_SINGLE_PIC_BASE) @@ -3024,19 +3066,6 @@ flag_schedule_insns = 0; } - if (optimize_size) - { - /* If optimizing for size, bump the number of instructions that we - are prepared to conditionally execute (even on a StrongARM). */ - max_insns_skipped = 6; - - /* For THUMB2, we limit the conditional sequence to one IT block. */ - if (TARGET_THUMB2) - max_insns_skipped = MAX_INSN_PER_IT_BLOCK; - } - else - max_insns_skipped = current_tune->max_insns_skipped; - /* Hot/Cold partitioning is not currently supported, since we can't handle literal pool placement in that case. */ if (flag_reorder_blocks_and_partition) @@ -3098,10 +3127,6 @@ global_options.x_param_values, global_options_set.x_param_values); - /* Disable shrink-wrap when optimizing function for size, since it tends to - generate additional returns. */ - if (optimize_function_for_size_p (cfun) && TARGET_THUMB2) - flag_shrink_wrap = false; /* TBD: Dwarf info for apcs frame is not handled yet. */ if (TARGET_APCS_FRAME) flag_shrink_wrap = false; @@ -3118,6 +3143,11 @@ /* 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 @@ -28356,13 +28386,19 @@ asm_fprintf (asm_out_file, "\n"); } +/* Allow interwork inside a module. */ +static bool emit_thumb = false; + static void arm_file_start (void) { int val; if (TARGET_UNIFIED_ASM) - asm_fprintf (asm_out_file, "\t.syntax unified\n"); + { + asm_fprintf (asm_out_file, "\t.syntax unified\n"); + emit_thumb = true; + } if (TARGET_BPABI) { @@ -32289,4 +32325,250 @@ && 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_override_internal (); +} + +/* Hook to determine if one function can safely inline another. */ + +static bool +arm_can_inline_p (tree caller, tree callee) +{ + tree callee_tree = DECL_FUNCTION_SPECIFIC_TARGET (callee); + tree caller_tree = DECL_FUNCTION_SPECIFIC_TARGET (caller); + + if (!callee_tree && !caller_tree) + return true; + + /* If caller or callee has no option attributes, but it is ok to + inline if they don't change mode. */ + else if (!callee_tree) + callee_tree = target_option_default_node; + + else if (!caller_tree) + caller_tree = target_option_default_node; + + struct cl_target_option *caller_opts = TREE_TARGET_OPTION (caller_tree); + struct cl_target_option *callee_opts = TREE_TARGET_OPTION (callee_tree); + + /* If both caller and callee have attributes, assume that if the + pointer is different, the two functions have different target + options since build_target_option_node uses a hash table for the + options. */ + gcc_assert (callee_opts); + gcc_assert (caller_opts); + return TREE_TARGET_THUMB (callee_opts) == TREE_TARGET_THUMB (caller_opts); +} + +/* 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 (!strncmp (argstr, "thumb", 5)) + { + if (! arm_arch_thumb2) + { + warning (0, "`target thumb only available for thumb2"); + return false; + } + + opts->x_target_flags |= MASK_THUMB; + return true; + } + + if (!strncmp (argstr, "arm", 3)) + { + if (! arm_arch_thumb2) + { + warning (0, "`target arm only available for thumb2"); + return false; + } + + opts->x_target_flags &= ~MASK_THUMB; + 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) +{ + if (!arm_valid_target_attribute_rec (args, opts)) + return NULL_TREE; + + 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)) +{ + tree cur_tree, new_optimize; + struct gcc_options func_options; + gcc_assert ((fndecl != NULL_TREE) && (args != NULL_TREE)); + + tree old_optimize = build_optimization_node (&global_options); + + /* Get the optimization options of the current function. */ + tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl); + + if (!func_optimize) + func_optimize = old_optimize; + + /* 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_optionsflags with new target mode. */ + cur_tree = arm_valid_target_attribute_tree (args, &func_options); + + if (cur_tree == NULL_TREE) + return false; + + new_optimize = build_optimization_node (&func_options); + + if (fndecl) + { + DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = cur_tree; + + if (old_optimize != new_optimize) + DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl) = new_optimize; + } + + return true; +} + +void +arm_declare_function_name (FILE *stream, const char *name, tree decl) +{ + if (TARGET_THUMB) + { + if (! emit_thumb && TARGET_UNIFIED_ASM) + { + emit_thumb = true; + fprintf (stream, "\t.syntax unified\n"); + } + + if (is_called_in_ARM_mode (decl) + || (TARGET_THUMB1 && !TARGET_THUMB1_ONLY + && cfun->is_thunk)) + fprintf (stream, "\t.code 32\n"); + else + { + if (TARGET_THUMB1) + fprintf (stream, "\t.code\t16\n\t.thumb_func\n"); + else + fprintf (stream, "\t.thumb\n\t.thumb_func\n"); + } + } + else if (emit_thumb) + { + emit_thumb = false; + fprintf (stream, "\t.syntax divided\n"); + fprintf (stream, "\t.arm\n"); + } + + if (TARGET_POKE_FUNCTION_NAME) + arm_poke_function_name (stream, (const char *) name); +} + + + #include "gt-arm.h" + Index: gcc/config/arm/arm.h =================================================================== --- gcc/config/arm/arm.h (revision 215680) +++ gcc/config/arm/arm.h (working copy) @@ -247,6 +247,9 @@ #define SUBTARGET_CPP_SPEC "" #endif +/* Tree Target Specification. */ +#define TREE_TARGET_THUMB(opt) (opt->x_target_flags & MASK_THUMB) + /* Run-time Target Specification. */ #define TARGET_SOFT_FLOAT (arm_float_abi == ARM_FLOAT_ABI_SOFT) /* Use hardware floating point instructions. */ @@ -2118,7 +2121,8 @@ c_register_pragma (0, "long_calls", arm_pr_long_calls); \ c_register_pragma (0, "no_long_calls", arm_pr_no_long_calls); \ c_register_pragma (0, "long_calls_off", arm_pr_long_calls_off); \ - arm_lang_object_attributes_init(); \ + arm_lang_object_attributes_init(); \ + arm_register_target_pragmas (); \ } while (0) /* Condition code information. */ @@ -2199,23 +2203,7 @@ ? 1 : 0) #define ARM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ - do \ - { \ - if (TARGET_THUMB) \ - { \ - if (is_called_in_ARM_mode (DECL) \ - || (TARGET_THUMB1 && !TARGET_THUMB1_ONLY \ - && cfun->is_thunk)) \ - fprintf (STREAM, "\t.code 32\n") ; \ - else if (TARGET_THUMB1) \ - fprintf (STREAM, "\t.code\t16\n\t.thumb_func\n") ; \ - else \ - fprintf (STREAM, "\t.thumb\n\t.thumb_func\n") ; \ - } \ - if (TARGET_POKE_FUNCTION_NAME) \ - arm_poke_function_name (STREAM, (const char *) NAME); \ - } \ - while (0) + arm_declare_function_name ((STREAM), (NAME), (DECL)); /* For aliases of functions we use .thumb_set instead. */ #define ASM_OUTPUT_DEF_FROM_DECLS(FILE, DECL1, DECL2) \ @@ -2390,4 +2378,8 @@ #define DRIVER_SELF_SPECS MCPU_MTUNE_NATIVE_SPECS #define TARGET_SUPPORTS_WIDE_INT 1 + +/* For switching between functions with different target attributes. */ +#define SWITCHABLE_TARGET 1 + #endif /* ! GCC_ARM_H */ Index: gcc/config/arm/arm.opt =================================================================== --- gcc/config/arm/arm.opt (revision 215680) +++ gcc/config/arm/arm.opt (working copy) @@ -186,7 +186,7 @@ Specify the minimum bit alignment of structures mthumb -Target Report RejectNegative Mask(THUMB) +Target Report RejectNegative Mask(THUMB) Save Generate code for Thumb state mthumb-interwork Index: gcc/doc/extend.texi =================================================================== --- gcc/doc/extend.texi (revision 215680) +++ gcc/doc/extend.texi (working copy) @@ -3938,9 +3938,25 @@ with a comma (@samp{,}). The @code{target} attribute is presently implemented for -i386/x86_64, PowerPC, and Nios II targets only. +ARM, i386/x86_64, PowerPC, and Nios II targets only. The options supported are specific to each target. +for ARM, the following options are allowed for post-ARMv6: + +@table @samp +@item thumb +@cindex @code{target("thumb")} attribute +Force Thumb code generation. Only available for architectures supporting Thumb2. + +@item arm +@cindex @code{target("arm")} attribute +Force ARM code generation. Only available for architectures supporting Thumb2. +@end table + +The inliner does not inline a function that has different mode than the caller. +For example a function declared with @code{target("arm")} will not be inlined if the file +is compiled with @option{-mthumb}. + On the 386, the following options are allowed: @table @samp @@ -17403,7 +17419,7 @@ @code{target} attribute and the attribute syntax. The @code{#pragma GCC target} pragma is presently implemented for -i386/x86_64, PowerPC, and Nios II targets only. +ARM, i386/x86_64, PowerPC, and Nios II targets only. @end table @table @code Index: gcc/doc/invoke.texi =================================================================== --- gcc/doc/invoke.texi (revision 215680) +++ gcc/doc/invoke.texi (working copy) @@ -12809,6 +12809,10 @@ configuring GCC with the @option{--with-mode=}@var{state} configure option. +You can also override the ARM and Thumb mode for each function +by using the @code{target("thumb")} and @code{target("arm")} function attributes +(@pxref{Function Attributes}) or pragmas (@pxref{Function Specific Option Pragmas}). + @item -mtpcs-frame @opindex mtpcs-frame Generate a stack frame that is compliant with the Thumb Procedure Call Index: gcc/testsuite/gcc.target/arm/attr_thumb.c =================================================================== --- gcc/testsuite/gcc.target/arm/attr_thumb.c (revision 0) +++ gcc/testsuite/gcc.target/arm/attr_thumb.c (working copy) @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ +/* { dg-final { scan-assembler ".thumb" } } */ + +void __attribute__((target("thumb"))) +foo(int argc) +{ +} +