Implements and document the hooks to support target_attributes.
The emission of blx is handled directly for armv5 to overcome a bug with the current binutils that fails with calls to a static symbol in a different section. (e.g .text -> .text.startup) in different modes.
(ref https://sourceware.org/bugzilla/show_bug.cgi?id=17505) Regtests included Thanks Christian
2014-09-23 Christian Bruel <christian.br...@st.com> * config/arm/arm.opt (mthumb): Save. * config/arm/arm.h (arm_valid_target_attribute_tree): Declare. (arm_reset_previous_fndecl, arm_change_mode_p): Likewise. (SWITCHABLE_TARGET): Define. * config/arm/arm.c (arm_reset_previous_fndecl): New functions. (arm_valid_target_attribute_tree, arm_change_mode_p): Likewise. (arm_valid_target_attribute_p): Likewise. (arm_set_current_function, arm_can_inline_p): Likewise. (arm_valid_target_attribute_rec): Likewise. (arm_previous_fndecl): New variable. (TARGET_SET_CURRENT_FUNCTION, TARGET_OPTION_VALID_ATTRIBUTE_P): Define. (TARGET_CAN_INLINE_P): Define. (arm_asm_trampoline_template): Emit mode. (arm_file_start): Don't set unified syntax. (arm_declare_function_name): Set unified syntax and mode. (arm_option_override): Init target_option_default_node. and target_option_current_node. * config/arm/arm.md (*call_value_symbol): Set mode when possible. (*call_symbol): Likewise. * doc/extend.texi: Document ARM target and pragma attribute. * doc/invoke.texi: Likewise. diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/config/arm/arm.c e/gcc/gcc/config/arm/arm.c --- d/gcc/gcc/config/arm/arm.c 2014-11-18 08:51:50.000000000 +0100 +++ e/gcc/gcc/config/arm/arm.c 2014-11-18 09:05:57.000000000 +0100 @@ -80,6 +80,7 @@ #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" @@ -258,6 +259,9 @@ static tree arm_build_builtin_va_list (v 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_cannot_copy_insn_p (rtx_insn *); static int arm_issue_rate (void); @@ -400,6 +404,9 @@ static const struct attribute_spec arm_a #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 @@ -412,6 +419,12 @@ static const struct attribute_spec arm_a #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 @@ -3205,6 +3218,11 @@ arm_option_override (void) /* 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 @@ -3358,13 +3376,20 @@ arm_warn_func_return (tree decl) 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", @@ -26874,6 +26899,23 @@ thumb_far_jump_used_p (void) return 0; } +/* 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); + + return (TREE_TARGET_THUMB (callee_opts) != TARGET_THUMB); +} /* Given the stack offsets and register mask in OFFSETS, decide how many additional registers to push instead of subtracting a constant @@ -28448,9 +28490,6 @@ arm_file_start (void) { int val; - if (TARGET_UNIFIED_ASM) - asm_fprintf (asm_out_file, "\t.syntax unified\n"); - if (TARGET_BPABI) { const char *fpu_name; @@ -32387,9 +32426,201 @@ arm_is_constant_pool_ref (rtx x) && 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 (!strncmp (argstr, "thumb", 5)) + { + opts->x_target_flags |= MASK_THUMB; + arm_option_check_internal (opts); + return true; + } + + if (!strncmp (argstr, "arm", 3)) + { + 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) +{ + tree t = NULL_TREE; + + if (!arm_valid_target_attribute_rec (args, opts)) + return NULL_TREE; + + t = build_target_option_node (opts); + + /* Do any overrides, such as global options arch=xxx. */ + arm_option_override_internal (opts); + + return t; +} + +/* 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_options flags 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_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) @@ -32401,6 +32635,8 @@ arm_declare_function_name (FILE *stream, 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); diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/config/arm/arm.h e/gcc/gcc/config/arm/arm.h --- d/gcc/gcc/config/arm/arm.h 2014-11-13 12:19:45.000000000 +0100 +++ e/gcc/gcc/config/arm/arm.h 2014-11-13 13:48:59.000000000 +0100 @@ -2359,4 +2359,8 @@ extern const char *host_detect_local_cpu #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 */ diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/config/arm/arm.md e/gcc/gcc/config/arm/arm.md --- d/gcc/gcc/config/arm/arm.md 2014-11-18 08:51:50.000000000 +0100 +++ e/gcc/gcc/config/arm/arm.md 2014-11-18 08:49:18.000000000 +0100 @@ -7728,6 +7728,13 @@ && !arm_is_long_call_p (SYMBOL_REF_DECL (operands[0]))" "* { + rtx op = operands[0]; + + /* Switch mode now when possible. */ + if (SYMBOL_REF_DECL (op) && !TREE_PUBLIC (SYMBOL_REF_DECL (op)) + && arm_arch5 && arm_change_mode_p (SYMBOL_REF_DECL (op))) + return NEED_PLT_RELOC ? \"blx%?\\t%a0(PLT)\" : \"blx%?\\t(%a0)\"; + return NEED_PLT_RELOC ? \"bl%?\\t%a0(PLT)\" : \"bl%?\\t%a0\"; }" [(set_attr "type" "call")] @@ -7745,6 +7754,13 @@ && !arm_is_long_call_p (SYMBOL_REF_DECL (operands[1]))" "* { + rtx op = operands[1]; + + /* Switch mode now when possible. */ + if (SYMBOL_REF_DECL (op) && !TREE_PUBLIC (SYMBOL_REF_DECL (op)) + && arm_arch5 && arm_change_mode_p (SYMBOL_REF_DECL (op))) + return NEED_PLT_RELOC ? \"blx%?\\t%a0(PLT)\" : \"blx%?\\t(%a0)\"; + return NEED_PLT_RELOC ? \"bl%?\\t%a1(PLT)\" : \"bl%?\\t%a1\"; }" [(set_attr "type" "call")] diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/config/arm/arm.opt e/gcc/gcc/config/arm/arm.opt --- d/gcc/gcc/config/arm/arm.opt 2014-11-13 12:15:32.000000000 +0100 +++ e/gcc/gcc/config/arm/arm.opt 2014-11-13 14:07:44.000000000 +0100 @@ -186,7 +186,7 @@ Target RejectNegative Joined UInteger Va 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 diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/config/arm/arm-protos.h e/gcc/gcc/config/arm/arm-protos.h --- d/gcc/gcc/config/arm/arm-protos.h 2014-11-13 12:21:36.000000000 +0100 +++ e/gcc/gcc/config/arm/arm-protos.h 2014-11-13 13:07:47.000000000 +0100 @@ -207,6 +207,8 @@ extern int arm_dllexport_p (tree); extern int arm_dllimport_p (tree); extern void arm_mark_dllexport (tree); extern void arm_mark_dllimport (tree); +extern bool arm_change_mode_p (tree); +extern tree arm_valid_target_attribute_tree (tree, struct gcc_options *); #endif extern void arm_pr_long_calls (struct cpp_reader *); @@ -297,6 +299,8 @@ extern bool arm_autoinc_modes_ok_p (mach 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); diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/doc/extend.texi e/gcc/gcc/doc/extend.texi --- d/gcc/gcc/doc/extend.texi 2014-11-12 13:58:42.000000000 +0100 +++ e/gcc/gcc/doc/extend.texi 2014-11-13 14:53:49.000000000 +0100 @@ -3953,10 +3953,24 @@ You can either use multiple strings to specify multiple options, or separate the options with a comma (@samp{,}). -The @code{target} attribute is presently implemented for -i386/x86_64, PowerPC, and Nios II targets only. +The @code{target} attribute is implemented for +ARM, i386/x86_64, PowerPC, and Nios II targets. The options supported are specific to each target. +for ARM, the following options are allowed: + +@table @samp +@item thumb +@cindex @code{target("thumb")} attribute +Force Thumb1 Thumb2 code generation depending on the architecture. + +@item arm +@cindex @code{target("arm")} attribute +Force ARM code generation. +@end table + +Functions from different modes can be inlined , unless the callee has asm statements. + On the 386, the following options are allowed: @table @samp @@ -17728,8 +17742,8 @@ function. The parenthesis around the op @xref{Function Attributes}, for more information about the @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. +The @code{#pragma GCC target} pragma is implemented for +ARM, i386/x86_64, PowerPC, and Nios II targets. @end table @table @code diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/doc/invoke.texi e/gcc/gcc/doc/invoke.texi --- d/gcc/gcc/doc/invoke.texi 2014-11-18 08:51:29.000000000 +0100 +++ e/gcc/gcc/doc/invoke.texi 2014-11-18 08:49:02.000000000 +0100 @@ -12982,6 +12982,10 @@ that executes in ARM state, but the defa 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
2014-09-23 Christian Bruel <christian.br...@st.com> * gcc.target/arm/attr_arm.c: New test. * gcc.target/arm/attr_thumb.c: New test. * gcc.target/arm/attr_thumb-static.c: New test. diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/testsuite/gcc.target/arm/attr_arm.c e/gcc/gcc/testsuite/gcc.target/arm/attr_arm.c --- d/gcc/gcc/testsuite/gcc.target/arm/attr_arm.c 1970-01-01 01:00:00.000000000 +0100 +++ e/gcc/gcc/testsuite/gcc.target/arm/attr_arm.c 2014-11-18 10:56:51.000000000 +0100 @@ -0,0 +1,13 @@ +/* Check that attribute target arm is recogniwed. */ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_arm_ok } */ +/* { dg-options "-O2" } */ +/* { dg-final { scan-assembler ".arm" } } */ +/* { dg-final { scan-assembler-not "ite" } } */ + +int __attribute__((target("arm"))) +foo(int a) +{ + return a ? 1 : 5; +} + diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/testsuite/gcc.target/arm/attr_arm-err.c e/gcc/gcc/testsuite/gcc.target/arm/attr_arm-err.c --- d/gcc/gcc/testsuite/gcc.target/arm/attr_arm-err.c 1970-01-01 01:00:00.000000000 +0100 +++ e/gcc/gcc/testsuite/gcc.target/arm/attr_arm-err.c 2014-11-18 10:56:19.000000000 +0100 @@ -0,0 +1,12 @@ +/* Check that attribute target arm is rejected for M profile. */ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_arm_ok } */ +/* { dg-add-options arm_arch_v6m } */ + +int __attribute__((target("arm"))) +foo(int a) +{ /* { dg-error "does not support" } */ + return a ? 1 : 5; +} + + diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/testsuite/gcc.target/arm/attr_thumb.c e/gcc/gcc/testsuite/gcc.target/arm/attr_thumb.c --- d/gcc/gcc/testsuite/gcc.target/arm/attr_thumb.c 1970-01-01 01:00:00.000000000 +0100 +++ e/gcc/gcc/testsuite/gcc.target/arm/attr_thumb.c 2014-11-18 11:02:25.000000000 +0100 @@ -0,0 +1,13 @@ +/* Check that attribute target thumb is recogniwed. */ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_thumb2_ok } */ +/* { dg-options "-O2" } */ +/* { dg-final { scan-assembler ".thumb" } } */ +/* { dg-final { scan-assembler "ite" } } */ + +int __attribute__((target("thumb"))) +foo(int a) +{ + return a ? 1 : 5; +} + diff '--exclude=ChangeLog*' '--exclude=.svn' '--exclude=*~' '--exclude=#*#' -rupN d/gcc/gcc/testsuite/gcc.target/arm/attr_thumb-static.c e/gcc/gcc/testsuite/gcc.target/arm/attr_thumb-static.c --- d/gcc/gcc/testsuite/gcc.target/arm/attr_thumb-static.c 1970-01-01 01:00:00.000000000 +0100 +++ e/gcc/gcc/testsuite/gcc.target/arm/attr_thumb-static.c 2014-11-18 11:02:22.000000000 +0100 @@ -0,0 +1,24 @@ +/* Check that a change mode to a static function is correctly handled. */ +/* { dg-do run } */ +/* { dg-require-effective-target arm_thumb1_ok } */ + +static void + __attribute__((__noinline__)) +foo (void) +{ + __asm__ (""); +} + +static void +__attribute__((__noinline__)) +__attribute__((target("thumb"))) +bar (void) +{ + __asm__ (""); +} + +int main() +{ + foo(); + bar(); +}