The RISC-V ABI currently defines that empty unions and zero length array
in struct should be ignored, but the implementation in GCC is not
correct.

e.g. for the following code:
```
struct S2eu_2f {
    union{} e1;
    float f;
    float g;
};
```

The RISC-V ABI defines that the layout of S2eu_2f should be equivalent
to:
```
struct S2eu_2f {
        float f;
        float g;
};
```

However, the current GCC implementation passes S2eu_2f in a0 (lp64d)
rather than fa0 and fa1 (lp64d).

Also for the following code:
```
struct S0ae_2f {
    struct{} e1[0];
    float f;
    float g;
};
```
The RISC-V ABI defines that the layout of S0ae_2f should be equivalent
to:
```
struct S0ae_2f {
        float f;
        float g;
};
```

And again, the current GCC implementation passes S0ae_2f in a0 (lp64d)
rather than fa0 and fa1 (lp64d).

This patch fixes the issue by updating the relevant functions to correctly
handle empty unions, also we have implemented the ABI change warning to
notify user that the ABI of empty unions and zero length array in struct
has been changed/fixed.

Generally ABI should not be changed, but the psABI is defined there for
long time and clang/LLVM has already implemented it correctly, so we
decide to fix it in GCC as well to maintain compatibility, and another
reason to fix that in GCC is zero length array and empty union in struct
should be rarely used in practice, so the impact should be limited.

References:
[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/464

gcc/ChangeLog:

        * config/riscv/riscv.cc (riscv_flatten_aggregate_field): Skip
        empty unions and zero-length arrays when flattening aggregate
        fields for ABI classification.
        (riscv_pass_aggregate_in_fpr_pair_p): Refactor to use separate
        field parsing and emit ABI change warning for affected types.
        (riscv_pass_aggregate_in_fpr_and_gpr_p): Likewise.

gcc/testsuite/ChangeLog:

        * g++.target/riscv/abi/empty-struct+union-1.cc: New test.
        * g++.target/riscv/abi/empty-struct+union-2.cc: New test.
        * g++.target/riscv/abi/empty-struct+union-3.cc: New test.
        * g++.target/riscv/abi/empty-struct+union-4.cc: New test.
        * g++.target/riscv/abi/empty-struct-1.cc: New test.
        * g++.target/riscv/abi/empty-struct-2.cc: New test.
        * g++.target/riscv/abi/empty-struct-3.cc: New test.
        * g++.target/riscv/abi/empty-struct-4.cc: New test.
        * g++.target/riscv/abi/empty-struct-5.cc: New test.
        * g++.target/riscv/abi/empty-struct-6.cc: New test.
        * g++.target/riscv/abi/empty-struct-7.cc: New test.
        * g++.target/riscv/abi/empty-struct-8.cc: New test.
        * g++.target/riscv/abi/empty-struct-9.cc: New test.
        * g++.target/riscv/abi/empty-struct-10.cc: New test.
        * g++.target/riscv/abi/empty-struct-11.cc: New test.
        * g++.target/riscv/abi/empty-struct-12.cc: New test.
        * g++.target/riscv/abi/empty-union-1.cc: New test.
        * g++.target/riscv/abi/empty-union-2.cc: New test.
        * g++.target/riscv/abi/empty-union-3.cc: New test.
        * g++.target/riscv/abi/empty-union-4.cc: New test.
        * g++.target/riscv/riscv.exp: Add abi subdirectory.
        * gcc.target/riscv/abi/empty-struct+union-1.c: New test.
        * gcc.target/riscv/abi/empty-struct+union-2.c: New test.
        * gcc.target/riscv/abi/empty-struct+union-3.c: New test.
        * gcc.target/riscv/abi/empty-struct+union-4.c: New test.
        * gcc.target/riscv/abi/empty-struct-1.c: New test.
        * gcc.target/riscv/abi/empty-struct-2.c: New test.
        * gcc.target/riscv/abi/empty-struct-3.c: New test.
        * gcc.target/riscv/abi/empty-struct-4.c: New test.
        * gcc.target/riscv/abi/empty-struct-5.c: New test.
        * gcc.target/riscv/abi/empty-struct-6.c: New test.
        * gcc.target/riscv/abi/empty-struct-7.c: New test.
        * gcc.target/riscv/abi/empty-struct-8.c: New test.
        * gcc.target/riscv/abi/empty-struct-9.c: New test.
        * gcc.target/riscv/abi/empty-struct-10.c: New test.
        * gcc.target/riscv/abi/empty-struct-11.c: New test.
        * gcc.target/riscv/abi/empty-struct-12.c: New test.
        * gcc.target/riscv/abi/empty-union-1.c: New test.
        * gcc.target/riscv/abi/empty-union-2.c: New test.
        * gcc.target/riscv/abi/empty-union-3.c: New test.
        * gcc.target/riscv/abi/empty-union-4.c: New test.
        * gcc.target/riscv/riscv.exp: Add abi subdirectory.
---
 gcc/config/riscv/riscv.cc                     | 212 ++++++++++++++----
 gcc/testsuite/g++.dg/abi/param2.C             |   2 +-
 .../riscv/abi/empty-struct+union-1.cc         |  17 ++
 .../riscv/abi/empty-struct+union-2.cc         |  20 ++
 .../riscv/abi/empty-struct+union-3.cc         |  27 +++
 .../riscv/abi/empty-struct+union-4.cc         |  30 +++
 .../g++.target/riscv/abi/empty-struct-1.cc    |  15 ++
 .../g++.target/riscv/abi/empty-struct-10.cc   |  18 ++
 .../g++.target/riscv/abi/empty-struct-11.cc   |  23 ++
 .../g++.target/riscv/abi/empty-struct-12.cc   |  21 ++
 .../g++.target/riscv/abi/empty-struct-2.cc    |  18 ++
 .../g++.target/riscv/abi/empty-struct-3.cc    |  21 ++
 .../g++.target/riscv/abi/empty-struct-4.cc    |  24 ++
 .../g++.target/riscv/abi/empty-struct-5.cc    |  15 ++
 .../g++.target/riscv/abi/empty-struct-6.cc    |  18 ++
 .../g++.target/riscv/abi/empty-struct-7.cc    |  21 ++
 .../g++.target/riscv/abi/empty-struct-8.cc    |  24 ++
 .../g++.target/riscv/abi/empty-struct-9.cc    |  15 ++
 .../g++.target/riscv/abi/empty-union-1.cc     |  15 ++
 .../g++.target/riscv/abi/empty-union-2.cc     |  18 ++
 .../g++.target/riscv/abi/empty-union-3.cc     |  21 ++
 .../g++.target/riscv/abi/empty-union-4.cc     |  24 ++
 gcc/testsuite/g++.target/riscv/riscv.exp      |   1 +
 gcc/testsuite/gcc.dg/compat/pr83487-1_x.c     |   1 +
 gcc/testsuite/gcc.dg/compat/pr83487-1_y.c     |   1 +
 gcc/testsuite/gcc.dg/compat/pr83487-2_x.c     |   1 +
 gcc/testsuite/gcc.dg/compat/pr83487-2_y.c     |   1 +
 gcc/testsuite/gcc.dg/torture/pr28814.c        |   1 +
 .../riscv/abi/empty-struct+union-1.c          |  17 ++
 .../riscv/abi/empty-struct+union-2.c          |  20 ++
 .../riscv/abi/empty-struct+union-3.c          |  27 +++
 .../riscv/abi/empty-struct+union-4.c          |  30 +++
 .../gcc.target/riscv/abi/empty-struct-1.c     |  15 ++
 .../gcc.target/riscv/abi/empty-struct-10.c    |  18 ++
 .../gcc.target/riscv/abi/empty-struct-11.c    |  21 ++
 .../gcc.target/riscv/abi/empty-struct-12.c    |  24 ++
 .../gcc.target/riscv/abi/empty-struct-2.c     |  18 ++
 .../gcc.target/riscv/abi/empty-struct-3.c     |  21 ++
 .../gcc.target/riscv/abi/empty-struct-4.c     |  24 ++
 .../gcc.target/riscv/abi/empty-struct-5.c     |  15 ++
 .../gcc.target/riscv/abi/empty-struct-6.c     |  18 ++
 .../gcc.target/riscv/abi/empty-struct-7.c     |  21 ++
 .../gcc.target/riscv/abi/empty-struct-8.c     |  24 ++
 .../gcc.target/riscv/abi/empty-struct-9.c     |  15 ++
 .../gcc.target/riscv/abi/empty-union-1.c      |  15 ++
 .../gcc.target/riscv/abi/empty-union-2.c      |  18 ++
 .../gcc.target/riscv/abi/empty-union-3.c      |  21 ++
 .../gcc.target/riscv/abi/empty-union-4.c      |  24 ++
 gcc/testsuite/gcc.target/riscv/riscv.exp      |   2 +
 49 files changed, 987 insertions(+), 46 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct+union-1.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct+union-2.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct+union-3.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct+union-4.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-1.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-10.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-11.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-12.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-2.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-3.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-4.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-5.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-6.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-7.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-8.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-struct-9.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-union-1.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-union-2.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-union-3.cc
 create mode 100644 gcc/testsuite/g++.target/riscv/abi/empty-union-4.cc
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-10.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-11.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-12.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-6.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-7.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-8.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-struct-9.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-union-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-union-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-union-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/abi/empty-union-4.c

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 63404d3d514..01c9ee94a89 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -5886,11 +5886,47 @@ static int
 riscv_flatten_aggregate_field (const_tree type, riscv_aggregate_field *fields,
                               int n, HOST_WIDE_INT offset,
                               bool ignore_zero_width_bit_field_p,
+                              bool ignore_empty_union_and_zero_len_array_p,
                               bool vls_p = false, unsigned abi_vlen = 0)
 {
   int max_aggregate_field = vls_p ? 8 : 2;
   switch (TREE_CODE (type))
     {
+    case UNION_TYPE:
+      {
+       if (!ignore_empty_union_and_zero_len_array_p)
+         return -1;
+       /* Empty union should ignore.  */
+       if (TYPE_SIZE (type) == NULL || integer_zerop (TYPE_SIZE (type)))
+         return n;
+       /* Or all union member are empty union or empty struct. */
+       for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f))
+         {
+           if (TREE_CODE (f) != FIELD_DECL)
+             continue;
+           int m;
+           HOST_WIDE_INT pos = offset + int_byte_position (f);
+           switch (TREE_CODE (TREE_TYPE (f)))
+             {
+             case ARRAY_TYPE:
+             case UNION_TYPE:
+             case RECORD_TYPE:
+               m = riscv_flatten_aggregate_field (
+                     TREE_TYPE (f), fields, n, pos,
+                     ignore_zero_width_bit_field_p,
+                     true);
+               /* Any non-empty struct/union/array will stop the flatten.  */
+               if (m != n)
+                 return -1;
+               break;
+             default:
+               /* Any member are not struct, union or array will stop the
+                  flatten.  */
+             return -1;
+           }
+       }
+      return n;
+      }
     case RECORD_TYPE:
      /* Can't handle incomplete types nor sizes that are not fixed.  */
      if (!COMPLETE_TYPE_P (type)
@@ -5916,7 +5952,9 @@ riscv_flatten_aggregate_field (const_tree type, 
riscv_aggregate_field *fields,
              {
                HOST_WIDE_INT pos = offset + int_byte_position (f);
                n = riscv_flatten_aggregate_field (
-                 TREE_TYPE (f), fields, n, pos, ignore_zero_width_bit_field_p,
+                 TREE_TYPE (f), fields, n, pos,
+                 ignore_zero_width_bit_field_p,
+                 ignore_empty_union_and_zero_len_array_p,
                  vls_p, abi_vlen);
              }
            if (n < 0)
@@ -5930,14 +5968,20 @@ riscv_flatten_aggregate_field (const_tree type, 
riscv_aggregate_field *fields,
        riscv_aggregate_field subfields[8];
        tree index = TYPE_DOMAIN (type);
        tree elt_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
+
+       /* Array with zero size member should be ignored.  */
+       if (ignore_empty_union_and_zero_len_array_p && integer_zerop (elt_size))
+         return n;
+
        int n_subfields
-         = riscv_flatten_aggregate_field (TREE_TYPE (type), subfields, 0,
-                                          offset,
-                                          ignore_zero_width_bit_field_p, vls_p,
-                                          abi_vlen);
+         = riscv_flatten_aggregate_field (
+             TREE_TYPE (type), subfields, 0,
+             offset,
+             ignore_zero_width_bit_field_p,
+             ignore_empty_union_and_zero_len_array_p,
+             vls_p, abi_vlen);
        /* Can't handle incomplete types nor sizes that are not fixed.  */
-       if (n_subfields <= 0
-           || !COMPLETE_TYPE_P (type)
+       if (!COMPLETE_TYPE_P (type)
            || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
            || !index
            || !TYPE_MAX_VALUE (index)
@@ -5947,6 +5991,15 @@ riscv_flatten_aggregate_field (const_tree type, 
riscv_aggregate_field *fields,
            || !tree_fits_uhwi_p (elt_size))
          return -1;
 
+       /* Zero-length array with empty union/struct should be ignored.  */
+       if (ignore_empty_union_and_zero_len_array_p && n_subfields == 0
+           && integer_zerop (TYPE_MIN_VALUE (index))
+           && integer_all_onesp (TYPE_MAX_VALUE (index)))
+         return n;
+
+       if (n_subfields <= 0)
+         return -1;
+
        n_elts = 1 + tree_to_uhwi (TYPE_MAX_VALUE (index))
                   - tree_to_uhwi (TYPE_MIN_VALUE (index));
        gcc_assert (n_elts >= 0);
@@ -6026,14 +6079,25 @@ static int
 riscv_flatten_aggregate_argument (const_tree type,
                                  riscv_aggregate_field *fields,
                                  bool ignore_zero_width_bit_field_p,
+                                 bool ignore_empty_union_and_zero_len_array_p,
                                  bool vls_p = false, unsigned abi_vlen = 0)
 {
   if (!type || TREE_CODE (type) != RECORD_TYPE)
     return -1;
 
   return riscv_flatten_aggregate_field (type, fields, 0, 0,
-                                       ignore_zero_width_bit_field_p, vls_p,
-                                       abi_vlen);
+                                       ignore_zero_width_bit_field_p,
+                                       ignore_empty_union_and_zero_len_array_p,
+                                       vls_p, abi_vlen);
+}
+
+static bool
+riscv_any_non_float_type_field (riscv_aggregate_field *fields, int n)
+{
+  for (int i = 0; i < n; i++)
+    if (!SCALAR_FLOAT_TYPE_P (fields[i].type))
+      return true;
+  return false;
 }
 
 /* See whether TYPE is a record whose fields should be returned in one or
@@ -6044,24 +6108,18 @@ riscv_pass_aggregate_in_fpr_pair_p (const_tree type,
                                    riscv_aggregate_field fields[2])
 {
   static int warned = 0;
+  if (!type)
+    return 0;
 
   /* This is the old ABI, which differs for C++ and C.  */
-  int n_old = riscv_flatten_aggregate_argument (type, fields, false);
-  for (int i = 0; i < n_old; i++)
-    if (!SCALAR_FLOAT_TYPE_P (fields[i].type))
-      {
-       n_old = -1;
-       break;
-      }
+  int n_old = riscv_flatten_aggregate_argument (type, fields, false, false);
+  if (riscv_any_non_float_type_field (fields, n_old))
+    n_old = -1;
 
   /* This is the new ABI, which is the same for C++ and C.  */
-  int n_new = riscv_flatten_aggregate_argument (type, fields, true);
-  for (int i = 0; i < n_new; i++)
-    if (!SCALAR_FLOAT_TYPE_P (fields[i].type))
-      {
-       n_new = -1;
-       break;
-      }
+  int n_new = riscv_flatten_aggregate_argument (type, fields, true, false);
+  if (riscv_any_non_float_type_field (fields, n_new))
+    n_new = -1;
 
   if ((n_old != n_new) && (warned == 0))
     {
@@ -6070,7 +6128,58 @@ riscv_pass_aggregate_in_fpr_pair_p (const_tree type,
       warned = 1;
     }
 
-  return n_new > 0 ? n_new : 0;
+  /* ABI with fixing flatten empty union.  */
+  int n_new2 = riscv_flatten_aggregate_argument (type, fields, true, true);
+  if (riscv_any_non_float_type_field (fields, n_new2))
+    n_new2 = -1;
+
+  bool num_fpr = riscv_pass_mode_in_fpr_p (TYPE_MODE (type));
+
+  /* There is a special case for struct with zero length array with struct and 
a
+     floating point member.
+     e.g:
+     struct S0ae_1f {
+       struct {
+       } e1[0];
+       float f;
+     };
+
+     This case we will got 1, but legacy ABI will got -1, however legacy ABI
+     will got 1 in later logic, so we should consider this case as compatible.
+  */
+  bool compatible_p = n_new2 == 1 && n_new == -1 && num_fpr == 1;
+
+  if ((n_new2 != n_new)
+      && !compatible_p && (warned == 0))
+    {
+      warning (OPT_Wpsabi, "ABI for flattened empty union and zero "
+              "length array changed in GCC 16");
+      warned = 1;
+    }
+
+  return n_new2 > 0 ? n_new2 : 0;
+}
+
+struct riscv_aggregate_field_info_t {
+  unsigned num_fpr;
+  unsigned num_gpr;
+
+  riscv_aggregate_field_info_t ()
+    : num_fpr (0), num_gpr (0)
+  {}
+};
+
+static riscv_aggregate_field_info_t
+riscv_parse_aggregate_field_info (riscv_aggregate_field *fields, int n)
+{
+  riscv_aggregate_field_info_t info;
+  for (int i = 0; i < n; i++)
+    {
+      info.num_fpr += SCALAR_FLOAT_TYPE_P (fields[i].type);
+      info.num_gpr += INTEGRAL_TYPE_P (fields[i].type);
+    }
+
+  return info;
 }
 
 /* See whether TYPE is a record whose fields should be returned in one or
@@ -6084,35 +6193,48 @@ riscv_pass_aggregate_in_fpr_and_gpr_p (const_tree type,
   static int warned = 0;
 
   /* This is the old ABI, which differs for C++ and C.  */
-  unsigned num_int_old = 0, num_float_old = 0;
-  int n_old = riscv_flatten_aggregate_argument (type, fields, false);
-  for (int i = 0; i < n_old; i++)
-    {
-      num_float_old += SCALAR_FLOAT_TYPE_P (fields[i].type);
-      num_int_old += INTEGRAL_TYPE_P (fields[i].type);
-    }
+  int n_old = riscv_flatten_aggregate_argument (type, fields, false, false);
+  riscv_aggregate_field_info_t old_info;
+  old_info = riscv_parse_aggregate_field_info (fields, n_old);
 
   /* This is the new ABI, which is the same for C++ and C.  */
-  unsigned num_int_new = 0, num_float_new = 0;
-  int n_new = riscv_flatten_aggregate_argument (type, fields, true);
-  for (int i = 0; i < n_new; i++)
-    {
-      num_float_new += SCALAR_FLOAT_TYPE_P (fields[i].type);
-      num_int_new += INTEGRAL_TYPE_P (fields[i].type);
-    }
+  int n_new = riscv_flatten_aggregate_argument (type, fields, true, false);
+  riscv_aggregate_field_info_t new_info;
+  new_info = riscv_parse_aggregate_field_info (fields, n_new);
 
-  if (((num_int_old == 1 && num_float_old == 1
-       && (num_int_old != num_int_new || num_float_old != num_float_new))
-       || (num_int_new == 1 && num_float_new == 1
-          && (num_int_old != num_int_new || num_float_old != num_float_new)))
-      && (warned == 0))
+  bool values_changed = old_info.num_fpr != new_info.num_fpr
+                       || old_info.num_gpr != new_info.num_gpr;
+  bool old_is_one_one = old_info.num_fpr == 1 && old_info.num_gpr == 1;
+  bool new_is_one_one = new_info.num_fpr == 1 && new_info.num_gpr == 1;
+
+  if (values_changed
+      && (old_is_one_one || new_is_one_one)
+      && warned == 0)
     {
       warning (OPT_Wpsabi, "ABI for flattened struct with zero-length "
                           "bit-fields changed in GCC 10");
       warned = 1;
     }
 
-  return num_int_new == 1 && num_float_new == 1;
+  /* ABI with fixing flatten empty union.  */
+  int n_new2 = riscv_flatten_aggregate_argument (type, fields, true, true);
+  riscv_aggregate_field_info_t new2_info;
+  new2_info = riscv_parse_aggregate_field_info (fields, n_new2);
+
+  values_changed = new_info.num_fpr != new2_info.num_fpr
+                  || new_info.num_gpr != new2_info.num_gpr;
+  bool new2_is_one_one = new2_info.num_fpr == 1 && new2_info.num_gpr == 1;
+
+  if (values_changed
+      && (new_is_one_one || new2_is_one_one)
+      && warned == 0)
+    {
+      warning (OPT_Wpsabi, "ABI for flattened empty union and zero "
+              "length array changed in GCC 16");
+      warned = 1;
+    }
+
+  return new2_is_one_one;
 }
 
 /* Return the representation of an argument passed or returned in an FPR
@@ -6466,7 +6588,7 @@ riscv_pass_aggregate_in_vr (struct riscv_arg_info *info,
   riscv_aggregate_field fields[8];
   unsigned int abi_vlen = riscv_get_cc_abi_vlen (cum->variant_cc);
   int i;
-  int n = riscv_flatten_aggregate_argument (type, fields, true,
+  int n = riscv_flatten_aggregate_argument (type, fields, true, true,
                                            /* vls_p */ true, abi_vlen);
 
   if (n == -1)
diff --git a/gcc/testsuite/g++.dg/abi/param2.C 
b/gcc/testsuite/g++.dg/abi/param2.C
index d28387ab3cc..4752717bfe0 100644
--- a/gcc/testsuite/g++.dg/abi/param2.C
+++ b/gcc/testsuite/g++.dg/abi/param2.C
@@ -1,7 +1,7 @@
 // PR target/20795
 // Test passing aligned empty aggregate
 // { dg-do compile }
-// { dg-options "-Wno-psabi" { target { { i?86-*-* x86_64-*-* } && ilp32 } } }
+// { dg-options "-Wno-psabi" { target { { { i?86-*-* x86_64-*-* } && ilp32 } 
|| { riscv*-*-* } } } }
 
 struct S { union {} a; } __attribute__((aligned));
 
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-1.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-1.cc
new file mode 100644
index 00000000000..69a1350a661
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-1.cc
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Su2e_1f {
+  union {
+    struct {
+    } e1, e2;
+  } u;
+  float f;
+};
+struct Su2e_1f echo_Su2e_1f(int i, float f, struct Su2e_1f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-2.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-2.cc
new file mode 100644
index 00000000000..763477c2e49
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-2.cc
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Su2e_2f {
+  union {
+    struct {
+    } e1, e2;
+  } u;
+  float f;
+  float g;
+};
+struct Su2e_2f echo_Su2e_2f(int i, float f, struct Su2e_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+8 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+8 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-3.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-3.cc
new file mode 100644
index 00000000000..5c9ce31ca42
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-3.cc
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smu2e_1f {
+  union {
+    struct {
+    } e1, e2;
+  } u1;
+  struct {
+    float f;
+    union {
+      struct {
+      } e1, e2;
+    } u;
+  } ue;
+  union {
+    struct {
+    } e1, e2;
+  } u2;
+};
+struct Smu2e_1f echo_Smu2e_1f(int i, float f, struct Smu2e_1f s) /* { 
dg-warning "ABI for flattened empty union and zero length array changed in GCC 
16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-4.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-4.cc
new file mode 100644
index 00000000000..ecefc9439cf
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct+union-4.cc
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smu2e_2f {
+  union {
+    struct {
+    } e1, e2;
+  } u1;
+  struct {
+    float f;
+    float g;
+    union {
+      struct {
+      } e1, e2;
+    } u;
+  } ue;
+  union {
+    struct {
+    } e1, e2;
+  } u2;
+};
+struct Smu2e_2f echo_Smu2e_2f(int i, float f, struct Smu2e_2f s) /* { 
dg-warning "ABI for flattened empty union and zero length array changed in GCC 
16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+8 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+8 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-1.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-1.cc
new file mode 100644
index 00000000000..81f563ebbf8
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-1.cc
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Se_1f {
+  struct {
+  } e1;
+  float f;
+};
+struct Se_1f echo_Se_1f(int i, float f, struct Se_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-10.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-10.cc
new file mode 100644
index 00000000000..167f54cc757
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-10.cc
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S1ae_2f {
+  struct {
+  } e1[1];
+  float f;
+  float g;
+};
+struct S1ae_2f echo_S1ae_2f(int i, float f, struct S1ae_2f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(mem.*:DI .*\[.* s\+0 
.*\]\)[[:space:]]+\(reg.*:DI \d+ a1\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(mem.*:DI .*\[.* s\+8 
.*\]\)[[:space:]]+\(reg.*:DI \d+ a2\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:DI \d+ a0 
.*\)[[:space:]]+\(subreg:DI \(reg.*:TI \d+ \[ <retval> \]\) 0\)\)} "expand" } } 
*/
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:DI \d+ a1 
.*\)[[:space:]]+\(subreg:DI \(reg.*:TI \d+ \[ <retval> \]\) 8\)\)} "expand" } } 
*/
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-11.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-11.cc
new file mode 100644
index 00000000000..057994db819
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-11.cc
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm1ae_1f {
+  struct {
+  } e1[1];
+  struct {
+    float f;
+    struct {
+    } e[1];
+  } fe;
+  struct {
+  } e2[1];
+};
+struct Sm1ae_1f echo_Sm1ae_1f(int i, float f, struct Sm1ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(mem.*:DI .*\[.* s\+0 
.*\]\)[[:space:]]+\(reg.*:DI \d+ a1\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(mem.*:DI .*\[.* s\+8 
.*\]\)[[:space:]]+\(reg.*:DI \d+ a2\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:DI \d+ a0 
.*\)[[:space:]]+\(subreg:DI \(reg.*:TI \d+ \[ <retval> \]\) 0\)\)} "expand" } } 
*/
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:DI \d+ a1 
.*\)[[:space:]]+\(subreg:DI \(reg.*:TI \d+ \[ <retval> \]\) 8\)\)} "expand" } } 
*/
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-12.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-12.cc
new file mode 100644
index 00000000000..d8f015445f0
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-12.cc
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm1ae_2f {
+  struct {
+  } e1[1];
+  struct {
+    float f;
+    float g;
+    struct {
+    } e[1];
+  } fe;
+  struct {
+  } e2[1];
+};
+struct Sm1ae_2f echo_Sm1ae_2f(int i, float f, struct Sm1ae_2f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\[.* \.result_ptr\+0 .*\]} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-2.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-2.cc
new file mode 100644
index 00000000000..9d5669cb89e
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-2.cc
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Se_2f {
+  struct {
+  } e1;
+  float f;
+  float g;
+};
+struct Se_2f echo_Se_2f(int i, float f, struct Se_2f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+8 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+8 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-3.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-3.cc
new file mode 100644
index 00000000000..7b9e71ae9f5
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-3.cc
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sme_1f {
+  struct {
+  } e1;
+  struct {
+    float f;
+    struct {
+    } e;
+  } fe;
+  struct {
+  } e2;
+};
+struct Sme_1f echo_Sme_1f(int i, float f, struct Sme_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-4.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-4.cc
new file mode 100644
index 00000000000..aaec892983f
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-4.cc
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sme_2f {
+  struct {
+  } e1;
+  struct {
+    float f;
+    float g;
+    struct {
+    } e;
+  } fe;
+  struct {
+  } e2;
+};
+struct Sme_2f echo_Sme_2f(int i, float f, struct Sme_2f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+8 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+8 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-5.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-5.cc
new file mode 100644
index 00000000000..0ae1e41e0cf
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-5.cc
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S0ae_1f {
+  struct {
+  } e1[0];
+  float f;
+};
+struct S0ae_1f echo_S0ae_1f(int i, float f, struct S0ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-6.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-6.cc
new file mode 100644
index 00000000000..d3d0b65c71c
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-6.cc
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S0ae_2f {
+  struct {
+  } e1[0];
+  float f;
+  float g;
+};
+struct S0ae_2f echo_S0ae_2f(int i, float f, struct S0ae_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-7.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-7.cc
new file mode 100644
index 00000000000..9eae13d2f9f
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-7.cc
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm0ae_1f {
+  struct {
+  } e1[0];
+  struct {
+    float f;
+    struct {
+    } e[0];
+  } fe;
+  struct {
+  } e2[0];
+};
+struct Sm0ae_1f echo_Sm0ae_1f(int i, float f, struct Sm0ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-8.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-8.cc
new file mode 100644
index 00000000000..e7c81f4783e
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-8.cc
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm0ae_2f {
+  struct {
+  } e1[0];
+  struct {
+    float f;
+    float g;
+    struct {
+    } e[0];
+  } fe;
+  struct {
+  } e2[0];
+};
+struct Sm0ae_2f echo_Sm0ae_2f(int i, float f, struct Sm0ae_2f s) /* { 
dg-warning "ABI for flattened empty union and zero length array changed in GCC 
16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-struct-9.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-struct-9.cc
new file mode 100644
index 00000000000..d36d50b212b
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-struct-9.cc
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S1ae_1f {
+  struct {
+  } e1[1];
+  float f;
+};
+struct S1ae_1f echo_S1ae_1f(int i, float f, struct S1ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(mem.*:DI .*\[.* s\+0 
.*\]\)[[:space:]]+\(reg.*:DI \d+ a1\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:DI \d+ a0\)[[:space:]]+\(reg.*:DI 
\d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-union-1.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-union-1.cc
new file mode 100644
index 00000000000..e3c23766d4f
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-union-1.cc
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Seu_1f {
+  union {
+  } e1;
+  float f;
+};
+struct Seu_1f echo_Seu_1f(int i, float f, struct Seu_1f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-union-2.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-union-2.cc
new file mode 100644
index 00000000000..d7b7d05995e
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-union-2.cc
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S2eu_2f {
+  union {
+  } e1;
+  float f;
+  float g;
+};
+struct S2eu_2f echo_S2eu_2f(int i, float f, struct S2eu_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+8 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+8 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-union-3.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-union-3.cc
new file mode 100644
index 00000000000..f12af7ad138
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-union-3.cc
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smeu_1f {
+  union {
+  } e1;
+  struct {
+    float f;
+    union {
+    } e;
+  } fe;
+  union {
+  } e2;
+};
+struct Smeu_1f echo_Smeu_1f(int i, float f, struct Smeu_1f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/abi/empty-union-4.cc 
b/gcc/testsuite/g++.target/riscv/abi/empty-union-4.cc
new file mode 100644
index 00000000000..ab8c56e5cfb
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/abi/empty-union-4.cc
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smeu_2f {
+  union {
+  } e1;
+  struct {
+    float f;
+    float g;
+    union {
+    } e;
+  } fe;
+  union {
+  } e2;
+};
+struct Smeu_2f echo_Smeu_2f(int i, float f, struct Smeu_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+8 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+8 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/g++.target/riscv/riscv.exp 
b/gcc/testsuite/g++.target/riscv/riscv.exp
index f58e6884ba8..e268bd81412 100644
--- a/gcc/testsuite/g++.target/riscv/riscv.exp
+++ b/gcc/testsuite/g++.target/riscv/riscv.exp
@@ -29,6 +29,7 @@ dg-init
 
 # Main loop.
 dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] "" ""
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/abi/*.cc]] "" ""
 
 # All done.
 dg-finish
diff --git a/gcc/testsuite/gcc.dg/compat/pr83487-1_x.c 
b/gcc/testsuite/gcc.dg/compat/pr83487-1_x.c
index b5b208f7d93..22b71cf1856 100644
--- a/gcc/testsuite/gcc.dg/compat/pr83487-1_x.c
+++ b/gcc/testsuite/gcc.dg/compat/pr83487-1_x.c
@@ -1,4 +1,5 @@
 /* { dg-options "-fno-common" { target { hppa*-*-hpux* } } } */
+/* { dg-options "-Wno-psabi" { target { riscv*-*-* } } } */
 #include "pr83487-1.h"
 
 extern
diff --git a/gcc/testsuite/gcc.dg/compat/pr83487-1_y.c 
b/gcc/testsuite/gcc.dg/compat/pr83487-1_y.c
index ad336dd9936..cf275d8f33c 100644
--- a/gcc/testsuite/gcc.dg/compat/pr83487-1_y.c
+++ b/gcc/testsuite/gcc.dg/compat/pr83487-1_y.c
@@ -1,4 +1,5 @@
 /* { dg-options "-fno-common" { target { hppa*-*-hpux* } } } */
+/* { dg-options "-Wno-psabi" { target { riscv*-*-* } } } */
 #include "pr83487-1.h"
 
 struct A a;
diff --git a/gcc/testsuite/gcc.dg/compat/pr83487-2_x.c 
b/gcc/testsuite/gcc.dg/compat/pr83487-2_x.c
index 71031948c78..399ac86f1d3 100644
--- a/gcc/testsuite/gcc.dg/compat/pr83487-2_x.c
+++ b/gcc/testsuite/gcc.dg/compat/pr83487-2_x.c
@@ -1,3 +1,4 @@
 /* { dg-options "-fno-common" { target { hppa*-*-hpux* } } } */
+/* { dg-options "-Wno-psabi" { target { riscv*-*-* } } } */
 #define PR83487_LARGE
 #include "pr83487-1_x.c"
diff --git a/gcc/testsuite/gcc.dg/compat/pr83487-2_y.c 
b/gcc/testsuite/gcc.dg/compat/pr83487-2_y.c
index e1767839418..dc6c1f83618 100644
--- a/gcc/testsuite/gcc.dg/compat/pr83487-2_y.c
+++ b/gcc/testsuite/gcc.dg/compat/pr83487-2_y.c
@@ -1,3 +1,4 @@
 /* { dg-options "-fno-common" { target { hppa*-*-hpux* } } } */
+/* { dg-options "-Wno-psabi" { target { riscv*-*-* } } } */
 #define PR83487_LARGE
 #include "pr83487-1_y.c"
diff --git a/gcc/testsuite/gcc.dg/torture/pr28814.c 
b/gcc/testsuite/gcc.dg/torture/pr28814.c
index cf641ca9058..e835ff5520b 100644
--- a/gcc/testsuite/gcc.dg/torture/pr28814.c
+++ b/gcc/testsuite/gcc.dg/torture/pr28814.c
@@ -1,4 +1,5 @@
 /* { dg-do compile { target { ilp32 || lp64 } } } */
+/* { dg-options "-Wno-psabi" { target { riscv*-*-* } } } */
 
 struct w49
 {
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-1.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-1.c
new file mode 100644
index 00000000000..4cea38edd01
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-1.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Su2e_1f {
+  union {
+    struct {
+    } e1, e2;
+  } u;
+  float f;
+};
+struct Su2e_1f echo_Su2e_1f(int i, float f, struct Su2e_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-2.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-2.c
new file mode 100644
index 00000000000..e7271e208f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Su2e_2f {
+  union {
+    struct {
+    } e1, e2;
+  } u;
+  float f;
+  float g;
+};
+struct Su2e_2f echo_Su2e_2f(int i, float f, struct Su2e_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-3.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-3.c
new file mode 100644
index 00000000000..9743d4affdd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-3.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smu2e_1f {
+  union {
+    struct {
+    } e1, e2;
+  } u1;
+  struct {
+    float f;
+    union {
+      struct {
+      } e1, e2;
+    } u;
+  } ue;
+  union {
+    struct {
+    } e1, e2;
+  } u2;
+};
+struct Smu2e_1f echo_Smu2e_1f(int i, float f, struct Smu2e_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-4.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-4.c
new file mode 100644
index 00000000000..081ea68ac98
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct+union-4.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smu2e_2f {
+  union {
+    struct {
+    } e1, e2;
+  } u1;
+  struct {
+    float f;
+    float g;
+    union {
+      struct {
+      } e1, e2;
+    } u;
+  } ue;
+  union {
+    struct {
+    } e1, e2;
+  } u2;
+};
+struct Smu2e_2f echo_Smu2e_2f(int i, float f, struct Smu2e_2f s) /* { 
dg-warning "ABI for flattened empty union and zero length array changed in GCC 
16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-1.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-1.c
new file mode 100644
index 00000000000..a0a48665ab9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Se_1f {
+  struct {
+  } e1;
+  float f;
+};
+struct Se_1f echo_Se_1f(int i, float f, struct Se_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-10.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-10.c
new file mode 100644
index 00000000000..de8ad027944
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-10.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S1ae_2f {
+  struct {
+  } e1[1];
+  float f;
+  float g;
+};
+struct S1ae_2f echo_S1ae_2f(int i, float f, struct S1ae_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-11.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-11.c
new file mode 100644
index 00000000000..c99bb225588
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-11.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm1ae_1f {
+  struct {
+  } e1[1];
+  struct {
+    float f;
+    struct {
+    } e[1];
+  } fe;
+  struct {
+  } e2[1];
+};
+struct Sm1ae_1f echo_Sm1ae_1f(int i, float f, struct Sm1ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-12.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-12.c
new file mode 100644
index 00000000000..065ff02c036
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-12.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm1ae_2f {
+  struct {
+  } e1[1];
+  struct {
+    float f;
+    float g;
+    struct {
+    } e[1];
+  } fe;
+  struct {
+  } e2[1];
+};
+struct Sm1ae_2f echo_Sm1ae_2f(int i, float f, struct Sm1ae_2f s) /* { 
dg-warning "ABI for flattened empty union and zero length array changed in GCC 
16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-2.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-2.c
new file mode 100644
index 00000000000..93210fea105
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-2.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Se_2f {
+  struct {
+  } e1;
+  float f;
+  float g;
+};
+struct Se_2f echo_Se_2f(int i, float f, struct Se_2f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-3.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-3.c
new file mode 100644
index 00000000000..b8c3362f345
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sme_1f {
+  struct {
+  } e1;
+  struct {
+    float f;
+    struct {
+    } e;
+  } fe;
+  struct {
+  } e2;
+};
+struct Sme_1f echo_Sme_1f(int i, float f, struct Sme_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-4.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-4.c
new file mode 100644
index 00000000000..0ce36d138ec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-4.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sme_2f {
+  struct {
+  } e1;
+  struct {
+    float f;
+    float g;
+    struct {
+    } e;
+  } fe;
+  struct {
+  } e2;
+};
+struct Sme_2f echo_Sme_2f(int i, float f, struct Sme_2f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-5.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-5.c
new file mode 100644
index 00000000000..0ae1e41e0cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S0ae_1f {
+  struct {
+  } e1[0];
+  float f;
+};
+struct S0ae_1f echo_S0ae_1f(int i, float f, struct S0ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-6.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-6.c
new file mode 100644
index 00000000000..d3d0b65c71c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-6.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S0ae_2f {
+  struct {
+  } e1[0];
+  float f;
+  float g;
+};
+struct S0ae_2f echo_S0ae_2f(int i, float f, struct S0ae_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-7.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-7.c
new file mode 100644
index 00000000000..9eae13d2f9f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-7.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm0ae_1f {
+  struct {
+  } e1[0];
+  struct {
+    float f;
+    struct {
+    } e[0];
+  } fe;
+  struct {
+  } e2[0];
+};
+struct Sm0ae_1f echo_Sm0ae_1f(int i, float f, struct Sm0ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-8.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-8.c
new file mode 100644
index 00000000000..e7c81f4783e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-8.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Sm0ae_2f {
+  struct {
+  } e1[0];
+  struct {
+    float f;
+    float g;
+    struct {
+    } e[0];
+  } fe;
+  struct {
+  } e2[0];
+};
+struct Sm0ae_2f echo_Sm0ae_2f(int i, float f, struct Sm0ae_2f s) /* { 
dg-warning "ABI for flattened empty union and zero length array changed in GCC 
16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-struct-9.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-9.c
new file mode 100644
index 00000000000..55c4be44d9d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-struct-9.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S1ae_1f {
+  struct {
+  } e1[1];
+  float f;
+};
+struct S1ae_1f echo_S1ae_1f(int i, float f, struct S1ae_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-union-1.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-union-1.c
new file mode 100644
index 00000000000..17beb0dfb19
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-union-1.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Seu_1f {
+  union {
+  } e1;
+  float f;
+};
+struct Seu_1f echo_Seu_1f(int i, float f, struct Seu_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-union-2.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-union-2.c
new file mode 100644
index 00000000000..c583186aa4f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-union-2.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct S2eu_2f {
+  union {
+  } e1;
+  float f;
+  float g;
+};
+struct S2eu_2f echo_S2eu_2f(int i, float f, struct S2eu_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-union-3.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-union-3.c
new file mode 100644
index 00000000000..e9e189bb244
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-union-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smeu_1f {
+  union {
+  } e1;
+  struct {
+    float f;
+    union {
+    } e;
+  } fe;
+  union {
+  } e2;
+};
+struct Smeu_1f echo_Smeu_1f(int i, float f, struct Smeu_1f s) {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/abi/empty-union-4.c 
b/gcc/testsuite/gcc.target/riscv/abi/empty-union-4.c
new file mode 100644
index 00000000000..91c2d898a0c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/abi/empty-union-4.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64g -mabi=lp64d -fdump-rtl-expand" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct Smeu_2f {
+  union {
+  } e1;
+  struct {
+    float f;
+    float g;
+    union {
+    } e;
+  } fe;
+  union {
+  } e2;
+};
+struct Smeu_2f echo_Smeu_2f(int i, float f, struct Smeu_2f s) /* { dg-warning 
"ABI for flattened empty union and zero length array changed in GCC 16" } */ {
+  return s;
+}
+
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa1 \[ s \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ .*\)[[:space:]]+\(reg.*:SF 
\d+ fa2 \[ s\+4 \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa0\)[[:space:]]+\(reg.*:SF \d+ \[ <retval> \]\)\)} "expand" } } */
+/* { dg-final { scan-rtl-dump {\(set \(reg.*:SF \d+ 
fa1\)[[:space:]]+\(reg.*:SF \d+ \[ <retval>\+4 \]\)\)} "expand" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/riscv.exp 
b/gcc/testsuite/gcc.target/riscv/riscv.exp
index b5e7618f704..dd8443df189 100644
--- a/gcc/testsuite/gcc.target/riscv/riscv.exp
+++ b/gcc/testsuite/gcc.target/riscv/riscv.exp
@@ -42,6 +42,8 @@ gcc-dg-runtest [lsort [glob -nocomplain 
$srcdir/$subdir/sched1-spills/*.{\[cS\],
        "" $DEFAULT_CFLAGS
 gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/xandes/*.\[cS\]]] \
        "" $DEFAULT_CFLAGS
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/abi/*.\[cS\]]] \
+       "" $DEFAULT_CFLAGS
 
 # Saturation alu
 foreach opt {
-- 
2.34.1

Reply via email to