diff mbox series

testsuite: Extend C++ struct-layout-1.exp testing to test C++14 vs. C++17 interoperability of structs with empty bases [PR94383]

Message ID 20200421095702.GY2424@tucnak
State New
Headers show
Series testsuite: Extend C++ struct-layout-1.exp testing to test C++14 vs. C++17 interoperability of structs with empty bases [PR94383] | expand

Commit Message

Jakub Jelinek April 21, 2020, 9:57 a.m. UTC
Hi!

Jonathan reported an ABI incompatibility between C++14 and C++17 in
passing some aggregates with empty bases on aarch64 (and apparently on arm
too).

The following patch adds 3000 (by default) tests for such interoperability,
using the struct-layout-1* framework.  The current 3000 tests are generated
as is (so unchanged from previous ones), and afterwards there is another set
of 3000 ones, where always one of the tNNN_x.C and tNNN_y.C tests get added
-std=c++14 -DCXX14_VS_CXX17 and another one -std=c++17 -DCXX14_VS_CXX17
options (which one which is chosen pseudo-randomly), which causes the
structs to have an empty base.

I haven't added (yet) checks if the alternate compiler does support these
options (I think that can be done incrementally), so for now this testing is
done only if the alternate compiler is not used.

I had to fix a bug in the flexible array handling, because while we were
lucky in the 3000 generated tests not to have toplevel fields after field
with flexible array members, in the next 3000 we aren't lucky anymore.
But even with that change, diff -upr between old and new
testsuite/g++/g++.dg/g++.dg-struct-layout-1/ doesn't show any differences
except for the ^Only in... messages for the new tests in there.

Bootstrapped/regtested on x86_64-linux and i686-linux and additionally
tested on aarch64-linux, where
FAIL: tmpdir-g++.dg-struct-layout-1/t032 cp_compat_x_tst.o-cp_compat_y_tst.o execute 
FAIL: tmpdir-g++.dg-struct-layout-1/t056 cp_compat_x_tst.o-cp_compat_y_tst.o execute 
FAIL: tmpdir-g++.dg-struct-layout-1/t057 cp_compat_x_tst.o-cp_compat_y_tst.o execute 
FAIL: tmpdir-g++.dg-struct-layout-1/t058 cp_compat_x_tst.o-cp_compat_y_tst.o execute 
FAIL: tmpdir-g++.dg-struct-layout-1/t059 cp_compat_x_tst.o-cp_compat_y_tst.o execute 
because of the backend bug, and with that bug fixed it succeeds.
Matthew has kindly tested it also on aarch64-linux and arm*-*.

The primary goal of the patch is catch if some targets other than aarch64 or
arm aren't affected too.

Ok for trunk?

2020-04-21  Jakub Jelinek  <jakub@redhat.com>

	PR c++/94383
	* g++.dg/compat/struct-layout-1.exp: If !$use_alt, add -c to generator
	args.
	* g++.dg/compat/struct-layout-1_generate.c (dg_options): Add another
	%s to the start of dg-options arg.
	(cxx14_vs_cxx17, do_cxx14_vs_cxx17): New variables.
	(switchfiles): If cxx14_vs_cxx17, prepend -std=c++14 -DCXX14_VS_CXX17
	or -std=c++17 -DCXX17_VS_CXX14 - randomly - to dg-options.
	(output): Don't append further fields once one with flexible array
	member is added.
	(generate_random_tests): Don't use toplevel unions if cxx14_vs_cxx17.
	(main): If -c, emit second set of tests for -std=c++14 vs. -std=c++17
	testing.
	* g++.dg/compat/struct-layout-1_x1.h (empty_base): New type.
	(EMPTY_BASE): Define.
	(TX): Use EMPTY_BASE.
	* g++.dg/compat/struct-layout-1_y1.h (empty_base): New type.
	(EMPTY_BASE): Define.
	(TX): Use EMPTY_BASE.



	Jakub

Comments

Jason Merrill April 21, 2020, 2:57 p.m. UTC | #1
On 4/21/20 5:57 AM, Jakub Jelinek wrote:
> Hi!
> 
> Jonathan reported an ABI incompatibility between C++14 and C++17 in
> passing some aggregates with empty bases on aarch64 (and apparently on arm
> too).
> 
> The following patch adds 3000 (by default) tests for such interoperability,
> using the struct-layout-1* framework.  The current 3000 tests are generated
> as is (so unchanged from previous ones), and afterwards there is another set
> of 3000 ones, where always one of the tNNN_x.C and tNNN_y.C tests get added
> -std=c++14 -DCXX14_VS_CXX17 and another one -std=c++17 -DCXX14_VS_CXX17
> options (which one which is chosen pseudo-randomly), which causes the
> structs to have an empty base.
> 
> I haven't added (yet) checks if the alternate compiler does support these
> options (I think that can be done incrementally), so for now this testing is
> done only if the alternate compiler is not used.
> 
> I had to fix a bug in the flexible array handling, because while we were
> lucky in the 3000 generated tests not to have toplevel fields after field
> with flexible array members, in the next 3000 we aren't lucky anymore.
> But even with that change, diff -upr between old and new
> testsuite/g++/g++.dg/g++.dg-struct-layout-1/ doesn't show any differences
> except for the ^Only in... messages for the new tests in there.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux and additionally
> tested on aarch64-linux, where
> FAIL: tmpdir-g++.dg-struct-layout-1/t032 cp_compat_x_tst.o-cp_compat_y_tst.o execute
> FAIL: tmpdir-g++.dg-struct-layout-1/t056 cp_compat_x_tst.o-cp_compat_y_tst.o execute
> FAIL: tmpdir-g++.dg-struct-layout-1/t057 cp_compat_x_tst.o-cp_compat_y_tst.o execute
> FAIL: tmpdir-g++.dg-struct-layout-1/t058 cp_compat_x_tst.o-cp_compat_y_tst.o execute
> FAIL: tmpdir-g++.dg-struct-layout-1/t059 cp_compat_x_tst.o-cp_compat_y_tst.o execute
> because of the backend bug, and with that bug fixed it succeeds.
> Matthew has kindly tested it also on aarch64-linux and arm*-*.
> 
> The primary goal of the patch is catch if some targets other than aarch64 or
> arm aren't affected too.
> 
> Ok for trunk?

OK.

> 2020-04-21  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/94383
> 	* g++.dg/compat/struct-layout-1.exp: If !$use_alt, add -c to generator
> 	args.
> 	* g++.dg/compat/struct-layout-1_generate.c (dg_options): Add another
> 	%s to the start of dg-options arg.
> 	(cxx14_vs_cxx17, do_cxx14_vs_cxx17): New variables.
> 	(switchfiles): If cxx14_vs_cxx17, prepend -std=c++14 -DCXX14_VS_CXX17
> 	or -std=c++17 -DCXX17_VS_CXX14 - randomly - to dg-options.
> 	(output): Don't append further fields once one with flexible array
> 	member is added.
> 	(generate_random_tests): Don't use toplevel unions if cxx14_vs_cxx17.
> 	(main): If -c, emit second set of tests for -std=c++14 vs. -std=c++17
> 	testing.
> 	* g++.dg/compat/struct-layout-1_x1.h (empty_base): New type.
> 	(EMPTY_BASE): Define.
> 	(TX): Use EMPTY_BASE.
> 	* g++.dg/compat/struct-layout-1_y1.h (empty_base): New type.
> 	(EMPTY_BASE): Define.
> 	(TX): Use EMPTY_BASE.
> 
> --- gcc/testsuite/g++.dg/compat/struct-layout-1.exp.jj	2020-01-12 11:54:37.044403889 +0100
> +++ gcc/testsuite/g++.dg/compat/struct-layout-1.exp	2020-04-20 19:00:54.956968103 +0200
> @@ -142,6 +142,9 @@ if { $status == 0 } then {
>       file delete -force $tstobjdir
>       file mkdir $tstobjdir
>       set generator_args "-s $srcdir/$subdir -d $tstobjdir"
> +    if { $use_alt == 0 } then {
> +	set generator_args "$generator_args -c"
> +    }
>       if [info exists env(RUN_ALL_COMPAT_TESTS) ] then {
>   	set generator_args "$generator_args -n 15000"
>       }
> --- gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c.jj	2020-01-12 11:54:37.045403874 +0100
> +++ gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c	2020-04-20 20:00:35.428544841 +0200
> @@ -1,5 +1,5 @@
>   /* Structure layout test generator.
> -   Copyright (C) 2004-2014
> +   Copyright (C) 2004-2020
>      Free Software Foundation, Inc.
>      Contributed by Jakub Jelinek <jakub@redhat.com>.
>   
> @@ -44,12 +44,12 @@ along with GCC; see the file COPYING3.
>   #endif
>   
>   const char *dg_options[] = {
> -"/* { dg-options \"%s-I%s -Wno-abi\" } */\n",
> -"/* { dg-options \"%s-I%s -mno-mmx -Wno-abi\" { target i?86-*-* x86_64-*-* } } */\n",
> -"/* { dg-options \"%s-I%s -fno-common\" { target hppa*-*-hpux* powerpc*-*-darwin* *-*-mingw32* *-*-cygwin* } } */\n",
> -"/* { dg-options \"%s-I%s -mno-mmx -fno-common -Wno-abi\" { target i?86-*-darwin* x86_64-*-darwin* i?86-*-mingw32* x86_64-*-mingw32* i?86-*-cygwin* } } */\n",
> -"/* { dg-options \"%s-I%s -mno-base-addresses\" { target mmix-*-* } } */\n",
> -"/* { dg-options \"%s-I%s -mlongcalls -mtext-section-literals\" { target xtensa*-*-* } } */\n"
> +"/* { dg-options \"%s%s-I%s -Wno-abi\" } */\n",
> +"/* { dg-options \"%s%s-I%s -mno-mmx -Wno-abi\" { target i?86-*-* x86_64-*-* } } */\n",
> +"/* { dg-options \"%s%s-I%s -fno-common\" { target hppa*-*-hpux* powerpc*-*-darwin* *-*-mingw32* *-*-cygwin* } } */\n",
> +"/* { dg-options \"%s%s-I%s -mno-mmx -fno-common -Wno-abi\" { target i?86-*-darwin* x86_64-*-darwin* i?86-*-mingw32* x86_64-*-mingw32* i?86-*-cygwin* } } */\n",
> +"/* { dg-options \"%s%s-I%s -mno-base-addresses\" { target mmix-*-* } } */\n",
> +"/* { dg-options \"%s%s-I%s -mlongcalls -mtext-section-literals\" { target xtensa*-*-* } } */\n"
>   #define NDG_OPTIONS (sizeof (dg_options) / sizeof (dg_options[0]))
>   };
>   
> @@ -508,6 +508,8 @@ static int short_enums;
>   static const char *destdir;
>   static const char *srcdir;
>   static const char *srcdir_safe;
> +static int cxx14_vs_cxx17;
> +static int do_cxx14_vs_cxx17;
>   FILE *outfile;
>   
>   void
> @@ -516,6 +518,8 @@ switchfiles (int fields)
>     static int filecnt;
>     static char *destbuf, *destptr;
>     int i;
> +  int cxx14_first = 0;
> +  const char *cxxnn = "";
>   
>     ++filecnt;
>     if (outfile)
> @@ -545,8 +549,15 @@ switchfiles (int fields)
>         exit (1);
>       }
>   
> +  if (cxx14_vs_cxx17)
> +    {
> +      cxx14_first = generate_random () & 1;
> +      cxxnn = (cxx14_first
> +	       ? "-std=c++14 -DCXX14_VS_CXX17 "
> +	       : "-std=c++17 -DCXX14_VS_CXX17 ");
> +    }
>     for (i = 0; i < NDG_OPTIONS; i++)
> -    fprintf (outfile, dg_options[i], "", srcdir_safe);
> +    fprintf (outfile, dg_options[i], "", "", srcdir_safe);
>     fprintf (outfile, "\n\
>   #include \"struct-layout-1.h\"\n\
>   \n\
> @@ -572,7 +583,7 @@ int main (void)\n\
>     if (outfile == NULL)
>       goto fail;
>     for (i = 0; i < NDG_OPTIONS; i++)
> -    fprintf (outfile, dg_options[i], "-w ", srcdir_safe);
> +    fprintf (outfile, dg_options[i], cxxnn, "-w ", srcdir_safe);
>     fprintf (outfile, "\n\
>   #include \"struct-layout-1_x1.h\"\n\
>   #include \"t%03d_test.h\"\n\
> @@ -583,8 +594,12 @@ int main (void)\n\
>     outfile = fopen (destbuf, "w");
>     if (outfile == NULL)
>       goto fail;
> +  if (cxx14_vs_cxx17)
> +    cxxnn = (cxx14_first
> +	     ? "-std=c++17 -DCXX14_VS_CXX17 "
> +	     : "-std=c++14 -DCXX14_VS_CXX17 ");
>     for (i = 0; i < NDG_OPTIONS; i++)
> -    fprintf (outfile, dg_options[i], "-w ", srcdir_safe);
> +    fprintf (outfile, dg_options[i], cxxnn, "-w ", srcdir_safe);
>     fprintf (outfile, "\n\
>   #include \"struct-layout-1_y1.h\"\n\
>   #include \"t%03d_test.h\"\n\
> @@ -1167,7 +1182,7 @@ e_insert (struct entry *e)
>   void
>   output (struct entry *e)
>   {
> -  int i;
> +  int i, flex, len;
>     char c;
>     struct entry *n;
>   
> @@ -1190,9 +1205,17 @@ output (struct entry *e)
>       fprintf (outfile, "U(%d,", idx);
>     c = 'a';
>   
> -  int flex = 0;
> +  flex = 0;
> +  len = e[0].len;
>     for (i = 1; i <= e[0].len; )
> -    i += subfield (e + i, &c, &flex, 0);
> +    {
> +      if (flex)
> +	{
> +	  e[0].len = i - 1;
> +	  break;
> +	}
> +      i += subfield (e + i, &c, &flex, 0);
> +    }
>     
>     fputs (",", outfile);
>     c = 'a';
> @@ -1202,6 +1225,7 @@ output (struct entry *e)
>         if (e[0].etype == ETYPE_UNION)
>   	break;
>       }
> +  e[0].len = len;
>     fputs (")\n", outfile);
>     if (output_one && idx == limidx)
>       exit (0);
> @@ -1539,7 +1563,7 @@ generate_random_tests (enum FEATURE feat
>       abort ();
>     memset (e, 0, sizeof (e));
>     r = generate_random ();
> -  if ((r & 7) == 0)
> +  if ((r & 7) == 0 && !cxx14_vs_cxx17)
>       e[0].etype = ETYPE_UNION;
>     else
>       e[0].etype = ETYPE_STRUCT;
> @@ -1577,7 +1601,7 @@ main (int argc, char **argv)
>         if (argv[i][0] == '-' && argv[i][2] == '\0')
>   	c = argv[i][1];
>         optarg = argv[i + 1];
> -      if (!optarg)
> +      if (!optarg && c != 'e' && c != 'c')
>   	goto usage;
>         switch (c)
>   	{
> @@ -1598,6 +1622,10 @@ main (int argc, char **argv)
>   	  short_enums = 1;
>   	  i--;
>   	  break;
> +	case 'c':
> +	  do_cxx14_vs_cxx17 = 1;
> +	  i--;
> +	  break;
>   	default:
>   	  fprintf (stderr, "unrecognized option %s\n", argv[i]);
>   	  goto usage;
> @@ -1614,13 +1642,18 @@ main (int argc, char **argv)
>   	  return 1;
>   	}
>         n = limidx + 1;
> +      if (do_cxx14_vs_cxx17)
> +	{
> +	  fputs ("-c is incompatible with -i", stderr);
> +	  return 1;
> +	}
>       }
>   
>     if (destdir == NULL && !output_one)
>       {
>       usage:
>         fprintf (stderr, "Usage:\n\
> -%s [-e] [-s srcdir -d destdir] [-n count] [-i idx]\n\
> +%s [-e] [-c] [-s srcdir -d destdir] [-n count] [-i idx]\n\
>   Either -s srcdir -d destdir or -i idx must be used\n", argv[0]);
>         return 1;
>       }
> @@ -1650,6 +1683,7 @@ Either -s srcdir -d destdir or -i idx mu
>     for (i = 0; i < NATYPES2; ++i)
>       if (attrib_types[i].bitfld)
>         aligned_bitfld_types[n_aligned_bitfld_types++] = attrib_types[i];
> +repeat:;
>     for (i = 0; i < sizeof (features) / sizeof (features[0]); ++i)
>       {
>         int startidx = idx;
> @@ -1696,6 +1730,14 @@ Either -s srcdir -d destdir or -i idx mu
>       limidx = idx;
>     while (idx < n)
>       generate_random_tests (ALL_FEATURES, 1 + (generate_random () % 25));
> +  if (do_cxx14_vs_cxx17)
> +    {
> +      cxx14_vs_cxx17 = 1;
> +      do_cxx14_vs_cxx17 = 0;
> +      limidx = 0;
> +      idx = 0;
> +      goto repeat;
> +    }
>     fclose (outfile);
>     return 0;
>   }
> --- gcc/testsuite/g++.dg/compat/struct-layout-1_x1.h.jj	2020-01-11 16:31:54.558300646 +0100
> +++ gcc/testsuite/g++.dg/compat/struct-layout-1_x1.h	2020-04-20 18:59:10.307557481 +0200
> @@ -35,8 +35,14 @@ int fn9 (void) { return 9; }
>     s##n.x = v;							\
>     a##n[2].x = w;						\
>     ++j;
> +#ifdef CXX14_VS_CXX17
> +struct empty_base {};
> +#define EMPTY_BASE : public empty_base
> +#else
> +#define EMPTY_BASE
> +#endif
>   #define TX(n, type, attrs, fields, ops) 			\
> -type S##n { fields } attrs;					\
> +type S##n EMPTY_BASE { fields } attrs;				\
>   type S##n s##n;							\
>   extern type S##n a##n[5];					\
>   extern type S##n check##n (type S##n, type S##n *,		\
> --- gcc/testsuite/g++.dg/compat/struct-layout-1_y1.h.jj	2020-01-11 16:31:54.558300646 +0100
> +++ gcc/testsuite/g++.dg/compat/struct-layout-1_y1.h	2020-04-20 18:59:51.045938756 +0200
> @@ -37,8 +37,14 @@
>       FAIL (n, 56);						\
>     ret.x = s##n.x;						\
>     ++j;
> +#ifdef CXX14_VS_CXX17
> +struct empty_base {};
> +#define EMPTY_BASE : public empty_base
> +#else
> +#define EMPTY_BASE
> +#endif
>   #define TX(n, type, attrs, fields, ops) 			\
> -type S##n { fields } attrs;					\
> +type S##n EMPTY_BASE { fields } attrs;				\
>   extern type S##n s##n;						\
>   type S##n a##n[5];						\
>   type S##n							\
> 
> 
> 	Jakub
>
diff mbox series

Patch

--- gcc/testsuite/g++.dg/compat/struct-layout-1.exp.jj	2020-01-12 11:54:37.044403889 +0100
+++ gcc/testsuite/g++.dg/compat/struct-layout-1.exp	2020-04-20 19:00:54.956968103 +0200
@@ -142,6 +142,9 @@  if { $status == 0 } then {
     file delete -force $tstobjdir
     file mkdir $tstobjdir
     set generator_args "-s $srcdir/$subdir -d $tstobjdir"
+    if { $use_alt == 0 } then {
+	set generator_args "$generator_args -c"
+    }
     if [info exists env(RUN_ALL_COMPAT_TESTS) ] then {
 	set generator_args "$generator_args -n 15000"
     }
--- gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c.jj	2020-01-12 11:54:37.045403874 +0100
+++ gcc/testsuite/g++.dg/compat/struct-layout-1_generate.c	2020-04-20 20:00:35.428544841 +0200
@@ -1,5 +1,5 @@ 
 /* Structure layout test generator.
-   Copyright (C) 2004-2014
+   Copyright (C) 2004-2020
    Free Software Foundation, Inc.
    Contributed by Jakub Jelinek <jakub@redhat.com>.
 
@@ -44,12 +44,12 @@  along with GCC; see the file COPYING3.
 #endif
 
 const char *dg_options[] = {
-"/* { dg-options \"%s-I%s -Wno-abi\" } */\n",
-"/* { dg-options \"%s-I%s -mno-mmx -Wno-abi\" { target i?86-*-* x86_64-*-* } } */\n",
-"/* { dg-options \"%s-I%s -fno-common\" { target hppa*-*-hpux* powerpc*-*-darwin* *-*-mingw32* *-*-cygwin* } } */\n",
-"/* { dg-options \"%s-I%s -mno-mmx -fno-common -Wno-abi\" { target i?86-*-darwin* x86_64-*-darwin* i?86-*-mingw32* x86_64-*-mingw32* i?86-*-cygwin* } } */\n",
-"/* { dg-options \"%s-I%s -mno-base-addresses\" { target mmix-*-* } } */\n",
-"/* { dg-options \"%s-I%s -mlongcalls -mtext-section-literals\" { target xtensa*-*-* } } */\n"
+"/* { dg-options \"%s%s-I%s -Wno-abi\" } */\n",
+"/* { dg-options \"%s%s-I%s -mno-mmx -Wno-abi\" { target i?86-*-* x86_64-*-* } } */\n",
+"/* { dg-options \"%s%s-I%s -fno-common\" { target hppa*-*-hpux* powerpc*-*-darwin* *-*-mingw32* *-*-cygwin* } } */\n",
+"/* { dg-options \"%s%s-I%s -mno-mmx -fno-common -Wno-abi\" { target i?86-*-darwin* x86_64-*-darwin* i?86-*-mingw32* x86_64-*-mingw32* i?86-*-cygwin* } } */\n",
+"/* { dg-options \"%s%s-I%s -mno-base-addresses\" { target mmix-*-* } } */\n",
+"/* { dg-options \"%s%s-I%s -mlongcalls -mtext-section-literals\" { target xtensa*-*-* } } */\n"
 #define NDG_OPTIONS (sizeof (dg_options) / sizeof (dg_options[0]))
 };
 
@@ -508,6 +508,8 @@  static int short_enums;
 static const char *destdir;
 static const char *srcdir;
 static const char *srcdir_safe;
+static int cxx14_vs_cxx17;
+static int do_cxx14_vs_cxx17;
 FILE *outfile;
 
 void
@@ -516,6 +518,8 @@  switchfiles (int fields)
   static int filecnt;
   static char *destbuf, *destptr;
   int i;
+  int cxx14_first = 0;
+  const char *cxxnn = "";
 
   ++filecnt;
   if (outfile)
@@ -545,8 +549,15 @@  switchfiles (int fields)
       exit (1);
     }
 
+  if (cxx14_vs_cxx17)
+    {
+      cxx14_first = generate_random () & 1;
+      cxxnn = (cxx14_first
+	       ? "-std=c++14 -DCXX14_VS_CXX17 "
+	       : "-std=c++17 -DCXX14_VS_CXX17 ");
+    }
   for (i = 0; i < NDG_OPTIONS; i++)
-    fprintf (outfile, dg_options[i], "", srcdir_safe);
+    fprintf (outfile, dg_options[i], "", "", srcdir_safe);
   fprintf (outfile, "\n\
 #include \"struct-layout-1.h\"\n\
 \n\
@@ -572,7 +583,7 @@  int main (void)\n\
   if (outfile == NULL)
     goto fail;
   for (i = 0; i < NDG_OPTIONS; i++)
-    fprintf (outfile, dg_options[i], "-w ", srcdir_safe);
+    fprintf (outfile, dg_options[i], cxxnn, "-w ", srcdir_safe);
   fprintf (outfile, "\n\
 #include \"struct-layout-1_x1.h\"\n\
 #include \"t%03d_test.h\"\n\
@@ -583,8 +594,12 @@  int main (void)\n\
   outfile = fopen (destbuf, "w");
   if (outfile == NULL)
     goto fail;
+  if (cxx14_vs_cxx17)
+    cxxnn = (cxx14_first
+	     ? "-std=c++17 -DCXX14_VS_CXX17 "
+	     : "-std=c++14 -DCXX14_VS_CXX17 ");
   for (i = 0; i < NDG_OPTIONS; i++)
-    fprintf (outfile, dg_options[i], "-w ", srcdir_safe);
+    fprintf (outfile, dg_options[i], cxxnn, "-w ", srcdir_safe);
   fprintf (outfile, "\n\
 #include \"struct-layout-1_y1.h\"\n\
 #include \"t%03d_test.h\"\n\
@@ -1167,7 +1182,7 @@  e_insert (struct entry *e)
 void
 output (struct entry *e)
 {
-  int i;
+  int i, flex, len;
   char c;
   struct entry *n;
 
@@ -1190,9 +1205,17 @@  output (struct entry *e)
     fprintf (outfile, "U(%d,", idx);
   c = 'a';
 
-  int flex = 0;
+  flex = 0;
+  len = e[0].len;
   for (i = 1; i <= e[0].len; )
-    i += subfield (e + i, &c, &flex, 0);
+    {
+      if (flex)
+	{
+	  e[0].len = i - 1;
+	  break;
+	}
+      i += subfield (e + i, &c, &flex, 0);
+    }
   
   fputs (",", outfile);
   c = 'a';
@@ -1202,6 +1225,7 @@  output (struct entry *e)
       if (e[0].etype == ETYPE_UNION)
 	break;
     }
+  e[0].len = len;
   fputs (")\n", outfile);
   if (output_one && idx == limidx)
     exit (0);
@@ -1539,7 +1563,7 @@  generate_random_tests (enum FEATURE feat
     abort ();
   memset (e, 0, sizeof (e));
   r = generate_random ();
-  if ((r & 7) == 0)
+  if ((r & 7) == 0 && !cxx14_vs_cxx17)
     e[0].etype = ETYPE_UNION;
   else
     e[0].etype = ETYPE_STRUCT;
@@ -1577,7 +1601,7 @@  main (int argc, char **argv)
       if (argv[i][0] == '-' && argv[i][2] == '\0')
 	c = argv[i][1];
       optarg = argv[i + 1];
-      if (!optarg)
+      if (!optarg && c != 'e' && c != 'c')
 	goto usage;
       switch (c)
 	{
@@ -1598,6 +1622,10 @@  main (int argc, char **argv)
 	  short_enums = 1;
 	  i--;
 	  break;
+	case 'c':
+	  do_cxx14_vs_cxx17 = 1;
+	  i--;
+	  break;
 	default:
 	  fprintf (stderr, "unrecognized option %s\n", argv[i]);
 	  goto usage;
@@ -1614,13 +1642,18 @@  main (int argc, char **argv)
 	  return 1;
 	}
       n = limidx + 1;
+      if (do_cxx14_vs_cxx17)
+	{
+	  fputs ("-c is incompatible with -i", stderr);
+	  return 1;
+	}
     }
 
   if (destdir == NULL && !output_one)
     {
     usage:
       fprintf (stderr, "Usage:\n\
-%s [-e] [-s srcdir -d destdir] [-n count] [-i idx]\n\
+%s [-e] [-c] [-s srcdir -d destdir] [-n count] [-i idx]\n\
 Either -s srcdir -d destdir or -i idx must be used\n", argv[0]);
       return 1;
     }
@@ -1650,6 +1683,7 @@  Either -s srcdir -d destdir or -i idx mu
   for (i = 0; i < NATYPES2; ++i)
     if (attrib_types[i].bitfld)
       aligned_bitfld_types[n_aligned_bitfld_types++] = attrib_types[i];
+repeat:;
   for (i = 0; i < sizeof (features) / sizeof (features[0]); ++i)
     {
       int startidx = idx;
@@ -1696,6 +1730,14 @@  Either -s srcdir -d destdir or -i idx mu
     limidx = idx;
   while (idx < n)
     generate_random_tests (ALL_FEATURES, 1 + (generate_random () % 25));
+  if (do_cxx14_vs_cxx17)
+    {
+      cxx14_vs_cxx17 = 1;
+      do_cxx14_vs_cxx17 = 0;
+      limidx = 0;
+      idx = 0;
+      goto repeat;
+    }
   fclose (outfile);
   return 0;
 }
--- gcc/testsuite/g++.dg/compat/struct-layout-1_x1.h.jj	2020-01-11 16:31:54.558300646 +0100
+++ gcc/testsuite/g++.dg/compat/struct-layout-1_x1.h	2020-04-20 18:59:10.307557481 +0200
@@ -35,8 +35,14 @@  int fn9 (void) { return 9; }
   s##n.x = v;							\
   a##n[2].x = w;						\
   ++j;
+#ifdef CXX14_VS_CXX17
+struct empty_base {};
+#define EMPTY_BASE : public empty_base
+#else
+#define EMPTY_BASE
+#endif
 #define TX(n, type, attrs, fields, ops) 			\
-type S##n { fields } attrs;					\
+type S##n EMPTY_BASE { fields } attrs;				\
 type S##n s##n;							\
 extern type S##n a##n[5];					\
 extern type S##n check##n (type S##n, type S##n *,		\
--- gcc/testsuite/g++.dg/compat/struct-layout-1_y1.h.jj	2020-01-11 16:31:54.558300646 +0100
+++ gcc/testsuite/g++.dg/compat/struct-layout-1_y1.h	2020-04-20 18:59:51.045938756 +0200
@@ -37,8 +37,14 @@ 
     FAIL (n, 56);						\
   ret.x = s##n.x;						\
   ++j;
+#ifdef CXX14_VS_CXX17
+struct empty_base {};
+#define EMPTY_BASE : public empty_base
+#else
+#define EMPTY_BASE
+#endif
 #define TX(n, type, attrs, fields, ops) 			\
-type S##n { fields } attrs;					\
+type S##n EMPTY_BASE { fields } attrs;				\
 extern type S##n s##n;						\
 type S##n a##n[5];						\
 type S##n							\