aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog136
-rw-r--r--gcc/calls.c2
-rw-r--r--gcc/config/arm/arm-protos.h4
-rw-r--r--gcc/config/arm/arm.c1065
-rw-r--r--gcc/config/arm/arm.h100
-rw-r--r--gcc/config/arm/bpabi.h14
-rw-r--r--gcc/config/arm/t-arm-elf7
-rw-r--r--gcc/config/sparc/sparc.c10
-rw-r--r--gcc/doc/invoke.texi5
-rw-r--r--gcc/doc/tm.texi12
-rw-r--r--gcc/explow.c4
-rw-r--r--gcc/expr.h2
-rw-r--r--gcc/optabs.c3
-rw-r--r--gcc/target-def.h2
-rw-r--r--gcc/target.h4
-rw-r--r--gcc/targhooks.c6
-rw-r--r--gcc/targhooks.h1
-rw-r--r--gcc/testsuite/ChangeLog.ARM26
-rw-r--r--gcc/testsuite/gcc.dg/builtin-apply2.c1
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp35
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/abitest.h118
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp1.c17
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp10.c38
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp11.c39
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp12.c38
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp13.c39
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp14.c24
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp2.c19
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp3.c21
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp4.c20
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp5.c30
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp6.c30
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp7.c37
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp8.c37
-rw-r--r--gcc/testsuite/gcc.target/arm/aapcs/vfp9.c38
-rw-r--r--gcc/testsuite/gcc.target/arm/eabi1.c71
-rw-r--r--gcc/testsuite/gcc.target/arm/mmx-1.c1
-rw-r--r--gcc/testsuite/lib/target-supports.exp15
38 files changed, 1943 insertions, 128 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 09c0307..5fb34a8 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,139 @@
+2009-08-06 Richard Earnshaw <rearnsha@arm.com>
+
+ Merge ARM/hard_vfp_branch to trunk.
+
+ 2009-08-04 Richard Earnshaw <rearnsha@arm.com>
+
+ * 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 <joseph@codesourcery.com>
+
+ * 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 <joseph@codesourcery.com>
+
+ * 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 <joseph@codesourcery.com>
+
+ * 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 <joseph@codesourcery.com>
+
+ * doc/invoke.texi (-mfloat-abi=@var{name}): Remove statement about
+ -mfloat-abi=hard not being supported for VFP.
+
+ 2009-05-11 Kazu Hirata <kazu@codesourcery.com>
+
+ * config/sparc/sparc.c (sparc_emit_float_lib_cmp): Pass a libcall
+ SYMBOL_REF to hard_libcall_value.
+
+ 2009-03-05 Joseph Myers <joseph@codesourcery.com>
+ Richard Earnshaw <rearnsha@arm.com>
+
+ * 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 <joseph@codesourcery.com>
+
+ * 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 <rearnsha@arm.com>
+
+ * doc/tm.texi (TARGET_LIBCALL_VALUE): Add missing end statement.
+
+ 2008-12-09 Richard Earnshaw <rearnsha@arm.com>
+
+ 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 <ubizjak@gmail.com>
H.J. Lu <hongjiu.lu@intel.com>
diff --git a/gcc/calls.c b/gcc/calls.c
index 6d186c5..7ad5b09 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 07772eb..ed70926 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 83db0ec..1af75f1 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 082b5fa..59d35dd 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 4d2974e..bc0c62f 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 6a90d33..8be87c8 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 52cbe70..033980b 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 cd0e0c2..774e602 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 bb4f61b..34c81c9 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 5176d1f..7388a45 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 8e23aec..7058354 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 16eb4dd..fcc1649 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 26464ed..8ad6b8a 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 27fd77b..7c60cfb 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 8c3c2ab..58a9aee 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -606,6 +606,12 @@ default_function_value (const_tree ret_type ATTRIBUTE_UNUSED,
}
rtx
+default_libcall_value (enum machine_mode mode, rtx fun ATTRIBUTE_UNUSED)
+{
+ return LIBCALL_VALUE (mode);
+}
+
+rtx
default_internal_arg_pointer (void)
{
/* If the reg that the virtual arg pointer will be translated into is
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 5564a79..4e5f631 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 0000000..260f0db
--- /dev/null
+++ b/gcc/testsuite/ChangeLog.ARM
@@ -0,0 +1,26 @@
+2009-08-04 Richard Earnshaw <rearnsha@arm.com>
+
+ * 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 <joseph@codesourcery.com>
+
+ * 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 <dan@codesourcery.com>
+
+ * lib/target-supports.exp (check_effective_target_arm_neon_ok):
+ Correct arm_neon.h typo.
+
+2009-03-06 Richard Earnshaw <rearnsha@arm.com>
+
+ * 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 bc49a64..a303e3d 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: <pierre.nguyen-tuong@asim.lip6.fr> */
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 0000000..fcc4333
--- /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
+# <http://www.gnu.org/licenses/>.
+
+# 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 0000000..f6474a98
--- /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 0000000..380a324
--- /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 0000000..58561aa
--- /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 0000000..2c143ba
--- /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 0000000..7b6b4cd
--- /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 0000000..ca0c5be
--- /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 0000000..b5131d7
--- /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 0000000..a2db349
--- /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 0000000..807292b
--- /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 0000000..8bb2a56
--- /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 0000000..0adc17f
--- /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 0000000..6d8df0d
--- /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 0000000..de4bdb4
--- /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 0000000..7865844
--- /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 0000000..f9aa296
--- /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 e88ba02..c90f5ff 100644
--- a/gcc/testsuite/gcc.target/arm/eabi1.c
+++ b/gcc/testsuite/gcc.target/arm/eabi1.c
@@ -30,43 +30,48 @@
#include <stdlib.h>
#include <math.h>
-#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 21cc479..5d51bd7 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 050292b..27a537c 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 {