[vm/ffi] Support nested structs

This CL adds support for nested structs in FFI calls, callbacks, and
memory loads and stores through the Struct classes itself.

Nesting empty structs and nesting a structs in themselves (directly or
indirectly) is reported as error.

This feature is almost fully implemented in the CFE transformation.

Because structs depend on the sizes of their nested structs, the structs
now need to be processed in topological order.

Field access to nested structs branches at runtime on making a derived
Pointer if the backing memory of the outer struct was a Pointer or
making a TypedDataView if the backing memory of the outer struct was
a TypedData.

Assigning to a nested struct is a byte for byte copy from the source.

The only changes in the VM are contained in the native calling
convention calculation which now recursively needs to reason about
fundamental types instead of just 1 struct deep.

Because of the amount of corner cases in the calling conventions that
need to be covered, the tests are generated, rather than hand-written.

ABIs tested on CQ: x64 (Linux, MacOS, Windows), ia32 (Linux, Windows),
arm (Android softFP, Linux hardFP), arm64 Android.
ABIs tested locally through Flutter: arm64 iOS.
ABIs not tested: ia32 Android (emulator), x64 iOS (simulator), arm iOS.
TEST=runtime/bin/ffi_test/ffi_test_functions_generated.cc
TEST=runtime/bin/ffi_test/ffi_test_functions.cc
TEST=tests/{ffi,ffi_2}/function_structs_by_value_generated_test.dart
TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_generated_tes
TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_test.dart
TEST=tests/{ffi,ffi_2}/vmspecific_static_checks_test.dart

Closes https://github.com/dart-lang/sdk/issues/37271.

Contains a temporary workaround for
https://github.com/dart-lang/sdk/issues/44454.

Change-Id: I5e5d10e09e5c3fc209f5f7e997efe17bd362214d
Cq-Include-Trybots: luci.dart.try:dart-sdk-linux-try,dart-sdk-mac-try,dart-sdk-win-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-mac-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-mac-release-x64-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,analyzer-analysis-server-linux-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/169221
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index d3dd826..0faba7a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -3789,6 +3789,31 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name, List<String> _names)>
+    templateFfiFieldCyclic =
+    const Template<Message Function(String name, List<String> _names)>(
+        messageTemplate: r"""Struct '#name' contains itself. Cycle elements:
+#names""", withArguments: _withArgumentsFfiFieldCyclic);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, List<String> _names)>
+    codeFfiFieldCyclic =
+    const Code<Message Function(String name, List<String> _names)>(
+  "FfiFieldCyclic",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiFieldCyclic(String name, List<String> _names) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (_names.isEmpty) throw 'No names provided';
+  String names = itemizeNames(_names);
+  return new Message(codeFfiFieldCyclic,
+      message: """Struct '${name}' contains itself. Cycle elements:
+${names}""", arguments: {'name': name, 'names': _names});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(String name)> templateFfiFieldInitializer = const Template<
         Message Function(String name)>(
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index b4ca2b5..965f076 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -450,6 +450,7 @@
   CompileTimeErrorCode.YIELD_IN_NON_GENERATOR,
   CompileTimeErrorCode.YIELD_OF_INVALID_TYPE,
   FfiCode.ANNOTATION_ON_POINTER_FIELD,
+  FfiCode.EMPTY_STRUCT,
   FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD,
   FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER,
   FfiCode.FIELD_INITIALIZER_IN_STRUCT,
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
index eda16d1..cacf7bc 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
@@ -23,6 +23,15 @@
       correction: "Try removing the annotation.");
 
   /**
+   * Parameters:
+   * 0: the name of the struct class
+   */
+  static const FfiCode EMPTY_STRUCT = FfiCode(
+      name: 'EMPTY_STRUCT',
+      message: "Struct '{0}' is empty. Empty structs are undefined behavior.",
+      correction: "Try adding a field to '{0}' or use a different Struct.");
+
+  /**
    * No parameters.
    */
   static const FfiCode EXTRA_ANNOTATION_ON_STRUCT_FIELD = FfiCode(
@@ -76,8 +85,9 @@
       name: 'INVALID_FIELD_TYPE_IN_STRUCT',
       message:
           "Fields in struct classes can't have the type '{0}'. They can only "
-          "be declared as 'int', 'double' or 'Pointer'.",
-      correction: "Try using 'int', 'double' or 'Pointer'.");
+          "be declared as 'int', 'double', 'Pointer', or subtype of 'Struct'.",
+      correction:
+          "Try using 'int', 'double', 'Pointer', or subtype of 'Struct'.");
 
   /**
    * No parameters.
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index f373869..160ed2f 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -174,6 +174,8 @@
         structFieldCount++;
       } else if (_isPointer(declaredType.element)) {
         structFieldCount++;
+      } else if (_isStructClass(declaredType)) {
+        structFieldCount++;
       }
     }
     return structFieldCount == 0;
@@ -228,7 +230,11 @@
   /// Returns `true` iff [nativeType] is a struct type.
   bool _isStructClass(DartType nativeType) {
     if (nativeType is InterfaceType) {
-      final superClassElement = nativeType.element.supertype.element;
+      final superType = nativeType.element.supertype;
+      if (superType == null) {
+        return false;
+      }
+      final superClassElement = superType.element;
       if (superClassElement.library.name == 'dart.ffi') {
         return superClassElement.name == 'Struct' &&
             nativeType.typeArguments?.isEmpty == true;
@@ -518,6 +524,12 @@
         _validateAnnotations(fieldType, annotations, _PrimitiveDartType.double);
       } else if (_isPointer(declaredType.element)) {
         _validateNoAnnotations(annotations);
+      } else if (_isStructClass(declaredType)) {
+        final clazz = (declaredType as InterfaceType).element;
+        if (_isEmptyStruct(clazz)) {
+          _errorReporter
+              .reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [clazz.name]);
+        }
       } else {
         _errorReporter.reportErrorForNode(FfiCode.INVALID_FIELD_TYPE_IN_STRUCT,
             fieldType, [fieldType.toSource()]);
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index f6c6267..6e45bfc 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -52,6 +52,7 @@
         templateFfiExpectedNoExceptionalReturn,
         templateFfiExtendsOrImplementsSealedClass,
         templateFfiFieldAnnotation,
+        templateFfiFieldCyclic,
         templateFfiFieldInitializer,
         templateFfiFieldNoAnnotation,
         templateFfiNotStatic,
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 55c1399..c275e1e 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -322,6 +322,7 @@
 FfiExpectedNoExceptionalReturn/analyzerCode: Fail
 FfiExtendsOrImplementsSealedClass/analyzerCode: Fail
 FfiFieldAnnotation/analyzerCode: Fail
+FfiFieldCyclic/analyzerCode: Fail
 FfiFieldInitializer/analyzerCode: Fail
 FfiFieldNoAnnotation/analyzerCode: Fail
 FfiNotStatic/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index e33cdcb..2bcb9d4 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4253,6 +4253,13 @@
   template: "Field '#name' requires no annotation to declare its native type, it is a Pointer which is represented by the same type in Dart and native code."
   external: test/ffi_test.dart
 
+FfiFieldCyclic:
+  # Used by dart:ffi
+  template: |
+    Struct '#name' contains itself. Cycle elements:
+    #names
+  external: test/ffi_test.dart
+
 FfiNotStatic:
   # Used by dart:ffi
   template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code."
diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect
index 5c415f7..906fb3f 100644
--- a/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect
@@ -13,7 +13,7 @@
   @#C3
   static final field core::int* #sizeOf = (#C11).{core::List::[]}(ffi::_abi());
   @#C3
-  constructor #fromPointer(dynamic #pointer) → dynamic
+  constructor #fromTypedDataBase(dynamic #pointer) → dynamic
     : super ffi::Struct::_fromPointer(#pointer)
     ;
   static factory allocate(core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect
index 241578e..387fd15 100644
--- a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect
@@ -12,7 +12,7 @@
   @#C3
   static final field core::int* #sizeOf = (#C6).{core::List::[]}(ffi::_abi());
   @#C3
-  constructor #fromPointer(dynamic #pointer) → dynamic
+  constructor #fromTypedDataBase(dynamic #pointer) → dynamic
     : super ffi::Struct::_fromPointer(#pointer)
     ;
   static factory allocate(core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect
index 5c415f7..906fb3f 100644
--- a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect
@@ -13,7 +13,7 @@
   @#C3
   static final field core::int* #sizeOf = (#C11).{core::List::[]}(ffi::_abi());
   @#C3
-  constructor #fromPointer(dynamic #pointer) → dynamic
+  constructor #fromTypedDataBase(dynamic #pointer) → dynamic
     : super ffi::Struct::_fromPointer(#pointer)
     ;
   static factory allocate(core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect
index 6ad0475..f93c1a3 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect
@@ -9,7 +9,7 @@
     @#C3
     static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
     @#C3
-    constructor #fromPointer(dynamic #pointer) → dynamic
+    constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
       ;
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect
index 0baac00..466b63f 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect
@@ -9,7 +9,7 @@
     @#C3
     static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
     @#C3
-    constructor #fromPointer(dynamic #pointer) → dynamic
+    constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
       ;
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect
index a3cbec1..5d79437 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect
@@ -9,7 +9,7 @@
     @#C3
     static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
     @#C3
-    constructor #fromPointer(dynamic #pointer) → dynamic
+    constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
       ;
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect
index 6ad0475..f93c1a3 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect
@@ -9,7 +9,7 @@
     @#C3
     static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
     @#C3
-    constructor #fromPointer(dynamic #pointer) → dynamic
+    constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
       ;
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect
index 05ed4f7..01f4106 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect
@@ -9,7 +9,7 @@
     @#C3
     static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
     @#C3
-    constructor #fromPointer(dynamic #pointer) → dynamic
+    constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
       ;
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect
index b1b1bda..2c057af 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect
@@ -9,7 +9,7 @@
     @#C3
     static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
     @#C3
-    constructor #fromPointer(dynamic #pointer) → dynamic
+    constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
       ;
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 7bdd598..f8b473f 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -193,10 +193,16 @@
   final Class doubleClass;
   final Class listClass;
   final Class typeClass;
+  final Procedure unsafeCastMethod;
+  final Class typedDataClass;
+  final Procedure typedDataBufferGetter;
+  final Procedure typedDataOffsetInBytesGetter;
+  final Procedure byteBufferAsUint8List;
   final Class pragmaClass;
   final Field pragmaName;
   final Field pragmaOptions;
   final Procedure listElementAt;
+  final Procedure numAddition;
 
   final Library ffiLibrary;
   final Class nativeFunctionClass;
@@ -221,6 +227,7 @@
   final Map<NativeType, Procedure> storeMethods;
   final Map<NativeType, Procedure> elementAtMethods;
   final Procedure loadStructMethod;
+  final Procedure memCopy;
   final Procedure asFunctionTearoff;
   final Procedure lookupFunctionTearoff;
 
@@ -235,10 +242,20 @@
         doubleClass = coreTypes.doubleClass,
         listClass = coreTypes.listClass,
         typeClass = coreTypes.typeClass,
+        unsafeCastMethod =
+            index.getTopLevelMember('dart:_internal', 'unsafeCast'),
+        typedDataClass = index.getClass('dart:typed_data', 'TypedData'),
+        typedDataBufferGetter =
+            index.getMember('dart:typed_data', 'TypedData', 'get:buffer'),
+        typedDataOffsetInBytesGetter = index.getMember(
+            'dart:typed_data', 'TypedData', 'get:offsetInBytes'),
+        byteBufferAsUint8List =
+            index.getMember('dart:typed_data', 'ByteBuffer', 'asUint8List'),
         pragmaClass = coreTypes.pragmaClass,
         pragmaName = coreTypes.pragmaName,
         pragmaOptions = coreTypes.pragmaOptions,
         listElementAt = coreTypes.index.getMember('dart:core', 'List', '[]'),
+        numAddition = coreTypes.index.getMember('dart:core', 'num', '+'),
         ffiLibrary = index.getLibrary('dart:ffi'),
         nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'),
         pointerClass = index.getClass('dart:ffi', 'Pointer'),
@@ -283,6 +300,7 @@
           return index.getTopLevelMember('dart:ffi', "_elementAt$name");
         }),
         loadStructMethod = index.getTopLevelMember('dart:ffi', '_loadStruct'),
+        memCopy = index.getTopLevelMember('dart:ffi', '_memCopy'),
         asFunctionTearoff = index.getMember('dart:ffi', 'NativeFunctionPointer',
             LibraryIndex.tearoffPrefix + 'asFunction'),
         lookupFunctionTearoff = index.getMember(
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index ec6849a..868524b 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -8,7 +8,9 @@
 
 import 'package:front_end/src/api_unstable/vm.dart'
     show
+        templateFfiEmptyStruct,
         templateFfiFieldAnnotation,
+        templateFfiFieldCyclic,
         templateFfiFieldNoAnnotation,
         templateFfiTypeMismatch,
         templateFfiFieldInitializer,
@@ -22,6 +24,7 @@
 import 'package:kernel/target/changed_structure_notifier.dart';
 import 'package:kernel/target/targets.dart' show DiagnosticReporter;
 import 'package:kernel/type_environment.dart' show SubtypeCheckMode;
+import 'package:kernel/util/graph.dart';
 
 import 'ffi.dart';
 
@@ -40,7 +43,7 @@
 ///
 /// Output:
 /// class Coord extends Struct {
-///   Coord.#fromPointer(Pointer<Coord> coord) : super._(coord);
+///   Coord.#fromTypedDataBase(Pointer<Coord> coord) : super._(coord);
 ///
 ///   Pointer<Double> get _xPtr => addressOf.cast();
 ///   set x(double v) => _xPtr.store(v);
@@ -64,8 +67,8 @@
     DiagnosticReporter diagnosticReporter,
     ReferenceFromIndex referenceFromIndex,
     ChangedStructureNotifier changedStructureNotifier) {
-  final LibraryIndex index =
-      LibraryIndex(component, const ["dart:ffi", "dart:core"]);
+  final LibraryIndex index = LibraryIndex(component,
+      const ["dart:core", "dart:ffi", "dart:_internal", "dart:typed_data"]);
   if (!index.containsLibrary("dart:ffi")) {
     // TODO: This check doesn't make sense: "dart:ffi" is always loaded/created
     // for the VM target.
@@ -79,14 +82,29 @@
   final transformer = new _FfiDefinitionTransformer(index, coreTypes, hierarchy,
       diagnosticReporter, referenceFromIndex, changedStructureNotifier);
   libraries.forEach(transformer.visitLibrary);
+  transformer.manualVisitInTopologicalOrder();
   return FfiTransformerData(transformer.replacedGetters,
       transformer.replacedSetters, transformer.emptyStructs);
 }
 
+class StructDependencyGraph<T> implements Graph<T> {
+  final Map<T, Iterable<T>> map;
+  StructDependencyGraph(this.map);
+
+  Iterable<T> get vertices => map.keys;
+  Iterable<T> neighborsOf(T vertex) => map[vertex];
+}
+
 /// Checks and elaborates the dart:ffi structs and fields.
 class _FfiDefinitionTransformer extends FfiTransformer {
   final LibraryIndex index;
 
+  // Data structures for topological navigation.
+  Map<Class, IndexedClass> indexedStructClasses = {};
+  Map<Class, Set<Class>> structClassDependencies = {};
+  Map<Class, bool> fieldsValid = {};
+  Map<Class, Map<Abi, StructLayout>> structLayouts = {};
+
   Map<Field, Procedure> replacedGetters = {};
   Map<Field, Procedure> replacedSetters = {};
   Set<Class> emptyStructs = {};
@@ -105,6 +123,40 @@
       : super(index, coreTypes, hierarchy, diagnosticReporter,
             referenceFromIndex) {}
 
+  void manualVisitInTopologicalOrder() {
+    final connectedComponents =
+        computeStrongComponents(StructDependencyGraph(structClassDependencies));
+
+    connectedComponents.forEach((List<Class> component) {
+      bool report = false;
+      if (component.length > 1) {
+        // Indirect cycle.
+        report = true;
+      }
+      if (component.length == 1) {
+        if (structClassDependencies[component.single]
+            .contains(component.single)) {
+          // Direct cycle.
+          report = true;
+        }
+      }
+      if (report) {
+        component.forEach((Class e) {
+          diagnosticReporter.report(
+              templateFfiFieldCyclic.withArguments(
+                  e.name, component.map((e) => e.name).toList()),
+              e.fileOffset,
+              e.name.length,
+              e.fileUri);
+        });
+      }
+    });
+
+    final structClassesSorted = connectedComponents.expand((i) => i).toList();
+
+    structClassesSorted.forEach(visitClassInTopologicalOrder);
+  }
+
   @override
   visitLibrary(Library node) {
     currentLibraryIndex = referenceFromIndex?.lookupLibrary(node);
@@ -128,17 +180,22 @@
     // Struct objects are manufactured in the VM by 'allocate' and 'load'.
     _makeEntryPoint(node);
 
-    var indexedClass = currentLibraryIndex?.lookupIndexedClass(node.name);
+    final indexedClass = currentLibraryIndex?.lookupIndexedClass(node.name);
     _checkConstructors(node, indexedClass);
-    final bool fieldsValid = _checkFieldAnnotations(node);
+    indexedStructClasses[node] = indexedClass;
 
-    if (fieldsValid) {
+    fieldsValid[node] = _checkFieldAnnotations(node);
+
+    return node;
+  }
+
+  void visitClassInTopologicalOrder(Class node) {
+    final indexedClass = indexedStructClasses[node];
+    if (fieldsValid[node]) {
       final structSize = _replaceFields(node, indexedClass);
       _replaceSizeOfMethod(node, structSize, indexedClass);
       changedStructureNotifier?.registerClassMemberChange(node);
     }
-
-    return node;
   }
 
   void _checkStructClass(Class node) {
@@ -167,6 +224,11 @@
         SubtypeCheckMode.ignoringNullabilities);
   }
 
+  bool _isStructSubtype(DartType type) {
+    return env.isSubtypeOf(type, InterfaceType(structClass, Nullability.legacy),
+        SubtypeCheckMode.ignoringNullabilities);
+  }
+
   /// Returns members of [node] that correspond to struct fields.
   ///
   /// Note that getters and setters that originate from an external field have
@@ -198,6 +260,7 @@
 
   bool _checkFieldAnnotations(Class node) {
     bool success = true;
+    structClassDependencies[node] = {};
     final membersWithAnnotations = _structFieldMembers(node)
       ..retainWhere((m) => (m is Field) || (m is Procedure && m.isGetter));
     for (final Member f in membersWithAnnotations) {
@@ -212,7 +275,7 @@
       }
       final nativeTypeAnnos = _getNativeTypeAnnotations(f).toList();
       final type = _structFieldMemberType(f);
-      if (_isPointerType(type)) {
+      if (_isPointerType(type) || _isStructSubtype(type)) {
         if (nativeTypeAnnos.length != 0) {
           diagnosticReporter.report(
               templateFfiFieldNoAnnotation.withArguments(f.name.text),
@@ -220,6 +283,10 @@
               f.name.text.length,
               f.fileUri);
         }
+        if (_isStructSubtype(type)) {
+          final clazz = (type as InterfaceType).classNode;
+          structClassDependencies[node].add(clazz);
+        }
       } else if (nativeTypeAnnos.length != 1) {
         diagnosticReporter.report(
             templateFfiFieldAnnotation.withArguments(f.name.text),
@@ -230,10 +297,9 @@
         final DartType nativeType = InterfaceType(
             nativeTypesClasses[_getFieldType(nativeTypeAnnos.first).index],
             Nullability.legacy);
-        // TODO(dartbug.com/37271): Support structs inside structs.
         final DartType shouldBeDartType = convertNativeTypeToDartType(
             nativeType,
-            allowStructs: false,
+            allowStructs: true,
             allowHandle: false);
         if (shouldBeDartType == null ||
             !env.isSubtypeOf(type, shouldBeDartType,
@@ -274,9 +340,9 @@
     }
 
     // Add a constructor which 'load' can use.
-    // C.#fromPointer(Pointer<Void> address) : super.fromPointer(address);
+    // C.#fromTypedDataBase(Object address) : super.fromPointer(address);
     final VariableDeclaration pointer = new VariableDeclaration("#pointer");
-    final name = Name("#fromPointer");
+    final name = Name("#fromTypedDataBase");
     final referenceFrom = indexedClass?.lookupConstructor(name.text);
     final Constructor ctor = Constructor(
         FunctionNode(EmptyStatement(), positionalParameters: [pointer]),
@@ -312,6 +378,16 @@
       if (_isPointerType(dartType)) {
         nativeType = NativeType.kPointer;
         clazz = pointerClass;
+      } else if (_isStructSubtype(dartType)) {
+        nativeType = NativeType.kStruct;
+        clazz = (dartType as InterfaceType).classNode;
+        if (emptyStructs.contains(clazz)) {
+          diagnosticReporter.report(
+              templateFfiEmptyStruct.withArguments(clazz.name),
+              m.fileOffset,
+              1,
+              m.location.file);
+        }
       } else {
         final nativeTypeAnnos = _getNativeTypeAnnotations(m).toList();
         if (nativeTypeAnnos.length == 1) {
@@ -345,14 +421,15 @@
       emptyStructs.add(node);
     }
 
-    final sizeAndOffsets = <Abi, SizeAndOffsets>{};
+    final structLayout = <Abi, StructLayout>{};
     for (final Abi abi in Abi.values) {
-      sizeAndOffsets[abi] = _calculateSizeAndOffsets(types, abi);
+      structLayout[abi] = _calculateStructLayout(types, classes, abi);
     }
+    structLayouts[node] = structLayout;
 
     for (final i in fields.keys) {
-      final fieldOffsets = sizeAndOffsets
-          .map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
+      final fieldOffsets = structLayout
+          .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
       final methods = _generateMethodsForField(
           fields[i], types[i], fieldOffsets, indexedClass);
       methods.forEach((p) => node.addProcedure(p));
@@ -363,8 +440,8 @@
     }
 
     for (final i in getters.keys) {
-      final fieldOffsets = sizeAndOffsets
-          .map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
+      final fieldOffsets = structLayout
+          .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
       Procedure getter = getters[i];
       getter.function.body = _generateGetterStatement(
           getter.function.returnType,
@@ -375,8 +452,8 @@
     }
 
     for (final i in setters.keys) {
-      final fieldOffsets = sizeAndOffsets
-          .map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
+      final fieldOffsets = structLayout
+          .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
       Procedure setter = setters[i];
       setter.function.body = _generateSetterStatement(
           setter.function.positionalParameters.single.type,
@@ -387,7 +464,7 @@
       setter.isExternal = false;
     }
 
-    return sizeAndOffsets.map((k, v) => MapEntry(k, v.size));
+    return structLayout.map((k, v) => MapEntry(k, v.size));
   }
 
   void _annoteStructWithFields(Class node, List<Class> fieldTypes) {
@@ -433,6 +510,7 @@
   Statement _generateGetterStatement(DartType dartType, NativeType type,
       int fileOffset, Map<Abi, int> offsets) {
     final bool isPointer = type == NativeType.kPointer;
+    final bool isStruct = type == NativeType.kStruct;
 
     // Sample output:
     // int get x => _loadInt8(pointer, offset);
@@ -440,11 +518,87 @@
     // Treat Pointer fields different to get correct behavior without casts:
     // Pointer<Int8> get x =>
     //   _fromAddress<Int8>(_loadIntPtr(pointer, offset));
-    final loadMethod = isPointer
-        ? loadMethods[NativeType.kIntptr]
-        : optimizedTypes.contains(type)
-            ? loadMethods[type]
-            : loadStructMethod;
+    //
+    // Nested structs:
+    // MyStruct get x =>
+    //   MyStruct.#fromTypedDataBase(
+    //     _addressOf is Pointer ?
+    //       _fromAddress<MyStruct>((_addressOf as Pointer).address + offset) :
+    //       (_addressOf as TypedData).buffer.asInt8List(
+    //         (_addressOf as TypedData).offsetInBytes + offset,
+    //         size
+    //       )
+    //   );
+    if (isStruct) {
+      final clazz = (dartType as InterfaceType).classNode;
+      final constructor = clazz.constructors
+          .firstWhere((c) => c.name == Name("#fromTypedDataBase"));
+      final lengths =
+          structLayouts[clazz].map((key, value) => MapEntry(key, value.size));
+      Expression thisDotAddressOf() =>
+          PropertyGet(ThisExpression(), addressOfField.name, addressOfField)
+            ..fileOffset = fileOffset;
+      return ReturnStatement(ConstructorInvocation(
+          constructor,
+          Arguments([
+            ConditionalExpression(
+                IsExpression(thisDotAddressOf(),
+                    InterfaceType(pointerClass, Nullability.legacy)),
+                StaticInvocation(
+                    fromAddressInternal,
+                    Arguments([
+                      MethodInvocation(
+                          PropertyGet(thisDotAddressOf(), addressGetter.name,
+                              addressGetter)
+                            ..fileOffset = fileOffset,
+                          numAddition.name,
+                          Arguments([_runtimeBranchOnLayout(offsets)]),
+                          numAddition)
+                    ], types: [
+                      dartType
+                    ]))
+                  ..fileOffset = fileOffset,
+                MethodInvocation(
+                    PropertyGet(
+                        StaticInvocation(
+                            unsafeCastMethod,
+                            Arguments([
+                              thisDotAddressOf()
+                            ], types: [
+                              InterfaceType(typedDataClass, Nullability.legacy)
+                            ]))
+                          ..fileOffset = fileOffset,
+                        typedDataBufferGetter.name,
+                        typedDataBufferGetter)
+                      ..fileOffset = fileOffset,
+                    byteBufferAsUint8List.name,
+                    Arguments([
+                      MethodInvocation(
+                          PropertyGet(
+                              StaticInvocation(
+                                  unsafeCastMethod,
+                                  Arguments([
+                                    thisDotAddressOf()
+                                  ], types: [
+                                    InterfaceType(
+                                        typedDataClass, Nullability.legacy)
+                                  ]))
+                                ..fileOffset = fileOffset,
+                              typedDataOffsetInBytesGetter.name,
+                              typedDataOffsetInBytesGetter)
+                            ..fileOffset = fileOffset,
+                          numAddition.name,
+                          Arguments([_runtimeBranchOnLayout(offsets)]),
+                          numAddition),
+                      _runtimeBranchOnLayout(lengths)
+                    ]),
+                    byteBufferAsUint8List),
+                InterfaceType(objectClass, Nullability.nonNullable))
+          ]))
+        ..fileOffset = fileOffset);
+    }
+    final loadMethod =
+        isPointer ? loadMethods[NativeType.kIntptr] : loadMethods[type];
     Expression getterReturnValue = StaticInvocation(
         loadMethod,
         Arguments([
@@ -465,6 +619,7 @@
   Statement _generateSetterStatement(DartType dartType, NativeType type,
       int fileOffset, Map<Abi, int> offsets, VariableDeclaration argument) {
     final bool isPointer = type == NativeType.kPointer;
+    final bool isStruct = type == NativeType.kStruct;
 
     // Sample output:
     // set x(int v) => _storeInt8(pointer, offset, v);
@@ -472,6 +627,28 @@
     // Treat Pointer fields different to get correct behavior without casts:
     // set x(Pointer<Int8> v) =>
     //   _storeIntPtr(pointer, offset, (v as Pointer<Int8>).address);
+    //
+    // Nested structs:
+    // set x(MyStruct v) =>
+    //   _memCopy(this._address, offset, v._address, 0, size);
+    if (isStruct) {
+      final clazz = (dartType as InterfaceType).classNode;
+      final lengths =
+          structLayouts[clazz].map((key, value) => MapEntry(key, value.size));
+      return ReturnStatement(StaticInvocation(
+          memCopy,
+          Arguments([
+            PropertyGet(ThisExpression(), addressOfField.name, addressOfField)
+              ..fileOffset = fileOffset,
+            _runtimeBranchOnLayout(offsets),
+            PropertyGet(
+                VariableGet(argument), addressOfField.name, addressOfField)
+              ..fileOffset = fileOffset,
+            ConstantExpression(IntConstant(0)),
+            _runtimeBranchOnLayout(lengths),
+          ]))
+        ..fileOffset = fileOffset);
+    }
     final storeMethod =
         isPointer ? storeMethods[NativeType.kIntptr] : storeMethods[type];
     Expression argumentExpression = VariableGet(argument)
@@ -530,7 +707,7 @@
   }
 
   /// Sample output:
-  /// static int #sizeOf() => 24;
+  /// int #sizeOf => [24,24,16][_abi()];
   void _replaceSizeOfMethod(
       Class struct, Map<Abi, int> sizes, IndexedClass indexedClass) {
     var name = Name("#sizeOf");
@@ -548,7 +725,15 @@
     struct.addField(sizeOf);
   }
 
-  int _sizeInBytes(NativeType type, Abi abi) {
+  int _sizeInBytes(NativeType type, Class clazz, Abi abi) {
+    if (type == NativeType.kStruct) {
+      final structLayout = structLayouts[clazz];
+      if (structLayout == null) {
+        // We have a cycle, so we don't know the size.
+        return 0;
+      }
+      return structLayout[abi].size;
+    }
     final int size = nativeTypeSizes[type.index];
     if (size == WORD_SIZE) {
       return wordSize[abi];
@@ -556,10 +741,18 @@
     return size;
   }
 
-  int _alignmentOf(NativeType type, Abi abi) {
+  int _alignmentOf(NativeType type, Class clazz, Abi abi) {
+    if (type == NativeType.kStruct) {
+      final structLayout = structLayouts[clazz];
+      if (structLayout == null) {
+        // We have a cycle, so we don't know the size.
+        return 0;
+      }
+      return structLayout[abi].alignment;
+    }
     final int alignment = nonSizeAlignment[abi][type];
     if (alignment != null) return alignment;
-    return _sizeInBytes(type, abi);
+    return _sizeInBytes(type, clazz, abi);
   }
 
   int _alignOffset(int offset, int alignment) {
@@ -573,24 +766,21 @@
 
   // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
   // NativeCompoundType::FromNativeTypes.
-  //
-  // TODO(37271): Support nested structs.
-  SizeAndOffsets _calculateSizeAndOffsets(List<NativeType> types, Abi abi) {
+  StructLayout _calculateStructLayout(
+      List<NativeType> types, List<Class> classes, Abi abi) {
     int offset = 0;
     final offsets = <int>[];
-    for (final NativeType t in types) {
-      final int size = _sizeInBytes(t, abi);
-      final int alignment = _alignmentOf(t, abi);
+    int structAlignment = 1;
+    for (int i = 0; i < types.length; i++) {
+      final int size = _sizeInBytes(types[i], classes[i], abi);
+      final int alignment = _alignmentOf(types[i], classes[i], abi);
       offset = _alignOffset(offset, alignment);
       offsets.add(offset);
       offset += size;
+      structAlignment = math.max(structAlignment, alignment);
     }
-    final int minimumAlignment = 1;
-    final sizeAlignment = types
-        .map((t) => _alignmentOf(t, abi))
-        .followedBy([minimumAlignment]).reduce(math.max);
-    final int size = _alignOffset(offset, sizeAlignment);
-    return SizeAndOffsets(size, offsets);
+    final int size = _alignOffset(offset, structAlignment);
+    return StructLayout(size, structAlignment, offsets);
   }
 
   void _makeEntryPoint(Annotatable node) {
@@ -622,12 +812,16 @@
   }
 }
 
-class SizeAndOffsets {
+/// The layout of a `Struct` in one [Abi].
+class StructLayout {
   /// Size of the entire struct.
   final int size;
 
+  /// Alignment of struct when nested in other struct.
+  final int alignment;
+
   /// Offset in bytes for each field, indexed by field number.
   final List<int> offsets;
 
-  SizeAndOffsets(this.size, this.offsets);
+  StructLayout(this.size, this.alignment, this.offsets);
 }
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index f4a9c05..295b92d 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -37,7 +37,8 @@
     DiagnosticReporter diagnosticReporter,
     FfiTransformerData ffiTransformerData,
     ReferenceFromIndex referenceFromIndex) {
-  final index = new LibraryIndex(component, ["dart:ffi"]);
+  final index = new LibraryIndex(
+      component, ["dart:ffi", "dart:_internal", "dart:typed_data"]);
   if (!index.containsLibrary("dart:ffi")) {
     // TODO: This check doesn't make sense: "dart:ffi" is always loaded/created
     // for the VM target.
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 57d6a01..69a0e51 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -612,7 +612,6 @@
 // A struct designed to exercise all kinds of alignment rules.
 // Note that offset32A (System V ia32, iOS arm) aligns doubles on 4 bytes while
 // offset32B (Arm 32 bit and MSVC ia32) aligns on 8 bytes.
-// TODO(37271): Support nested structs.
 // TODO(37470): Add uncommon primitive data types when we want to support them.
 struct VeryLargeStruct {
   //                             size32 size64 offset32A offset32B offset64
@@ -784,6 +783,31 @@
   return result;
 }
 
+// Can't easily share this with the generated file.
+struct Struct4BytesHomogeneousInt16Copy {
+  int16_t a0;
+  int16_t a1;
+};
+
+// Can't easily share this with the generated file.
+struct Struct8BytesNestedIntCopy {
+  Struct4BytesHomogeneousInt16Copy a0;
+  Struct4BytesHomogeneousInt16Copy a1;
+};
+
+DART_EXPORT void CallbackWithStruct(void (*f)(Struct8BytesNestedIntCopy)) {
+  std::cout << "CallbackWithStruct"
+            << "(" << reinterpret_cast<void*>(f) << ")\n";
+
+  Struct8BytesNestedIntCopy arg;
+  arg.a0.a0 = 10;
+  arg.a0.a1 = 11;
+  arg.a1.a0 = 12;
+  arg.a1.a1 = 13;
+
+  f(arg);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Tests for callbacks.
 
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index 74f27e4..a77bff1 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -56,6 +56,10 @@
   int16_t a1;
 };
 
+struct Struct4BytesFloat {
+  float a0;
+};
+
 struct Struct7BytesHomogeneousUint8 {
   uint8_t a0;
   uint8_t a1;
@@ -339,6 +343,75 @@
   int8_t a2;
 };
 
+struct Struct8BytesNestedInt {
+  Struct4BytesHomogeneousInt16 a0;
+  Struct4BytesHomogeneousInt16 a1;
+};
+
+struct Struct8BytesNestedFloat {
+  Struct4BytesFloat a0;
+  Struct4BytesFloat a1;
+};
+
+struct Struct8BytesNestedFloat2 {
+  Struct4BytesFloat a0;
+  float a1;
+};
+
+struct Struct8BytesNestedMixed {
+  Struct4BytesHomogeneousInt16 a0;
+  Struct4BytesFloat a1;
+};
+
+struct Struct16BytesNestedInt {
+  Struct8BytesNestedInt a0;
+  Struct8BytesNestedInt a1;
+};
+
+struct Struct32BytesNestedInt {
+  Struct16BytesNestedInt a0;
+  Struct16BytesNestedInt a1;
+};
+
+struct StructNestedIntStructAlignmentInt16 {
+  StructAlignmentInt16 a0;
+  StructAlignmentInt16 a1;
+};
+
+struct StructNestedIntStructAlignmentInt32 {
+  StructAlignmentInt32 a0;
+  StructAlignmentInt32 a1;
+};
+
+struct StructNestedIntStructAlignmentInt64 {
+  StructAlignmentInt64 a0;
+  StructAlignmentInt64 a1;
+};
+
+struct StructNestedIrregularBig {
+  uint16_t a0;
+  Struct8BytesNestedMixed a1;
+  uint16_t a2;
+  Struct8BytesNestedFloat2 a3;
+  uint16_t a4;
+  Struct8BytesNestedFloat a5;
+  uint16_t a6;
+};
+
+struct StructNestedIrregularBigger {
+  StructNestedIrregularBig a0;
+  Struct8BytesNestedMixed a1;
+  float a2;
+  double a3;
+};
+
+struct StructNestedIrregularEvenBigger {
+  uint64_t a0;
+  StructNestedIrregularBigger a1;
+  StructNestedIrregularBigger a2;
+  double a3;
+};
+
 // Used for testing structs by value.
 // Smallest struct with data.
 // 10 struct arguments will exhaust available registers.
@@ -2569,6 +2642,639 @@
 }
 
 // Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust registers on all platforms.
+DART_EXPORT int64_t PassStruct8BytesNestedIntx10(Struct8BytesNestedInt a0,
+                                                 Struct8BytesNestedInt a1,
+                                                 Struct8BytesNestedInt a2,
+                                                 Struct8BytesNestedInt a3,
+                                                 Struct8BytesNestedInt a4,
+                                                 Struct8BytesNestedInt a5,
+                                                 Struct8BytesNestedInt a6,
+                                                 Struct8BytesNestedInt a7,
+                                                 Struct8BytesNestedInt a8,
+                                                 Struct8BytesNestedInt a9) {
+  std::cout << "PassStruct8BytesNestedIntx10"
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+            << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")), ((" << a2.a0.a0
+            << ", " << a2.a0.a1 << "), (" << a2.a1.a0 << ", " << a2.a1.a1
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+            << ", " << a3.a1.a1 << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1
+            << "), (" << a4.a1.a0 << ", " << a4.a1.a1 << ")), ((" << a5.a0.a0
+            << ", " << a5.a0.a1 << "), (" << a5.a1.a0 << ", " << a5.a1.a1
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+            << ", " << a6.a1.a1 << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1
+            << "), (" << a7.a1.a0 << ", " << a7.a1.a1 << ")), ((" << a8.a0.a0
+            << ", " << a8.a0.a1 << "), (" << a8.a1.a0 << ", " << a8.a1.a1
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+            << ", " << a9.a1.a1 << ")))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a0.a1;
+  result += a0.a1.a0;
+  result += a0.a1.a1;
+  result += a1.a0.a0;
+  result += a1.a0.a1;
+  result += a1.a1.a0;
+  result += a1.a1.a1;
+  result += a2.a0.a0;
+  result += a2.a0.a1;
+  result += a2.a1.a0;
+  result += a2.a1.a1;
+  result += a3.a0.a0;
+  result += a3.a0.a1;
+  result += a3.a1.a0;
+  result += a3.a1.a1;
+  result += a4.a0.a0;
+  result += a4.a0.a1;
+  result += a4.a1.a0;
+  result += a4.a1.a1;
+  result += a5.a0.a0;
+  result += a5.a0.a1;
+  result += a5.a1.a0;
+  result += a5.a1.a1;
+  result += a6.a0.a0;
+  result += a6.a0.a1;
+  result += a6.a1.a0;
+  result += a6.a1.a1;
+  result += a7.a0.a0;
+  result += a7.a0.a1;
+  result += a7.a1.a0;
+  result += a7.a1.a1;
+  result += a8.a0.a0;
+  result += a8.a0.a1;
+  result += a8.a1.a0;
+  result += a8.a1.a1;
+  result += a9.a0.a0;
+  result += a9.a0.a1;
+  result += a9.a1.a0;
+  result += a9.a1.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+DART_EXPORT float PassStruct8BytesNestedFloatx10(Struct8BytesNestedFloat a0,
+                                                 Struct8BytesNestedFloat a1,
+                                                 Struct8BytesNestedFloat a2,
+                                                 Struct8BytesNestedFloat a3,
+                                                 Struct8BytesNestedFloat a4,
+                                                 Struct8BytesNestedFloat a5,
+                                                 Struct8BytesNestedFloat a6,
+                                                 Struct8BytesNestedFloat a7,
+                                                 Struct8BytesNestedFloat a8,
+                                                 Struct8BytesNestedFloat a9) {
+  std::cout << "PassStruct8BytesNestedFloatx10"
+            << "(((" << a0.a0.a0 << "), (" << a0.a1.a0 << ")), ((" << a1.a0.a0
+            << "), (" << a1.a1.a0 << ")), ((" << a2.a0.a0 << "), (" << a2.a1.a0
+            << ")), ((" << a3.a0.a0 << "), (" << a3.a1.a0 << ")), (("
+            << a4.a0.a0 << "), (" << a4.a1.a0 << ")), ((" << a5.a0.a0 << "), ("
+            << a5.a1.a0 << ")), ((" << a6.a0.a0 << "), (" << a6.a1.a0
+            << ")), ((" << a7.a0.a0 << "), (" << a7.a1.a0 << ")), (("
+            << a8.a0.a0 << "), (" << a8.a1.a0 << ")), ((" << a9.a0.a0 << "), ("
+            << a9.a1.a0 << ")))"
+            << "\n";
+
+  float result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a1.a0;
+  result += a1.a0.a0;
+  result += a1.a1.a0;
+  result += a2.a0.a0;
+  result += a2.a1.a0;
+  result += a3.a0.a0;
+  result += a3.a1.a0;
+  result += a4.a0.a0;
+  result += a4.a1.a0;
+  result += a5.a0.a0;
+  result += a5.a1.a0;
+  result += a6.a0.a0;
+  result += a6.a1.a0;
+  result += a7.a0.a0;
+  result += a7.a1.a0;
+  result += a8.a0.a0;
+  result += a8.a1.a0;
+  result += a9.a0.a0;
+  result += a9.a1.a0;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT float PassStruct8BytesNestedFloat2x10(Struct8BytesNestedFloat2 a0,
+                                                  Struct8BytesNestedFloat2 a1,
+                                                  Struct8BytesNestedFloat2 a2,
+                                                  Struct8BytesNestedFloat2 a3,
+                                                  Struct8BytesNestedFloat2 a4,
+                                                  Struct8BytesNestedFloat2 a5,
+                                                  Struct8BytesNestedFloat2 a6,
+                                                  Struct8BytesNestedFloat2 a7,
+                                                  Struct8BytesNestedFloat2 a8,
+                                                  Struct8BytesNestedFloat2 a9) {
+  std::cout << "PassStruct8BytesNestedFloat2x10"
+            << "(((" << a0.a0.a0 << "), " << a0.a1 << "), ((" << a1.a0.a0
+            << "), " << a1.a1 << "), ((" << a2.a0.a0 << "), " << a2.a1
+            << "), ((" << a3.a0.a0 << "), " << a3.a1 << "), ((" << a4.a0.a0
+            << "), " << a4.a1 << "), ((" << a5.a0.a0 << "), " << a5.a1
+            << "), ((" << a6.a0.a0 << "), " << a6.a1 << "), ((" << a7.a0.a0
+            << "), " << a7.a1 << "), ((" << a8.a0.a0 << "), " << a8.a1
+            << "), ((" << a9.a0.a0 << "), " << a9.a1 << "))"
+            << "\n";
+
+  float result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a1;
+  result += a1.a0.a0;
+  result += a1.a1;
+  result += a2.a0.a0;
+  result += a2.a1;
+  result += a3.a0.a0;
+  result += a3.a1;
+  result += a4.a0.a0;
+  result += a4.a1;
+  result += a5.a0.a0;
+  result += a5.a1;
+  result += a6.a0.a0;
+  result += a6.a1;
+  result += a7.a0.a0;
+  result += a7.a1;
+  result += a8.a0.a0;
+  result += a8.a1;
+  result += a9.a0.a0;
+  result += a9.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust all registers on all platforms.
+DART_EXPORT double PassStruct8BytesNestedMixedx10(Struct8BytesNestedMixed a0,
+                                                  Struct8BytesNestedMixed a1,
+                                                  Struct8BytesNestedMixed a2,
+                                                  Struct8BytesNestedMixed a3,
+                                                  Struct8BytesNestedMixed a4,
+                                                  Struct8BytesNestedMixed a5,
+                                                  Struct8BytesNestedMixed a6,
+                                                  Struct8BytesNestedMixed a7,
+                                                  Struct8BytesNestedMixed a8,
+                                                  Struct8BytesNestedMixed a9) {
+  std::cout << "PassStruct8BytesNestedMixedx10"
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << "), (" << a1.a1.a0
+            << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << "), (" << a2.a1.a0
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+            << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << "), (" << a4.a1.a0
+            << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << "), (" << a5.a1.a0
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+            << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << "), (" << a7.a1.a0
+            << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << "), (" << a8.a1.a0
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+            << ")))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a0.a1;
+  result += a0.a1.a0;
+  result += a1.a0.a0;
+  result += a1.a0.a1;
+  result += a1.a1.a0;
+  result += a2.a0.a0;
+  result += a2.a0.a1;
+  result += a2.a1.a0;
+  result += a3.a0.a0;
+  result += a3.a0.a1;
+  result += a3.a1.a0;
+  result += a4.a0.a0;
+  result += a4.a0.a1;
+  result += a4.a1.a0;
+  result += a5.a0.a0;
+  result += a5.a0.a1;
+  result += a5.a1.a0;
+  result += a6.a0.a0;
+  result += a6.a0.a1;
+  result += a6.a1.a0;
+  result += a7.a0.a0;
+  result += a7.a0.a1;
+  result += a7.a1.a0;
+  result += a8.a0.a0;
+  result += a8.a0.a1;
+  result += a8.a1.a0;
+  result += a9.a0.a0;
+  result += a9.a0.a1;
+  result += a9.a1.a0;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT int64_t PassStruct16BytesNestedIntx2(Struct16BytesNestedInt a0,
+                                                 Struct16BytesNestedInt a1) {
+  std::cout << "PassStruct16BytesNestedIntx2"
+            << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+            << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+            << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+            << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+            << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+            << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+            << ", " << a1.a1.a1.a1 << "))))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0.a0.a0;
+  result += a0.a0.a0.a1;
+  result += a0.a0.a1.a0;
+  result += a0.a0.a1.a1;
+  result += a0.a1.a0.a0;
+  result += a0.a1.a0.a1;
+  result += a0.a1.a1.a0;
+  result += a0.a1.a1.a1;
+  result += a1.a0.a0.a0;
+  result += a1.a0.a0.a1;
+  result += a1.a0.a1.a0;
+  result += a1.a0.a1.a1;
+  result += a1.a1.a0.a0;
+  result += a1.a1.a0.a1;
+  result += a1.a1.a1.a0;
+  result += a1.a1.a1.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT int64_t PassStruct32BytesNestedIntx2(Struct32BytesNestedInt a0,
+                                                 Struct32BytesNestedInt a1) {
+  std::cout << "PassStruct32BytesNestedIntx2"
+            << "(((((" << a0.a0.a0.a0.a0 << ", " << a0.a0.a0.a0.a1 << "), ("
+            << a0.a0.a0.a1.a0 << ", " << a0.a0.a0.a1.a1 << ")), (("
+            << a0.a0.a1.a0.a0 << ", " << a0.a0.a1.a0.a1 << "), ("
+            << a0.a0.a1.a1.a0 << ", " << a0.a0.a1.a1.a1 << "))), ((("
+            << a0.a1.a0.a0.a0 << ", " << a0.a1.a0.a0.a1 << "), ("
+            << a0.a1.a0.a1.a0 << ", " << a0.a1.a0.a1.a1 << ")), (("
+            << a0.a1.a1.a0.a0 << ", " << a0.a1.a1.a0.a1 << "), ("
+            << a0.a1.a1.a1.a0 << ", " << a0.a1.a1.a1.a1 << ")))), (((("
+            << a1.a0.a0.a0.a0 << ", " << a1.a0.a0.a0.a1 << "), ("
+            << a1.a0.a0.a1.a0 << ", " << a1.a0.a0.a1.a1 << ")), (("
+            << a1.a0.a1.a0.a0 << ", " << a1.a0.a1.a0.a1 << "), ("
+            << a1.a0.a1.a1.a0 << ", " << a1.a0.a1.a1.a1 << "))), ((("
+            << a1.a1.a0.a0.a0 << ", " << a1.a1.a0.a0.a1 << "), ("
+            << a1.a1.a0.a1.a0 << ", " << a1.a1.a0.a1.a1 << ")), (("
+            << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1 << "), ("
+            << a1.a1.a1.a1.a0 << ", " << a1.a1.a1.a1.a1 << ")))))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0.a0.a0.a0;
+  result += a0.a0.a0.a0.a1;
+  result += a0.a0.a0.a1.a0;
+  result += a0.a0.a0.a1.a1;
+  result += a0.a0.a1.a0.a0;
+  result += a0.a0.a1.a0.a1;
+  result += a0.a0.a1.a1.a0;
+  result += a0.a0.a1.a1.a1;
+  result += a0.a1.a0.a0.a0;
+  result += a0.a1.a0.a0.a1;
+  result += a0.a1.a0.a1.a0;
+  result += a0.a1.a0.a1.a1;
+  result += a0.a1.a1.a0.a0;
+  result += a0.a1.a1.a0.a1;
+  result += a0.a1.a1.a1.a0;
+  result += a0.a1.a1.a1.a1;
+  result += a1.a0.a0.a0.a0;
+  result += a1.a0.a0.a0.a1;
+  result += a1.a0.a0.a1.a0;
+  result += a1.a0.a0.a1.a1;
+  result += a1.a0.a1.a0.a0;
+  result += a1.a0.a1.a0.a1;
+  result += a1.a0.a1.a1.a0;
+  result += a1.a0.a1.a1.a1;
+  result += a1.a1.a0.a0.a0;
+  result += a1.a1.a0.a0.a1;
+  result += a1.a1.a0.a1.a0;
+  result += a1.a1.a0.a1.a1;
+  result += a1.a1.a1.a0.a0;
+  result += a1.a1.a1.a0.a1;
+  result += a1.a1.a1.a1.a0;
+  result += a1.a1.a1.a1.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt16(
+    StructNestedIntStructAlignmentInt16 a0) {
+  std::cout << "PassStructNestedIntStructAlignmentInt16"
+            << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+            << static_cast<int>(a0.a0.a2) << "), ("
+            << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+            << static_cast<int>(a0.a1.a2) << ")))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a0.a1;
+  result += a0.a0.a2;
+  result += a0.a1.a0;
+  result += a0.a1.a1;
+  result += a0.a1.a2;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt32(
+    StructNestedIntStructAlignmentInt32 a0) {
+  std::cout << "PassStructNestedIntStructAlignmentInt32"
+            << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+            << static_cast<int>(a0.a0.a2) << "), ("
+            << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+            << static_cast<int>(a0.a1.a2) << ")))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a0.a1;
+  result += a0.a0.a2;
+  result += a0.a1.a0;
+  result += a0.a1.a1;
+  result += a0.a1.a2;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt64(
+    StructNestedIntStructAlignmentInt64 a0) {
+  std::cout << "PassStructNestedIntStructAlignmentInt64"
+            << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+            << static_cast<int>(a0.a0.a2) << "), ("
+            << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+            << static_cast<int>(a0.a1.a2) << ")))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0.a0;
+  result += a0.a0.a1;
+  result += a0.a0.a2;
+  result += a0.a1.a0;
+  result += a0.a1.a1;
+  result += a0.a1.a2;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT double PassStructNestedIrregularEvenBiggerx4(
+    StructNestedIrregularEvenBigger a0,
+    StructNestedIrregularEvenBigger a1,
+    StructNestedIrregularEvenBigger a2,
+    StructNestedIrregularEvenBigger a3) {
+  std::cout
+      << "PassStructNestedIrregularEvenBiggerx4"
+      << "((" << a0.a0 << ", ((" << a0.a1.a0.a0 << ", ((" << a0.a1.a0.a1.a0.a0
+      << ", " << a0.a1.a0.a1.a0.a1 << "), (" << a0.a1.a0.a1.a1.a0 << ")), "
+      << a0.a1.a0.a2 << ", ((" << a0.a1.a0.a3.a0.a0 << "), " << a0.a1.a0.a3.a1
+      << "), " << a0.a1.a0.a4 << ", ((" << a0.a1.a0.a5.a0.a0 << "), ("
+      << a0.a1.a0.a5.a1.a0 << ")), " << a0.a1.a0.a6 << "), ((" << a0.a1.a1.a0.a0
+      << ", " << a0.a1.a1.a0.a1 << "), (" << a0.a1.a1.a1.a0 << ")), "
+      << a0.a1.a2 << ", " << a0.a1.a3 << "), ((" << a0.a2.a0.a0 << ", (("
+      << a0.a2.a0.a1.a0.a0 << ", " << a0.a2.a0.a1.a0.a1 << "), ("
+      << a0.a2.a0.a1.a1.a0 << ")), " << a0.a2.a0.a2 << ", (("
+      << a0.a2.a0.a3.a0.a0 << "), " << a0.a2.a0.a3.a1 << "), " << a0.a2.a0.a4
+      << ", ((" << a0.a2.a0.a5.a0.a0 << "), (" << a0.a2.a0.a5.a1.a0 << ")), "
+      << a0.a2.a0.a6 << "), ((" << a0.a2.a1.a0.a0 << ", " << a0.a2.a1.a0.a1
+      << "), (" << a0.a2.a1.a1.a0 << ")), " << a0.a2.a2 << ", " << a0.a2.a3
+      << "), " << a0.a3 << "), (" << a1.a0 << ", ((" << a1.a1.a0.a0 << ", (("
+      << a1.a1.a0.a1.a0.a0 << ", " << a1.a1.a0.a1.a0.a1 << "), ("
+      << a1.a1.a0.a1.a1.a0 << ")), " << a1.a1.a0.a2 << ", (("
+      << a1.a1.a0.a3.a0.a0 << "), " << a1.a1.a0.a3.a1 << "), " << a1.a1.a0.a4
+      << ", ((" << a1.a1.a0.a5.a0.a0 << "), (" << a1.a1.a0.a5.a1.a0 << ")), "
+      << a1.a1.a0.a6 << "), ((" << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1
+      << "), (" << a1.a1.a1.a1.a0 << ")), " << a1.a1.a2 << ", " << a1.a1.a3
+      << "), ((" << a1.a2.a0.a0 << ", ((" << a1.a2.a0.a1.a0.a0 << ", "
+      << a1.a2.a0.a1.a0.a1 << "), (" << a1.a2.a0.a1.a1.a0 << ")), "
+      << a1.a2.a0.a2 << ", ((" << a1.a2.a0.a3.a0.a0 << "), " << a1.a2.a0.a3.a1
+      << "), " << a1.a2.a0.a4 << ", ((" << a1.a2.a0.a5.a0.a0 << "), ("
+      << a1.a2.a0.a5.a1.a0 << ")), " << a1.a2.a0.a6 << "), ((" << a1.a2.a1.a0.a0
+      << ", " << a1.a2.a1.a0.a1 << "), (" << a1.a2.a1.a1.a0 << ")), "
+      << a1.a2.a2 << ", " << a1.a2.a3 << "), " << a1.a3 << "), (" << a2.a0
+      << ", ((" << a2.a1.a0.a0 << ", ((" << a2.a1.a0.a1.a0.a0 << ", "
+      << a2.a1.a0.a1.a0.a1 << "), (" << a2.a1.a0.a1.a1.a0 << ")), "
+      << a2.a1.a0.a2 << ", ((" << a2.a1.a0.a3.a0.a0 << "), " << a2.a1.a0.a3.a1
+      << "), " << a2.a1.a0.a4 << ", ((" << a2.a1.a0.a5.a0.a0 << "), ("
+      << a2.a1.a0.a5.a1.a0 << ")), " << a2.a1.a0.a6 << "), ((" << a2.a1.a1.a0.a0
+      << ", " << a2.a1.a1.a0.a1 << "), (" << a2.a1.a1.a1.a0 << ")), "
+      << a2.a1.a2 << ", " << a2.a1.a3 << "), ((" << a2.a2.a0.a0 << ", (("
+      << a2.a2.a0.a1.a0.a0 << ", " << a2.a2.a0.a1.a0.a1 << "), ("
+      << a2.a2.a0.a1.a1.a0 << ")), " << a2.a2.a0.a2 << ", (("
+      << a2.a2.a0.a3.a0.a0 << "), " << a2.a2.a0.a3.a1 << "), " << a2.a2.a0.a4
+      << ", ((" << a2.a2.a0.a5.a0.a0 << "), (" << a2.a2.a0.a5.a1.a0 << ")), "
+      << a2.a2.a0.a6 << "), ((" << a2.a2.a1.a0.a0 << ", " << a2.a2.a1.a0.a1
+      << "), (" << a2.a2.a1.a1.a0 << ")), " << a2.a2.a2 << ", " << a2.a2.a3
+      << "), " << a2.a3 << "), (" << a3.a0 << ", ((" << a3.a1.a0.a0 << ", (("
+      << a3.a1.a0.a1.a0.a0 << ", " << a3.a1.a0.a1.a0.a1 << "), ("
+      << a3.a1.a0.a1.a1.a0 << ")), " << a3.a1.a0.a2 << ", (("
+      << a3.a1.a0.a3.a0.a0 << "), " << a3.a1.a0.a3.a1 << "), " << a3.a1.a0.a4
+      << ", ((" << a3.a1.a0.a5.a0.a0 << "), (" << a3.a1.a0.a5.a1.a0 << ")), "
+      << a3.a1.a0.a6 << "), ((" << a3.a1.a1.a0.a0 << ", " << a3.a1.a1.a0.a1
+      << "), (" << a3.a1.a1.a1.a0 << ")), " << a3.a1.a2 << ", " << a3.a1.a3
+      << "), ((" << a3.a2.a0.a0 << ", ((" << a3.a2.a0.a1.a0.a0 << ", "
+      << a3.a2.a0.a1.a0.a1 << "), (" << a3.a2.a0.a1.a1.a0 << ")), "
+      << a3.a2.a0.a2 << ", ((" << a3.a2.a0.a3.a0.a0 << "), " << a3.a2.a0.a3.a1
+      << "), " << a3.a2.a0.a4 << ", ((" << a3.a2.a0.a5.a0.a0 << "), ("
+      << a3.a2.a0.a5.a1.a0 << ")), " << a3.a2.a0.a6 << "), ((" << a3.a2.a1.a0.a0
+      << ", " << a3.a2.a1.a0.a1 << "), (" << a3.a2.a1.a1.a0 << ")), "
+      << a3.a2.a2 << ", " << a3.a2.a3 << "), " << a3.a3 << "))"
+      << "\n";
+
+  double result = 0;
+
+  result += a0.a0;
+  result += a0.a1.a0.a0;
+  result += a0.a1.a0.a1.a0.a0;
+  result += a0.a1.a0.a1.a0.a1;
+  result += a0.a1.a0.a1.a1.a0;
+  result += a0.a1.a0.a2;
+  result += a0.a1.a0.a3.a0.a0;
+  result += a0.a1.a0.a3.a1;
+  result += a0.a1.a0.a4;
+  result += a0.a1.a0.a5.a0.a0;
+  result += a0.a1.a0.a5.a1.a0;
+  result += a0.a1.a0.a6;
+  result += a0.a1.a1.a0.a0;
+  result += a0.a1.a1.a0.a1;
+  result += a0.a1.a1.a1.a0;
+  result += a0.a1.a2;
+  result += a0.a1.a3;
+  result += a0.a2.a0.a0;
+  result += a0.a2.a0.a1.a0.a0;
+  result += a0.a2.a0.a1.a0.a1;
+  result += a0.a2.a0.a1.a1.a0;
+  result += a0.a2.a0.a2;
+  result += a0.a2.a0.a3.a0.a0;
+  result += a0.a2.a0.a3.a1;
+  result += a0.a2.a0.a4;
+  result += a0.a2.a0.a5.a0.a0;
+  result += a0.a2.a0.a5.a1.a0;
+  result += a0.a2.a0.a6;
+  result += a0.a2.a1.a0.a0;
+  result += a0.a2.a1.a0.a1;
+  result += a0.a2.a1.a1.a0;
+  result += a0.a2.a2;
+  result += a0.a2.a3;
+  result += a0.a3;
+  result += a1.a0;
+  result += a1.a1.a0.a0;
+  result += a1.a1.a0.a1.a0.a0;
+  result += a1.a1.a0.a1.a0.a1;
+  result += a1.a1.a0.a1.a1.a0;
+  result += a1.a1.a0.a2;
+  result += a1.a1.a0.a3.a0.a0;
+  result += a1.a1.a0.a3.a1;
+  result += a1.a1.a0.a4;
+  result += a1.a1.a0.a5.a0.a0;
+  result += a1.a1.a0.a5.a1.a0;
+  result += a1.a1.a0.a6;
+  result += a1.a1.a1.a0.a0;
+  result += a1.a1.a1.a0.a1;
+  result += a1.a1.a1.a1.a0;
+  result += a1.a1.a2;
+  result += a1.a1.a3;
+  result += a1.a2.a0.a0;
+  result += a1.a2.a0.a1.a0.a0;
+  result += a1.a2.a0.a1.a0.a1;
+  result += a1.a2.a0.a1.a1.a0;
+  result += a1.a2.a0.a2;
+  result += a1.a2.a0.a3.a0.a0;
+  result += a1.a2.a0.a3.a1;
+  result += a1.a2.a0.a4;
+  result += a1.a2.a0.a5.a0.a0;
+  result += a1.a2.a0.a5.a1.a0;
+  result += a1.a2.a0.a6;
+  result += a1.a2.a1.a0.a0;
+  result += a1.a2.a1.a0.a1;
+  result += a1.a2.a1.a1.a0;
+  result += a1.a2.a2;
+  result += a1.a2.a3;
+  result += a1.a3;
+  result += a2.a0;
+  result += a2.a1.a0.a0;
+  result += a2.a1.a0.a1.a0.a0;
+  result += a2.a1.a0.a1.a0.a1;
+  result += a2.a1.a0.a1.a1.a0;
+  result += a2.a1.a0.a2;
+  result += a2.a1.a0.a3.a0.a0;
+  result += a2.a1.a0.a3.a1;
+  result += a2.a1.a0.a4;
+  result += a2.a1.a0.a5.a0.a0;
+  result += a2.a1.a0.a5.a1.a0;
+  result += a2.a1.a0.a6;
+  result += a2.a1.a1.a0.a0;
+  result += a2.a1.a1.a0.a1;
+  result += a2.a1.a1.a1.a0;
+  result += a2.a1.a2;
+  result += a2.a1.a3;
+  result += a2.a2.a0.a0;
+  result += a2.a2.a0.a1.a0.a0;
+  result += a2.a2.a0.a1.a0.a1;
+  result += a2.a2.a0.a1.a1.a0;
+  result += a2.a2.a0.a2;
+  result += a2.a2.a0.a3.a0.a0;
+  result += a2.a2.a0.a3.a1;
+  result += a2.a2.a0.a4;
+  result += a2.a2.a0.a5.a0.a0;
+  result += a2.a2.a0.a5.a1.a0;
+  result += a2.a2.a0.a6;
+  result += a2.a2.a1.a0.a0;
+  result += a2.a2.a1.a0.a1;
+  result += a2.a2.a1.a1.a0;
+  result += a2.a2.a2;
+  result += a2.a2.a3;
+  result += a2.a3;
+  result += a3.a0;
+  result += a3.a1.a0.a0;
+  result += a3.a1.a0.a1.a0.a0;
+  result += a3.a1.a0.a1.a0.a1;
+  result += a3.a1.a0.a1.a1.a0;
+  result += a3.a1.a0.a2;
+  result += a3.a1.a0.a3.a0.a0;
+  result += a3.a1.a0.a3.a1;
+  result += a3.a1.a0.a4;
+  result += a3.a1.a0.a5.a0.a0;
+  result += a3.a1.a0.a5.a1.a0;
+  result += a3.a1.a0.a6;
+  result += a3.a1.a1.a0.a0;
+  result += a3.a1.a1.a0.a1;
+  result += a3.a1.a1.a1.a0;
+  result += a3.a1.a2;
+  result += a3.a1.a3;
+  result += a3.a2.a0.a0;
+  result += a3.a2.a0.a1.a0.a0;
+  result += a3.a2.a0.a1.a0.a1;
+  result += a3.a2.a0.a1.a1.a0;
+  result += a3.a2.a0.a2;
+  result += a3.a2.a0.a3.a0.a0;
+  result += a3.a2.a0.a3.a1;
+  result += a3.a2.a0.a4;
+  result += a3.a2.a0.a5.a0.a0;
+  result += a3.a2.a0.a5.a1.a0;
+  result += a3.a2.a0.a6;
+  result += a3.a2.a1.a0.a0;
+  result += a3.a2.a1.a0.a1;
+  result += a3.a2.a1.a1.a0;
+  result += a3.a2.a2;
+  result += a3.a2.a3;
+  result += a3.a3;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
 // Smallest struct with data.
 DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
   std::cout << "ReturnStruct1ByteInt"
@@ -3695,6 +4401,349 @@
 }
 
 // Used for testing structs by value.
+// Simple nested struct.
+DART_EXPORT Struct8BytesNestedInt
+ReturnStruct8BytesNestedInt(Struct4BytesHomogeneousInt16 a0,
+                            Struct4BytesHomogeneousInt16 a1) {
+  std::cout << "ReturnStruct8BytesNestedInt"
+            << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << ", "
+            << a1.a1 << "))"
+            << "\n";
+
+  Struct8BytesNestedInt result;
+
+  result.a0.a0 = a0.a0;
+  result.a0.a1 = a0.a1;
+  result.a1.a0 = a1.a0;
+  result.a1.a1 = a1.a1;
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+            << result.a1.a0 << ", " << result.a1.a1 << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with floats.
+DART_EXPORT Struct8BytesNestedFloat
+ReturnStruct8BytesNestedFloat(Struct4BytesFloat a0, Struct4BytesFloat a1) {
+  std::cout << "ReturnStruct8BytesNestedFloat"
+            << "((" << a0.a0 << "), (" << a1.a0 << "))"
+            << "\n";
+
+  Struct8BytesNestedFloat result;
+
+  result.a0.a0 = a0.a0;
+  result.a1.a0 = a1.a0;
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << "), (" << result.a1.a0 << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT Struct8BytesNestedFloat2
+ReturnStruct8BytesNestedFloat2(Struct4BytesFloat a0, float a1) {
+  std::cout << "ReturnStruct8BytesNestedFloat2"
+            << "((" << a0.a0 << "), " << a1 << ")"
+            << "\n";
+
+  Struct8BytesNestedFloat2 result;
+
+  result.a0.a0 = a0.a0;
+  result.a1 = a1;
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << "), " << result.a1 << ")"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with mixed members.
+DART_EXPORT Struct8BytesNestedMixed
+ReturnStruct8BytesNestedMixed(Struct4BytesHomogeneousInt16 a0,
+                              Struct4BytesFloat a1) {
+  std::cout << "ReturnStruct8BytesNestedMixed"
+            << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << "))"
+            << "\n";
+
+  Struct8BytesNestedMixed result;
+
+  result.a0.a0 = a0.a0;
+  result.a0.a1 = a0.a1;
+  result.a1.a0 = a1.a0;
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+            << result.a1.a0 << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT Struct16BytesNestedInt
+ReturnStruct16BytesNestedInt(Struct8BytesNestedInt a0,
+                             Struct8BytesNestedInt a1) {
+  std::cout << "ReturnStruct16BytesNestedInt"
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+            << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")))"
+            << "\n";
+
+  Struct16BytesNestedInt result;
+
+  result.a0.a0.a0 = a0.a0.a0;
+  result.a0.a0.a1 = a0.a0.a1;
+  result.a0.a1.a0 = a0.a1.a0;
+  result.a0.a1.a1 = a0.a1.a1;
+  result.a1.a0.a0 = a1.a0.a0;
+  result.a1.a0.a1 = a1.a0.a1;
+  result.a1.a1.a0 = a1.a1.a0;
+  result.a1.a1.a1 = a1.a1.a1;
+
+  std::cout << "result = "
+            << "(((" << result.a0.a0.a0 << ", " << result.a0.a0.a1 << "), ("
+            << result.a0.a1.a0 << ", " << result.a0.a1.a1 << ")), (("
+            << result.a1.a0.a0 << ", " << result.a1.a0.a1 << "), ("
+            << result.a1.a1.a0 << ", " << result.a1.a1.a1 << ")))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT Struct32BytesNestedInt
+ReturnStruct32BytesNestedInt(Struct16BytesNestedInt a0,
+                             Struct16BytesNestedInt a1) {
+  std::cout << "ReturnStruct32BytesNestedInt"
+            << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+            << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+            << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+            << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+            << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+            << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+            << ", " << a1.a1.a1.a1 << "))))"
+            << "\n";
+
+  Struct32BytesNestedInt result;
+
+  result.a0.a0.a0.a0 = a0.a0.a0.a0;
+  result.a0.a0.a0.a1 = a0.a0.a0.a1;
+  result.a0.a0.a1.a0 = a0.a0.a1.a0;
+  result.a0.a0.a1.a1 = a0.a0.a1.a1;
+  result.a0.a1.a0.a0 = a0.a1.a0.a0;
+  result.a0.a1.a0.a1 = a0.a1.a0.a1;
+  result.a0.a1.a1.a0 = a0.a1.a1.a0;
+  result.a0.a1.a1.a1 = a0.a1.a1.a1;
+  result.a1.a0.a0.a0 = a1.a0.a0.a0;
+  result.a1.a0.a0.a1 = a1.a0.a0.a1;
+  result.a1.a0.a1.a0 = a1.a0.a1.a0;
+  result.a1.a0.a1.a1 = a1.a0.a1.a1;
+  result.a1.a1.a0.a0 = a1.a1.a0.a0;
+  result.a1.a1.a0.a1 = a1.a1.a0.a1;
+  result.a1.a1.a1.a0 = a1.a1.a1.a0;
+  result.a1.a1.a1.a1 = a1.a1.a1.a1;
+
+  std::cout << "result = "
+            << "((((" << result.a0.a0.a0.a0 << ", " << result.a0.a0.a0.a1
+            << "), (" << result.a0.a0.a1.a0 << ", " << result.a0.a0.a1.a1
+            << ")), ((" << result.a0.a1.a0.a0 << ", " << result.a0.a1.a0.a1
+            << "), (" << result.a0.a1.a1.a0 << ", " << result.a0.a1.a1.a1
+            << "))), (((" << result.a1.a0.a0.a0 << ", " << result.a1.a0.a0.a1
+            << "), (" << result.a1.a0.a1.a0 << ", " << result.a1.a0.a1.a1
+            << ")), ((" << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1
+            << "), (" << result.a1.a1.a1.a0 << ", " << result.a1.a1.a1.a1
+            << "))))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT StructNestedIntStructAlignmentInt16
+ReturnStructNestedIntStructAlignmentInt16(StructAlignmentInt16 a0,
+                                          StructAlignmentInt16 a1) {
+  std::cout << "ReturnStructNestedIntStructAlignmentInt16"
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+            << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+            << "\n";
+
+  StructNestedIntStructAlignmentInt16 result;
+
+  result.a0.a0 = a0.a0;
+  result.a0.a1 = a0.a1;
+  result.a0.a2 = a0.a2;
+  result.a1.a0 = a1.a0;
+  result.a1.a1 = a1.a1;
+  result.a1.a2 = a1.a2;
+
+  std::cout << "result = "
+            << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+            << ", " << static_cast<int>(result.a0.a2) << "), ("
+            << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+            << static_cast<int>(result.a1.a2) << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT StructNestedIntStructAlignmentInt32
+ReturnStructNestedIntStructAlignmentInt32(StructAlignmentInt32 a0,
+                                          StructAlignmentInt32 a1) {
+  std::cout << "ReturnStructNestedIntStructAlignmentInt32"
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+            << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+            << "\n";
+
+  StructNestedIntStructAlignmentInt32 result;
+
+  result.a0.a0 = a0.a0;
+  result.a0.a1 = a0.a1;
+  result.a0.a2 = a0.a2;
+  result.a1.a0 = a1.a0;
+  result.a1.a1 = a1.a1;
+  result.a1.a2 = a1.a2;
+
+  std::cout << "result = "
+            << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+            << ", " << static_cast<int>(result.a0.a2) << "), ("
+            << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+            << static_cast<int>(result.a1.a2) << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT StructNestedIntStructAlignmentInt64
+ReturnStructNestedIntStructAlignmentInt64(StructAlignmentInt64 a0,
+                                          StructAlignmentInt64 a1) {
+  std::cout << "ReturnStructNestedIntStructAlignmentInt64"
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+            << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+            << "\n";
+
+  StructNestedIntStructAlignmentInt64 result;
+
+  result.a0.a0 = a0.a0;
+  result.a0.a1 = a0.a1;
+  result.a0.a2 = a0.a2;
+  result.a1.a0 = a1.a0;
+  result.a1.a1 = a1.a1;
+  result.a1.a2 = a1.a2;
+
+  std::cout << "result = "
+            << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+            << ", " << static_cast<int>(result.a0.a2) << "), ("
+            << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+            << static_cast<int>(result.a1.a2) << "))"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT StructNestedIrregularEvenBigger
+ReturnStructNestedIrregularEvenBigger(uint64_t a0,
+                                      StructNestedIrregularBigger a1,
+                                      StructNestedIrregularBigger a2,
+                                      double a3) {
+  std::cout << "ReturnStructNestedIrregularEvenBigger"
+            << "(" << a0 << ", ((" << a1.a0.a0 << ", ((" << a1.a0.a1.a0.a0
+            << ", " << a1.a0.a1.a0.a1 << "), (" << a1.a0.a1.a1.a0 << ")), "
+            << a1.a0.a2 << ", ((" << a1.a0.a3.a0.a0 << "), " << a1.a0.a3.a1
+            << "), " << a1.a0.a4 << ", ((" << a1.a0.a5.a0.a0 << "), ("
+            << a1.a0.a5.a1.a0 << ")), " << a1.a0.a6 << "), ((" << a1.a1.a0.a0
+            << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0 << ")), " << a1.a2
+            << ", " << a1.a3 << "), ((" << a2.a0.a0 << ", ((" << a2.a0.a1.a0.a0
+            << ", " << a2.a0.a1.a0.a1 << "), (" << a2.a0.a1.a1.a0 << ")), "
+            << a2.a0.a2 << ", ((" << a2.a0.a3.a0.a0 << "), " << a2.a0.a3.a1
+            << "), " << a2.a0.a4 << ", ((" << a2.a0.a5.a0.a0 << "), ("
+            << a2.a0.a5.a1.a0 << ")), " << a2.a0.a6 << "), ((" << a2.a1.a0.a0
+            << ", " << a2.a1.a0.a1 << "), (" << a2.a1.a1.a0 << ")), " << a2.a2
+            << ", " << a2.a3 << "), " << a3 << ")"
+            << "\n";
+
+  StructNestedIrregularEvenBigger result;
+
+  result.a0 = a0;
+  result.a1.a0.a0 = a1.a0.a0;
+  result.a1.a0.a1.a0.a0 = a1.a0.a1.a0.a0;
+  result.a1.a0.a1.a0.a1 = a1.a0.a1.a0.a1;
+  result.a1.a0.a1.a1.a0 = a1.a0.a1.a1.a0;
+  result.a1.a0.a2 = a1.a0.a2;
+  result.a1.a0.a3.a0.a0 = a1.a0.a3.a0.a0;
+  result.a1.a0.a3.a1 = a1.a0.a3.a1;
+  result.a1.a0.a4 = a1.a0.a4;
+  result.a1.a0.a5.a0.a0 = a1.a0.a5.a0.a0;
+  result.a1.a0.a5.a1.a0 = a1.a0.a5.a1.a0;
+  result.a1.a0.a6 = a1.a0.a6;
+  result.a1.a1.a0.a0 = a1.a1.a0.a0;
+  result.a1.a1.a0.a1 = a1.a1.a0.a1;
+  result.a1.a1.a1.a0 = a1.a1.a1.a0;
+  result.a1.a2 = a1.a2;
+  result.a1.a3 = a1.a3;
+  result.a2.a0.a0 = a2.a0.a0;
+  result.a2.a0.a1.a0.a0 = a2.a0.a1.a0.a0;
+  result.a2.a0.a1.a0.a1 = a2.a0.a1.a0.a1;
+  result.a2.a0.a1.a1.a0 = a2.a0.a1.a1.a0;
+  result.a2.a0.a2 = a2.a0.a2;
+  result.a2.a0.a3.a0.a0 = a2.a0.a3.a0.a0;
+  result.a2.a0.a3.a1 = a2.a0.a3.a1;
+  result.a2.a0.a4 = a2.a0.a4;
+  result.a2.a0.a5.a0.a0 = a2.a0.a5.a0.a0;
+  result.a2.a0.a5.a1.a0 = a2.a0.a5.a1.a0;
+  result.a2.a0.a6 = a2.a0.a6;
+  result.a2.a1.a0.a0 = a2.a1.a0.a0;
+  result.a2.a1.a0.a1 = a2.a1.a0.a1;
+  result.a2.a1.a1.a0 = a2.a1.a1.a0;
+  result.a2.a2 = a2.a2;
+  result.a2.a3 = a2.a3;
+  result.a3 = a3;
+
+  std::cout << "result = "
+            << "(" << result.a0 << ", ((" << result.a1.a0.a0 << ", (("
+            << result.a1.a0.a1.a0.a0 << ", " << result.a1.a0.a1.a0.a1 << "), ("
+            << result.a1.a0.a1.a1.a0 << ")), " << result.a1.a0.a2 << ", (("
+            << result.a1.a0.a3.a0.a0 << "), " << result.a1.a0.a3.a1 << "), "
+            << result.a1.a0.a4 << ", ((" << result.a1.a0.a5.a0.a0 << "), ("
+            << result.a1.a0.a5.a1.a0 << ")), " << result.a1.a0.a6 << "), (("
+            << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1 << "), ("
+            << result.a1.a1.a1.a0 << ")), " << result.a1.a2 << ", "
+            << result.a1.a3 << "), ((" << result.a2.a0.a0 << ", (("
+            << result.a2.a0.a1.a0.a0 << ", " << result.a2.a0.a1.a0.a1 << "), ("
+            << result.a2.a0.a1.a1.a0 << ")), " << result.a2.a0.a2 << ", (("
+            << result.a2.a0.a3.a0.a0 << "), " << result.a2.a0.a3.a1 << "), "
+            << result.a2.a0.a4 << ", ((" << result.a2.a0.a5.a0.a0 << "), ("
+            << result.a2.a0.a5.a1.a0 << ")), " << result.a2.a0.a6 << "), (("
+            << result.a2.a1.a0.a0 << ", " << result.a2.a1.a0.a1 << "), ("
+            << result.a2.a1.a1.a0 << ")), " << result.a2.a2 << ", "
+            << result.a2.a3 << "), " << result.a3 << ")"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
 // Smallest struct with data.
 // 10 struct arguments will exhaust available registers.
 DART_EXPORT intptr_t TestPassStruct1ByteIntx10(
@@ -6790,6 +7839,874 @@
 }
 
 // Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust registers on all platforms.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedIntx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(Struct8BytesNestedInt a0,
+                 Struct8BytesNestedInt a1,
+                 Struct8BytesNestedInt a2,
+                 Struct8BytesNestedInt a3,
+                 Struct8BytesNestedInt a4,
+                 Struct8BytesNestedInt a5,
+                 Struct8BytesNestedInt a6,
+                 Struct8BytesNestedInt a7,
+                 Struct8BytesNestedInt a8,
+                 Struct8BytesNestedInt a9)) {
+  Struct8BytesNestedInt a0;
+  Struct8BytesNestedInt a1;
+  Struct8BytesNestedInt a2;
+  Struct8BytesNestedInt a3;
+  Struct8BytesNestedInt a4;
+  Struct8BytesNestedInt a5;
+  Struct8BytesNestedInt a6;
+  Struct8BytesNestedInt a7;
+  Struct8BytesNestedInt a8;
+  Struct8BytesNestedInt a9;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3;
+  a0.a1.a1 = 4;
+  a1.a0.a0 = -5;
+  a1.a0.a1 = 6;
+  a1.a1.a0 = -7;
+  a1.a1.a1 = 8;
+  a2.a0.a0 = -9;
+  a2.a0.a1 = 10;
+  a2.a1.a0 = -11;
+  a2.a1.a1 = 12;
+  a3.a0.a0 = -13;
+  a3.a0.a1 = 14;
+  a3.a1.a0 = -15;
+  a3.a1.a1 = 16;
+  a4.a0.a0 = -17;
+  a4.a0.a1 = 18;
+  a4.a1.a0 = -19;
+  a4.a1.a1 = 20;
+  a5.a0.a0 = -21;
+  a5.a0.a1 = 22;
+  a5.a1.a0 = -23;
+  a5.a1.a1 = 24;
+  a6.a0.a0 = -25;
+  a6.a0.a1 = 26;
+  a6.a1.a0 = -27;
+  a6.a1.a1 = 28;
+  a7.a0.a0 = -29;
+  a7.a0.a1 = 30;
+  a7.a1.a0 = -31;
+  a7.a1.a1 = 32;
+  a8.a0.a0 = -33;
+  a8.a0.a1 = 34;
+  a8.a1.a0 = -35;
+  a8.a1.a1 = 36;
+  a9.a0.a0 = -37;
+  a9.a0.a1 = 38;
+  a9.a1.a0 = -39;
+  a9.a1.a1 = 40;
+
+  std::cout << "Calling TestPassStruct8BytesNestedIntx10("
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+            << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")), ((" << a2.a0.a0
+            << ", " << a2.a0.a1 << "), (" << a2.a1.a0 << ", " << a2.a1.a1
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+            << ", " << a3.a1.a1 << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1
+            << "), (" << a4.a1.a0 << ", " << a4.a1.a1 << ")), ((" << a5.a0.a0
+            << ", " << a5.a0.a1 << "), (" << a5.a1.a0 << ", " << a5.a1.a1
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+            << ", " << a6.a1.a1 << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1
+            << "), (" << a7.a1.a0 << ", " << a7.a1.a1 << ")), ((" << a8.a0.a0
+            << ", " << a8.a0.a1 << "), (" << a8.a1.a0 << ", " << a8.a1.a1
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+            << ", " << a9.a1.a1 << ")))"
+            << ")\n";
+
+  int64_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(20, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedFloatx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    float (*f)(Struct8BytesNestedFloat a0,
+               Struct8BytesNestedFloat a1,
+               Struct8BytesNestedFloat a2,
+               Struct8BytesNestedFloat a3,
+               Struct8BytesNestedFloat a4,
+               Struct8BytesNestedFloat a5,
+               Struct8BytesNestedFloat a6,
+               Struct8BytesNestedFloat a7,
+               Struct8BytesNestedFloat a8,
+               Struct8BytesNestedFloat a9)) {
+  Struct8BytesNestedFloat a0;
+  Struct8BytesNestedFloat a1;
+  Struct8BytesNestedFloat a2;
+  Struct8BytesNestedFloat a3;
+  Struct8BytesNestedFloat a4;
+  Struct8BytesNestedFloat a5;
+  Struct8BytesNestedFloat a6;
+  Struct8BytesNestedFloat a7;
+  Struct8BytesNestedFloat a8;
+  Struct8BytesNestedFloat a9;
+
+  a0.a0.a0 = -1.0;
+  a0.a1.a0 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a1.a0 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a1.a0 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a1.a0 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a1.a0 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a1.a0 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a1.a0 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a1.a0 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a1.a0 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a1.a0 = 20.0;
+
+  std::cout << "Calling TestPassStruct8BytesNestedFloatx10("
+            << "(((" << a0.a0.a0 << "), (" << a0.a1.a0 << ")), ((" << a1.a0.a0
+            << "), (" << a1.a1.a0 << ")), ((" << a2.a0.a0 << "), (" << a2.a1.a0
+            << ")), ((" << a3.a0.a0 << "), (" << a3.a1.a0 << ")), (("
+            << a4.a0.a0 << "), (" << a4.a1.a0 << ")), ((" << a5.a0.a0 << "), ("
+            << a5.a1.a0 << ")), ((" << a6.a0.a0 << "), (" << a6.a1.a0
+            << ")), ((" << a7.a0.a0 << "), (" << a7.a1.a0 << ")), (("
+            << a8.a0.a0 << "), (" << a8.a1.a0 << ")), ((" << a9.a0.a0 << "), ("
+            << a9.a1.a0 << ")))"
+            << ")\n";
+
+  float result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(10.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedFloat2x10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    float (*f)(Struct8BytesNestedFloat2 a0,
+               Struct8BytesNestedFloat2 a1,
+               Struct8BytesNestedFloat2 a2,
+               Struct8BytesNestedFloat2 a3,
+               Struct8BytesNestedFloat2 a4,
+               Struct8BytesNestedFloat2 a5,
+               Struct8BytesNestedFloat2 a6,
+               Struct8BytesNestedFloat2 a7,
+               Struct8BytesNestedFloat2 a8,
+               Struct8BytesNestedFloat2 a9)) {
+  Struct8BytesNestedFloat2 a0;
+  Struct8BytesNestedFloat2 a1;
+  Struct8BytesNestedFloat2 a2;
+  Struct8BytesNestedFloat2 a3;
+  Struct8BytesNestedFloat2 a4;
+  Struct8BytesNestedFloat2 a5;
+  Struct8BytesNestedFloat2 a6;
+  Struct8BytesNestedFloat2 a7;
+  Struct8BytesNestedFloat2 a8;
+  Struct8BytesNestedFloat2 a9;
+
+  a0.a0.a0 = -1.0;
+  a0.a1 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a1 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a1 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a1 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a1 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a1 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a1 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a1 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a1 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a1 = 20.0;
+
+  std::cout << "Calling TestPassStruct8BytesNestedFloat2x10("
+            << "(((" << a0.a0.a0 << "), " << a0.a1 << "), ((" << a1.a0.a0
+            << "), " << a1.a1 << "), ((" << a2.a0.a0 << "), " << a2.a1
+            << "), ((" << a3.a0.a0 << "), " << a3.a1 << "), ((" << a4.a0.a0
+            << "), " << a4.a1 << "), ((" << a5.a0.a0 << "), " << a5.a1
+            << "), ((" << a6.a0.a0 << "), " << a6.a1 << "), ((" << a7.a0.a0
+            << "), " << a7.a1 << "), ((" << a8.a0.a0 << "), " << a8.a1
+            << "), ((" << a9.a0.a0 << "), " << a9.a1 << "))"
+            << ")\n";
+
+  float result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(10.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust all registers on all platforms.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedMixedx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Struct8BytesNestedMixed a0,
+                Struct8BytesNestedMixed a1,
+                Struct8BytesNestedMixed a2,
+                Struct8BytesNestedMixed a3,
+                Struct8BytesNestedMixed a4,
+                Struct8BytesNestedMixed a5,
+                Struct8BytesNestedMixed a6,
+                Struct8BytesNestedMixed a7,
+                Struct8BytesNestedMixed a8,
+                Struct8BytesNestedMixed a9)) {
+  Struct8BytesNestedMixed a0;
+  Struct8BytesNestedMixed a1;
+  Struct8BytesNestedMixed a2;
+  Struct8BytesNestedMixed a3;
+  Struct8BytesNestedMixed a4;
+  Struct8BytesNestedMixed a5;
+  Struct8BytesNestedMixed a6;
+  Struct8BytesNestedMixed a7;
+  Struct8BytesNestedMixed a8;
+  Struct8BytesNestedMixed a9;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3.0;
+  a1.a0.a0 = 4;
+  a1.a0.a1 = -5;
+  a1.a1.a0 = 6.0;
+  a2.a0.a0 = -7;
+  a2.a0.a1 = 8;
+  a2.a1.a0 = -9.0;
+  a3.a0.a0 = 10;
+  a3.a0.a1 = -11;
+  a3.a1.a0 = 12.0;
+  a4.a0.a0 = -13;
+  a4.a0.a1 = 14;
+  a4.a1.a0 = -15.0;
+  a5.a0.a0 = 16;
+  a5.a0.a1 = -17;
+  a5.a1.a0 = 18.0;
+  a6.a0.a0 = -19;
+  a6.a0.a1 = 20;
+  a6.a1.a0 = -21.0;
+  a7.a0.a0 = 22;
+  a7.a0.a1 = -23;
+  a7.a1.a0 = 24.0;
+  a8.a0.a0 = -25;
+  a8.a0.a1 = 26;
+  a8.a1.a0 = -27.0;
+  a9.a0.a0 = 28;
+  a9.a0.a1 = -29;
+  a9.a1.a0 = 30.0;
+
+  std::cout << "Calling TestPassStruct8BytesNestedMixedx10("
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << "), (" << a1.a1.a0
+            << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << "), (" << a2.a1.a0
+            << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+            << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << "), (" << a4.a1.a0
+            << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << "), (" << a5.a1.a0
+            << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+            << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << "), (" << a7.a1.a0
+            << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << "), (" << a8.a1.a0
+            << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+            << ")))"
+            << ")\n";
+
+  double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(15.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestPassStruct16BytesNestedIntx2(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(Struct16BytesNestedInt a0, Struct16BytesNestedInt a1)) {
+  Struct16BytesNestedInt a0;
+  Struct16BytesNestedInt a1;
+
+  a0.a0.a0.a0 = -1;
+  a0.a0.a0.a1 = 2;
+  a0.a0.a1.a0 = -3;
+  a0.a0.a1.a1 = 4;
+  a0.a1.a0.a0 = -5;
+  a0.a1.a0.a1 = 6;
+  a0.a1.a1.a0 = -7;
+  a0.a1.a1.a1 = 8;
+  a1.a0.a0.a0 = -9;
+  a1.a0.a0.a1 = 10;
+  a1.a0.a1.a0 = -11;
+  a1.a0.a1.a1 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15;
+  a1.a1.a1.a1 = 16;
+
+  std::cout << "Calling TestPassStruct16BytesNestedIntx2("
+            << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+            << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+            << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+            << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+            << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+            << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+            << ", " << a1.a1.a1.a1 << "))))"
+            << ")\n";
+
+  int64_t result = f(a0, a1);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(8, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestPassStruct32BytesNestedIntx2(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(Struct32BytesNestedInt a0, Struct32BytesNestedInt a1)) {
+  Struct32BytesNestedInt a0;
+  Struct32BytesNestedInt a1;
+
+  a0.a0.a0.a0.a0 = -1;
+  a0.a0.a0.a0.a1 = 2;
+  a0.a0.a0.a1.a0 = -3;
+  a0.a0.a0.a1.a1 = 4;
+  a0.a0.a1.a0.a0 = -5;
+  a0.a0.a1.a0.a1 = 6;
+  a0.a0.a1.a1.a0 = -7;
+  a0.a0.a1.a1.a1 = 8;
+  a0.a1.a0.a0.a0 = -9;
+  a0.a1.a0.a0.a1 = 10;
+  a0.a1.a0.a1.a0 = -11;
+  a0.a1.a0.a1.a1 = 12;
+  a0.a1.a1.a0.a0 = -13;
+  a0.a1.a1.a0.a1 = 14;
+  a0.a1.a1.a1.a0 = -15;
+  a0.a1.a1.a1.a1 = 16;
+  a1.a0.a0.a0.a0 = -17;
+  a1.a0.a0.a0.a1 = 18;
+  a1.a0.a0.a1.a0 = -19;
+  a1.a0.a0.a1.a1 = 20;
+  a1.a0.a1.a0.a0 = -21;
+  a1.a0.a1.a0.a1 = 22;
+  a1.a0.a1.a1.a0 = -23;
+  a1.a0.a1.a1.a1 = 24;
+  a1.a1.a0.a0.a0 = -25;
+  a1.a1.a0.a0.a1 = 26;
+  a1.a1.a0.a1.a0 = -27;
+  a1.a1.a0.a1.a1 = 28;
+  a1.a1.a1.a0.a0 = -29;
+  a1.a1.a1.a0.a1 = 30;
+  a1.a1.a1.a1.a0 = -31;
+  a1.a1.a1.a1.a1 = 32;
+
+  std::cout << "Calling TestPassStruct32BytesNestedIntx2("
+            << "(((((" << a0.a0.a0.a0.a0 << ", " << a0.a0.a0.a0.a1 << "), ("
+            << a0.a0.a0.a1.a0 << ", " << a0.a0.a0.a1.a1 << ")), (("
+            << a0.a0.a1.a0.a0 << ", " << a0.a0.a1.a0.a1 << "), ("
+            << a0.a0.a1.a1.a0 << ", " << a0.a0.a1.a1.a1 << "))), ((("
+            << a0.a1.a0.a0.a0 << ", " << a0.a1.a0.a0.a1 << "), ("
+            << a0.a1.a0.a1.a0 << ", " << a0.a1.a0.a1.a1 << ")), (("
+            << a0.a1.a1.a0.a0 << ", " << a0.a1.a1.a0.a1 << "), ("
+            << a0.a1.a1.a1.a0 << ", " << a0.a1.a1.a1.a1 << ")))), (((("
+            << a1.a0.a0.a0.a0 << ", " << a1.a0.a0.a0.a1 << "), ("
+            << a1.a0.a0.a1.a0 << ", " << a1.a0.a0.a1.a1 << ")), (("
+            << a1.a0.a1.a0.a0 << ", " << a1.a0.a1.a0.a1 << "), ("
+            << a1.a0.a1.a1.a0 << ", " << a1.a0.a1.a1.a1 << "))), ((("
+            << a1.a1.a0.a0.a0 << ", " << a1.a1.a0.a0.a1 << "), ("
+            << a1.a1.a0.a1.a0 << ", " << a1.a1.a0.a1.a1 << ")), (("
+            << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1 << "), ("
+            << a1.a1.a1.a1.a0 << ", " << a1.a1.a1.a1.a1 << ")))))"
+            << ")\n";
+
+  int64_t result = f(a0, a1);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(16, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0.a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0.a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt16(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(StructNestedIntStructAlignmentInt16 a0)) {
+  StructNestedIntStructAlignmentInt16 a0;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  std::cout << "Calling TestPassStructNestedIntStructAlignmentInt16("
+            << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+            << static_cast<int>(a0.a0.a2) << "), ("
+            << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+            << static_cast<int>(a0.a1.a2) << ")))"
+            << ")\n";
+
+  int64_t result = f(a0);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(3, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt32(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(StructNestedIntStructAlignmentInt32 a0)) {
+  StructNestedIntStructAlignmentInt32 a0;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  std::cout << "Calling TestPassStructNestedIntStructAlignmentInt32("
+            << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+            << static_cast<int>(a0.a0.a2) << "), ("
+            << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+            << static_cast<int>(a0.a1.a2) << ")))"
+            << ")\n";
+
+  int64_t result = f(a0);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(3, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt64(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(StructNestedIntStructAlignmentInt64 a0)) {
+  StructNestedIntStructAlignmentInt64 a0;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  std::cout << "Calling TestPassStructNestedIntStructAlignmentInt64("
+            << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+            << static_cast<int>(a0.a0.a2) << "), ("
+            << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+            << static_cast<int>(a0.a1.a2) << ")))"
+            << ")\n";
+
+  int64_t result = f(a0);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(3, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT intptr_t TestPassStructNestedIrregularEvenBiggerx4(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(StructNestedIrregularEvenBigger a0,
+                StructNestedIrregularEvenBigger a1,
+                StructNestedIrregularEvenBigger a2,
+                StructNestedIrregularEvenBigger a3)) {
+  StructNestedIrregularEvenBigger a0;
+  StructNestedIrregularEvenBigger a1;
+  StructNestedIrregularEvenBigger a2;
+  StructNestedIrregularEvenBigger a3;
+
+  a0.a0 = 1;
+  a0.a1.a0.a0 = 2;
+  a0.a1.a0.a1.a0.a0 = -3;
+  a0.a1.a0.a1.a0.a1 = 4;
+  a0.a1.a0.a1.a1.a0 = -5.0;
+  a0.a1.a0.a2 = 6;
+  a0.a1.a0.a3.a0.a0 = -7.0;
+  a0.a1.a0.a3.a1 = 8.0;
+  a0.a1.a0.a4 = 9;
+  a0.a1.a0.a5.a0.a0 = 10.0;
+  a0.a1.a0.a5.a1.a0 = -11.0;
+  a0.a1.a0.a6 = 12;
+  a0.a1.a1.a0.a0 = -13;
+  a0.a1.a1.a0.a1 = 14;
+  a0.a1.a1.a1.a0 = -15.0;
+  a0.a1.a2 = 16.0;
+  a0.a1.a3 = -17.0;
+  a0.a2.a0.a0 = 18;
+  a0.a2.a0.a1.a0.a0 = -19;
+  a0.a2.a0.a1.a0.a1 = 20;
+  a0.a2.a0.a1.a1.a0 = -21.0;
+  a0.a2.a0.a2 = 22;
+  a0.a2.a0.a3.a0.a0 = -23.0;
+  a0.a2.a0.a3.a1 = 24.0;
+  a0.a2.a0.a4 = 25;
+  a0.a2.a0.a5.a0.a0 = 26.0;
+  a0.a2.a0.a5.a1.a0 = -27.0;
+  a0.a2.a0.a6 = 28;
+  a0.a2.a1.a0.a0 = -29;
+  a0.a2.a1.a0.a1 = 30;
+  a0.a2.a1.a1.a0 = -31.0;
+  a0.a2.a2 = 32.0;
+  a0.a2.a3 = -33.0;
+  a0.a3 = 34.0;
+  a1.a0 = 35;
+  a1.a1.a0.a0 = 36;
+  a1.a1.a0.a1.a0.a0 = -37;
+  a1.a1.a0.a1.a0.a1 = 38;
+  a1.a1.a0.a1.a1.a0 = -39.0;
+  a1.a1.a0.a2 = 40;
+  a1.a1.a0.a3.a0.a0 = -41.0;
+  a1.a1.a0.a3.a1 = 42.0;
+  a1.a1.a0.a4 = 43;
+  a1.a1.a0.a5.a0.a0 = 44.0;
+  a1.a1.a0.a5.a1.a0 = -45.0;
+  a1.a1.a0.a6 = 46;
+  a1.a1.a1.a0.a0 = -47;
+  a1.a1.a1.a0.a1 = 48;
+  a1.a1.a1.a1.a0 = -49.0;
+  a1.a1.a2 = 50.0;
+  a1.a1.a3 = -51.0;
+  a1.a2.a0.a0 = 52;
+  a1.a2.a0.a1.a0.a0 = -53;
+  a1.a2.a0.a1.a0.a1 = 54;
+  a1.a2.a0.a1.a1.a0 = -55.0;
+  a1.a2.a0.a2 = 56;
+  a1.a2.a0.a3.a0.a0 = -57.0;
+  a1.a2.a0.a3.a1 = 58.0;
+  a1.a2.a0.a4 = 59;
+  a1.a2.a0.a5.a0.a0 = 60.0;
+  a1.a2.a0.a5.a1.a0 = -61.0;
+  a1.a2.a0.a6 = 62;
+  a1.a2.a1.a0.a0 = -63;
+  a1.a2.a1.a0.a1 = 64;
+  a1.a2.a1.a1.a0 = -65.0;
+  a1.a2.a2 = 66.0;
+  a1.a2.a3 = -67.0;
+  a1.a3 = 68.0;
+  a2.a0 = 69;
+  a2.a1.a0.a0 = 70;
+  a2.a1.a0.a1.a0.a0 = -71;
+  a2.a1.a0.a1.a0.a1 = 72;
+  a2.a1.a0.a1.a1.a0 = -73.0;
+  a2.a1.a0.a2 = 74;
+  a2.a1.a0.a3.a0.a0 = -75.0;
+  a2.a1.a0.a3.a1 = 76.0;
+  a2.a1.a0.a4 = 77;
+  a2.a1.a0.a5.a0.a0 = 78.0;
+  a2.a1.a0.a5.a1.a0 = -79.0;
+  a2.a1.a0.a6 = 80;
+  a2.a1.a1.a0.a0 = -81;
+  a2.a1.a1.a0.a1 = 82;
+  a2.a1.a1.a1.a0 = -83.0;
+  a2.a1.a2 = 84.0;
+  a2.a1.a3 = -85.0;
+  a2.a2.a0.a0 = 86;
+  a2.a2.a0.a1.a0.a0 = -87;
+  a2.a2.a0.a1.a0.a1 = 88;
+  a2.a2.a0.a1.a1.a0 = -89.0;
+  a2.a2.a0.a2 = 90;
+  a2.a2.a0.a3.a0.a0 = -91.0;
+  a2.a2.a0.a3.a1 = 92.0;
+  a2.a2.a0.a4 = 93;
+  a2.a2.a0.a5.a0.a0 = 94.0;
+  a2.a2.a0.a5.a1.a0 = -95.0;
+  a2.a2.a0.a6 = 96;
+  a2.a2.a1.a0.a0 = -97;
+  a2.a2.a1.a0.a1 = 98;
+  a2.a2.a1.a1.a0 = -99.0;
+  a2.a2.a2 = 100.0;
+  a2.a2.a3 = -101.0;
+  a2.a3 = 102.0;
+  a3.a0 = 103;
+  a3.a1.a0.a0 = 104;
+  a3.a1.a0.a1.a0.a0 = -105;
+  a3.a1.a0.a1.a0.a1 = 106;
+  a3.a1.a0.a1.a1.a0 = -107.0;
+  a3.a1.a0.a2 = 108;
+  a3.a1.a0.a3.a0.a0 = -109.0;
+  a3.a1.a0.a3.a1 = 110.0;
+  a3.a1.a0.a4 = 111;
+  a3.a1.a0.a5.a0.a0 = 112.0;
+  a3.a1.a0.a5.a1.a0 = -113.0;
+  a3.a1.a0.a6 = 114;
+  a3.a1.a1.a0.a0 = -115;
+  a3.a1.a1.a0.a1 = 116;
+  a3.a1.a1.a1.a0 = -117.0;
+  a3.a1.a2 = 118.0;
+  a3.a1.a3 = -119.0;
+  a3.a2.a0.a0 = 120;
+  a3.a2.a0.a1.a0.a0 = -121;
+  a3.a2.a0.a1.a0.a1 = 122;
+  a3.a2.a0.a1.a1.a0 = -123.0;
+  a3.a2.a0.a2 = 124;
+  a3.a2.a0.a3.a0.a0 = -125.0;
+  a3.a2.a0.a3.a1 = 126.0;
+  a3.a2.a0.a4 = 127;
+  a3.a2.a0.a5.a0.a0 = 128.0;
+  a3.a2.a0.a5.a1.a0 = -129.0;
+  a3.a2.a0.a6 = 130;
+  a3.a2.a1.a0.a0 = -131;
+  a3.a2.a1.a0.a1 = 132;
+  a3.a2.a1.a1.a0 = -133.0;
+  a3.a2.a2 = 134.0;
+  a3.a2.a3 = -135.0;
+  a3.a3 = 136.0;
+
+  std::cout
+      << "Calling TestPassStructNestedIrregularEvenBiggerx4("
+      << "((" << a0.a0 << ", ((" << a0.a1.a0.a0 << ", ((" << a0.a1.a0.a1.a0.a0
+      << ", " << a0.a1.a0.a1.a0.a1 << "), (" << a0.a1.a0.a1.a1.a0 << ")), "
+      << a0.a1.a0.a2 << ", ((" << a0.a1.a0.a3.a0.a0 << "), " << a0.a1.a0.a3.a1
+      << "), " << a0.a1.a0.a4 << ", ((" << a0.a1.a0.a5.a0.a0 << "), ("
+      << a0.a1.a0.a5.a1.a0 << ")), " << a0.a1.a0.a6 << "), ((" << a0.a1.a1.a0.a0
+      << ", " << a0.a1.a1.a0.a1 << "), (" << a0.a1.a1.a1.a0 << ")), "
+      << a0.a1.a2 << ", " << a0.a1.a3 << "), ((" << a0.a2.a0.a0 << ", (("
+      << a0.a2.a0.a1.a0.a0 << ", " << a0.a2.a0.a1.a0.a1 << "), ("
+      << a0.a2.a0.a1.a1.a0 << ")), " << a0.a2.a0.a2 << ", (("
+      << a0.a2.a0.a3.a0.a0 << "), " << a0.a2.a0.a3.a1 << "), " << a0.a2.a0.a4
+      << ", ((" << a0.a2.a0.a5.a0.a0 << "), (" << a0.a2.a0.a5.a1.a0 << ")), "
+      << a0.a2.a0.a6 << "), ((" << a0.a2.a1.a0.a0 << ", " << a0.a2.a1.a0.a1
+      << "), (" << a0.a2.a1.a1.a0 << ")), " << a0.a2.a2 << ", " << a0.a2.a3
+      << "), " << a0.a3 << "), (" << a1.a0 << ", ((" << a1.a1.a0.a0 << ", (("
+      << a1.a1.a0.a1.a0.a0 << ", " << a1.a1.a0.a1.a0.a1 << "), ("
+      << a1.a1.a0.a1.a1.a0 << ")), " << a1.a1.a0.a2 << ", (("
+      << a1.a1.a0.a3.a0.a0 << "), " << a1.a1.a0.a3.a1 << "), " << a1.a1.a0.a4
+      << ", ((" << a1.a1.a0.a5.a0.a0 << "), (" << a1.a1.a0.a5.a1.a0 << ")), "
+      << a1.a1.a0.a6 << "), ((" << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1
+      << "), (" << a1.a1.a1.a1.a0 << ")), " << a1.a1.a2 << ", " << a1.a1.a3
+      << "), ((" << a1.a2.a0.a0 << ", ((" << a1.a2.a0.a1.a0.a0 << ", "
+      << a1.a2.a0.a1.a0.a1 << "), (" << a1.a2.a0.a1.a1.a0 << ")), "
+      << a1.a2.a0.a2 << ", ((" << a1.a2.a0.a3.a0.a0 << "), " << a1.a2.a0.a3.a1
+      << "), " << a1.a2.a0.a4 << ", ((" << a1.a2.a0.a5.a0.a0 << "), ("
+      << a1.a2.a0.a5.a1.a0 << ")), " << a1.a2.a0.a6 << "), ((" << a1.a2.a1.a0.a0
+      << ", " << a1.a2.a1.a0.a1 << "), (" << a1.a2.a1.a1.a0 << ")), "
+      << a1.a2.a2 << ", " << a1.a2.a3 << "), " << a1.a3 << "), (" << a2.a0
+      << ", ((" << a2.a1.a0.a0 << ", ((" << a2.a1.a0.a1.a0.a0 << ", "
+      << a2.a1.a0.a1.a0.a1 << "), (" << a2.a1.a0.a1.a1.a0 << ")), "
+      << a2.a1.a0.a2 << ", ((" << a2.a1.a0.a3.a0.a0 << "), " << a2.a1.a0.a3.a1
+      << "), " << a2.a1.a0.a4 << ", ((" << a2.a1.a0.a5.a0.a0 << "), ("
+      << a2.a1.a0.a5.a1.a0 << ")), " << a2.a1.a0.a6 << "), ((" << a2.a1.a1.a0.a0
+      << ", " << a2.a1.a1.a0.a1 << "), (" << a2.a1.a1.a1.a0 << ")), "
+      << a2.a1.a2 << ", " << a2.a1.a3 << "), ((" << a2.a2.a0.a0 << ", (("
+      << a2.a2.a0.a1.a0.a0 << ", " << a2.a2.a0.a1.a0.a1 << "), ("
+      << a2.a2.a0.a1.a1.a0 << ")), " << a2.a2.a0.a2 << ", (("
+      << a2.a2.a0.a3.a0.a0 << "), " << a2.a2.a0.a3.a1 << "), " << a2.a2.a0.a4
+      << ", ((" << a2.a2.a0.a5.a0.a0 << "), (" << a2.a2.a0.a5.a1.a0 << ")), "
+      << a2.a2.a0.a6 << "), ((" << a2.a2.a1.a0.a0 << ", " << a2.a2.a1.a0.a1
+      << "), (" << a2.a2.a1.a1.a0 << ")), " << a2.a2.a2 << ", " << a2.a2.a3
+      << "), " << a2.a3 << "), (" << a3.a0 << ", ((" << a3.a1.a0.a0 << ", (("
+      << a3.a1.a0.a1.a0.a0 << ", " << a3.a1.a0.a1.a0.a1 << "), ("
+      << a3.a1.a0.a1.a1.a0 << ")), " << a3.a1.a0.a2 << ", (("
+      << a3.a1.a0.a3.a0.a0 << "), " << a3.a1.a0.a3.a1 << "), " << a3.a1.a0.a4
+      << ", ((" << a3.a1.a0.a5.a0.a0 << "), (" << a3.a1.a0.a5.a1.a0 << ")), "
+      << a3.a1.a0.a6 << "), ((" << a3.a1.a1.a0.a0 << ", " << a3.a1.a1.a0.a1
+      << "), (" << a3.a1.a1.a1.a0 << ")), " << a3.a1.a2 << ", " << a3.a1.a3
+      << "), ((" << a3.a2.a0.a0 << ", ((" << a3.a2.a0.a1.a0.a0 << ", "
+      << a3.a2.a0.a1.a0.a1 << "), (" << a3.a2.a0.a1.a1.a0 << ")), "
+      << a3.a2.a0.a2 << ", ((" << a3.a2.a0.a3.a0.a0 << "), " << a3.a2.a0.a3.a1
+      << "), " << a3.a2.a0.a4 << ", ((" << a3.a2.a0.a5.a0.a0 << "), ("
+      << a3.a2.a0.a5.a1.a0 << ")), " << a3.a2.a0.a6 << "), ((" << a3.a2.a1.a0.a0
+      << ", " << a3.a2.a1.a0.a1 << "), (" << a3.a2.a1.a1.a0 << ")), "
+      << a3.a2.a2 << ", " << a3.a2.a3 << "), " << a3.a3 << "))"
+      << ")\n";
+
+  double result = f(a0, a1, a2, a3);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(1572.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
 // Smallest struct with data.
 DART_EXPORT intptr_t TestReturnStruct1ByteInt(
     // NOLINTNEXTLINE(whitespace/parens)
@@ -9387,4 +11304,780 @@
   return 0;
 }
 
+// Used for testing structs by value.
+// Simple nested struct.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedInt(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct8BytesNestedInt (*f)(Struct4BytesHomogeneousInt16 a0,
+                               Struct4BytesHomogeneousInt16 a1)) {
+  Struct4BytesHomogeneousInt16 a0;
+  Struct4BytesHomogeneousInt16 a1;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3;
+  a1.a1 = 4;
+
+  std::cout << "Calling TestReturnStruct8BytesNestedInt("
+            << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << ", "
+            << a1.a1 << "))"
+            << ")\n";
+
+  Struct8BytesNestedInt result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+            << result.a1.a0 << ", " << result.a1.a1 << "))"
+            << "\n";
+
+  CHECK_EQ(a0.a0, result.a0.a0);
+  CHECK_EQ(a0.a1, result.a0.a1);
+  CHECK_EQ(a1.a0, result.a1.a0);
+  CHECK_EQ(a1.a1, result.a1.a1);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with floats.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedFloat(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct8BytesNestedFloat (*f)(Struct4BytesFloat a0, Struct4BytesFloat a1)) {
+  Struct4BytesFloat a0;
+  Struct4BytesFloat a1;
+
+  a0.a0 = -1.0;
+  a1.a0 = 2.0;
+
+  std::cout << "Calling TestReturnStruct8BytesNestedFloat("
+            << "((" << a0.a0 << "), (" << a1.a0 << "))"
+            << ")\n";
+
+  Struct8BytesNestedFloat result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << "), (" << result.a1.a0 << "))"
+            << "\n";
+
+  CHECK_APPROX(a0.a0, result.a0.a0);
+  CHECK_APPROX(a1.a0, result.a1.a0);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_APPROX(0.0, result.a0.a0);
+  CHECK_APPROX(0.0, result.a1.a0);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_APPROX(0.0, result.a0.a0);
+  CHECK_APPROX(0.0, result.a1.a0);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedFloat2(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct8BytesNestedFloat2 (*f)(Struct4BytesFloat a0, float a1)) {
+  Struct4BytesFloat a0;
+  float a1;
+
+  a0.a0 = -1.0;
+  a1 = 2.0;
+
+  std::cout << "Calling TestReturnStruct8BytesNestedFloat2("
+            << "((" << a0.a0 << "), " << a1 << ")"
+            << ")\n";
+
+  Struct8BytesNestedFloat2 result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << "), " << result.a1 << ")"
+            << "\n";
+
+  CHECK_APPROX(a0.a0, result.a0.a0);
+  CHECK_APPROX(a1, result.a1);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_APPROX(0.0, result.a0.a0);
+  CHECK_APPROX(0.0, result.a1);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_APPROX(0.0, result.a0.a0);
+  CHECK_APPROX(0.0, result.a1);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with mixed members.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedMixed(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct8BytesNestedMixed (*f)(Struct4BytesHomogeneousInt16 a0,
+                                 Struct4BytesFloat a1)) {
+  Struct4BytesHomogeneousInt16 a0;
+  Struct4BytesFloat a1;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3.0;
+
+  std::cout << "Calling TestReturnStruct8BytesNestedMixed("
+            << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << "))"
+            << ")\n";
+
+  Struct8BytesNestedMixed result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+            << result.a1.a0 << "))"
+            << "\n";
+
+  CHECK_EQ(a0.a0, result.a0.a0);
+  CHECK_EQ(a0.a1, result.a0.a1);
+  CHECK_APPROX(a1.a0, result.a1.a0);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_APPROX(0.0, result.a1.a0);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_APPROX(0.0, result.a1.a0);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestReturnStruct16BytesNestedInt(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct16BytesNestedInt (*f)(Struct8BytesNestedInt a0,
+                                Struct8BytesNestedInt a1)) {
+  Struct8BytesNestedInt a0;
+  Struct8BytesNestedInt a1;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3;
+  a0.a1.a1 = 4;
+  a1.a0.a0 = -5;
+  a1.a0.a1 = 6;
+  a1.a1.a0 = -7;
+  a1.a1.a1 = 8;
+
+  std::cout << "Calling TestReturnStruct16BytesNestedInt("
+            << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+            << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+            << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")))"
+            << ")\n";
+
+  Struct16BytesNestedInt result = f(a0, a1);
+
+  std::cout << "result = "
+            << "(((" << result.a0.a0.a0 << ", " << result.a0.a0.a1 << "), ("
+            << result.a0.a1.a0 << ", " << result.a0.a1.a1 << ")), (("
+            << result.a1.a0.a0 << ", " << result.a1.a0.a1 << "), ("
+            << result.a1.a1.a0 << ", " << result.a1.a1.a1 << ")))"
+            << "\n";
+
+  CHECK_EQ(a0.a0.a0, result.a0.a0.a0);
+  CHECK_EQ(a0.a0.a1, result.a0.a0.a1);
+  CHECK_EQ(a0.a1.a0, result.a0.a1.a0);
+  CHECK_EQ(a0.a1.a1, result.a0.a1.a1);
+  CHECK_EQ(a1.a0.a0, result.a1.a0.a0);
+  CHECK_EQ(a1.a0.a1, result.a1.a0.a1);
+  CHECK_EQ(a1.a1.a0, result.a1.a1.a0);
+  CHECK_EQ(a1.a1.a1, result.a1.a1.a1);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0.a0);
+  CHECK_EQ(0, result.a0.a0.a1);
+  CHECK_EQ(0, result.a0.a1.a0);
+  CHECK_EQ(0, result.a0.a1.a1);
+  CHECK_EQ(0, result.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a1);
+  CHECK_EQ(0, result.a1.a1.a0);
+  CHECK_EQ(0, result.a1.a1.a1);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0.a0);
+  CHECK_EQ(0, result.a0.a0.a1);
+  CHECK_EQ(0, result.a0.a1.a0);
+  CHECK_EQ(0, result.a0.a1.a1);
+  CHECK_EQ(0, result.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a1);
+  CHECK_EQ(0, result.a1.a1.a0);
+  CHECK_EQ(0, result.a1.a1.a1);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestReturnStruct32BytesNestedInt(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct32BytesNestedInt (*f)(Struct16BytesNestedInt a0,
+                                Struct16BytesNestedInt a1)) {
+  Struct16BytesNestedInt a0;
+  Struct16BytesNestedInt a1;
+
+  a0.a0.a0.a0 = -1;
+  a0.a0.a0.a1 = 2;
+  a0.a0.a1.a0 = -3;
+  a0.a0.a1.a1 = 4;
+  a0.a1.a0.a0 = -5;
+  a0.a1.a0.a1 = 6;
+  a0.a1.a1.a0 = -7;
+  a0.a1.a1.a1 = 8;
+  a1.a0.a0.a0 = -9;
+  a1.a0.a0.a1 = 10;
+  a1.a0.a1.a0 = -11;
+  a1.a0.a1.a1 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15;
+  a1.a1.a1.a1 = 16;
+
+  std::cout << "Calling TestReturnStruct32BytesNestedInt("
+            << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+            << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+            << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+            << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+            << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+            << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+            << ", " << a1.a1.a1.a1 << "))))"
+            << ")\n";
+
+  Struct32BytesNestedInt result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((((" << result.a0.a0.a0.a0 << ", " << result.a0.a0.a0.a1
+            << "), (" << result.a0.a0.a1.a0 << ", " << result.a0.a0.a1.a1
+            << ")), ((" << result.a0.a1.a0.a0 << ", " << result.a0.a1.a0.a1
+            << "), (" << result.a0.a1.a1.a0 << ", " << result.a0.a1.a1.a1
+            << "))), (((" << result.a1.a0.a0.a0 << ", " << result.a1.a0.a0.a1
+            << "), (" << result.a1.a0.a1.a0 << ", " << result.a1.a0.a1.a1
+            << ")), ((" << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1
+            << "), (" << result.a1.a1.a1.a0 << ", " << result.a1.a1.a1.a1
+            << "))))"
+            << "\n";
+
+  CHECK_EQ(a0.a0.a0.a0, result.a0.a0.a0.a0);
+  CHECK_EQ(a0.a0.a0.a1, result.a0.a0.a0.a1);
+  CHECK_EQ(a0.a0.a1.a0, result.a0.a0.a1.a0);
+  CHECK_EQ(a0.a0.a1.a1, result.a0.a0.a1.a1);
+  CHECK_EQ(a0.a1.a0.a0, result.a0.a1.a0.a0);
+  CHECK_EQ(a0.a1.a0.a1, result.a0.a1.a0.a1);
+  CHECK_EQ(a0.a1.a1.a0, result.a0.a1.a1.a0);
+  CHECK_EQ(a0.a1.a1.a1, result.a0.a1.a1.a1);
+  CHECK_EQ(a1.a0.a0.a0, result.a1.a0.a0.a0);
+  CHECK_EQ(a1.a0.a0.a1, result.a1.a0.a0.a1);
+  CHECK_EQ(a1.a0.a1.a0, result.a1.a0.a1.a0);
+  CHECK_EQ(a1.a0.a1.a1, result.a1.a0.a1.a1);
+  CHECK_EQ(a1.a1.a0.a0, result.a1.a1.a0.a0);
+  CHECK_EQ(a1.a1.a0.a1, result.a1.a1.a0.a1);
+  CHECK_EQ(a1.a1.a1.a0, result.a1.a1.a1.a0);
+  CHECK_EQ(a1.a1.a1.a1, result.a1.a1.a1.a1);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0.a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0.a0.a0);
+  CHECK_EQ(0, result.a0.a0.a0.a1);
+  CHECK_EQ(0, result.a0.a0.a1.a0);
+  CHECK_EQ(0, result.a0.a0.a1.a1);
+  CHECK_EQ(0, result.a0.a1.a0.a0);
+  CHECK_EQ(0, result.a0.a1.a0.a1);
+  CHECK_EQ(0, result.a0.a1.a1.a0);
+  CHECK_EQ(0, result.a0.a1.a1.a1);
+  CHECK_EQ(0, result.a1.a0.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a0.a1);
+  CHECK_EQ(0, result.a1.a0.a1.a0);
+  CHECK_EQ(0, result.a1.a0.a1.a1);
+  CHECK_EQ(0, result.a1.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a1.a0.a1);
+  CHECK_EQ(0, result.a1.a1.a1.a0);
+  CHECK_EQ(0, result.a1.a1.a1.a1);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0.a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0.a0.a0);
+  CHECK_EQ(0, result.a0.a0.a0.a1);
+  CHECK_EQ(0, result.a0.a0.a1.a0);
+  CHECK_EQ(0, result.a0.a0.a1.a1);
+  CHECK_EQ(0, result.a0.a1.a0.a0);
+  CHECK_EQ(0, result.a0.a1.a0.a1);
+  CHECK_EQ(0, result.a0.a1.a1.a0);
+  CHECK_EQ(0, result.a0.a1.a1.a1);
+  CHECK_EQ(0, result.a1.a0.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a0.a1);
+  CHECK_EQ(0, result.a1.a0.a1.a0);
+  CHECK_EQ(0, result.a1.a0.a1.a1);
+  CHECK_EQ(0, result.a1.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a1.a0.a1);
+  CHECK_EQ(0, result.a1.a1.a1.a0);
+  CHECK_EQ(0, result.a1.a1.a1.a1);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt16(
+    // NOLINTNEXTLINE(whitespace/parens)
+    StructNestedIntStructAlignmentInt16 (*f)(StructAlignmentInt16 a0,
+                                             StructAlignmentInt16 a1)) {
+  StructAlignmentInt16 a0;
+  StructAlignmentInt16 a1;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  std::cout << "Calling TestReturnStructNestedIntStructAlignmentInt16("
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+            << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+            << ")\n";
+
+  StructNestedIntStructAlignmentInt16 result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+            << ", " << static_cast<int>(result.a0.a2) << "), ("
+            << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+            << static_cast<int>(result.a1.a2) << "))"
+            << "\n";
+
+  CHECK_EQ(a0.a0, result.a0.a0);
+  CHECK_EQ(a0.a1, result.a0.a1);
+  CHECK_EQ(a0.a2, result.a0.a2);
+  CHECK_EQ(a1.a0, result.a1.a0);
+  CHECK_EQ(a1.a1, result.a1.a1);
+  CHECK_EQ(a1.a2, result.a1.a2);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+  CHECK_EQ(0, result.a1.a2);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+  CHECK_EQ(0, result.a1.a2);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt32(
+    // NOLINTNEXTLINE(whitespace/parens)
+    StructNestedIntStructAlignmentInt32 (*f)(StructAlignmentInt32 a0,
+                                             StructAlignmentInt32 a1)) {
+  StructAlignmentInt32 a0;
+  StructAlignmentInt32 a1;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  std::cout << "Calling TestReturnStructNestedIntStructAlignmentInt32("
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+            << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+            << ")\n";
+
+  StructNestedIntStructAlignmentInt32 result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+            << ", " << static_cast<int>(result.a0.a2) << "), ("
+            << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+            << static_cast<int>(result.a1.a2) << "))"
+            << "\n";
+
+  CHECK_EQ(a0.a0, result.a0.a0);
+  CHECK_EQ(a0.a1, result.a0.a1);
+  CHECK_EQ(a0.a2, result.a0.a2);
+  CHECK_EQ(a1.a0, result.a1.a0);
+  CHECK_EQ(a1.a1, result.a1.a1);
+  CHECK_EQ(a1.a2, result.a1.a2);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+  CHECK_EQ(0, result.a1.a2);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+  CHECK_EQ(0, result.a1.a2);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt64(
+    // NOLINTNEXTLINE(whitespace/parens)
+    StructNestedIntStructAlignmentInt64 (*f)(StructAlignmentInt64 a0,
+                                             StructAlignmentInt64 a1)) {
+  StructAlignmentInt64 a0;
+  StructAlignmentInt64 a1;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  std::cout << "Calling TestReturnStructNestedIntStructAlignmentInt64("
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+            << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+            << ")\n";
+
+  StructNestedIntStructAlignmentInt64 result = f(a0, a1);
+
+  std::cout << "result = "
+            << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+            << ", " << static_cast<int>(result.a0.a2) << "), ("
+            << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+            << static_cast<int>(result.a1.a2) << "))"
+            << "\n";
+
+  CHECK_EQ(a0.a0, result.a0.a0);
+  CHECK_EQ(a0.a1, result.a0.a1);
+  CHECK_EQ(a0.a2, result.a0.a2);
+  CHECK_EQ(a1.a0, result.a1.a0);
+  CHECK_EQ(a1.a1, result.a1.a1);
+  CHECK_EQ(a1.a2, result.a1.a2);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+  CHECK_EQ(0, result.a1.a2);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0.a0);
+  CHECK_EQ(0, result.a0.a1);
+  CHECK_EQ(0, result.a0.a2);
+  CHECK_EQ(0, result.a1.a0);
+  CHECK_EQ(0, result.a1.a1);
+  CHECK_EQ(0, result.a1.a2);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT intptr_t TestReturnStructNestedIrregularEvenBigger(
+    // NOLINTNEXTLINE(whitespace/parens)
+    StructNestedIrregularEvenBigger (*f)(uint64_t a0,
+                                         StructNestedIrregularBigger a1,
+                                         StructNestedIrregularBigger a2,
+                                         double a3)) {
+  uint64_t a0;
+  StructNestedIrregularBigger a1;
+  StructNestedIrregularBigger a2;
+  double a3;
+
+  a0 = 1;
+  a1.a0.a0 = 2;
+  a1.a0.a1.a0.a0 = -3;
+  a1.a0.a1.a0.a1 = 4;
+  a1.a0.a1.a1.a0 = -5.0;
+  a1.a0.a2 = 6;
+  a1.a0.a3.a0.a0 = -7.0;
+  a1.a0.a3.a1 = 8.0;
+  a1.a0.a4 = 9;
+  a1.a0.a5.a0.a0 = 10.0;
+  a1.a0.a5.a1.a0 = -11.0;
+  a1.a0.a6 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15.0;
+  a1.a2 = 16.0;
+  a1.a3 = -17.0;
+  a2.a0.a0 = 18;
+  a2.a0.a1.a0.a0 = -19;
+  a2.a0.a1.a0.a1 = 20;
+  a2.a0.a1.a1.a0 = -21.0;
+  a2.a0.a2 = 22;
+  a2.a0.a3.a0.a0 = -23.0;
+  a2.a0.a3.a1 = 24.0;
+  a2.a0.a4 = 25;
+  a2.a0.a5.a0.a0 = 26.0;
+  a2.a0.a5.a1.a0 = -27.0;
+  a2.a0.a6 = 28;
+  a2.a1.a0.a0 = -29;
+  a2.a1.a0.a1 = 30;
+  a2.a1.a1.a0 = -31.0;
+  a2.a2 = 32.0;
+  a2.a3 = -33.0;
+  a3 = 34.0;
+
+  std::cout << "Calling TestReturnStructNestedIrregularEvenBigger("
+            << "(" << a0 << ", ((" << a1.a0.a0 << ", ((" << a1.a0.a1.a0.a0
+            << ", " << a1.a0.a1.a0.a1 << "), (" << a1.a0.a1.a1.a0 << ")), "
+            << a1.a0.a2 << ", ((" << a1.a0.a3.a0.a0 << "), " << a1.a0.a3.a1
+            << "), " << a1.a0.a4 << ", ((" << a1.a0.a5.a0.a0 << "), ("
+            << a1.a0.a5.a1.a0 << ")), " << a1.a0.a6 << "), ((" << a1.a1.a0.a0
+            << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0 << ")), " << a1.a2
+            << ", " << a1.a3 << "), ((" << a2.a0.a0 << ", ((" << a2.a0.a1.a0.a0
+            << ", " << a2.a0.a1.a0.a1 << "), (" << a2.a0.a1.a1.a0 << ")), "
+            << a2.a0.a2 << ", ((" << a2.a0.a3.a0.a0 << "), " << a2.a0.a3.a1
+            << "), " << a2.a0.a4 << ", ((" << a2.a0.a5.a0.a0 << "), ("
+            << a2.a0.a5.a1.a0 << ")), " << a2.a0.a6 << "), ((" << a2.a1.a0.a0
+            << ", " << a2.a1.a0.a1 << "), (" << a2.a1.a1.a0 << ")), " << a2.a2
+            << ", " << a2.a3 << "), " << a3 << ")"
+            << ")\n";
+
+  StructNestedIrregularEvenBigger result = f(a0, a1, a2, a3);
+
+  std::cout << "result = "
+            << "(" << result.a0 << ", ((" << result.a1.a0.a0 << ", (("
+            << result.a1.a0.a1.a0.a0 << ", " << result.a1.a0.a1.a0.a1 << "), ("
+            << result.a1.a0.a1.a1.a0 << ")), " << result.a1.a0.a2 << ", (("
+            << result.a1.a0.a3.a0.a0 << "), " << result.a1.a0.a3.a1 << "), "
+            << result.a1.a0.a4 << ", ((" << result.a1.a0.a5.a0.a0 << "), ("
+            << result.a1.a0.a5.a1.a0 << ")), " << result.a1.a0.a6 << "), (("
+            << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1 << "), ("
+            << result.a1.a1.a1.a0 << ")), " << result.a1.a2 << ", "
+            << result.a1.a3 << "), ((" << result.a2.a0.a0 << ", (("
+            << result.a2.a0.a1.a0.a0 << ", " << result.a2.a0.a1.a0.a1 << "), ("
+            << result.a2.a0.a1.a1.a0 << ")), " << result.a2.a0.a2 << ", (("
+            << result.a2.a0.a3.a0.a0 << "), " << result.a2.a0.a3.a1 << "), "
+            << result.a2.a0.a4 << ", ((" << result.a2.a0.a5.a0.a0 << "), ("
+            << result.a2.a0.a5.a1.a0 << ")), " << result.a2.a0.a6 << "), (("
+            << result.a2.a1.a0.a0 << ", " << result.a2.a1.a0.a1 << "), ("
+            << result.a2.a1.a1.a0 << ")), " << result.a2.a2 << ", "
+            << result.a2.a3 << "), " << result.a3 << ")"
+            << "\n";
+
+  CHECK_EQ(a0, result.a0);
+  CHECK_EQ(a1.a0.a0, result.a1.a0.a0);
+  CHECK_EQ(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0);
+  CHECK_EQ(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1);
+  CHECK_APPROX(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0);
+  CHECK_EQ(a1.a0.a2, result.a1.a0.a2);
+  CHECK_APPROX(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0);
+  CHECK_APPROX(a1.a0.a3.a1, result.a1.a0.a3.a1);
+  CHECK_EQ(a1.a0.a4, result.a1.a0.a4);
+  CHECK_APPROX(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0);
+  CHECK_APPROX(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0);
+  CHECK_EQ(a1.a0.a6, result.a1.a0.a6);
+  CHECK_EQ(a1.a1.a0.a0, result.a1.a1.a0.a0);
+  CHECK_EQ(a1.a1.a0.a1, result.a1.a1.a0.a1);
+  CHECK_APPROX(a1.a1.a1.a0, result.a1.a1.a1.a0);
+  CHECK_APPROX(a1.a2, result.a1.a2);
+  CHECK_APPROX(a1.a3, result.a1.a3);
+  CHECK_EQ(a2.a0.a0, result.a2.a0.a0);
+  CHECK_EQ(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0);
+  CHECK_EQ(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1);
+  CHECK_APPROX(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0);
+  CHECK_EQ(a2.a0.a2, result.a2.a0.a2);
+  CHECK_APPROX(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0);
+  CHECK_APPROX(a2.a0.a3.a1, result.a2.a0.a3.a1);
+  CHECK_EQ(a2.a0.a4, result.a2.a0.a4);
+  CHECK_APPROX(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0);
+  CHECK_APPROX(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0);
+  CHECK_EQ(a2.a0.a6, result.a2.a0.a6);
+  CHECK_EQ(a2.a1.a0.a0, result.a2.a1.a0.a0);
+  CHECK_EQ(a2.a1.a0.a1, result.a2.a1.a0.a1);
+  CHECK_APPROX(a2.a1.a1.a0, result.a2.a1.a1.a0);
+  CHECK_APPROX(a2.a2, result.a2.a2);
+  CHECK_APPROX(a2.a3, result.a2.a3);
+  CHECK_APPROX(a3, result.a3);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0, a1, a2, a3);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_EQ(0, result.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a1.a0.a1.a1.a0);
+  CHECK_EQ(0, result.a1.a0.a2);
+  CHECK_APPROX(0.0, result.a1.a0.a3.a0.a0);
+  CHECK_APPROX(0.0, result.a1.a0.a3.a1);
+  CHECK_EQ(0, result.a1.a0.a4);
+  CHECK_APPROX(0.0, result.a1.a0.a5.a0.a0);
+  CHECK_APPROX(0.0, result.a1.a0.a5.a1.a0);
+  CHECK_EQ(0, result.a1.a0.a6);
+  CHECK_EQ(0, result.a1.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a1.a1.a1.a0);
+  CHECK_APPROX(0.0, result.a1.a2);
+  CHECK_APPROX(0.0, result.a1.a3);
+  CHECK_EQ(0, result.a2.a0.a0);
+  CHECK_EQ(0, result.a2.a0.a1.a0.a0);
+  CHECK_EQ(0, result.a2.a0.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a2.a0.a1.a1.a0);
+  CHECK_EQ(0, result.a2.a0.a2);
+  CHECK_APPROX(0.0, result.a2.a0.a3.a0.a0);
+  CHECK_APPROX(0.0, result.a2.a0.a3.a1);
+  CHECK_EQ(0, result.a2.a0.a4);
+  CHECK_APPROX(0.0, result.a2.a0.a5.a0.a0);
+  CHECK_APPROX(0.0, result.a2.a0.a5.a1.a0);
+  CHECK_EQ(0, result.a2.a0.a6);
+  CHECK_EQ(0, result.a2.a1.a0.a0);
+  CHECK_EQ(0, result.a2.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a2.a1.a1.a0);
+  CHECK_APPROX(0.0, result.a2.a2);
+  CHECK_APPROX(0.0, result.a2.a3);
+  CHECK_APPROX(0.0, result.a3);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0, a1, a2, a3);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_EQ(0, result.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a0.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a1.a0.a1.a1.a0);
+  CHECK_EQ(0, result.a1.a0.a2);
+  CHECK_APPROX(0.0, result.a1.a0.a3.a0.a0);
+  CHECK_APPROX(0.0, result.a1.a0.a3.a1);
+  CHECK_EQ(0, result.a1.a0.a4);
+  CHECK_APPROX(0.0, result.a1.a0.a5.a0.a0);
+  CHECK_APPROX(0.0, result.a1.a0.a5.a1.a0);
+  CHECK_EQ(0, result.a1.a0.a6);
+  CHECK_EQ(0, result.a1.a1.a0.a0);
+  CHECK_EQ(0, result.a1.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a1.a1.a1.a0);
+  CHECK_APPROX(0.0, result.a1.a2);
+  CHECK_APPROX(0.0, result.a1.a3);
+  CHECK_EQ(0, result.a2.a0.a0);
+  CHECK_EQ(0, result.a2.a0.a1.a0.a0);
+  CHECK_EQ(0, result.a2.a0.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a2.a0.a1.a1.a0);
+  CHECK_EQ(0, result.a2.a0.a2);
+  CHECK_APPROX(0.0, result.a2.a0.a3.a0.a0);
+  CHECK_APPROX(0.0, result.a2.a0.a3.a1);
+  CHECK_EQ(0, result.a2.a0.a4);
+  CHECK_APPROX(0.0, result.a2.a0.a5.a0.a0);
+  CHECK_APPROX(0.0, result.a2.a0.a5.a1.a0);
+  CHECK_EQ(0, result.a2.a0.a6);
+  CHECK_EQ(0, result.a2.a1.a0.a0);
+  CHECK_EQ(0, result.a2.a1.a0.a1);
+  CHECK_APPROX(0.0, result.a2.a1.a1.a0);
+  CHECK_APPROX(0.0, result.a2.a2);
+  CHECK_APPROX(0.0, result.a2.a3);
+  CHECK_APPROX(0.0, result.a3);
+
+  return 0;
+}
+
 }  // namespace dart
diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc
index cd97151..cefe5a0 100644
--- a/runtime/lib/ffi.cc
+++ b/runtime/lib/ffi.cc
@@ -91,14 +91,14 @@
 static ObjectPtr LoadValueStruct(Zone* zone,
                                  const Pointer& target,
                                  const AbstractType& instance_type_arg) {
-  // Result is a struct class -- find <class name>.#fromPointer
+  // Result is a struct class -- find <class name>.#fromTypedDataBase
   // constructor and call it.
   const Class& cls = Class::Handle(zone, instance_type_arg.type_class());
   const Function& constructor =
       Function::Handle(cls.LookupFunctionAllowPrivate(String::Handle(
           String::Concat(String::Handle(String::Concat(
                              String::Handle(cls.Name()), Symbols::Dot())),
-                         Symbols::StructFromPointer()))));
+                         Symbols::StructFromTypedDataBase()))));
   ASSERT(!constructor.IsNull());
   ASSERT(constructor.IsGenerativeConstructor());
   ASSERT(!Object::Handle(constructor.VerifyCallEntryPoint()).IsError());
diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc
index e53a1e8..86b2eba 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention.cc
+++ b/runtime/vm/compiler/ffi/native_calling_convention.cc
@@ -225,10 +225,10 @@
       const NativeCompoundType& payload_type) {
     const auto& compound_type = payload_type.AsCompound();
     if (compound_type.ContainsHomogenuousFloats() && !SoftFpAbi() &&
-        compound_type.members().length() <= 4) {
-      const auto& elem_type = *(compound_type.members().At(0));
+        compound_type.NumPrimitiveMembersRecursive() <= 4) {
+      const auto& elem_type = compound_type.FirstPrimitiveMember();
       const intptr_t size = compound_type.SizeInBytes();
-      const intptr_t elem_size = compound_type.members().At(0)->SizeInBytes();
+      const intptr_t elem_size = elem_type.SizeInBytes();
       const auto reg_kind = FpuRegisterKindFromSize(elem_size);
       ASSERT(size % elem_size == 0);
       const intptr_t num_registers = size / elem_size;
@@ -291,9 +291,9 @@
     const auto& compound_type = payload_type.AsCompound();
     const intptr_t size = compound_type.SizeInBytes();
     if (compound_type.ContainsHomogenuousFloats() &&
-        compound_type.members().length() <= 4) {
-      const auto& elem_type = *(compound_type.members().At(0));
-      const intptr_t elem_size = compound_type.members().At(0)->SizeInBytes();
+        compound_type.NumPrimitiveMembersRecursive() <= 4) {
+      const auto& elem_type = compound_type.FirstPrimitiveMember();
+      const intptr_t elem_size = elem_type.SizeInBytes();
       const auto reg_kind = kQuadFpuReg;
       ASSERT(size % elem_size == 0);
       const intptr_t num_registers = size / elem_size;
@@ -623,13 +623,13 @@
 static const NativeLocation& CompoundResultLocation(
     Zone* zone,
     const NativeCompoundType& payload_type) {
-  const intptr_t num_members = payload_type.members().length();
+  const intptr_t num_members = payload_type.NumPrimitiveMembersRecursive();
   if (payload_type.ContainsHomogenuousFloats() && !SoftFpAbi() &&
       num_members <= 4) {
     NativeLocations& multiple_locations =
         *new (zone) NativeLocations(zone, num_members);
     for (int i = 0; i < num_members; i++) {
-      const auto& member = payload_type.members().At(0)->AsPrimitive();
+      const auto& member = payload_type.FirstPrimitiveMember();
       multiple_locations.Add(new (zone) NativeFpuRegistersLocation(
           member, member, FpuRegisterKindFromSize(member.SizeInBytes()), i));
     }
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 1ffb1a2e..837c17f 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -156,7 +156,7 @@
 static bool ContainsHomogenuousFloatsInternal(const NativeTypes& types);
 
 // Keep consistent with
-// pkg/vm/lib/transformations/ffi_definitions.dart:_calculateSizeAndOffsets.
+// pkg/vm/lib/transformations/ffi_definitions.dart:_calculateStructLayout.
 NativeCompoundType& NativeCompoundType::FromNativeTypes(
     Zone* zone,
     const NativeTypes& members) {
@@ -514,6 +514,32 @@
   return_type_.PrintTo(f);
 }
 
+intptr_t NativePrimitiveType::NumPrimitiveMembersRecursive() const {
+  return 1;
+}
+
+intptr_t NativeCompoundType::NumPrimitiveMembersRecursive() const {
+  intptr_t count = 0;
+  for (intptr_t i = 0; i < members_.length(); i++) {
+    count += members_[i]->NumPrimitiveMembersRecursive();
+  }
+  return count;
+}
+
+const NativePrimitiveType& NativePrimitiveType::FirstPrimitiveMember() const {
+  return *this;
+}
+
+const NativePrimitiveType& NativeCompoundType::FirstPrimitiveMember() const {
+  ASSERT(NumPrimitiveMembersRecursive() >= 1);
+  for (intptr_t i = 0; i < members().length(); i++) {
+    if (members_[i]->NumPrimitiveMembersRecursive() >= 1) {
+      return members_[i]->FirstPrimitiveMember();
+    }
+  }
+  UNREACHABLE();
+}
+
 bool NativeCompoundType::ContainsOnlyFloats(intptr_t offset_in_bytes,
                                             intptr_t size_in_bytes) const {
   ASSERT(size_in_bytes >= 0);
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 140c54f..73d6915 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -117,6 +117,11 @@
   const char* ToCString() const;
 #endif
 
+  virtual intptr_t NumPrimitiveMembersRecursive() const { UNREACHABLE(); }
+  virtual const NativePrimitiveType& FirstPrimitiveMember() const {
+    UNREACHABLE();
+  }
+
   virtual ~NativeType() {}
 
  protected:
@@ -177,6 +182,9 @@
                        bool multi_line = false,
                        bool verbose = true) const;
 
+  virtual intptr_t NumPrimitiveMembersRecursive() const;
+  virtual const NativePrimitiveType& FirstPrimitiveMember() const;
+
   virtual ~NativePrimitiveType() {}
 
  private:
@@ -188,7 +196,6 @@
 // Struct
 //
 // TODO(dartbug.com/38491): Support unions.
-// TODO(dartbug.com/37271): Support nested compound types.
 // TODO(dartbug.com/35763): Support inline fixed-length arrays.
 class NativeCompoundType : public NativeType {
  public:
@@ -234,6 +241,9 @@
   // and arm64.
   bool ContainsHomogenuousFloats() const;
 
+  virtual intptr_t NumPrimitiveMembersRecursive() const;
+  virtual const NativePrimitiveType& FirstPrimitiveMember() const;
+
  private:
   NativeCompoundType(const NativeTypes& members,
                      const ZoneGrowableArray<intptr_t>& member_offsets,
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index f6fe2fa..32ca631 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1646,6 +1646,22 @@
       fields_.Add(&deleted_enum_sentinel);
     }
 
+    // TODO(https://dartbug.com/44454): Make VM recognize the Struct class.
+    //
+    // The FfiTrampolines currently allocate subtypes of structs and store
+    // TypedData in them, without using guards because they are force
+    // optimized. We immediately set the guarded_cid_ to kDynamicCid, which
+    // is effectively the same as calling this method first with Pointer and
+    // subsequently with TypedData with field guards.
+    if (klass.Name() == Symbols::Struct().raw() &&
+        Library::Handle(Z, klass.library()).url() == Symbols::DartFfi().raw()) {
+      ASSERT(fields_.length() == 1);
+      ASSERT(String::Handle(Z, fields_[0]->name())
+                 .StartsWith(Symbols::_addressOf()));
+      fields_[0]->set_guarded_cid(kDynamicCid);
+      fields_[0]->set_is_nullable(true);
+    }
+
     // Due to ReadVMAnnotations(), the klass may have been loaded at this point
     // (loading the class while evaluating annotations).
     if (klass.is_loaded()) {
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 5642b0a..2d21c6f 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -262,7 +262,7 @@
   V(StreamIteratorConstructor, "StreamIterator.")                              \
   V(StringBase, "_StringBase")                                                 \
   V(Struct, "Struct")                                                          \
-  V(StructFromPointer, "#fromPointer")                                         \
+  V(StructFromTypedDataBase, "#fromTypedDataBase")                             \
   V(SubtypeTestCache, "SubtypeTestCache")                                      \
   V(LoadingUnit, "LoadingUnit")                                                \
   V(SwitchExpr, ":switch_expr")                                                \
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index 79fc3a3..8bc4c14 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -119,6 +119,48 @@
 int _abi()
     native "Recognized method: IR graph is built in the flow graph builder.";
 
+/// Copies data byte-wise from [source] to [target].
+///
+/// [source] and [target] should either be [Pointer] or [TypedData].
+///
+/// TODO(dartbug.com/37271): Make recognized method and use MemoryCopyInstr.
+void _memCopy(Object target, int targetOffsetInBytes, Object source,
+    int sourceOffsetInBytes, int lengthInBytes) {
+  assert(source is Pointer || source is TypedData);
+  assert(target is Pointer || target is TypedData);
+  if (source is Pointer) {
+    final sourcePointer = source.cast<Uint8>();
+    if (target is Pointer) {
+      final targetPointer = target.cast<Uint8>();
+      for (int i = 0; i < lengthInBytes; i++) {
+        targetPointer[i + targetOffsetInBytes] =
+            sourcePointer[i + sourceOffsetInBytes];
+      }
+    } else if (target is TypedData) {
+      final targetTypedData = target.buffer.asUint8List(target.offsetInBytes);
+      for (int i = 0; i < lengthInBytes; i++) {
+        targetTypedData[i + targetOffsetInBytes] =
+            sourcePointer[i + sourceOffsetInBytes];
+      }
+    }
+  } else if (source is TypedData) {
+    final sourceTypedData = source.buffer.asUint8List(source.offsetInBytes);
+    if (target is Pointer) {
+      final targetPointer = target.cast<Uint8>();
+      for (int i = 0; i < lengthInBytes; i++) {
+        targetPointer[i + targetOffsetInBytes] =
+            sourceTypedData[i + sourceOffsetInBytes];
+      }
+    } else if (target is TypedData) {
+      final targetTypedData = target.buffer.asUint8List(target.offsetInBytes);
+      targetTypedData.setRange(
+          targetOffsetInBytes,
+          targetOffsetInBytes + lengthInBytes,
+          sourceTypedData.sublist(sourceOffsetInBytes));
+    }
+  }
+}
+
 // The following functions are implemented in the method recognizer.
 //
 // TODO(38172): Since these are not inlined (force optimize), they force
diff --git a/sdk/lib/ffi/struct.dart b/sdk/lib/ffi/struct.dart
index da034ef..0a65119 100644
--- a/sdk/lib/ffi/struct.dart
+++ b/sdk/lib/ffi/struct.dart
@@ -47,6 +47,7 @@
 /// by native memory. The may allocated via allocation or loaded from a
 /// [Pointer], but cannot be created by a generative constructor.
 abstract class Struct extends NativeType {
+  @pragma("vm:entry-point")
   final Object _addressOf;
 
   /// Construct a reference to the [nullptr].
diff --git a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
index 845ebb9..75ef76f 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -193,6 +193,56 @@
           passStructAlignmentInt64, 0),
       passStructAlignmentInt64AfterCallback),
   CallbackTest.withCheck(
+      "PassStruct8BytesNestedIntx10",
+      Pointer.fromFunction<PassStruct8BytesNestedIntx10Type>(
+          passStruct8BytesNestedIntx10, 0),
+      passStruct8BytesNestedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesNestedFloatx10",
+      Pointer.fromFunction<PassStruct8BytesNestedFloatx10Type>(
+          passStruct8BytesNestedFloatx10, 0.0),
+      passStruct8BytesNestedFloatx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesNestedFloat2x10",
+      Pointer.fromFunction<PassStruct8BytesNestedFloat2x10Type>(
+          passStruct8BytesNestedFloat2x10, 0.0),
+      passStruct8BytesNestedFloat2x10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesNestedMixedx10",
+      Pointer.fromFunction<PassStruct8BytesNestedMixedx10Type>(
+          passStruct8BytesNestedMixedx10, 0.0),
+      passStruct8BytesNestedMixedx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct16BytesNestedIntx2",
+      Pointer.fromFunction<PassStruct16BytesNestedIntx2Type>(
+          passStruct16BytesNestedIntx2, 0),
+      passStruct16BytesNestedIntx2AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct32BytesNestedIntx2",
+      Pointer.fromFunction<PassStruct32BytesNestedIntx2Type>(
+          passStruct32BytesNestedIntx2, 0),
+      passStruct32BytesNestedIntx2AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIntStructAlignmentInt16",
+      Pointer.fromFunction<PassStructNestedIntStructAlignmentInt16Type>(
+          passStructNestedIntStructAlignmentInt16, 0),
+      passStructNestedIntStructAlignmentInt16AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIntStructAlignmentInt32",
+      Pointer.fromFunction<PassStructNestedIntStructAlignmentInt32Type>(
+          passStructNestedIntStructAlignmentInt32, 0),
+      passStructNestedIntStructAlignmentInt32AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIntStructAlignmentInt64",
+      Pointer.fromFunction<PassStructNestedIntStructAlignmentInt64Type>(
+          passStructNestedIntStructAlignmentInt64, 0),
+      passStructNestedIntStructAlignmentInt64AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIrregularEvenBiggerx4",
+      Pointer.fromFunction<PassStructNestedIrregularEvenBiggerx4Type>(
+          passStructNestedIrregularEvenBiggerx4, 0.0),
+      passStructNestedIrregularEvenBiggerx4AfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -342,6 +392,56 @@
       Pointer.fromFunction<ReturnStructAlignmentInt64Type>(
           returnStructAlignmentInt64),
       returnStructAlignmentInt64AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedInt",
+      Pointer.fromFunction<ReturnStruct8BytesNestedIntType>(
+          returnStruct8BytesNestedInt),
+      returnStruct8BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedFloat",
+      Pointer.fromFunction<ReturnStruct8BytesNestedFloatType>(
+          returnStruct8BytesNestedFloat),
+      returnStruct8BytesNestedFloatAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedFloat2",
+      Pointer.fromFunction<ReturnStruct8BytesNestedFloat2Type>(
+          returnStruct8BytesNestedFloat2),
+      returnStruct8BytesNestedFloat2AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedMixed",
+      Pointer.fromFunction<ReturnStruct8BytesNestedMixedType>(
+          returnStruct8BytesNestedMixed),
+      returnStruct8BytesNestedMixedAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct16BytesNestedInt",
+      Pointer.fromFunction<ReturnStruct16BytesNestedIntType>(
+          returnStruct16BytesNestedInt),
+      returnStruct16BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct32BytesNestedInt",
+      Pointer.fromFunction<ReturnStruct32BytesNestedIntType>(
+          returnStruct32BytesNestedInt),
+      returnStruct32BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIntStructAlignmentInt16",
+      Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt16Type>(
+          returnStructNestedIntStructAlignmentInt16),
+      returnStructNestedIntStructAlignmentInt16AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIntStructAlignmentInt32",
+      Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt32Type>(
+          returnStructNestedIntStructAlignmentInt32),
+      returnStructNestedIntStructAlignmentInt32AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIntStructAlignmentInt64",
+      Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt64Type>(
+          returnStructNestedIntStructAlignmentInt64),
+      returnStructNestedIntStructAlignmentInt64AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIrregularEvenBigger",
+      Pointer.fromFunction<ReturnStructNestedIrregularEvenBiggerType>(
+          returnStructNestedIrregularEvenBigger),
+      returnStructNestedIrregularEvenBiggerAfterCallback),
 ];
 typedef PassStruct1ByteIntx10Type = Int64 Function(
     Struct1ByteInt,
@@ -4128,6 +4228,1013 @@
   Expect.equals(-2, result);
 }
 
+typedef PassStruct8BytesNestedIntx10Type = Int64 Function(
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a1 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a2 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a3 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a4 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a5 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a6 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a7 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a8 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a9 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesNestedIntx10Result = 0;
+
+int passStruct8BytesNestedIntx10CalculateResult() {
+  int result = 0;
+
+  result += passStruct8BytesNestedIntx10_a0.a0.a0;
+  result += passStruct8BytesNestedIntx10_a0.a0.a1;
+  result += passStruct8BytesNestedIntx10_a0.a1.a0;
+  result += passStruct8BytesNestedIntx10_a0.a1.a1;
+  result += passStruct8BytesNestedIntx10_a1.a0.a0;
+  result += passStruct8BytesNestedIntx10_a1.a0.a1;
+  result += passStruct8BytesNestedIntx10_a1.a1.a0;
+  result += passStruct8BytesNestedIntx10_a1.a1.a1;
+  result += passStruct8BytesNestedIntx10_a2.a0.a0;
+  result += passStruct8BytesNestedIntx10_a2.a0.a1;
+  result += passStruct8BytesNestedIntx10_a2.a1.a0;
+  result += passStruct8BytesNestedIntx10_a2.a1.a1;
+  result += passStruct8BytesNestedIntx10_a3.a0.a0;
+  result += passStruct8BytesNestedIntx10_a3.a0.a1;
+  result += passStruct8BytesNestedIntx10_a3.a1.a0;
+  result += passStruct8BytesNestedIntx10_a3.a1.a1;
+  result += passStruct8BytesNestedIntx10_a4.a0.a0;
+  result += passStruct8BytesNestedIntx10_a4.a0.a1;
+  result += passStruct8BytesNestedIntx10_a4.a1.a0;
+  result += passStruct8BytesNestedIntx10_a4.a1.a1;
+  result += passStruct8BytesNestedIntx10_a5.a0.a0;
+  result += passStruct8BytesNestedIntx10_a5.a0.a1;
+  result += passStruct8BytesNestedIntx10_a5.a1.a0;
+  result += passStruct8BytesNestedIntx10_a5.a1.a1;
+  result += passStruct8BytesNestedIntx10_a6.a0.a0;
+  result += passStruct8BytesNestedIntx10_a6.a0.a1;
+  result += passStruct8BytesNestedIntx10_a6.a1.a0;
+  result += passStruct8BytesNestedIntx10_a6.a1.a1;
+  result += passStruct8BytesNestedIntx10_a7.a0.a0;
+  result += passStruct8BytesNestedIntx10_a7.a0.a1;
+  result += passStruct8BytesNestedIntx10_a7.a1.a0;
+  result += passStruct8BytesNestedIntx10_a7.a1.a1;
+  result += passStruct8BytesNestedIntx10_a8.a0.a0;
+  result += passStruct8BytesNestedIntx10_a8.a0.a1;
+  result += passStruct8BytesNestedIntx10_a8.a1.a0;
+  result += passStruct8BytesNestedIntx10_a8.a1.a1;
+  result += passStruct8BytesNestedIntx10_a9.a0.a0;
+  result += passStruct8BytesNestedIntx10_a9.a0.a1;
+  result += passStruct8BytesNestedIntx10_a9.a1.a0;
+  result += passStruct8BytesNestedIntx10_a9.a1.a1;
+
+  passStruct8BytesNestedIntx10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+int passStruct8BytesNestedIntx10(
+    Struct8BytesNestedInt a0,
+    Struct8BytesNestedInt a1,
+    Struct8BytesNestedInt a2,
+    Struct8BytesNestedInt a3,
+    Struct8BytesNestedInt a4,
+    Struct8BytesNestedInt a5,
+    Struct8BytesNestedInt a6,
+    Struct8BytesNestedInt a7,
+    Struct8BytesNestedInt a8,
+    Struct8BytesNestedInt a9) {
+  print(
+      "passStruct8BytesNestedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedIntx10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedIntx10_a0 = a0;
+  passStruct8BytesNestedIntx10_a1 = a1;
+  passStruct8BytesNestedIntx10_a2 = a2;
+  passStruct8BytesNestedIntx10_a3 = a3;
+  passStruct8BytesNestedIntx10_a4 = a4;
+  passStruct8BytesNestedIntx10_a5 = a5;
+  passStruct8BytesNestedIntx10_a6 = a6;
+  passStruct8BytesNestedIntx10_a7 = a7;
+  passStruct8BytesNestedIntx10_a8 = a8;
+  passStruct8BytesNestedIntx10_a9 = a9;
+
+  final result = passStruct8BytesNestedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedIntx10AfterCallback() {
+  final result = passStruct8BytesNestedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(20, result);
+}
+
+typedef PassStruct8BytesNestedFloatx10Type = Float Function(
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a0 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a1 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a2 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a3 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a4 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a5 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a6 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a7 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a8 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a9 =
+    Struct8BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloatx10Result = 0.0;
+
+double passStruct8BytesNestedFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passStruct8BytesNestedFloatx10_a0.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a0.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a1.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a1.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a2.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a2.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a3.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a3.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a4.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a4.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a5.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a5.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a6.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a6.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a7.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a7.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a8.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a8.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a9.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a9.a1.a0;
+
+  passStruct8BytesNestedFloatx10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+double passStruct8BytesNestedFloatx10(
+    Struct8BytesNestedFloat a0,
+    Struct8BytesNestedFloat a1,
+    Struct8BytesNestedFloat a2,
+    Struct8BytesNestedFloat a3,
+    Struct8BytesNestedFloat a4,
+    Struct8BytesNestedFloat a5,
+    Struct8BytesNestedFloat a6,
+    Struct8BytesNestedFloat a7,
+    Struct8BytesNestedFloat a8,
+    Struct8BytesNestedFloat a9) {
+  print(
+      "passStruct8BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedFloatx10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedFloatx10_a0 = a0;
+  passStruct8BytesNestedFloatx10_a1 = a1;
+  passStruct8BytesNestedFloatx10_a2 = a2;
+  passStruct8BytesNestedFloatx10_a3 = a3;
+  passStruct8BytesNestedFloatx10_a4 = a4;
+  passStruct8BytesNestedFloatx10_a5 = a5;
+  passStruct8BytesNestedFloatx10_a6 = a6;
+  passStruct8BytesNestedFloatx10_a7 = a7;
+  passStruct8BytesNestedFloatx10_a8 = a8;
+  passStruct8BytesNestedFloatx10_a9 = a9;
+
+  final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedFloatx10AfterCallback() {
+  final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedFloat2x10Type = Float Function(
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a0 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a1 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a2 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a3 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a4 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a5 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a6 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a7 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a8 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a9 =
+    Struct8BytesNestedFloat2();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloat2x10Result = 0.0;
+
+double passStruct8BytesNestedFloat2x10CalculateResult() {
+  double result = 0;
+
+  result += passStruct8BytesNestedFloat2x10_a0.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a0.a1;
+  result += passStruct8BytesNestedFloat2x10_a1.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a1.a1;
+  result += passStruct8BytesNestedFloat2x10_a2.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a2.a1;
+  result += passStruct8BytesNestedFloat2x10_a3.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a3.a1;
+  result += passStruct8BytesNestedFloat2x10_a4.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a4.a1;
+  result += passStruct8BytesNestedFloat2x10_a5.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a5.a1;
+  result += passStruct8BytesNestedFloat2x10_a6.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a6.a1;
+  result += passStruct8BytesNestedFloat2x10_a7.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a7.a1;
+  result += passStruct8BytesNestedFloat2x10_a8.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a8.a1;
+  result += passStruct8BytesNestedFloat2x10_a9.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a9.a1;
+
+  passStruct8BytesNestedFloat2x10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+double passStruct8BytesNestedFloat2x10(
+    Struct8BytesNestedFloat2 a0,
+    Struct8BytesNestedFloat2 a1,
+    Struct8BytesNestedFloat2 a2,
+    Struct8BytesNestedFloat2 a3,
+    Struct8BytesNestedFloat2 a4,
+    Struct8BytesNestedFloat2 a5,
+    Struct8BytesNestedFloat2 a6,
+    Struct8BytesNestedFloat2 a7,
+    Struct8BytesNestedFloat2 a8,
+    Struct8BytesNestedFloat2 a9) {
+  print(
+      "passStruct8BytesNestedFloat2x10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedFloat2x10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedFloat2x10_a0 = a0;
+  passStruct8BytesNestedFloat2x10_a1 = a1;
+  passStruct8BytesNestedFloat2x10_a2 = a2;
+  passStruct8BytesNestedFloat2x10_a3 = a3;
+  passStruct8BytesNestedFloat2x10_a4 = a4;
+  passStruct8BytesNestedFloat2x10_a5 = a5;
+  passStruct8BytesNestedFloat2x10_a6 = a6;
+  passStruct8BytesNestedFloat2x10_a7 = a7;
+  passStruct8BytesNestedFloat2x10_a8 = a8;
+  passStruct8BytesNestedFloat2x10_a9 = a9;
+
+  final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedFloat2x10AfterCallback() {
+  final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedMixedx10Type = Double Function(
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a0 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a1 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a2 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a3 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a4 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a5 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a6 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a7 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a8 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a9 =
+    Struct8BytesNestedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedMixedx10Result = 0.0;
+
+double passStruct8BytesNestedMixedx10CalculateResult() {
+  double result = 0;
+
+  result += passStruct8BytesNestedMixedx10_a0.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a0.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a0.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a1.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a1.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a1.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a2.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a2.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a2.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a3.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a3.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a3.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a4.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a4.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a4.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a5.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a5.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a5.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a6.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a6.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a6.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a7.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a7.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a7.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a8.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a8.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a8.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a9.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a9.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a9.a1.a0;
+
+  passStruct8BytesNestedMixedx10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+double passStruct8BytesNestedMixedx10(
+    Struct8BytesNestedMixed a0,
+    Struct8BytesNestedMixed a1,
+    Struct8BytesNestedMixed a2,
+    Struct8BytesNestedMixed a3,
+    Struct8BytesNestedMixed a4,
+    Struct8BytesNestedMixed a5,
+    Struct8BytesNestedMixed a6,
+    Struct8BytesNestedMixed a7,
+    Struct8BytesNestedMixed a8,
+    Struct8BytesNestedMixed a9) {
+  print(
+      "passStruct8BytesNestedMixedx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedMixedx10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedMixedx10_a0 = a0;
+  passStruct8BytesNestedMixedx10_a1 = a1;
+  passStruct8BytesNestedMixedx10_a2 = a2;
+  passStruct8BytesNestedMixedx10_a3 = a3;
+  passStruct8BytesNestedMixedx10_a4 = a4;
+  passStruct8BytesNestedMixedx10_a5 = a5;
+  passStruct8BytesNestedMixedx10_a6 = a6;
+  passStruct8BytesNestedMixedx10_a7 = a7;
+  passStruct8BytesNestedMixedx10_a8 = a8;
+  passStruct8BytesNestedMixedx10_a9 = a9;
+
+  final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedMixedx10AfterCallback() {
+  final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(15.0, result);
+}
+
+typedef PassStruct16BytesNestedIntx2Type = Int64 Function(
+    Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a0 =
+    Struct16BytesNestedInt();
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a1 =
+    Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct16BytesNestedIntx2Result = 0;
+
+int passStruct16BytesNestedIntx2CalculateResult() {
+  int result = 0;
+
+  result += passStruct16BytesNestedIntx2_a0.a0.a0.a0;
+  result += passStruct16BytesNestedIntx2_a0.a0.a0.a1;
+  result += passStruct16BytesNestedIntx2_a0.a0.a1.a0;
+  result += passStruct16BytesNestedIntx2_a0.a0.a1.a1;
+  result += passStruct16BytesNestedIntx2_a0.a1.a0.a0;
+  result += passStruct16BytesNestedIntx2_a0.a1.a0.a1;
+  result += passStruct16BytesNestedIntx2_a0.a1.a1.a0;
+  result += passStruct16BytesNestedIntx2_a0.a1.a1.a1;
+  result += passStruct16BytesNestedIntx2_a1.a0.a0.a0;
+  result += passStruct16BytesNestedIntx2_a1.a0.a0.a1;
+  result += passStruct16BytesNestedIntx2_a1.a0.a1.a0;
+  result += passStruct16BytesNestedIntx2_a1.a0.a1.a1;
+  result += passStruct16BytesNestedIntx2_a1.a1.a0.a0;
+  result += passStruct16BytesNestedIntx2_a1.a1.a0.a1;
+  result += passStruct16BytesNestedIntx2_a1.a1.a1.a0;
+  result += passStruct16BytesNestedIntx2_a1.a1.a1.a1;
+
+  passStruct16BytesNestedIntx2Result = result;
+
+  return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+int passStruct16BytesNestedIntx2(
+    Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+  print("passStruct16BytesNestedIntx2(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct16BytesNestedIntx2 throwing on purpuse!");
+  }
+
+  passStruct16BytesNestedIntx2_a0 = a0;
+  passStruct16BytesNestedIntx2_a1 = a1;
+
+  final result = passStruct16BytesNestedIntx2CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct16BytesNestedIntx2AfterCallback() {
+  final result = passStruct16BytesNestedIntx2CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(8, result);
+}
+
+typedef PassStruct32BytesNestedIntx2Type = Int64 Function(
+    Struct32BytesNestedInt, Struct32BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a0 =
+    Struct32BytesNestedInt();
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a1 =
+    Struct32BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct32BytesNestedIntx2Result = 0;
+
+int passStruct32BytesNestedIntx2CalculateResult() {
+  int result = 0;
+
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a1;
+
+  passStruct32BytesNestedIntx2Result = result;
+
+  return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+int passStruct32BytesNestedIntx2(
+    Struct32BytesNestedInt a0, Struct32BytesNestedInt a1) {
+  print("passStruct32BytesNestedIntx2(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0.a0.a0 == 42 || a0.a0.a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct32BytesNestedIntx2 throwing on purpuse!");
+  }
+
+  passStruct32BytesNestedIntx2_a0 = a0;
+  passStruct32BytesNestedIntx2_a1 = a1;
+
+  final result = passStruct32BytesNestedIntx2CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct32BytesNestedIntx2AfterCallback() {
+  final result = passStruct32BytesNestedIntx2CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(16, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt16Type = Int64 Function(
+    StructNestedIntStructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt16 passStructNestedIntStructAlignmentInt16_a0 =
+    StructNestedIntStructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt16Result = 0;
+
+int passStructNestedIntStructAlignmentInt16CalculateResult() {
+  int result = 0;
+
+  result += passStructNestedIntStructAlignmentInt16_a0.a0.a0;
+  result += passStructNestedIntStructAlignmentInt16_a0.a0.a1;
+  result += passStructNestedIntStructAlignmentInt16_a0.a0.a2;
+  result += passStructNestedIntStructAlignmentInt16_a0.a1.a0;
+  result += passStructNestedIntStructAlignmentInt16_a0.a1.a1;
+  result += passStructNestedIntStructAlignmentInt16_a0.a1.a2;
+
+  passStructNestedIntStructAlignmentInt16Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+int passStructNestedIntStructAlignmentInt16(
+    StructNestedIntStructAlignmentInt16 a0) {
+  print("passStructNestedIntStructAlignmentInt16(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+  }
+
+  passStructNestedIntStructAlignmentInt16_a0 = a0;
+
+  final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIntStructAlignmentInt16AfterCallback() {
+  final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt32Type = Int64 Function(
+    StructNestedIntStructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt32 passStructNestedIntStructAlignmentInt32_a0 =
+    StructNestedIntStructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt32Result = 0;
+
+int passStructNestedIntStructAlignmentInt32CalculateResult() {
+  int result = 0;
+
+  result += passStructNestedIntStructAlignmentInt32_a0.a0.a0;
+  result += passStructNestedIntStructAlignmentInt32_a0.a0.a1;
+  result += passStructNestedIntStructAlignmentInt32_a0.a0.a2;
+  result += passStructNestedIntStructAlignmentInt32_a0.a1.a0;
+  result += passStructNestedIntStructAlignmentInt32_a0.a1.a1;
+  result += passStructNestedIntStructAlignmentInt32_a0.a1.a2;
+
+  passStructNestedIntStructAlignmentInt32Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+int passStructNestedIntStructAlignmentInt32(
+    StructNestedIntStructAlignmentInt32 a0) {
+  print("passStructNestedIntStructAlignmentInt32(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+  }
+
+  passStructNestedIntStructAlignmentInt32_a0 = a0;
+
+  final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIntStructAlignmentInt32AfterCallback() {
+  final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt64Type = Int64 Function(
+    StructNestedIntStructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt64 passStructNestedIntStructAlignmentInt64_a0 =
+    StructNestedIntStructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt64Result = 0;
+
+int passStructNestedIntStructAlignmentInt64CalculateResult() {
+  int result = 0;
+
+  result += passStructNestedIntStructAlignmentInt64_a0.a0.a0;
+  result += passStructNestedIntStructAlignmentInt64_a0.a0.a1;
+  result += passStructNestedIntStructAlignmentInt64_a0.a0.a2;
+  result += passStructNestedIntStructAlignmentInt64_a0.a1.a0;
+  result += passStructNestedIntStructAlignmentInt64_a0.a1.a1;
+  result += passStructNestedIntStructAlignmentInt64_a0.a1.a2;
+
+  passStructNestedIntStructAlignmentInt64Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+int passStructNestedIntStructAlignmentInt64(
+    StructNestedIntStructAlignmentInt64 a0) {
+  print("passStructNestedIntStructAlignmentInt64(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+  }
+
+  passStructNestedIntStructAlignmentInt64_a0 = a0;
+
+  final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIntStructAlignmentInt64AfterCallback() {
+  final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(3, result);
+}
+
+typedef PassStructNestedIrregularEvenBiggerx4Type = Double Function(
+    StructNestedIrregularEvenBigger,
+    StructNestedIrregularEvenBigger,
+    StructNestedIrregularEvenBigger,
+    StructNestedIrregularEvenBigger);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a0 =
+    StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a1 =
+    StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a2 =
+    StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a3 =
+    StructNestedIrregularEvenBigger();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedIrregularEvenBiggerx4Result = 0.0;
+
+double passStructNestedIrregularEvenBiggerx4CalculateResult() {
+  double result = 0;
+
+  result += passStructNestedIrregularEvenBiggerx4_a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a3;
+
+  passStructNestedIrregularEvenBiggerx4Result = result;
+
+  return result;
+}
+
+/// Return big irregular struct as smoke test.
+double passStructNestedIrregularEvenBiggerx4(
+    StructNestedIrregularEvenBigger a0,
+    StructNestedIrregularEvenBigger a1,
+    StructNestedIrregularEvenBigger a2,
+    StructNestedIrregularEvenBigger a3) {
+  print("passStructNestedIrregularEvenBiggerx4(${a0}, ${a1}, ${a2}, ${a3})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIrregularEvenBiggerx4 throwing on purpuse!");
+  }
+
+  passStructNestedIrregularEvenBiggerx4_a0 = a0;
+  passStructNestedIrregularEvenBiggerx4_a1 = a1;
+  passStructNestedIrregularEvenBiggerx4_a2 = a2;
+  passStructNestedIrregularEvenBiggerx4_a3 = a3;
+
+  final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIrregularEvenBiggerx4AfterCallback() {
+  final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(1572.0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
@@ -6666,3 +7773,657 @@
 
   free(returnStructAlignmentInt64Result.addressOf);
 }
+
+typedef ReturnStruct8BytesNestedIntType = Struct8BytesNestedInt Function(
+    Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a0 =
+    Struct4BytesHomogeneousInt16();
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a1 =
+    Struct4BytesHomogeneousInt16();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedInt returnStruct8BytesNestedIntResult =
+    Struct8BytesNestedInt();
+
+Struct8BytesNestedInt returnStruct8BytesNestedIntCalculateResult() {
+  Struct8BytesNestedInt result = allocate<Struct8BytesNestedInt>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedInt_a0.a0;
+  result.a0.a1 = returnStruct8BytesNestedInt_a0.a1;
+  result.a1.a0 = returnStruct8BytesNestedInt_a1.a0;
+  result.a1.a1 = returnStruct8BytesNestedInt_a1.a1;
+
+  returnStruct8BytesNestedIntResult = result;
+
+  return result;
+}
+
+/// Simple nested struct.
+Struct8BytesNestedInt returnStruct8BytesNestedInt(
+    Struct4BytesHomogeneousInt16 a0, Struct4BytesHomogeneousInt16 a1) {
+  print("returnStruct8BytesNestedInt(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedInt throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedInt_a0 = a0;
+  returnStruct8BytesNestedInt_a1 = a1;
+
+  final result = returnStruct8BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedIntAfterCallback() {
+  free(returnStruct8BytesNestedIntResult.addressOf);
+
+  final result = returnStruct8BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloatType = Struct8BytesNestedFloat Function(
+    Struct4BytesFloat, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat_a0 = Struct4BytesFloat();
+Struct4BytesFloat returnStruct8BytesNestedFloat_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatResult =
+    Struct8BytesNestedFloat();
+
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatCalculateResult() {
+  Struct8BytesNestedFloat result = allocate<Struct8BytesNestedFloat>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedFloat_a0.a0;
+  result.a1.a0 = returnStruct8BytesNestedFloat_a1.a0;
+
+  returnStruct8BytesNestedFloatResult = result;
+
+  return result;
+}
+
+/// Simple nested struct with floats.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloat(
+    Struct4BytesFloat a0, Struct4BytesFloat a1) {
+  print("returnStruct8BytesNestedFloat(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedFloat throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedFloat_a0 = a0;
+  returnStruct8BytesNestedFloat_a1 = a1;
+
+  final result = returnStruct8BytesNestedFloatCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedFloatAfterCallback() {
+  free(returnStruct8BytesNestedFloatResult.addressOf);
+
+  final result = returnStruct8BytesNestedFloatCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedFloatResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloat2Type = Struct8BytesNestedFloat2 Function(
+    Struct4BytesFloat, Float);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat2_a0 = Struct4BytesFloat();
+double returnStruct8BytesNestedFloat2_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2Result =
+    Struct8BytesNestedFloat2();
+
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2CalculateResult() {
+  Struct8BytesNestedFloat2 result = allocate<Struct8BytesNestedFloat2>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedFloat2_a0.a0;
+  result.a1 = returnStruct8BytesNestedFloat2_a1;
+
+  returnStruct8BytesNestedFloat2Result = result;
+
+  return result;
+}
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2(
+    Struct4BytesFloat a0, double a1) {
+  print("returnStruct8BytesNestedFloat2(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedFloat2 throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedFloat2_a0 = a0;
+  returnStruct8BytesNestedFloat2_a1 = a1;
+
+  final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedFloat2AfterCallback() {
+  free(returnStruct8BytesNestedFloat2Result.addressOf);
+
+  final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedFloat2Result.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedMixedType = Struct8BytesNestedMixed Function(
+    Struct4BytesHomogeneousInt16, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedMixed_a0 =
+    Struct4BytesHomogeneousInt16();
+Struct4BytesFloat returnStruct8BytesNestedMixed_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedResult =
+    Struct8BytesNestedMixed();
+
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedCalculateResult() {
+  Struct8BytesNestedMixed result = allocate<Struct8BytesNestedMixed>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedMixed_a0.a0;
+  result.a0.a1 = returnStruct8BytesNestedMixed_a0.a1;
+  result.a1.a0 = returnStruct8BytesNestedMixed_a1.a0;
+
+  returnStruct8BytesNestedMixedResult = result;
+
+  return result;
+}
+
+/// Simple nested struct with mixed members.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixed(
+    Struct4BytesHomogeneousInt16 a0, Struct4BytesFloat a1) {
+  print("returnStruct8BytesNestedMixed(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedMixed throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedMixed_a0 = a0;
+  returnStruct8BytesNestedMixed_a1 = a1;
+
+  final result = returnStruct8BytesNestedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedMixedAfterCallback() {
+  free(returnStruct8BytesNestedMixedResult.addressOf);
+
+  final result = returnStruct8BytesNestedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedMixedResult.addressOf);
+}
+
+typedef ReturnStruct16BytesNestedIntType = Struct16BytesNestedInt Function(
+    Struct8BytesNestedInt, Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a1 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct16BytesNestedInt returnStruct16BytesNestedIntResult =
+    Struct16BytesNestedInt();
+
+Struct16BytesNestedInt returnStruct16BytesNestedIntCalculateResult() {
+  Struct16BytesNestedInt result = allocate<Struct16BytesNestedInt>().ref;
+
+  result.a0.a0.a0 = returnStruct16BytesNestedInt_a0.a0.a0;
+  result.a0.a0.a1 = returnStruct16BytesNestedInt_a0.a0.a1;
+  result.a0.a1.a0 = returnStruct16BytesNestedInt_a0.a1.a0;
+  result.a0.a1.a1 = returnStruct16BytesNestedInt_a0.a1.a1;
+  result.a1.a0.a0 = returnStruct16BytesNestedInt_a1.a0.a0;
+  result.a1.a0.a1 = returnStruct16BytesNestedInt_a1.a0.a1;
+  result.a1.a1.a0 = returnStruct16BytesNestedInt_a1.a1.a0;
+  result.a1.a1.a1 = returnStruct16BytesNestedInt_a1.a1.a1;
+
+  returnStruct16BytesNestedIntResult = result;
+
+  return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+Struct16BytesNestedInt returnStruct16BytesNestedInt(
+    Struct8BytesNestedInt a0, Struct8BytesNestedInt a1) {
+  print("returnStruct16BytesNestedInt(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct16BytesNestedInt throwing on purpuse!");
+  }
+
+  returnStruct16BytesNestedInt_a0 = a0;
+  returnStruct16BytesNestedInt_a1 = a1;
+
+  final result = returnStruct16BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct16BytesNestedIntAfterCallback() {
+  free(returnStruct16BytesNestedIntResult.addressOf);
+
+  final result = returnStruct16BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct16BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct32BytesNestedIntType = Struct32BytesNestedInt Function(
+    Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a0 =
+    Struct16BytesNestedInt();
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a1 =
+    Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct32BytesNestedInt returnStruct32BytesNestedIntResult =
+    Struct32BytesNestedInt();
+
+Struct32BytesNestedInt returnStruct32BytesNestedIntCalculateResult() {
+  Struct32BytesNestedInt result = allocate<Struct32BytesNestedInt>().ref;
+
+  result.a0.a0.a0.a0 = returnStruct32BytesNestedInt_a0.a0.a0.a0;
+  result.a0.a0.a0.a1 = returnStruct32BytesNestedInt_a0.a0.a0.a1;
+  result.a0.a0.a1.a0 = returnStruct32BytesNestedInt_a0.a0.a1.a0;
+  result.a0.a0.a1.a1 = returnStruct32BytesNestedInt_a0.a0.a1.a1;
+  result.a0.a1.a0.a0 = returnStruct32BytesNestedInt_a0.a1.a0.a0;
+  result.a0.a1.a0.a1 = returnStruct32BytesNestedInt_a0.a1.a0.a1;
+  result.a0.a1.a1.a0 = returnStruct32BytesNestedInt_a0.a1.a1.a0;
+  result.a0.a1.a1.a1 = returnStruct32BytesNestedInt_a0.a1.a1.a1;
+  result.a1.a0.a0.a0 = returnStruct32BytesNestedInt_a1.a0.a0.a0;
+  result.a1.a0.a0.a1 = returnStruct32BytesNestedInt_a1.a0.a0.a1;
+  result.a1.a0.a1.a0 = returnStruct32BytesNestedInt_a1.a0.a1.a0;
+  result.a1.a0.a1.a1 = returnStruct32BytesNestedInt_a1.a0.a1.a1;
+  result.a1.a1.a0.a0 = returnStruct32BytesNestedInt_a1.a1.a0.a0;
+  result.a1.a1.a0.a1 = returnStruct32BytesNestedInt_a1.a1.a0.a1;
+  result.a1.a1.a1.a0 = returnStruct32BytesNestedInt_a1.a1.a1.a0;
+  result.a1.a1.a1.a1 = returnStruct32BytesNestedInt_a1.a1.a1.a1;
+
+  returnStruct32BytesNestedIntResult = result;
+
+  return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+Struct32BytesNestedInt returnStruct32BytesNestedInt(
+    Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+  print("returnStruct32BytesNestedInt(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct32BytesNestedInt throwing on purpuse!");
+  }
+
+  returnStruct32BytesNestedInt_a0 = a0;
+  returnStruct32BytesNestedInt_a1 = a1;
+
+  final result = returnStruct32BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct32BytesNestedIntAfterCallback() {
+  free(returnStruct32BytesNestedIntResult.addressOf);
+
+  final result = returnStruct32BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct32BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt16Type
+    = StructNestedIntStructAlignmentInt16 Function(
+        StructAlignmentInt16, StructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a0 =
+    StructAlignmentInt16();
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a1 =
+    StructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt16
+    returnStructNestedIntStructAlignmentInt16Result =
+    StructNestedIntStructAlignmentInt16();
+
+StructNestedIntStructAlignmentInt16
+    returnStructNestedIntStructAlignmentInt16CalculateResult() {
+  StructNestedIntStructAlignmentInt16 result =
+      allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+  result.a0.a0 = returnStructNestedIntStructAlignmentInt16_a0.a0;
+  result.a0.a1 = returnStructNestedIntStructAlignmentInt16_a0.a1;
+  result.a0.a2 = returnStructNestedIntStructAlignmentInt16_a0.a2;
+  result.a1.a0 = returnStructNestedIntStructAlignmentInt16_a1.a0;
+  result.a1.a1 = returnStructNestedIntStructAlignmentInt16_a1.a1;
+  result.a1.a2 = returnStructNestedIntStructAlignmentInt16_a1.a2;
+
+  returnStructNestedIntStructAlignmentInt16Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+StructNestedIntStructAlignmentInt16 returnStructNestedIntStructAlignmentInt16(
+    StructAlignmentInt16 a0, StructAlignmentInt16 a1) {
+  print("returnStructNestedIntStructAlignmentInt16(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+  }
+
+  returnStructNestedIntStructAlignmentInt16_a0 = a0;
+  returnStructNestedIntStructAlignmentInt16_a1 = a1;
+
+  final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIntStructAlignmentInt16AfterCallback() {
+  free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+
+  final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt32Type
+    = StructNestedIntStructAlignmentInt32 Function(
+        StructAlignmentInt32, StructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a0 =
+    StructAlignmentInt32();
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a1 =
+    StructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt32
+    returnStructNestedIntStructAlignmentInt32Result =
+    StructNestedIntStructAlignmentInt32();
+
+StructNestedIntStructAlignmentInt32
+    returnStructNestedIntStructAlignmentInt32CalculateResult() {
+  StructNestedIntStructAlignmentInt32 result =
+      allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+  result.a0.a0 = returnStructNestedIntStructAlignmentInt32_a0.a0;
+  result.a0.a1 = returnStructNestedIntStructAlignmentInt32_a0.a1;
+  result.a0.a2 = returnStructNestedIntStructAlignmentInt32_a0.a2;
+  result.a1.a0 = returnStructNestedIntStructAlignmentInt32_a1.a0;
+  result.a1.a1 = returnStructNestedIntStructAlignmentInt32_a1.a1;
+  result.a1.a2 = returnStructNestedIntStructAlignmentInt32_a1.a2;
+
+  returnStructNestedIntStructAlignmentInt32Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+StructNestedIntStructAlignmentInt32 returnStructNestedIntStructAlignmentInt32(
+    StructAlignmentInt32 a0, StructAlignmentInt32 a1) {
+  print("returnStructNestedIntStructAlignmentInt32(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+  }
+
+  returnStructNestedIntStructAlignmentInt32_a0 = a0;
+  returnStructNestedIntStructAlignmentInt32_a1 = a1;
+
+  final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIntStructAlignmentInt32AfterCallback() {
+  free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+
+  final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt64Type
+    = StructNestedIntStructAlignmentInt64 Function(
+        StructAlignmentInt64, StructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a0 =
+    StructAlignmentInt64();
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a1 =
+    StructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt64
+    returnStructNestedIntStructAlignmentInt64Result =
+    StructNestedIntStructAlignmentInt64();
+
+StructNestedIntStructAlignmentInt64
+    returnStructNestedIntStructAlignmentInt64CalculateResult() {
+  StructNestedIntStructAlignmentInt64 result =
+      allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+  result.a0.a0 = returnStructNestedIntStructAlignmentInt64_a0.a0;
+  result.a0.a1 = returnStructNestedIntStructAlignmentInt64_a0.a1;
+  result.a0.a2 = returnStructNestedIntStructAlignmentInt64_a0.a2;
+  result.a1.a0 = returnStructNestedIntStructAlignmentInt64_a1.a0;
+  result.a1.a1 = returnStructNestedIntStructAlignmentInt64_a1.a1;
+  result.a1.a2 = returnStructNestedIntStructAlignmentInt64_a1.a2;
+
+  returnStructNestedIntStructAlignmentInt64Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+StructNestedIntStructAlignmentInt64 returnStructNestedIntStructAlignmentInt64(
+    StructAlignmentInt64 a0, StructAlignmentInt64 a1) {
+  print("returnStructNestedIntStructAlignmentInt64(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+  }
+
+  returnStructNestedIntStructAlignmentInt64_a0 = a0;
+  returnStructNestedIntStructAlignmentInt64_a1 = a1;
+
+  final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIntStructAlignmentInt64AfterCallback() {
+  free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+
+  final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+}
+
+typedef ReturnStructNestedIrregularEvenBiggerType
+    = StructNestedIrregularEvenBigger Function(Uint64,
+        StructNestedIrregularBigger, StructNestedIrregularBigger, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStructNestedIrregularEvenBigger_a0 = 0;
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a1 =
+    StructNestedIrregularBigger();
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a2 =
+    StructNestedIrregularBigger();
+double returnStructNestedIrregularEvenBigger_a3 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBiggerResult =
+    StructNestedIrregularEvenBigger();
+
+StructNestedIrregularEvenBigger
+    returnStructNestedIrregularEvenBiggerCalculateResult() {
+  StructNestedIrregularEvenBigger result =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+
+  result.a0 = returnStructNestedIrregularEvenBigger_a0;
+  result.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a0;
+  result.a1.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a0;
+  result.a1.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a1;
+  result.a1.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a1.a0;
+  result.a1.a0.a2 = returnStructNestedIrregularEvenBigger_a1.a0.a2;
+  result.a1.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a0.a0;
+  result.a1.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a1;
+  result.a1.a0.a4 = returnStructNestedIrregularEvenBigger_a1.a0.a4;
+  result.a1.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a0.a0;
+  result.a1.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a1.a0;
+  result.a1.a0.a6 = returnStructNestedIrregularEvenBigger_a1.a0.a6;
+  result.a1.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a0;
+  result.a1.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a1;
+  result.a1.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a1.a0;
+  result.a1.a2 = returnStructNestedIrregularEvenBigger_a1.a2;
+  result.a1.a3 = returnStructNestedIrregularEvenBigger_a1.a3;
+  result.a2.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a0;
+  result.a2.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a0;
+  result.a2.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a1;
+  result.a2.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a1.a0;
+  result.a2.a0.a2 = returnStructNestedIrregularEvenBigger_a2.a0.a2;
+  result.a2.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a0.a0;
+  result.a2.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a1;
+  result.a2.a0.a4 = returnStructNestedIrregularEvenBigger_a2.a0.a4;
+  result.a2.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a0.a0;
+  result.a2.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a1.a0;
+  result.a2.a0.a6 = returnStructNestedIrregularEvenBigger_a2.a0.a6;
+  result.a2.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a0;
+  result.a2.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a1;
+  result.a2.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a1.a0;
+  result.a2.a2 = returnStructNestedIrregularEvenBigger_a2.a2;
+  result.a2.a3 = returnStructNestedIrregularEvenBigger_a2.a3;
+  result.a3 = returnStructNestedIrregularEvenBigger_a3;
+
+  returnStructNestedIrregularEvenBiggerResult = result;
+
+  return result;
+}
+
+/// Return big irregular struct as smoke test.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBigger(int a0,
+    StructNestedIrregularBigger a1, StructNestedIrregularBigger a2, double a3) {
+  print("returnStructNestedIrregularEvenBigger(${a0}, ${a1}, ${a2}, ${a3})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIrregularEvenBigger throwing on purpuse!");
+  }
+
+  returnStructNestedIrregularEvenBigger_a0 = a0;
+  returnStructNestedIrregularEvenBigger_a1 = a1;
+  returnStructNestedIrregularEvenBigger_a2 = a2;
+  returnStructNestedIrregularEvenBigger_a3 = a3;
+
+  final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIrregularEvenBiggerAfterCallback() {
+  free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+
+  final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+}
diff --git a/tests/ffi/function_callbacks_structs_by_value_test.dart b/tests/ffi/function_callbacks_structs_by_value_test.dart
index 98ab518..23c9946 100644
--- a/tests/ffi/function_callbacks_structs_by_value_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_test.dart
@@ -18,6 +18,7 @@
   for (int i = 0; i < 10; i++) {
     recursiveTest(10);
     recursiveTest(11);
+    testCopyLogic();
   }
 }
 
@@ -69,3 +70,65 @@
         Struct20BytesHomogeneousInt32 struct, Pointer callbackAddress),
     Struct20BytesHomogeneousInt32 Function(int recursionCounter,
         Struct20BytesHomogeneousInt32, Pointer)>("PassStructRecursive");
+
+Struct8BytesNestedInt typedDataBackedStruct = Struct8BytesNestedInt();
+bool typedDataBackedStructSet = false;
+void _receiveStructByValue(Struct8BytesNestedInt struct) {
+  typedDataBackedStruct = struct;
+  typedDataBackedStructSet = true;
+}
+
+final _receiveStructByValuePointer =
+    Pointer.fromFunction<Void Function(Struct8BytesNestedInt)>(
+        _receiveStructByValue);
+
+final _invokeReceiveStructByValue = ffiTestFunctions.lookupFunction<
+        Void Function(
+            Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>),
+        void Function(
+            Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>)>(
+    "CallbackWithStruct");
+
+void testCopyLogic() {
+  _invokeReceiveStructByValue(_receiveStructByValuePointer);
+  Expect.isTrue(typedDataBackedStructSet);
+
+  final pointerBackedStruct = allocate<Struct8BytesNestedInt>().ref;
+
+  void reset() {
+    pointerBackedStruct.a0.a0 = 1;
+    pointerBackedStruct.a0.a1 = 2;
+    pointerBackedStruct.a1.a0 = 3;
+    pointerBackedStruct.a1.a1 = 4;
+    typedDataBackedStruct.a0.a0 = 5;
+    typedDataBackedStruct.a0.a1 = 6;
+    typedDataBackedStruct.a1.a0 = 7;
+    typedDataBackedStruct.a1.a1 = 8;
+  }
+
+  // Pointer -> Pointer.
+  reset();
+  pointerBackedStruct.a1 = pointerBackedStruct.a0;
+  Expect.equals(1, pointerBackedStruct.a1.a0);
+  Expect.equals(2, pointerBackedStruct.a1.a1);
+
+  // Pointer -> TypedData.
+  reset();
+  typedDataBackedStruct.a1 = pointerBackedStruct.a0;
+  Expect.equals(1, typedDataBackedStruct.a1.a0);
+  Expect.equals(2, typedDataBackedStruct.a1.a1);
+
+  // TypedData -> Pointer.
+  reset();
+  pointerBackedStruct.a1 = typedDataBackedStruct.a0;
+  Expect.equals(5, pointerBackedStruct.a1.a0);
+  Expect.equals(6, pointerBackedStruct.a1.a1);
+
+  // TypedData -> TypedData.
+  reset();
+  typedDataBackedStruct.a1 = typedDataBackedStruct.a0;
+  Expect.equals(5, typedDataBackedStruct.a1.a0);
+  Expect.equals(6, typedDataBackedStruct.a1.a1);
+
+  free(pointerBackedStruct.addressOf);
+}
diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart
index 35936e9..8e800fc 100644
--- a/tests/ffi/function_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_test.dart
@@ -52,6 +52,16 @@
     testPassStructAlignmentInt16();
     testPassStructAlignmentInt32();
     testPassStructAlignmentInt64();
+    testPassStruct8BytesNestedIntx10();
+    testPassStruct8BytesNestedFloatx10();
+    testPassStruct8BytesNestedFloat2x10();
+    testPassStruct8BytesNestedMixedx10();
+    testPassStruct16BytesNestedIntx2();
+    testPassStruct32BytesNestedIntx2();
+    testPassStructNestedIntStructAlignmentInt16();
+    testPassStructNestedIntStructAlignmentInt32();
+    testPassStructNestedIntStructAlignmentInt64();
+    testPassStructNestedIrregularEvenBiggerx4();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -82,6 +92,16 @@
     testReturnStructAlignmentInt16();
     testReturnStructAlignmentInt32();
     testReturnStructAlignmentInt64();
+    testReturnStruct8BytesNestedInt();
+    testReturnStruct8BytesNestedFloat();
+    testReturnStruct8BytesNestedFloat2();
+    testReturnStruct8BytesNestedMixed();
+    testReturnStruct16BytesNestedInt();
+    testReturnStruct32BytesNestedInt();
+    testReturnStructNestedIntStructAlignmentInt16();
+    testReturnStructNestedIntStructAlignmentInt32();
+    testReturnStructNestedIntStructAlignmentInt64();
+    testReturnStructNestedIrregularEvenBigger();
   }
 }
 
@@ -129,6 +149,13 @@
   String toString() => "(${a0}, ${a1})";
 }
 
+class Struct4BytesFloat extends Struct {
+  @Float()
+  external double a0;
+
+  String toString() => "(${a0})";
+}
+
 class Struct7BytesHomogeneousUint8 extends Struct {
   @Uint8()
   external int a0;
@@ -876,6 +903,129 @@
   String toString() => "(${a0}, ${a1}, ${a2})";
 }
 
+class Struct8BytesNestedInt extends Struct {
+  external Struct4BytesHomogeneousInt16 a0;
+
+  external Struct4BytesHomogeneousInt16 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat extends Struct {
+  external Struct4BytesFloat a0;
+
+  external Struct4BytesFloat a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat2 extends Struct {
+  external Struct4BytesFloat a0;
+
+  @Float()
+  external double a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedMixed extends Struct {
+  external Struct4BytesHomogeneousInt16 a0;
+
+  external Struct4BytesFloat a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct16BytesNestedInt extends Struct {
+  external Struct8BytesNestedInt a0;
+
+  external Struct8BytesNestedInt a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct32BytesNestedInt extends Struct {
+  external Struct16BytesNestedInt a0;
+
+  external Struct16BytesNestedInt a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt16 extends Struct {
+  external StructAlignmentInt16 a0;
+
+  external StructAlignmentInt16 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt32 extends Struct {
+  external StructAlignmentInt32 a0;
+
+  external StructAlignmentInt32 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt64 extends Struct {
+  external StructAlignmentInt64 a0;
+
+  external StructAlignmentInt64 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIrregularBig extends Struct {
+  @Uint16()
+  external int a0;
+
+  external Struct8BytesNestedMixed a1;
+
+  @Uint16()
+  external int a2;
+
+  external Struct8BytesNestedFloat2 a3;
+
+  @Uint16()
+  external int a4;
+
+  external Struct8BytesNestedFloat a5;
+
+  @Uint16()
+  external int a6;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6})";
+}
+
+class StructNestedIrregularBigger extends Struct {
+  external StructNestedIrregularBig a0;
+
+  external Struct8BytesNestedMixed a1;
+
+  @Float()
+  external double a2;
+
+  @Double()
+  external double a3;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
+class StructNestedIrregularEvenBigger extends Struct {
+  @Uint64()
+  external int a0;
+
+  external StructNestedIrregularBigger a1;
+
+  external StructNestedIrregularBigger a2;
+
+  @Double()
+  external double a3;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
 final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
     Int64 Function(
         Struct1ByteInt,
@@ -3660,6 +3810,691 @@
   free(a0.addressOf);
 }
 
+final passStruct8BytesNestedIntx10 = ffiTestFunctions.lookupFunction<
+    Int64 Function(
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt),
+    int Function(
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt)>("PassStruct8BytesNestedIntx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+void testPassStruct8BytesNestedIntx10() {
+  Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a2 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a3 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a4 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a5 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a6 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a7 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a8 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a9 = allocate<Struct8BytesNestedInt>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3;
+  a0.a1.a1 = 4;
+  a1.a0.a0 = -5;
+  a1.a0.a1 = 6;
+  a1.a1.a0 = -7;
+  a1.a1.a1 = 8;
+  a2.a0.a0 = -9;
+  a2.a0.a1 = 10;
+  a2.a1.a0 = -11;
+  a2.a1.a1 = 12;
+  a3.a0.a0 = -13;
+  a3.a0.a1 = 14;
+  a3.a1.a0 = -15;
+  a3.a1.a1 = 16;
+  a4.a0.a0 = -17;
+  a4.a0.a1 = 18;
+  a4.a1.a0 = -19;
+  a4.a1.a1 = 20;
+  a5.a0.a0 = -21;
+  a5.a0.a1 = 22;
+  a5.a1.a0 = -23;
+  a5.a1.a1 = 24;
+  a6.a0.a0 = -25;
+  a6.a0.a1 = 26;
+  a6.a1.a0 = -27;
+  a6.a1.a1 = 28;
+  a7.a0.a0 = -29;
+  a7.a0.a1 = 30;
+  a7.a1.a0 = -31;
+  a7.a1.a1 = 32;
+  a8.a0.a0 = -33;
+  a8.a0.a1 = 34;
+  a8.a1.a0 = -35;
+  a8.a1.a1 = 36;
+  a9.a0.a0 = -37;
+  a9.a0.a1 = 38;
+  a9.a1.a0 = -39;
+  a9.a1.a1 = 40;
+
+  final result =
+      passStruct8BytesNestedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.equals(20, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+    Float Function(
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat),
+    double Function(
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat)>("PassStruct8BytesNestedFloatx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+void testPassStruct8BytesNestedFloatx10() {
+  Struct8BytesNestedFloat a0 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a1 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a2 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a3 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a4 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a5 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a6 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a7 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a8 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a9 = allocate<Struct8BytesNestedFloat>().ref;
+
+  a0.a0.a0 = -1.0;
+  a0.a1.a0 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a1.a0 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a1.a0 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a1.a0 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a1.a0 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a1.a0 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a1.a0 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a1.a0 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a1.a0 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a1.a0 = 20.0;
+
+  final result =
+      passStruct8BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(10.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloat2x10 = ffiTestFunctions.lookupFunction<
+    Float Function(
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2),
+    double Function(
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2)>("PassStruct8BytesNestedFloat2x10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testPassStruct8BytesNestedFloat2x10() {
+  Struct8BytesNestedFloat2 a0 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a1 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a2 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a3 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a4 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a5 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a6 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a7 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a8 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a9 = allocate<Struct8BytesNestedFloat2>().ref;
+
+  a0.a0.a0 = -1.0;
+  a0.a1 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a1 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a1 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a1 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a1 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a1 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a1 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a1 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a1 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a1 = 20.0;
+
+  final result =
+      passStruct8BytesNestedFloat2x10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(10.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct8BytesNestedMixedx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed),
+    double Function(
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed)>("PassStruct8BytesNestedMixedx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+void testPassStruct8BytesNestedMixedx10() {
+  Struct8BytesNestedMixed a0 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a1 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a2 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a3 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a4 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a5 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a6 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a7 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a8 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a9 = allocate<Struct8BytesNestedMixed>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3.0;
+  a1.a0.a0 = 4;
+  a1.a0.a1 = -5;
+  a1.a1.a0 = 6.0;
+  a2.a0.a0 = -7;
+  a2.a0.a1 = 8;
+  a2.a1.a0 = -9.0;
+  a3.a0.a0 = 10;
+  a3.a0.a1 = -11;
+  a3.a1.a0 = 12.0;
+  a4.a0.a0 = -13;
+  a4.a0.a1 = 14;
+  a4.a1.a0 = -15.0;
+  a5.a0.a0 = 16;
+  a5.a0.a1 = -17;
+  a5.a1.a0 = 18.0;
+  a6.a0.a0 = -19;
+  a6.a0.a1 = 20;
+  a6.a1.a0 = -21.0;
+  a7.a0.a0 = 22;
+  a7.a0.a1 = -23;
+  a7.a1.a0 = 24.0;
+  a8.a0.a0 = -25;
+  a8.a0.a1 = 26;
+  a8.a1.a0 = -27.0;
+  a9.a0.a0 = 28;
+  a9.a0.a1 = -29;
+  a9.a1.a0 = 30.0;
+
+  final result =
+      passStruct8BytesNestedMixedx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(15.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct16BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+    Int64 Function(Struct16BytesNestedInt, Struct16BytesNestedInt),
+    int Function(Struct16BytesNestedInt,
+        Struct16BytesNestedInt)>("PassStruct16BytesNestedIntx2");
+
+/// Deeper nested struct to test recursive member access.
+void testPassStruct16BytesNestedIntx2() {
+  Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+  Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+  a0.a0.a0.a0 = -1;
+  a0.a0.a0.a1 = 2;
+  a0.a0.a1.a0 = -3;
+  a0.a0.a1.a1 = 4;
+  a0.a1.a0.a0 = -5;
+  a0.a1.a0.a1 = 6;
+  a0.a1.a1.a0 = -7;
+  a0.a1.a1.a1 = 8;
+  a1.a0.a0.a0 = -9;
+  a1.a0.a0.a1 = 10;
+  a1.a0.a1.a0 = -11;
+  a1.a0.a1.a1 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15;
+  a1.a1.a1.a1 = 16;
+
+  final result = passStruct16BytesNestedIntx2(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(8, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final passStruct32BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+    Int64 Function(Struct32BytesNestedInt, Struct32BytesNestedInt),
+    int Function(Struct32BytesNestedInt,
+        Struct32BytesNestedInt)>("PassStruct32BytesNestedIntx2");
+
+/// Even deeper nested struct to test recursive member access.
+void testPassStruct32BytesNestedIntx2() {
+  Struct32BytesNestedInt a0 = allocate<Struct32BytesNestedInt>().ref;
+  Struct32BytesNestedInt a1 = allocate<Struct32BytesNestedInt>().ref;
+
+  a0.a0.a0.a0.a0 = -1;
+  a0.a0.a0.a0.a1 = 2;
+  a0.a0.a0.a1.a0 = -3;
+  a0.a0.a0.a1.a1 = 4;
+  a0.a0.a1.a0.a0 = -5;
+  a0.a0.a1.a0.a1 = 6;
+  a0.a0.a1.a1.a0 = -7;
+  a0.a0.a1.a1.a1 = 8;
+  a0.a1.a0.a0.a0 = -9;
+  a0.a1.a0.a0.a1 = 10;
+  a0.a1.a0.a1.a0 = -11;
+  a0.a1.a0.a1.a1 = 12;
+  a0.a1.a1.a0.a0 = -13;
+  a0.a1.a1.a0.a1 = 14;
+  a0.a1.a1.a1.a0 = -15;
+  a0.a1.a1.a1.a1 = 16;
+  a1.a0.a0.a0.a0 = -17;
+  a1.a0.a0.a0.a1 = 18;
+  a1.a0.a0.a1.a0 = -19;
+  a1.a0.a0.a1.a1 = 20;
+  a1.a0.a1.a0.a0 = -21;
+  a1.a0.a1.a0.a1 = 22;
+  a1.a0.a1.a1.a0 = -23;
+  a1.a0.a1.a1.a1 = 24;
+  a1.a1.a0.a0.a0 = -25;
+  a1.a1.a0.a0.a1 = 26;
+  a1.a1.a0.a1.a0 = -27;
+  a1.a1.a0.a1.a1 = 28;
+  a1.a1.a1.a0.a0 = -29;
+  a1.a1.a1.a0.a1 = 30;
+  a1.a1.a1.a1.a0 = -31;
+  a1.a1.a1.a1.a1 = 32;
+
+  final result = passStruct32BytesNestedIntx2(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(16, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt16 = ffiTestFunctions.lookupFunction<
+        Int64 Function(StructNestedIntStructAlignmentInt16),
+        int Function(StructNestedIntStructAlignmentInt16)>(
+    "PassStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testPassStructNestedIntStructAlignmentInt16() {
+  StructNestedIntStructAlignmentInt16 a0 =
+      allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  final result = passStructNestedIntStructAlignmentInt16(a0);
+
+  print("result = $result");
+
+  Expect.equals(3, result);
+
+  free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt32 = ffiTestFunctions.lookupFunction<
+        Int64 Function(StructNestedIntStructAlignmentInt32),
+        int Function(StructNestedIntStructAlignmentInt32)>(
+    "PassStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testPassStructNestedIntStructAlignmentInt32() {
+  StructNestedIntStructAlignmentInt32 a0 =
+      allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  final result = passStructNestedIntStructAlignmentInt32(a0);
+
+  print("result = $result");
+
+  Expect.equals(3, result);
+
+  free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt64 = ffiTestFunctions.lookupFunction<
+        Int64 Function(StructNestedIntStructAlignmentInt64),
+        int Function(StructNestedIntStructAlignmentInt64)>(
+    "PassStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testPassStructNestedIntStructAlignmentInt64() {
+  StructNestedIntStructAlignmentInt64 a0 =
+      allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  final result = passStructNestedIntStructAlignmentInt64(a0);
+
+  print("result = $result");
+
+  Expect.equals(3, result);
+
+  free(a0.addressOf);
+}
+
+final passStructNestedIrregularEvenBiggerx4 = ffiTestFunctions.lookupFunction<
+        Double Function(
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger),
+        double Function(
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger)>(
+    "PassStructNestedIrregularEvenBiggerx4");
+
+/// Return big irregular struct as smoke test.
+void testPassStructNestedIrregularEvenBiggerx4() {
+  StructNestedIrregularEvenBigger a0 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+  StructNestedIrregularEvenBigger a1 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+  StructNestedIrregularEvenBigger a2 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+  StructNestedIrregularEvenBigger a3 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+
+  a0.a0 = 1;
+  a0.a1.a0.a0 = 2;
+  a0.a1.a0.a1.a0.a0 = -3;
+  a0.a1.a0.a1.a0.a1 = 4;
+  a0.a1.a0.a1.a1.a0 = -5.0;
+  a0.a1.a0.a2 = 6;
+  a0.a1.a0.a3.a0.a0 = -7.0;
+  a0.a1.a0.a3.a1 = 8.0;
+  a0.a1.a0.a4 = 9;
+  a0.a1.a0.a5.a0.a0 = 10.0;
+  a0.a1.a0.a5.a1.a0 = -11.0;
+  a0.a1.a0.a6 = 12;
+  a0.a1.a1.a0.a0 = -13;
+  a0.a1.a1.a0.a1 = 14;
+  a0.a1.a1.a1.a0 = -15.0;
+  a0.a1.a2 = 16.0;
+  a0.a1.a3 = -17.0;
+  a0.a2.a0.a0 = 18;
+  a0.a2.a0.a1.a0.a0 = -19;
+  a0.a2.a0.a1.a0.a1 = 20;
+  a0.a2.a0.a1.a1.a0 = -21.0;
+  a0.a2.a0.a2 = 22;
+  a0.a2.a0.a3.a0.a0 = -23.0;
+  a0.a2.a0.a3.a1 = 24.0;
+  a0.a2.a0.a4 = 25;
+  a0.a2.a0.a5.a0.a0 = 26.0;
+  a0.a2.a0.a5.a1.a0 = -27.0;
+  a0.a2.a0.a6 = 28;
+  a0.a2.a1.a0.a0 = -29;
+  a0.a2.a1.a0.a1 = 30;
+  a0.a2.a1.a1.a0 = -31.0;
+  a0.a2.a2 = 32.0;
+  a0.a2.a3 = -33.0;
+  a0.a3 = 34.0;
+  a1.a0 = 35;
+  a1.a1.a0.a0 = 36;
+  a1.a1.a0.a1.a0.a0 = -37;
+  a1.a1.a0.a1.a0.a1 = 38;
+  a1.a1.a0.a1.a1.a0 = -39.0;
+  a1.a1.a0.a2 = 40;
+  a1.a1.a0.a3.a0.a0 = -41.0;
+  a1.a1.a0.a3.a1 = 42.0;
+  a1.a1.a0.a4 = 43;
+  a1.a1.a0.a5.a0.a0 = 44.0;
+  a1.a1.a0.a5.a1.a0 = -45.0;
+  a1.a1.a0.a6 = 46;
+  a1.a1.a1.a0.a0 = -47;
+  a1.a1.a1.a0.a1 = 48;
+  a1.a1.a1.a1.a0 = -49.0;
+  a1.a1.a2 = 50.0;
+  a1.a1.a3 = -51.0;
+  a1.a2.a0.a0 = 52;
+  a1.a2.a0.a1.a0.a0 = -53;
+  a1.a2.a0.a1.a0.a1 = 54;
+  a1.a2.a0.a1.a1.a0 = -55.0;
+  a1.a2.a0.a2 = 56;
+  a1.a2.a0.a3.a0.a0 = -57.0;
+  a1.a2.a0.a3.a1 = 58.0;
+  a1.a2.a0.a4 = 59;
+  a1.a2.a0.a5.a0.a0 = 60.0;
+  a1.a2.a0.a5.a1.a0 = -61.0;
+  a1.a2.a0.a6 = 62;
+  a1.a2.a1.a0.a0 = -63;
+  a1.a2.a1.a0.a1 = 64;
+  a1.a2.a1.a1.a0 = -65.0;
+  a1.a2.a2 = 66.0;
+  a1.a2.a3 = -67.0;
+  a1.a3 = 68.0;
+  a2.a0 = 69;
+  a2.a1.a0.a0 = 70;
+  a2.a1.a0.a1.a0.a0 = -71;
+  a2.a1.a0.a1.a0.a1 = 72;
+  a2.a1.a0.a1.a1.a0 = -73.0;
+  a2.a1.a0.a2 = 74;
+  a2.a1.a0.a3.a0.a0 = -75.0;
+  a2.a1.a0.a3.a1 = 76.0;
+  a2.a1.a0.a4 = 77;
+  a2.a1.a0.a5.a0.a0 = 78.0;
+  a2.a1.a0.a5.a1.a0 = -79.0;
+  a2.a1.a0.a6 = 80;
+  a2.a1.a1.a0.a0 = -81;
+  a2.a1.a1.a0.a1 = 82;
+  a2.a1.a1.a1.a0 = -83.0;
+  a2.a1.a2 = 84.0;
+  a2.a1.a3 = -85.0;
+  a2.a2.a0.a0 = 86;
+  a2.a2.a0.a1.a0.a0 = -87;
+  a2.a2.a0.a1.a0.a1 = 88;
+  a2.a2.a0.a1.a1.a0 = -89.0;
+  a2.a2.a0.a2 = 90;
+  a2.a2.a0.a3.a0.a0 = -91.0;
+  a2.a2.a0.a3.a1 = 92.0;
+  a2.a2.a0.a4 = 93;
+  a2.a2.a0.a5.a0.a0 = 94.0;
+  a2.a2.a0.a5.a1.a0 = -95.0;
+  a2.a2.a0.a6 = 96;
+  a2.a2.a1.a0.a0 = -97;
+  a2.a2.a1.a0.a1 = 98;
+  a2.a2.a1.a1.a0 = -99.0;
+  a2.a2.a2 = 100.0;
+  a2.a2.a3 = -101.0;
+  a2.a3 = 102.0;
+  a3.a0 = 103;
+  a3.a1.a0.a0 = 104;
+  a3.a1.a0.a1.a0.a0 = -105;
+  a3.a1.a0.a1.a0.a1 = 106;
+  a3.a1.a0.a1.a1.a0 = -107.0;
+  a3.a1.a0.a2 = 108;
+  a3.a1.a0.a3.a0.a0 = -109.0;
+  a3.a1.a0.a3.a1 = 110.0;
+  a3.a1.a0.a4 = 111;
+  a3.a1.a0.a5.a0.a0 = 112.0;
+  a3.a1.a0.a5.a1.a0 = -113.0;
+  a3.a1.a0.a6 = 114;
+  a3.a1.a1.a0.a0 = -115;
+  a3.a1.a1.a0.a1 = 116;
+  a3.a1.a1.a1.a0 = -117.0;
+  a3.a1.a2 = 118.0;
+  a3.a1.a3 = -119.0;
+  a3.a2.a0.a0 = 120;
+  a3.a2.a0.a1.a0.a0 = -121;
+  a3.a2.a0.a1.a0.a1 = 122;
+  a3.a2.a0.a1.a1.a0 = -123.0;
+  a3.a2.a0.a2 = 124;
+  a3.a2.a0.a3.a0.a0 = -125.0;
+  a3.a2.a0.a3.a1 = 126.0;
+  a3.a2.a0.a4 = 127;
+  a3.a2.a0.a5.a0.a0 = 128.0;
+  a3.a2.a0.a5.a1.a0 = -129.0;
+  a3.a2.a0.a6 = 130;
+  a3.a2.a1.a0.a0 = -131;
+  a3.a2.a1.a0.a1 = 132;
+  a3.a2.a1.a1.a0 = -133.0;
+  a3.a2.a2 = 134.0;
+  a3.a2.a3 = -135.0;
+  a3.a3 = 136.0;
+
+  final result = passStructNestedIrregularEvenBiggerx4(a0, a1, a2, a3);
+
+  print("result = $result");
+
+  Expect.approxEquals(1572.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -5319,3 +6154,396 @@
   Expect.equals(a1, result.a1);
   Expect.equals(a2, result.a2);
 }
+
+final returnStruct8BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedInt Function(
+        Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16),
+    Struct8BytesNestedInt Function(Struct4BytesHomogeneousInt16,
+        Struct4BytesHomogeneousInt16)>("ReturnStruct8BytesNestedInt");
+
+/// Simple nested struct.
+void testReturnStruct8BytesNestedInt() {
+  Struct4BytesHomogeneousInt16 a0 =
+      allocate<Struct4BytesHomogeneousInt16>().ref;
+  Struct4BytesHomogeneousInt16 a1 =
+      allocate<Struct4BytesHomogeneousInt16>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3;
+  a1.a1 = 4;
+
+  final result = returnStruct8BytesNestedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedFloat Function(Struct4BytesFloat, Struct4BytesFloat),
+    Struct8BytesNestedFloat Function(
+        Struct4BytesFloat, Struct4BytesFloat)>("ReturnStruct8BytesNestedFloat");
+
+/// Simple nested struct with floats.
+void testReturnStruct8BytesNestedFloat() {
+  Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+  Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+  a0.a0 = -1.0;
+  a1.a0 = 2.0;
+
+  final result = returnStruct8BytesNestedFloat(a0, a1);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0.a0, result.a0.a0);
+  Expect.approxEquals(a1.a0, result.a1.a0);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat2 = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedFloat2 Function(Struct4BytesFloat, Float),
+    Struct8BytesNestedFloat2 Function(
+        Struct4BytesFloat, double)>("ReturnStruct8BytesNestedFloat2");
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testReturnStruct8BytesNestedFloat2() {
+  Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+  double a1;
+
+  a0.a0 = -1.0;
+  a1 = 2.0;
+
+  final result = returnStruct8BytesNestedFloat2(a0, a1);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0.a0, result.a0.a0);
+  Expect.approxEquals(a1, result.a1);
+
+  free(a0.addressOf);
+}
+
+final returnStruct8BytesNestedMixed = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedMixed Function(
+        Struct4BytesHomogeneousInt16, Struct4BytesFloat),
+    Struct8BytesNestedMixed Function(Struct4BytesHomogeneousInt16,
+        Struct4BytesFloat)>("ReturnStruct8BytesNestedMixed");
+
+/// Simple nested struct with mixed members.
+void testReturnStruct8BytesNestedMixed() {
+  Struct4BytesHomogeneousInt16 a0 =
+      allocate<Struct4BytesHomogeneousInt16>().ref;
+  Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3.0;
+
+  final result = returnStruct8BytesNestedMixed(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.approxEquals(a1.a0, result.a1.a0);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct16BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Struct16BytesNestedInt Function(
+        Struct8BytesNestedInt, Struct8BytesNestedInt),
+    Struct16BytesNestedInt Function(Struct8BytesNestedInt,
+        Struct8BytesNestedInt)>("ReturnStruct16BytesNestedInt");
+
+/// Deeper nested struct to test recursive member access.
+void testReturnStruct16BytesNestedInt() {
+  Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3;
+  a0.a1.a1 = 4;
+  a1.a0.a0 = -5;
+  a1.a0.a1 = 6;
+  a1.a1.a0 = -7;
+  a1.a1.a1 = 8;
+
+  final result = returnStruct16BytesNestedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0.a0, result.a0.a0.a0);
+  Expect.equals(a0.a0.a1, result.a0.a0.a1);
+  Expect.equals(a0.a1.a0, result.a0.a1.a0);
+  Expect.equals(a0.a1.a1, result.a0.a1.a1);
+  Expect.equals(a1.a0.a0, result.a1.a0.a0);
+  Expect.equals(a1.a0.a1, result.a1.a0.a1);
+  Expect.equals(a1.a1.a0, result.a1.a1.a0);
+  Expect.equals(a1.a1.a1, result.a1.a1.a1);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct32BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Struct32BytesNestedInt Function(
+        Struct16BytesNestedInt, Struct16BytesNestedInt),
+    Struct32BytesNestedInt Function(Struct16BytesNestedInt,
+        Struct16BytesNestedInt)>("ReturnStruct32BytesNestedInt");
+
+/// Even deeper nested struct to test recursive member access.
+void testReturnStruct32BytesNestedInt() {
+  Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+  Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+  a0.a0.a0.a0 = -1;
+  a0.a0.a0.a1 = 2;
+  a0.a0.a1.a0 = -3;
+  a0.a0.a1.a1 = 4;
+  a0.a1.a0.a0 = -5;
+  a0.a1.a0.a1 = 6;
+  a0.a1.a1.a0 = -7;
+  a0.a1.a1.a1 = 8;
+  a1.a0.a0.a0 = -9;
+  a1.a0.a0.a1 = 10;
+  a1.a0.a1.a0 = -11;
+  a1.a0.a1.a1 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15;
+  a1.a1.a1.a1 = 16;
+
+  final result = returnStruct32BytesNestedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0.a0.a0, result.a0.a0.a0.a0);
+  Expect.equals(a0.a0.a0.a1, result.a0.a0.a0.a1);
+  Expect.equals(a0.a0.a1.a0, result.a0.a0.a1.a0);
+  Expect.equals(a0.a0.a1.a1, result.a0.a0.a1.a1);
+  Expect.equals(a0.a1.a0.a0, result.a0.a1.a0.a0);
+  Expect.equals(a0.a1.a0.a1, result.a0.a1.a0.a1);
+  Expect.equals(a0.a1.a1.a0, result.a0.a1.a1.a0);
+  Expect.equals(a0.a1.a1.a1, result.a0.a1.a1.a1);
+  Expect.equals(a1.a0.a0.a0, result.a1.a0.a0.a0);
+  Expect.equals(a1.a0.a0.a1, result.a1.a0.a0.a1);
+  Expect.equals(a1.a0.a1.a0, result.a1.a0.a1.a0);
+  Expect.equals(a1.a0.a1.a1, result.a1.a0.a1.a1);
+  Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+  Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+  Expect.equals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+  Expect.equals(a1.a1.a1.a1, result.a1.a1.a1.a1);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt16 =
+    ffiTestFunctions.lookupFunction<
+        StructNestedIntStructAlignmentInt16 Function(
+            StructAlignmentInt16, StructAlignmentInt16),
+        StructNestedIntStructAlignmentInt16 Function(StructAlignmentInt16,
+            StructAlignmentInt16)>("ReturnStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testReturnStructNestedIntStructAlignmentInt16() {
+  StructAlignmentInt16 a0 = allocate<StructAlignmentInt16>().ref;
+  StructAlignmentInt16 a1 = allocate<StructAlignmentInt16>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  final result = returnStructNestedIntStructAlignmentInt16(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+  Expect.equals(a1.a2, result.a1.a2);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt32 =
+    ffiTestFunctions.lookupFunction<
+        StructNestedIntStructAlignmentInt32 Function(
+            StructAlignmentInt32, StructAlignmentInt32),
+        StructNestedIntStructAlignmentInt32 Function(StructAlignmentInt32,
+            StructAlignmentInt32)>("ReturnStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testReturnStructNestedIntStructAlignmentInt32() {
+  StructAlignmentInt32 a0 = allocate<StructAlignmentInt32>().ref;
+  StructAlignmentInt32 a1 = allocate<StructAlignmentInt32>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  final result = returnStructNestedIntStructAlignmentInt32(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+  Expect.equals(a1.a2, result.a1.a2);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt64 =
+    ffiTestFunctions.lookupFunction<
+        StructNestedIntStructAlignmentInt64 Function(
+            StructAlignmentInt64, StructAlignmentInt64),
+        StructNestedIntStructAlignmentInt64 Function(StructAlignmentInt64,
+            StructAlignmentInt64)>("ReturnStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testReturnStructNestedIntStructAlignmentInt64() {
+  StructAlignmentInt64 a0 = allocate<StructAlignmentInt64>().ref;
+  StructAlignmentInt64 a1 = allocate<StructAlignmentInt64>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  final result = returnStructNestedIntStructAlignmentInt64(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+  Expect.equals(a1.a2, result.a1.a2);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIrregularEvenBigger = ffiTestFunctions.lookupFunction<
+    StructNestedIrregularEvenBigger Function(Uint64,
+        StructNestedIrregularBigger, StructNestedIrregularBigger, Double),
+    StructNestedIrregularEvenBigger Function(
+        int,
+        StructNestedIrregularBigger,
+        StructNestedIrregularBigger,
+        double)>("ReturnStructNestedIrregularEvenBigger");
+
+/// Return big irregular struct as smoke test.
+void testReturnStructNestedIrregularEvenBigger() {
+  int a0;
+  StructNestedIrregularBigger a1 = allocate<StructNestedIrregularBigger>().ref;
+  StructNestedIrregularBigger a2 = allocate<StructNestedIrregularBigger>().ref;
+  double a3;
+
+  a0 = 1;
+  a1.a0.a0 = 2;
+  a1.a0.a1.a0.a0 = -3;
+  a1.a0.a1.a0.a1 = 4;
+  a1.a0.a1.a1.a0 = -5.0;
+  a1.a0.a2 = 6;
+  a1.a0.a3.a0.a0 = -7.0;
+  a1.a0.a3.a1 = 8.0;
+  a1.a0.a4 = 9;
+  a1.a0.a5.a0.a0 = 10.0;
+  a1.a0.a5.a1.a0 = -11.0;
+  a1.a0.a6 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15.0;
+  a1.a2 = 16.0;
+  a1.a3 = -17.0;
+  a2.a0.a0 = 18;
+  a2.a0.a1.a0.a0 = -19;
+  a2.a0.a1.a0.a1 = 20;
+  a2.a0.a1.a1.a0 = -21.0;
+  a2.a0.a2 = 22;
+  a2.a0.a3.a0.a0 = -23.0;
+  a2.a0.a3.a1 = 24.0;
+  a2.a0.a4 = 25;
+  a2.a0.a5.a0.a0 = 26.0;
+  a2.a0.a5.a1.a0 = -27.0;
+  a2.a0.a6 = 28;
+  a2.a1.a0.a0 = -29;
+  a2.a1.a0.a1 = 30;
+  a2.a1.a1.a0 = -31.0;
+  a2.a2 = 32.0;
+  a2.a3 = -33.0;
+  a3 = 34.0;
+
+  final result = returnStructNestedIrregularEvenBigger(a0, a1, a2, a3);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.equals(a1.a0.a0, result.a1.a0.a0);
+  Expect.equals(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0);
+  Expect.equals(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1);
+  Expect.approxEquals(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0);
+  Expect.equals(a1.a0.a2, result.a1.a0.a2);
+  Expect.approxEquals(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0);
+  Expect.approxEquals(a1.a0.a3.a1, result.a1.a0.a3.a1);
+  Expect.equals(a1.a0.a4, result.a1.a0.a4);
+  Expect.approxEquals(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0);
+  Expect.approxEquals(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0);
+  Expect.equals(a1.a0.a6, result.a1.a0.a6);
+  Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+  Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+  Expect.approxEquals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+  Expect.approxEquals(a1.a2, result.a1.a2);
+  Expect.approxEquals(a1.a3, result.a1.a3);
+  Expect.equals(a2.a0.a0, result.a2.a0.a0);
+  Expect.equals(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0);
+  Expect.equals(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1);
+  Expect.approxEquals(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0);
+  Expect.equals(a2.a0.a2, result.a2.a0.a2);
+  Expect.approxEquals(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0);
+  Expect.approxEquals(a2.a0.a3.a1, result.a2.a0.a3.a1);
+  Expect.equals(a2.a0.a4, result.a2.a0.a4);
+  Expect.approxEquals(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0);
+  Expect.approxEquals(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0);
+  Expect.equals(a2.a0.a6, result.a2.a0.a6);
+  Expect.equals(a2.a1.a0.a0, result.a2.a1.a0.a0);
+  Expect.equals(a2.a1.a0.a1, result.a2.a1.a0.a1);
+  Expect.approxEquals(a2.a1.a1.a0, result.a2.a1.a1.a0);
+  Expect.approxEquals(a2.a2, result.a2.a2);
+  Expect.approxEquals(a2.a3, result.a2.a3);
+  Expect.approxEquals(a3, result.a3);
+
+  free(a1.addressOf);
+  free(a2.addressOf);
+}
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index f2948fa..56736bf5 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -140,11 +140,19 @@
   /// To disambiguate same size structs.
   final String suffix;
 
+  /// To override names.
+  final String overrideName;
+
   StructType(List<CType> memberTypes)
       : this.members = generateMemberNames(memberTypes),
-        this.suffix = "";
+        this.suffix = "",
+        this.overrideName = "";
   StructType.disambiguate(List<CType> memberTypes, this.suffix)
-      : this.members = generateMemberNames(memberTypes);
+      : this.members = generateMemberNames(memberTypes),
+        this.overrideName = "";
+  StructType.override(List<CType> memberTypes, this.overrideName)
+      : this.members = generateMemberNames(memberTypes),
+        this.suffix = "";
 
   List<CType> get memberTypes => members.map((a) => a.type).toList();
 
@@ -191,6 +199,9 @@
 
   String get name {
     String result = "Struct";
+    if (overrideName != "") {
+      return result + overrideName;
+    }
     if (hasSize) {
       result += "${size}Byte" + (size != 1 ? "s" : "");
     }
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index 46f5108..718caea 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -255,6 +255,41 @@
       int64,
       """
 Test alignment and padding of 64 byte int within struct."""),
+  FunctionType(List.filled(10, struct8bytesNestedInt), int64, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust registers on all platforms."""),
+  FunctionType(List.filled(10, struct8bytesNestedFloat), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms."""),
+  FunctionType(List.filled(10, struct8bytesNestedFloat2), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms.
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+  FunctionType(List.filled(10, struct8bytesNestedMixed), double_, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust all registers on all platforms."""),
+  FunctionType(List.filled(2, struct16bytesNestedInt), int64, """
+Deeper nested struct to test recursive member access."""),
+  FunctionType(List.filled(2, struct32bytesNestedInt), int64, """
+Even deeper nested struct to test recursive member access."""),
+  FunctionType(
+      [structNestedAlignmentInt16],
+      int64,
+      """
+Test alignment and padding of nested struct with 16 byte int."""),
+  FunctionType(
+      [structNestedAlignmentInt32],
+      int64,
+      """
+Test alignment and padding of nested struct with 32 byte int."""),
+  FunctionType(
+      [structNestedAlignmentInt64],
+      int64,
+      """
+Test alignment and padding of nested struct with 64 byte int."""),
+  FunctionType(List.filled(4, structNestedEvenBigger), double_, """
+Return big irregular struct as smoke test."""),
   FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
 Smallest struct with data."""),
   FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -358,6 +393,31 @@
 Test alignment and padding of 32 byte int within struct."""),
   FunctionType(structAlignmentInt64.memberTypes, structAlignmentInt64, """
 Test alignment and padding of 64 byte int within struct."""),
+  FunctionType(struct8bytesNestedInt.memberTypes, struct8bytesNestedInt, """
+Simple nested struct."""),
+  FunctionType(struct8bytesNestedFloat.memberTypes, struct8bytesNestedFloat, """
+Simple nested struct with floats."""),
+  FunctionType(
+      struct8bytesNestedFloat2.memberTypes, struct8bytesNestedFloat2, """
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+  FunctionType(struct8bytesNestedMixed.memberTypes, struct8bytesNestedMixed, """
+Simple nested struct with mixed members."""),
+  FunctionType(struct16bytesNestedInt.memberTypes, struct16bytesNestedInt, """
+Deeper nested struct to test recursive member access."""),
+  FunctionType(struct32bytesNestedInt.memberTypes, struct32bytesNestedInt, """
+Even deeper nested struct to test recursive member access."""),
+  FunctionType(
+      structNestedAlignmentInt16.memberTypes, structNestedAlignmentInt16, """
+Test alignment and padding of nested struct with 16 byte int."""),
+  FunctionType(
+      structNestedAlignmentInt32.memberTypes, structNestedAlignmentInt32, """
+Test alignment and padding of nested struct with 32 byte int."""),
+  FunctionType(
+      structNestedAlignmentInt64.memberTypes, structNestedAlignmentInt64, """
+Test alignment and padding of nested struct with 64 byte int."""),
+  FunctionType(structNestedEvenBigger.memberTypes, structNestedEvenBigger, """
+Return big irregular struct as smoke test."""),
 ];
 
 final structs = [
@@ -366,6 +426,7 @@
   struct3bytesInt,
   struct3bytesInt2,
   struct4bytesInt,
+  struct4bytesFloat,
   struct7bytesInt,
   struct7bytesInt2,
   struct8bytesInt,
@@ -387,6 +448,18 @@
   structAlignmentInt16,
   structAlignmentInt32,
   structAlignmentInt64,
+  struct8bytesNestedInt,
+  struct8bytesNestedFloat,
+  struct8bytesNestedFloat2,
+  struct8bytesNestedMixed,
+  struct16bytesNestedInt,
+  struct32bytesNestedInt,
+  structNestedAlignmentInt16,
+  structNestedAlignmentInt32,
+  structNestedAlignmentInt64,
+  structNestedBig,
+  structNestedBigger,
+  structNestedEvenBigger,
 ];
 
 /// Using empty structs is undefined behavior in C.
@@ -396,6 +469,7 @@
 final struct3bytesInt = StructType(List.filled(3, uint8));
 final struct3bytesInt2 = StructType.disambiguate([int16, int8], "2ByteAligned");
 final struct4bytesInt = StructType([int16, int16]);
+final struct4bytesFloat = StructType([float]);
 final struct7bytesInt = StructType(List.filled(7, uint8));
 final struct7bytesInt2 =
     StructType.disambiguate([int32, int16, int8], "4ByteAligned");
@@ -444,3 +518,39 @@
 final structAlignmentInt16 = StructType([int8, int16, int8]);
 final structAlignmentInt32 = StructType([int8, int32, int8]);
 final structAlignmentInt64 = StructType([int8, int64, int8]);
+
+final struct8bytesNestedInt = StructType([struct4bytesInt, struct4bytesInt]);
+final struct8bytesNestedFloat =
+    StructType([struct4bytesFloat, struct4bytesFloat]);
+final struct8bytesNestedFloat2 =
+    StructType.disambiguate([struct4bytesFloat, float], "2");
+final struct8bytesNestedMixed =
+    StructType([struct4bytesInt, struct4bytesFloat]);
+
+final struct16bytesNestedInt =
+    StructType([struct8bytesNestedInt, struct8bytesNestedInt]);
+final struct32bytesNestedInt =
+    StructType([struct16bytesNestedInt, struct16bytesNestedInt]);
+
+final structNestedAlignmentInt16 = StructType.disambiguate(
+    List.filled(2, structAlignmentInt16), structAlignmentInt16.name);
+final structNestedAlignmentInt32 = StructType.disambiguate(
+    List.filled(2, structAlignmentInt32), structAlignmentInt32.name);
+final structNestedAlignmentInt64 = StructType.disambiguate(
+    List.filled(2, structAlignmentInt64), structAlignmentInt64.name);
+
+final structNestedBig = StructType.override([
+  uint16,
+  struct8bytesNestedMixed,
+  uint16,
+  struct8bytesNestedFloat2,
+  uint16,
+  struct8bytesNestedFloat,
+  uint16
+], "NestedIrregularBig");
+final structNestedBigger = StructType.override(
+    [structNestedBig, struct8bytesNestedMixed, float, double_],
+    "NestedIrregularBigger");
+final structNestedEvenBigger = StructType.override(
+    [uint64, structNestedBigger, structNestedBigger, double_],
+    "NestedIrregularEvenBigger");
diff --git a/tests/ffi/structs_nested_test.dart b/tests/ffi/structs_nested_test.dart
new file mode 100644
index 0000000..ea4b6db
--- /dev/null
+++ b/tests/ffi/structs_nested_test.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// This tests the non trampoline aspects of nested structs.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import "package:ffi/ffi.dart";
+
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+void main() {
+  for (int i = 0; i < 10; ++i) {
+    testSizeOf();
+    testAllocate();
+    testRead();
+    testWrite();
+    testCopy();
+  }
+}
+
+class Struct4BytesHomogeneousInt16 extends Struct {
+  @Int16()
+  external int a0;
+
+  @Int16()
+  external int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedInt extends Struct {
+  external Struct4BytesHomogeneousInt16 a0;
+
+  external Struct4BytesHomogeneousInt16 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+void testSizeOf() {
+  print(sizeOf<Struct8BytesNestedInt>());
+  Expect.equals(8, sizeOf<Struct8BytesNestedInt>());
+}
+
+void testAllocate() {
+  final p = allocate<Struct8BytesNestedInt>();
+  Expect.type<Pointer<Struct8BytesNestedInt>>(p);
+  print(p);
+  free(p);
+}
+
+/// Test that reading does not segfault, even uninitialized.
+void testRead() {
+  print("read");
+  final p = allocate<Struct8BytesNestedInt>();
+  print(p);
+  print(p.ref.runtimeType);
+  print(p.ref.addressOf);
+  print(p.ref.addressOf.address);
+  print(p.ref.a0.runtimeType);
+  print(p.ref.a0.addressOf);
+  print(p.ref.a0.a0);
+  free(p);
+  print("read");
+}
+
+void testWrite() {
+  print("write");
+  final p = allocate<Struct8BytesNestedInt>(count: 2);
+  p[0].a0.a0 = 12;
+  p[0].a0.a1 = 13;
+  p[0].a1.a0 = 14;
+  p[0].a1.a1 = 15;
+  p[1].a0.a0 = 16;
+  p[1].a0.a1 = 17;
+  p[1].a1.a0 = 18;
+  p[1].a1.a1 = 19;
+  Expect.equals(12, p[0].a0.a0);
+  Expect.equals(13, p[0].a0.a1);
+  Expect.equals(14, p[0].a1.a0);
+  Expect.equals(15, p[0].a1.a1);
+  Expect.equals(16, p[1].a0.a0);
+  Expect.equals(17, p[1].a0.a1);
+  Expect.equals(18, p[1].a1.a0);
+  Expect.equals(19, p[1].a1.a1);
+  free(p);
+  print("written");
+}
+
+void testCopy() {
+  print("copy");
+  final p = allocate<Struct8BytesNestedInt>();
+  p.ref.a0.a0 = 12;
+  p.ref.a0.a1 = 13;
+  p.ref.a1 = p.ref.a0;
+  Expect.equals(12, p.ref.a1.a0);
+  Expect.equals(13, p.ref.a1.a1);
+  free(p);
+  print("copied");
+}
diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart
index b32acba..c77245e 100644
--- a/tests/ffi/vmspecific_static_checks_test.dart
+++ b/tests/ffi/vmspecific_static_checks_test.dart
@@ -526,3 +526,7 @@
   Pointer.fromFunction<EmptyStruct Function()>(//# 1105: compile-time error
       _returnEmptyStruct); //# 1105: compile-time error
 }
+
+class HasNestedEmptyStruct extends Struct {
+  external EmptyStruct nestedEmptyStruct; //# 1106: compile-time error
+}
diff --git a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
index 38714fd..bebd119 100644
--- a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
@@ -193,6 +193,56 @@
           passStructAlignmentInt64, 0),
       passStructAlignmentInt64AfterCallback),
   CallbackTest.withCheck(
+      "PassStruct8BytesNestedIntx10",
+      Pointer.fromFunction<PassStruct8BytesNestedIntx10Type>(
+          passStruct8BytesNestedIntx10, 0),
+      passStruct8BytesNestedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesNestedFloatx10",
+      Pointer.fromFunction<PassStruct8BytesNestedFloatx10Type>(
+          passStruct8BytesNestedFloatx10, 0.0),
+      passStruct8BytesNestedFloatx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesNestedFloat2x10",
+      Pointer.fromFunction<PassStruct8BytesNestedFloat2x10Type>(
+          passStruct8BytesNestedFloat2x10, 0.0),
+      passStruct8BytesNestedFloat2x10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesNestedMixedx10",
+      Pointer.fromFunction<PassStruct8BytesNestedMixedx10Type>(
+          passStruct8BytesNestedMixedx10, 0.0),
+      passStruct8BytesNestedMixedx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct16BytesNestedIntx2",
+      Pointer.fromFunction<PassStruct16BytesNestedIntx2Type>(
+          passStruct16BytesNestedIntx2, 0),
+      passStruct16BytesNestedIntx2AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct32BytesNestedIntx2",
+      Pointer.fromFunction<PassStruct32BytesNestedIntx2Type>(
+          passStruct32BytesNestedIntx2, 0),
+      passStruct32BytesNestedIntx2AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIntStructAlignmentInt16",
+      Pointer.fromFunction<PassStructNestedIntStructAlignmentInt16Type>(
+          passStructNestedIntStructAlignmentInt16, 0),
+      passStructNestedIntStructAlignmentInt16AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIntStructAlignmentInt32",
+      Pointer.fromFunction<PassStructNestedIntStructAlignmentInt32Type>(
+          passStructNestedIntStructAlignmentInt32, 0),
+      passStructNestedIntStructAlignmentInt32AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIntStructAlignmentInt64",
+      Pointer.fromFunction<PassStructNestedIntStructAlignmentInt64Type>(
+          passStructNestedIntStructAlignmentInt64, 0),
+      passStructNestedIntStructAlignmentInt64AfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedIrregularEvenBiggerx4",
+      Pointer.fromFunction<PassStructNestedIrregularEvenBiggerx4Type>(
+          passStructNestedIrregularEvenBiggerx4, 0.0),
+      passStructNestedIrregularEvenBiggerx4AfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -342,6 +392,56 @@
       Pointer.fromFunction<ReturnStructAlignmentInt64Type>(
           returnStructAlignmentInt64),
       returnStructAlignmentInt64AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedInt",
+      Pointer.fromFunction<ReturnStruct8BytesNestedIntType>(
+          returnStruct8BytesNestedInt),
+      returnStruct8BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedFloat",
+      Pointer.fromFunction<ReturnStruct8BytesNestedFloatType>(
+          returnStruct8BytesNestedFloat),
+      returnStruct8BytesNestedFloatAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedFloat2",
+      Pointer.fromFunction<ReturnStruct8BytesNestedFloat2Type>(
+          returnStruct8BytesNestedFloat2),
+      returnStruct8BytesNestedFloat2AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesNestedMixed",
+      Pointer.fromFunction<ReturnStruct8BytesNestedMixedType>(
+          returnStruct8BytesNestedMixed),
+      returnStruct8BytesNestedMixedAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct16BytesNestedInt",
+      Pointer.fromFunction<ReturnStruct16BytesNestedIntType>(
+          returnStruct16BytesNestedInt),
+      returnStruct16BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct32BytesNestedInt",
+      Pointer.fromFunction<ReturnStruct32BytesNestedIntType>(
+          returnStruct32BytesNestedInt),
+      returnStruct32BytesNestedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIntStructAlignmentInt16",
+      Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt16Type>(
+          returnStructNestedIntStructAlignmentInt16),
+      returnStructNestedIntStructAlignmentInt16AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIntStructAlignmentInt32",
+      Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt32Type>(
+          returnStructNestedIntStructAlignmentInt32),
+      returnStructNestedIntStructAlignmentInt32AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIntStructAlignmentInt64",
+      Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt64Type>(
+          returnStructNestedIntStructAlignmentInt64),
+      returnStructNestedIntStructAlignmentInt64AfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStructNestedIrregularEvenBigger",
+      Pointer.fromFunction<ReturnStructNestedIrregularEvenBiggerType>(
+          returnStructNestedIrregularEvenBigger),
+      returnStructNestedIrregularEvenBiggerAfterCallback),
 ];
 typedef PassStruct1ByteIntx10Type = Int64 Function(
     Struct1ByteInt,
@@ -4256,6 +4356,1053 @@
   Expect.equals(-2, result);
 }
 
+typedef PassStruct8BytesNestedIntx10Type = Int64 Function(
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt,
+    Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a1 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a2 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a3 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a4 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a5 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a6 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a7 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a8 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a9 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesNestedIntx10Result = 0;
+
+int passStruct8BytesNestedIntx10CalculateResult() {
+  int result = 0;
+
+  result += passStruct8BytesNestedIntx10_a0.a0.a0;
+  result += passStruct8BytesNestedIntx10_a0.a0.a1;
+  result += passStruct8BytesNestedIntx10_a0.a1.a0;
+  result += passStruct8BytesNestedIntx10_a0.a1.a1;
+  result += passStruct8BytesNestedIntx10_a1.a0.a0;
+  result += passStruct8BytesNestedIntx10_a1.a0.a1;
+  result += passStruct8BytesNestedIntx10_a1.a1.a0;
+  result += passStruct8BytesNestedIntx10_a1.a1.a1;
+  result += passStruct8BytesNestedIntx10_a2.a0.a0;
+  result += passStruct8BytesNestedIntx10_a2.a0.a1;
+  result += passStruct8BytesNestedIntx10_a2.a1.a0;
+  result += passStruct8BytesNestedIntx10_a2.a1.a1;
+  result += passStruct8BytesNestedIntx10_a3.a0.a0;
+  result += passStruct8BytesNestedIntx10_a3.a0.a1;
+  result += passStruct8BytesNestedIntx10_a3.a1.a0;
+  result += passStruct8BytesNestedIntx10_a3.a1.a1;
+  result += passStruct8BytesNestedIntx10_a4.a0.a0;
+  result += passStruct8BytesNestedIntx10_a4.a0.a1;
+  result += passStruct8BytesNestedIntx10_a4.a1.a0;
+  result += passStruct8BytesNestedIntx10_a4.a1.a1;
+  result += passStruct8BytesNestedIntx10_a5.a0.a0;
+  result += passStruct8BytesNestedIntx10_a5.a0.a1;
+  result += passStruct8BytesNestedIntx10_a5.a1.a0;
+  result += passStruct8BytesNestedIntx10_a5.a1.a1;
+  result += passStruct8BytesNestedIntx10_a6.a0.a0;
+  result += passStruct8BytesNestedIntx10_a6.a0.a1;
+  result += passStruct8BytesNestedIntx10_a6.a1.a0;
+  result += passStruct8BytesNestedIntx10_a6.a1.a1;
+  result += passStruct8BytesNestedIntx10_a7.a0.a0;
+  result += passStruct8BytesNestedIntx10_a7.a0.a1;
+  result += passStruct8BytesNestedIntx10_a7.a1.a0;
+  result += passStruct8BytesNestedIntx10_a7.a1.a1;
+  result += passStruct8BytesNestedIntx10_a8.a0.a0;
+  result += passStruct8BytesNestedIntx10_a8.a0.a1;
+  result += passStruct8BytesNestedIntx10_a8.a1.a0;
+  result += passStruct8BytesNestedIntx10_a8.a1.a1;
+  result += passStruct8BytesNestedIntx10_a9.a0.a0;
+  result += passStruct8BytesNestedIntx10_a9.a0.a1;
+  result += passStruct8BytesNestedIntx10_a9.a1.a0;
+  result += passStruct8BytesNestedIntx10_a9.a1.a1;
+
+  passStruct8BytesNestedIntx10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+int passStruct8BytesNestedIntx10(
+    Struct8BytesNestedInt a0,
+    Struct8BytesNestedInt a1,
+    Struct8BytesNestedInt a2,
+    Struct8BytesNestedInt a3,
+    Struct8BytesNestedInt a4,
+    Struct8BytesNestedInt a5,
+    Struct8BytesNestedInt a6,
+    Struct8BytesNestedInt a7,
+    Struct8BytesNestedInt a8,
+    Struct8BytesNestedInt a9) {
+  print(
+      "passStruct8BytesNestedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedIntx10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedIntx10_a0 = a0;
+  passStruct8BytesNestedIntx10_a1 = a1;
+  passStruct8BytesNestedIntx10_a2 = a2;
+  passStruct8BytesNestedIntx10_a3 = a3;
+  passStruct8BytesNestedIntx10_a4 = a4;
+  passStruct8BytesNestedIntx10_a5 = a5;
+  passStruct8BytesNestedIntx10_a6 = a6;
+  passStruct8BytesNestedIntx10_a7 = a7;
+  passStruct8BytesNestedIntx10_a8 = a8;
+  passStruct8BytesNestedIntx10_a9 = a9;
+
+  final result = passStruct8BytesNestedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedIntx10AfterCallback() {
+  final result = passStruct8BytesNestedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(20, result);
+}
+
+typedef PassStruct8BytesNestedFloatx10Type = Float Function(
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat,
+    Struct8BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a0 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a1 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a2 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a3 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a4 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a5 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a6 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a7 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a8 =
+    Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a9 =
+    Struct8BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloatx10Result = 0.0;
+
+double passStruct8BytesNestedFloatx10CalculateResult() {
+  double result = 0;
+
+  result += passStruct8BytesNestedFloatx10_a0.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a0.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a1.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a1.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a2.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a2.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a3.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a3.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a4.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a4.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a5.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a5.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a6.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a6.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a7.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a7.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a8.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a8.a1.a0;
+  result += passStruct8BytesNestedFloatx10_a9.a0.a0;
+  result += passStruct8BytesNestedFloatx10_a9.a1.a0;
+
+  passStruct8BytesNestedFloatx10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+double passStruct8BytesNestedFloatx10(
+    Struct8BytesNestedFloat a0,
+    Struct8BytesNestedFloat a1,
+    Struct8BytesNestedFloat a2,
+    Struct8BytesNestedFloat a3,
+    Struct8BytesNestedFloat a4,
+    Struct8BytesNestedFloat a5,
+    Struct8BytesNestedFloat a6,
+    Struct8BytesNestedFloat a7,
+    Struct8BytesNestedFloat a8,
+    Struct8BytesNestedFloat a9) {
+  print(
+      "passStruct8BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedFloatx10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedFloatx10_a0 = a0;
+  passStruct8BytesNestedFloatx10_a1 = a1;
+  passStruct8BytesNestedFloatx10_a2 = a2;
+  passStruct8BytesNestedFloatx10_a3 = a3;
+  passStruct8BytesNestedFloatx10_a4 = a4;
+  passStruct8BytesNestedFloatx10_a5 = a5;
+  passStruct8BytesNestedFloatx10_a6 = a6;
+  passStruct8BytesNestedFloatx10_a7 = a7;
+  passStruct8BytesNestedFloatx10_a8 = a8;
+  passStruct8BytesNestedFloatx10_a9 = a9;
+
+  final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedFloatx10AfterCallback() {
+  final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedFloat2x10Type = Float Function(
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2,
+    Struct8BytesNestedFloat2);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a0 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a1 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a2 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a3 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a4 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a5 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a6 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a7 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a8 =
+    Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a9 =
+    Struct8BytesNestedFloat2();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloat2x10Result = 0.0;
+
+double passStruct8BytesNestedFloat2x10CalculateResult() {
+  double result = 0;
+
+  result += passStruct8BytesNestedFloat2x10_a0.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a0.a1;
+  result += passStruct8BytesNestedFloat2x10_a1.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a1.a1;
+  result += passStruct8BytesNestedFloat2x10_a2.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a2.a1;
+  result += passStruct8BytesNestedFloat2x10_a3.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a3.a1;
+  result += passStruct8BytesNestedFloat2x10_a4.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a4.a1;
+  result += passStruct8BytesNestedFloat2x10_a5.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a5.a1;
+  result += passStruct8BytesNestedFloat2x10_a6.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a6.a1;
+  result += passStruct8BytesNestedFloat2x10_a7.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a7.a1;
+  result += passStruct8BytesNestedFloat2x10_a8.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a8.a1;
+  result += passStruct8BytesNestedFloat2x10_a9.a0.a0;
+  result += passStruct8BytesNestedFloat2x10_a9.a1;
+
+  passStruct8BytesNestedFloat2x10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+double passStruct8BytesNestedFloat2x10(
+    Struct8BytesNestedFloat2 a0,
+    Struct8BytesNestedFloat2 a1,
+    Struct8BytesNestedFloat2 a2,
+    Struct8BytesNestedFloat2 a3,
+    Struct8BytesNestedFloat2 a4,
+    Struct8BytesNestedFloat2 a5,
+    Struct8BytesNestedFloat2 a6,
+    Struct8BytesNestedFloat2 a7,
+    Struct8BytesNestedFloat2 a8,
+    Struct8BytesNestedFloat2 a9) {
+  print(
+      "passStruct8BytesNestedFloat2x10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedFloat2x10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedFloat2x10_a0 = a0;
+  passStruct8BytesNestedFloat2x10_a1 = a1;
+  passStruct8BytesNestedFloat2x10_a2 = a2;
+  passStruct8BytesNestedFloat2x10_a3 = a3;
+  passStruct8BytesNestedFloat2x10_a4 = a4;
+  passStruct8BytesNestedFloat2x10_a5 = a5;
+  passStruct8BytesNestedFloat2x10_a6 = a6;
+  passStruct8BytesNestedFloat2x10_a7 = a7;
+  passStruct8BytesNestedFloat2x10_a8 = a8;
+  passStruct8BytesNestedFloat2x10_a9 = a9;
+
+  final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedFloat2x10AfterCallback() {
+  final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedMixedx10Type = Double Function(
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed,
+    Struct8BytesNestedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a0 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a1 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a2 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a3 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a4 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a5 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a6 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a7 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a8 =
+    Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a9 =
+    Struct8BytesNestedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedMixedx10Result = 0.0;
+
+double passStruct8BytesNestedMixedx10CalculateResult() {
+  double result = 0;
+
+  result += passStruct8BytesNestedMixedx10_a0.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a0.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a0.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a1.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a1.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a1.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a2.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a2.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a2.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a3.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a3.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a3.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a4.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a4.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a4.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a5.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a5.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a5.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a6.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a6.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a6.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a7.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a7.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a7.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a8.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a8.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a8.a1.a0;
+  result += passStruct8BytesNestedMixedx10_a9.a0.a0;
+  result += passStruct8BytesNestedMixedx10_a9.a0.a1;
+  result += passStruct8BytesNestedMixedx10_a9.a1.a0;
+
+  passStruct8BytesNestedMixedx10Result = result;
+
+  return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+double passStruct8BytesNestedMixedx10(
+    Struct8BytesNestedMixed a0,
+    Struct8BytesNestedMixed a1,
+    Struct8BytesNestedMixed a2,
+    Struct8BytesNestedMixed a3,
+    Struct8BytesNestedMixed a4,
+    Struct8BytesNestedMixed a5,
+    Struct8BytesNestedMixed a6,
+    Struct8BytesNestedMixed a7,
+    Struct8BytesNestedMixed a8,
+    Struct8BytesNestedMixed a9) {
+  print(
+      "passStruct8BytesNestedMixedx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesNestedMixedx10 throwing on purpuse!");
+  }
+
+  passStruct8BytesNestedMixedx10_a0 = a0;
+  passStruct8BytesNestedMixedx10_a1 = a1;
+  passStruct8BytesNestedMixedx10_a2 = a2;
+  passStruct8BytesNestedMixedx10_a3 = a3;
+  passStruct8BytesNestedMixedx10_a4 = a4;
+  passStruct8BytesNestedMixedx10_a5 = a5;
+  passStruct8BytesNestedMixedx10_a6 = a6;
+  passStruct8BytesNestedMixedx10_a7 = a7;
+  passStruct8BytesNestedMixedx10_a8 = a8;
+  passStruct8BytesNestedMixedx10_a9 = a9;
+
+  final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesNestedMixedx10AfterCallback() {
+  final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(15.0, result);
+}
+
+typedef PassStruct16BytesNestedIntx2Type = Int64 Function(
+    Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a0 =
+    Struct16BytesNestedInt();
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a1 =
+    Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct16BytesNestedIntx2Result = 0;
+
+int passStruct16BytesNestedIntx2CalculateResult() {
+  int result = 0;
+
+  result += passStruct16BytesNestedIntx2_a0.a0.a0.a0;
+  result += passStruct16BytesNestedIntx2_a0.a0.a0.a1;
+  result += passStruct16BytesNestedIntx2_a0.a0.a1.a0;
+  result += passStruct16BytesNestedIntx2_a0.a0.a1.a1;
+  result += passStruct16BytesNestedIntx2_a0.a1.a0.a0;
+  result += passStruct16BytesNestedIntx2_a0.a1.a0.a1;
+  result += passStruct16BytesNestedIntx2_a0.a1.a1.a0;
+  result += passStruct16BytesNestedIntx2_a0.a1.a1.a1;
+  result += passStruct16BytesNestedIntx2_a1.a0.a0.a0;
+  result += passStruct16BytesNestedIntx2_a1.a0.a0.a1;
+  result += passStruct16BytesNestedIntx2_a1.a0.a1.a0;
+  result += passStruct16BytesNestedIntx2_a1.a0.a1.a1;
+  result += passStruct16BytesNestedIntx2_a1.a1.a0.a0;
+  result += passStruct16BytesNestedIntx2_a1.a1.a0.a1;
+  result += passStruct16BytesNestedIntx2_a1.a1.a1.a0;
+  result += passStruct16BytesNestedIntx2_a1.a1.a1.a1;
+
+  passStruct16BytesNestedIntx2Result = result;
+
+  return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+int passStruct16BytesNestedIntx2(
+    Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+  print("passStruct16BytesNestedIntx2(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct16BytesNestedIntx2 throwing on purpuse!");
+  }
+
+  passStruct16BytesNestedIntx2_a0 = a0;
+  passStruct16BytesNestedIntx2_a1 = a1;
+
+  final result = passStruct16BytesNestedIntx2CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct16BytesNestedIntx2AfterCallback() {
+  final result = passStruct16BytesNestedIntx2CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(8, result);
+}
+
+typedef PassStruct32BytesNestedIntx2Type = Int64 Function(
+    Struct32BytesNestedInt, Struct32BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a0 =
+    Struct32BytesNestedInt();
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a1 =
+    Struct32BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct32BytesNestedIntx2Result = 0;
+
+int passStruct32BytesNestedIntx2CalculateResult() {
+  int result = 0;
+
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a1;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a0;
+  result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a1;
+
+  passStruct32BytesNestedIntx2Result = result;
+
+  return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+int passStruct32BytesNestedIntx2(
+    Struct32BytesNestedInt a0, Struct32BytesNestedInt a1) {
+  print("passStruct32BytesNestedIntx2(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0.a0.a0 == 42 || a0.a0.a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct32BytesNestedIntx2 throwing on purpuse!");
+  }
+
+  passStruct32BytesNestedIntx2_a0 = a0;
+  passStruct32BytesNestedIntx2_a1 = a1;
+
+  final result = passStruct32BytesNestedIntx2CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct32BytesNestedIntx2AfterCallback() {
+  final result = passStruct32BytesNestedIntx2CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(16, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt16Type = Int64 Function(
+    StructNestedIntStructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt16 passStructNestedIntStructAlignmentInt16_a0 =
+    StructNestedIntStructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt16Result = 0;
+
+int passStructNestedIntStructAlignmentInt16CalculateResult() {
+  int result = 0;
+
+  result += passStructNestedIntStructAlignmentInt16_a0.a0.a0;
+  result += passStructNestedIntStructAlignmentInt16_a0.a0.a1;
+  result += passStructNestedIntStructAlignmentInt16_a0.a0.a2;
+  result += passStructNestedIntStructAlignmentInt16_a0.a1.a0;
+  result += passStructNestedIntStructAlignmentInt16_a0.a1.a1;
+  result += passStructNestedIntStructAlignmentInt16_a0.a1.a2;
+
+  passStructNestedIntStructAlignmentInt16Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+int passStructNestedIntStructAlignmentInt16(
+    StructNestedIntStructAlignmentInt16 a0) {
+  print("passStructNestedIntStructAlignmentInt16(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+  }
+
+  passStructNestedIntStructAlignmentInt16_a0 = a0;
+
+  final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIntStructAlignmentInt16AfterCallback() {
+  final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt32Type = Int64 Function(
+    StructNestedIntStructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt32 passStructNestedIntStructAlignmentInt32_a0 =
+    StructNestedIntStructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt32Result = 0;
+
+int passStructNestedIntStructAlignmentInt32CalculateResult() {
+  int result = 0;
+
+  result += passStructNestedIntStructAlignmentInt32_a0.a0.a0;
+  result += passStructNestedIntStructAlignmentInt32_a0.a0.a1;
+  result += passStructNestedIntStructAlignmentInt32_a0.a0.a2;
+  result += passStructNestedIntStructAlignmentInt32_a0.a1.a0;
+  result += passStructNestedIntStructAlignmentInt32_a0.a1.a1;
+  result += passStructNestedIntStructAlignmentInt32_a0.a1.a2;
+
+  passStructNestedIntStructAlignmentInt32Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+int passStructNestedIntStructAlignmentInt32(
+    StructNestedIntStructAlignmentInt32 a0) {
+  print("passStructNestedIntStructAlignmentInt32(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+  }
+
+  passStructNestedIntStructAlignmentInt32_a0 = a0;
+
+  final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIntStructAlignmentInt32AfterCallback() {
+  final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt64Type = Int64 Function(
+    StructNestedIntStructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt64 passStructNestedIntStructAlignmentInt64_a0 =
+    StructNestedIntStructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt64Result = 0;
+
+int passStructNestedIntStructAlignmentInt64CalculateResult() {
+  int result = 0;
+
+  result += passStructNestedIntStructAlignmentInt64_a0.a0.a0;
+  result += passStructNestedIntStructAlignmentInt64_a0.a0.a1;
+  result += passStructNestedIntStructAlignmentInt64_a0.a0.a2;
+  result += passStructNestedIntStructAlignmentInt64_a0.a1.a0;
+  result += passStructNestedIntStructAlignmentInt64_a0.a1.a1;
+  result += passStructNestedIntStructAlignmentInt64_a0.a1.a2;
+
+  passStructNestedIntStructAlignmentInt64Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+int passStructNestedIntStructAlignmentInt64(
+    StructNestedIntStructAlignmentInt64 a0) {
+  print("passStructNestedIntStructAlignmentInt64(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+  }
+
+  passStructNestedIntStructAlignmentInt64_a0 = a0;
+
+  final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIntStructAlignmentInt64AfterCallback() {
+  final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(3, result);
+}
+
+typedef PassStructNestedIrregularEvenBiggerx4Type = Double Function(
+    StructNestedIrregularEvenBigger,
+    StructNestedIrregularEvenBigger,
+    StructNestedIrregularEvenBigger,
+    StructNestedIrregularEvenBigger);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a0 =
+    StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a1 =
+    StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a2 =
+    StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a3 =
+    StructNestedIrregularEvenBigger();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedIrregularEvenBiggerx4Result = 0.0;
+
+double passStructNestedIrregularEvenBiggerx4CalculateResult() {
+  double result = 0;
+
+  result += passStructNestedIrregularEvenBiggerx4_a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a0.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a1.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a4;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a6;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a1;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a1.a0;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a2;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a2.a3;
+  result += passStructNestedIrregularEvenBiggerx4_a3.a3;
+
+  passStructNestedIrregularEvenBiggerx4Result = result;
+
+  return result;
+}
+
+/// Return big irregular struct as smoke test.
+double passStructNestedIrregularEvenBiggerx4(
+    StructNestedIrregularEvenBigger a0,
+    StructNestedIrregularEvenBigger a1,
+    StructNestedIrregularEvenBigger a2,
+    StructNestedIrregularEvenBigger a3) {
+  print("passStructNestedIrregularEvenBiggerx4(${a0}, ${a1}, ${a2}, ${a3})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassStructNestedIrregularEvenBiggerx4 throwing on purpuse!");
+  }
+
+  passStructNestedIrregularEvenBiggerx4_a0 = a0;
+  passStructNestedIrregularEvenBiggerx4_a1 = a1;
+  passStructNestedIrregularEvenBiggerx4_a2 = a2;
+  passStructNestedIrregularEvenBiggerx4_a3 = a3;
+
+  final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedIrregularEvenBiggerx4AfterCallback() {
+  final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(1572.0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
@@ -6914,3 +8061,697 @@
 
   free(returnStructAlignmentInt64Result.addressOf);
 }
+
+typedef ReturnStruct8BytesNestedIntType = Struct8BytesNestedInt Function(
+    Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a0 =
+    Struct4BytesHomogeneousInt16();
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a1 =
+    Struct4BytesHomogeneousInt16();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedInt returnStruct8BytesNestedIntResult =
+    Struct8BytesNestedInt();
+
+Struct8BytesNestedInt returnStruct8BytesNestedIntCalculateResult() {
+  Struct8BytesNestedInt result = allocate<Struct8BytesNestedInt>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedInt_a0.a0;
+  result.a0.a1 = returnStruct8BytesNestedInt_a0.a1;
+  result.a1.a0 = returnStruct8BytesNestedInt_a1.a0;
+  result.a1.a1 = returnStruct8BytesNestedInt_a1.a1;
+
+  returnStruct8BytesNestedIntResult = result;
+
+  return result;
+}
+
+/// Simple nested struct.
+Struct8BytesNestedInt returnStruct8BytesNestedInt(
+    Struct4BytesHomogeneousInt16 a0, Struct4BytesHomogeneousInt16 a1) {
+  print("returnStruct8BytesNestedInt(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedInt throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedInt_a0 = a0;
+  returnStruct8BytesNestedInt_a1 = a1;
+
+  final result = returnStruct8BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedIntAfterCallback() {
+  free(returnStruct8BytesNestedIntResult.addressOf);
+
+  final result = returnStruct8BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloatType = Struct8BytesNestedFloat Function(
+    Struct4BytesFloat, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat_a0 = Struct4BytesFloat();
+Struct4BytesFloat returnStruct8BytesNestedFloat_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatResult =
+    Struct8BytesNestedFloat();
+
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatCalculateResult() {
+  Struct8BytesNestedFloat result = allocate<Struct8BytesNestedFloat>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedFloat_a0.a0;
+  result.a1.a0 = returnStruct8BytesNestedFloat_a1.a0;
+
+  returnStruct8BytesNestedFloatResult = result;
+
+  return result;
+}
+
+/// Simple nested struct with floats.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloat(
+    Struct4BytesFloat a0, Struct4BytesFloat a1) {
+  print("returnStruct8BytesNestedFloat(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedFloat throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedFloat_a0 = a0;
+  returnStruct8BytesNestedFloat_a1 = a1;
+
+  final result = returnStruct8BytesNestedFloatCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedFloatAfterCallback() {
+  free(returnStruct8BytesNestedFloatResult.addressOf);
+
+  final result = returnStruct8BytesNestedFloatCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedFloatResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloat2Type = Struct8BytesNestedFloat2 Function(
+    Struct4BytesFloat, Float);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat2_a0 = Struct4BytesFloat();
+double returnStruct8BytesNestedFloat2_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2Result =
+    Struct8BytesNestedFloat2();
+
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2CalculateResult() {
+  Struct8BytesNestedFloat2 result = allocate<Struct8BytesNestedFloat2>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedFloat2_a0.a0;
+  result.a1 = returnStruct8BytesNestedFloat2_a1;
+
+  returnStruct8BytesNestedFloat2Result = result;
+
+  return result;
+}
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2(
+    Struct4BytesFloat a0, double a1) {
+  print("returnStruct8BytesNestedFloat2(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedFloat2 throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedFloat2_a0 = a0;
+  returnStruct8BytesNestedFloat2_a1 = a1;
+
+  final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedFloat2AfterCallback() {
+  free(returnStruct8BytesNestedFloat2Result.addressOf);
+
+  final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedFloat2Result.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedMixedType = Struct8BytesNestedMixed Function(
+    Struct4BytesHomogeneousInt16, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedMixed_a0 =
+    Struct4BytesHomogeneousInt16();
+Struct4BytesFloat returnStruct8BytesNestedMixed_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedResult =
+    Struct8BytesNestedMixed();
+
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedCalculateResult() {
+  Struct8BytesNestedMixed result = allocate<Struct8BytesNestedMixed>().ref;
+
+  result.a0.a0 = returnStruct8BytesNestedMixed_a0.a0;
+  result.a0.a1 = returnStruct8BytesNestedMixed_a0.a1;
+  result.a1.a0 = returnStruct8BytesNestedMixed_a1.a0;
+
+  returnStruct8BytesNestedMixedResult = result;
+
+  return result;
+}
+
+/// Simple nested struct with mixed members.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixed(
+    Struct4BytesHomogeneousInt16 a0, Struct4BytesFloat a1) {
+  print("returnStruct8BytesNestedMixed(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesNestedMixed throwing on purpuse!");
+  }
+
+  returnStruct8BytesNestedMixed_a0 = a0;
+  returnStruct8BytesNestedMixed_a1 = a1;
+
+  final result = returnStruct8BytesNestedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesNestedMixedAfterCallback() {
+  free(returnStruct8BytesNestedMixedResult.addressOf);
+
+  final result = returnStruct8BytesNestedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct8BytesNestedMixedResult.addressOf);
+}
+
+typedef ReturnStruct16BytesNestedIntType = Struct16BytesNestedInt Function(
+    Struct8BytesNestedInt, Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a1 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct16BytesNestedInt returnStruct16BytesNestedIntResult =
+    Struct16BytesNestedInt();
+
+Struct16BytesNestedInt returnStruct16BytesNestedIntCalculateResult() {
+  Struct16BytesNestedInt result = allocate<Struct16BytesNestedInt>().ref;
+
+  result.a0.a0.a0 = returnStruct16BytesNestedInt_a0.a0.a0;
+  result.a0.a0.a1 = returnStruct16BytesNestedInt_a0.a0.a1;
+  result.a0.a1.a0 = returnStruct16BytesNestedInt_a0.a1.a0;
+  result.a0.a1.a1 = returnStruct16BytesNestedInt_a0.a1.a1;
+  result.a1.a0.a0 = returnStruct16BytesNestedInt_a1.a0.a0;
+  result.a1.a0.a1 = returnStruct16BytesNestedInt_a1.a0.a1;
+  result.a1.a1.a0 = returnStruct16BytesNestedInt_a1.a1.a0;
+  result.a1.a1.a1 = returnStruct16BytesNestedInt_a1.a1.a1;
+
+  returnStruct16BytesNestedIntResult = result;
+
+  return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+Struct16BytesNestedInt returnStruct16BytesNestedInt(
+    Struct8BytesNestedInt a0, Struct8BytesNestedInt a1) {
+  print("returnStruct16BytesNestedInt(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct16BytesNestedInt throwing on purpuse!");
+  }
+
+  returnStruct16BytesNestedInt_a0 = a0;
+  returnStruct16BytesNestedInt_a1 = a1;
+
+  final result = returnStruct16BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct16BytesNestedIntAfterCallback() {
+  free(returnStruct16BytesNestedIntResult.addressOf);
+
+  final result = returnStruct16BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct16BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct32BytesNestedIntType = Struct32BytesNestedInt Function(
+    Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a0 =
+    Struct16BytesNestedInt();
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a1 =
+    Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct32BytesNestedInt returnStruct32BytesNestedIntResult =
+    Struct32BytesNestedInt();
+
+Struct32BytesNestedInt returnStruct32BytesNestedIntCalculateResult() {
+  Struct32BytesNestedInt result = allocate<Struct32BytesNestedInt>().ref;
+
+  result.a0.a0.a0.a0 = returnStruct32BytesNestedInt_a0.a0.a0.a0;
+  result.a0.a0.a0.a1 = returnStruct32BytesNestedInt_a0.a0.a0.a1;
+  result.a0.a0.a1.a0 = returnStruct32BytesNestedInt_a0.a0.a1.a0;
+  result.a0.a0.a1.a1 = returnStruct32BytesNestedInt_a0.a0.a1.a1;
+  result.a0.a1.a0.a0 = returnStruct32BytesNestedInt_a0.a1.a0.a0;
+  result.a0.a1.a0.a1 = returnStruct32BytesNestedInt_a0.a1.a0.a1;
+  result.a0.a1.a1.a0 = returnStruct32BytesNestedInt_a0.a1.a1.a0;
+  result.a0.a1.a1.a1 = returnStruct32BytesNestedInt_a0.a1.a1.a1;
+  result.a1.a0.a0.a0 = returnStruct32BytesNestedInt_a1.a0.a0.a0;
+  result.a1.a0.a0.a1 = returnStruct32BytesNestedInt_a1.a0.a0.a1;
+  result.a1.a0.a1.a0 = returnStruct32BytesNestedInt_a1.a0.a1.a0;
+  result.a1.a0.a1.a1 = returnStruct32BytesNestedInt_a1.a0.a1.a1;
+  result.a1.a1.a0.a0 = returnStruct32BytesNestedInt_a1.a1.a0.a0;
+  result.a1.a1.a0.a1 = returnStruct32BytesNestedInt_a1.a1.a0.a1;
+  result.a1.a1.a1.a0 = returnStruct32BytesNestedInt_a1.a1.a1.a0;
+  result.a1.a1.a1.a1 = returnStruct32BytesNestedInt_a1.a1.a1.a1;
+
+  returnStruct32BytesNestedIntResult = result;
+
+  return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+Struct32BytesNestedInt returnStruct32BytesNestedInt(
+    Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+  print("returnStruct32BytesNestedInt(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0.a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct32BytesNestedInt throwing on purpuse!");
+  }
+
+  returnStruct32BytesNestedInt_a0 = a0;
+  returnStruct32BytesNestedInt_a1 = a1;
+
+  final result = returnStruct32BytesNestedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct32BytesNestedIntAfterCallback() {
+  free(returnStruct32BytesNestedIntResult.addressOf);
+
+  final result = returnStruct32BytesNestedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStruct32BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt16Type
+    = StructNestedIntStructAlignmentInt16 Function(
+        StructAlignmentInt16, StructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a0 =
+    StructAlignmentInt16();
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a1 =
+    StructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt16
+    returnStructNestedIntStructAlignmentInt16Result =
+    StructNestedIntStructAlignmentInt16();
+
+StructNestedIntStructAlignmentInt16
+    returnStructNestedIntStructAlignmentInt16CalculateResult() {
+  StructNestedIntStructAlignmentInt16 result =
+      allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+  result.a0.a0 = returnStructNestedIntStructAlignmentInt16_a0.a0;
+  result.a0.a1 = returnStructNestedIntStructAlignmentInt16_a0.a1;
+  result.a0.a2 = returnStructNestedIntStructAlignmentInt16_a0.a2;
+  result.a1.a0 = returnStructNestedIntStructAlignmentInt16_a1.a0;
+  result.a1.a1 = returnStructNestedIntStructAlignmentInt16_a1.a1;
+  result.a1.a2 = returnStructNestedIntStructAlignmentInt16_a1.a2;
+
+  returnStructNestedIntStructAlignmentInt16Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+StructNestedIntStructAlignmentInt16 returnStructNestedIntStructAlignmentInt16(
+    StructAlignmentInt16 a0, StructAlignmentInt16 a1) {
+  print("returnStructNestedIntStructAlignmentInt16(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+  }
+
+  returnStructNestedIntStructAlignmentInt16_a0 = a0;
+  returnStructNestedIntStructAlignmentInt16_a1 = a1;
+
+  final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIntStructAlignmentInt16AfterCallback() {
+  free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+
+  final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt32Type
+    = StructNestedIntStructAlignmentInt32 Function(
+        StructAlignmentInt32, StructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a0 =
+    StructAlignmentInt32();
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a1 =
+    StructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt32
+    returnStructNestedIntStructAlignmentInt32Result =
+    StructNestedIntStructAlignmentInt32();
+
+StructNestedIntStructAlignmentInt32
+    returnStructNestedIntStructAlignmentInt32CalculateResult() {
+  StructNestedIntStructAlignmentInt32 result =
+      allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+  result.a0.a0 = returnStructNestedIntStructAlignmentInt32_a0.a0;
+  result.a0.a1 = returnStructNestedIntStructAlignmentInt32_a0.a1;
+  result.a0.a2 = returnStructNestedIntStructAlignmentInt32_a0.a2;
+  result.a1.a0 = returnStructNestedIntStructAlignmentInt32_a1.a0;
+  result.a1.a1 = returnStructNestedIntStructAlignmentInt32_a1.a1;
+  result.a1.a2 = returnStructNestedIntStructAlignmentInt32_a1.a2;
+
+  returnStructNestedIntStructAlignmentInt32Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+StructNestedIntStructAlignmentInt32 returnStructNestedIntStructAlignmentInt32(
+    StructAlignmentInt32 a0, StructAlignmentInt32 a1) {
+  print("returnStructNestedIntStructAlignmentInt32(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+  }
+
+  returnStructNestedIntStructAlignmentInt32_a0 = a0;
+  returnStructNestedIntStructAlignmentInt32_a1 = a1;
+
+  final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIntStructAlignmentInt32AfterCallback() {
+  free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+
+  final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt64Type
+    = StructNestedIntStructAlignmentInt64 Function(
+        StructAlignmentInt64, StructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a0 =
+    StructAlignmentInt64();
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a1 =
+    StructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt64
+    returnStructNestedIntStructAlignmentInt64Result =
+    StructNestedIntStructAlignmentInt64();
+
+StructNestedIntStructAlignmentInt64
+    returnStructNestedIntStructAlignmentInt64CalculateResult() {
+  StructNestedIntStructAlignmentInt64 result =
+      allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+  result.a0.a0 = returnStructNestedIntStructAlignmentInt64_a0.a0;
+  result.a0.a1 = returnStructNestedIntStructAlignmentInt64_a0.a1;
+  result.a0.a2 = returnStructNestedIntStructAlignmentInt64_a0.a2;
+  result.a1.a0 = returnStructNestedIntStructAlignmentInt64_a1.a0;
+  result.a1.a1 = returnStructNestedIntStructAlignmentInt64_a1.a1;
+  result.a1.a2 = returnStructNestedIntStructAlignmentInt64_a1.a2;
+
+  returnStructNestedIntStructAlignmentInt64Result = result;
+
+  return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+StructNestedIntStructAlignmentInt64 returnStructNestedIntStructAlignmentInt64(
+    StructAlignmentInt64 a0, StructAlignmentInt64 a1) {
+  print("returnStructNestedIntStructAlignmentInt64(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+  }
+
+  returnStructNestedIntStructAlignmentInt64_a0 = a0;
+  returnStructNestedIntStructAlignmentInt64_a1 = a1;
+
+  final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIntStructAlignmentInt64AfterCallback() {
+  free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+
+  final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+}
+
+typedef ReturnStructNestedIrregularEvenBiggerType
+    = StructNestedIrregularEvenBigger Function(Uint64,
+        StructNestedIrregularBigger, StructNestedIrregularBigger, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStructNestedIrregularEvenBigger_a0 = 0;
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a1 =
+    StructNestedIrregularBigger();
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a2 =
+    StructNestedIrregularBigger();
+double returnStructNestedIrregularEvenBigger_a3 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBiggerResult =
+    StructNestedIrregularEvenBigger();
+
+StructNestedIrregularEvenBigger
+    returnStructNestedIrregularEvenBiggerCalculateResult() {
+  StructNestedIrregularEvenBigger result =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+
+  result.a0 = returnStructNestedIrregularEvenBigger_a0;
+  result.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a0;
+  result.a1.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a0;
+  result.a1.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a1;
+  result.a1.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a1.a0;
+  result.a1.a0.a2 = returnStructNestedIrregularEvenBigger_a1.a0.a2;
+  result.a1.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a0.a0;
+  result.a1.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a1;
+  result.a1.a0.a4 = returnStructNestedIrregularEvenBigger_a1.a0.a4;
+  result.a1.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a0.a0;
+  result.a1.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a1.a0;
+  result.a1.a0.a6 = returnStructNestedIrregularEvenBigger_a1.a0.a6;
+  result.a1.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a0;
+  result.a1.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a1;
+  result.a1.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a1.a0;
+  result.a1.a2 = returnStructNestedIrregularEvenBigger_a1.a2;
+  result.a1.a3 = returnStructNestedIrregularEvenBigger_a1.a3;
+  result.a2.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a0;
+  result.a2.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a0;
+  result.a2.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a1;
+  result.a2.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a1.a0;
+  result.a2.a0.a2 = returnStructNestedIrregularEvenBigger_a2.a0.a2;
+  result.a2.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a0.a0;
+  result.a2.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a1;
+  result.a2.a0.a4 = returnStructNestedIrregularEvenBigger_a2.a0.a4;
+  result.a2.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a0.a0;
+  result.a2.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a1.a0;
+  result.a2.a0.a6 = returnStructNestedIrregularEvenBigger_a2.a0.a6;
+  result.a2.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a0;
+  result.a2.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a1;
+  result.a2.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a1.a0;
+  result.a2.a2 = returnStructNestedIrregularEvenBigger_a2.a2;
+  result.a2.a3 = returnStructNestedIrregularEvenBigger_a2.a3;
+  result.a3 = returnStructNestedIrregularEvenBigger_a3;
+
+  returnStructNestedIrregularEvenBiggerResult = result;
+
+  return result;
+}
+
+/// Return big irregular struct as smoke test.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBigger(int a0,
+    StructNestedIrregularBigger a1, StructNestedIrregularBigger a2, double a3) {
+  print("returnStructNestedIrregularEvenBigger(${a0}, ${a1}, ${a2}, ${a3})");
+
+  // In legacy mode, possibly return null.
+  if (a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "ReturnStructNestedIrregularEvenBigger throwing on purpuse!");
+  }
+
+  returnStructNestedIrregularEvenBigger_a0 = a0;
+  returnStructNestedIrregularEvenBigger_a1 = a1;
+  returnStructNestedIrregularEvenBigger_a2 = a2;
+  returnStructNestedIrregularEvenBigger_a3 = a3;
+
+  final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStructNestedIrregularEvenBiggerAfterCallback() {
+  free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+
+  final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+  print("after callback result = $result");
+
+  free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+}
diff --git a/tests/ffi_2/function_callbacks_structs_by_value_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_test.dart
index 98ab518..23c9946 100644
--- a/tests/ffi_2/function_callbacks_structs_by_value_test.dart
+++ b/tests/ffi_2/function_callbacks_structs_by_value_test.dart
@@ -18,6 +18,7 @@
   for (int i = 0; i < 10; i++) {
     recursiveTest(10);
     recursiveTest(11);
+    testCopyLogic();
   }
 }
 
@@ -69,3 +70,65 @@
         Struct20BytesHomogeneousInt32 struct, Pointer callbackAddress),
     Struct20BytesHomogeneousInt32 Function(int recursionCounter,
         Struct20BytesHomogeneousInt32, Pointer)>("PassStructRecursive");
+
+Struct8BytesNestedInt typedDataBackedStruct = Struct8BytesNestedInt();
+bool typedDataBackedStructSet = false;
+void _receiveStructByValue(Struct8BytesNestedInt struct) {
+  typedDataBackedStruct = struct;
+  typedDataBackedStructSet = true;
+}
+
+final _receiveStructByValuePointer =
+    Pointer.fromFunction<Void Function(Struct8BytesNestedInt)>(
+        _receiveStructByValue);
+
+final _invokeReceiveStructByValue = ffiTestFunctions.lookupFunction<
+        Void Function(
+            Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>),
+        void Function(
+            Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>)>(
+    "CallbackWithStruct");
+
+void testCopyLogic() {
+  _invokeReceiveStructByValue(_receiveStructByValuePointer);
+  Expect.isTrue(typedDataBackedStructSet);
+
+  final pointerBackedStruct = allocate<Struct8BytesNestedInt>().ref;
+
+  void reset() {
+    pointerBackedStruct.a0.a0 = 1;
+    pointerBackedStruct.a0.a1 = 2;
+    pointerBackedStruct.a1.a0 = 3;
+    pointerBackedStruct.a1.a1 = 4;
+    typedDataBackedStruct.a0.a0 = 5;
+    typedDataBackedStruct.a0.a1 = 6;
+    typedDataBackedStruct.a1.a0 = 7;
+    typedDataBackedStruct.a1.a1 = 8;
+  }
+
+  // Pointer -> Pointer.
+  reset();
+  pointerBackedStruct.a1 = pointerBackedStruct.a0;
+  Expect.equals(1, pointerBackedStruct.a1.a0);
+  Expect.equals(2, pointerBackedStruct.a1.a1);
+
+  // Pointer -> TypedData.
+  reset();
+  typedDataBackedStruct.a1 = pointerBackedStruct.a0;
+  Expect.equals(1, typedDataBackedStruct.a1.a0);
+  Expect.equals(2, typedDataBackedStruct.a1.a1);
+
+  // TypedData -> Pointer.
+  reset();
+  pointerBackedStruct.a1 = typedDataBackedStruct.a0;
+  Expect.equals(5, pointerBackedStruct.a1.a0);
+  Expect.equals(6, pointerBackedStruct.a1.a1);
+
+  // TypedData -> TypedData.
+  reset();
+  typedDataBackedStruct.a1 = typedDataBackedStruct.a0;
+  Expect.equals(5, typedDataBackedStruct.a1.a0);
+  Expect.equals(6, typedDataBackedStruct.a1.a1);
+
+  free(pointerBackedStruct.addressOf);
+}
diff --git a/tests/ffi_2/function_structs_by_value_generated_test.dart b/tests/ffi_2/function_structs_by_value_generated_test.dart
index dfb17cd..d8ed635 100644
--- a/tests/ffi_2/function_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_test.dart
@@ -52,6 +52,16 @@
     testPassStructAlignmentInt16();
     testPassStructAlignmentInt32();
     testPassStructAlignmentInt64();
+    testPassStruct8BytesNestedIntx10();
+    testPassStruct8BytesNestedFloatx10();
+    testPassStruct8BytesNestedFloat2x10();
+    testPassStruct8BytesNestedMixedx10();
+    testPassStruct16BytesNestedIntx2();
+    testPassStruct32BytesNestedIntx2();
+    testPassStructNestedIntStructAlignmentInt16();
+    testPassStructNestedIntStructAlignmentInt32();
+    testPassStructNestedIntStructAlignmentInt64();
+    testPassStructNestedIrregularEvenBiggerx4();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -82,6 +92,16 @@
     testReturnStructAlignmentInt16();
     testReturnStructAlignmentInt32();
     testReturnStructAlignmentInt64();
+    testReturnStruct8BytesNestedInt();
+    testReturnStruct8BytesNestedFloat();
+    testReturnStruct8BytesNestedFloat2();
+    testReturnStruct8BytesNestedMixed();
+    testReturnStruct16BytesNestedInt();
+    testReturnStruct32BytesNestedInt();
+    testReturnStructNestedIntStructAlignmentInt16();
+    testReturnStructNestedIntStructAlignmentInt32();
+    testReturnStructNestedIntStructAlignmentInt64();
+    testReturnStructNestedIrregularEvenBigger();
   }
 }
 
@@ -129,6 +149,13 @@
   String toString() => "(${a0}, ${a1})";
 }
 
+class Struct4BytesFloat extends Struct {
+  @Float()
+  double a0;
+
+  String toString() => "(${a0})";
+}
+
 class Struct7BytesHomogeneousUint8 extends Struct {
   @Uint8()
   int a0;
@@ -876,6 +903,129 @@
   String toString() => "(${a0}, ${a1}, ${a2})";
 }
 
+class Struct8BytesNestedInt extends Struct {
+  Struct4BytesHomogeneousInt16 a0;
+
+  Struct4BytesHomogeneousInt16 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat extends Struct {
+  Struct4BytesFloat a0;
+
+  Struct4BytesFloat a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat2 extends Struct {
+  Struct4BytesFloat a0;
+
+  @Float()
+  double a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedMixed extends Struct {
+  Struct4BytesHomogeneousInt16 a0;
+
+  Struct4BytesFloat a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct16BytesNestedInt extends Struct {
+  Struct8BytesNestedInt a0;
+
+  Struct8BytesNestedInt a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct32BytesNestedInt extends Struct {
+  Struct16BytesNestedInt a0;
+
+  Struct16BytesNestedInt a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt16 extends Struct {
+  StructAlignmentInt16 a0;
+
+  StructAlignmentInt16 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt32 extends Struct {
+  StructAlignmentInt32 a0;
+
+  StructAlignmentInt32 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt64 extends Struct {
+  StructAlignmentInt64 a0;
+
+  StructAlignmentInt64 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIrregularBig extends Struct {
+  @Uint16()
+  int a0;
+
+  Struct8BytesNestedMixed a1;
+
+  @Uint16()
+  int a2;
+
+  Struct8BytesNestedFloat2 a3;
+
+  @Uint16()
+  int a4;
+
+  Struct8BytesNestedFloat a5;
+
+  @Uint16()
+  int a6;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6})";
+}
+
+class StructNestedIrregularBigger extends Struct {
+  StructNestedIrregularBig a0;
+
+  Struct8BytesNestedMixed a1;
+
+  @Float()
+  double a2;
+
+  @Double()
+  double a3;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
+class StructNestedIrregularEvenBigger extends Struct {
+  @Uint64()
+  int a0;
+
+  StructNestedIrregularBigger a1;
+
+  StructNestedIrregularBigger a2;
+
+  @Double()
+  double a3;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
 final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
     Int64 Function(
         Struct1ByteInt,
@@ -3660,6 +3810,691 @@
   free(a0.addressOf);
 }
 
+final passStruct8BytesNestedIntx10 = ffiTestFunctions.lookupFunction<
+    Int64 Function(
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt),
+    int Function(
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt,
+        Struct8BytesNestedInt)>("PassStruct8BytesNestedIntx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+void testPassStruct8BytesNestedIntx10() {
+  Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a2 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a3 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a4 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a5 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a6 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a7 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a8 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a9 = allocate<Struct8BytesNestedInt>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3;
+  a0.a1.a1 = 4;
+  a1.a0.a0 = -5;
+  a1.a0.a1 = 6;
+  a1.a1.a0 = -7;
+  a1.a1.a1 = 8;
+  a2.a0.a0 = -9;
+  a2.a0.a1 = 10;
+  a2.a1.a0 = -11;
+  a2.a1.a1 = 12;
+  a3.a0.a0 = -13;
+  a3.a0.a1 = 14;
+  a3.a1.a0 = -15;
+  a3.a1.a1 = 16;
+  a4.a0.a0 = -17;
+  a4.a0.a1 = 18;
+  a4.a1.a0 = -19;
+  a4.a1.a1 = 20;
+  a5.a0.a0 = -21;
+  a5.a0.a1 = 22;
+  a5.a1.a0 = -23;
+  a5.a1.a1 = 24;
+  a6.a0.a0 = -25;
+  a6.a0.a1 = 26;
+  a6.a1.a0 = -27;
+  a6.a1.a1 = 28;
+  a7.a0.a0 = -29;
+  a7.a0.a1 = 30;
+  a7.a1.a0 = -31;
+  a7.a1.a1 = 32;
+  a8.a0.a0 = -33;
+  a8.a0.a1 = 34;
+  a8.a1.a0 = -35;
+  a8.a1.a1 = 36;
+  a9.a0.a0 = -37;
+  a9.a0.a1 = 38;
+  a9.a1.a0 = -39;
+  a9.a1.a1 = 40;
+
+  final result =
+      passStruct8BytesNestedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.equals(20, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+    Float Function(
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat),
+    double Function(
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat,
+        Struct8BytesNestedFloat)>("PassStruct8BytesNestedFloatx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+void testPassStruct8BytesNestedFloatx10() {
+  Struct8BytesNestedFloat a0 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a1 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a2 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a3 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a4 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a5 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a6 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a7 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a8 = allocate<Struct8BytesNestedFloat>().ref;
+  Struct8BytesNestedFloat a9 = allocate<Struct8BytesNestedFloat>().ref;
+
+  a0.a0.a0 = -1.0;
+  a0.a1.a0 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a1.a0 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a1.a0 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a1.a0 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a1.a0 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a1.a0 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a1.a0 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a1.a0 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a1.a0 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a1.a0 = 20.0;
+
+  final result =
+      passStruct8BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(10.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloat2x10 = ffiTestFunctions.lookupFunction<
+    Float Function(
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2),
+    double Function(
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2,
+        Struct8BytesNestedFloat2)>("PassStruct8BytesNestedFloat2x10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testPassStruct8BytesNestedFloat2x10() {
+  Struct8BytesNestedFloat2 a0 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a1 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a2 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a3 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a4 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a5 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a6 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a7 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a8 = allocate<Struct8BytesNestedFloat2>().ref;
+  Struct8BytesNestedFloat2 a9 = allocate<Struct8BytesNestedFloat2>().ref;
+
+  a0.a0.a0 = -1.0;
+  a0.a1 = 2.0;
+  a1.a0.a0 = -3.0;
+  a1.a1 = 4.0;
+  a2.a0.a0 = -5.0;
+  a2.a1 = 6.0;
+  a3.a0.a0 = -7.0;
+  a3.a1 = 8.0;
+  a4.a0.a0 = -9.0;
+  a4.a1 = 10.0;
+  a5.a0.a0 = -11.0;
+  a5.a1 = 12.0;
+  a6.a0.a0 = -13.0;
+  a6.a1 = 14.0;
+  a7.a0.a0 = -15.0;
+  a7.a1 = 16.0;
+  a8.a0.a0 = -17.0;
+  a8.a1 = 18.0;
+  a9.a0.a0 = -19.0;
+  a9.a1 = 20.0;
+
+  final result =
+      passStruct8BytesNestedFloat2x10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(10.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct8BytesNestedMixedx10 = ffiTestFunctions.lookupFunction<
+    Double Function(
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed),
+    double Function(
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed,
+        Struct8BytesNestedMixed)>("PassStruct8BytesNestedMixedx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+void testPassStruct8BytesNestedMixedx10() {
+  Struct8BytesNestedMixed a0 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a1 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a2 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a3 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a4 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a5 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a6 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a7 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a8 = allocate<Struct8BytesNestedMixed>().ref;
+  Struct8BytesNestedMixed a9 = allocate<Struct8BytesNestedMixed>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3.0;
+  a1.a0.a0 = 4;
+  a1.a0.a1 = -5;
+  a1.a1.a0 = 6.0;
+  a2.a0.a0 = -7;
+  a2.a0.a1 = 8;
+  a2.a1.a0 = -9.0;
+  a3.a0.a0 = 10;
+  a3.a0.a1 = -11;
+  a3.a1.a0 = 12.0;
+  a4.a0.a0 = -13;
+  a4.a0.a1 = 14;
+  a4.a1.a0 = -15.0;
+  a5.a0.a0 = 16;
+  a5.a0.a1 = -17;
+  a5.a1.a0 = 18.0;
+  a6.a0.a0 = -19;
+  a6.a0.a1 = 20;
+  a6.a1.a0 = -21.0;
+  a7.a0.a0 = 22;
+  a7.a0.a1 = -23;
+  a7.a1.a0 = 24.0;
+  a8.a0.a0 = -25;
+  a8.a0.a1 = 26;
+  a8.a1.a0 = -27.0;
+  a9.a0.a0 = 28;
+  a9.a0.a1 = -29;
+  a9.a1.a0 = 30.0;
+
+  final result =
+      passStruct8BytesNestedMixedx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.approxEquals(15.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+  free(a4.addressOf);
+  free(a5.addressOf);
+  free(a6.addressOf);
+  free(a7.addressOf);
+  free(a8.addressOf);
+  free(a9.addressOf);
+}
+
+final passStruct16BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+    Int64 Function(Struct16BytesNestedInt, Struct16BytesNestedInt),
+    int Function(Struct16BytesNestedInt,
+        Struct16BytesNestedInt)>("PassStruct16BytesNestedIntx2");
+
+/// Deeper nested struct to test recursive member access.
+void testPassStruct16BytesNestedIntx2() {
+  Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+  Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+  a0.a0.a0.a0 = -1;
+  a0.a0.a0.a1 = 2;
+  a0.a0.a1.a0 = -3;
+  a0.a0.a1.a1 = 4;
+  a0.a1.a0.a0 = -5;
+  a0.a1.a0.a1 = 6;
+  a0.a1.a1.a0 = -7;
+  a0.a1.a1.a1 = 8;
+  a1.a0.a0.a0 = -9;
+  a1.a0.a0.a1 = 10;
+  a1.a0.a1.a0 = -11;
+  a1.a0.a1.a1 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15;
+  a1.a1.a1.a1 = 16;
+
+  final result = passStruct16BytesNestedIntx2(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(8, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final passStruct32BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+    Int64 Function(Struct32BytesNestedInt, Struct32BytesNestedInt),
+    int Function(Struct32BytesNestedInt,
+        Struct32BytesNestedInt)>("PassStruct32BytesNestedIntx2");
+
+/// Even deeper nested struct to test recursive member access.
+void testPassStruct32BytesNestedIntx2() {
+  Struct32BytesNestedInt a0 = allocate<Struct32BytesNestedInt>().ref;
+  Struct32BytesNestedInt a1 = allocate<Struct32BytesNestedInt>().ref;
+
+  a0.a0.a0.a0.a0 = -1;
+  a0.a0.a0.a0.a1 = 2;
+  a0.a0.a0.a1.a0 = -3;
+  a0.a0.a0.a1.a1 = 4;
+  a0.a0.a1.a0.a0 = -5;
+  a0.a0.a1.a0.a1 = 6;
+  a0.a0.a1.a1.a0 = -7;
+  a0.a0.a1.a1.a1 = 8;
+  a0.a1.a0.a0.a0 = -9;
+  a0.a1.a0.a0.a1 = 10;
+  a0.a1.a0.a1.a0 = -11;
+  a0.a1.a0.a1.a1 = 12;
+  a0.a1.a1.a0.a0 = -13;
+  a0.a1.a1.a0.a1 = 14;
+  a0.a1.a1.a1.a0 = -15;
+  a0.a1.a1.a1.a1 = 16;
+  a1.a0.a0.a0.a0 = -17;
+  a1.a0.a0.a0.a1 = 18;
+  a1.a0.a0.a1.a0 = -19;
+  a1.a0.a0.a1.a1 = 20;
+  a1.a0.a1.a0.a0 = -21;
+  a1.a0.a1.a0.a1 = 22;
+  a1.a0.a1.a1.a0 = -23;
+  a1.a0.a1.a1.a1 = 24;
+  a1.a1.a0.a0.a0 = -25;
+  a1.a1.a0.a0.a1 = 26;
+  a1.a1.a0.a1.a0 = -27;
+  a1.a1.a0.a1.a1 = 28;
+  a1.a1.a1.a0.a0 = -29;
+  a1.a1.a1.a0.a1 = 30;
+  a1.a1.a1.a1.a0 = -31;
+  a1.a1.a1.a1.a1 = 32;
+
+  final result = passStruct32BytesNestedIntx2(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(16, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt16 = ffiTestFunctions.lookupFunction<
+        Int64 Function(StructNestedIntStructAlignmentInt16),
+        int Function(StructNestedIntStructAlignmentInt16)>(
+    "PassStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testPassStructNestedIntStructAlignmentInt16() {
+  StructNestedIntStructAlignmentInt16 a0 =
+      allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  final result = passStructNestedIntStructAlignmentInt16(a0);
+
+  print("result = $result");
+
+  Expect.equals(3, result);
+
+  free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt32 = ffiTestFunctions.lookupFunction<
+        Int64 Function(StructNestedIntStructAlignmentInt32),
+        int Function(StructNestedIntStructAlignmentInt32)>(
+    "PassStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testPassStructNestedIntStructAlignmentInt32() {
+  StructNestedIntStructAlignmentInt32 a0 =
+      allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  final result = passStructNestedIntStructAlignmentInt32(a0);
+
+  print("result = $result");
+
+  Expect.equals(3, result);
+
+  free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt64 = ffiTestFunctions.lookupFunction<
+        Int64 Function(StructNestedIntStructAlignmentInt64),
+        int Function(StructNestedIntStructAlignmentInt64)>(
+    "PassStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testPassStructNestedIntStructAlignmentInt64() {
+  StructNestedIntStructAlignmentInt64 a0 =
+      allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a0.a2 = -3;
+  a0.a1.a0 = 4;
+  a0.a1.a1 = -5;
+  a0.a1.a2 = 6;
+
+  final result = passStructNestedIntStructAlignmentInt64(a0);
+
+  print("result = $result");
+
+  Expect.equals(3, result);
+
+  free(a0.addressOf);
+}
+
+final passStructNestedIrregularEvenBiggerx4 = ffiTestFunctions.lookupFunction<
+        Double Function(
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger),
+        double Function(
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger,
+            StructNestedIrregularEvenBigger)>(
+    "PassStructNestedIrregularEvenBiggerx4");
+
+/// Return big irregular struct as smoke test.
+void testPassStructNestedIrregularEvenBiggerx4() {
+  StructNestedIrregularEvenBigger a0 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+  StructNestedIrregularEvenBigger a1 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+  StructNestedIrregularEvenBigger a2 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+  StructNestedIrregularEvenBigger a3 =
+      allocate<StructNestedIrregularEvenBigger>().ref;
+
+  a0.a0 = 1;
+  a0.a1.a0.a0 = 2;
+  a0.a1.a0.a1.a0.a0 = -3;
+  a0.a1.a0.a1.a0.a1 = 4;
+  a0.a1.a0.a1.a1.a0 = -5.0;
+  a0.a1.a0.a2 = 6;
+  a0.a1.a0.a3.a0.a0 = -7.0;
+  a0.a1.a0.a3.a1 = 8.0;
+  a0.a1.a0.a4 = 9;
+  a0.a1.a0.a5.a0.a0 = 10.0;
+  a0.a1.a0.a5.a1.a0 = -11.0;
+  a0.a1.a0.a6 = 12;
+  a0.a1.a1.a0.a0 = -13;
+  a0.a1.a1.a0.a1 = 14;
+  a0.a1.a1.a1.a0 = -15.0;
+  a0.a1.a2 = 16.0;
+  a0.a1.a3 = -17.0;
+  a0.a2.a0.a0 = 18;
+  a0.a2.a0.a1.a0.a0 = -19;
+  a0.a2.a0.a1.a0.a1 = 20;
+  a0.a2.a0.a1.a1.a0 = -21.0;
+  a0.a2.a0.a2 = 22;
+  a0.a2.a0.a3.a0.a0 = -23.0;
+  a0.a2.a0.a3.a1 = 24.0;
+  a0.a2.a0.a4 = 25;
+  a0.a2.a0.a5.a0.a0 = 26.0;
+  a0.a2.a0.a5.a1.a0 = -27.0;
+  a0.a2.a0.a6 = 28;
+  a0.a2.a1.a0.a0 = -29;
+  a0.a2.a1.a0.a1 = 30;
+  a0.a2.a1.a1.a0 = -31.0;
+  a0.a2.a2 = 32.0;
+  a0.a2.a3 = -33.0;
+  a0.a3 = 34.0;
+  a1.a0 = 35;
+  a1.a1.a0.a0 = 36;
+  a1.a1.a0.a1.a0.a0 = -37;
+  a1.a1.a0.a1.a0.a1 = 38;
+  a1.a1.a0.a1.a1.a0 = -39.0;
+  a1.a1.a0.a2 = 40;
+  a1.a1.a0.a3.a0.a0 = -41.0;
+  a1.a1.a0.a3.a1 = 42.0;
+  a1.a1.a0.a4 = 43;
+  a1.a1.a0.a5.a0.a0 = 44.0;
+  a1.a1.a0.a5.a1.a0 = -45.0;
+  a1.a1.a0.a6 = 46;
+  a1.a1.a1.a0.a0 = -47;
+  a1.a1.a1.a0.a1 = 48;
+  a1.a1.a1.a1.a0 = -49.0;
+  a1.a1.a2 = 50.0;
+  a1.a1.a3 = -51.0;
+  a1.a2.a0.a0 = 52;
+  a1.a2.a0.a1.a0.a0 = -53;
+  a1.a2.a0.a1.a0.a1 = 54;
+  a1.a2.a0.a1.a1.a0 = -55.0;
+  a1.a2.a0.a2 = 56;
+  a1.a2.a0.a3.a0.a0 = -57.0;
+  a1.a2.a0.a3.a1 = 58.0;
+  a1.a2.a0.a4 = 59;
+  a1.a2.a0.a5.a0.a0 = 60.0;
+  a1.a2.a0.a5.a1.a0 = -61.0;
+  a1.a2.a0.a6 = 62;
+  a1.a2.a1.a0.a0 = -63;
+  a1.a2.a1.a0.a1 = 64;
+  a1.a2.a1.a1.a0 = -65.0;
+  a1.a2.a2 = 66.0;
+  a1.a2.a3 = -67.0;
+  a1.a3 = 68.0;
+  a2.a0 = 69;
+  a2.a1.a0.a0 = 70;
+  a2.a1.a0.a1.a0.a0 = -71;
+  a2.a1.a0.a1.a0.a1 = 72;
+  a2.a1.a0.a1.a1.a0 = -73.0;
+  a2.a1.a0.a2 = 74;
+  a2.a1.a0.a3.a0.a0 = -75.0;
+  a2.a1.a0.a3.a1 = 76.0;
+  a2.a1.a0.a4 = 77;
+  a2.a1.a0.a5.a0.a0 = 78.0;
+  a2.a1.a0.a5.a1.a0 = -79.0;
+  a2.a1.a0.a6 = 80;
+  a2.a1.a1.a0.a0 = -81;
+  a2.a1.a1.a0.a1 = 82;
+  a2.a1.a1.a1.a0 = -83.0;
+  a2.a1.a2 = 84.0;
+  a2.a1.a3 = -85.0;
+  a2.a2.a0.a0 = 86;
+  a2.a2.a0.a1.a0.a0 = -87;
+  a2.a2.a0.a1.a0.a1 = 88;
+  a2.a2.a0.a1.a1.a0 = -89.0;
+  a2.a2.a0.a2 = 90;
+  a2.a2.a0.a3.a0.a0 = -91.0;
+  a2.a2.a0.a3.a1 = 92.0;
+  a2.a2.a0.a4 = 93;
+  a2.a2.a0.a5.a0.a0 = 94.0;
+  a2.a2.a0.a5.a1.a0 = -95.0;
+  a2.a2.a0.a6 = 96;
+  a2.a2.a1.a0.a0 = -97;
+  a2.a2.a1.a0.a1 = 98;
+  a2.a2.a1.a1.a0 = -99.0;
+  a2.a2.a2 = 100.0;
+  a2.a2.a3 = -101.0;
+  a2.a3 = 102.0;
+  a3.a0 = 103;
+  a3.a1.a0.a0 = 104;
+  a3.a1.a0.a1.a0.a0 = -105;
+  a3.a1.a0.a1.a0.a1 = 106;
+  a3.a1.a0.a1.a1.a0 = -107.0;
+  a3.a1.a0.a2 = 108;
+  a3.a1.a0.a3.a0.a0 = -109.0;
+  a3.a1.a0.a3.a1 = 110.0;
+  a3.a1.a0.a4 = 111;
+  a3.a1.a0.a5.a0.a0 = 112.0;
+  a3.a1.a0.a5.a1.a0 = -113.0;
+  a3.a1.a0.a6 = 114;
+  a3.a1.a1.a0.a0 = -115;
+  a3.a1.a1.a0.a1 = 116;
+  a3.a1.a1.a1.a0 = -117.0;
+  a3.a1.a2 = 118.0;
+  a3.a1.a3 = -119.0;
+  a3.a2.a0.a0 = 120;
+  a3.a2.a0.a1.a0.a0 = -121;
+  a3.a2.a0.a1.a0.a1 = 122;
+  a3.a2.a0.a1.a1.a0 = -123.0;
+  a3.a2.a0.a2 = 124;
+  a3.a2.a0.a3.a0.a0 = -125.0;
+  a3.a2.a0.a3.a1 = 126.0;
+  a3.a2.a0.a4 = 127;
+  a3.a2.a0.a5.a0.a0 = 128.0;
+  a3.a2.a0.a5.a1.a0 = -129.0;
+  a3.a2.a0.a6 = 130;
+  a3.a2.a1.a0.a0 = -131;
+  a3.a2.a1.a0.a1 = 132;
+  a3.a2.a1.a1.a0 = -133.0;
+  a3.a2.a2 = 134.0;
+  a3.a2.a3 = -135.0;
+  a3.a3 = 136.0;
+
+  final result = passStructNestedIrregularEvenBiggerx4(a0, a1, a2, a3);
+
+  print("result = $result");
+
+  Expect.approxEquals(1572.0, result);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+  free(a2.addressOf);
+  free(a3.addressOf);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -5319,3 +6154,396 @@
   Expect.equals(a1, result.a1);
   Expect.equals(a2, result.a2);
 }
+
+final returnStruct8BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedInt Function(
+        Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16),
+    Struct8BytesNestedInt Function(Struct4BytesHomogeneousInt16,
+        Struct4BytesHomogeneousInt16)>("ReturnStruct8BytesNestedInt");
+
+/// Simple nested struct.
+void testReturnStruct8BytesNestedInt() {
+  Struct4BytesHomogeneousInt16 a0 =
+      allocate<Struct4BytesHomogeneousInt16>().ref;
+  Struct4BytesHomogeneousInt16 a1 =
+      allocate<Struct4BytesHomogeneousInt16>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3;
+  a1.a1 = 4;
+
+  final result = returnStruct8BytesNestedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedFloat Function(Struct4BytesFloat, Struct4BytesFloat),
+    Struct8BytesNestedFloat Function(
+        Struct4BytesFloat, Struct4BytesFloat)>("ReturnStruct8BytesNestedFloat");
+
+/// Simple nested struct with floats.
+void testReturnStruct8BytesNestedFloat() {
+  Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+  Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+  a0.a0 = -1.0;
+  a1.a0 = 2.0;
+
+  final result = returnStruct8BytesNestedFloat(a0, a1);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0.a0, result.a0.a0);
+  Expect.approxEquals(a1.a0, result.a1.a0);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat2 = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedFloat2 Function(Struct4BytesFloat, Float),
+    Struct8BytesNestedFloat2 Function(
+        Struct4BytesFloat, double)>("ReturnStruct8BytesNestedFloat2");
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testReturnStruct8BytesNestedFloat2() {
+  Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+  double a1;
+
+  a0.a0 = -1.0;
+  a1 = 2.0;
+
+  final result = returnStruct8BytesNestedFloat2(a0, a1);
+
+  print("result = $result");
+
+  Expect.approxEquals(a0.a0, result.a0.a0);
+  Expect.approxEquals(a1, result.a1);
+
+  free(a0.addressOf);
+}
+
+final returnStruct8BytesNestedMixed = ffiTestFunctions.lookupFunction<
+    Struct8BytesNestedMixed Function(
+        Struct4BytesHomogeneousInt16, Struct4BytesFloat),
+    Struct8BytesNestedMixed Function(Struct4BytesHomogeneousInt16,
+        Struct4BytesFloat)>("ReturnStruct8BytesNestedMixed");
+
+/// Simple nested struct with mixed members.
+void testReturnStruct8BytesNestedMixed() {
+  Struct4BytesHomogeneousInt16 a0 =
+      allocate<Struct4BytesHomogeneousInt16>().ref;
+  Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3.0;
+
+  final result = returnStruct8BytesNestedMixed(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.approxEquals(a1.a0, result.a1.a0);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct16BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Struct16BytesNestedInt Function(
+        Struct8BytesNestedInt, Struct8BytesNestedInt),
+    Struct16BytesNestedInt Function(Struct8BytesNestedInt,
+        Struct8BytesNestedInt)>("ReturnStruct16BytesNestedInt");
+
+/// Deeper nested struct to test recursive member access.
+void testReturnStruct16BytesNestedInt() {
+  Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+  Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+
+  a0.a0.a0 = -1;
+  a0.a0.a1 = 2;
+  a0.a1.a0 = -3;
+  a0.a1.a1 = 4;
+  a1.a0.a0 = -5;
+  a1.a0.a1 = 6;
+  a1.a1.a0 = -7;
+  a1.a1.a1 = 8;
+
+  final result = returnStruct16BytesNestedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0.a0, result.a0.a0.a0);
+  Expect.equals(a0.a0.a1, result.a0.a0.a1);
+  Expect.equals(a0.a1.a0, result.a0.a1.a0);
+  Expect.equals(a0.a1.a1, result.a0.a1.a1);
+  Expect.equals(a1.a0.a0, result.a1.a0.a0);
+  Expect.equals(a1.a0.a1, result.a1.a0.a1);
+  Expect.equals(a1.a1.a0, result.a1.a1.a0);
+  Expect.equals(a1.a1.a1, result.a1.a1.a1);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStruct32BytesNestedInt = ffiTestFunctions.lookupFunction<
+    Struct32BytesNestedInt Function(
+        Struct16BytesNestedInt, Struct16BytesNestedInt),
+    Struct32BytesNestedInt Function(Struct16BytesNestedInt,
+        Struct16BytesNestedInt)>("ReturnStruct32BytesNestedInt");
+
+/// Even deeper nested struct to test recursive member access.
+void testReturnStruct32BytesNestedInt() {
+  Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+  Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+  a0.a0.a0.a0 = -1;
+  a0.a0.a0.a1 = 2;
+  a0.a0.a1.a0 = -3;
+  a0.a0.a1.a1 = 4;
+  a0.a1.a0.a0 = -5;
+  a0.a1.a0.a1 = 6;
+  a0.a1.a1.a0 = -7;
+  a0.a1.a1.a1 = 8;
+  a1.a0.a0.a0 = -9;
+  a1.a0.a0.a1 = 10;
+  a1.a0.a1.a0 = -11;
+  a1.a0.a1.a1 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15;
+  a1.a1.a1.a1 = 16;
+
+  final result = returnStruct32BytesNestedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0.a0.a0, result.a0.a0.a0.a0);
+  Expect.equals(a0.a0.a0.a1, result.a0.a0.a0.a1);
+  Expect.equals(a0.a0.a1.a0, result.a0.a0.a1.a0);
+  Expect.equals(a0.a0.a1.a1, result.a0.a0.a1.a1);
+  Expect.equals(a0.a1.a0.a0, result.a0.a1.a0.a0);
+  Expect.equals(a0.a1.a0.a1, result.a0.a1.a0.a1);
+  Expect.equals(a0.a1.a1.a0, result.a0.a1.a1.a0);
+  Expect.equals(a0.a1.a1.a1, result.a0.a1.a1.a1);
+  Expect.equals(a1.a0.a0.a0, result.a1.a0.a0.a0);
+  Expect.equals(a1.a0.a0.a1, result.a1.a0.a0.a1);
+  Expect.equals(a1.a0.a1.a0, result.a1.a0.a1.a0);
+  Expect.equals(a1.a0.a1.a1, result.a1.a0.a1.a1);
+  Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+  Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+  Expect.equals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+  Expect.equals(a1.a1.a1.a1, result.a1.a1.a1.a1);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt16 =
+    ffiTestFunctions.lookupFunction<
+        StructNestedIntStructAlignmentInt16 Function(
+            StructAlignmentInt16, StructAlignmentInt16),
+        StructNestedIntStructAlignmentInt16 Function(StructAlignmentInt16,
+            StructAlignmentInt16)>("ReturnStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testReturnStructNestedIntStructAlignmentInt16() {
+  StructAlignmentInt16 a0 = allocate<StructAlignmentInt16>().ref;
+  StructAlignmentInt16 a1 = allocate<StructAlignmentInt16>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  final result = returnStructNestedIntStructAlignmentInt16(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+  Expect.equals(a1.a2, result.a1.a2);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt32 =
+    ffiTestFunctions.lookupFunction<
+        StructNestedIntStructAlignmentInt32 Function(
+            StructAlignmentInt32, StructAlignmentInt32),
+        StructNestedIntStructAlignmentInt32 Function(StructAlignmentInt32,
+            StructAlignmentInt32)>("ReturnStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testReturnStructNestedIntStructAlignmentInt32() {
+  StructAlignmentInt32 a0 = allocate<StructAlignmentInt32>().ref;
+  StructAlignmentInt32 a1 = allocate<StructAlignmentInt32>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  final result = returnStructNestedIntStructAlignmentInt32(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+  Expect.equals(a1.a2, result.a1.a2);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt64 =
+    ffiTestFunctions.lookupFunction<
+        StructNestedIntStructAlignmentInt64 Function(
+            StructAlignmentInt64, StructAlignmentInt64),
+        StructNestedIntStructAlignmentInt64 Function(StructAlignmentInt64,
+            StructAlignmentInt64)>("ReturnStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testReturnStructNestedIntStructAlignmentInt64() {
+  StructAlignmentInt64 a0 = allocate<StructAlignmentInt64>().ref;
+  StructAlignmentInt64 a1 = allocate<StructAlignmentInt64>().ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a0.a2 = -3;
+  a1.a0 = 4;
+  a1.a1 = -5;
+  a1.a2 = 6;
+
+  final result = returnStructNestedIntStructAlignmentInt64(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0.a0, result.a0.a0);
+  Expect.equals(a0.a1, result.a0.a1);
+  Expect.equals(a0.a2, result.a0.a2);
+  Expect.equals(a1.a0, result.a1.a0);
+  Expect.equals(a1.a1, result.a1.a1);
+  Expect.equals(a1.a2, result.a1.a2);
+
+  free(a0.addressOf);
+  free(a1.addressOf);
+}
+
+final returnStructNestedIrregularEvenBigger = ffiTestFunctions.lookupFunction<
+    StructNestedIrregularEvenBigger Function(Uint64,
+        StructNestedIrregularBigger, StructNestedIrregularBigger, Double),
+    StructNestedIrregularEvenBigger Function(
+        int,
+        StructNestedIrregularBigger,
+        StructNestedIrregularBigger,
+        double)>("ReturnStructNestedIrregularEvenBigger");
+
+/// Return big irregular struct as smoke test.
+void testReturnStructNestedIrregularEvenBigger() {
+  int a0;
+  StructNestedIrregularBigger a1 = allocate<StructNestedIrregularBigger>().ref;
+  StructNestedIrregularBigger a2 = allocate<StructNestedIrregularBigger>().ref;
+  double a3;
+
+  a0 = 1;
+  a1.a0.a0 = 2;
+  a1.a0.a1.a0.a0 = -3;
+  a1.a0.a1.a0.a1 = 4;
+  a1.a0.a1.a1.a0 = -5.0;
+  a1.a0.a2 = 6;
+  a1.a0.a3.a0.a0 = -7.0;
+  a1.a0.a3.a1 = 8.0;
+  a1.a0.a4 = 9;
+  a1.a0.a5.a0.a0 = 10.0;
+  a1.a0.a5.a1.a0 = -11.0;
+  a1.a0.a6 = 12;
+  a1.a1.a0.a0 = -13;
+  a1.a1.a0.a1 = 14;
+  a1.a1.a1.a0 = -15.0;
+  a1.a2 = 16.0;
+  a1.a3 = -17.0;
+  a2.a0.a0 = 18;
+  a2.a0.a1.a0.a0 = -19;
+  a2.a0.a1.a0.a1 = 20;
+  a2.a0.a1.a1.a0 = -21.0;
+  a2.a0.a2 = 22;
+  a2.a0.a3.a0.a0 = -23.0;
+  a2.a0.a3.a1 = 24.0;
+  a2.a0.a4 = 25;
+  a2.a0.a5.a0.a0 = 26.0;
+  a2.a0.a5.a1.a0 = -27.0;
+  a2.a0.a6 = 28;
+  a2.a1.a0.a0 = -29;
+  a2.a1.a0.a1 = 30;
+  a2.a1.a1.a0 = -31.0;
+  a2.a2 = 32.0;
+  a2.a3 = -33.0;
+  a3 = 34.0;
+
+  final result = returnStructNestedIrregularEvenBigger(a0, a1, a2, a3);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.equals(a1.a0.a0, result.a1.a0.a0);
+  Expect.equals(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0);
+  Expect.equals(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1);
+  Expect.approxEquals(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0);
+  Expect.equals(a1.a0.a2, result.a1.a0.a2);
+  Expect.approxEquals(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0);
+  Expect.approxEquals(a1.a0.a3.a1, result.a1.a0.a3.a1);
+  Expect.equals(a1.a0.a4, result.a1.a0.a4);
+  Expect.approxEquals(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0);
+  Expect.approxEquals(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0);
+  Expect.equals(a1.a0.a6, result.a1.a0.a6);
+  Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+  Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+  Expect.approxEquals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+  Expect.approxEquals(a1.a2, result.a1.a2);
+  Expect.approxEquals(a1.a3, result.a1.a3);
+  Expect.equals(a2.a0.a0, result.a2.a0.a0);
+  Expect.equals(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0);
+  Expect.equals(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1);
+  Expect.approxEquals(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0);
+  Expect.equals(a2.a0.a2, result.a2.a0.a2);
+  Expect.approxEquals(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0);
+  Expect.approxEquals(a2.a0.a3.a1, result.a2.a0.a3.a1);
+  Expect.equals(a2.a0.a4, result.a2.a0.a4);
+  Expect.approxEquals(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0);
+  Expect.approxEquals(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0);
+  Expect.equals(a2.a0.a6, result.a2.a0.a6);
+  Expect.equals(a2.a1.a0.a0, result.a2.a1.a0.a0);
+  Expect.equals(a2.a1.a0.a1, result.a2.a1.a0.a1);
+  Expect.approxEquals(a2.a1.a1.a0, result.a2.a1.a1.a0);
+  Expect.approxEquals(a2.a2, result.a2.a2);
+  Expect.approxEquals(a2.a3, result.a2.a3);
+  Expect.approxEquals(a3, result.a3);
+
+  free(a1.addressOf);
+  free(a2.addressOf);
+}
diff --git a/tests/ffi_2/generator/c_types.dart b/tests/ffi_2/generator/c_types.dart
index f2948fa..56736bf5 100644
--- a/tests/ffi_2/generator/c_types.dart
+++ b/tests/ffi_2/generator/c_types.dart
@@ -140,11 +140,19 @@
   /// To disambiguate same size structs.
   final String suffix;
 
+  /// To override names.
+  final String overrideName;
+
   StructType(List<CType> memberTypes)
       : this.members = generateMemberNames(memberTypes),
-        this.suffix = "";
+        this.suffix = "",
+        this.overrideName = "";
   StructType.disambiguate(List<CType> memberTypes, this.suffix)
-      : this.members = generateMemberNames(memberTypes);
+      : this.members = generateMemberNames(memberTypes),
+        this.overrideName = "";
+  StructType.override(List<CType> memberTypes, this.overrideName)
+      : this.members = generateMemberNames(memberTypes),
+        this.suffix = "";
 
   List<CType> get memberTypes => members.map((a) => a.type).toList();
 
@@ -191,6 +199,9 @@
 
   String get name {
     String result = "Struct";
+    if (overrideName != "") {
+      return result + overrideName;
+    }
     if (hasSize) {
       result += "${size}Byte" + (size != 1 ? "s" : "");
     }
diff --git a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
index 46f5108..718caea 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
@@ -255,6 +255,41 @@
       int64,
       """
 Test alignment and padding of 64 byte int within struct."""),
+  FunctionType(List.filled(10, struct8bytesNestedInt), int64, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust registers on all platforms."""),
+  FunctionType(List.filled(10, struct8bytesNestedFloat), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms."""),
+  FunctionType(List.filled(10, struct8bytesNestedFloat2), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms.
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+  FunctionType(List.filled(10, struct8bytesNestedMixed), double_, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust all registers on all platforms."""),
+  FunctionType(List.filled(2, struct16bytesNestedInt), int64, """
+Deeper nested struct to test recursive member access."""),
+  FunctionType(List.filled(2, struct32bytesNestedInt), int64, """
+Even deeper nested struct to test recursive member access."""),
+  FunctionType(
+      [structNestedAlignmentInt16],
+      int64,
+      """
+Test alignment and padding of nested struct with 16 byte int."""),
+  FunctionType(
+      [structNestedAlignmentInt32],
+      int64,
+      """
+Test alignment and padding of nested struct with 32 byte int."""),
+  FunctionType(
+      [structNestedAlignmentInt64],
+      int64,
+      """
+Test alignment and padding of nested struct with 64 byte int."""),
+  FunctionType(List.filled(4, structNestedEvenBigger), double_, """
+Return big irregular struct as smoke test."""),
   FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
 Smallest struct with data."""),
   FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -358,6 +393,31 @@
 Test alignment and padding of 32 byte int within struct."""),
   FunctionType(structAlignmentInt64.memberTypes, structAlignmentInt64, """
 Test alignment and padding of 64 byte int within struct."""),
+  FunctionType(struct8bytesNestedInt.memberTypes, struct8bytesNestedInt, """
+Simple nested struct."""),
+  FunctionType(struct8bytesNestedFloat.memberTypes, struct8bytesNestedFloat, """
+Simple nested struct with floats."""),
+  FunctionType(
+      struct8bytesNestedFloat2.memberTypes, struct8bytesNestedFloat2, """
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+  FunctionType(struct8bytesNestedMixed.memberTypes, struct8bytesNestedMixed, """
+Simple nested struct with mixed members."""),
+  FunctionType(struct16bytesNestedInt.memberTypes, struct16bytesNestedInt, """
+Deeper nested struct to test recursive member access."""),
+  FunctionType(struct32bytesNestedInt.memberTypes, struct32bytesNestedInt, """
+Even deeper nested struct to test recursive member access."""),
+  FunctionType(
+      structNestedAlignmentInt16.memberTypes, structNestedAlignmentInt16, """
+Test alignment and padding of nested struct with 16 byte int."""),
+  FunctionType(
+      structNestedAlignmentInt32.memberTypes, structNestedAlignmentInt32, """
+Test alignment and padding of nested struct with 32 byte int."""),
+  FunctionType(
+      structNestedAlignmentInt64.memberTypes, structNestedAlignmentInt64, """
+Test alignment and padding of nested struct with 64 byte int."""),
+  FunctionType(structNestedEvenBigger.memberTypes, structNestedEvenBigger, """
+Return big irregular struct as smoke test."""),
 ];
 
 final structs = [
@@ -366,6 +426,7 @@
   struct3bytesInt,
   struct3bytesInt2,
   struct4bytesInt,
+  struct4bytesFloat,
   struct7bytesInt,
   struct7bytesInt2,
   struct8bytesInt,
@@ -387,6 +448,18 @@
   structAlignmentInt16,
   structAlignmentInt32,
   structAlignmentInt64,
+  struct8bytesNestedInt,
+  struct8bytesNestedFloat,
+  struct8bytesNestedFloat2,
+  struct8bytesNestedMixed,
+  struct16bytesNestedInt,
+  struct32bytesNestedInt,
+  structNestedAlignmentInt16,
+  structNestedAlignmentInt32,
+  structNestedAlignmentInt64,
+  structNestedBig,
+  structNestedBigger,
+  structNestedEvenBigger,
 ];
 
 /// Using empty structs is undefined behavior in C.
@@ -396,6 +469,7 @@
 final struct3bytesInt = StructType(List.filled(3, uint8));
 final struct3bytesInt2 = StructType.disambiguate([int16, int8], "2ByteAligned");
 final struct4bytesInt = StructType([int16, int16]);
+final struct4bytesFloat = StructType([float]);
 final struct7bytesInt = StructType(List.filled(7, uint8));
 final struct7bytesInt2 =
     StructType.disambiguate([int32, int16, int8], "4ByteAligned");
@@ -444,3 +518,39 @@
 final structAlignmentInt16 = StructType([int8, int16, int8]);
 final structAlignmentInt32 = StructType([int8, int32, int8]);
 final structAlignmentInt64 = StructType([int8, int64, int8]);
+
+final struct8bytesNestedInt = StructType([struct4bytesInt, struct4bytesInt]);
+final struct8bytesNestedFloat =
+    StructType([struct4bytesFloat, struct4bytesFloat]);
+final struct8bytesNestedFloat2 =
+    StructType.disambiguate([struct4bytesFloat, float], "2");
+final struct8bytesNestedMixed =
+    StructType([struct4bytesInt, struct4bytesFloat]);
+
+final struct16bytesNestedInt =
+    StructType([struct8bytesNestedInt, struct8bytesNestedInt]);
+final struct32bytesNestedInt =
+    StructType([struct16bytesNestedInt, struct16bytesNestedInt]);
+
+final structNestedAlignmentInt16 = StructType.disambiguate(
+    List.filled(2, structAlignmentInt16), structAlignmentInt16.name);
+final structNestedAlignmentInt32 = StructType.disambiguate(
+    List.filled(2, structAlignmentInt32), structAlignmentInt32.name);
+final structNestedAlignmentInt64 = StructType.disambiguate(
+    List.filled(2, structAlignmentInt64), structAlignmentInt64.name);
+
+final structNestedBig = StructType.override([
+  uint16,
+  struct8bytesNestedMixed,
+  uint16,
+  struct8bytesNestedFloat2,
+  uint16,
+  struct8bytesNestedFloat,
+  uint16
+], "NestedIrregularBig");
+final structNestedBigger = StructType.override(
+    [structNestedBig, struct8bytesNestedMixed, float, double_],
+    "NestedIrregularBigger");
+final structNestedEvenBigger = StructType.override(
+    [uint64, structNestedBigger, structNestedBigger, double_],
+    "NestedIrregularEvenBigger");
diff --git a/tests/ffi_2/structs_nested_test.dart b/tests/ffi_2/structs_nested_test.dart
new file mode 100644
index 0000000..7083044
--- /dev/null
+++ b/tests/ffi_2/structs_nested_test.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// This tests the non trampoline aspects of nested structs.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import "package:ffi/ffi.dart";
+
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+void main() {
+  for (int i = 0; i < 10; ++i) {
+    testSizeOf();
+    testAllocate();
+    testRead();
+    testWrite();
+    testCopy();
+  }
+}
+
+class Struct4BytesHomogeneousInt16 extends Struct {
+  @Int16()
+  int a0;
+
+  @Int16()
+  int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedInt extends Struct {
+  Struct4BytesHomogeneousInt16 a0;
+
+  Struct4BytesHomogeneousInt16 a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+void testSizeOf() {
+  print(sizeOf<Struct8BytesNestedInt>());
+  Expect.equals(8, sizeOf<Struct8BytesNestedInt>());
+}
+
+void testAllocate() {
+  final p = allocate<Struct8BytesNestedInt>();
+  Expect.type<Pointer<Struct8BytesNestedInt>>(p);
+  print(p);
+  free(p);
+}
+
+/// Test that reading does not segfault, even uninitialized.
+void testRead() {
+  print("read");
+  final p = allocate<Struct8BytesNestedInt>();
+  print(p);
+  print(p.ref.runtimeType);
+  print(p.ref.addressOf);
+  print(p.ref.addressOf.address);
+  print(p.ref.a0.runtimeType);
+  print(p.ref.a0.addressOf);
+  print(p.ref.a0.a0);
+  free(p);
+  print("read");
+}
+
+void testWrite() {
+  print("write");
+  final p = allocate<Struct8BytesNestedInt>(count: 2);
+  p[0].a0.a0 = 12;
+  p[0].a0.a1 = 13;
+  p[0].a1.a0 = 14;
+  p[0].a1.a1 = 15;
+  p[1].a0.a0 = 16;
+  p[1].a0.a1 = 17;
+  p[1].a1.a0 = 18;
+  p[1].a1.a1 = 19;
+  Expect.equals(12, p[0].a0.a0);
+  Expect.equals(13, p[0].a0.a1);
+  Expect.equals(14, p[0].a1.a0);
+  Expect.equals(15, p[0].a1.a1);
+  Expect.equals(16, p[1].a0.a0);
+  Expect.equals(17, p[1].a0.a1);
+  Expect.equals(18, p[1].a1.a0);
+  Expect.equals(19, p[1].a1.a1);
+  free(p);
+  print("written");
+}
+
+void testCopy() {
+  print("copy");
+  final p = allocate<Struct8BytesNestedInt>();
+  p.ref.a0.a0 = 12;
+  p.ref.a0.a1 = 13;
+  p.ref.a1 = p.ref.a0;
+  Expect.equals(12, p.ref.a1.a0);
+  Expect.equals(13, p.ref.a1.a1);
+  free(p);
+  print("copied");
+}
diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart
index dfa52a9..69efa84 100644
--- a/tests/ffi_2/vmspecific_static_checks_test.dart
+++ b/tests/ffi_2/vmspecific_static_checks_test.dart
@@ -526,3 +526,7 @@
   Pointer.fromFunction<EmptyStruct Function()>(//# 1105: compile-time error
       _returnEmptyStruct); //# 1105: compile-time error
 }
+
+class HasNestedEmptyStruct extends Struct {
+  EmptyStruct nestedEmptyStruct; //# 1106: compile-time error
+}