Version 2.12.0-168.0.dev

Merge commit 'b6b82dd3ac756b39e7fe9cab21f060fae74e358d' into 'dev'
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/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 62cf759..0f60e67 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -38,6 +38,14 @@
 import 'package:watcher/watcher.dart';
 import 'package:yaml/yaml.dart';
 
+/// Enables watching of files generated by Bazel.
+///
+/// TODO(michalt): This is a temporary flag that we use to disable this
+/// functionality due its performance issues. We plan to benchmark and optimize
+/// it and re-enable it everywhere.
+/// Not private to enable testing.
+var experimentalEnableBazelWatching = false;
+
 /// An indication of which files have been added, changed, removed, or deleted.
 ///
 /// No file should be added to the change set more than once, either with the
@@ -1560,6 +1568,7 @@
   ///
   /// Does nothing if the [driver] is not in a Bazel workspace.
   void _watchBazelFilesIfNeeded(Folder folder, AnalysisDriver analysisDriver) {
+    if (!experimentalEnableBazelWatching) return;
     var workspace = analysisDriver.analysisContext.workspace;
     if (workspace is BazelWorkspace &&
         !bazelSubscriptions.containsKey(folder)) {
diff --git a/pkg/analysis_server/test/analysis/bazel_changes_test.dart b/pkg/analysis_server/test/analysis/bazel_changes_test.dart
index 168f461..896d08c 100644
--- a/pkg/analysis_server/test/analysis/bazel_changes_test.dart
+++ b/pkg/analysis_server/test/analysis/bazel_changes_test.dart
@@ -6,6 +6,7 @@
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_constants.dart';
 import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/context_manager.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -36,6 +37,8 @@
   void setUp() {
     super.setUp();
 
+    experimentalEnableBazelWatching = true;
+
     projectPath = convertPath('/workspaceRoot/third_party/dart/project');
     testFile =
         convertPath('/workspaceRoot/third_party/dart/project/lib/test.dart');
@@ -50,6 +53,8 @@
     // file watchers.
     server.contextManager.setRoots([], []);
 
+    experimentalEnableBazelWatching = false;
+
     super.tearDown();
   }
 
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
+}
diff --git a/tools/VERSION b/tools/VERSION
index f676ec5..f214593 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 167
+PRERELEASE 168
 PRERELEASE_PATCH 0
\ No newline at end of file