From: Eric Botcazou <ebotca...@adacore.com>

The second problem occurs on 64-bit platforms where there is a second Out
parameter that is smaller than the access parameter, creating a hole in the
return structure.

gcc/ada/ChangeLog:

        * gcc-interface/decl.cc (gnat_to_gnu_subprog_type): In the case of a
        subprogram using the Copy-In/Copy-Out mechanism, deal specially with
        the case of 2 parameters of differing sizes.
        * gcc-interface/trans.cc (Subprogram_Body_to_gnu): In the case of a
        subprogram using the Copy-In/Copy-Out mechanism, make sure the types
        are consistent on the two sides for all the parameters.

Tested on x86_64-pc-linux-gnu, committed on master.

---
 gcc/ada/gcc-interface/decl.cc  | 23 +++++++++++++++++++++++
 gcc/ada/gcc-interface/trans.cc | 19 +++++++++++++++----
 2 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/gcc/ada/gcc-interface/decl.cc b/gcc/ada/gcc-interface/decl.cc
index 903ec844b96..63c6851d142 100644
--- a/gcc/ada/gcc-interface/decl.cc
+++ b/gcc/ada/gcc-interface/decl.cc
@@ -6421,6 +6421,29 @@ gnat_to_gnu_subprog_type (Entity_Id gnat_subprog, bool 
definition,
         since structures are incomplete for the back-end.  */
       else if (Convention (gnat_subprog) != Convention_Stubbed)
        {
+         /* If we have two entries that may be returned in integer registers,
+            the larger has power-of-2 size and the smaller is integral, then
+            extend the smaller to this power-of-2 size to get a return type
+            with power-of-2 size and no holes, again to speed up accesses.  */
+         if (list_length (gnu_cico_field_list) == 2
+             && gnu_cico_only_integral_type)
+           {
+             tree typ1 = TREE_TYPE (gnu_cico_field_list);
+             tree typ2 = TREE_TYPE (DECL_CHAIN (gnu_cico_field_list));
+             if (INTEGRAL_TYPE_P (typ1)
+                 && integer_pow2p (TYPE_SIZE (typ2))
+                 && tree_int_cst_lt (TYPE_SIZE (typ1), TYPE_SIZE (typ2)))
+               TREE_TYPE (gnu_cico_field_list)
+                 = gnat_type_for_size (TREE_INT_CST_LOW (TYPE_SIZE (typ2)),
+                                       TYPE_UNSIGNED (typ1));
+             else if (INTEGRAL_TYPE_P (typ2)
+                      && integer_pow2p (TYPE_SIZE (typ1))
+                      && tree_int_cst_lt (TYPE_SIZE (typ2), TYPE_SIZE (typ1)))
+               TREE_TYPE (DECL_CHAIN (gnu_cico_field_list))
+                 = gnat_type_for_size (TREE_INT_CST_LOW (TYPE_SIZE (typ1)),
+                                       TYPE_UNSIGNED (typ2));
+           }
+
          finish_record_type (gnu_cico_return_type,
                              nreverse (gnu_cico_field_list),
                              0, false);
diff --git a/gcc/ada/gcc-interface/trans.cc b/gcc/ada/gcc-interface/trans.cc
index e02804b75af..520611e7d79 100644
--- a/gcc/ada/gcc-interface/trans.cc
+++ b/gcc/ada/gcc-interface/trans.cc
@@ -4049,7 +4049,7 @@ Subprogram_Body_to_gnu (Node_Id gnat_node)
            tree gnu_decl;
 
            /* Skip any entries that have been already filled in; they must
-              correspond to In Out parameters.  */
+              correspond to In Out parameters or previous Out parameters.  */
            while (gnu_cico_entry && TREE_VALUE (gnu_cico_entry))
              gnu_cico_entry = TREE_CHAIN (gnu_cico_entry);
 
@@ -4059,11 +4059,22 @@ Subprogram_Body_to_gnu (Node_Id gnat_node)
            if (DECL_BY_REF_P (gnu_decl))
              gnu_decl = build_unary_op (INDIRECT_REF, NULL_TREE, gnu_decl);
 
-           /* Do any needed references for padded types.  */
-           TREE_VALUE (gnu_cico_entry)
-             = convert (TREE_TYPE (TREE_PURPOSE (gnu_cico_entry)), gnu_decl);
+           TREE_VALUE (gnu_cico_entry) = gnu_decl;
          }
+
+      /* Finally, ensure type consistency between TREE_PURPOSE and TREE_VALUE
+        so that the assignment of the latter to the former can be done.  */
+      tree gnu_cico_entry = gnu_cico_list;
+      while (gnu_cico_entry)
+       {
+         if (!VOID_TYPE_P (TREE_VALUE (gnu_cico_entry)))
+           TREE_VALUE (gnu_cico_entry)
+             = convert (TREE_TYPE (TREE_PURPOSE (gnu_cico_entry)),
+                        TREE_VALUE (gnu_cico_entry));
+         gnu_cico_entry = TREE_CHAIN (gnu_cico_entry);
+       }
     }
+
   else
     vec_safe_push (gnu_return_label_stack, NULL_TREE);
 
-- 
2.43.0

Reply via email to