[RFC] passing C++ decimal class as underlying scalar type

Janis Johnson janis187@us.ibm.com
Tue Oct 13 20:54:00 GMT 2009


C++ support for decimal floating-point arithmetic as defined in TR 24733
uses classes for the data types, while C support uses scalar types.
Without special support the C++ classes are not binary compatible with
the C scalars with ABIs that pass small aggregates differently from
scalars.

The consensus that came out of a discussion about this on the C++ ABI
mailing list was the a C++ compiler should special-case the decimal
classes to pass and return them the same as the scalar types.  Nothing
formal has happened and I just noticed a response (not copied to me)
about how to request a change to the C++ ABI document.

In the meantime I've found out a way to get the G++ compiler to treat
the decimal classes as scalars for the purposes of passing arguments
and function results.  I'm not submitting this as a regular patch now,
but would like some feedback about whether this is a reasonable way
to accomplish this.  The patch has been tested on powerpc64-linux,
including with a new batch of compatibility tests that check calls
between source files that use scalars and source files that use classes.

My early attempts replaced types during gimplification but that got
messy with class member functions accessing data within the class, and
with assigning function results to class objects.

This solution introduces the concept of "transparent record", like the
the existing transparent union except that it also applies to function
results, not just argument passing.  It uses a tree flag that was not
used for record types.

If I get the change into the C++ ABI, does this look like a reasonable
way to support it in GCC or should I start over?

2009-10-13  Janis Johnson  <janis187@us.ibm.com>

gcc/
	* tree.h (TYPE_TRANSPARENT_RECORD): Define.
	* calls.c (initialize_argument_information): Handle it.
	* c-typeck.c (type_lists_compatible_p): Ditto.
	(convert_for_assignment): Ditto.
	* function.c (aggregate_value_p): Ditto.
	(pass_by_reference): Ditto.
	(assign_parm_find_data_types): Ditto.
	* lto-streamer-in.c (unpack_ts_type_value_fields): Ditto.
	* lto-streamer-out.c (pack_ts_type_value_fields): Ditto.
	* print-tree.c (print_node): Ditto.

gcc/cp/
	* semantics.c (begin_class_definition): Recognize decimal classes
	and set transparent record flag.
	* mangle.c (write_type): Handle transparent record flag.

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 152726)
+++ gcc/tree.h	(working copy)
@@ -2232,6 +2232,11 @@ extern enum machine_mode vector_type_mod
 #define TYPE_TRANSPARENT_UNION(NODE)  \
   (UNION_TYPE_CHECK (NODE)->type.transparent_union_flag)
 
+/* Indicates that objects of this type (a RECORD_TYPE) should be passed
+   the same way that the first (and only) member would be passed.  */
+#define TYPE_TRANSPARENT_RECORD(NODE)  \
+  (RECORD_TYPE_CHECK (NODE)->type.transparent_union_flag)
+
 /* For an ARRAY_TYPE, indicates that it is not permitted to take the
    address of a component of the type.  This is the counterpart of
    DECL_NONADDRESSABLE_P for arrays, see the definition of this flag.  */
Index: gcc/calls.c
===================================================================
--- gcc/calls.c	(revision 152726)
+++ gcc/calls.c	(working copy)
@@ -1017,10 +1017,13 @@ initialize_argument_information (int num
       if (type == error_mark_node || !COMPLETE_TYPE_P (type))
 	args[i].tree_value = integer_zero_node, type = integer_type_node;
 
-      /* If TYPE is a transparent union, pass things the way we would
-	 pass the first field of the union.  We have already verified that
-	 the modes are the same.  */
-      if (TREE_CODE (type) == UNION_TYPE && TYPE_TRANSPARENT_UNION (type))
+      /* If TYPE is a transparent union or record, pass things the way
+	 we would pass the first field of the union or record.  We have
+	 already verified that the modes are the same.  */
+      if ((TREE_CODE (type) == UNION_TYPE
+	   && TYPE_TRANSPARENT_UNION (type))
+	  || (TREE_CODE (type) == RECORD_TYPE
+	      && TYPE_TRANSPARENT_RECORD(type)))
 	type = TREE_TYPE (TYPE_FIELDS (type));
 
       /* Decide where to pass this arg.
Index: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(revision 152726)
+++ gcc/c-typeck.c	(working copy)
@@ -1554,9 +1554,11 @@ type_lists_compatible_p (const_tree args
 	{
 	  /* Allow  wait (union {union wait *u; int *i} *)
 	     and  wait (union wait *)  to be compatible.  */
-	  if (TREE_CODE (a1) == UNION_TYPE
+	  if (((TREE_CODE (a1) == UNION_TYPE
 	      && (TYPE_NAME (a1) == 0
-		  || TYPE_TRANSPARENT_UNION (a1))
+		  || TYPE_TRANSPARENT_UNION (a1)))
+	       || ((TREE_CODE (a1) == RECORD_TYPE)
+		   && TYPE_TRANSPARENT_RECORD (a1)))
 	      && TREE_CODE (TYPE_SIZE (a1)) == INTEGER_CST
 	      && tree_int_cst_equal (TYPE_SIZE (a1),
 				     TYPE_SIZE (a2)))
@@ -1575,9 +1577,11 @@ type_lists_compatible_p (const_tree args
 	      if (memb == 0)
 		return 0;
 	    }
-	  else if (TREE_CODE (a2) == UNION_TYPE
-		   && (TYPE_NAME (a2) == 0
-		       || TYPE_TRANSPARENT_UNION (a2))
+	  else if (((TREE_CODE (a2) == UNION_TYPE
+		     && (TYPE_NAME (a2) == 0
+		        || TYPE_TRANSPARENT_UNION (a2)))
+		    || (TREE_CODE (a2) == RECORD_TYPE
+			&& TYPE_TRANSPARENT_RECORD (a2)))
 		   && TREE_CODE (TYPE_SIZE (a2)) == INTEGER_CST
 		   && tree_int_cst_equal (TYPE_SIZE (a2),
 					  TYPE_SIZE (a1)))
@@ -4842,9 +4846,10 @@ convert_for_assignment (location_t locat
       && comptypes (type, rhstype))
     return convert_and_check (type, rhs);
 
-  /* Conversion to a transparent union from its member types.
+  /* Conversion to a transparent union or record from its member types.
      This applies only to function arguments.  */
-  if (codel == UNION_TYPE && TYPE_TRANSPARENT_UNION (type)
+  if (((codel == UNION_TYPE && TYPE_TRANSPARENT_UNION (type))
+       || (codel == RECORD_TYPE && TYPE_TRANSPARENT_RECORD (type)))
       && errtype == ic_argpass)
     {
       tree memb, marginal_memb = NULL_TREE;
Index: gcc/function.c
===================================================================
--- gcc/function.c	(revision 152726)
+++ gcc/function.c	(working copy)
@@ -1872,6 +1872,11 @@ aggregate_value_p (const_tree exp, const
   if (TREE_CODE (type) == VOID_TYPE)
     return 0;
 
+  /* If a record should be passed the same as its first (and only) member
+     don't pass it as an aggregate.  */
+  if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_RECORD (type))
+    return 0;
+
   /* If the front end has decided that this needs to be passed by
      reference, do so.  */
   if ((TREE_CODE (exp) == PARM_DECL || TREE_CODE (exp) == RESULT_DECL)
@@ -1986,6 +1991,14 @@ pass_by_reference (CUMULATIVE_ARGS *ca, 
       /* GCC post 3.4 passes *all* variable sized types by reference.  */
       if (!TYPE_SIZE (type) || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	return true;
+
+      /* If a record type should be passed the same as its first (and only)
+	 member, use the type and mode of that member.  */
+      if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_RECORD (type))
+	{
+	  type = TREE_TYPE (TYPE_FIELDS (type));
+	  mode = TYPE_MODE (type);
+	}
     }
 
   return targetm.calls.pass_by_reference (ca, mode, type, named_arg);
@@ -2210,11 +2223,13 @@ assign_parm_find_data_types (struct assi
   passed_mode = TYPE_MODE (passed_type);
   nominal_mode = TYPE_MODE (nominal_type);
 
-  /* If the parm is to be passed as a transparent union, use the type of
-     the first field for the tests below.  We have already verified that
-     the modes are the same.  */
-  if (TREE_CODE (passed_type) == UNION_TYPE
-      && TYPE_TRANSPARENT_UNION (passed_type))
+  /* If the parm is to be passed as a transparent union or record, use the
+     type of the first field for the tests below.  We have already verified
+     that the modes are the same.  */
+  if ((TREE_CODE (passed_type) == UNION_TYPE
+       && TYPE_TRANSPARENT_UNION (passed_type))
+      || (TREE_CODE (passed_type) == RECORD_TYPE
+	  && TYPE_TRANSPARENT_RECORD (passed_type)))
     passed_type = TREE_TYPE (TYPE_FIELDS (passed_type));
 
   /* See if this arg was passed by invisible reference.  */
Index: gcc/lto-streamer-in.c
===================================================================
--- gcc/lto-streamer-in.c	(revision 152726)
+++ gcc/lto-streamer-in.c	(working copy)
@@ -1567,6 +1567,8 @@ unpack_ts_type_value_fields (struct bitp
   TYPE_NEEDS_CONSTRUCTING(expr) = (unsigned) bp_unpack_value (bp, 1);
   if (TREE_CODE (expr) == UNION_TYPE)
     TYPE_TRANSPARENT_UNION (expr) = (unsigned) bp_unpack_value (bp, 1);
+  if (TREE_CODE (expr) == RECORD_TYPE)
+    TYPE_TRANSPARENT_RECORD (expr) = (unsigned) bp_unpack_value (bp, 1);
   TYPE_PACKED (expr) = (unsigned) bp_unpack_value (bp, 1);
   TYPE_RESTRICT (expr) = (unsigned) bp_unpack_value (bp, 1);
   TYPE_CONTAINS_PLACEHOLDER_INTERNAL (expr)
Index: gcc/lto-streamer-out.c
===================================================================
--- gcc/lto-streamer-out.c	(revision 152726)
+++ gcc/lto-streamer-out.c	(working copy)
@@ -518,6 +518,8 @@ pack_ts_type_value_fields (struct bitpac
   bp_pack_value (bp, TYPE_NEEDS_CONSTRUCTING(expr), 1);
   if (TREE_CODE (expr) == UNION_TYPE)
     bp_pack_value (bp, TYPE_TRANSPARENT_UNION (expr), 1);
+  if (TREE_CODE (expr) == RECORD_TYPE)
+    bp_pack_value (bp, TYPE_TRANSPARENT_RECORD (expr), 1);
   bp_pack_value (bp, TYPE_PACKED (expr), 1);
   bp_pack_value (bp, TYPE_RESTRICT (expr), 1);
   bp_pack_value (bp, TYPE_CONTAINS_PLACEHOLDER_INTERNAL (expr), 2);
Index: gcc/print-tree.c
===================================================================
--- gcc/print-tree.c	(revision 152726)
+++ gcc/print-tree.c	(working copy)
@@ -579,6 +579,8 @@ print_node (FILE *file, const char *pref
 	 different nodes.  */
       if (code == UNION_TYPE && TYPE_TRANSPARENT_UNION (node))
 	fputs (" transparent-union", file);
+      if (code == RECORD_TYPE && TYPE_TRANSPARENT_RECORD (node))
+	fputs (" transparent-record", file);
       else if (code == ARRAY_TYPE
 	       && TYPE_NONALIASED_COMPONENT (node))
 	fputs (" nonaliased-component", file);
Index: gcc/cp/semantics.c
===================================================================
--- gcc/cp/semantics.c	(revision 152726)
+++ gcc/cp/semantics.c	(working copy)
@@ -2329,6 +2329,18 @@ begin_class_definition (tree t, tree att
       error ("definition of %q#T inside template parameter list", t);
       return error_mark_node;
     }
+
+  /* According to the C++ ABI, decimal classes defined in ISO/IEC TR 24733
+     are passed the same as decimal scalar types.  */
+  if (TREE_CODE (t) == RECORD_TYPE)
+    {
+      const char *n = type_as_string (t, TFF_CLASS_KEY_OR_ENUM);
+      if ((strcmp (n, "class std::decimal::decimal32") == 0)
+	  || (strcmp (n, "class std::decimal::decimal64") == 0)
+	  || (strcmp (n, "class std::decimal::decimal128") == 0))
+	TYPE_TRANSPARENT_RECORD (t) = 1;
+    }
+
   /* A non-implicit typename comes from code like:
 
        template <typename T> struct A {
Index: gcc/cp/mangle.c
===================================================================
--- gcc/cp/mangle.c	(revision 152726)
+++ gcc/cp/mangle.c	(working copy)
@@ -1756,6 +1756,12 @@ write_type (tree type)
   if (find_substitution (type))
     return;
 
+  /* According to the C++ ABI, some library classes are passed the
+     same as the scalar type of their single member and use the same
+     mangling.  */
+  if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_RECORD (type))
+    type = TREE_TYPE (TYPE_FIELDS (type));
+
   if (write_CV_qualifiers_for_type (type) > 0)
     /* If TYPE was CV-qualified, we just wrote the qualifiers; now
        mangle the unqualified type.  The recursive call is needed here




More information about the Gcc-patches mailing list