[2/2] AArch64 AAPCS: Ignore static members

Message ID 20190116155734.53824-3-alan.hayward@arm.com
State New, archived
Headers

Commit Message

Alan Hayward Jan. 16, 2019, 3:57 p.m. UTC
  Static members in C++ structs are global data and therefore not part of the
list of struct members considered for passing in registers.

Note the corresponding code in GCC (from which the GDB AAPCS code is based)
does not have any static member checks due to the static members not being
part of the struct type at that point.

Add a new test based on gdb.base/infcall-nested-structs.exp, adding static
members.

Also fixes gdb.dwarf2/dw2-cp-infcall-ref-static.exp.

gdb/ChangeLog:

2019-01-16  Alan Hayward  <alan.hayward@arm.com>

	* aarch64-tdep.c (aapcs_is_vfp_call_or_return_candidate_1): Check
	for static members.
	(pass_in_v_vfp_candidate): Likewise.

gdb/testsuite/ChangeLog:

2019-01-16  Alan Hayward  <alan.hayward@arm.com>

	* gdb.cp/infcall-nested-static-structs.cc: New test.
	* gdb.cp/infcall-nested-static-structs.exp: New file.
---
 gdb/aarch64-tdep.c                            |   8 +
 .../gdb.cp/infcall-nested-static-structs.cc   | 222 ++++++++++++++++++
 .../gdb.cp/infcall-nested-static-structs.exp  | 173 ++++++++++++++
 3 files changed, 403 insertions(+)
 create mode 100644 gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc
 create mode 100644 gdb/testsuite/gdb.cp/infcall-nested-static-structs.exp
  

Comments

Pedro Alves Jan. 17, 2019, 5:22 p.m. UTC | #1
On 01/16/2019 03:57 PM, Alan Hayward wrote:
> Static members in C++ structs are global data and therefore not part of the
> list of struct members considered for passing in registers.
> 
> Note the corresponding code in GCC (from which the GDB AAPCS code is based)
> does not have any static member checks due to the static members not being
> part of the struct type at that point.
> 
> Add a new test based on gdb.base/infcall-nested-structs.exp, adding static
> members.

Diffing the files, it seems a lot is shared.  Would it be
possible and make sense to put the static members tests in
infcall-nested-structs.c, wrapped with #ifdef __cplusplus?

> diff --git a/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc
> new file mode 100644
> index 0000000000..bec63728f4
> --- /dev/null
> +++ b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc
> @@ -0,0 +1,222 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2018 Free Software Foundation, Inc.

This would be 2018-2019.

> +
> +/* Structures with two fields nested to various depths, one of which is static,
> +   along with some empty structures.  */

Is the "along with some empty structures." part important for this test?
Kind of seems like the other bug bleeds into this test?

> +
> +int cmp_struct_02_01 (struct struct_02_01 a,
> +			     struct struct_02_01 b)
> +{ return a.s2.s1.a == b.s2.s1.a && a.s2.s1.b == a.s2.s1.b; }
> +
> +int cmp_struct_02_02 (struct struct_02_02 a,
> +			     struct struct_02_02 b)
> +{ return a.a == b.a && a.b == b.b; }
> +
> +int cmp_struct_02_03 (struct struct_02_03 a,
> +			     struct struct_02_03 b)
> +{ return a.s4.s3.a == b.s4.s3.a && a.s6.s5.b == b.s6.s5.b; }
> +
> +int cmp_struct_02_04 (struct struct_02_04 a,
> +			     struct struct_02_04 b)
> +{ return a.a == b.a && a.b == b.b; }
> +

While diffing I noticed formatting changes spurious here.

> +foreach ta $int_types {
> +    start_gdb_and_run_tests $ta
> +}
> +
> +# if [support_complex_tests] {
> +#     foreach ta $complex_types {
> +# 	start_gdb_and_run_tests $ta
> +#     }
> +# }
> +

Looks like you meant to remove this.

Thanks,
Pedro Alves
  
Alan Hayward Jan. 18, 2019, 10:49 a.m. UTC | #2
> On 17 Jan 2019, at 17:22, Pedro Alves <palves@redhat.com> wrote:

> 

> On 01/16/2019 03:57 PM, Alan Hayward wrote:

>> Static members in C++ structs are global data and therefore not part of the

>> list of struct members considered for passing in registers.

>> 

>> Note the corresponding code in GCC (from which the GDB AAPCS code is based)

>> does not have any static member checks due to the static members not being

>> part of the struct type at that point.

>> 

>> Add a new test based on gdb.base/infcall-nested-structs.exp, adding static

>> members.

> 

> Diffing the files, it seems a lot is shared.  Would it be

> possible and make sense to put the static members tests in

> infcall-nested-structs.c, wrapped with #ifdef __cplusplus?

> 


My concern was not wanting to make the original test too big - it already has
7000 cases and takes 30secs to run for me.  I started with a single test, and
it looked messy.
Happy to merge them back together.

>> diff --git a/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc

>> new file mode 100644

>> index 0000000000..bec63728f4

>> --- /dev/null

>> +++ b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc

>> @@ -0,0 +1,222 @@

>> +/* This testcase is part of GDB, the GNU debugger.

>> +

>> +   Copyright 2018 Free Software Foundation, Inc.

> 

> This would be 2018-2019.

> 

>> +

>> +/* Structures with two fields nested to various depths, one of which is static,

>> +   along with some empty structures.  */

> 

> Is the "along with some empty structures." part important for this test?

> Kind of seems like the other bug bleeds into this test?


That part isn’t really important in this test (as they will just hit
the code for the previous patch).
But, I thought it was worth having some cases that tested both together.
Maybe just needs that part of the comment removed.


> 

>> +

>> +int cmp_struct_02_01 (struct struct_02_01 a,

>> +			     struct struct_02_01 b)

>> +{ return a.s2.s1.a == b.s2.s1.a && a.s2.s1.b == a.s2.s1.b; }

>> +

>> +int cmp_struct_02_02 (struct struct_02_02 a,

>> +			     struct struct_02_02 b)

>> +{ return a.a == b.a && a.b == b.b; }

>> +

>> +int cmp_struct_02_03 (struct struct_02_03 a,

>> +			     struct struct_02_03 b)

>> +{ return a.s4.s3.a == b.s4.s3.a && a.s6.s5.b == b.s6.s5.b; }

>> +

>> +int cmp_struct_02_04 (struct struct_02_04 a,

>> +			     struct struct_02_04 b)

>> +{ return a.a == b.a && a.b == b.b; }

>> +

> 

> While diffing I noticed formatting changes spurious here.

> 

>> +foreach ta $int_types {

>> +    start_gdb_and_run_tests $ta

>> +}

>> +

>> +# if [support_complex_tests] {

>> +#     foreach ta $complex_types {

>> +# 	start_gdb_and_run_tests $ta

>> +#     }

>> +# }

>> +

> 

> Looks like you meant to remove this.

> 

> Thanks,

> Pedro Alves
  

Patch

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index ff6ca5c6e0..863e62b5e0 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1384,6 +1384,10 @@  aapcs_is_vfp_call_or_return_candidate_1 (struct type *type,
 
 	for (int i = 0; i < TYPE_NFIELDS (type); i++)
 	  {
+	    /* Ignore any static fields.  */
+	    if (field_is_static (&TYPE_FIELD (type, i)))
+	      continue;
+
 	    struct type *member = check_typedef (TYPE_FIELD_TYPE (type, i));
 
 	    int sub_count = aapcs_is_vfp_call_or_return_candidate_1
@@ -1662,6 +1666,10 @@  pass_in_v_vfp_candidate (struct gdbarch *gdbarch, struct regcache *regcache,
     case TYPE_CODE_UNION:
       for (int i = 0; i < TYPE_NFIELDS (arg_type); i++)
 	{
+	  /* Don't include static fields.  */
+	  if (field_is_static (&TYPE_FIELD (arg_type, i)))
+	    continue;
+
 	  struct value *field = value_primitive_field (arg, 0, i, arg_type);
 	  struct type *field_type = check_typedef (value_type (field));
 
diff --git a/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc
new file mode 100644
index 0000000000..bec63728f4
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.cc
@@ -0,0 +1,222 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2018 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This file is used for testing GDBs ability to pass structures to, and
+   return structures from, functions, where those structs contain static
+   members.  This test is based on gdb.base/infcall-nested-structs.exp.  */
+
+#include <string.h>
+
+/* Useful abreviations.  */
+typedef char tc;
+typedef short ts;
+typedef int ti;
+typedef long tl;
+typedef long long tll;
+typedef float tf;
+typedef double td;
+typedef long double tld;
+
+#ifdef TEST_COMPLEX
+typedef float _Complex tfc;
+typedef double _Complex tdc;
+typedef long double _Complex tldc;
+#endif /* TEST_COMPLEX */
+
+#define MAKE_CHECK_FUNCS(TYPE)					\
+  int								\
+  check_arg_ ## TYPE (struct TYPE arg)				\
+  {								\
+    return cmp_ ## TYPE (arg, ref_val_ ## TYPE);		\
+  }								\
+								\
+  struct TYPE							\
+  rtn_str_ ## TYPE (void)					\
+  {								\
+    return (ref_val_ ## TYPE);					\
+  }
+
+#define REF_VAL(NAME) struct NAME ref_val_ ## NAME
+#define ES(NAME) struct { } NAME
+
+/* Test is either for a single type or two differing types.  */
+#if defined tA && ! defined tB
+#define tB tA
+#endif
+#if ! defined tB
+#error "Incorrect configuration of tA and tB defines"
+#endif
+
+/* Structures with two fields nested to various depths, one of which is static,
+   along with some empty structures.  */
+struct struct_02_01 { struct sa { struct sb { tA a; static tB b; } s1; } s2; };
+struct struct_02_02 { static tA a; struct { struct { ES(es1); } s1; } s2; tB b; };
+struct struct_02_03 { struct { struct { ES(es1); } s1; } s2; ES(es1); struct { struct { tA a; } s3; } s4; struct sa { struct sb { static tB b; } s5; } s6;};
+struct struct_02_04 { static tA a; tB b; };
+
+/* Structures with four fields nested to various depths, some of which are
+   static, along with some empty structures.  */
+struct struct_04_01 { struct sa { struct sb { static tA a; tB b; tA c; tB d; } s1; } s2; };
+struct struct_04_02 { tA a; struct { struct { ES(es1); } s1; } s2; tB b; struct { struct { ES(es1); } s2; } s3; static tA c; struct { struct { ES(es2); } s4; } s5; static tB d;};
+struct struct_04_03 { struct sa { struct sb { static tA a; } s3; } s4; struct sc { struct sd { static tB b; } s5; } s6; struct se { struct sf { static tA c; } s7; } s8; struct sg { struct sh { static tB d; } s9; } s10;};
+struct struct_04_04 { ES(es1); ES(es2); tA a; ES(es3); tB b; ES(es4); tA c; ES(es5); static tB d; };
+
+/* Structures with six fields nested to various depths, some of which are
+   static, along with some empty structures.  */
+struct struct_06_01 { struct sa { struct sb { tA a; static tB b; tA c; tB d; tA e; } s1; } s2; tB f; };
+struct struct_06_02 { tA a; static tB b; static tA c; tB d; tB e; tA f;};
+struct struct_06_03 { struct { struct { ES(es1); } s1; } s2; ES(es1); struct sa { struct sb { static tA a; } s3; } s4; struct sc { struct sd { tB b; } s5; } s6; struct se { struct sf { static tA c; } s7; } s8; struct sg { struct sh { static tB d; } s9; } s10; struct { struct { tA e; tB f; } s11; } s12;};
+struct struct_06_04 { ES(es1); ES(es2); static tA a; ES(es3); static tB b; ES(es4); static tA c; ES(es5); static tB d; ES(es6); static tA e; ES(es7); tB f; };
+
+int cmp_struct_02_01 (struct struct_02_01 a,
+			     struct struct_02_01 b)
+{ return a.s2.s1.a == b.s2.s1.a && a.s2.s1.b == a.s2.s1.b; }
+
+int cmp_struct_02_02 (struct struct_02_02 a,
+			     struct struct_02_02 b)
+{ return a.a == b.a && a.b == b.b; }
+
+int cmp_struct_02_03 (struct struct_02_03 a,
+			     struct struct_02_03 b)
+{ return a.s4.s3.a == b.s4.s3.a && a.s6.s5.b == b.s6.s5.b; }
+
+int cmp_struct_02_04 (struct struct_02_04 a,
+			     struct struct_02_04 b)
+{ return a.a == b.a && a.b == b.b; }
+
+int cmp_struct_04_01 (struct struct_04_01 a, struct struct_04_01 b)
+{ return a.s2.s1.a == b.s2.s1.a && a.s2.s1.b == a.s2.s1.b
+	 && a.s2.s1.c == b.s2.s1.c && a.s2.s1.d == a.s2.s1.d; }
+
+int cmp_struct_04_02 (struct struct_04_02 a, struct struct_04_02 b)
+{ return a.a == b.a && a.b == b.b && a.c == b.c && a.d == b.d; }
+
+int cmp_struct_04_03 (struct struct_04_03 a, struct struct_04_03 b)
+{ return a.s4.s3.a == b.s4.s3.a && a.s6.s5.b == b.s6.s5.b
+	 && a.s8.s7.c == b.s8.s7.c && a.s10.s9.d == b.s10.s9.d; }
+
+int cmp_struct_04_04 (struct struct_04_04 a, struct struct_04_04 b)
+{ return a.a == b.a && a.b == b.b && a.c == b.c && a.d == b.d; }
+
+int cmp_struct_06_01 (struct struct_06_01 a, struct struct_06_01 b)
+{ return a.s2.s1.a == b.s2.s1.a && a.s2.s1.b == a.s2.s1.b
+	 && a.s2.s1.c == b.s2.s1.c && a.s2.s1.d == a.s2.s1.d
+	 && a.s2.s1.e == b.s2.s1.e && a.f == b.f; }
+
+int cmp_struct_06_02 (struct struct_06_02 a, struct struct_06_02 b)
+{ return a.a == b.a && a.b == b.b && a.c == b.c && a.d == b.d && a.e == b.e
+	 && a.f == b.f; }
+
+int cmp_struct_06_03 (struct struct_06_03 a, struct struct_06_03 b)
+{ return a.s4.s3.a == b.s4.s3.a && a.s6.s5.b == b.s6.s5.b
+	 && a.s8.s7.c == b.s8.s7.c && a.s10.s9.d == b.s10.s9.d
+	 && a.s12.s11.e == b.s12.s11.e && a.s12.s11.f == b.s12.s11.f; }
+
+int cmp_struct_06_04 (struct struct_06_04 a, struct struct_06_04 b)
+{ return a.a == b.a && a.b == b.b && a.c == b.c && a.d == b.d && a.e == b.e
+	 && a.f == b.f; }
+
+/* Initialise static members.  */
+tB struct_02_01::sa::sb::b = '1';
+tA struct_02_02::a = '2';
+tB struct_02_03::sa::sb::b = '3';
+tA struct_02_04::a = '4';
+tA struct_04_01::sa::sb::a = '5';
+tA struct_04_02::c = '6';
+tB struct_04_02::d = '7';
+tA struct_04_03::sa::sb::a = '8';
+tB struct_04_03::sc::sd::b = '9';
+tA struct_04_03::se::sf::c = '0';
+tB struct_04_03::sg::sh::d = 'A';
+tB struct_04_04::d = 'B';
+tB struct_06_01::sa::sb::b = 'C';
+tB struct_06_02::b = 'D';
+tA struct_06_02::c = 'E';
+tA struct_06_03::sa::sb::a = 'F';
+tA struct_06_03::se::sf::c = 'G';
+tB struct_06_03::sg::sh::d = 'H';
+tA struct_06_04::a = 'I';
+tB struct_06_04::b = 'J';
+tA struct_06_04::c = 'K';
+tB struct_06_04::d = 'L';
+tA struct_06_04::e = 'M';
+
+REF_VAL(struct_02_01) = { { { 'a' } } };
+REF_VAL(struct_02_02) = { { { {} } }, 'b' };
+REF_VAL(struct_02_03) = { { { {} } }, {}, { { 'a' } }, { { } } };
+REF_VAL(struct_02_04) = { 'b' };
+REF_VAL(struct_04_01) = { { { 'b', 'c', 'd' } } };
+REF_VAL(struct_04_02) = { 'a', { { {} } }, 'b', { { {} } }, { { {} } } };
+REF_VAL(struct_04_03) = {};
+REF_VAL(struct_04_04) = { {}, {}, 'a', {}, 'b', {}, 'c', {} };
+REF_VAL(struct_06_01) = { { { 'a', 'c', 'd', 'e' } }, 'f' };
+REF_VAL(struct_06_02) = { 'a', 'd', 'e', 'f' };
+REF_VAL(struct_06_03) = { { { {} } }, {}, {}, { { 'b' } }, {}, /*{ { 'e', 'f' } }*/ };
+REF_VAL(struct_06_04) = { {}, {}, {}, {}, {}, {}, {}, 'f' };
+
+/* Create all of the functions GDB will call to check functionality.  */
+MAKE_CHECK_FUNCS(struct_02_01)
+MAKE_CHECK_FUNCS(struct_02_02)
+MAKE_CHECK_FUNCS(struct_02_03)
+MAKE_CHECK_FUNCS(struct_02_04)
+MAKE_CHECK_FUNCS(struct_04_01)
+MAKE_CHECK_FUNCS(struct_04_02)
+MAKE_CHECK_FUNCS(struct_04_03)
+MAKE_CHECK_FUNCS(struct_04_04)
+MAKE_CHECK_FUNCS(struct_06_01)
+MAKE_CHECK_FUNCS(struct_06_02)
+MAKE_CHECK_FUNCS(struct_06_03)
+MAKE_CHECK_FUNCS(struct_06_04)
+
+#define CALL_LINE(NAME) val += check_arg_ ## NAME (rtn_str_ ## NAME ())
+
+int
+call_all ()
+{
+  int val;
+
+  CALL_LINE(struct_02_01);
+  CALL_LINE(struct_02_02);
+  CALL_LINE(struct_02_03);
+  CALL_LINE(struct_02_04);
+  CALL_LINE(struct_04_01);
+  CALL_LINE(struct_04_02);
+  CALL_LINE(struct_04_03);
+  CALL_LINE(struct_04_04);
+  CALL_LINE(struct_06_01);
+  CALL_LINE(struct_06_02);
+  CALL_LINE(struct_06_03);
+  CALL_LINE(struct_06_04);
+
+  return (val != 4);
+}
+
+void
+breakpt (void)
+{
+  /* Nothing.  */
+}
+
+int
+main ()
+{
+  int res;
+
+  res = call_all ();
+  breakpt (); /* Break Here.  */
+  return res;
+}
diff --git a/gdb/testsuite/gdb.cp/infcall-nested-static-structs.exp b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.exp
new file mode 100644
index 0000000000..23d8466be5
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/infcall-nested-static-structs.exp
@@ -0,0 +1,173 @@ 
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Some targets can't call functions, so don't even bother with this
+# test.
+if [target_info exists gdb,cannot_call_functions] {
+    unsupported "this target can not call functions"
+    continue
+}
+
+# static members in structs/classes require C++
+if [get_compiler_info "c++"] {
+    untested "couldn't find a valid c++ compiler"
+    return -1
+}
+
+set int_types { tc ts ti tl tll }
+set float_types { tf td tld }
+set complex_types { tfc tdc tldc }
+
+set compile_flags {debug}
+if [support_complex_tests] {
+    lappend compile_flags "additional_flags=-DTEST_COMPLEX"
+}
+
+# Given N (0..25), return the corresponding alphabetic letter in upper
+# case.
+
+proc I2A { n } {
+    return [string range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" $n $n]
+}
+
+# Compile a variant of nested-structs.cc using TYPES to specify the
+# types of the struct fields within the source.  Run up to main.
+# Also updates the global "testfile" to reflect the most recent build.
+
+proc start_nested_structs_test { types } {
+    global testfile
+    global srcfile
+    global binfile
+    global subdir
+    global srcdir
+    global compile_flags
+
+    standard_testfile .cc
+
+    # Create the additional flags
+    set flags $compile_flags
+
+    for {set n 0} {$n<[llength ${types}]} {incr n} {
+	set m [I2A ${n}]
+	set t [lindex ${types} $n]
+	lappend flags "additional_flags=-Dt${m}=${t}"
+	append testfile "-" "$t"
+    }
+
+    set binfile [standard_output_file ${testfile}]
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "c++ ${flags}"] != "" } {
+	unresolved "failed to compile"
+	return 0
+    }
+
+    # Start with a fresh gdb.
+    clean_restart ${binfile}
+
+    # Make certain that the output is consistent
+    gdb_test_no_output "set print sevenbit-strings"
+    gdb_test_no_output "set print address off"
+    gdb_test_no_output "set print pretty off"
+    gdb_test_no_output "set width 0"
+    gdb_test_no_output "set print elements 300"
+
+    # Advance to main
+    if { ![runto_main] } then {
+	fail "can't run to main"
+	return 0
+    }
+
+    # Now continue forward to a suitable location to run the tests.
+    # Some targets only enable the FPU on first use, so ensure that we
+    # have used the FPU before we make calls from GDB to code that
+    # could use the FPU.
+    gdb_breakpoint [gdb_get_line_number "Break Here"] temporary
+    gdb_continue_to_breakpoint "breakpt" ".* Break Here\\. .*"
+
+    return 1
+}
+
+# Assuming GDB is stopped at main within a test binary, run some tests
+# passing structures, and reading return value structures.
+
+proc run_tests {} {
+    global gdb_prompt
+
+    foreach {name} {struct_02_01 struct_02_02 struct_02_03 struct_02_04 } {
+	gdb_test "p/d check_arg_${name} (ref_val_${name})" "= 1"
+
+	set refval [ get_valueof "" "ref_val_${name}" "" ]
+	verbose -log "Refval: ${refval}"
+
+	set test "check return value ${name}"
+	if { ${refval} != "" } {
+	    set answer [ get_valueof "" "rtn_str_${name} ()" "XXXX"]
+	    verbose -log "Answer: ${answer}"
+	    gdb_assert [string eq ${answer} ${refval}] ${test}
+	} else {
+	    unresolved $test
+	}
+    }
+}
+
+# Set up a test prefix, compile the test binary, run to main, and then
+# run some tests.
+
+proc start_gdb_and_run_tests { types } {
+    set prefix "types"
+
+    foreach t $types {
+	append prefix "-" "${t}"
+    }
+
+    with_test_prefix $prefix {
+	if { [start_nested_structs_test $types] } {
+	    run_tests
+	}
+    }
+}
+
+foreach ta $int_types {
+    start_gdb_and_run_tests $ta
+}
+
+# if [support_complex_tests] {
+#     foreach ta $complex_types {
+# 	start_gdb_and_run_tests $ta
+#     }
+# }
+
+if ![gdb_skip_float_test] {
+    foreach ta $float_types {
+	start_gdb_and_run_tests $ta
+    }
+
+    foreach ta $int_types {
+	foreach tb $float_types {
+	    start_gdb_and_run_tests [list $ta $tb]
+	}
+    }
+
+    foreach ta $float_types {
+	foreach tb $int_types {
+	    start_gdb_and_run_tests [list $ta $tb]
+	}
+
+	foreach tb $float_types {
+	    start_gdb_and_run_tests [list $ta $tb]
+	}
+    }
+}