Merge ARM/hard_vfp_branch to trunk
authorrearnsha <rearnsha@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 6 Aug 2009 14:27:45 +0000 (14:27 +0000)
committerrearnsha <rearnsha@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 6 Aug 2009 14:27:45 +0000 (14:27 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@150525 138bc75d-0d04-0410-961f-82ee72b054a4

38 files changed:
gcc/ChangeLog
gcc/calls.c
gcc/config/arm/arm-protos.h
gcc/config/arm/arm.c
gcc/config/arm/arm.h
gcc/config/arm/bpabi.h
gcc/config/arm/t-arm-elf
gcc/config/sparc/sparc.c
gcc/doc/invoke.texi
gcc/doc/tm.texi
gcc/explow.c
gcc/expr.h
gcc/optabs.c
gcc/target-def.h
gcc/target.h
gcc/targhooks.c
gcc/targhooks.h
gcc/testsuite/ChangeLog.ARM [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-apply2.c
gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/abitest.h [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp11.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp12.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp13.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp14.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp4.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp5.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp6.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp7.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp8.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/aapcs/vfp9.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/eabi1.c
gcc/testsuite/gcc.target/arm/mmx-1.c
gcc/testsuite/lib/target-supports.exp

index 09c0307..5fb34a8 100644 (file)
@@ -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>
 
index 6d186c5..7ad5b09 100644 (file)
@@ -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
index 07772eb..ed70926 100644 (file)
@@ -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 */
 
index 83db0ec..1af75f1 100644 (file)
@@ -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;
index 082b5fa..59d35dd 100644 (file)
@@ -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)))
 
 \f
index 4d2974e..bc0c62f 100644 (file)
 #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)
index 6a90d33..8be87c8 100644 (file)
@@ -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*
index 52cbe70..033980b 100644 (file)
@@ -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)
     {
index cd0e0c2..774e602 100644 (file)
@@ -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
index bb4f61b..34c81c9 100644 (file)
@@ -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.
index 5176d1f..7388a45 100644 (file)
@@ -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
index 8e23aec..7058354 100644 (file)
@@ -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
index 16eb4dd..fcc1649 100644 (file)
@@ -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 ();
 
index 26464ed..8ad6b8a 100644 (file)
 #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
    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,                                                \
index 27fd77b..7c60cfb 100644 (file)
@@ -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);
index 8c3c2ab..58a9aee 100644 (file)
@@ -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
index 5564a79..4e5f631 100644 (file)
@@ -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 (file)
index 0000000..260f0db
--- /dev/null
@@ -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.
+
index bc49a64..a303e3d 100644 (file)
@@ -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 (file)
index 0000000..fcc4333
--- /dev/null
@@ -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 (file)
index 0000000..f6474a9
--- /dev/null
@@ -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 (file)
index 0000000..380a324
--- /dev/null
@@ -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 (file)
index 0000000..58561aa
--- /dev/null
@@ -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 (file)
index 0000000..2c143ba
--- /dev/null
@@ -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 (file)
index 0000000..7b6b4cd
--- /dev/null
@@ -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 (file)
index 0000000..ca0c5be
--- /dev/null
@@ -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 (file)
index 0000000..b5131d7
--- /dev/null
@@ -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 (file)
index 0000000..a2db349
--- /dev/null
@@ -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 (file)
index 0000000..807292b
--- /dev/null
@@ -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 (file)
index 0000000..8bb2a56
--- /dev/null
@@ -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 (file)
index 0000000..0adc17f
--- /dev/null
@@ -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 (file)
index 0000000..6d8df0d
--- /dev/null
@@ -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 (file)
index 0000000..de4bdb4
--- /dev/null
@@ -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 (file)
index 0000000..7865844
--- /dev/null
@@ -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 (file)
index 0000000..f9aa296
--- /dev/null
@@ -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
index e88ba02..c90f5ff 100644 (file)
 #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);
index 21cc479..5d51bd7 100644 (file)
@@ -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 } */
index 050292b..27a537c 100644 (file)
@@ -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 {