From 390b17c28c10ab2b6752cb94b150c831762575fc Mon Sep 17 00:00:00 2001 From: Richard Earnshaw Date: Thu, 6 Aug 2009 14:27:45 +0000 Subject: [PATCH] Merge ARM/hard_vfp_branch to trunk From-SVN: r150525 --- gcc/ChangeLog | 136 +++ gcc/calls.c | 2 +- gcc/config/arm/arm-protos.h | 4 +- gcc/config/arm/arm.c | 1065 +++++++++++++++++- gcc/config/arm/arm.h | 100 +- gcc/config/arm/bpabi.h | 14 +- gcc/config/arm/t-arm-elf | 7 + gcc/config/sparc/sparc.c | 10 +- gcc/doc/invoke.texi | 5 - gcc/doc/tm.texi | 12 + gcc/explow.c | 4 +- gcc/expr.h | 2 +- gcc/optabs.c | 3 +- gcc/target-def.h | 2 + gcc/target.h | 4 + gcc/targhooks.c | 6 + gcc/targhooks.h | 1 + gcc/testsuite/ChangeLog.ARM | 26 + gcc/testsuite/gcc.dg/builtin-apply2.c | 1 + gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp | 35 + gcc/testsuite/gcc.target/arm/aapcs/abitest.h | 118 ++ gcc/testsuite/gcc.target/arm/aapcs/vfp1.c | 17 + gcc/testsuite/gcc.target/arm/aapcs/vfp10.c | 38 + gcc/testsuite/gcc.target/arm/aapcs/vfp11.c | 39 + gcc/testsuite/gcc.target/arm/aapcs/vfp12.c | 38 + gcc/testsuite/gcc.target/arm/aapcs/vfp13.c | 39 + gcc/testsuite/gcc.target/arm/aapcs/vfp14.c | 24 + gcc/testsuite/gcc.target/arm/aapcs/vfp2.c | 19 + gcc/testsuite/gcc.target/arm/aapcs/vfp3.c | 21 + gcc/testsuite/gcc.target/arm/aapcs/vfp4.c | 20 + gcc/testsuite/gcc.target/arm/aapcs/vfp5.c | 30 + gcc/testsuite/gcc.target/arm/aapcs/vfp6.c | 30 + gcc/testsuite/gcc.target/arm/aapcs/vfp7.c | 37 + gcc/testsuite/gcc.target/arm/aapcs/vfp8.c | 37 + gcc/testsuite/gcc.target/arm/aapcs/vfp9.c | 38 + gcc/testsuite/gcc.target/arm/eabi1.c | 71 +- gcc/testsuite/gcc.target/arm/mmx-1.c | 1 + gcc/testsuite/lib/target-supports.exp | 15 + 38 files changed, 1943 insertions(+), 128 deletions(-) create mode 100644 gcc/testsuite/ChangeLog.ARM create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/abitest.h create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp1.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp10.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp11.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp12.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp13.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp14.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp2.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp3.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp4.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp5.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp6.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp7.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp8.c create mode 100644 gcc/testsuite/gcc.target/arm/aapcs/vfp9.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 09c03076221..5fb34a87dee 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,139 @@ +2009-08-06 Richard Earnshaw + + Merge ARM/hard_vfp_branch to trunk. + + 2009-08-04 Richard Earnshaw + + * arm.c (libcall_eq): New function. + (libcall_hash): New function. + (add_libcall): New function. + (arm_libcall_uses_aapcs_base): New function. + (arm_libcall_value): Use arm_libcall_uses_aapcs_base to check for + libcalls using the base PCS. + (arm_init_cumulative_args): Likewise. + + 2009-07-20 Joseph Myers + + * config/arm/arm.c (arm_libcall_value, arm_init_cumulative_args): + Use base ABI for conversion libfuncs between HFmode and SFmode. + + 2009-05-12 Joseph Myers + + * config/arm/arm.c (aapcs_vfp_sub_candidate): Use V2SImode and + V4SImode as representatives of all 64-bit and 128-bit vector + types. Allow vector types without vector modes. + (aapcs_vfp_is_call_or_return_candidate): Handle vector types + without vector modes like BLKmode. + (aapcs_vfp_allocate): Handle TImode for non-TARGET_NEON like + BLKmode. Avoid unsupported vector modes or TImode moves for + non-TARGET_NEON. + (aapcs_vfp_allocate_return_reg): Likewise. + (arm_vector_mode_supported_p): Only support V2SImode, V4HImode and + V8QImode if TARGET_NEON || TARGET_IWMMXT. + + 2009-05-12 Joseph Myers + + * config/arm/arm.c (arm_handle_pcs_attribute): New. + (arm_get_pcs_model): Pass attribute arguments to + arm_pcs_from_attribute. + (arm_init_cumulative_args): Use base AAPCS for conversions from + floating-point types to DImode. + (arm_attribute_table): Add pcs attribute. + (arm_handle_pcs_attribute): New. + * config/arm/bpabi.h (DECLARE_LIBRARY_RENAMES): When renaming + conversions from floating-point types to DImode, also declare them + to use base AAPCS and declare functions they call to use base + AAPCS and their RTABI names. + + 2009-05-12 Joseph Myers + + * doc/invoke.texi (-mfloat-abi=@var{name}): Remove statement about + -mfloat-abi=hard not being supported for VFP. + + 2009-05-11 Kazu Hirata + + * config/sparc/sparc.c (sparc_emit_float_lib_cmp): Pass a libcall + SYMBOL_REF to hard_libcall_value. + + 2009-03-05 Joseph Myers + Richard Earnshaw + + * config/arm/arm.c (aapcs_layout_arg): Once a co-processor argument + has been put on the stack, all remaining co-processory arguments for + that co-processor also go on the stack. + + 2009-03-05 Joseph Myers + + * config/arm/arm.c (arm_return_in_memory): Handle returning + vectors of suitable size in registers also for AAPCS case. + + 2009-01-13 Richard Earnshaw + + * doc/tm.texi (TARGET_LIBCALL_VALUE): Add missing end statement. + + 2008-12-09 Richard Earnshaw + + ARM Hard-VFP calling convention + * target-def.h (TARGET_LIBCALL_VALUE): New hook. + * target.h (gcc_target): Add libcall_value to table of call hooks. + * targhooks.h (default_libcall_value): Default implementation. + * targhooks.c (default_libcall_value): Likewise. + * doc/tm.texi (TARGET_LIBCALL_VALUE): Document it. + * optabs.c (expand_unop): Use it. + * expr.h (hard_libcall_value): Pass the function RTX through. + * calls.c (emit_library_call_value_1): Update call to + hard_libcall_value. + * explow.c (hard_libcall_value): Use new target hook. + * testsuite/lib/target-supports.exp + (check_effective_target_arm_hard_vfp_ok): New hook. + (check_effective_target_arm_neon_ok): Improve test for neon + availability. + * testsuite/gcc.target/arm/eabi1.c: Only run test in base variant. + * config/arm/arm.c: Include cgraph.h + (TARGET_FUNCTION_VALUE): Override default hook. + (arm_pcs_default): New variable. + (arm_override_options): Don't fault hard calling convention with VFP. + Add support for AAPCS variants. + (arm_function_value): Make static. Handle AAPCS variants. + (arm_libcall_value): New function. + (arm_apply_result_size): Handle VFP registers in results. + (arm_return_in_memory): Rework all AAPCS variants; handle hard-vfp + conventions. + (pcs_attribute_args): New variable. + (arm_pcs_from_attribute): New function. + (arm_get_pcs_model): New function. + (aapcs_vfp_cum_init): New function. + (aapcs_vfp_sub_candidate): New function. + (aapcs_vfp_is_return_candidate): New function. + (aapcs_vfp_is_call_candidate): New function. + (aapcs_vfp_allocate): New function. + (aapcs_vfp_allocate_return_reg): New function. + (aapcs_vfp_advance): New function. + (aapcs_cp_arg_layout): New variable. + (aapcs_select_call_coproc): New function. + (aapcs_select_return_coproc): New function. + (aapcs_allocate_return_reg): New function. + (aapcs_libcall_value): New function. + (aapcs_layout_arg): New function. + (arm_init_cumulative_args): Initialize AAPCS args data. + (arm_function_arg): Handle AAPCS variants using new interface. + (arm_arg_parital_bytes): Likewise. + (arm_function_arg_advance): New function. + (arm_function_ok_for_sibcall): Ensure that sibling calls agree on + calling conventions. + (arm_setup_incoming_varargs): Handle new AAPCS args data. + * arm.h (NUM_VFP_ARG_REGS): Define. + (LIBCALL_VALUE): Update. + (FUNCTION_VALUE): Delete. + (FUNCTION_VALUE_REGNO_P): Add VFP regs. + (arm_pcs): New enum. + (CUMULATIVE_ARGS): New data to support AAPCS argument marshalling. + (FUNCTION_ARG_ADVANCE): Call arm_function_arg_advance. + (FUNCTION_ARG_REGNO_P): Add VFP regs. + * arm-protos.h (arm_function_arg_advance): Add. + (aapcs_libcall_value): Add. + (arm_function_value): Delete. + 2009-08-06 Uros Bizjak H.J. Lu diff --git a/gcc/calls.c b/gcc/calls.c index 6d186c581c3..7ad5b099db1 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -3805,7 +3805,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, cse'ing of library calls could delete a call and leave the pop. */ NO_DEFER_POP; valreg = (mem_value == 0 && outmode != VOIDmode - ? hard_libcall_value (outmode) : NULL_RTX); + ? hard_libcall_value (outmode, orgfun) : NULL_RTX); /* Stack must be properly aligned now. */ gcc_assert (!(stack_pointer_delta diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 07772eb8c88..ed70926e9a2 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -151,13 +151,15 @@ extern bool arm_output_addr_const_extra (FILE *, rtx); #if defined TREE_CODE extern rtx arm_function_arg (CUMULATIVE_ARGS *, enum machine_mode, tree, int); +extern void arm_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); extern void arm_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree); extern bool arm_pad_arg_upward (enum machine_mode, const_tree); extern bool arm_pad_reg_upward (enum machine_mode, tree, int); extern bool arm_needs_doubleword_align (enum machine_mode, tree); -extern rtx arm_function_value(const_tree, const_tree); #endif extern int arm_apply_result_size (void); +extern rtx aapcs_libcall_value (enum machine_mode); #endif /* RTX_CODE */ diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 83db0ec1cda..1af75f19dd9 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -43,6 +43,7 @@ #include "optabs.h" #include "toplev.h" #include "recog.h" +#include "cgraph.h" #include "ggc.h" #include "except.h" #include "c-pragma.h" @@ -112,6 +113,7 @@ static unsigned long arm_compute_save_reg_mask (void); static unsigned long arm_isr_value (tree); static unsigned long arm_compute_func_type (void); static tree arm_handle_fndecl_attribute (tree *, tree, tree, int, bool *); +static tree arm_handle_pcs_attribute (tree *, tree, tree, int, bool *); static tree arm_handle_isr_attribute (tree *, tree, tree, int, bool *); #if TARGET_DLLIMPORT_DECL_ATTRIBUTES static tree arm_handle_notshared_attribute (tree *, tree, tree, int, bool *); @@ -125,8 +127,13 @@ static int arm_adjust_cost (rtx, rtx, rtx, int); static int count_insns_for_constant (HOST_WIDE_INT, int); static int arm_get_strip_length (int); static bool arm_function_ok_for_sibcall (tree, tree); -static enum machine_mode arm_promote_function_mode (const_tree, enum machine_mode, - int *, const_tree, int); +static enum machine_mode arm_promote_function_mode (const_tree, + enum machine_mode, int *, + const_tree, int); +static bool arm_return_in_memory (const_tree, const_tree); +static rtx arm_function_value (const_tree, const_tree, bool); +static rtx arm_libcall_value (enum machine_mode, rtx); + static void arm_internal_label (FILE *, const char *, unsigned long); static void arm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); @@ -152,6 +159,9 @@ static void emit_constant_insn (rtx cond, rtx pattern); static rtx emit_set_insn (rtx, rtx); static int arm_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, tree, bool); +static rtx aapcs_allocate_return_reg (enum machine_mode, const_tree, + const_tree); +static int aapcs_select_return_coproc (const_tree, const_tree); #ifdef OBJECT_FORMAT_ELF static void arm_elf_asm_constructor (rtx, int) ATTRIBUTE_UNUSED; @@ -220,6 +230,8 @@ static const struct attribute_spec arm_attribute_table[] = /* Whereas these functions are always known to reside within the 26 bit addressing range. */ { "short_call", 0, 0, false, true, true, NULL }, + /* Specify the procedure call conventions for a function. */ + { "pcs", 1, 1, false, true, true, arm_handle_pcs_attribute }, /* Interrupt Service Routines have special prologue and epilogue requirements. */ { "isr", 0, 1, false, false, false, arm_handle_isr_attribute }, { "interrupt", 0, 1, false, false, false, arm_handle_isr_attribute }, @@ -305,6 +317,12 @@ static const struct attribute_spec arm_attribute_table[] = #undef TARGET_FUNCTION_OK_FOR_SIBCALL #define TARGET_FUNCTION_OK_FOR_SIBCALL arm_function_ok_for_sibcall +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE arm_function_value + +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE arm_libcall_value + #undef TARGET_ASM_OUTPUT_MI_THUNK #define TARGET_ASM_OUTPUT_MI_THUNK arm_output_mi_thunk #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK @@ -656,6 +674,8 @@ static int after_arm_reorg = 0; /* The maximum number of insns to be used when loading a constant. */ static int arm_constant_limit = 3; +static enum arm_pcs arm_pcs_default; + /* For an explanation of these variables, see final_prescan_insn below. */ int arm_ccfsm_state; /* arm_current_cc is also used for Thumb-2 cond_exec blocks. */ @@ -1644,9 +1664,6 @@ arm_override_options (void) else arm_float_abi = TARGET_DEFAULT_FLOAT_ABI; - if (arm_float_abi == ARM_FLOAT_ABI_HARD && TARGET_VFP) - sorry ("-mfloat-abi=hard and VFP"); - if (TARGET_AAPCS_BASED && (arm_fp_model == ARM_FP_MODEL_FPA)) error ("FPA is unsupported in the AAPCS"); @@ -1678,6 +1695,28 @@ arm_override_options (void) if (TARGET_SOFT_FLOAT) arm_fpu_arch = FPUTYPE_NONE; + if (TARGET_AAPCS_BASED) + { + if (arm_abi == ARM_ABI_IWMMXT) + arm_pcs_default = ARM_PCS_AAPCS_IWMMXT; + else if (arm_float_abi == ARM_FLOAT_ABI_HARD + && TARGET_HARD_FLOAT + && TARGET_VFP) + arm_pcs_default = ARM_PCS_AAPCS_VFP; + else + arm_pcs_default = ARM_PCS_AAPCS; + } + else + { + if (arm_float_abi == ARM_FLOAT_ABI_HARD && TARGET_VFP) + sorry ("-mfloat-abi=hard and VFP"); + + if (arm_abi == ARM_ABI_APCS) + arm_pcs_default = ARM_PCS_APCS; + else + arm_pcs_default = ARM_PCS_ATPCS; + } + /* For arm2/3 there is no need to do any scheduling if there is only a floating point emulator, or we are doing software floating-point. */ if ((TARGET_SOFT_FLOAT @@ -3071,14 +3110,19 @@ arm_canonicalize_comparison (enum rtx_code code, enum machine_mode mode, /* Define how to find the value returned by a function. */ -rtx -arm_function_value(const_tree type, const_tree func) +static rtx +arm_function_value(const_tree type, const_tree func, + bool outgoing ATTRIBUTE_UNUSED) { enum machine_mode mode; int unsignedp ATTRIBUTE_UNUSED; rtx r ATTRIBUTE_UNUSED; mode = TYPE_MODE (type); + + if (TARGET_AAPCS_BASED) + return aapcs_allocate_return_reg (mode, type, func); + /* Promote integer types. */ if (INTEGRAL_TYPE_P (type)) mode = arm_promote_function_mode (type, mode, &unsignedp, func, 1); @@ -3095,7 +3139,88 @@ arm_function_value(const_tree type, const_tree func) } } - return LIBCALL_VALUE(mode); + return LIBCALL_VALUE (mode); +} + +static int +libcall_eq (const void *p1, const void *p2) +{ + return rtx_equal_p ((const_rtx) p1, (const_rtx) p2); +} + +static hashval_t +libcall_hash (const void *p1) +{ + return hash_rtx ((const_rtx) p1, VOIDmode, NULL, NULL, FALSE); +} + +static void +add_libcall (htab_t htab, rtx libcall) +{ + *htab_find_slot (htab, libcall, INSERT) = libcall; +} + +static bool +arm_libcall_uses_aapcs_base (rtx libcall) +{ + static bool init_done = false; + static htab_t libcall_htab; + + if (!init_done) + { + init_done = true; + + libcall_htab = htab_create (31, libcall_hash, libcall_eq, + NULL); + add_libcall (libcall_htab, + convert_optab_libfunc (sfloat_optab, SFmode, SImode)); + add_libcall (libcall_htab, + convert_optab_libfunc (sfloat_optab, DFmode, SImode)); + add_libcall (libcall_htab, + convert_optab_libfunc (sfloat_optab, SFmode, DImode)); + add_libcall (libcall_htab, + convert_optab_libfunc (sfloat_optab, DFmode, DImode)); + + add_libcall (libcall_htab, + convert_optab_libfunc (ufloat_optab, SFmode, SImode)); + add_libcall (libcall_htab, + convert_optab_libfunc (ufloat_optab, DFmode, SImode)); + add_libcall (libcall_htab, + convert_optab_libfunc (ufloat_optab, SFmode, DImode)); + add_libcall (libcall_htab, + convert_optab_libfunc (ufloat_optab, DFmode, DImode)); + + add_libcall (libcall_htab, + convert_optab_libfunc (sext_optab, SFmode, HFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (trunc_optab, HFmode, SFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (sfix_optab, DImode, DFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (ufix_optab, DImode, DFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (sfix_optab, DImode, SFmode)); + add_libcall (libcall_htab, + convert_optab_libfunc (ufix_optab, DImode, SFmode)); + } + + return libcall && htab_find (libcall_htab, libcall) != NULL; +} + +rtx +arm_libcall_value (enum machine_mode mode, rtx libcall) +{ + if (TARGET_AAPCS_BASED && arm_pcs_default != ARM_PCS_AAPCS + && GET_MODE_CLASS (mode) == MODE_FLOAT) + { + /* The following libcalls return their result in integer registers, + even though they return a floating point value. */ + if (arm_libcall_uses_aapcs_base (libcall)) + return gen_rtx_REG (mode, ARG_REGISTER(1)); + + } + + return LIBCALL_VALUE (mode); } /* Determine the amount of memory needed to store the possible return @@ -3105,10 +3230,12 @@ arm_apply_result_size (void) { int size = 16; - if (TARGET_ARM) + if (TARGET_32BIT) { if (TARGET_HARD_FLOAT_ABI) { + if (TARGET_VFP) + size += 32; if (TARGET_FPA) size += 12; if (TARGET_MAVERICK) @@ -3121,27 +3248,56 @@ arm_apply_result_size (void) return size; } -/* Decide whether a type should be returned in memory (true) - or in a register (false). This is called as the target hook - TARGET_RETURN_IN_MEMORY. */ +/* Decide whether TYPE should be returned in memory (true) + or in a register (false). FNTYPE is the type of the function making + the call. */ static bool -arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) +arm_return_in_memory (const_tree type, const_tree fntype) { HOST_WIDE_INT size; - size = int_size_in_bytes (type); + size = int_size_in_bytes (type); /* Negative if not fixed size. */ + + if (TARGET_AAPCS_BASED) + { + /* Simple, non-aggregate types (ie not including vectors and + complex) are always returned in a register (or registers). + We don't care about which register here, so we can short-cut + some of the detail. */ + if (!AGGREGATE_TYPE_P (type) + && TREE_CODE (type) != VECTOR_TYPE + && TREE_CODE (type) != COMPLEX_TYPE) + return false; + + /* Any return value that is no larger than one word can be + returned in r0. */ + if (((unsigned HOST_WIDE_INT) size) <= UNITS_PER_WORD) + return false; + + /* Check any available co-processors to see if they accept the + type as a register candidate (VFP, for example, can return + some aggregates in consecutive registers). These aren't + available if the call is variadic. */ + if (aapcs_select_return_coproc (type, fntype) >= 0) + return false; + + /* Vector values should be returned using ARM registers, not + memory (unless they're over 16 bytes, which will break since + we only have four call-clobbered registers to play with). */ + if (TREE_CODE (type) == VECTOR_TYPE) + return (size < 0 || size > (4 * UNITS_PER_WORD)); + + /* The rest go in memory. */ + return true; + } - /* Vector values should be returned using ARM registers, not memory (unless - they're over 16 bytes, which will break since we only have four - call-clobbered registers to play with). */ if (TREE_CODE (type) == VECTOR_TYPE) return (size < 0 || size > (4 * UNITS_PER_WORD)); if (!AGGREGATE_TYPE_P (type) && - !(TARGET_AAPCS_BASED && TREE_CODE (type) == COMPLEX_TYPE)) - /* All simple types are returned in registers. - For AAPCS, complex types are treated the same as aggregates. */ - return 0; + (TREE_CODE (type) != VECTOR_TYPE)) + /* All simple types are returned in registers. */ + return false; if (arm_abi != ARM_ABI_APCS) { @@ -3158,7 +3314,7 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) the aggregate is either huge or of variable size, and in either case we will want to return it via memory and not in a register. */ if (size < 0 || size > UNITS_PER_WORD) - return 1; + return true; if (TREE_CODE (type) == RECORD_TYPE) { @@ -3178,18 +3334,18 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) continue; if (field == NULL) - return 0; /* An empty structure. Allowed by an extension to ANSI C. */ + return false; /* An empty structure. Allowed by an extension to ANSI C. */ /* Check that the first field is valid for returning in a register. */ /* ... Floats are not allowed */ if (FLOAT_TYPE_P (TREE_TYPE (field))) - return 1; + return true; /* ... Aggregates that are not themselves valid for returning in a register are not allowed. */ if (arm_return_in_memory (TREE_TYPE (field), NULL_TREE)) - return 1; + return true; /* Now check the remaining fields, if any. Only bitfields are allowed, since they are not addressable. */ @@ -3201,10 +3357,10 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) continue; if (!DECL_BIT_FIELD_TYPE (field)) - return 1; + return true; } - return 0; + return false; } if (TREE_CODE (type) == UNION_TYPE) @@ -3221,18 +3377,18 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) continue; if (FLOAT_TYPE_P (TREE_TYPE (field))) - return 1; + return true; if (arm_return_in_memory (TREE_TYPE (field), NULL_TREE)) - return 1; + return true; } - return 0; + return false; } #endif /* not ARM_WINCE */ /* Return all other types in memory. */ - return 1; + return true; } /* Indicate whether or not words of a double are in big-endian order. */ @@ -3257,14 +3413,749 @@ arm_float_words_big_endian (void) return 1; } +const struct pcs_attribute_arg +{ + const char *arg; + enum arm_pcs value; +} pcs_attribute_args[] = + { + {"aapcs", ARM_PCS_AAPCS}, + {"aapcs-vfp", ARM_PCS_AAPCS_VFP}, + {"aapcs-iwmmxt", ARM_PCS_AAPCS_IWMMXT}, + {"atpcs", ARM_PCS_ATPCS}, + {"apcs", ARM_PCS_APCS}, + {NULL, ARM_PCS_UNKNOWN} + }; + +static enum arm_pcs +arm_pcs_from_attribute (tree attr) +{ + const struct pcs_attribute_arg *ptr; + const char *arg; + + /* Get the value of the argument. */ + if (TREE_VALUE (attr) == NULL_TREE + || TREE_CODE (TREE_VALUE (attr)) != STRING_CST) + return ARM_PCS_UNKNOWN; + + arg = TREE_STRING_POINTER (TREE_VALUE (attr)); + + /* Check it against the list of known arguments. */ + for (ptr = pcs_attribute_args; ptr->arg != NULL; ptr++) + if (streq (arg, ptr->arg)) + return ptr->value; + + /* An unrecognized interrupt type. */ + return ARM_PCS_UNKNOWN; +} + +/* Get the PCS variant to use for this call. TYPE is the function's type + specification, DECL is the specific declartion. DECL may be null if + the call could be indirect or if this is a library call. */ +static enum arm_pcs +arm_get_pcs_model (const_tree type, const_tree decl) +{ + bool user_convention = false; + enum arm_pcs user_pcs = arm_pcs_default; + tree attr; + + gcc_assert (type); + + attr = lookup_attribute ("pcs", TYPE_ATTRIBUTES (type)); + if (attr) + { + user_pcs = arm_pcs_from_attribute (TREE_VALUE (attr)); + user_convention = true; + } + + if (TARGET_AAPCS_BASED) + { + /* Detect varargs functions. These always use the base rules + (no argument is ever a candidate for a co-processor + register). */ + bool base_rules = (TYPE_ARG_TYPES (type) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (type))) + != void_type_node)); + + if (user_convention) + { + if (user_pcs > ARM_PCS_AAPCS_LOCAL) + sorry ("Non-AAPCS derived PCS variant"); + else if (base_rules && user_pcs != ARM_PCS_AAPCS) + error ("Variadic functions must use the base AAPCS variant"); + } + + if (base_rules) + return ARM_PCS_AAPCS; + else if (user_convention) + return user_pcs; + else if (decl && flag_unit_at_a_time) + { + /* Local functions never leak outside this compilation unit, + so we are free to use whatever conventions are + appropriate. */ + /* FIXME: remove CONST_CAST_TREE when cgraph is constified. */ + struct cgraph_local_info *i = cgraph_local_info (CONST_CAST_TREE(decl)); + if (i && i->local) + return ARM_PCS_AAPCS_LOCAL; + } + } + else if (user_convention && user_pcs != arm_pcs_default) + sorry ("PCS variant"); + + /* For everything else we use the target's default. */ + return arm_pcs_default; +} + + +static void +aapcs_vfp_cum_init (CUMULATIVE_ARGS *pcum ATTRIBUTE_UNUSED, + const_tree fntype ATTRIBUTE_UNUSED, + rtx libcall ATTRIBUTE_UNUSED, + const_tree fndecl ATTRIBUTE_UNUSED) +{ + /* Record the unallocated VFP registers. */ + pcum->aapcs_vfp_regs_free = (1 << NUM_VFP_ARG_REGS) - 1; + pcum->aapcs_vfp_reg_alloc = 0; +} + +/* Walk down the type tree of TYPE counting consecutive base elements. + If *MODEP is VOIDmode, then set it to the first valid floating point + type. If a non-floating point type is found, or if a floating point + type that doesn't match a non-VOIDmode *MODEP is found, then return -1, + otherwise return the count in the sub-tree. */ +static int +aapcs_vfp_sub_candidate (const_tree type, enum machine_mode *modep) +{ + enum machine_mode mode; + HOST_WIDE_INT size; + + switch (TREE_CODE (type)) + { + case REAL_TYPE: + mode = TYPE_MODE (type); + if (mode != DFmode && mode != SFmode) + return -1; + + if (*modep == VOIDmode) + *modep = mode; + + if (*modep == mode) + return 1; + + break; + + case COMPLEX_TYPE: + mode = TYPE_MODE (TREE_TYPE (type)); + if (mode != DFmode && mode != SFmode) + return -1; + + if (*modep == VOIDmode) + *modep = mode; + + if (*modep == mode) + return 2; + + break; + + case VECTOR_TYPE: + /* Use V2SImode and V4SImode as representatives of all 64-bit + and 128-bit vector types, whether or not those modes are + supported with the present options. */ + size = int_size_in_bytes (type); + switch (size) + { + case 8: + mode = V2SImode; + break; + case 16: + mode = V4SImode; + break; + default: + return -1; + } + + if (*modep == VOIDmode) + *modep = mode; + + /* Vector modes are considered to be opaque: two vectors are + equivalent for the purposes of being homogeneous aggregates + if they are the same size. */ + if (*modep == mode) + return 1; + + break; + + case ARRAY_TYPE: + { + int count; + tree index = TYPE_DOMAIN (type); + + /* Can't handle incomplete types. */ + if (!COMPLETE_TYPE_P(type)) + return -1; + + count = aapcs_vfp_sub_candidate (TREE_TYPE (type), modep); + if (count == -1 + || !index + || !TYPE_MAX_VALUE (index) + || !host_integerp (TYPE_MAX_VALUE (index), 1) + || !TYPE_MIN_VALUE (index) + || !host_integerp (TYPE_MIN_VALUE (index), 1) + || count < 0) + return -1; + + count *= (1 + tree_low_cst (TYPE_MAX_VALUE (index), 1) + - tree_low_cst (TYPE_MIN_VALUE (index), 1)); + + /* There must be no padding. */ + if (!host_integerp (TYPE_SIZE (type), 1) + || (tree_low_cst (TYPE_SIZE (type), 1) + != count * GET_MODE_BITSIZE (*modep))) + return -1; + + return count; + } + + case RECORD_TYPE: + { + int count = 0; + int sub_count; + tree field; + + /* Can't handle incomplete types. */ + if (!COMPLETE_TYPE_P(type)) + return -1; + + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + + sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep); + if (sub_count < 0) + return -1; + count += sub_count; + } + + /* There must be no padding. */ + if (!host_integerp (TYPE_SIZE (type), 1) + || (tree_low_cst (TYPE_SIZE (type), 1) + != count * GET_MODE_BITSIZE (*modep))) + return -1; + + return count; + } + + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + /* These aren't very interesting except in a degenerate case. */ + int count = 0; + int sub_count; + tree field; + + /* Can't handle incomplete types. */ + if (!COMPLETE_TYPE_P(type)) + return -1; + + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + + sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep); + if (sub_count < 0) + return -1; + count = count > sub_count ? count : sub_count; + } + + /* There must be no padding. */ + if (!host_integerp (TYPE_SIZE (type), 1) + || (tree_low_cst (TYPE_SIZE (type), 1) + != count * GET_MODE_BITSIZE (*modep))) + return -1; + + return count; + } + + default: + break; + } + + return -1; +} + +static bool +aapcs_vfp_is_call_or_return_candidate (enum machine_mode mode, const_tree type, + int *base_mode, + int *count) +{ + if (GET_MODE_CLASS (mode) == MODE_FLOAT + || GET_MODE_CLASS (mode) == MODE_VECTOR_INT + || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) + { + *count = 1; + *base_mode = mode; + return true; + } + else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) + { + *count = 2; + *base_mode = (mode == DCmode ? DFmode : SFmode); + return true; + } + else if (type && (mode == BLKmode || TREE_CODE (type) == VECTOR_TYPE)) + { + enum machine_mode aggregate_mode = VOIDmode; + int ag_count = aapcs_vfp_sub_candidate (type, &aggregate_mode); + + if (ag_count > 0 && ag_count <= 4) + { + *count = ag_count; + *base_mode = aggregate_mode; + return true; + } + } + return false; +} + +static bool +aapcs_vfp_is_return_candidate (enum arm_pcs pcs_variant, + enum machine_mode mode, const_tree type) +{ + int count ATTRIBUTE_UNUSED; + int ag_mode ATTRIBUTE_UNUSED; + + if (!(pcs_variant == ARM_PCS_AAPCS_VFP + || (pcs_variant == ARM_PCS_AAPCS_LOCAL + && TARGET_32BIT && TARGET_VFP && TARGET_HARD_FLOAT))) + return false; + return aapcs_vfp_is_call_or_return_candidate (mode, type, &ag_mode, &count); +} + +static bool +aapcs_vfp_is_call_candidate (CUMULATIVE_ARGS *pcum, enum machine_mode mode, + const_tree type) +{ + if (!(pcum->pcs_variant == ARM_PCS_AAPCS_VFP + || (pcum->pcs_variant == ARM_PCS_AAPCS_LOCAL + && TARGET_32BIT && TARGET_VFP && TARGET_HARD_FLOAT))) + return false; + return aapcs_vfp_is_call_or_return_candidate (mode, type, + &pcum->aapcs_vfp_rmode, + &pcum->aapcs_vfp_rcount); +} + +static bool +aapcs_vfp_allocate (CUMULATIVE_ARGS *pcum, enum machine_mode mode, + const_tree type ATTRIBUTE_UNUSED) +{ + int shift = GET_MODE_SIZE (pcum->aapcs_vfp_rmode) / GET_MODE_SIZE (SFmode); + unsigned mask = (1 << (shift * pcum->aapcs_vfp_rcount)) - 1; + int regno; + + for (regno = 0; regno < NUM_VFP_ARG_REGS; regno += shift) + if (((pcum->aapcs_vfp_regs_free >> regno) & mask) == mask) + { + pcum->aapcs_vfp_reg_alloc = mask << regno; + if (mode == BLKmode || (mode == TImode && !TARGET_NEON)) + { + int i; + int rcount = pcum->aapcs_vfp_rcount; + int rshift = shift; + enum machine_mode rmode = pcum->aapcs_vfp_rmode; + rtx par; + if (!TARGET_NEON) + { + /* Avoid using unsupported vector modes. */ + if (rmode == V2SImode) + rmode = DImode; + else if (rmode == V4SImode) + { + rmode = DImode; + rcount *= 2; + rshift /= 2; + } + } + par = gen_rtx_PARALLEL (mode, rtvec_alloc (rcount)); + for (i = 0; i < rcount; i++) + { + rtx tmp = gen_rtx_REG (rmode, + FIRST_VFP_REGNUM + regno + i * rshift); + tmp = gen_rtx_EXPR_LIST + (VOIDmode, tmp, + GEN_INT (i * GET_MODE_SIZE (rmode))); + XVECEXP (par, 0, i) = tmp; + } + + pcum->aapcs_reg = par; + } + else + pcum->aapcs_reg = gen_rtx_REG (mode, FIRST_VFP_REGNUM + regno); + return true; + } + return false; +} + +static rtx +aapcs_vfp_allocate_return_reg (enum arm_pcs pcs_variant ATTRIBUTE_UNUSED, + enum machine_mode mode, + const_tree type ATTRIBUTE_UNUSED) +{ + if (!(pcs_variant == ARM_PCS_AAPCS_VFP + || (pcs_variant == ARM_PCS_AAPCS_LOCAL + && TARGET_32BIT && TARGET_VFP && TARGET_HARD_FLOAT))) + return false; + if (mode == BLKmode || (mode == TImode && !TARGET_NEON)) + { + int count; + int ag_mode; + int i; + rtx par; + int shift; + + aapcs_vfp_is_call_or_return_candidate (mode, type, &ag_mode, &count); + + if (!TARGET_NEON) + { + if (ag_mode == V2SImode) + ag_mode = DImode; + else if (ag_mode == V4SImode) + { + ag_mode = DImode; + count *= 2; + } + } + shift = GET_MODE_SIZE(ag_mode) / GET_MODE_SIZE(SFmode); + par = gen_rtx_PARALLEL (mode, rtvec_alloc (count)); + for (i = 0; i < count; i++) + { + rtx tmp = gen_rtx_REG (ag_mode, FIRST_VFP_REGNUM + i * shift); + tmp = gen_rtx_EXPR_LIST (VOIDmode, tmp, + GEN_INT (i * GET_MODE_SIZE (ag_mode))); + XVECEXP (par, 0, i) = tmp; + } + + return par; + } + + return gen_rtx_REG (mode, FIRST_VFP_REGNUM); +} + +static void +aapcs_vfp_advance (CUMULATIVE_ARGS *pcum ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + const_tree type ATTRIBUTE_UNUSED) +{ + pcum->aapcs_vfp_regs_free &= ~pcum->aapcs_vfp_reg_alloc; + pcum->aapcs_vfp_reg_alloc = 0; + return; +} + +#define AAPCS_CP(X) \ + { \ + aapcs_ ## X ## _cum_init, \ + aapcs_ ## X ## _is_call_candidate, \ + aapcs_ ## X ## _allocate, \ + aapcs_ ## X ## _is_return_candidate, \ + aapcs_ ## X ## _allocate_return_reg, \ + aapcs_ ## X ## _advance \ + } + +/* Table of co-processors that can be used to pass arguments in + registers. Idealy no arugment should be a candidate for more than + one co-processor table entry, but the table is processed in order + and stops after the first match. If that entry then fails to put + the argument into a co-processor register, the argument will go on + the stack. */ +static struct +{ + /* Initialize co-processor related state in CUMULATIVE_ARGS structure. */ + void (*cum_init) (CUMULATIVE_ARGS *, const_tree, rtx, const_tree); + + /* Return true if an argument of mode MODE (or type TYPE if MODE is + BLKmode) is a candidate for this co-processor's registers; this + function should ignore any position-dependent state in + CUMULATIVE_ARGS and only use call-type dependent information. */ + bool (*is_call_candidate) (CUMULATIVE_ARGS *, enum machine_mode, const_tree); + + /* Return true if the argument does get a co-processor register; it + should set aapcs_reg to an RTX of the register allocated as is + required for a return from FUNCTION_ARG. */ + bool (*allocate) (CUMULATIVE_ARGS *, enum machine_mode, const_tree); + + /* Return true if a result of mode MODE (or type TYPE if MODE is + BLKmode) is can be returned in this co-processor's registers. */ + bool (*is_return_candidate) (enum arm_pcs, enum machine_mode, const_tree); + + /* Allocate and return an RTX element to hold the return type of a + call, this routine must not fail and will only be called if + is_return_candidate returned true with the same parameters. */ + rtx (*allocate_return_reg) (enum arm_pcs, enum machine_mode, const_tree); + + /* Finish processing this argument and prepare to start processing + the next one. */ + void (*advance) (CUMULATIVE_ARGS *, enum machine_mode, const_tree); +} aapcs_cp_arg_layout[ARM_NUM_COPROC_SLOTS] = + { + AAPCS_CP(vfp) + }; + +#undef AAPCS_CP + +static int +aapcs_select_call_coproc (CUMULATIVE_ARGS *pcum, enum machine_mode mode, + tree type) +{ + int i; + + for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++) + if (aapcs_cp_arg_layout[i].is_call_candidate (pcum, mode, type)) + return i; + + return -1; +} + +static int +aapcs_select_return_coproc (const_tree type, const_tree fntype) +{ + /* We aren't passed a decl, so we can't check that a call is local. + However, it isn't clear that that would be a win anyway, since it + might limit some tail-calling opportunities. */ + enum arm_pcs pcs_variant; + + if (fntype) + { + const_tree fndecl = NULL_TREE; + + if (TREE_CODE (fntype) == FUNCTION_DECL) + { + fndecl = fntype; + fntype = TREE_TYPE (fntype); + } + + pcs_variant = arm_get_pcs_model (fntype, fndecl); + } + else + pcs_variant = arm_pcs_default; + + if (pcs_variant != ARM_PCS_AAPCS) + { + int i; + + for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++) + if (aapcs_cp_arg_layout[i].is_return_candidate (pcs_variant, + TYPE_MODE (type), + type)) + return i; + } + return -1; +} + +static rtx +aapcs_allocate_return_reg (enum machine_mode mode, const_tree type, + const_tree fntype) +{ + /* We aren't passed a decl, so we can't check that a call is local. + However, it isn't clear that that would be a win anyway, since it + might limit some tail-calling opportunities. */ + enum arm_pcs pcs_variant; + int unsignedp ATTRIBUTE_UNUSED; + + if (fntype) + { + const_tree fndecl = NULL_TREE; + + if (TREE_CODE (fntype) == FUNCTION_DECL) + { + fndecl = fntype; + fntype = TREE_TYPE (fntype); + } + + pcs_variant = arm_get_pcs_model (fntype, fndecl); + } + else + pcs_variant = arm_pcs_default; + + /* Promote integer types. */ + if (type && INTEGRAL_TYPE_P (type)) + mode = arm_promote_function_mode (type, mode, &unsignedp, fntype, 1); + + if (pcs_variant != ARM_PCS_AAPCS) + { + int i; + + for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++) + if (aapcs_cp_arg_layout[i].is_return_candidate (pcs_variant, mode, + type)) + return aapcs_cp_arg_layout[i].allocate_return_reg (pcs_variant, + mode, type); + } + + /* Promotes small structs returned in a register to full-word size + for big-endian AAPCS. */ + if (type && arm_return_in_msb (type)) + { + HOST_WIDE_INT size = int_size_in_bytes (type); + if (size % UNITS_PER_WORD != 0) + { + size += UNITS_PER_WORD - size % UNITS_PER_WORD; + mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0); + } + } + + return gen_rtx_REG (mode, R0_REGNUM); +} + +rtx +aapcs_libcall_value (enum machine_mode mode) +{ + return aapcs_allocate_return_reg (mode, NULL_TREE, NULL_TREE); +} + +/* Lay out a function argument using the AAPCS rules. The rule + numbers referred to here are those in the AAPCS. */ +static void +aapcs_layout_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode, + tree type, int named) +{ + int nregs, nregs2; + int ncrn; + + /* We only need to do this once per argument. */ + if (pcum->aapcs_arg_processed) + return; + + pcum->aapcs_arg_processed = true; + + /* Special case: if named is false then we are handling an incoming + anonymous argument which is on the stack. */ + if (!named) + return; + + /* Is this a potential co-processor register candidate? */ + if (pcum->pcs_variant != ARM_PCS_AAPCS) + { + int slot = aapcs_select_call_coproc (pcum, mode, type); + pcum->aapcs_cprc_slot = slot; + + /* We don't have to apply any of the rules from part B of the + preparation phase, these are handled elsewhere in the + compiler. */ + + if (slot >= 0) + { + /* A Co-processor register candidate goes either in its own + class of registers or on the stack. */ + if (!pcum->aapcs_cprc_failed[slot]) + { + /* C1.cp - Try to allocate the argument to co-processor + registers. */ + if (aapcs_cp_arg_layout[slot].allocate (pcum, mode, type)) + return; + + /* C2.cp - Put the argument on the stack and note that we + can't assign any more candidates in this slot. We also + need to note that we have allocated stack space, so that + we won't later try to split a non-cprc candidate between + core registers and the stack. */ + pcum->aapcs_cprc_failed[slot] = true; + pcum->can_split = false; + } + + /* We didn't get a register, so this argument goes on the + stack. */ + gcc_assert (pcum->can_split == false); + return; + } + } + + /* C3 - For double-word aligned arguments, round the NCRN up to the + next even number. */ + ncrn = pcum->aapcs_ncrn; + if ((ncrn & 1) && arm_needs_doubleword_align (mode, type)) + ncrn++; + + nregs = ARM_NUM_REGS2(mode, type); + + /* Sigh, this test should really assert that nregs > 0, but a GCC + extension allows empty structs and then gives them empty size; it + then allows such a structure to be passed by value. For some of + the code below we have to pretend that such an argument has + non-zero size so that we 'locate' it correctly either in + registers or on the stack. */ + gcc_assert (nregs >= 0); + + nregs2 = nregs ? nregs : 1; + + /* C4 - Argument fits entirely in core registers. */ + if (ncrn + nregs2 <= NUM_ARG_REGS) + { + pcum->aapcs_reg = gen_rtx_REG (mode, ncrn); + pcum->aapcs_next_ncrn = ncrn + nregs; + return; + } + + /* C5 - Some core registers left and there are no arguments already + on the stack: split this argument between the remaining core + registers and the stack. */ + if (ncrn < NUM_ARG_REGS && pcum->can_split) + { + pcum->aapcs_reg = gen_rtx_REG (mode, ncrn); + pcum->aapcs_next_ncrn = NUM_ARG_REGS; + pcum->aapcs_partial = (NUM_ARG_REGS - ncrn) * UNITS_PER_WORD; + return; + } + + /* C6 - NCRN is set to 4. */ + pcum->aapcs_next_ncrn = NUM_ARG_REGS; + + /* C7,C8 - arugment goes on the stack. We have nothing to do here. */ + return; +} + /* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a function whose data type is FNTYPE. For a library call, FNTYPE is NULL. */ void arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype, - rtx libname ATTRIBUTE_UNUSED, + rtx libname, tree fndecl ATTRIBUTE_UNUSED) { + /* Long call handling. */ + if (fntype) + pcum->pcs_variant = arm_get_pcs_model (fntype, fndecl); + else + pcum->pcs_variant = arm_pcs_default; + + if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL) + { + if (arm_libcall_uses_aapcs_base (libname)) + pcum->pcs_variant = ARM_PCS_AAPCS; + + pcum->aapcs_ncrn = pcum->aapcs_next_ncrn = 0; + pcum->aapcs_reg = NULL_RTX; + pcum->aapcs_partial = 0; + pcum->aapcs_arg_processed = false; + pcum->aapcs_cprc_slot = -1; + pcum->can_split = true; + + if (pcum->pcs_variant != ARM_PCS_AAPCS) + { + int i; + + for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++) + { + pcum->aapcs_cprc_failed[i] = false; + aapcs_cp_arg_layout[i].cum_init (pcum, fntype, libname, fndecl); + } + } + return; + } + + /* Legacy ABIs */ + /* On the ARM, the offset starts at 0. */ pcum->nregs = 0; pcum->iwmmxt_nregs = 0; @@ -3318,6 +4209,17 @@ arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode, { int nregs; + /* Handle the special case quickly. Pick an arbitrary value for op2 of + a call insn (op3 of a call_value insn). */ + if (mode == VOIDmode) + return const0_rtx; + + if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL) + { + aapcs_layout_arg (pcum, mode, type, named); + return pcum->aapcs_reg; + } + /* Varargs vectors are treated the same as long long. named_count avoids having to change the way arm handles 'named' */ if (TARGET_IWMMXT_ABI @@ -3359,10 +4261,16 @@ arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode, static int arm_arg_partial_bytes (CUMULATIVE_ARGS *pcum, enum machine_mode mode, - tree type, bool named ATTRIBUTE_UNUSED) + tree type, bool named) { int nregs = pcum->nregs; + if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL) + { + aapcs_layout_arg (pcum, mode, type, named); + return pcum->aapcs_partial; + } + if (TARGET_IWMMXT_ABI && arm_vector_mode_supported_p (mode)) return 0; @@ -3374,6 +4282,39 @@ arm_arg_partial_bytes (CUMULATIVE_ARGS *pcum, enum machine_mode mode, return 0; } +void +arm_function_arg_advance (CUMULATIVE_ARGS *pcum, enum machine_mode mode, + tree type, bool named) +{ + if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL) + { + aapcs_layout_arg (pcum, mode, type, named); + + if (pcum->aapcs_cprc_slot >= 0) + { + aapcs_cp_arg_layout[pcum->aapcs_cprc_slot].advance (pcum, mode, + type); + pcum->aapcs_cprc_slot = -1; + } + + /* Generic stuff. */ + pcum->aapcs_arg_processed = false; + pcum->aapcs_ncrn = pcum->aapcs_next_ncrn; + pcum->aapcs_reg = NULL_RTX; + pcum->aapcs_partial = 0; + } + else + { + pcum->nargs += 1; + if (arm_vector_mode_supported_p (mode) + && pcum->named_count > pcum->nargs + && TARGET_IWMMXT_ABI) + pcum->iwmmxt_nregs += 1; + else + pcum->nregs += ARM_NUM_REGS2 (mode, type); + } +} + /* Variable sized types are passed by reference. This is a GCC extension to the ARM ABI. */ @@ -3490,6 +4431,21 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags, return NULL_TREE; } +/* Handle a "pcs" attribute; arguments as in struct + attribute_spec.handler. */ +static tree +arm_handle_pcs_attribute (tree *node ATTRIBUTE_UNUSED, tree name, tree args, + int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) +{ + if (arm_pcs_from_attribute (args) == ARM_PCS_UNKNOWN) + { + warning (OPT_Wattributes, "%qs attribute ignored", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } + return NULL_TREE; +} + #if TARGET_DLLIMPORT_DECL_ATTRIBUTES /* Handle the "notshared" attribute. This attribute is another way of requesting hidden visibility. ARM's compiler supports @@ -3651,7 +4607,7 @@ arm_is_long_call_p (tree decl) /* Return nonzero if it is ok to make a tail-call to DECL. */ static bool -arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) +arm_function_ok_for_sibcall (tree decl, tree exp) { unsigned long func_type; @@ -3684,6 +4640,21 @@ arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED) if (IS_INTERRUPT (func_type)) return false; + if (!VOID_TYPE_P (TREE_TYPE (DECL_RESULT (cfun->decl)))) + { + /* Check that the return value locations are the same. For + example that we aren't returning a value from the sibling in + a VFP register but then need to transfer it to a core + register. */ + rtx a, b; + + a = arm_function_value (TREE_TYPE (exp), decl, false); + b = arm_function_value (TREE_TYPE (DECL_RESULT (cfun->decl)), + cfun->decl, false); + if (!rtx_equal_p (a, b)) + return false; + } + /* Never tailcall if function may be called with a misaligned SP. */ if (IS_STACKALIGN (func_type)) return false; @@ -18948,19 +19919,24 @@ arm_output_load_gr (rtx *operands) that way. */ static void -arm_setup_incoming_varargs (CUMULATIVE_ARGS *cum, +arm_setup_incoming_varargs (CUMULATIVE_ARGS *pcum, enum machine_mode mode, tree type, int *pretend_size, int second_time ATTRIBUTE_UNUSED) { - int nregs = cum->nregs; - if (nregs & 1 - && ARM_DOUBLEWORD_ALIGN - && arm_needs_doubleword_align (mode, type)) - nregs++; - + int nregs; + cfun->machine->uses_anonymous_args = 1; + if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL) + { + nregs = pcum->aapcs_ncrn; + if ((nregs & 1) && arm_needs_doubleword_align (mode, type)) + nregs++; + } + else + nregs = pcum->nregs; + if (nregs < NUM_ARG_REGS) *pretend_size = (NUM_ARG_REGS - nregs) * UNITS_PER_WORD; } @@ -19357,9 +20333,10 @@ arm_vector_mode_supported_p (enum machine_mode mode) || mode == V16QImode || mode == V4SFmode || mode == V2DImode)) return true; - if ((mode == V2SImode) - || (mode == V4HImode) - || (mode == V8QImode)) + if ((TARGET_NEON || TARGET_IWMMXT) + && ((mode == V2SImode) + || (mode == V4HImode) + || (mode == V8QImode))) return true; return false; diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index 082b5fabc3a..59d35dd6833 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -893,6 +893,9 @@ extern int arm_structure_size_boundary; /* The number of (integer) argument register available. */ #define NUM_ARG_REGS 4 +/* And similarly for the VFP. */ +#define NUM_VFP_ARG_REGS 16 + /* Return the register number of the N'th (integer) argument. */ #define ARG_REGISTER(N) (N - 1) @@ -1502,9 +1505,10 @@ do { \ /* Define how to find the value returned by a library function assuming the value has mode MODE. */ -#define LIBCALL_VALUE(MODE) \ - (TARGET_32BIT && TARGET_HARD_FLOAT_ABI && TARGET_FPA \ - && GET_MODE_CLASS (MODE) == MODE_FLOAT \ +#define LIBCALL_VALUE(MODE) \ + (TARGET_AAPCS_BASED ? aapcs_libcall_value (MODE) \ + : (TARGET_32BIT && TARGET_HARD_FLOAT_ABI && TARGET_FPA \ + && GET_MODE_CLASS (MODE) == MODE_FLOAT) \ ? gen_rtx_REG (MODE, FIRST_FPA_REGNUM) \ : TARGET_32BIT && TARGET_HARD_FLOAT_ABI && TARGET_MAVERICK \ && GET_MODE_CLASS (MODE) == MODE_FLOAT \ @@ -1513,22 +1517,16 @@ do { \ ? gen_rtx_REG (MODE, FIRST_IWMMXT_REGNUM) \ : gen_rtx_REG (MODE, ARG_REGISTER (1))) -/* Define how to find the value returned by a function. - VALTYPE is the data type of the value (as a tree). - If the precise function being called is known, FUNC is its FUNCTION_DECL; - otherwise, FUNC is 0. */ -#define FUNCTION_VALUE(VALTYPE, FUNC) \ - arm_function_value (VALTYPE, FUNC); - -/* 1 if N is a possible register number for a function value. - On the ARM, only r0 and f0 can return results. */ -/* On a Cirrus chip, mvf0 can return results. */ -#define FUNCTION_VALUE_REGNO_P(REGNO) \ - ((REGNO) == ARG_REGISTER (1) \ - || (TARGET_32BIT && ((REGNO) == FIRST_CIRRUS_FP_REGNUM) \ - && TARGET_HARD_FLOAT_ABI && TARGET_MAVERICK) \ - || ((REGNO) == FIRST_IWMMXT_REGNUM && TARGET_IWMMXT_ABI) \ - || (TARGET_32BIT && ((REGNO) == FIRST_FPA_REGNUM) \ +/* 1 if REGNO is a possible register number for a function value. */ +#define FUNCTION_VALUE_REGNO_P(REGNO) \ + ((REGNO) == ARG_REGISTER (1) \ + || (TARGET_AAPCS_BASED && TARGET_32BIT \ + && TARGET_VFP && TARGET_HARD_FLOAT \ + && (REGNO) == FIRST_VFP_REGNUM) \ + || (TARGET_32BIT && ((REGNO) == FIRST_CIRRUS_FP_REGNUM) \ + && TARGET_HARD_FLOAT_ABI && TARGET_MAVERICK) \ + || ((REGNO) == FIRST_IWMMXT_REGNUM && TARGET_IWMMXT_ABI) \ + || (TARGET_32BIT && ((REGNO) == FIRST_FPA_REGNUM) \ && TARGET_HARD_FLOAT_ABI && TARGET_FPA)) /* Amount of memory needed for an untyped call to save all possible return @@ -1631,9 +1629,27 @@ machine_function; that is in text_section. */ extern GTY(()) rtx thumb_call_via_label[14]; +/* The number of potential ways of assigning to a co-processor. */ +#define ARM_NUM_COPROC_SLOTS 1 + +/* Enumeration of procedure calling standard variants. We don't really + support all of these yet. */ +enum arm_pcs +{ + ARM_PCS_AAPCS, /* Base standard AAPCS. */ + ARM_PCS_AAPCS_VFP, /* Use VFP registers for floating point values. */ + ARM_PCS_AAPCS_IWMMXT, /* Use iWMMXT registers for vectors. */ + /* This must be the last AAPCS variant. */ + ARM_PCS_AAPCS_LOCAL, /* Private call within this compilation unit. */ + ARM_PCS_ATPCS, /* ATPCS. */ + ARM_PCS_APCS, /* APCS (legacy Linux etc). */ + ARM_PCS_UNKNOWN +}; + +/* We can't define this inside a generator file because it needs enum + machine_mode. */ /* A C type for declaring a variable that is used as the first argument of - `FUNCTION_ARG' and other related values. For some target machines, the - type `int' suffices and can hold the number of bytes of argument so far. */ + `FUNCTION_ARG' and other related values. */ typedef struct { /* This is the number of registers of arguments scanned so far. */ @@ -1642,9 +1658,33 @@ typedef struct int iwmmxt_nregs; int named_count; int nargs; - int can_split; + /* Which procedure call variant to use for this call. */ + enum arm_pcs pcs_variant; + + /* AAPCS related state tracking. */ + int aapcs_arg_processed; /* No need to lay out this argument again. */ + int aapcs_cprc_slot; /* Index of co-processor rules to handle + this argument, or -1 if using core + registers. */ + int aapcs_ncrn; + int aapcs_next_ncrn; + rtx aapcs_reg; /* Register assigned to this argument. */ + int aapcs_partial; /* How many bytes are passed in regs (if + split between core regs and stack. + Zero otherwise. */ + int aapcs_cprc_failed[ARM_NUM_COPROC_SLOTS]; + int can_split; /* Argument can be split between core regs + and the stack. */ + /* Private data for tracking VFP register allocation */ + unsigned aapcs_vfp_regs_free; + unsigned aapcs_vfp_reg_alloc; + int aapcs_vfp_rcount; + /* Can't include insn-modes.h because this header is needed before we + generate it. */ + int /* enum machine_mode */ aapcs_vfp_rmode; } CUMULATIVE_ARGS; + /* Define where to put the arguments to a function. Value is zero to push the argument on the stack, or a hard register in which to store the argument. @@ -1688,13 +1728,7 @@ typedef struct of mode MODE and data type TYPE. (TYPE is null for libcalls where that information may not be available.) */ #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ - (CUM).nargs += 1; \ - if (arm_vector_mode_supported_p (MODE) \ - && (CUM).named_count > (CUM).nargs \ - && TARGET_IWMMXT_ABI) \ - (CUM).iwmmxt_nregs += 1; \ - else \ - (CUM).nregs += ARM_NUM_REGS2 (MODE, TYPE) + arm_function_arg_advance (&(CUM), (MODE), (TYPE), (NAMED)) /* If defined, a C expression that gives the alignment boundary, in bits, of an argument with the specified mode and type. If it is not defined, @@ -1706,9 +1740,11 @@ typedef struct /* 1 if N is a possible register number for function argument passing. On the ARM, r0-r3 are used to pass args. */ -#define FUNCTION_ARG_REGNO_P(REGNO) \ - (IN_RANGE ((REGNO), 0, 3) \ - || (TARGET_IWMMXT_ABI \ +#define FUNCTION_ARG_REGNO_P(REGNO) \ + (IN_RANGE ((REGNO), 0, 3) \ + || (TARGET_AAPCS_BASED && TARGET_VFP && TARGET_HARD_FLOAT \ + && IN_RANGE ((REGNO), FIRST_VFP_REGNUM, FIRST_VFP_REGNUM + 15)) \ + || (TARGET_IWMMXT_ABI \ && IN_RANGE ((REGNO), FIRST_IWMMXT_REGNUM, FIRST_IWMMXT_REGNUM + 9))) diff --git a/gcc/config/arm/bpabi.h b/gcc/config/arm/bpabi.h index 4d2974e32da..bc0c62f401e 100644 --- a/gcc/config/arm/bpabi.h +++ b/gcc/config/arm/bpabi.h @@ -90,16 +90,22 @@ #define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (muldi3, lmul) #endif #ifdef L_fixdfdi -#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixdfdi, d2lz) +#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixdfdi, d2lz) \ + extern DWtype __fixdfdi (DFtype) __attribute__((pcs("aapcs"))); \ + extern UDWtype __fixunsdfdi (DFtype) __asm__("__aeabi_d2ulz") __attribute__((pcs("aapcs"))); #endif #ifdef L_fixunsdfdi -#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixunsdfdi, d2ulz) +#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixunsdfdi, d2ulz) \ + extern UDWtype __fixunsdfdi (DFtype) __attribute__((pcs("aapcs"))); #endif #ifdef L_fixsfdi -#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixsfdi, f2lz) +#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixsfdi, f2lz) \ + extern DWtype __fixsfdi (SFtype) __attribute__((pcs("aapcs"))); \ + extern UDWtype __fixunssfdi (SFtype) __asm__("__aeabi_f2ulz") __attribute__((pcs("aapcs"))); #endif #ifdef L_fixunssfdi -#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixunssfdi, f2ulz) +#define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (fixunssfdi, f2ulz) \ + extern UDWtype __fixunssfdi (SFtype) __attribute__((pcs("aapcs"))); #endif #ifdef L_floatdidf #define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (floatdidf, l2d) diff --git a/gcc/config/arm/t-arm-elf b/gcc/config/arm/t-arm-elf index 6a90d331148..8be87c8d947 100644 --- a/gcc/config/arm/t-arm-elf +++ b/gcc/config/arm/t-arm-elf @@ -46,6 +46,13 @@ MULTILIB_MATCHES = #MULTILIB_MATCHES += march?armv7=mcpu?cortex-r4 #MULTILIB_MATCHES += march?armv7=mcpu?cortex-m3 +# Not quite true. We can support hard-vfp calling in Thumb2, but how do we +# express that here? Also, we really need architecture v5e or later +# (mcrr etc). +MULTILIB_OPTIONS += mfloat-abi=hard +MULTILIB_DIRNAMES += fpu +MULTILIB_EXCEPTIONS += *mthumb/*mfloat-abi=hard* + # MULTILIB_OPTIONS += mcpu=ep9312 # MULTILIB_DIRNAMES += ep9312 # MULTILIB_EXCEPTIONS += *mthumb/*mcpu=ep9312* diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index 52cbe703458..033980bd441 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -6255,7 +6255,7 @@ rtx sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison) { const char *qpfunc; - rtx slot0, slot1, result, tem, tem2; + rtx slot0, slot1, result, tem, tem2, libfunc; enum machine_mode mode; enum rtx_code new_comparison; @@ -6318,7 +6318,8 @@ sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison) emit_move_insn (slot1, y); } - emit_library_call (gen_rtx_SYMBOL_REF (Pmode, qpfunc), LCT_NORMAL, + libfunc = gen_rtx_SYMBOL_REF (Pmode, qpfunc); + emit_library_call (libfunc, LCT_NORMAL, DImode, 2, XEXP (slot0, 0), Pmode, XEXP (slot1, 0), Pmode); @@ -6326,7 +6327,8 @@ sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison) } else { - emit_library_call (gen_rtx_SYMBOL_REF (Pmode, qpfunc), LCT_NORMAL, + libfunc = gen_rtx_SYMBOL_REF (Pmode, qpfunc); + emit_library_call (libfunc, LCT_NORMAL, SImode, 2, x, TFmode, y, TFmode); mode = SImode; @@ -6337,7 +6339,7 @@ sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison) register so reload doesn't clobber the value if it needs the return register for a spill reg. */ result = gen_reg_rtx (mode); - emit_move_insn (result, hard_libcall_value (mode)); + emit_move_insn (result, hard_libcall_value (mode, libfunc)); switch (comparison) { diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index cd0e0c2c126..774e602b06d 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -9322,11 +9322,6 @@ instructions, but still uses the soft-float calling conventions. @samp{hard} allows generation of floating-point instructions and uses FPU-specific calling conventions. -Using @option{-mfloat-abi=hard} with VFP coprocessors is not supported. -Use @option{-mfloat-abi=softfp} with the appropriate @option{-mfpu} option -to allow the compiler to generate code that makes use of the hardware -floating-point capabilities for these CPUs. - The default depends on the specific target configuration. Note that the hard-float and soft-float ABIs are not link-compatible; you must compile your entire program with the same ABI, and link with a diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index bb4f61b9e83..34c81c9e15c 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -4387,6 +4387,18 @@ specially by the compiler and was not mentioned in the C code being compiled. @end defmac +@deftypefn {Target Hook} rtx TARGET_LIBCALL_VALUE (enum machine_mode +@var{mode}, rtx @var{fun}) +Define this hook if the back-end needs to know the name of the libcall +function in order to determine where the result should be returned. + +The mode of the result is given by @var{mode} and the name of the called +library function is given by @var{fun}. The hook should return an RTX +representing the place where the library function result will be returned. + +If this hook is not defined, then LIBCALL_VALUE will be used. +@end deftypefn + @defmac FUNCTION_VALUE_REGNO_P (@var{regno}) A C expression that is nonzero if @var{regno} is the number of a hard register in which the values of called function may come back. diff --git a/gcc/explow.c b/gcc/explow.c index 5176d1f918f..7388a455b1d 100644 --- a/gcc/explow.c +++ b/gcc/explow.c @@ -1529,9 +1529,9 @@ hard_function_value (const_tree valtype, const_tree func, const_tree fntype, in which a scalar value of mode MODE was returned by a library call. */ rtx -hard_libcall_value (enum machine_mode mode) +hard_libcall_value (enum machine_mode mode, rtx fun) { - return LIBCALL_VALUE (mode); + return targetm.calls.libcall_value (mode, fun); } /* Look up the tree code for a given rtx code diff --git a/gcc/expr.h b/gcc/expr.h index 8e23aecb4b9..7058354a866 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -762,7 +762,7 @@ extern void probe_stack_range (HOST_WIDE_INT, rtx); /* Return an rtx that refers to the value returned by a library call in its original home. This becomes invalid if any more code is emitted. */ -extern rtx hard_libcall_value (enum machine_mode); +extern rtx hard_libcall_value (enum machine_mode, rtx); /* Return the mode desired by operand N of a particular bitfield insert/extract insn, or MAX_MACHINE_MODE if no such insn is diff --git a/gcc/optabs.c b/gcc/optabs.c index 16eb4dd3dbb..fcc1649caf3 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -3278,7 +3278,8 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, if (unoptab == ffs_optab || unoptab == clz_optab || unoptab == ctz_optab || unoptab == popcount_optab || unoptab == parity_optab) outmode - = GET_MODE (hard_libcall_value (TYPE_MODE (integer_type_node))); + = GET_MODE (hard_libcall_value (TYPE_MODE (integer_type_node), + optab_libfunc (unoptab, mode))); start_sequence (); diff --git a/gcc/target-def.h b/gcc/target-def.h index 26464ed4472..8ad6b8a52ef 100644 --- a/gcc/target-def.h +++ b/gcc/target-def.h @@ -598,6 +598,7 @@ #define TARGET_ARG_PARTIAL_BYTES hook_int_CUMULATIVE_ARGS_mode_tree_bool_0 #define TARGET_FUNCTION_VALUE default_function_value +#define TARGET_LIBCALL_VALUE default_libcall_value #define TARGET_INTERNAL_ARG_POINTER default_internal_arg_pointer #define TARGET_UPDATE_STACK_BOUNDARY NULL #define TARGET_GET_DRAP_RTX NULL @@ -620,6 +621,7 @@ TARGET_ARG_PARTIAL_BYTES, \ TARGET_INVALID_ARG_FOR_UNPROTOTYPED_FN, \ TARGET_FUNCTION_VALUE, \ + TARGET_LIBCALL_VALUE, \ TARGET_INTERNAL_ARG_POINTER, \ TARGET_UPDATE_STACK_BOUNDARY, \ TARGET_GET_DRAP_RTX, \ diff --git a/gcc/target.h b/gcc/target.h index 27fd77b7779..7c60cfb139b 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -892,6 +892,10 @@ struct gcc_target rtx (*function_value) (const_tree ret_type, const_tree fn_decl_or_type, bool outgoing); + /* Return the rtx for the result of a libcall of mode MODE, + calling the function FN_NAME. */ + rtx (*libcall_value) (enum machine_mode, rtx); + /* Return an rtx for the argument pointer incoming to the current function. */ rtx (*internal_arg_pointer) (void); diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 8c3c2ab1e24..58a9aeea403 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -605,6 +605,12 @@ default_function_value (const_tree ret_type ATTRIBUTE_UNUSED, #endif } +rtx +default_libcall_value (enum machine_mode mode, rtx fun ATTRIBUTE_UNUSED) +{ + return LIBCALL_VALUE (mode); +} + rtx default_internal_arg_pointer (void) { diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 5564a7983cf..4e5f631e76c 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -94,6 +94,7 @@ extern const char *hook_invalid_arg_for_unprototyped_fn (const_tree, const_tree, const_tree); extern bool hook_bool_const_rtx_commutative_p (const_rtx, int); extern rtx default_function_value (const_tree, const_tree, bool); +extern rtx default_libcall_value (enum machine_mode, rtx); extern rtx default_internal_arg_pointer (void); extern enum reg_class default_branch_target_register_class (void); #ifdef IRA_COVER_CLASSES diff --git a/gcc/testsuite/ChangeLog.ARM b/gcc/testsuite/ChangeLog.ARM new file mode 100644 index 00000000000..260f0db86ec --- /dev/null +++ b/gcc/testsuite/ChangeLog.ARM @@ -0,0 +1,26 @@ +2009-08-04 Richard Earnshaw + + * gcc.target/arm/mmx-1.c: Skip if using -mfloat-abi=hard. + * gcc.dg/builtin-apply2.c: Skip for ARM if using -mfloat-abi=hard. + +2009-05-12 Joseph Myers + + * gcc.target/arm/eabi1.c: Do not skip for non-base ABI variants. + (PCS): Define macro to use base AAPCS. + (decl_float, __aeabi_d2f, __aeabi_f2d): Use PCS macro. + +2009-05-11 Daniel Jacobowitz + + * lib/target-supports.exp (check_effective_target_arm_neon_ok): + Correct arm_neon.h typo. + +2009-03-06 Richard Earnshaw + + * lib/target-supports.exp (check_effective_target_hard_vfp_ok): Make + this a linkage test. + * gcc.target/arm/aapcs/aapcs.exp: New framework for testing AAPCS + argument marshalling. + * abitest.h: New file. + * vfp1.c, vfp2.c, vfp3.c, vfp4.c, vfp5.c, vfp6.c, vfp7.c: New tests. + * vfp8.c, vfp9.c, vfp10.c, vfp11.c, vfp12.c, vfp13.c, vfp14.c: New. + diff --git a/gcc/testsuite/gcc.dg/builtin-apply2.c b/gcc/testsuite/gcc.dg/builtin-apply2.c index bc49a645809..a303e3ddb79 100644 --- a/gcc/testsuite/gcc.dg/builtin-apply2.c +++ b/gcc/testsuite/gcc.dg/builtin-apply2.c @@ -1,5 +1,6 @@ /* { dg-do run } */ /* { dg-skip-if "Variadic funcs have all args on stack. Normal funcs have args in registers." { "avr-*-*" } { "*" } { "" } } */ +/* { dg-skip-if "Variadic funcs use Base AAPCS. Normal funcs use VFP variant." { "arm*-*-*" } { "-mfloat-abi=hard" } { "" } } */ /* PR target/12503 */ /* Origin: */ diff --git a/gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp b/gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp new file mode 100644 index 00000000000..fcc4333464c --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp @@ -0,0 +1,35 @@ +# Copyright (C) 1997, 2004, 2006, 2007 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# GCC testsuite that uses the `dg.exp' driver. + +# Exit immediately if this isn't an ARM target. +if ![istarget arm*-*-*] then { + return +} + +# Load support procs. +load_lib gcc-dg.exp + +# Initialize `dg'. +dg-init + +# Main loop. +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \ + "" "" + +# All done. +dg-finish diff --git a/gcc/testsuite/gcc.target/arm/aapcs/abitest.h b/gcc/testsuite/gcc.target/arm/aapcs/abitest.h new file mode 100644 index 00000000000..f6474a988a0 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/abitest.h @@ -0,0 +1,118 @@ +#define IN_FRAMEWORK + +#ifdef VFP +#define D0 0 +#define D1 8 +#define D2 16 +#define D3 24 +#define D4 32 +#define D5 40 +#define D6 48 +#define D7 56 + +#define S0 64 +#define S1 68 +#define S2 72 +#define S3 76 +#define S4 80 +#define S5 84 +#define S6 88 +#define S7 92 +#define S8 86 +#define S9 100 +#define S10 104 +#define S11 108 +#define S12 112 +#define S13 116 +#define S14 120 +#define S15 124 + +#define R0 128 +#define R1 132 +#define R2 136 +#define R3 140 + +#define STACK 144 + +#else + +#define R0 0 +#define R1 4 +#define R2 8 +#define R3 12 + +#define STACK 16 + +#endif + +extern void abort (void); + +__attribute__((naked)) void dumpregs () __asm("myfunc"); +__attribute__((naked)) void dumpregs () +{ + asm( + "mov ip, sp\n\t" + "stmfd sp!, {r0-r3}\n\t" +#ifdef VFP + "fstmdbs sp!, {s0-s15}\n\t" + "fstmdbd sp!, {d0-d7}\n\t" +#endif + "mov r0, sp\n\t" + "stmfd sp!, {ip, r14}\n\t" + "bl testfunc\n\t" + "ldmfd sp!, {r0, r14}\n\t" + "mov sp, r0\n\t" + "bx lr"); +} + + +#define LAST_ARG(type,val,offset) { type __x = val; if (memcmp(&__x, stack+offset, sizeof(type)) != 0) abort(); } +#define ARG(type,val,offset) LAST_ARG(type, val, offset) +#define ANON(type,val,offset) LAST_ARG(type, val, offset) +#define LAST_ANON(type,val,offset) LAST_ARG(type, val, offset) +#define DOTS + +void testfunc(char* stack) +{ +#include TESTFILE + return; +} + +#undef LAST_ARG +#undef ARG +#undef DOTS +#undef ANON +#undef LAST_ANON +#define LAST_ARG(type,val,offset) type +#define ARG(type,val,offset) LAST_ARG(type, val, offset), +#define DOTS ... +#define ANON(type,val, offset) +#define LAST_ANON(type,val, offset) + +#ifndef MYFUNCTYPE +#define MYFUNCTYPE void +#endif + +MYFUNCTYPE myfunc( +#include TESTFILE +); + +#undef LAST_ARG +#undef ARG +#undef DOTS +#undef ANON +#undef LAST_ANON +#define LAST_ARG(type,val,offset) val +#define ARG(type,val,offset) LAST_ARG(type, val, offset), +#define DOTS +#define LAST_ANON(type,val,offset) LAST_ARG(type, val, offset) +#define ANON(type,val,offset) LAST_ARG(type, val, offset), + + +int main() +{ + myfunc( +#include TESTFILE +); + return 0; +} diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp1.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp1.c new file mode 100644 index 00000000000..380a3244dd4 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp1.c @@ -0,0 +1,17 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp1.c" +#include "abitest.h" + +#else + ARG(int, 4, R0) + ARG(double, 4.0, D0) + LAST_ARG(int, 3, R1) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp10.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp10.c new file mode 100644 index 00000000000..58561aac9fc --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp10.c @@ -0,0 +1,38 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp10.c" + +__complex__ x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +struct z +{ + double x[4]; +}; + +struct z a = { 5.0, 6.0, 7.0, 8.0 }; +struct z b = { 9.0, 10.0, 11.0, 12.0 }; + +#include "abitest.h" +#else + /* A variadic function passes using the base ABI */ + ARG(double, 11.0, R0) + DOTS + ANON(struct z, a, R2) + ANON(struct z, b, STACK+24) + LAST_ANON(double, 0.5, STACK+56) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp11.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp11.c new file mode 100644 index 00000000000..2c143bafb06 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp11.c @@ -0,0 +1,39 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp11.c" + +__complex__ x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +struct z +{ + double x[4]; +}; + +struct z a = { 5.0, 6.0, 7.0, 8.0 }; +struct z b = { 9.0, 10.0, 11.0, 12.0 }; + +#define MYFUNCTYPE struct y + +#include "abitest.h" +#else + ARG(int, 7, R1) + ARG(struct y, v, R2) + ARG(struct z, a, D0) + ARG(struct z, b, D4) + LAST_ARG(double, 0.5, STACK+8) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp12.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp12.c new file mode 100644 index 00000000000..7b6b4cd54aa --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp12.c @@ -0,0 +1,38 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp12.c" + +__complex__ x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +struct z +{ + double x[4]; +}; + +struct z a = { 5.0, 6.0, 7.0, 8.0 }; +struct z b = { 9.0, 10.0, 11.0, 12.0 }; + +#include "abitest.h" +#else + ARG(int, 7, R0) + ARG(struct y, v, R1) + ARG(struct z, a, D0) + ARG(double, 1.0, D4) + ARG(struct z, b, STACK+8) + LAST_ARG(double, 0.5, STACK+40) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp13.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp13.c new file mode 100644 index 00000000000..ca0c5be7c3a --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp13.c @@ -0,0 +1,39 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp13.c" + +__complex__ x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +struct z +{ + double x[4]; +}; + +struct z a = { 5.0, 6.0, 7.0, 8.0 }; +struct z b = { 9.0, 10.0, 11.0, 12.0 }; + +#include "abitest.h" +#else + ARG(int, 7, R0) + ARG(int, 9, R1) + ARG(struct z, a, D0) + ARG(double, 1.0, D4) + ARG(struct z, b, STACK) + ARG(int, 4, R2) + LAST_ARG(double, 0.5, STACK+32) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp14.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp14.c new file mode 100644 index 00000000000..b5131d7fcff --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp14.c @@ -0,0 +1,24 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp14.c" + +#include "abitest.h" +#else + ARG(double, 1.0, D0) + ARG(double, 2.0, D1) + ARG(double, 3.0, D2) + ARG(double, 4.0, D3) + ARG(double, 5.0, D4) + ARG(double, 6.0, D5) + ARG(double, 7.0, D6) + ARG(double, 8.0, D7) + ARG(double, 9.0, STACK) + LAST_ARG(double, 10.0, STACK+8) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp2.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp2.c new file mode 100644 index 00000000000..a2db349e4a2 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp2.c @@ -0,0 +1,19 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp2.c" +#include "abitest.h" + +#else + ARG(float, 1.0f, S0) + ARG(double, 4.0, D1) + ARG(float, 2.0f, S1) + ARG(double, 5.0, D2) + LAST_ARG(int, 3, R0) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp3.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp3.c new file mode 100644 index 00000000000..807292b5721 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp3.c @@ -0,0 +1,21 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp3.c" + +__complex__ x = 1.0+2.0i; + +#include "abitest.h" +#else + ARG(float, 1.0f, S0) + ARG(__complex__ double, x, D1) + ARG(float, 2.0f, S1) + ARG(double, 5.0, D3) + LAST_ARG(int, 3, R0) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp4.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp4.c new file mode 100644 index 00000000000..8bb2a5678b9 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp4.c @@ -0,0 +1,20 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp4.c" + +__complex__ float x = 1.0f + 2.0fi; +#include "abitest.h" +#else + ARG(float, 1.0f, S0) + ARG(__complex__ float, x, S1) + ARG(float, 2.0f, S3) + ARG(double, 5.0, D2) + LAST_ARG(int, 3, R0) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp5.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp5.c new file mode 100644 index 00000000000..0adc17fde11 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp5.c @@ -0,0 +1,30 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp5.c" + +__complex__ float x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +#include "abitest.h" +#else + ARG(float, 1.0f, S0) + ARG(__complex__ float, x, S1) + ARG(float, 2.0f, S3) + ARG(double, 5.0, D2) + ARG(struct y, v, R0) + LAST_ARG(int, 3, STACK) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp6.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp6.c new file mode 100644 index 00000000000..6d8df0d62ac --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp6.c @@ -0,0 +1,30 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp6.c" + +__complex__ float x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +#include "abitest.h" +#else + ARG(struct y, v, R0) + ARG(float, 1.0f, S0) + ARG(__complex__ float, x, S1) + ARG(float, 2.0f, S3) + ARG(double, 5.0, D2) + LAST_ARG(int, 3, STACK) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp7.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp7.c new file mode 100644 index 00000000000..de4bdb4c421 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp7.c @@ -0,0 +1,37 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp7.c" + +__complex__ x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +struct z +{ + double x[4]; +}; + +struct z a = { 5.0, 6.0, 7.0, 8.0 }; +struct z b = { 9.0, 10.0, 11.0, 12.0 }; + +#include "abitest.h" +#else + ARG(struct z, a, D0) + ARG(struct z, b, D4) + ARG(double, 0.5, STACK) + ARG(int, 7, R0) + LAST_ARG(struct y, v, STACK+8) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp8.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp8.c new file mode 100644 index 00000000000..7865844ebbc --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp8.c @@ -0,0 +1,37 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp8.c" + +__complex__ x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +struct z +{ + double x[4]; +}; + +struct z a = { 5.0, 6.0, 7.0, 8.0 }; +struct z b = { 9.0, 10.0, 11.0, 12.0 }; + +#include "abitest.h" +#else + ARG(int, 7, R0) + ARG(struct y, v, R1) + ARG(struct z, a, D0) + ARG(struct z, b, D4) + LAST_ARG(double, 0.5, STACK+8) +#endif diff --git a/gcc/testsuite/gcc.target/arm/aapcs/vfp9.c b/gcc/testsuite/gcc.target/arm/aapcs/vfp9.c new file mode 100644 index 00000000000..f9aa2960ca8 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/aapcs/vfp9.c @@ -0,0 +1,38 @@ +/* Test AAPCS layout (VFP variant) */ + +/* { dg-do run { target arm*-*-eabi* } } */ +/* { dg-require-effective-target arm_hard_vfp_ok } */ +/* { dg-require-effective-target arm32 } */ +/* { dg-options "-O -mfpu=vfp -mfloat-abi=hard" } */ + +#ifndef IN_FRAMEWORK +#define VFP +#define TESTFILE "vfp9.c" + +__complex__ x = 1.0+2.0i; + +struct y +{ + int p; + int q; + int r; + int s; +} v = { 1, 2, 3, 4 }; + +struct z +{ + double x[4]; +}; + +struct z a = { 5.0, 6.0, 7.0, 8.0 }; +struct z b = { 9.0, 10.0, 11.0, 12.0 }; + +#include "abitest.h" +#else + /* A variadic function passes using the base ABI */ + ARG(int, 7, R0) + DOTS + ANON(struct z, a, R2) + ANON(struct z, b, STACK+24) + LAST_ANON(double, 0.5, STACK+56) +#endif diff --git a/gcc/testsuite/gcc.target/arm/eabi1.c b/gcc/testsuite/gcc.target/arm/eabi1.c index e88ba021fdc..c90f5ff0856 100644 --- a/gcc/testsuite/gcc.target/arm/eabi1.c +++ b/gcc/testsuite/gcc.target/arm/eabi1.c @@ -30,43 +30,48 @@ #include #include -#define decl_float(code, type) \ - extern type __aeabi_ ## code ## add (type, type); \ - extern type __aeabi_ ## code ## div (type, type); \ - extern type __aeabi_ ## code ## mul (type, type); \ - extern type __aeabi_ ## code ## neg (type); \ - extern type __aeabi_ ## code ## rsub (type, type); \ - extern type __aeabi_ ## code ## sub (type, type); \ - extern int __aeabi_ ## code ## cmpeq (type, type); \ - extern int __aeabi_ ## code ## cmplt (type, type); \ - extern int __aeabi_ ## code ## cmple (type, type); \ - extern int __aeabi_ ## code ## cmpge (type, type); \ - extern int __aeabi_ ## code ## cmpgt (type, type); \ - extern int __aeabi_ ## code ## cmpun (type, type); \ - extern int __aeabi_ ## code ## 2iz (type); \ - extern unsigned int __aeabi_ ## code ## 2uiz (type); \ - extern long long __aeabi_ ## code ## 2lz (type); \ - extern unsigned long long __aeabi_ ## code ## 2ulz (type); \ - extern type __aeabi_i2 ## code (int); \ - extern type __aeabi_ui2 ## code (int); \ - extern type __aeabi_l2 ## code (long long); \ - extern type __aeabi_ul2 ## code (unsigned long long); \ - \ - type code ## zero = 0.0; \ - type code ## one = 1.0; \ - type code ## two = 2.0; \ - type code ## four = 4.0; \ - type code ## minus_one = -1.0; \ - type code ## minus_two = -2.0; \ - type code ## minus_four = -4.0; \ - type code ## epsilon = 1E-32; \ - type code ## NaN = 0.0 / 0.0; +/* All these functions are defined to use the base ABI, so use the + attribute to ensure the tests use the base ABI to call them even + when the VFP ABI is otherwise in effect. */ +#define PCS __attribute__((pcs("aapcs"))) + +#define decl_float(code, type) \ + extern type __aeabi_ ## code ## add (type, type) PCS; \ + extern type __aeabi_ ## code ## div (type, type) PCS; \ + extern type __aeabi_ ## code ## mul (type, type) PCS; \ + extern type __aeabi_ ## code ## neg (type) PCS; \ + extern type __aeabi_ ## code ## rsub (type, type) PCS; \ + extern type __aeabi_ ## code ## sub (type, type) PCS; \ + extern int __aeabi_ ## code ## cmpeq (type, type) PCS; \ + extern int __aeabi_ ## code ## cmplt (type, type) PCS; \ + extern int __aeabi_ ## code ## cmple (type, type) PCS; \ + extern int __aeabi_ ## code ## cmpge (type, type) PCS; \ + extern int __aeabi_ ## code ## cmpgt (type, type) PCS; \ + extern int __aeabi_ ## code ## cmpun (type, type) PCS; \ + extern int __aeabi_ ## code ## 2iz (type) PCS; \ + extern unsigned int __aeabi_ ## code ## 2uiz (type) PCS; \ + extern long long __aeabi_ ## code ## 2lz (type) PCS; \ + extern unsigned long long __aeabi_ ## code ## 2ulz (type) PCS; \ + extern type __aeabi_i2 ## code (int) PCS; \ + extern type __aeabi_ui2 ## code (int) PCS; \ + extern type __aeabi_l2 ## code (long long) PCS; \ + extern type __aeabi_ul2 ## code (unsigned long long) PCS; \ + \ + type code ## zero = 0.0; \ + type code ## one = 1.0; \ + type code ## two = 2.0; \ + type code ## four = 4.0; \ + type code ## minus_one = -1.0; \ + type code ## minus_two = -2.0; \ + type code ## minus_four = -4.0; \ + type code ## epsilon = 1E-32; \ + type code ## NaN = 0.0 / 0.0; decl_float (d, double) decl_float (f, float) -extern float __aeabi_d2f (double); -extern double __aeabi_f2d (float); +extern float __aeabi_d2f (double) PCS; +extern double __aeabi_f2d (float) PCS; extern long long __aeabi_lmul (long long, long long); extern long long __aeabi_llsl (long long, int); extern long long __aeabi_llsr (long long, int); diff --git a/gcc/testsuite/gcc.target/arm/mmx-1.c b/gcc/testsuite/gcc.target/arm/mmx-1.c index 21cc47912c0..5d51bd7b31d 100644 --- a/gcc/testsuite/gcc.target/arm/mmx-1.c +++ b/gcc/testsuite/gcc.target/arm/mmx-1.c @@ -4,6 +4,7 @@ /* { dg-skip-if "Test is specific to the iWMMXt" { arm*-*-* } { "-mcpu=*" } { "-mcpu=iwmmxt" } } */ /* { dg-skip-if "Test is specific to the iWMMXt" { arm*-*-* } { "-mabi=*" } { "-mabi=iwmmxt" } } */ /* { dg-skip-if "Test is specific to the iWMMXt" { arm*-*-* } { "-mfloat-abi=softfp" } { "" } } */ +/* { dg-skip-if "Test is specific to the iWMMXt" { arm*-*-* } { "-mfloat-abi=hard" } { "" } } */ /* { dg-skip-if "Test is specific to the iWMMXt" { arm*-*-* } { "-march=*" } { "-march=iwmmxt" } } */ /* { dg-options "-O -mno-apcs-frame -mcpu=iwmmxt -mabi=iwmmxt" } */ /* { dg-require-effective-target arm32 } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 050292b3feb..27a537c6a08 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -1511,6 +1511,20 @@ proc check_effective_target_arm_vfp_ok { } { } } +# Return 1 if this is an ARM target supporting -mfpu=vfp +# -mfloat-abi=hard. Some multilibs may be incompatible with these +# options. + +proc check_effective_target_arm_hard_vfp_ok { } { + if { [check_effective_target_arm32] } { + return [check_no_compiler_messages arm_hard_vfp_ok executable { + int main() { return 0;} + } "-mfpu=vfp -mfloat-abi=hard"] + } else { + return 0 + } +} + # Return 1 if this is an ARM target supporting -mfpu=neon # -mfloat-abi=softfp. Some multilibs may be incompatible with these # options. @@ -1518,6 +1532,7 @@ proc check_effective_target_arm_vfp_ok { } { proc check_effective_target_arm_neon_ok { } { if { [check_effective_target_arm32] } { return [check_no_compiler_messages arm_neon_ok object { + #include "arm_neon.h" int dummy; } "-mfpu=neon -mfloat-abi=softfp"] } else { -- 2.30.2