This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: ARM: __attribute__ ((reg_return))


The attached patch adds a new ARM-specific type attribute whose effect is to force a 'struct' type to be returned in registers, even when this would be contrary to the ABI. The primary purpose of this feature is to eliminate the need for assembly language wrappers in the implementation of the BPABI's __aeabi_*divmod functions. They are a special case; they return [ul]ldiv_t structures in registers r0-r3, when normally two- and four-word structures would be returned in memory. However, they could also be useful for squeezing cycles and/or code size out of code that passes lots of small structures around.

As a demonstration, I have reworked bpabi.c to use the new types. I preserve the old __gnu_*divmod_helper functions in a separate file, bpabi-compat.c, for backward compatibility's sake. I would appreciate knowing if this is necessary.

I had to invent a new target hook, TARGET_OVERRIDE_RECORD_MODE. As the name implies, its function is to allow targets to override the choice made by compute_record_mode (in stor-layout.c). Without that, a local variable whose type is lldiv_t tagged with attribute reg_return winds up (partially) in memory, even though we're going to have to copy it back to registers to return it. As long as I was messing with compute_record_mode I made MEMBER_TYPE_FORCES_BLK into a target hook (steps 1 and 2 only of the transition sequence in targhooks.c).

A final, minor issue - the code generated for __aeabi_ldivmod has a bunch of unnecessary register shuffling in it:

        push    {r4, r5, r6, r7, r8, lr}
        mov     r5, r2
        mov     r6, r3
        mov     r7, r0
        mov     r8, r1
        bl      __divdi3
        mul     r2, r5, r1
        umull   r3, r4, r5, r0
        mla     ip, r0, r6, r2
        mov     r5, r7
        add     r4, ip, r4
        mov     r6, r8
        subs    r5, r5, r3
        sbc     r6, r6, r4
        mov     r3, r6
        mov     r2, r5
        pop     {r4, r5, r6, r7, r8, lr}
        bx      lr

-- there is no reason why this could not be

        push    {r4, r5, r6, r7, r8, lr}
        mov     r5, r2
        mov     r6, r3
        mov     r7, r0
        mov     r8, r1
        bl      __divdi3
        mul     r2, r5, r1
        umull   r3, r4, r5, r0
        mla     ip, r0, r6, r2
        add     r4, ip, r4
        subs    r2, r7, r3
        sbc     r3, r8, r4
        pop     {r4, r5, r6, r7, r8, lr}
        bx      lr

It looks like one of those unfixable reload headaches, but if anyone can suggest tweaks to the ARM back end that would get rid of the register shuffling I would love to hear them.

I have built a cross-compiler to arm-aeabi with this patch. Simulator testing is in progress.

zw
	* target.h (member_forces_blkmode, override_record_mode): New hooks.
	* targhooks.c (default_member_forces_blkmode)
	(default_override_record_mode): New functions.
	* target-def.h, targhooks.h: Update to match.
	* stor-layout.c (compute_record_mode): Use targetm.member_forces_blkmode
	instead of MEMBER_TYPE_FORCES_BLK.  Call targetm.override_record_mode
	at very end.
	(layout_type): Use targetm.member_forces_blkmode.
	* doc/tm.texi: Document new target hooks; mark MEMBER_TYPE_FORCES_BLK
	as obsolete.

	* config/arm/arm-protos.h (arm_return_in_memory, arm_function_value):
	Don't declare here.
	* config/arm/arm.c (arm_handle_regreturn_attribute)
	(arm_override_record_mode): New functions.
	(arm_attributes): Add reg_return.
	(TARGET_OVERRIDE_RECORD_MODE, TARGET_FUNCTION_VALUE)
	(TARGET_RETURN_IN_MEMORY): Define.
	(arm_return_in_memory): Now static.  RECORD_TYPEs with the
	reg_return attribute are returned in registers if their TYPE_SIZE is
	no more than than 4*BITS_PER_WORD.  Recursively call this function,
	not RETURN_IN_MEMORY.
	(arm_function_value): Now static.  Abort if structures with the
	reg_return attribute do not have the appropriate TYPE_MODE.
	* config/arm/arm.h (FUNCTION_VALUE, RETURN_IN_MEMORY): Don't define.

	* config/arm/bpabi.S (__aeabi_ldivmod, __aeabi_uldivmod): Delete.
	* config/arm/bpabi.c: Define them here, using reg_return structs.
	* config/arm/bpabi-compat.c: New file. Define the old
	__gnu_ldivmod_helper and __gnu_uldivmod_helper functions here.
	* config/arm/t-bpabi (LIB1ASMFUNCS): Remove _aeabi_ldivmod and
	_aeabi_uldivmod.
	(LIB2FUNCS_EXTRA): Add bpabi-compat.c.

==================================================================
--- config/arm/arm-protos.h	(revision 128116)
+++ config/arm/arm-protos.h	(local)
@@ -41,10 +41,6 @@ extern HOST_WIDE_INT thumb_compute_initi
 extern unsigned int arm_dbx_register_number (unsigned int);
 extern void arm_output_fn_unwind (FILE *, bool);
   
-
-#ifdef TREE_CODE
-extern int arm_return_in_memory (const_tree);
-#endif
 #ifdef RTX_CODE
 extern bool arm_vector_mode_supported_p (enum machine_mode);
 extern int arm_hard_regno_mode_ok (unsigned int, enum machine_mode);
@@ -159,7 +155,6 @@ extern void arm_init_cumulative_args (CU
 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);
 
==================================================================
--- config/arm/arm.c	(revision 128116)
+++ config/arm/arm.c	(local)
@@ -108,6 +108,7 @@ 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_isr_attribute (tree *, tree, tree, int, bool *);
+static tree arm_handle_regreturn_attribute (tree *, tree, tree, int, bool *);
 #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
 static tree arm_handle_notshared_attribute (tree *, tree, tree, int, bool *);
 #endif
@@ -116,6 +117,7 @@ static void arm_output_function_prologue
 static void thumb1_output_function_prologue (FILE *, HOST_WIDE_INT);
 static int arm_comp_type_attributes (const_tree, const_tree);
 static void arm_set_default_type_attributes (tree);
+static enum machine_mode arm_override_record_mode (const_tree);
 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);
@@ -163,6 +165,8 @@ static bool arm_pass_by_reference (CUMUL
 static bool arm_promote_prototypes (const_tree);
 static bool arm_default_short_enums (void);
 static bool arm_align_anon_bitfield (void);
+static rtx arm_function_value (const_tree, const_tree, bool);
+static bool arm_return_in_memory (const_tree, const_tree);
 static bool arm_return_in_msb (const_tree);
 static bool arm_must_pass_in_stack (enum machine_mode, const_tree);
 #ifdef TARGET_UNWIND_INFO
@@ -227,6 +231,9 @@ static void arm_output_dwarf_dtprel (FIL
 #undef  TARGET_SET_DEFAULT_TYPE_ATTRIBUTES
 #define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES arm_set_default_type_attributes
 
+#undef  TARGET_OVERRIDE_RECORD_MODE
+#define TARGET_OVERRIDE_RECORD_MODE arm_override_record_mode
+
 #undef  TARGET_SCHED_ADJUST_COST
 #define TARGET_SCHED_ADJUST_COST arm_adjust_cost
 
@@ -324,9 +331,15 @@ static void arm_output_dwarf_dtprel (FIL
 #undef TARGET_CXX_CLASS_DATA_ALWAYS_COMDAT
 #define TARGET_CXX_CLASS_DATA_ALWAYS_COMDAT arm_cxx_class_data_always_comdat
 
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE arm_function_value
+
 #undef TARGET_RETURN_IN_MSB
 #define TARGET_RETURN_IN_MSB arm_return_in_msb
 
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY arm_return_in_memory
+
 #undef TARGET_MUST_PASS_IN_STACK
 #define TARGET_MUST_PASS_IN_STACK arm_must_pass_in_stack
 
@@ -2676,8 +2689,9 @@ arm_canonicalize_comparison (enum rtx_co
 
 /* Define how to find the value returned by a function.  */
 
-rtx
-arm_function_value(const_tree type, const_tree func ATTRIBUTE_UNUSED)
+static rtx
+arm_function_value(const_tree type, const_tree func ATTRIBUTE_UNUSED,
+		   bool outgoing ATTRIBUTE_UNUSED)
 {
   enum machine_mode mode;
   int unsignedp ATTRIBUTE_UNUSED;
@@ -2688,6 +2702,12 @@ arm_function_value(const_tree type, cons
   if (INTEGRAL_TYPE_P (type))
     PROMOTE_FUNCTION_MODE (mode, unsignedp, type);
 
+  /* Structures tagged with reg_return should already have the
+     appropriate integer mode.  */
+  if (AGGREGATE_TYPE_P (type)
+      && lookup_attribute ("reg_return", TYPE_ATTRIBUTES (type)))
+    gcc_assert (mode == mode_for_size_tree (TYPE_SIZE (type), MODE_INT, 0));
+
   /* Promotes small structs returned in a register to full-word size
      for big-endian AAPCS.  */
   if (arm_return_in_msb (type))
@@ -2727,10 +2747,9 @@ arm_apply_result_size (void)
 }
 
 /* Decide whether a type should be returned in memory (true)
-   or in a register (false).  This is called by the macro
-   RETURN_IN_MEMORY.  */
-int
-arm_return_in_memory (const_tree type)
+   or in a register (false).  */
+static bool
+arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
 {
   HOST_WIDE_INT size;
 
@@ -2738,8 +2757,13 @@ arm_return_in_memory (const_tree type)
 
   /* 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)
+     call-clobbered registers to play with).
+
+     Structures tagged with the reg_return attribute should also be returned
+     using registers, unless they are too big.  */
+  if (TREE_CODE (type) == VECTOR_TYPE
+      || (TREE_CODE (type) == RECORD_TYPE
+	  && lookup_attribute ("reg_return", TYPE_ATTRIBUTES (type))))
     return (size < 0 || size > (4 * UNITS_PER_WORD));
 
   if (!AGGREGATE_TYPE_P (type) &&
@@ -2793,7 +2817,7 @@ arm_return_in_memory (const_tree type)
 
       /* ... Aggregates that are not themselves valid for returning in
 	 a register are not allowed.  */
-      if (RETURN_IN_MEMORY (TREE_TYPE (field)))
+      if (arm_return_in_memory (TREE_TYPE (field), fntype))
 	return 1;
 
       /* Now check the remaining fields, if any.  Only bitfields are allowed,
@@ -2828,7 +2852,7 @@ arm_return_in_memory (const_tree type)
 	  if (FLOAT_TYPE_P (TREE_TYPE (field)))
 	    return 1;
 
-	  if (RETURN_IN_MEMORY (TREE_TYPE (field)))
+	  if (arm_return_in_memory (TREE_TYPE (field), fntype))
 	    return 1;
 	}
 
@@ -3033,6 +3057,9 @@ const struct attribute_spec arm_attribut
   { "isr",          0, 1, false, false, false, arm_handle_isr_attribute },
   { "interrupt",    0, 1, false, false, false, arm_handle_isr_attribute },
   { "naked",        0, 0, true,  false, false, arm_handle_fndecl_attribute },
+  /* Return this structure in registers even if the ABI says return it in
+     memory.  */
+  { "reg_return",   0, 0, false, true, false, arm_handle_regreturn_attribute },
 #ifdef ARM_PE
   /* ARM/PE has three new attributes:
      interfacearm - ?
@@ -3131,6 +3158,85 @@ arm_handle_isr_attribute (tree *node, tr
   return NULL_TREE;
 }
 
+static tree
+arm_handle_regreturn_attribute (tree *node,
+				tree name,
+				tree args ATTRIBUTE_UNUSED,
+				int flags,
+				bool *no_add_attrs)
+{
+  HOST_WIDE_INT size;
+  tree field;
+  
+  if (TREE_CODE (*node) != RECORD_TYPE)
+    {
+      warning (OPT_Wattributes,
+	       "%qs attribute is only meaningful for %<struct%>s",
+	       IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  if (!COMPLETE_TYPE_P (*node))
+    return NULL_TREE;  /* incomplete, cannot be audited further */
+
+  size = int_size_in_bytes (*node);
+
+  if (size == -1)
+    {
+      warning (OPT_Wattributes,
+	       "%qs attribute cannot be applied to variable-size structures",
+	       IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* Only r0-r3 can be used for structure returns.  */
+  if (size > 4*UNITS_PER_WORD)
+    {
+      warning (OPT_Wattributes,
+	       "structure %qs is too large to return in registers",
+	       TREE_CODE (TYPE_NAME (*node)) == TYPE_DECL
+	       ? IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (*node)))
+	       : IDENTIFIER_POINTER (TYPE_NAME (*node)));
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }      
+
+  for (field = TYPE_FIELDS (*node); field; field = TREE_CHAIN (field))
+    {
+      /* This is stricter than it needs to be.  In fact, we might not
+	 need this check at all.  */
+      if (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (field))) != MODE_INT)
+	{
+	  warning (OPT_Wattributes,
+		   "structure %qs contains a field with non-integer type "
+		   "so it cannot be returned in registers",
+		   TREE_CODE (TYPE_NAME (*node)) == TYPE_DECL
+		   ? IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (*node)))
+		   : IDENTIFIER_POINTER (TYPE_NAME (*node)));
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+    }
+
+  if (!(flags & ATTR_FLAG_TYPE_IN_PLACE))
+    *node = build_variant_type_copy (*node);
+
+  return NULL_TREE;
+}
+
+/* If we're going to return a type in registers, we should keep it in
+   registers too, even if we normally wouldn't.  */
+static enum machine_mode
+arm_override_record_mode (const_tree record)
+{
+  if (lookup_attribute ("reg_return", TYPE_ATTRIBUTES (record)))
+    return mode_for_size_tree (TYPE_SIZE (record), MODE_INT, 0);
+  else
+    return TYPE_MODE (record);
+}
+
 #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
 /* Handle the "notshared" attribute.  This attribute is another way of
    requesting hidden visibility.  ARM's compiler supports
==================================================================
--- config/arm/arm.h	(revision 128116)
+++ config/arm/arm.h	(local)
@@ -1447,13 +1447,6 @@ 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.  */
@@ -1470,9 +1463,6 @@ do {									      \
 #define APPLY_RESULT_SIZE arm_apply_result_size()
 
 /* How large values are returned */
-/* A C expression which can inhibit the returning of certain function values
-   in registers, based on the type of value.  */
-#define RETURN_IN_MEMORY(TYPE) arm_return_in_memory (TYPE)
 
 /* Define DEFAULT_PCC_STRUCT_RETURN to 1 if all structure and union return
    values must be in memory.  On the ARM, they need only do so if larger
==================================================================
--- config/arm/bpabi.S	(revision 128116)
+++ config/arm/bpabi.S	(local)
@@ -80,40 +80,3 @@ ARM_FUNC_START aeabi_ulcmp
 	FUNC_END aeabi_ulcmp
 
 #endif /* L_aeabi_ulcmp */
-
-#ifdef L_aeabi_ldivmod
-
-ARM_FUNC_START aeabi_ldivmod
-	sub sp, sp, #8
-#if defined(__thumb2__)
-	mov ip, sp
-	push {ip, lr}
-#else
-	do_push {sp, lr}
-#endif
-	bl SYM(__gnu_ldivmod_helper) __PLT__
-	ldr lr, [sp, #4]
-	add sp, sp, #8
-	do_pop {r2, r3}
-	RET
-	
-#endif /* L_aeabi_ldivmod */
-
-#ifdef L_aeabi_uldivmod
-
-ARM_FUNC_START aeabi_uldivmod
-	sub sp, sp, #8
-#if defined(__thumb2__)
-	mov ip, sp
-	push {ip, lr}
-#else
-	do_push {sp, lr}
-#endif
-	bl SYM(__gnu_uldivmod_helper) __PLT__
-	ldr lr, [sp, #4]
-	add sp, sp, #8
-	do_pop {r2, r3}
-	RET
-	
-#endif /* L_aeabi_divmod */
-	
==================================================================
--- config/arm/bpabi.c	(revision 128116)
+++ config/arm/bpabi.c	(local)
@@ -1,6 +1,6 @@
 /* Miscellaneous BPABI functions.
 
-   Copyright (C) 2003, 2004  Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2007  Free Software Foundation, Inc.
    Contributed by CodeSourcery, LLC.
 
    This file is free software; you can redistribute it and/or modify it
@@ -30,32 +30,36 @@
 extern long long __divdi3 (long long, long long);
 extern unsigned long long __udivdi3 (unsigned long long, 
 				     unsigned long long);
-extern long long __gnu_ldivmod_helper (long long, long long, long long *);
-extern unsigned long long __gnu_uldivmod_helper (unsigned long long, 
-						 unsigned long long, 
-						 unsigned long long *);
 
+typedef struct __attribute__ ((reg_return))
+{
+  long long quot;
+  long long rem;
+} lldiv_t_rr;
 
-long long
-__gnu_ldivmod_helper (long long a, 
-		      long long b, 
-		      long long *remainder)
+typedef struct __attribute__ ((reg_return))
 {
-  long long quotient;
+  unsigned long long quot;
+  unsigned long long rem;
+} ulldiv_t_rr;
 
-  quotient = __divdi3 (a, b);
-  *remainder = a - b * quotient;
-  return quotient;
-}
+extern lldiv_t_rr __aeabi_ldivmod(long long, long long);
+extern ulldiv_t_rr __aeabi_uldivmod(unsigned long long, unsigned long long);
 
-unsigned long long
-__gnu_uldivmod_helper (unsigned long long a, 
-		       unsigned long long b,
-		       unsigned long long *remainder)
+lldiv_t_rr
+__aeabi_ldivmod (long long a, long long b)
 {
-  unsigned long long quotient;
+  lldiv_t_rr r;
+  r.quot =__divdi3 (a, b);
+  r.rem = a - b * r.quot;
+  return r;
+}
 
-  quotient = __udivdi3 (a, b);
-  *remainder = a - b * quotient;
-  return quotient;
+ulldiv_t_rr
+__aeabi_uldivmod (unsigned long long a, unsigned long long b)
+{
+  ulldiv_t_rr r;
+  r.quot = __udivdi3 (a, b);
+  r.rem = a - b * r.quot;
+  return r;
 }
==================================================================
--- config/arm/t-bpabi	(revision 128116)
+++ config/arm/t-bpabi	(local)
@@ -1,8 +1,9 @@
 # Add the bpabi.S functions.
-LIB1ASMFUNCS += _aeabi_lcmp _aeabi_ulcmp _aeabi_ldivmod _aeabi_uldivmod
+LIB1ASMFUNCS += _aeabi_lcmp _aeabi_ulcmp
 
 # Add the BPABI C functions.
 LIB2FUNCS_EXTRA = $(srcdir)/config/arm/bpabi.c \
+		  $(srcdir)/config/arm/bpabi-compat.c \
 		  $(srcdir)/config/arm/unaligned-funcs.c
 
 UNWIND_H = $(srcdir)/config/arm/unwind-arm.h
==================================================================
--- config/arm/bpabi-compat.c	(revision 128116)
+++ config/arm/bpabi-compat.c	(local)
@@ -0,0 +1,61 @@
+/* Miscellaneous BPABI functions.
+
+   Copyright (C) 2003, 2004  Free Software Foundation, Inc.
+   Contributed by CodeSourcery, LLC.
+
+   This file 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 2, or (at your option) any
+   later version.
+
+   In addition to the permissions in the GNU General Public License, the
+   Free Software Foundation gives you unlimited permission to link the
+   compiled version of this file into combinations with other programs,
+   and to distribute those combinations without any restriction coming
+   from the use of this file.  (The General Public License restrictions
+   do apply in other respects; for example, they cover modification of
+   the file, and distribution when not linked into a combine
+   executable.)
+
+   This file 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 this program; see the file COPYING.  If not, write to
+   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+extern long long __divdi3 (long long, long long);
+extern unsigned long long __udivdi3 (unsigned long long, 
+				     unsigned long long);
+extern long long __gnu_ldivmod_helper (long long, long long, long long *);
+extern unsigned long long __gnu_uldivmod_helper (unsigned long long, 
+						 unsigned long long, 
+						 unsigned long long *);
+
+
+long long
+__gnu_ldivmod_helper (long long a, 
+		      long long b, 
+		      long long *remainder)
+{
+  long long quotient;
+
+  quotient = __divdi3 (a, b);
+  *remainder = a - b * quotient;
+  return quotient;
+}
+
+unsigned long long
+__gnu_uldivmod_helper (unsigned long long a, 
+		       unsigned long long b,
+		       unsigned long long *remainder)
+{
+  unsigned long long quotient;
+
+  quotient = __udivdi3 (a, b);
+  *remainder = a - b * quotient;
+  return quotient;
+}
==================================================================
--- doc/tm.texi	(revision 128116)
+++ doc/tm.texi	(local)
@@ -1278,19 +1278,38 @@ these accesses should use the bitfield c
 The default is @code{!TARGET_STRICT_ALIGN}.
 @end deftypefn
 
-@defmac MEMBER_TYPE_FORCES_BLK (@var{field}, @var{mode})
-Return 1 if a structure or array containing @var{field} should be accessed using
-@code{BLKMODE}.
+@deftypefn {Target Hook} bool TARGET_MEMBER_FORCES_BLKMODE (@var{field}, @var{mode})
+Return @code{true} if an aggregate type containing @var{field} should
+be assigned @code{BLKmode}, even when it otherwise would not.
+
+If @var{field} is the only field in the aggregate, @var{mode} is its
+mode, otherwise @var{mode} is @code{VOIDmode}.  @var{mode} is provided
+for the case where structures of one field would require the
+structure's mode to retain the field's mode.  The default always
+returns @code{false}.
 
-If @var{field} is the only field in the structure, @var{mode} is its
-mode, otherwise @var{mode} is VOIDmode.  @var{mode} is provided in the
-case where structures of one field would require the structure's mode to
-retain the field's mode.
-
-Normally, this is not needed.  See the file @file{c4x.h} for an example
-of how to use this macro to prevent a structure having a floating point
-field from being accessed in an integer mode.
-@end defmac
+This hook is normally not needed.  See the @code{c4x} back end for a
+case where it is.
+@end deftypefn
+
+@defmac MEMBER_TYPE_FORCES_BLK (@var{field}, @var{mode})
+This is an obsolete macro with the same function as the
+@code{TARGET_MEMBER_FORCES_BLKMODE} hook.  New ports should define
+that hook instead.
+@end defmac
+
+@deftypefn {Target Hook} {enum machine_mode} TARGET_OVERRIDE_RECORD_MODE (@var{record})
+Return the mode that @var{record} should be assigned.  When it is
+called, @code{TYPE_MODE (@var{record})} is the mode that
+@code{compute_record_mode} has just selected; return that mode if the
+machine-independent logic made the right choice, or some other mode if
+not.  The default is to accept @code{compute_record_mode}'s choice
+always.
+
+One reason to define this hook is if some @code{RECORD_TYPE}s bigger
+than @code{MAX_FIXED_MODE_SIZE} should be assigned scalar modes
+anyway.  See the @code{arm} back end for an example.
+@end deftypefn
 
 @defmac ROUND_TYPE_ALIGN (@var{type}, @var{computed}, @var{specified})
 Define this macro as an expression for the alignment of a type (given
==================================================================
--- stor-layout.c	(revision 128116)
+++ stor-layout.c	(local)
@@ -1363,13 +1363,10 @@ compute_record_mode (tree type)
       if (simple_cst_equal (TYPE_SIZE (type), DECL_SIZE (field)))
 	mode = DECL_MODE (field);
 
-#ifdef MEMBER_TYPE_FORCES_BLK
       /* With some targets, eg. c4x, it is sub-optimal
 	 to access an aligned BLKmode structure as a scalar.  */
-
-      if (MEMBER_TYPE_FORCES_BLK (field, mode))
+      if (targetm.member_forces_blkmode (field, mode))
 	return;
-#endif /* MEMBER_TYPE_FORCES_BLK  */
     }
 
   /* If we only have one real field; use its mode if that mode's size
@@ -1394,6 +1391,10 @@ compute_record_mode (tree type)
       TYPE_NO_FORCE_BLK (type) = 1;
       TYPE_MODE (type) = BLKmode;
     }
+
+  /* As long as we were not forced to use BLKmode for any of the
+     reasons above, targets have a chance to override the decision.  */
+  TYPE_MODE (type) = targetm.override_record_mode (type);
 }
 
 /* Compute TYPE_SIZE and TYPE_ALIGN for TYPE, once it has been laid
@@ -1812,9 +1813,7 @@ layout_type (tree type)
 	TYPE_USER_ALIGN (type) = TYPE_USER_ALIGN (element);
 	TYPE_MODE (type) = BLKmode;
 	if (TYPE_SIZE (type) != 0
-#ifdef MEMBER_TYPE_FORCES_BLK
-	    && ! MEMBER_TYPE_FORCES_BLK (type, VOIDmode)
-#endif
+	    && ! targetm.member_forces_blkmode (type, VOIDmode)
 	    /* BLKmode elements force BLKmode aggregate;
 	       else extract/store fields may lose.  */
 	    && (TYPE_MODE (TREE_TYPE (type)) != BLKmode
==================================================================
--- target-def.h	(revision 128116)
+++ target-def.h	(local)
@@ -473,6 +473,8 @@
 #define TARGET_INSERT_ATTRIBUTES hook_void_tree_treeptr
 #define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_false
 #define TARGET_MS_BITFIELD_LAYOUT_P hook_bool_const_tree_false
+#define TARGET_MEMBER_FORCES_BLKMODE default_member_forces_blkmode
+#define TARGET_OVERRIDE_RECORD_MODE default_override_record_mode
 #define TARGET_ALIGN_ANON_BITFIELD hook_bool_void_false
 #define TARGET_NARROW_VOLATILE_BITFIELD hook_bool_void_false
 #define TARGET_RTX_COSTS hook_bool_rtx_int_int_intp_false
@@ -696,6 +698,8 @@
   TARGET_INSERT_ATTRIBUTES,			\
   TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P,	\
   TARGET_MS_BITFIELD_LAYOUT_P,			\
+  TARGET_MEMBER_FORCES_BLKMODE,			\
+  TARGET_OVERRIDE_RECORD_MODE,			\
   TARGET_DECIMAL_FLOAT_SUPPORTED_P,		\
   TARGET_FIXED_POINT_SUPPORTED_P,		\
   TARGET_ALIGN_ANON_BITFIELD,			\
==================================================================
--- target.h	(revision 128116)
+++ target.h	(local)
@@ -483,6 +483,15 @@ struct gcc_target
      Microsoft Visual C++ bitfield layout rules.  */
   bool (* ms_bitfield_layout_p) (const_tree record_type);
 
+  /* True if the presence of FIELD (a FIELD_DECL) in a structure
+     forces that structure to be given BLKmode.  MODE is FIELD's mode
+     if it is the only field, VOIDmode otherwise.  */
+  bool (* member_forces_blkmode) (const_tree field, enum machine_mode mode);
+
+  /* Called at the end of compute_record_mode to allow targets to override
+     its choices.  RECORD is the record whose mode is being determined.  */
+  enum machine_mode (*override_record_mode) (const_tree record);
+
   /* True if the target supports decimal floating point.  */
   bool (* decimal_float_supported_p) (void);
 
==================================================================
--- targhooks.c	(revision 128116)
+++ targhooks.c	(local)
@@ -256,6 +256,28 @@ default_unwind_emit (FILE * stream ATTRI
   gcc_unreachable ();
 }
 
+/* Return true if a field requires its containing structure to be BLKmode
+   (i.e. to be handled in memory).  This is a transitional hook which defers
+   to the old MEMBER_TYPE_FORCES_BLK macro.  */
+bool
+default_member_forces_blkmode (const_tree field ATTRIBUTE_UNUSED,
+			       enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+#ifdef MEMBER_TYPE_FORCES_BLK
+  return MEMBER_TYPE_FORCES_BLK (field, mode);
+#else
+  return false;
+#endif
+}
+
+/* Targets may override the decision made by compute_record_mode, if
+   they wish, by defining this hook.  */
+enum machine_mode
+default_override_record_mode (const_tree record)
+{
+  return TYPE_MODE (record);
+}
+
 /* True if MODE is valid for the target.  By "valid", we mean able to
    be manipulated in non-trivial ways.  In particular, this means all
    the arithmetic is supported.
==================================================================
--- targhooks.h	(revision 128116)
+++ targhooks.h	(local)
@@ -53,6 +53,9 @@ extern bool hook_callee_copies_named
 
 extern void default_unwind_emit (FILE *, rtx);
 
+extern bool default_member_forces_blkmode (const_tree, enum machine_mode);
+extern enum machine_mode default_override_record_mode (const_tree);
+
 extern bool default_scalar_mode_supported_p (enum machine_mode);
 extern bool default_decimal_float_supported_p (void);
 extern bool default_fixed_point_supported_p (void);

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]