Version 2.13.0-153.0.dev

Merge commit '77dc74bcd57b8c3b0d1a67737275fb2206e46aeb' into 'dev'
diff --git a/BUILD.gn b/BUILD.gn
index 5d3846c..c25c176 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -23,6 +23,7 @@
     testonly = true
   }
   deps = [
+    ":analysis_server",
     ":create_sdk",
     ":dart2js",
     ":dartanalyzer",
@@ -30,9 +31,6 @@
     ":runtime",
     ":samples",
   ]
-  if (dart_target_arch != "arm") {
-    deps += [ ":analysis_server" ]
-  }
 }
 
 group("runtime") {
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 32821ea2..f1ac8c2 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -271,15 +271,12 @@
     cflags += [ "-fcolor-diagnostics" ]
   }
 
-  # C++11 compiler flags setup.
+  # C++ standard compiler flags setup.
   # ---------------------------
   if (is_win) {
-    # Up-to-date toolchain MSVC doesn't support c++11 flag any longer.
-    cc_std = [ "/std:c++14" ]
-  } else if (is_fuchsia) {
-    cc_std = [ "-std=c++17" ]
+    cc_std = [ "/std:c++17" ]
   } else {
-    cc_std = [ "-std=c++11" ]
+    cc_std = [ "-std=c++17" ]
   }
   cflags_cc += cc_std
   cflags_objcc += cc_std
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 c2b2f95..0850c7c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -3904,6 +3904,68 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)> templateFfiPackedAnnotation =
+    const Template<Message Function(String name)>(
+        messageTemplate:
+            r"""Struct '#name' must have at most one 'Packed' annotation.""",
+        withArguments: _withArgumentsFfiPackedAnnotation);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeFfiPackedAnnotation =
+    const Code<Message Function(String name)>(
+  "FfiPackedAnnotation",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiPackedAnnotation(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeFfiPackedAnnotation,
+      message:
+          """Struct '${name}' must have at most one 'Packed' annotation.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeFfiPackedAnnotationAlignment =
+    messageFfiPackedAnnotationAlignment;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageFfiPackedAnnotationAlignment = const MessageCode(
+    "FfiPackedAnnotationAlignment",
+    message: r"""Only packing to 1, 2, 4, 8, and 16 bytes is supported.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String name,
+        String
+            name2)> templateFfiPackedNestingNonPacked = const Template<
+        Message Function(String name, String name2)>(
+    messageTemplate:
+        r"""Nesting the non-packed or less tightly packed struct '#name' in a packed struct '#name2' is not supported.""",
+    withArguments: _withArgumentsFfiPackedNestingNonPacked);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, String name2)>
+    codeFfiPackedNestingNonPacked =
+    const Code<Message Function(String name, String name2)>(
+  "FfiPackedNestingNonPacked",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiPackedNestingNonPacked(String name, String name2) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  return new Message(codeFfiPackedNestingNonPacked,
+      message:
+          """Nesting the non-packed or less tightly packed struct '${name}' in a packed struct '${name2}' is not supported.""",
+      arguments: {'name': name, 'name2': name2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)> templateFfiSizeAnnotation =
     const Template<Message Function(String name)>(
         messageTemplate:
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
index d55238b..c56e6e9 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -7,11 +7,7 @@
 // "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
 
 // ignore_for_file: annotate_overrides
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: unnecessary_brace_in_string_interps
 // ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: unused_import
 // ignore_for_file: unused_shown_name
 
 import 'dart:core' hide deprecated;
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
index d3c551b..6559e10 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
@@ -7,11 +7,7 @@
 // "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
 
 // ignore_for_file: annotate_overrides
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: unnecessary_brace_in_string_interps
 // ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: unused_import
 // ignore_for_file: unused_shown_name
 
 import 'dart:core' hide deprecated;
@@ -9537,7 +9533,7 @@
                     item != null
                         ? TextDocumentContentChangeEvent2.fromJson(item)
                         : null)
-                : (throw '''${item} was not one of (TextDocumentContentChangeEvent1, TextDocumentContentChangeEvent2)''')))
+                : (throw '''$item was not one of (TextDocumentContentChangeEvent1, TextDocumentContentChangeEvent2)''')))
         ?.cast<Either2<TextDocumentContentChangeEvent1, TextDocumentContentChangeEvent2>>()
         ?.toList();
     return DidChangeTextDocumentParams(
@@ -30675,7 +30671,7 @@
                 : (TextEdit.canParse(item, nullLspJsonReporter)
                     ? Either3<SnippetTextEdit, AnnotatedTextEdit, TextEdit>.t3(
                         item != null ? TextEdit.fromJson(item) : null)
-                    : (throw '''${item} was not one of (SnippetTextEdit, AnnotatedTextEdit, TextEdit)'''))))
+                    : (throw '''$item was not one of (SnippetTextEdit, AnnotatedTextEdit, TextEdit)'''))))
         ?.cast<Either3<SnippetTextEdit, AnnotatedTextEdit, TextEdit>>()
         ?.toList();
     return TextDocumentEdit(textDocument: textDocument, edits: edits);
@@ -33645,7 +33641,7 @@
                         item != null ? TextDocumentEdit.fromJson(item) : null)
                     : (CreateFile.canParse(item, nullLspJsonReporter)
                         ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t2(item != null ? CreateFile.fromJson(item) : null)
-                        : (RenameFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t3(item != null ? RenameFile.fromJson(item) : null) : (DeleteFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t4(item != null ? DeleteFile.fromJson(item) : null) : (item == null ? null : (throw '''${item} was not one of (TextDocumentEdit, CreateFile, RenameFile, DeleteFile)'''))))))
+                        : (RenameFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t3(item != null ? RenameFile.fromJson(item) : null) : (DeleteFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t4(item != null ? DeleteFile.fromJson(item) : null) : (item == null ? null : (throw '''$item was not one of (TextDocumentEdit, CreateFile, RenameFile, DeleteFile)'''))))))
                 ?.cast<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>()
                 ?.toList())
             : (json['documentChanges'] == null ? null : (throw '''${json['documentChanges']} was not one of (List<TextDocumentEdit>, List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>)''')));
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 1e17989..0836959 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:dart_style/dart_style.dart';
+import 'package:meta/meta.dart';
 
 import 'typescript.dart';
 import 'typescript_parser.dart';
@@ -463,7 +464,7 @@
 
 void _writeFromJsonCode(
     IndentableStringBuffer buffer, TypeBase type, String valueCode,
-    {bool allowsNull}) {
+    {bool allowsNull, bool requiresBracesInInterpolation = false}) {
   type = resolveTypeAlias(type);
 
   if (_isSimpleType(type)) {
@@ -493,7 +494,9 @@
     _writeFromJsonCodeForLiteralUnion(buffer, type, valueCode,
         allowsNull: allowsNull);
   } else if (type is UnionType) {
-    _writeFromJsonCodeForUnion(buffer, type, valueCode, allowsNull: allowsNull);
+    _writeFromJsonCodeForUnion(buffer, type, valueCode,
+        allowsNull: allowsNull,
+        requiresBracesInInterpolation: requiresBracesInInterpolation);
   } else {
     buffer.write('$valueCode');
   }
@@ -513,7 +516,7 @@
 
 void _writeFromJsonCodeForUnion(
     IndentableStringBuffer buffer, UnionType union, String valueCode,
-    {bool allowsNull}) {
+    {bool allowsNull, @required bool requiresBracesInInterpolation}) {
   // Write a check against each type, eg.:
   // x is y ? new Either.tx(x) : (...)
   var hasIncompleteCondition = false;
@@ -532,7 +535,9 @@
     // The code to construct a value with this "side" of the union.
     buffer.write('${union.dartTypeWithTypeArgs}.t${i + 1}(');
     _writeFromJsonCode(buffer, type, valueCode,
-        allowsNull: allowsNull); // Call recursively!
+        allowsNull: allowsNull,
+        requiresBracesInInterpolation:
+            requiresBracesInInterpolation); // Call recursively!
     buffer.write(')');
 
     // If we output the type condition at the top, prepare for the next condition.
@@ -551,8 +556,10 @@
       buffer.write('$valueCode == null ? null : (');
       unclosedParens++;
     }
+    var interpolation =
+        requiresBracesInInterpolation ? '\${$valueCode}' : '\$$valueCode';
     buffer.write(
-        "throw '''\${$valueCode} was not one of (${union.types.map((t) => t.dartTypeWithTypeArgs).join(', ')})'''");
+        "throw '''$interpolation was not one of (${union.types.map((t) => t.dartTypeWithTypeArgs).join(', ')})'''");
   }
   buffer.write(')' * unclosedParens);
 }
@@ -578,7 +585,8 @@
   for (final field in allFields) {
     buffer.writeIndented('final ${field.name} = ');
     _writeFromJsonCode(buffer, field.type, "json['${field.name}']",
-        allowsNull: field.allowsNull || field.allowsUndefined);
+        allowsNull: field.allowsNull || field.allowsUndefined,
+        requiresBracesInInterpolation: true);
     buffer.writeln(';');
   }
   buffer
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index ace8ef1..3475f3a 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -160,11 +160,7 @@
 // "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
 
 // ignore_for_file: annotate_overrides
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: unnecessary_brace_in_string_interps
 // ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: unused_import
 // ignore_for_file: unused_shown_name
 
 import 'dart:core' hide deprecated;
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 80da869..bf01283 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 @@
         LocatedMessage,
         messageFfiExceptionalReturnNull,
         messageFfiExpectedConstant,
+        messageFfiPackedAnnotationAlignment,
         noLength,
         templateFfiDartTypeMismatch,
         templateFfiEmptyStruct,
@@ -64,6 +65,8 @@
         templateFfiFieldNoAnnotation,
         templateFfiFieldNull,
         templateFfiNotStatic,
+        templateFfiPackedAnnotation,
+        templateFfiPackedNestingNonPacked,
         templateFfiSizeAnnotation,
         templateFfiSizeAnnotationDimensions,
         templateFfiStructGeneric,
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 7d556ba..d25dfc3 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -329,6 +329,9 @@
 FfiFieldNoAnnotation/analyzerCode: Fail
 FfiFieldNull/analyzerCode: Fail
 FfiNotStatic/analyzerCode: Fail
+FfiPackedAnnotation/analyzerCode: Fail
+FfiPackedAnnotationAlignment/analyzerCode: Fail
+FfiPackedNestingNonPacked/analyzerCode: Fail
 FfiSizeAnnotation/analyzerCode: Fail
 FfiSizeAnnotationDimensions/analyzerCode: Fail
 FfiStructAnnotation/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index abab3a0..31a8357 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4279,6 +4279,21 @@
   template: "Class '#name' cannot be extended or implemented."
   external: test/ffi_test.dart
 
+FfiPackedAnnotation:
+  # Used by dart:ffi
+  template: "Struct '#name' must have at most one 'Packed' annotation."
+  external: test/ffi_test.dart
+
+FfiPackedAnnotationAlignment:
+  # Used by dart:ffi
+  template: "Only packing to 1, 2, 4, 8, and 16 bytes is supported."
+  external: test/ffi_test.dart
+
+FfiPackedNestingNonPacked:
+  # Used by dart:ffi
+  template: "Nesting the non-packed or less tightly packed struct '#name' in a packed struct '#name2' is not supported."
+  external: test/ffi_test.dart
+
 FfiSizeAnnotation:
   # Used by dart:ffi
   template: "Field '#name' must have exactly one 'Array' annotation."
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index d4cc164..8702311 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -162,6 +162,8 @@
 import 'package:vm/transformations/type_flow/transformer.dart' as type_flow;
 import 'package:vm/transformations/pragma.dart' as type_flow;
 
+import '../../testing_utils.dart' show checkEnvironment;
+
 import '../../utils/kernel_chain.dart'
     show
         ComponentResult,
@@ -376,13 +378,11 @@
       steps.add(new MatchExpectation(
           fullCompile ? "$fullPrefix.expect" : "$outlinePrefix.expect",
           serializeFirst: false,
-          isLastMatchStep: updateExpectations));
-      if (!updateExpectations) {
-        steps.add(new MatchExpectation(
-            fullCompile ? "$fullPrefix.expect" : "$outlinePrefix.expect",
-            serializeFirst: true,
-            isLastMatchStep: true));
-      }
+          isLastMatchStep: false));
+      steps.add(new MatchExpectation(
+          fullCompile ? "$fullPrefix.expect" : "$outlinePrefix.expect",
+          serializeFirst: true,
+          isLastMatchStep: true));
     }
     steps.add(const TypeCheck());
     steps.add(const EnsureNoErrors());
@@ -723,6 +723,23 @@
 
   static Future<FastaContext> create(
       Chain suite, Map<String, String> environment) async {
+    const Set<String> knownEnvironmentKeys = {
+      "enableExtensionMethods",
+      "enableNonNullable",
+      "soundNullSafety",
+      "onlyCrashes",
+      "ignoreExpectations",
+      UPDATE_EXPECTATIONS,
+      UPDATE_COMMENTS,
+      "skipVm",
+      "semiFuzz",
+      "verify",
+      KERNEL_TEXT_SERIALIZATION,
+      "platformBinaries",
+      ENABLE_FULL_COMPILE,
+    };
+    checkEnvironment(environment, knownEnvironmentKeys);
+
     String resolvedExecutable = Platform.environment['resolvedExecutable'] ??
         Platform.resolvedExecutable;
     Uri vm = Uri.base.resolveUri(new Uri.file(resolvedExecutable));
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 25e141b..cb51c68 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -2113,6 +2113,8 @@
 package
 packaged
 packages
+packed
+packing
 pad
 padded
 padding
@@ -3042,6 +3044,7 @@
 thumb
 thunk
 thus
+tightly
 time
 timeline
 times
diff --git a/pkg/front_end/test/testing_utils.dart b/pkg/front_end/test/testing_utils.dart
index 6781673..502ac99 100644
--- a/pkg/front_end/test/testing_utils.dart
+++ b/pkg/front_end/test/testing_utils.dart
@@ -43,7 +43,9 @@
   Set<String> environmentKeys = environment.keys.toSet();
   environmentKeys.removeAll(knownEnvironmentKeys);
   if (environmentKeys.isNotEmpty) {
-    throw "Unknown environment(s) given: ${environmentKeys.toList()}.\n"
-        "Knows about ${knownEnvironmentKeys.toList()}";
+    throw "Unknown environment(s) given:"
+        "\n - ${environmentKeys.join("\n- ")}\n"
+        "Knows about these environment(s):"
+        "\n - ${knownEnvironmentKeys.join("\n - ")}";
   }
 }
diff --git a/pkg/front_end/test/utils/kernel_chain.dart b/pkg/front_end/test/utils/kernel_chain.dart
index f7b52fc..f3d539c 100644
--- a/pkg/front_end/test/utils/kernel_chain.dart
+++ b/pkg/front_end/test/utils/kernel_chain.dart
@@ -84,7 +84,9 @@
       expectationSet["ExpectationFileMissing"];
 
   Future<Result<O>> match<O>(String suffix, String actual, Uri uri, O output,
-      {Expectation onMismatch}) async {
+      {Expectation onMismatch, bool overwriteUpdateExpectationsWith}) async {
+    bool updateExpectations =
+        overwriteUpdateExpectationsWith ?? this.updateExpectations;
     actual = actual.trim();
     if (actual.isNotEmpty) {
       actual += "\n";
@@ -338,7 +340,8 @@
     return context.match<ComponentResult>(suffix, actual, uri, result,
         onMismatch: serializeFirst
             ? context.expectationFileMismatchSerialized
-            : context.expectationFileMismatch);
+            : context.expectationFileMismatch,
+        overwriteUpdateExpectationsWith: serializeFirst ? false : null);
   }
 }
 
diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
index eefcad1..4e3bcdc 100644
--- a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
@@ -6,9 +6,9 @@
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
-@#C6
+@#C7
 class Coordinate extends ffi::Struct {
-  static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi());
+  static final field core::int* #sizeOf = (#C10).{core::List::[]}(ffi::_abi());
   @#C12
   constructor #fromTypedDataBase(dynamic #pointer) → dynamic
     : super ffi::Struct::_fromPointer(#pointer)
@@ -51,14 +51,14 @@
   #C2 = TypeLiteralConstant(ffi::Double)
   #C3 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
   #C4 = <core::Type>[#C2, #C2, #C3]
-  #C5 = ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = core::pragma {name:#C11, options:#C5}
   #C13 = 0
   #C14 = <core::int*>[#C13, #C13, #C13]
   #C15 = 8
diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
index b112423..5946f0f 100644
--- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
@@ -3,9 +3,9 @@
 
   import "dart:ffi";
 
-  @#C6
+  @#C7
   class Coordinate extends dart.ffi::Struct {
-    static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+    static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
     @#C12
     constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
@@ -54,14 +54,14 @@
   #C2 = TypeLiteralConstant(dart.ffi::Double)
   #C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
   #C4 = <dart.core::Type>[#C2, #C2, #C3]
-  #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = dart.core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <dart.core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = dart.core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = dart.core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <dart.core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = dart.core::pragma {name:#C11, options:#C5}
   #C13 = 0
   #C14 = <dart.core::int*>[#C13, #C13, #C13]
   #C15 = 8
diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
index 406e797..7095982 100644
--- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
@@ -3,9 +3,9 @@
 
   import "dart:ffi";
 
-  @#C6
+  @#C7
   class Coordinate extends dart.ffi::Struct {
-    static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+    static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
     @#C12
     constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
@@ -58,14 +58,14 @@
   #C2 = TypeLiteralConstant(dart.ffi::Double)
   #C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
   #C4 = <dart.core::Type>[#C2, #C2, #C3]
-  #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = dart.core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <dart.core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = dart.core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = dart.core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <dart.core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = dart.core::pragma {name:#C11, options:#C5}
   #C13 = 0
   #C14 = <dart.core::int*>[#C13, #C13, #C13]
   #C15 = 8
diff --git a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
index 29633d6..1bd3802 100644
--- a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
@@ -3,9 +3,9 @@
 
   import "dart:ffi";
 
-  @#C6
+  @#C7
   class Coordinate extends dart.ffi::Struct {
-    static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+    static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
     @#C12
     constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
@@ -55,14 +55,14 @@
   #C2 = TypeLiteralConstant(dart.ffi::Double)
   #C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
   #C4 = <dart.core::Type>[#C2, #C2, #C3]
-  #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = dart.core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <dart.core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = dart.core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = dart.core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <dart.core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = dart.core::pragma {name:#C11, options:#C5}
   #C13 = 0
   #C14 = <dart.core::int*>[#C13, #C13, #C13]
   #C15 = 8
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
index b112423..5946f0f 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
@@ -3,9 +3,9 @@
 
   import "dart:ffi";
 
-  @#C6
+  @#C7
   class Coordinate extends dart.ffi::Struct {
-    static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+    static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
     @#C12
     constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
@@ -54,14 +54,14 @@
   #C2 = TypeLiteralConstant(dart.ffi::Double)
   #C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
   #C4 = <dart.core::Type>[#C2, #C2, #C3]
-  #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = dart.core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <dart.core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = dart.core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = dart.core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <dart.core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = dart.core::pragma {name:#C11, options:#C5}
   #C13 = 0
   #C14 = <dart.core::int*>[#C13, #C13, #C13]
   #C15 = 8
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
index 5a4e426..9d3a106 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
@@ -3,9 +3,9 @@
 
   import "dart:ffi";
 
-  @#C6
+  @#C7
   class Coordinate extends dart.ffi::Struct {
-    static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+    static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
     @#C12
     constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
@@ -55,14 +55,14 @@
   #C2 = TypeLiteralConstant(dart.ffi::Double)
   #C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
   #C4 = <dart.core::Type>[#C2, #C2, #C3]
-  #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = dart.core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <dart.core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = dart.core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = dart.core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <dart.core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = dart.core::pragma {name:#C11, options:#C5}
   #C13 = 0
   #C14 = <dart.core::int*>[#C13, #C13, #C13]
   #C15 = 8
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
index 3d3c99a..9094ac3 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
@@ -3,9 +3,9 @@
 
   import "dart:ffi";
 
-  @#C6
+  @#C7
   class Coordinate extends dart.ffi::Struct {
-    static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+    static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
     @#C12
     constructor #fromTypedDataBase(dynamic #pointer) → dynamic
       : super dart.ffi::Struct::_fromPointer(#pointer)
@@ -56,14 +56,14 @@
   #C2 = TypeLiteralConstant(dart.ffi::Double)
   #C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
   #C4 = <dart.core::Type>[#C2, #C2, #C3]
-  #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = dart.core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <dart.core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = dart.core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = dart.core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <dart.core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = dart.core::pragma {name:#C11, options:#C5}
   #C13 = 0
   #C14 = <dart.core::int*>[#C13, #C13, #C13]
   #C15 = 8
diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
index e039c5f..ff9d534 100644
--- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
@@ -6,9 +6,9 @@
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
-@#C6
+@#C7
 class Coordinate extends ffi::Struct {
-  static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+  static final field core::int* #sizeOf = (#C10).{core::List::[]}(ffi::_abi())/*isLegacy*/;
   @#C12
   constructor #fromTypedDataBase(dynamic #pointer) → dynamic
     : super ffi::Struct::_fromPointer(#pointer)
@@ -44,14 +44,14 @@
   #C2 = TypeLiteralConstant(ffi::Double)
   #C3 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
   #C4 = <core::Type>[#C2, #C2, #C3]
-  #C5 = ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = core::pragma {name:#C11, options:#C5}
   #C13 = ffi::Double {}
   #C14 = 0
   #C15 = <core::int*>[#C14, #C14, #C14]
diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
index e039c5f..ff9d534 100644
--- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
@@ -6,9 +6,9 @@
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
-@#C6
+@#C7
 class Coordinate extends ffi::Struct {
-  static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+  static final field core::int* #sizeOf = (#C10).{core::List::[]}(ffi::_abi())/*isLegacy*/;
   @#C12
   constructor #fromTypedDataBase(dynamic #pointer) → dynamic
     : super ffi::Struct::_fromPointer(#pointer)
@@ -44,14 +44,14 @@
   #C2 = TypeLiteralConstant(ffi::Double)
   #C3 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
   #C4 = <core::Type>[#C2, #C2, #C3]
-  #C5 = ffi::_FfiStructLayout {fieldTypes:#C4}
-  #C6 = core::pragma {name:#C1, options:#C5}
-  #C7 = 24
-  #C8 = 20
-  #C9 = <core::int*>[#C7, #C8, #C7]
-  #C10 = "vm:entry-point"
-  #C11 = null
-  #C12 = core::pragma {name:#C10, options:#C11}
+  #C5 = null
+  #C6 = ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+  #C7 = core::pragma {name:#C1, options:#C6}
+  #C8 = 24
+  #C9 = 20
+  #C10 = <core::int*>[#C8, #C9, #C8]
+  #C11 = "vm:entry-point"
+  #C12 = core::pragma {name:#C11, options:#C5}
   #C13 = ffi::Double {}
   #C14 = 0
   #C15 = <core::int*>[#C14, #C14, #C14]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
index 7610147..a943907 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
@@ -8,9 +8,9 @@
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
-@#C7
+@#C8
 class StructInlineArray extends ffi::Struct {
-  static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+  static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
   synthetic constructor •() → self::StructInlineArray
     : super ffi::Struct::•()
     ;
@@ -23,10 +23,10 @@
     return new ffi::Array::_<ffi::Uint8>( block {
       core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
       core::int #offset = (#C14).{core::List::[]}(ffi::_abi());
-    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3, #C15);
+    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C3, #C15);
   @#C12
   set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C8).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {}
 
@@ -36,13 +36,13 @@
   #C3 = 8
   #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
   #C5 = <core::Type>[#C4]
-  #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
-  #C7 = core::pragma {name:#C1, options:#C6}
-  #C8 = <core::int*>[#C3, #C3, #C3]
-  #C9 = "vm:entry-point"
-  #C10 = null
-  #C11 = core::pragma {name:#C9, options:#C10}
-  #C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C10, dimension3:#C10, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+  #C6 = null
+  #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+  #C8 = core::pragma {name:#C1, options:#C7}
+  #C9 = <core::int*>[#C3, #C3, #C3]
+  #C10 = "vm:entry-point"
+  #C11 = core::pragma {name:#C10, options:#C6}
+  #C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6}
   #C13 = 0
   #C14 = <core::int*>[#C13, #C13, #C13]
   #C15 = <core::int*>[]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
index 2b04c25..77bed60 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
@@ -8,9 +8,9 @@
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
-@#C7
+@#C8
 class StructInlineArray extends ffi::Struct {
-  static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+  static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
   synthetic constructor •() → self::StructInlineArray
     : super ffi::Struct::•()
     ;
@@ -23,10 +23,10 @@
     return new ffi::Array::_<ffi::Uint8>( block {
       core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
       core::int #offset = (#C14).{core::List::[]}(ffi::_abi());
-    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3, #C15);
+    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C3, #C15);
   @#C12
   set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C8).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {}
 
@@ -36,13 +36,13 @@
   #C3 = 8
   #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
   #C5 = <core::Type>[#C4]
-  #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
-  #C7 = core::pragma {name:#C1, options:#C6}
-  #C8 = <core::int*>[#C3, #C3, #C3]
-  #C9 = "vm:entry-point"
-  #C10 = null
-  #C11 = core::pragma {name:#C9, options:#C10}
-  #C12 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C3, dimension2:#C10, dimension3:#C10, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+  #C6 = null
+  #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+  #C8 = core::pragma {name:#C1, options:#C7}
+  #C9 = <core::int*>[#C3, #C3, #C3]
+  #C10 = "vm:entry-point"
+  #C11 = core::pragma {name:#C10, options:#C6}
+  #C12 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6}
   #C13 = 0
   #C14 = <core::int*>[#C13, #C13, #C13]
   #C15 = <core::int*>[]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
index 7f11381..6ccde5b 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
@@ -8,9 +8,9 @@
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
-@#C7
+@#C8
 class StructInlineArrayMultiDimensional extends ffi::Struct {
-  static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+  static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
   synthetic constructor •() → self::StructInlineArrayMultiDimensional
     : super ffi::Struct::•()
     ;
@@ -23,10 +23,10 @@
     return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
       core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
       core::int #offset = (#C15).{core::List::[]}(ffi::_abi());
-    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C12, #C16);
+    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C12, #C16);
   @#C13
   set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C8).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {
   final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C17).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
@@ -60,14 +60,14 @@
   #C3 = 8
   #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
   #C5 = <core::Type>[#C4]
-  #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
-  #C7 = core::pragma {name:#C1, options:#C6}
-  #C8 = <core::int*>[#C3, #C3, #C3]
-  #C9 = "vm:entry-point"
-  #C10 = null
-  #C11 = core::pragma {name:#C9, options:#C10}
+  #C6 = null
+  #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+  #C8 = core::pragma {name:#C1, options:#C7}
+  #C9 = <core::int*>[#C3, #C3, #C3]
+  #C10 = "vm:entry-point"
+  #C11 = core::pragma {name:#C10, options:#C6}
   #C12 = 2
-  #C13 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+  #C13 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C6, dimension5:#C6, dimensions:#C6}
   #C14 = 0
   #C15 = <core::int*>[#C14, #C14, #C14]
   #C16 = <core::int*>[#C12, #C12]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
index 18b2371..2147809 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
@@ -8,9 +8,9 @@
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
-@#C7
+@#C8
 class StructInlineArrayMultiDimensional extends ffi::Struct {
-  static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+  static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
   synthetic constructor •() → self::StructInlineArrayMultiDimensional
     : super ffi::Struct::•()
     ;
@@ -23,10 +23,10 @@
     return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
       core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
       core::int #offset = (#C15).{core::List::[]}(ffi::_abi());
-    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C12, #C16);
+    } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C12, #C16);
   @#C13
   set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C8).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {
   final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C17).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
@@ -60,14 +60,14 @@
   #C3 = 8
   #C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
   #C5 = <core::Type>[#C4]
-  #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
-  #C7 = core::pragma {name:#C1, options:#C6}
-  #C8 = <core::int*>[#C3, #C3, #C3]
-  #C9 = "vm:entry-point"
-  #C10 = null
-  #C11 = core::pragma {name:#C9, options:#C10}
+  #C6 = null
+  #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+  #C8 = core::pragma {name:#C1, options:#C7}
+  #C9 = <core::int*>[#C3, #C3, #C3]
+  #C10 = "vm:entry-point"
+  #C11 = core::pragma {name:#C10, options:#C6}
   #C12 = 2
-  #C13 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+  #C13 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C6, dimension5:#C6, dimensions:#C6}
   #C14 = 0
   #C15 = <core::int*>[#C14, #C14, #C14]
   #C16 = <core::int*>[#C12, #C12]
diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json
index 5ccfe48..ef56494 100644
--- a/pkg/front_end/testing.json
+++ b/pkg/front_end/testing.json
@@ -134,7 +134,8 @@
       "path": "testcases/",
       "status": "testcases/weak.status",
       "pattern": [
-        "\\.dart$"
+        "\\.dart$",
+        "\\.crash_dart$"
       ],
       "exclude": [
         "/testcases/.*_part[0-9]*\\.dart$",
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 7f1d707..1551f46 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -182,6 +182,11 @@
   NativeType.kPointer,
 ];
 
+const List<NativeType> unalignedLoadsStores = [
+  NativeType.kFloat,
+  NativeType.kDouble,
+];
+
 /// [FfiTransformer] contains logic which is shared between
 /// _FfiUseSiteTransformer and _FfiDefinitionTransformer.
 class FfiTransformer extends Transformer {
@@ -225,9 +230,12 @@
   final Class structClass;
   final Class ffiStructLayoutClass;
   final Field ffiStructLayoutTypesField;
+  final Field ffiStructLayoutPackingField;
   final Class ffiInlineArrayClass;
   final Field ffiInlineArrayElementTypeField;
   final Field ffiInlineArrayLengthField;
+  final Class packedClass;
+  final Field packedMemberAlignmentField;
   final Procedure allocateMethod;
   final Procedure allocatorAllocateMethod;
   final Procedure castMethod;
@@ -260,7 +268,9 @@
   final Procedure pointerFromFunctionProcedure;
   final Procedure nativeCallbackFunctionProcedure;
   final Map<NativeType, Procedure> loadMethods;
+  final Map<NativeType, Procedure> loadUnalignedMethods;
   final Map<NativeType, Procedure> storeMethods;
+  final Map<NativeType, Procedure> storeUnalignedMethods;
   final Map<NativeType, Procedure> elementAtMethods;
   final Procedure memCopy;
   final Procedure allocationTearoff;
@@ -319,11 +329,16 @@
         ffiStructLayoutClass = index.getClass('dart:ffi', '_FfiStructLayout'),
         ffiStructLayoutTypesField =
             index.getMember('dart:ffi', '_FfiStructLayout', 'fieldTypes'),
+        ffiStructLayoutPackingField =
+            index.getMember('dart:ffi', '_FfiStructLayout', 'packing'),
         ffiInlineArrayClass = index.getClass('dart:ffi', '_FfiInlineArray'),
         ffiInlineArrayElementTypeField =
             index.getMember('dart:ffi', '_FfiInlineArray', 'elementType'),
         ffiInlineArrayLengthField =
             index.getMember('dart:ffi', '_FfiInlineArray', 'length'),
+        packedClass = index.getClass('dart:ffi', 'Packed'),
+        packedMemberAlignmentField =
+            index.getMember('dart:ffi', 'Packed', 'memberAlignment'),
         allocateMethod = index.getMember('dart:ffi', 'AllocatorAlloc', 'call'),
         allocatorAllocateMethod =
             index.getMember('dart:ffi', 'Allocator', 'allocate'),
@@ -379,10 +394,20 @@
           final name = nativeTypeClassNames[t.index];
           return index.getTopLevelMember('dart:ffi', "_load$name");
         }),
+        loadUnalignedMethods =
+            Map.fromIterable(unalignedLoadsStores, value: (t) {
+          final name = nativeTypeClassNames[t.index];
+          return index.getTopLevelMember('dart:ffi', "_load${name}Unaligned");
+        }),
         storeMethods = Map.fromIterable(optimizedTypes, value: (t) {
           final name = nativeTypeClassNames[t.index];
           return index.getTopLevelMember('dart:ffi', "_store$name");
         }),
+        storeUnalignedMethods =
+            Map.fromIterable(unalignedLoadsStores, value: (t) {
+          final name = nativeTypeClassNames[t.index];
+          return index.getTopLevelMember('dart:ffi', "_store${name}Unaligned");
+        }),
         elementAtMethods = Map.fromIterable(optimizedTypes, value: (t) {
           final name = nativeTypeClassNames[t.index];
           return index.getTopLevelMember('dart:ffi', "_elementAt$name");
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index 2de1c0b..522510b 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -8,6 +8,7 @@
 
 import 'package:front_end/src/api_unstable/vm.dart'
     show
+        messageFfiPackedAnnotationAlignment,
         templateFfiEmptyStruct,
         templateFfiFieldAnnotation,
         templateFfiFieldNull,
@@ -15,6 +16,8 @@
         templateFfiFieldNoAnnotation,
         templateFfiTypeMismatch,
         templateFfiFieldInitializer,
+        templateFfiPackedAnnotation,
+        templateFfiPackedNestingNonPacked,
         templateFfiSizeAnnotation,
         templateFfiSizeAnnotationDimensions,
         templateFfiStructGeneric;
@@ -177,13 +180,13 @@
       return node;
     }
 
-    _checkStructClass(node);
+    final packing = _checkStructClass(node);
 
     final indexedClass = currentLibraryIndex?.lookupIndexedClass(node.name);
     _checkConstructors(node, indexedClass);
     indexedStructClasses[node] = indexedClass;
 
-    fieldsValid[node] = _checkFieldAnnotations(node);
+    fieldsValid[node] = _checkFieldAnnotations(node, packing);
 
     return node;
   }
@@ -197,7 +200,8 @@
     }
   }
 
-  void _checkStructClass(Class node) {
+  /// Returns packing if any.
+  int _checkStructClass(Class node) {
     if (node.typeParameters.length > 0) {
       diagnosticReporter.report(
           templateFfiStructGeneric.withArguments(node.name),
@@ -209,8 +213,30 @@
     if (node.supertype?.classNode != structClass) {
       // Not a struct, but extends a struct. The error will be emitted by
       // _FfiUseSiteTransformer.
-      return;
+      return null;
     }
+
+    final packingAnnotations = _getPackedAnnotations(node);
+    if (packingAnnotations.length > 1) {
+      diagnosticReporter.report(
+          templateFfiPackedAnnotation.withArguments(node.name),
+          node.fileOffset,
+          node.name.length,
+          node.location.file);
+    }
+    if (packingAnnotations.isNotEmpty) {
+      final packing = packingAnnotations.first;
+      if (!(packing == 1 ||
+          packing == 2 ||
+          packing == 4 ||
+          packing == 8 ||
+          packing == 16)) {
+        diagnosticReporter.report(messageFfiPackedAnnotationAlignment,
+            node.fileOffset, node.name.length, node.location.file);
+      }
+      return packing;
+    }
+    return null;
   }
 
   /// Returns members of [node] that correspond to struct fields.
@@ -242,7 +268,7 @@
     return p.function.positionalParameters.single.type;
   }
 
-  bool _checkFieldAnnotations(Class node) {
+  bool _checkFieldAnnotations(Class node, int packing) {
     bool success = true;
     structClassDependencies[node] = {};
     final membersWithAnnotations = _structFieldMembers(node)
@@ -284,6 +310,7 @@
         if (isStructSubtype(type)) {
           final clazz = (type as InterfaceType).classNode;
           structClassDependencies[node].add(clazz);
+          _checkPacking(node, packing, clazz, f);
         } else if (isArrayType(type)) {
           final sizeAnnotations = _getArraySizeAnnotations(f);
           if (sizeAnnotations.length == 1) {
@@ -291,6 +318,7 @@
             if (isStructSubtype(singleElementType)) {
               final clazz = (singleElementType as InterfaceType).classNode;
               structClassDependencies[node].add(clazz);
+              _checkPacking(node, packing, clazz, f);
             }
             if (arrayDimensions(type) != sizeAnnotations.single.length) {
               diagnosticReporter.report(
@@ -342,6 +370,35 @@
     return success;
   }
 
+  void _checkPacking(Class outerClass, int outerClassPacking, Class fieldClass,
+      Member errorNode) {
+    if (outerClassPacking == null) {
+      // Outer struct has no packing, nesting anything is fine.
+      return;
+    }
+
+    final fieldPackingAnnotations = _getPackedAnnotations(fieldClass);
+    bool error = false;
+    if (fieldPackingAnnotations.isEmpty) {
+      // Outer struct has packing but inner one doesn't.
+      error = true;
+    } else {
+      final fieldPacking = fieldPackingAnnotations.first;
+      if (fieldPacking > outerClassPacking) {
+        // Outer struct has stricter packing than the inner.
+        error = true;
+      }
+    }
+    if (error) {
+      diagnosticReporter.report(
+          templateFfiPackedNestingNonPacked.withArguments(
+              fieldClass.name, outerClass.name),
+          errorNode.fileOffset,
+          errorNode.name.text.length,
+          errorNode.fileUri);
+    }
+  }
+
   void _checkConstructors(Class node, IndexedClass indexedClass) {
     final toRemove = <Initializer>[];
 
@@ -441,22 +498,27 @@
       }
     }
 
-    _annoteStructWithFields(node, types);
+    final packingAnnotations = _getPackedAnnotations(node);
+    final packing =
+        (!packingAnnotations.isEmpty) ? packingAnnotations.first : null;
+
+    _annoteStructWithFields(node, types, packing);
     if (types.isEmpty) {
       diagnosticReporter.report(templateFfiEmptyStruct.withArguments(node.name),
           node.fileOffset, node.name.length, node.location.file);
       emptyStructs.add(node);
     }
 
-    final structType = StructNativeTypeCfe(node, types);
+    final structType = StructNativeTypeCfe(node, types, packing: packing);
     structCache[node] = structType;
     final structLayout = structType.layout;
 
+    final unalignedAccess = packing != null;
     for (final i in fields.keys) {
       final fieldOffsets = structLayout
           .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
       final methods = _generateMethodsForField(
-          fields[i], types[i], fieldOffsets, indexedClass);
+          fields[i], types[i], fieldOffsets, unalignedAccess, indexedClass);
       methods.forEach((p) => node.addProcedure(p));
     }
 
@@ -469,7 +531,11 @@
           .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
       Procedure getter = getters[i];
       getter.function.body = types[i].generateGetterStatement(
-          getter.function.returnType, getter.fileOffset, fieldOffsets, this);
+          getter.function.returnType,
+          getter.fileOffset,
+          fieldOffsets,
+          unalignedAccess,
+          this);
       getter.isExternal = false;
     }
 
@@ -481,6 +547,7 @@
           setter.function.positionalParameters.single.type,
           setter.fileOffset,
           fieldOffsets,
+          unalignedAccess,
           setter.function.positionalParameters.single,
           this);
       setter.isExternal = false;
@@ -489,7 +556,9 @@
     return structLayout.map((k, v) => MapEntry(k, v.size));
   }
 
-  void _annoteStructWithFields(Class node, List<NativeTypeCfe> types) {
+  // packing is `int?`.
+  void _annoteStructWithFields(
+      Class node, List<NativeTypeCfe> types, int packing) {
     List<Constant> constants =
         types.map((t) => t.generateConstant(this)).toList();
 
@@ -499,19 +568,21 @@
           pragmaOptions.getterReference:
               InstanceConstant(ffiStructLayoutClass.reference, [], {
             ffiStructLayoutTypesField.getterReference: ListConstant(
-                InterfaceType(typeClass, Nullability.nonNullable), constants)
+                InterfaceType(typeClass, Nullability.nonNullable), constants),
+            ffiStructLayoutPackingField.getterReference:
+                packing == null ? NullConstant() : IntConstant(packing)
           })
         }),
         InterfaceType(pragmaClass, Nullability.nonNullable, [])));
   }
 
   List<Procedure> _generateMethodsForField(Field field, NativeTypeCfe type,
-      Map<Abi, int> offsets, IndexedClass indexedClass) {
+      Map<Abi, int> offsets, bool unalignedAccess, IndexedClass indexedClass) {
     // TODO(johnniwinther): Avoid passing [indexedClass]. When compiling
     // incrementally, [field] should already carry the references from
     // [indexedClass].
     final getterStatement = type.generateGetterStatement(
-        field.type, field.fileOffset, offsets, this);
+        field.type, field.fileOffset, offsets, unalignedAccess, this);
     Reference getterReference =
         indexedClass?.lookupGetterReference(field.name) ??
             field.getterReference;
@@ -533,8 +604,8 @@
       final VariableDeclaration argument =
           VariableDeclaration('#v', type: field.type)
             ..fileOffset = field.fileOffset;
-      final setterStatement = type.generateSetterStatement(
-          field.type, field.fileOffset, offsets, argument, this);
+      final setterStatement = type.generateSetterStatement(field.type,
+          field.fileOffset, offsets, unalignedAccess, argument, this);
       setter = Procedure(
           field.name,
           ProcedureKind.Setter,
@@ -633,6 +704,17 @@
         .toList();
     return result;
   }
+
+  Iterable<int> _getPackedAnnotations(Class node) {
+    return node.annotations
+        .whereType<ConstantExpression>()
+        .map((expr) => expr.constant)
+        .whereType<InstanceConstant>()
+        .where((e) => e.classNode == packedClass)
+        .map((e) => e.fieldValues.values.single)
+        .whereType<IntConstant>()
+        .map((e) => e.value);
+  }
 }
 
 /// The layout of a `Struct` in one [Abi].
@@ -704,7 +786,7 @@
   ///
   /// Takes [transformer] to be able to lookup classes and methods.
   ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-      Map<Abi, int> offsets, FfiTransformer transformer);
+      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer);
 
   /// Generates the return statement for a struct field setter with this type.
   ///
@@ -713,6 +795,7 @@
       DartType dartType,
       int fileOffset,
       Map<Abi, int> offsets,
+      bool unalignedAccess,
       VariableDeclaration argument,
       FfiTransformer transformer);
 }
@@ -741,16 +824,37 @@
   Constant generateConstant(FfiTransformer transformer) =>
       TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
 
+  bool get isFloat =>
+      nativeType == NativeType.kFloat || nativeType == NativeType.kDouble;
+
+  bool isUnaligned(Map<Abi, int> offsets) {
+    final alignments = alignment;
+    for (final abi in offsets.keys) {
+      final offset = offsets[abi];
+      final alignment = alignments[abi];
+      if (offset % alignment != 0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   /// Sample output for `int get x =>`:
   ///
   /// ```
   /// _loadInt8(_addressOf, offset);
   /// ```
   @override
-  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-          Map<Abi, int> offsets, FfiTransformer transformer) =>
+  ReturnStatement generateGetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          FfiTransformer transformer) =>
       ReturnStatement(StaticInvocation(
-          transformer.loadMethods[nativeType],
+          (unalignedAccess && isFloat
+              ? transformer.loadUnalignedMethods
+              : transformer.loadMethods)[nativeType],
           Arguments([
             PropertyGet(ThisExpression(), transformer.addressOfField.name,
                 transformer.addressOfField)
@@ -769,10 +873,13 @@
           DartType dartType,
           int fileOffset,
           Map<Abi, int> offsets,
+          bool unalignedAccess,
           VariableDeclaration argument,
           FfiTransformer transformer) =>
       ReturnStatement(StaticInvocation(
-          transformer.storeMethods[nativeType],
+          (unalignedAccess && isFloat
+              ? transformer.storeUnalignedMethods
+              : transformer.storeMethods)[nativeType],
           Arguments([
             PropertyGet(ThisExpression(), transformer.addressOfField.name,
                 transformer.addressOfField)
@@ -803,8 +910,12 @@
   /// _fromAddress<Int8>(_loadIntPtr(_addressOf, offset));
   /// ```
   @override
-  ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-          Map<Abi, int> offsets, FfiTransformer transformer) =>
+  ReturnStatement generateGetterStatement(
+          DartType dartType,
+          int fileOffset,
+          Map<Abi, int> offsets,
+          bool unalignedAccess,
+          FfiTransformer transformer) =>
       ReturnStatement(StaticInvocation(
           transformer.fromAddressInternal,
           Arguments([
@@ -832,6 +943,7 @@
           DartType dartType,
           int fileOffset,
           Map<Abi, int> offsets,
+          bool unalignedAccess,
           VariableDeclaration argument,
           FfiTransformer transformer) =>
       ReturnStatement(StaticInvocation(
@@ -853,23 +965,31 @@
 
   final List<NativeTypeCfe> members;
 
+  // Nullable int.
+  final int packing;
+
   final Map<Abi, StructLayout> layout;
 
-  factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members) {
-    final layout = Map.fromEntries(
-        Abi.values.map((abi) => MapEntry(abi, _calculateLayout(members, abi))));
-    return StructNativeTypeCfe._(clazz, members, layout);
+  factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members,
+      {int packing}) {
+    final layout = Map.fromEntries(Abi.values
+        .map((abi) => MapEntry(abi, _calculateLayout(members, packing, abi))));
+    return StructNativeTypeCfe._(clazz, members, packing, layout);
   }
 
   // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
   // NativeCompoundType::FromNativeTypes.
-  static StructLayout _calculateLayout(List<NativeTypeCfe> types, Abi abi) {
+  static StructLayout _calculateLayout(
+      List<NativeTypeCfe> types, int packing, Abi abi) {
     int offset = 0;
     final offsets = <int>[];
     int structAlignment = 1;
     for (int i = 0; i < types.length; i++) {
       final int size = types[i].size[abi];
-      final int alignment = types[i].alignment[abi];
+      int alignment = types[i].alignment[abi];
+      if (packing != null && packing < alignment) {
+        alignment = packing;
+      }
       offset = _alignOffset(offset, alignment);
       offsets.add(offset);
       offset += size;
@@ -879,7 +999,7 @@
     return StructLayout(size, structAlignment, offsets);
   }
 
-  StructNativeTypeCfe._(this.clazz, this.members, this.layout);
+  StructNativeTypeCfe._(this.clazz, this.members, this.packing, this.layout);
 
   @override
   Map<Abi, int> get size =>
@@ -902,7 +1022,7 @@
   /// ```
   @override
   ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-      Map<Abi, int> offsets, FfiTransformer transformer) {
+      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
     final constructor = clazz.constructors
         .firstWhere((c) => c.name == Name("#fromTypedDataBase"));
 
@@ -931,6 +1051,7 @@
           DartType dartType,
           int fileOffset,
           Map<Abi, int> offsets,
+          bool unalignedAccess,
           VariableDeclaration argument,
           FfiTransformer transformer) =>
       ReturnStatement(StaticInvocation(
@@ -1012,7 +1133,7 @@
   /// ```
   @override
   ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
-      Map<Abi, int> offsets, FfiTransformer transformer) {
+      Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
     InterfaceType typeArgument =
         (dartType as InterfaceType).typeArguments.single as InterfaceType;
     return ReturnStatement(ConstructorInvocation(
@@ -1044,6 +1165,7 @@
           DartType dartType,
           int fileOffset,
           Map<Abi, int> offsets,
+          bool unalignedAccess,
           VariableDeclaration argument,
           FfiTransformer transformer) =>
       ReturnStatement(StaticInvocation(
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 69a0e51..49ab86f 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -1113,4 +1113,15 @@
   return my_struct->someValue;
 }
 
+#pragma pack(push, 1)
+struct Struct3BytesPackedIntCopy {
+  int8_t a0;
+  int16_t a1;
+};
+#pragma pack(pop)
+
+DART_EXPORT uint64_t SizeOfStruct3BytesPackedInt() {
+  return sizeof(Struct3BytesPackedIntCopy);
+}
+
 }  // namespace dart
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index a352c54..6d3a2cf 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -467,6 +467,57 @@
   Struct1ByteInt a0[2][2];
 };
 
+#pragma pack(push, 1)
+struct Struct3BytesPackedInt {
+  int8_t a0;
+  int16_t a1;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct Struct3BytesPackedIntMembersAligned {
+  int8_t a0;
+  int16_t a1;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct Struct5BytesPackedMixed {
+  float a0;
+  uint8_t a1;
+};
+#pragma pack(pop)
+
+struct StructNestedAlignmentStruct5BytesPackedMixed {
+  uint8_t a0;
+  Struct5BytesPackedMixed a1;
+};
+
+struct Struct6BytesInlineArrayInt {
+  Struct3BytesPackedIntMembersAligned a0[2];
+};
+
+#pragma pack(push, 1)
+struct Struct8BytesPackedInt {
+  uint8_t a0;
+  uint32_t a1;
+  uint8_t a2;
+  uint8_t a3;
+  uint8_t a4;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct Struct9BytesPackedMixed {
+  uint8_t a0;
+  double a1;
+};
+#pragma pack(pop)
+
+struct Struct15BytesInlineArrayMixed {
+  Struct5BytesPackedMixed a0[3];
+};
+
 // Used for testing structs by value.
 // Smallest struct with data.
 // 10 struct arguments will exhaust available registers.
@@ -3996,6 +4047,306 @@
 }
 
 // Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT int64_t PassStruct3BytesPackedIntx10(Struct3BytesPackedInt a0,
+                                                 Struct3BytesPackedInt a1,
+                                                 Struct3BytesPackedInt a2,
+                                                 Struct3BytesPackedInt a3,
+                                                 Struct3BytesPackedInt a4,
+                                                 Struct3BytesPackedInt a5,
+                                                 Struct3BytesPackedInt a6,
+                                                 Struct3BytesPackedInt a7,
+                                                 Struct3BytesPackedInt a8,
+                                                 Struct3BytesPackedInt a9) {
+  std::cout << "PassStruct3BytesPackedIntx10"
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+            << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+            << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+            << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+            << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+            << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+            << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+            << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+            << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+            << static_cast<int>(a9.a0) << ", " << a9.a1 << "))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0;
+  result += a0.a1;
+  result += a1.a0;
+  result += a1.a1;
+  result += a2.a0;
+  result += a2.a1;
+  result += a3.a0;
+  result += a3.a1;
+  result += a4.a0;
+  result += a4.a1;
+  result += a5.a0;
+  result += a5.a1;
+  result += a6.a0;
+  result += a6.a1;
+  result += a7.a0;
+  result += a7.a1;
+  result += a8.a0;
+  result += a8.a1;
+  result += a9.a0;
+  result += a9.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+DART_EXPORT int64_t PassStruct8BytesPackedIntx10(Struct8BytesPackedInt a0,
+                                                 Struct8BytesPackedInt a1,
+                                                 Struct8BytesPackedInt a2,
+                                                 Struct8BytesPackedInt a3,
+                                                 Struct8BytesPackedInt a4,
+                                                 Struct8BytesPackedInt a5,
+                                                 Struct8BytesPackedInt a6,
+                                                 Struct8BytesPackedInt a7,
+                                                 Struct8BytesPackedInt a8,
+                                                 Struct8BytesPackedInt a9) {
+  std::cout << "PassStruct8BytesPackedIntx10"
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << ", " << static_cast<int>(a0.a3)
+            << ", " << static_cast<int>(a0.a4) << "), ("
+            << static_cast<int>(a1.a0) << ", " << a1.a1 << ", "
+            << static_cast<int>(a1.a2) << ", " << static_cast<int>(a1.a3)
+            << ", " << static_cast<int>(a1.a4) << "), ("
+            << static_cast<int>(a2.a0) << ", " << a2.a1 << ", "
+            << static_cast<int>(a2.a2) << ", " << static_cast<int>(a2.a3)
+            << ", " << static_cast<int>(a2.a4) << "), ("
+            << static_cast<int>(a3.a0) << ", " << a3.a1 << ", "
+            << static_cast<int>(a3.a2) << ", " << static_cast<int>(a3.a3)
+            << ", " << static_cast<int>(a3.a4) << "), ("
+            << static_cast<int>(a4.a0) << ", " << a4.a1 << ", "
+            << static_cast<int>(a4.a2) << ", " << static_cast<int>(a4.a3)
+            << ", " << static_cast<int>(a4.a4) << "), ("
+            << static_cast<int>(a5.a0) << ", " << a5.a1 << ", "
+            << static_cast<int>(a5.a2) << ", " << static_cast<int>(a5.a3)
+            << ", " << static_cast<int>(a5.a4) << "), ("
+            << static_cast<int>(a6.a0) << ", " << a6.a1 << ", "
+            << static_cast<int>(a6.a2) << ", " << static_cast<int>(a6.a3)
+            << ", " << static_cast<int>(a6.a4) << "), ("
+            << static_cast<int>(a7.a0) << ", " << a7.a1 << ", "
+            << static_cast<int>(a7.a2) << ", " << static_cast<int>(a7.a3)
+            << ", " << static_cast<int>(a7.a4) << "), ("
+            << static_cast<int>(a8.a0) << ", " << a8.a1 << ", "
+            << static_cast<int>(a8.a2) << ", " << static_cast<int>(a8.a3)
+            << ", " << static_cast<int>(a8.a4) << "), ("
+            << static_cast<int>(a9.a0) << ", " << a9.a1 << ", "
+            << static_cast<int>(a9.a2) << ", " << static_cast<int>(a9.a3)
+            << ", " << static_cast<int>(a9.a4) << "))"
+            << "\n";
+
+  int64_t result = 0;
+
+  result += a0.a0;
+  result += a0.a1;
+  result += a0.a2;
+  result += a0.a3;
+  result += a0.a4;
+  result += a1.a0;
+  result += a1.a1;
+  result += a1.a2;
+  result += a1.a3;
+  result += a1.a4;
+  result += a2.a0;
+  result += a2.a1;
+  result += a2.a2;
+  result += a2.a3;
+  result += a2.a4;
+  result += a3.a0;
+  result += a3.a1;
+  result += a3.a2;
+  result += a3.a3;
+  result += a3.a4;
+  result += a4.a0;
+  result += a4.a1;
+  result += a4.a2;
+  result += a4.a3;
+  result += a4.a4;
+  result += a5.a0;
+  result += a5.a1;
+  result += a5.a2;
+  result += a5.a3;
+  result += a5.a4;
+  result += a6.a0;
+  result += a6.a1;
+  result += a6.a2;
+  result += a6.a3;
+  result += a6.a4;
+  result += a7.a0;
+  result += a7.a1;
+  result += a7.a2;
+  result += a7.a3;
+  result += a7.a4;
+  result += a8.a0;
+  result += a8.a1;
+  result += a8.a2;
+  result += a8.a3;
+  result += a8.a4;
+  result += a9.a0;
+  result += a9.a1;
+  result += a9.a2;
+  result += a9.a3;
+  result += a9.a4;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT double PassStruct9BytesPackedMixedx10DoubleInt32(
+    Struct9BytesPackedMixed a0,
+    Struct9BytesPackedMixed a1,
+    Struct9BytesPackedMixed a2,
+    Struct9BytesPackedMixed a3,
+    Struct9BytesPackedMixed a4,
+    Struct9BytesPackedMixed a5,
+    Struct9BytesPackedMixed a6,
+    Struct9BytesPackedMixed a7,
+    Struct9BytesPackedMixed a8,
+    Struct9BytesPackedMixed a9,
+    double a10,
+    int32_t a11) {
+  std::cout << "PassStruct9BytesPackedMixedx10DoubleInt32"
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+            << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+            << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+            << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+            << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+            << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+            << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+            << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+            << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+            << static_cast<int>(a9.a0) << ", " << a9.a1 << "), " << a10 << ", "
+            << a11 << ")"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0;
+  result += a0.a1;
+  result += a1.a0;
+  result += a1.a1;
+  result += a2.a0;
+  result += a2.a1;
+  result += a3.a0;
+  result += a3.a1;
+  result += a4.a0;
+  result += a4.a1;
+  result += a5.a0;
+  result += a5.a1;
+  result += a6.a0;
+  result += a6.a1;
+  result += a7.a0;
+  result += a7.a1;
+  result += a8.a0;
+  result += a8.a1;
+  result += a9.a0;
+  result += a9.a1;
+  result += a10;
+  result += a11;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// This packed struct happens to have only aligned members.
+DART_EXPORT double PassStruct5BytesPackedMixed(Struct5BytesPackedMixed a0) {
+  std::cout << "PassStruct5BytesPackedMixed"
+            << "((" << a0.a0 << ", " << static_cast<int>(a0.a1) << "))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0;
+  result += a0.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct in non-packed struct.
+DART_EXPORT double PassStructNestedAlignmentStruct5BytesPackedMixed(
+    StructNestedAlignmentStruct5BytesPackedMixed a0) {
+  std::cout << "PassStructNestedAlignmentStruct5BytesPackedMixed"
+            << "((" << static_cast<int>(a0.a0) << ", (" << a0.a1.a0 << ", "
+            << static_cast<int>(a0.a1.a1) << ")))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0;
+  result += a0.a1.a0;
+  result += a0.a1.a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT double PassStruct6BytesInlineArrayInt(
+    Struct6BytesInlineArrayInt a0) {
+  std::cout << "PassStruct6BytesInlineArrayInt"
+            << "(([(" << static_cast<int>(a0.a0[0].a0) << ", " << a0.a0[0].a1
+            << "), (" << static_cast<int>(a0.a0[1].a0) << ", " << a0.a0[1].a1
+            << ")]))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0[0].a0;
+  result += a0.a0[0].a1;
+  result += a0.a0[1].a0;
+  result += a0.a0[1].a1;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT double PassStruct15BytesInlineArrayMixed(
+    Struct15BytesInlineArrayMixed a0) {
+  std::cout << "PassStruct15BytesInlineArrayMixed"
+            << "(([(" << a0.a0[0].a0 << ", " << static_cast<int>(a0.a0[0].a1)
+            << "), (" << a0.a0[1].a0 << ", " << static_cast<int>(a0.a0[1].a1)
+            << "), (" << a0.a0[2].a0 << ", " << static_cast<int>(a0.a0[2].a1)
+            << ")]))"
+            << "\n";
+
+  double result = 0;
+
+  result += a0.a0[0].a0;
+  result += a0.a0[0].a1;
+  result += a0.a0[1].a0;
+  result += a0.a0[1].a1;
+  result += a0.a0[2].a0;
+  result += a0.a0[2].a1;
+
+  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"
@@ -4932,6 +5283,78 @@
 }
 
 // Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT Struct3BytesPackedInt ReturnStruct3BytesPackedInt(int8_t a0,
+                                                              int16_t a1) {
+  std::cout << "ReturnStruct3BytesPackedInt"
+            << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+            << "\n";
+
+  Struct3BytesPackedInt result;
+
+  result.a0 = a0;
+  result.a1 = a1;
+
+  std::cout << "result = "
+            << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+DART_EXPORT Struct8BytesPackedInt ReturnStruct8BytesPackedInt(uint8_t a0,
+                                                              uint32_t a1,
+                                                              uint8_t a2,
+                                                              uint8_t a3,
+                                                              uint8_t a4) {
+  std::cout << "ReturnStruct8BytesPackedInt"
+            << "(" << static_cast<int>(a0) << ", " << a1 << ", "
+            << static_cast<int>(a2) << ", " << static_cast<int>(a3) << ", "
+            << static_cast<int>(a4) << ")"
+            << "\n";
+
+  Struct8BytesPackedInt result;
+
+  result.a0 = a0;
+  result.a1 = a1;
+  result.a2 = a2;
+  result.a3 = a3;
+  result.a4 = a4;
+
+  std::cout << "result = "
+            << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ", "
+            << static_cast<int>(result.a2) << ", "
+            << static_cast<int>(result.a3) << ", "
+            << static_cast<int>(result.a4) << ")"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT Struct9BytesPackedMixed ReturnStruct9BytesPackedMixed(uint8_t a0,
+                                                                  double a1) {
+  std::cout << "ReturnStruct9BytesPackedMixed"
+            << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+            << "\n";
+
+  Struct9BytesPackedMixed result;
+
+  result.a0 = a0;
+  result.a1 = a1;
+
+  std::cout << "result = "
+            << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+            << "\n";
+
+  return result;
+}
+
+// Used for testing structs by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed in int registers in most ABIs.
@@ -10367,6 +10790,471 @@
 }
 
 // Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT intptr_t TestPassStruct3BytesPackedIntx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(Struct3BytesPackedInt a0,
+                 Struct3BytesPackedInt a1,
+                 Struct3BytesPackedInt a2,
+                 Struct3BytesPackedInt a3,
+                 Struct3BytesPackedInt a4,
+                 Struct3BytesPackedInt a5,
+                 Struct3BytesPackedInt a6,
+                 Struct3BytesPackedInt a7,
+                 Struct3BytesPackedInt a8,
+                 Struct3BytesPackedInt a9)) {
+  Struct3BytesPackedInt a0;
+  Struct3BytesPackedInt a1;
+  Struct3BytesPackedInt a2;
+  Struct3BytesPackedInt a3;
+  Struct3BytesPackedInt a4;
+  Struct3BytesPackedInt a5;
+  Struct3BytesPackedInt a6;
+  Struct3BytesPackedInt a7;
+  Struct3BytesPackedInt a8;
+  Struct3BytesPackedInt a9;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3;
+  a1.a1 = 4;
+  a2.a0 = -5;
+  a2.a1 = 6;
+  a3.a0 = -7;
+  a3.a1 = 8;
+  a4.a0 = -9;
+  a4.a1 = 10;
+  a5.a0 = -11;
+  a5.a1 = 12;
+  a6.a0 = -13;
+  a6.a1 = 14;
+  a7.a0 = -15;
+  a7.a1 = 16;
+  a8.a0 = -17;
+  a8.a1 = 18;
+  a9.a0 = -19;
+  a9.a1 = 20;
+
+  std::cout << "Calling TestPassStruct3BytesPackedIntx10("
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+            << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+            << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+            << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+            << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+            << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+            << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+            << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+            << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+            << static_cast<int>(a9.a0) << ", " << a9.a1 << "))"
+            << ")\n";
+
+  int64_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(10, result);
+
+  // Pass argument that will make the Dart callback throw.
+  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 = 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.
+// Struct with mis-aligned member.
+DART_EXPORT intptr_t TestPassStruct8BytesPackedIntx10(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int64_t (*f)(Struct8BytesPackedInt a0,
+                 Struct8BytesPackedInt a1,
+                 Struct8BytesPackedInt a2,
+                 Struct8BytesPackedInt a3,
+                 Struct8BytesPackedInt a4,
+                 Struct8BytesPackedInt a5,
+                 Struct8BytesPackedInt a6,
+                 Struct8BytesPackedInt a7,
+                 Struct8BytesPackedInt a8,
+                 Struct8BytesPackedInt a9)) {
+  Struct8BytesPackedInt a0;
+  Struct8BytesPackedInt a1;
+  Struct8BytesPackedInt a2;
+  Struct8BytesPackedInt a3;
+  Struct8BytesPackedInt a4;
+  Struct8BytesPackedInt a5;
+  Struct8BytesPackedInt a6;
+  Struct8BytesPackedInt a7;
+  Struct8BytesPackedInt a8;
+  Struct8BytesPackedInt a9;
+
+  a0.a0 = 1;
+  a0.a1 = 2;
+  a0.a2 = 3;
+  a0.a3 = 4;
+  a0.a4 = 5;
+  a1.a0 = 6;
+  a1.a1 = 7;
+  a1.a2 = 8;
+  a1.a3 = 9;
+  a1.a4 = 10;
+  a2.a0 = 11;
+  a2.a1 = 12;
+  a2.a2 = 13;
+  a2.a3 = 14;
+  a2.a4 = 15;
+  a3.a0 = 16;
+  a3.a1 = 17;
+  a3.a2 = 18;
+  a3.a3 = 19;
+  a3.a4 = 20;
+  a4.a0 = 21;
+  a4.a1 = 22;
+  a4.a2 = 23;
+  a4.a3 = 24;
+  a4.a4 = 25;
+  a5.a0 = 26;
+  a5.a1 = 27;
+  a5.a2 = 28;
+  a5.a3 = 29;
+  a5.a4 = 30;
+  a6.a0 = 31;
+  a6.a1 = 32;
+  a6.a2 = 33;
+  a6.a3 = 34;
+  a6.a4 = 35;
+  a7.a0 = 36;
+  a7.a1 = 37;
+  a7.a2 = 38;
+  a7.a3 = 39;
+  a7.a4 = 40;
+  a8.a0 = 41;
+  a8.a1 = 42;
+  a8.a2 = 43;
+  a8.a3 = 44;
+  a8.a4 = 45;
+  a9.a0 = 46;
+  a9.a1 = 47;
+  a9.a2 = 48;
+  a9.a3 = 49;
+  a9.a4 = 50;
+
+  std::cout << "Calling TestPassStruct8BytesPackedIntx10("
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+            << static_cast<int>(a0.a2) << ", " << static_cast<int>(a0.a3)
+            << ", " << static_cast<int>(a0.a4) << "), ("
+            << static_cast<int>(a1.a0) << ", " << a1.a1 << ", "
+            << static_cast<int>(a1.a2) << ", " << static_cast<int>(a1.a3)
+            << ", " << static_cast<int>(a1.a4) << "), ("
+            << static_cast<int>(a2.a0) << ", " << a2.a1 << ", "
+            << static_cast<int>(a2.a2) << ", " << static_cast<int>(a2.a3)
+            << ", " << static_cast<int>(a2.a4) << "), ("
+            << static_cast<int>(a3.a0) << ", " << a3.a1 << ", "
+            << static_cast<int>(a3.a2) << ", " << static_cast<int>(a3.a3)
+            << ", " << static_cast<int>(a3.a4) << "), ("
+            << static_cast<int>(a4.a0) << ", " << a4.a1 << ", "
+            << static_cast<int>(a4.a2) << ", " << static_cast<int>(a4.a3)
+            << ", " << static_cast<int>(a4.a4) << "), ("
+            << static_cast<int>(a5.a0) << ", " << a5.a1 << ", "
+            << static_cast<int>(a5.a2) << ", " << static_cast<int>(a5.a3)
+            << ", " << static_cast<int>(a5.a4) << "), ("
+            << static_cast<int>(a6.a0) << ", " << a6.a1 << ", "
+            << static_cast<int>(a6.a2) << ", " << static_cast<int>(a6.a3)
+            << ", " << static_cast<int>(a6.a4) << "), ("
+            << static_cast<int>(a7.a0) << ", " << a7.a1 << ", "
+            << static_cast<int>(a7.a2) << ", " << static_cast<int>(a7.a3)
+            << ", " << static_cast<int>(a7.a4) << "), ("
+            << static_cast<int>(a8.a0) << ", " << a8.a1 << ", "
+            << static_cast<int>(a8.a2) << ", " << static_cast<int>(a8.a3)
+            << ", " << static_cast<int>(a8.a4) << "), ("
+            << static_cast<int>(a9.a0) << ", " << a9.a1 << ", "
+            << static_cast<int>(a9.a2) << ", " << static_cast<int>(a9.a3)
+            << ", " << static_cast<int>(a9.a4) << "))"
+            << ")\n";
+
+  int64_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(1275, result);
+
+  // Pass argument that will make the Dart callback throw.
+  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 = 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.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT intptr_t TestPassStruct9BytesPackedMixedx10DoubleInt32(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Struct9BytesPackedMixed a0,
+                Struct9BytesPackedMixed a1,
+                Struct9BytesPackedMixed a2,
+                Struct9BytesPackedMixed a3,
+                Struct9BytesPackedMixed a4,
+                Struct9BytesPackedMixed a5,
+                Struct9BytesPackedMixed a6,
+                Struct9BytesPackedMixed a7,
+                Struct9BytesPackedMixed a8,
+                Struct9BytesPackedMixed a9,
+                double a10,
+                int32_t a11)) {
+  Struct9BytesPackedMixed a0;
+  Struct9BytesPackedMixed a1;
+  Struct9BytesPackedMixed a2;
+  Struct9BytesPackedMixed a3;
+  Struct9BytesPackedMixed a4;
+  Struct9BytesPackedMixed a5;
+  Struct9BytesPackedMixed a6;
+  Struct9BytesPackedMixed a7;
+  Struct9BytesPackedMixed a8;
+  Struct9BytesPackedMixed a9;
+  double a10;
+  int32_t a11;
+
+  a0.a0 = 1;
+  a0.a1 = 2.0;
+  a1.a0 = 3;
+  a1.a1 = 4.0;
+  a2.a0 = 5;
+  a2.a1 = 6.0;
+  a3.a0 = 7;
+  a3.a1 = 8.0;
+  a4.a0 = 9;
+  a4.a1 = 10.0;
+  a5.a0 = 11;
+  a5.a1 = 12.0;
+  a6.a0 = 13;
+  a6.a1 = 14.0;
+  a7.a0 = 15;
+  a7.a1 = 16.0;
+  a8.a0 = 17;
+  a8.a1 = 18.0;
+  a9.a0 = 19;
+  a9.a1 = 20.0;
+  a10 = -21.0;
+  a11 = 22;
+
+  std::cout << "Calling TestPassStruct9BytesPackedMixedx10DoubleInt32("
+            << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+            << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+            << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+            << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+            << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+            << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+            << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+            << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+            << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+            << static_cast<int>(a9.a0) << ", " << a9.a1 << "), " << a10 << ", "
+            << a11 << ")"
+            << ")\n";
+
+  double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(211.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// This packed struct happens to have only aligned members.
+DART_EXPORT intptr_t TestPassStruct5BytesPackedMixed(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Struct5BytesPackedMixed a0)) {
+  Struct5BytesPackedMixed a0;
+
+  a0.a0 = -1.0;
+  a0.a1 = 2;
+
+  std::cout << "Calling TestPassStruct5BytesPackedMixed("
+            << "((" << a0.a0 << ", " << static_cast<int>(a0.a1) << "))"
+            << ")\n";
+
+  double result = f(a0);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(1.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct in non-packed struct.
+DART_EXPORT intptr_t TestPassStructNestedAlignmentStruct5BytesPackedMixed(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(StructNestedAlignmentStruct5BytesPackedMixed a0)) {
+  StructNestedAlignmentStruct5BytesPackedMixed a0;
+
+  a0.a0 = 1;
+  a0.a1.a0 = 2.0;
+  a0.a1.a1 = 3;
+
+  std::cout << "Calling TestPassStructNestedAlignmentStruct5BytesPackedMixed("
+            << "((" << static_cast<int>(a0.a0) << ", (" << a0.a1.a0 << ", "
+            << static_cast<int>(a0.a1.a1) << ")))"
+            << ")\n";
+
+  double result = f(a0);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(6.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0 = 42;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0 = 84;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT intptr_t TestPassStruct6BytesInlineArrayInt(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Struct6BytesInlineArrayInt a0)) {
+  Struct6BytesInlineArrayInt a0;
+
+  a0.a0[0].a0 = -1;
+  a0.a0[0].a1 = 2;
+  a0.a0[1].a0 = -3;
+  a0.a0[1].a1 = 4;
+
+  std::cout << "Calling TestPassStruct6BytesInlineArrayInt("
+            << "(([(" << static_cast<int>(a0.a0[0].a0) << ", " << a0.a0[0].a1
+            << "), (" << static_cast<int>(a0.a0[1].a0) << ", " << a0.a0[1].a1
+            << ")]))"
+            << ")\n";
+
+  double result = f(a0);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(2.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0[0].a0 = 42;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0[0].a0 = 84;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT intptr_t TestPassStruct15BytesInlineArrayMixed(
+    // NOLINTNEXTLINE(whitespace/parens)
+    double (*f)(Struct15BytesInlineArrayMixed a0)) {
+  Struct15BytesInlineArrayMixed a0;
+
+  a0.a0[0].a0 = -1.0;
+  a0.a0[0].a1 = 2;
+  a0.a0[1].a0 = -3.0;
+  a0.a0[1].a1 = 4;
+  a0.a0[2].a0 = -5.0;
+  a0.a0[2].a1 = 6;
+
+  std::cout << "Calling TestPassStruct15BytesInlineArrayMixed("
+            << "(([(" << a0.a0[0].a0 << ", " << static_cast<int>(a0.a0[0].a1)
+            << "), (" << a0.a0[1].a0 << ", " << static_cast<int>(a0.a0[1].a1)
+            << "), (" << a0.a0[2].a0 << ", " << static_cast<int>(a0.a0[2].a1)
+            << ")]))"
+            << ")\n";
+
+  double result = f(a0);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(3.0, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0.a0[0].a0 = 42;
+
+  result = f(a0);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0.a0[0].a0 = 84;
+
+  result = f(a0);
+
+  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)
@@ -12527,6 +13415,157 @@
 }
 
 // Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT intptr_t TestReturnStruct3BytesPackedInt(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct3BytesPackedInt (*f)(int8_t a0, int16_t a1)) {
+  int8_t a0;
+  int16_t a1;
+
+  a0 = -1;
+  a1 = 2;
+
+  std::cout << "Calling TestReturnStruct3BytesPackedInt("
+            << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+            << ")\n";
+
+  Struct3BytesPackedInt result = f(a0, a1);
+
+  std::cout << "result = "
+            << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+            << "\n";
+
+  CHECK_EQ(a0, result.a0);
+  CHECK_EQ(a1, result.a1);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_EQ(0, result.a1);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_EQ(0, result.a1);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+DART_EXPORT intptr_t TestReturnStruct8BytesPackedInt(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct8BytesPackedInt (
+        *f)(uint8_t a0, uint32_t a1, uint8_t a2, uint8_t a3, uint8_t a4)) {
+  uint8_t a0;
+  uint32_t a1;
+  uint8_t a2;
+  uint8_t a3;
+  uint8_t a4;
+
+  a0 = 1;
+  a1 = 2;
+  a2 = 3;
+  a3 = 4;
+  a4 = 5;
+
+  std::cout << "Calling TestReturnStruct8BytesPackedInt("
+            << "(" << static_cast<int>(a0) << ", " << a1 << ", "
+            << static_cast<int>(a2) << ", " << static_cast<int>(a3) << ", "
+            << static_cast<int>(a4) << ")"
+            << ")\n";
+
+  Struct8BytesPackedInt result = f(a0, a1, a2, a3, a4);
+
+  std::cout << "result = "
+            << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ", "
+            << static_cast<int>(result.a2) << ", "
+            << static_cast<int>(result.a3) << ", "
+            << static_cast<int>(result.a4) << ")"
+            << "\n";
+
+  CHECK_EQ(a0, result.a0);
+  CHECK_EQ(a1, result.a1);
+  CHECK_EQ(a2, result.a2);
+  CHECK_EQ(a3, result.a3);
+  CHECK_EQ(a4, result.a4);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_EQ(0, result.a1);
+  CHECK_EQ(0, result.a2);
+  CHECK_EQ(0, result.a3);
+  CHECK_EQ(0, result.a4);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_EQ(0, result.a1);
+  CHECK_EQ(0, result.a2);
+  CHECK_EQ(0, result.a3);
+  CHECK_EQ(0, result.a4);
+
+  return 0;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT intptr_t TestReturnStruct9BytesPackedMixed(
+    // NOLINTNEXTLINE(whitespace/parens)
+    Struct9BytesPackedMixed (*f)(uint8_t a0, double a1)) {
+  uint8_t a0;
+  double a1;
+
+  a0 = 1;
+  a1 = 2.0;
+
+  std::cout << "Calling TestReturnStruct9BytesPackedMixed("
+            << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+            << ")\n";
+
+  Struct9BytesPackedMixed result = f(a0, a1);
+
+  std::cout << "result = "
+            << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+            << "\n";
+
+  CHECK_EQ(a0, result.a0);
+  CHECK_APPROX(a1, result.a1);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_APPROX(0.0, result.a1);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_EQ(0, result.a0);
+  CHECK_APPROX(0.0, result.a1);
+
+  return 0;
+}
+
+// Used for testing structs by value.
 // Test that a struct passed in as argument can be returned.
 // Especially for ffi callbacks.
 // Struct is passed in int registers in most ABIs.
diff --git a/runtime/observatory/tests/service/allocations_test.dart b/runtime/observatory/tests/service/allocations_test.dart
index 6a7d002..bc98c0e 100644
--- a/runtime/observatory/tests/service/allocations_test.dart
+++ b/runtime/observatory/tests/service/allocations_test.dart
@@ -8,6 +8,7 @@
 import 'package:test/test.dart';
 import 'test_helper.dart';
 
+@pragma('vm:entry-point')
 class Foo {}
 
 // Prevent TFA from removing this static field to ensure the objects are kept
diff --git a/runtime/observatory_2/tests/service_2/allocations_test.dart b/runtime/observatory_2/tests/service_2/allocations_test.dart
index 3fb6ce4..2732498 100644
--- a/runtime/observatory_2/tests/service_2/allocations_test.dart
+++ b/runtime/observatory_2/tests/service_2/allocations_test.dart
@@ -8,6 +8,7 @@
 import 'package:test/test.dart';
 import 'test_helper.dart';
 
+@pragma('vm:entry-point')
 class Foo {}
 
 // Prevent TFA from removing this static field to ensure the objects are kept
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
index 407304e..52201dc 100644
--- a/runtime/platform/globals.h
+++ b/runtime/platform/globals.h
@@ -20,7 +20,13 @@
 // from the way the Dart project expects it: DEBUG indicating a debug build.
 #if !defined(NDEBUG) && !defined(DEBUG)
 #define DEBUG
-#endif  // !NDEBUG && !DEBUG
+#endif  // !NDEBUG && !DEBUG                                                   \
+#else
+// Since <cassert> uses NDEBUG to signify that assert() macros should be turned
+// off, we'll define it when DEBUG is _not_ set.
+#if !defined(DEBUG)
+#define NDEBUG
+#endif
 #endif  // GOOGLE3
 
 // __STDC_FORMAT_MACROS has to be defined before including <inttypes.h> to
@@ -85,6 +91,8 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include <cassert>  // For assert() in constant expressions.
+
 #if defined(_WIN32)
 #include "platform/floating_point_win.h"
 #endif  // defined(_WIN32)
@@ -136,12 +144,6 @@
 #define DEBUG_ONLY(code)
 #endif  // defined(DEBUG)
 
-#if defined(DEBUG)
-#define UNLESS_DEBUG(code)
-#else  // defined(DEBUG)
-#define UNLESS_DEBUG(code) code
-#endif  // defined(DEBUG)
-
 namespace dart {
 
 struct simd128_value_t {
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index eecab24..788dd2a 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -57,7 +57,7 @@
   }
 
   template <typename T>
-  static inline bool IsPowerOfTwo(T x) {
+  static constexpr bool IsPowerOfTwo(T x) {
     return ((x & (x - 1)) == 0) && (x != 0);
   }
 
@@ -73,13 +73,13 @@
   }
 
   template <typename T>
-  static inline bool IsAligned(T x, intptr_t n) {
-    ASSERT(IsPowerOfTwo(n));
+  static constexpr bool IsAligned(T x, intptr_t n) {
+    assert(IsPowerOfTwo(n));
     return (x & (n - 1)) == 0;
   }
 
   template <typename T>
-  static inline bool IsAligned(T* x, intptr_t n) {
+  static constexpr bool IsAligned(T* x, intptr_t n) {
     return IsAligned(reinterpret_cast<uword>(x), n);
   }
 
diff --git a/runtime/tests/vm/dart/regress_45270_test.dart b/runtime/tests/vm/dart/regress_45270_test.dart
new file mode 100644
index 0000000..867e2bd
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45270_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.
+//
+// Regression test for https://github.com/dart-lang/sdk/issues/45270
+
+void main() {
+  // Function.apply.
+  test<int?>(42);
+  test<int?>(null);
+
+  // Dynamic closure calls.
+  test2<int?>(42);
+  test2<int?>(null);
+}
+
+void test<T>(T value) {
+  final f = (T inner) {
+    print('f inner=$inner T=$T');
+  };
+  Function.apply(f, [value]);
+}
+
+void test2<T>(T value) {
+  dynamic f = (T inner) {
+    print('f inner=$inner T=$T');
+  };
+  f(value);
+}
diff --git a/runtime/vm/bitfield.h b/runtime/vm/bitfield.h
index 4a4f45f..b982d83 100644
--- a/runtime/vm/bitfield.h
+++ b/runtime/vm/bitfield.h
@@ -7,7 +7,6 @@
 
 #include <type_traits>
 
-#include "platform/assert.h"
 #include "platform/globals.h"
 
 namespace dart {
@@ -52,8 +51,8 @@
   static constexpr int bitsize() { return size; }
 
   // Returns an S with the bit field value encoded.
-  static UNLESS_DEBUG(constexpr) S encode(T value) {
-    DEBUG_ASSERT(is_valid(value));
+  static constexpr S encode(T value) {
+    assert(is_valid(value));
     return encode_unchecked(value);
   }
 
@@ -62,29 +61,28 @@
     // Ensure we slide down the sign bit if the value in the bit field is signed
     // and negative. We use 64-bit ints inside the expression since we can have
     // both cases: sizeof(S) > sizeof(T) or sizeof(S) < sizeof(T).
-    return static_cast<T>(
-        (sign_extend
-             ? (static_cast<int64_t>(static_cast<uint64_t>(value)
-                                     << (64 - (size + position))) >>
-                (64 - size))
-             : ((static_cast<typename std::make_unsigned<S>::type>(value) >>
-                 position) &
-                mask())));
+    if constexpr (sign_extend) {
+      auto const u = static_cast<uint64_t>(value);
+      return static_cast<T>((static_cast<int64_t>(u << (64 - kNextBit))) >>
+                            (64 - size));
+    } else {
+      auto const u = static_cast<typename std::make_unsigned<S>::type>(value);
+      return static_cast<T>((u >> position) & mask());
+    }
   }
 
   // Returns an S with the bit field value encoded based on the
   // original value. Only the bits corresponding to this bit field
   // will be changed.
-  static UNLESS_DEBUG(constexpr) S update(T value, S original) {
-    DEBUG_ASSERT(is_valid(value));
-    return encode_unchecked(value) | (~mask_in_place() & original);
+  static constexpr S update(T value, S original) {
+    return encode(value) | (~mask_in_place() & original);
   }
 
  private:
   // Returns an S with the bit field value encoded.
   static constexpr S encode_unchecked(T value) {
-    return (static_cast<typename std::make_unsigned<S>::type>(value) & mask())
-           << position;
+    auto const u = static_cast<typename std::make_unsigned<S>::type>(value);
+    return (u & mask()) << position;
   }
 };
 
diff --git a/runtime/vm/bitfield_test.cc b/runtime/vm/bitfield_test.cc
index e90a257..e2d40f0 100644
--- a/runtime/vm/bitfield_test.cc
+++ b/runtime/vm/bitfield_test.cc
@@ -70,4 +70,16 @@
   TestSignExtendedBitField<int32_t>();
 }
 
+#if defined(DEBUG)
+#define DEBUG_CRASH "Crash"
+#else
+#define DEBUG_CRASH "Pass"
+#endif
+
+VM_UNIT_TEST_CASE_WITH_EXPECTATION(BitFields_Assert, DEBUG_CRASH) {
+  class F : public BitField<uint32_t, uint32_t, 0, 8, /*sign_extend=*/false> {};
+  const uint32_t value = F::encode(kMaxUint32);
+  EXPECT_EQ(kMaxUint8, value);
+}
+
 }  // namespace dart
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index b33e519..56c4f3e 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -1763,20 +1763,19 @@
 }
 
 void Assembler::LoadFromOffset(Register reg,
-                               Register base,
-                               int32_t offset,
+                               const Address& address,
                                OperandSize type) {
   switch (type) {
     case kByte:
-      return movsxb(reg, Address(base, offset));
+      return movsxb(reg, address);
     case kUnsignedByte:
-      return movzxb(reg, Address(base, offset));
+      return movzxb(reg, address);
     case kTwoBytes:
-      return movsxw(reg, Address(base, offset));
+      return movsxw(reg, address);
     case kUnsignedTwoBytes:
-      return movzxw(reg, Address(base, offset));
+      return movzxw(reg, address);
     case kFourBytes:
-      return movl(reg, Address(base, offset));
+      return movl(reg, address);
     default:
       UNREACHABLE();
       break;
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.h b/runtime/vm/compiler/assembler/assembler_ia32.h
index 1573131..fecfe5a1 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.h
+++ b/runtime/vm/compiler/assembler/assembler_ia32.h
@@ -584,29 +584,40 @@
     j(ZERO, label, distance);
   }
 
-  void LoadFromOffset(Register reg,
+  // Arch-specific LoadFromOffset to choose the right operation for [sz].
+  void LoadFromOffset(Register dst,
+                      const Address& address,
+                      OperandSize sz = kFourBytes);
+  void LoadFromOffset(Register dst,
                       Register base,
                       int32_t offset,
-                      OperandSize type = kFourBytes);
-  void LoadField(Register dst, FieldAddress address) { movl(dst, address); }
+                      OperandSize sz = kFourBytes) {
+    LoadFromOffset(dst, Address(base, offset), sz);
+  }
+  void LoadField(Register dst,
+                 const FieldAddress& address,
+                 OperandSize sz = kFourBytes) {
+    LoadFromOffset(dst, address, sz);
+  }
   void LoadFieldFromOffset(Register reg,
                            Register base,
                            int32_t offset,
-                           OperandSize type = kFourBytes) {
-    LoadFromOffset(reg, base, offset - kHeapObjectTag, type);
+                           OperandSize sz = kFourBytes) {
+    LoadFromOffset(reg, FieldAddress(base, offset), sz);
   }
   void LoadCompressedFieldFromOffset(Register reg,
                                      Register base,
                                      int32_t offset,
-                                     OperandSize type = kFourBytes) {
-    LoadFieldFromOffset(reg, base, offset, type);
+                                     OperandSize sz = kFourBytes) {
+    LoadFieldFromOffset(reg, base, offset, sz);
   }
-  void LoadIndexedFieldFromOffset(Register reg,
-                                  Register base,
-                                  int32_t offset,
-                                  Register index,
-                                  ScaleFactor scale) {
-    LoadField(reg, FieldAddress(base, index, scale, offset));
+  void LoadIndexedPayload(Register dst,
+                          Register base,
+                          int32_t payload_offset,
+                          Register index,
+                          ScaleFactor scale,
+                          OperandSize sz = kFourBytes) {
+    LoadFromOffset(dst, FieldAddress(base, index, scale, payload_offset), sz);
   }
   void LoadFromStack(Register dst, intptr_t depth);
   void StoreToStack(Register src, intptr_t depth);
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 0c2dcff..139eaeb 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5431,6 +5431,67 @@
                              env());
 }
 
+LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
+    Zone* zone,
+    bool opt) const {
+  const intptr_t kNumInputs = 3;
+  const intptr_t kNumTemps = 0;
+  LocationSummary* locs = new (zone)
+      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
+  locs->set_in(0, Location::RegisterLocation(
+                      InstantiationABI::kInstantiatorTypeArgumentsReg));
+  locs->set_in(1, Location::RegisterLocation(
+                      InstantiationABI::kFunctionTypeArgumentsReg));
+  locs->set_in(2, Location::RegisterLocation(
+                      InstantiationABI::kUninstantiatedTypeArgumentsReg));
+  locs->set_out(
+      0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
+  return locs;
+}
+
+void InstantiateTypeArgumentsInstr::EmitNativeCode(
+    FlowGraphCompiler* compiler) {
+  // We should never try and instantiate a TAV known at compile time to be null,
+  // so we can use a null value below for the dynamic case.
+  ASSERT(!type_arguments()->BindsToConstant() ||
+         !type_arguments()->BoundConstant().IsNull());
+  const auto& type_args =
+      type_arguments()->BindsToConstant()
+          ? TypeArguments::Cast(type_arguments()->BoundConstant())
+          : Object::null_type_arguments();
+  const intptr_t len = type_args.Length();
+  const bool can_function_type_args_be_null =
+      function_type_arguments()->CanBe(Object::null_object());
+
+  compiler::Label type_arguments_instantiated;
+  if (type_args.IsNull()) {
+    // Currently we only create dynamic InstantiateTypeArguments instructions
+    // in cases where we know the type argument is uninstantiated at runtime,
+    // so there are no extra checks needed to call the stub successfully.
+  } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
+             can_function_type_args_be_null) {
+    // If both the instantiator and function type arguments are null and if the
+    // type argument vector instantiated from null becomes a vector of dynamic,
+    // then use null as the type arguments.
+    compiler::Label non_null_type_args;
+    __ LoadObject(InstantiationABI::kResultTypeArgumentsReg,
+                  Object::null_object());
+    __ CompareRegisters(InstantiationABI::kInstantiatorTypeArgumentsReg,
+                        InstantiationABI::kResultTypeArgumentsReg);
+    if (!function_type_arguments()->BindsToConstant()) {
+      __ BranchIf(NOT_EQUAL, &non_null_type_args);
+      __ CompareRegisters(InstantiationABI::kFunctionTypeArgumentsReg,
+                          InstantiationABI::kResultTypeArgumentsReg);
+    }
+    __ BranchIf(EQUAL, &type_arguments_instantiated);
+    __ Bind(&non_null_type_args);
+  }
+
+  compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
+                             locs(), deopt_id());
+  __ Bind(&type_arguments_instantiated);
+}
+
 LocationSummary* DeoptimizeInstr::MakeLocationSummary(Zone* zone,
                                                       bool opt) const {
   return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kNoCall);
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 0655206..02b811a 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -3456,69 +3456,6 @@
   __ Bind(&done);
 }
 
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  const intptr_t kNumInputs = 3;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* locs = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  locs->set_in(0, Location::RegisterLocation(
-                      InstantiationABI::kInstantiatorTypeArgumentsReg));
-  locs->set_in(1, Location::RegisterLocation(
-                      InstantiationABI::kFunctionTypeArgumentsReg));
-  locs->set_in(2, Location::RegisterLocation(
-                      InstantiationABI::kUninstantiatedTypeArgumentsReg));
-  locs->set_out(
-      0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
-  return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
-    FlowGraphCompiler* compiler) {
-  // We should never try and instantiate a TAV known at compile time to be null,
-  // so we can use a null value below for the dynamic case.
-  ASSERT(!type_arguments()->BindsToConstant() ||
-         !type_arguments()->BoundConstant().IsNull());
-  const auto& type_args =
-      type_arguments()->BindsToConstant()
-          ? TypeArguments::Cast(type_arguments()->BoundConstant())
-          : Object::null_type_arguments();
-  const intptr_t len = type_args.Length();
-  const bool can_function_type_args_be_null =
-      function_type_arguments()->CanBe(Object::null_object());
-
-  compiler::Label type_arguments_instantiated;
-  if (type_args.IsNull()) {
-    // Currently we only create dynamic InstantiateTypeArguments instructions
-    // in cases where we know the type argument is uninstantiated at runtime,
-    // so there are no extra checks needed to call the stub successfully.
-  } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
-             can_function_type_args_be_null) {
-    // If both the instantiator and function type arguments are null and if the
-    // type argument vector instantiated from null becomes a vector of dynamic,
-    // then use null as the type arguments.
-    //
-    // 'instantiator_type_args_reg' is a TypeArguments object (or null).
-    // 'function_type_args_reg' is a TypeArguments object (or null).
-    const Register instantiator_type_args_reg = locs()->in(0).reg();
-    const Register function_type_args_reg = locs()->in(1).reg();
-    const Register result_reg = locs()->out(0).reg();
-    ASSERT(result_reg != instantiator_type_args_reg &&
-           result_reg != function_type_args_reg);
-    __ LoadObject(result_reg, Object::null_object());
-    __ cmp(instantiator_type_args_reg, compiler::Operand(result_reg));
-    if (!function_type_arguments()->BindsToConstant()) {
-      __ cmp(function_type_args_reg, compiler::Operand(result_reg), EQ);
-    }
-    __ b(&type_arguments_instantiated, EQ);
-  }
-  // Lookup cache in stub before calling runtime.
-  compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
-                             locs());
-  __ Bind(&type_arguments_instantiated);
-}
-
 LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
     Zone* zone,
     bool opt) const {
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index a4d8689..170d8e6 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -3031,72 +3031,6 @@
   __ Bind(&done);
 }
 
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  const intptr_t kNumInputs = 3;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* locs = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  locs->set_in(0, Location::RegisterLocation(
-                      InstantiationABI::kInstantiatorTypeArgumentsReg));
-  locs->set_in(1, Location::RegisterLocation(
-                      InstantiationABI::kFunctionTypeArgumentsReg));
-  locs->set_in(2, Location::RegisterLocation(
-                      InstantiationABI::kUninstantiatedTypeArgumentsReg));
-  locs->set_out(
-      0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
-  return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
-    FlowGraphCompiler* compiler) {
-  // We should never try and instantiate a TAV known at compile time to be null,
-  // so we can use a null value below for the dynamic case.
-  ASSERT(!type_arguments()->BindsToConstant() ||
-         !type_arguments()->BoundConstant().IsNull());
-  const auto& type_args =
-      type_arguments()->BindsToConstant()
-          ? TypeArguments::Cast(type_arguments()->BoundConstant())
-          : Object::null_type_arguments();
-  const intptr_t len = type_args.Length();
-  const bool can_function_type_args_be_null =
-      function_type_arguments()->CanBe(Object::null_object());
-
-  compiler::Label type_arguments_instantiated;
-  if (type_args.IsNull()) {
-    // Currently we only create dynamic InstantiateTypeArguments instructions
-    // in cases where we know the type argument is uninstantiated at runtime,
-    // so there are no extra checks needed to call the stub successfully.
-  } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
-             can_function_type_args_be_null) {
-    // If both the instantiator and function type arguments are null and if the
-    // type argument vector instantiated from null becomes a vector of dynamic,
-    // then use null as the type arguments.
-    compiler::Label non_null_type_args;
-    // 'instantiator_type_args_reg' is a TypeArguments object (or null).
-    // 'function_type_args_reg' is a TypeArguments object (or null).
-    const Register instantiator_type_args_reg = locs()->in(0).reg();
-    const Register function_type_args_reg = locs()->in(1).reg();
-    const Register result_reg = locs()->out(0).reg();
-    ASSERT(result_reg != instantiator_type_args_reg &&
-           result_reg != function_type_args_reg);
-    __ LoadObject(result_reg, Object::null_object());
-    __ CompareRegisters(instantiator_type_args_reg, result_reg);
-    if (!function_type_arguments()->BindsToConstant()) {
-      __ b(&non_null_type_args, NE);
-      __ CompareRegisters(function_type_args_reg, result_reg);
-    }
-    __ b(&type_arguments_instantiated, EQ);
-    __ Bind(&non_null_type_args);
-  }
-  // Lookup cache in stub before calling runtime.
-
-  compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
-                             locs());
-  __ Bind(&type_arguments_instantiated);
-}
-
 LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
     Zone* zone,
     bool opt) const {
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 2b8fd43..3381064 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -2782,71 +2782,6 @@
   __ Bind(&done);
 }
 
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  const intptr_t kNumInputs = 3;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* locs = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  locs->set_in(0, Location::RegisterLocation(
-                      InstantiationABI::kInstantiatorTypeArgumentsReg));
-  locs->set_in(1, Location::RegisterLocation(
-                      InstantiationABI::kFunctionTypeArgumentsReg));
-  locs->set_in(2, Location::RegisterLocation(
-                      InstantiationABI::kUninstantiatedTypeArgumentsReg));
-  locs->set_out(
-      0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
-  return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
-    FlowGraphCompiler* compiler) {
-  // We should never try and instantiate a TAV known at compile time to be null,
-  // so we can use a null value below for the dynamic case.
-  ASSERT(!type_arguments()->BindsToConstant() ||
-         !type_arguments()->BoundConstant().IsNull());
-  const auto& type_args =
-      type_arguments()->BindsToConstant()
-          ? TypeArguments::Cast(type_arguments()->BoundConstant())
-          : Object::null_type_arguments();
-  const intptr_t len = type_args.Length();
-  const bool can_function_type_args_be_null =
-      function_type_arguments()->CanBe(Object::null_object());
-
-  compiler::Label type_arguments_instantiated;
-  if (type_args.IsNull()) {
-    // Currently we only create dynamic InstantiateTypeArguments instructions
-    // in cases where we know the type argument is uninstantiated at runtime,
-    // so there are no extra checks needed to call the stub successfully.
-  } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
-             can_function_type_args_be_null) {
-    // If both the instantiator and function type arguments are null and if the
-    // type argument vector instantiated from null becomes a vector of dynamic,
-    // then use null as the type arguments.
-    compiler::Label non_null_type_args;
-    // 'instantiator_type_args_reg' is a TypeArguments object (or null).
-    // 'function_type_args_reg' is a TypeArguments object (or null).
-    const Register instantiator_type_args_reg = locs()->in(0).reg();
-    const Register function_type_args_reg = locs()->in(1).reg();
-    const Register result_reg = locs()->out(0).reg();
-    ASSERT(result_reg != instantiator_type_args_reg &&
-           result_reg != function_type_args_reg);
-    __ LoadObject(result_reg, Object::null_object());
-    __ cmpl(instantiator_type_args_reg, result_reg);
-    if (!function_type_arguments()->BindsToConstant()) {
-      __ j(NOT_EQUAL, &non_null_type_args, compiler::Assembler::kNearJump);
-      __ cmpl(function_type_args_reg, result_reg);
-    }
-    __ j(EQUAL, &type_arguments_instantiated, compiler::Assembler::kNearJump);
-    __ Bind(&non_null_type_args);
-  }
-  // Lookup cache in stub before calling runtime.
-  compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
-                             locs());
-  __ Bind(&type_arguments_instantiated);
-}
-
 LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
     Zone* zone,
     bool opt) const {
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 574395a..f755d79 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -3115,71 +3115,6 @@
   __ Bind(&done);
 }
 
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  const intptr_t kNumInputs = 3;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* locs = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  locs->set_in(0, Location::RegisterLocation(
-                      InstantiationABI::kInstantiatorTypeArgumentsReg));
-  locs->set_in(1, Location::RegisterLocation(
-                      InstantiationABI::kFunctionTypeArgumentsReg));
-  locs->set_in(2, Location::RegisterLocation(
-                      InstantiationABI::kUninstantiatedTypeArgumentsReg));
-  locs->set_out(
-      0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
-  return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
-    FlowGraphCompiler* compiler) {
-  // We should never try and instantiate a TAV known at compile time to be null,
-  // so we can use a null value below for the dynamic case.
-  ASSERT(!type_arguments()->BindsToConstant() ||
-         !type_arguments()->BoundConstant().IsNull());
-  const auto& type_args =
-      type_arguments()->BindsToConstant()
-          ? TypeArguments::Cast(type_arguments()->BoundConstant())
-          : Object::null_type_arguments();
-  const intptr_t len = type_args.Length();
-  const bool can_function_type_args_be_null =
-      function_type_arguments()->CanBe(Object::null_object());
-
-  compiler::Label type_arguments_instantiated;
-  if (type_args.IsNull()) {
-    // Currently we only create dynamic InstantiateTypeArguments instructions
-    // in cases where we know the type argument is uninstantiated at runtime,
-    // so there are no extra checks needed to call the stub successfully.
-  } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
-             can_function_type_args_be_null) {
-    // If both the instantiator and function type arguments are null and if the
-    // type argument vector instantiated from null becomes a vector of dynamic,
-    // then use null as the type arguments.
-    compiler::Label non_null_type_args;
-    // 'instantiator_type_args_reg' is a TypeArguments object (or null).
-    // 'function_type_args_reg' is a TypeArguments object (or null).
-    const Register instantiator_type_args_reg = locs()->in(0).reg();
-    const Register function_type_args_reg = locs()->in(1).reg();
-    const Register result_reg = locs()->out(0).reg();
-    ASSERT(result_reg != instantiator_type_args_reg &&
-           result_reg != function_type_args_reg);
-    __ LoadObject(result_reg, Object::null_object());
-    __ cmpq(instantiator_type_args_reg, result_reg);
-    if (!function_type_arguments()->BindsToConstant()) {
-      __ j(NOT_EQUAL, &non_null_type_args, compiler::Assembler::kNearJump);
-      __ cmpq(function_type_args_reg, result_reg);
-    }
-    __ j(EQUAL, &type_arguments_instantiated, compiler::Assembler::kNearJump);
-    __ Bind(&non_null_type_args);
-  }
-  // Lookup cache in stub before calling runtime.
-  compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
-                             locs());
-  __ Bind(&type_arguments_instantiated);
-}
-
 LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
     Zone* zone,
     bool opt) const {
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 19a2ace..cd4cbbb 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -399,12 +399,21 @@
              .Equals(Symbols::FfiStructLayout()));
   ASSERT(String::Handle(zone, library.url()).Equals(Symbols::DartFfi()));
   const auto& struct_layout_fields = Array::Handle(zone, clazz.fields());
-  ASSERT(struct_layout_fields.Length() == 1);
-  const auto& field =
+  ASSERT(struct_layout_fields.Length() == 2);
+  const auto& types_field =
       Field::Handle(zone, Field::RawCast(struct_layout_fields.At(0)));
-  ASSERT(String::Handle(zone, field.name()).Equals(Symbols::FfiFieldTypes()));
+  ASSERT(String::Handle(zone, types_field.name())
+             .Equals(Symbols::FfiFieldTypes()));
   const auto& field_types =
-      Array::Handle(zone, Array::RawCast(struct_layout.GetField(field)));
+      Array::Handle(zone, Array::RawCast(struct_layout.GetField(types_field)));
+  const auto& packed_field =
+      Field::Handle(zone, Field::RawCast(struct_layout_fields.At(1)));
+  ASSERT(String::Handle(zone, packed_field.name())
+             .Equals(Symbols::FfiFieldPacking()));
+  const auto& packed_value = Integer::Handle(
+      zone, Integer::RawCast(struct_layout.GetField(packed_field)));
+  const intptr_t member_packing =
+      packed_value.IsNull() ? kMaxInt32 : packed_value.AsInt64Value();
 
   auto& field_instance = Instance::Handle(zone);
   auto& field_type = AbstractType::Handle(zone);
@@ -445,7 +454,8 @@
     }
   }
 
-  return NativeCompoundType::FromNativeTypes(zone, field_native_types);
+  return NativeCompoundType::FromNativeTypes(zone, field_native_types,
+                                             member_packing);
 }
 #endif
 
@@ -736,23 +746,32 @@
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
-bool NativePrimitiveType::ContainsUnalignedMembers() const {
+bool NativePrimitiveType::ContainsUnalignedMembers(intptr_t offset) const {
+  return offset % AlignmentInBytesField() != 0;
+}
+
+bool NativeArrayType::ContainsUnalignedMembers(intptr_t offset) const {
+  const intptr_t element_size = element_type_.SizeInBytes();
+  // We're only checking the first two elements of the array.
+  //
+  // If the element size is divisible by the alignment of the largest type
+  // contained within the element type, the alignment of all elements is the
+  // same. If not, either the first or the second element is unaligned.
+  const intptr_t max_check = 2;
+  for (intptr_t i = 0; i < Utils::Minimum(length_, max_check); i++) {
+    const intptr_t element_offset = i * element_size;
+    if (element_type_.ContainsUnalignedMembers(offset + element_offset)) {
+      return true;
+    }
+  }
   return false;
 }
 
-bool NativeArrayType::ContainsUnalignedMembers() const {
-  return element_type_.ContainsUnalignedMembers();
-}
-
-bool NativeCompoundType::ContainsUnalignedMembers() const {
+bool NativeCompoundType::ContainsUnalignedMembers(intptr_t offset) const {
   for (intptr_t i = 0; i < members_.length(); i++) {
     const auto& member = *members_.At(i);
     const intptr_t member_offset = member_offsets_.At(i);
-    const intptr_t member_alignment = member.AlignmentInBytesField();
-    if (member_offset % member_alignment != 0) {
-      return true;
-    }
-    if (member.ContainsUnalignedMembers()) {
+    if (member.ContainsUnalignedMembers(offset + member_offset)) {
       return true;
     }
   }
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 8fbcbc5..e7a156e 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -98,7 +98,7 @@
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
   // True iff any members are misaligned recursively due to packing.
-  virtual bool ContainsUnalignedMembers() const = 0;
+  virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const = 0;
 
 #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
   // NativeTypes which are available as unboxed Representations.
@@ -188,7 +188,7 @@
   virtual bool ContainsOnlyFloats(Range range) const;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
-  virtual bool ContainsUnalignedMembers() const;
+  virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
 
 #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
   virtual bool IsExpressibleAsRepresentation() const;
@@ -239,7 +239,7 @@
   virtual bool ContainsOnlyFloats(Range range) const;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
-  virtual bool ContainsUnalignedMembers() const;
+  virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
 
   virtual bool Equals(const NativeType& other) const;
 
@@ -298,7 +298,7 @@
   intptr_t NumberOfWordSizeChunksNotOnlyFloat() const;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
-  virtual bool ContainsUnalignedMembers() const;
+  virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
 
   // Whether this type has only same-size floating point members.
   //
diff --git a/runtime/vm/compiler/ffi/native_type_test.cc b/runtime/vm/compiler/ffi/native_type_test.cc
index d6d5ece..63cc7b4 100644
--- a/runtime/vm/compiler/ffi/native_type_test.cc
+++ b/runtime/vm/compiler/ffi/native_type_test.cc
@@ -214,6 +214,61 @@
   EXPECT(struct_type.ContainsUnalignedMembers());
 }
 
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_array) {
+  const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
+  const auto& uint16_type = *new (Z) NativePrimitiveType(kUint16);
+
+  auto& inner_members = *new (Z) NativeTypes(Z, 2);
+  inner_members.Add(&uint16_type);
+  inner_members.Add(&uint8_type);
+  const intptr_t packing = 1;
+  const auto& inner_struct_type =
+      NativeCompoundType::FromNativeTypes(Z, inner_members, packing);
+
+  EXPECT_EQ(3, inner_struct_type.SizeInBytes());
+  // Non-windows x64 considers this struct as all members aligned, even though
+  // its size is not a multiple of its individual member alignment.
+  EXPECT(!inner_struct_type.ContainsUnalignedMembers());
+
+  const auto& array_type = *new (Z) NativeArrayType(inner_struct_type, 2);
+
+  auto& members = *new (Z) NativeTypes(Z, 1);
+  members.Add(&array_type);
+  const auto& struct_type = NativeCompoundType::FromNativeTypes(Z, members);
+
+  EXPECT_EQ(6, struct_type.SizeInBytes());
+  // Non-windows x64 passes this as a struct with unaligned members, because
+  // the second element of the array contains unaligned members.
+  EXPECT(struct_type.ContainsUnalignedMembers());
+}
+
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_nested) {
+  const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
+  const auto& uint32_type = *new (Z) NativePrimitiveType(kUint32);
+
+  auto& inner_members = *new (Z) NativeTypes(Z, 2);
+  inner_members.Add(&uint32_type);
+  inner_members.Add(&uint8_type);
+  const intptr_t packing = 1;
+  const auto& inner_struct_type =
+      NativeCompoundType::FromNativeTypes(Z, inner_members, packing);
+
+  EXPECT_EQ(5, inner_struct_type.SizeInBytes());
+  // Non-windows x64 considers this struct as all members aligned, even though
+  // its size is not a multiple of its individual member alignment.
+  EXPECT(!inner_struct_type.ContainsUnalignedMembers());
+
+  auto& members = *new (Z) NativeTypes(Z, 2);
+  members.Add(&uint8_type);
+  members.Add(&inner_struct_type);
+  const auto& struct_type = NativeCompoundType::FromNativeTypes(Z, members);
+
+  EXPECT_EQ(6, struct_type.SizeInBytes());
+  // Non-windows x64 passes this as a struct with unaligned members, even
+  // though the nested struct itself has all its members aligned in isolation.
+  EXPECT(struct_type.ContainsUnalignedMembers());
+}
+
 }  // namespace ffi
 }  // namespace compiler
 }  // namespace dart
diff --git a/runtime/vm/compiler/ffi/recognized_method.cc b/runtime/vm/compiler/ffi/recognized_method.cc
index f13f041..6900a83 100644
--- a/runtime/vm/compiler/ffi/recognized_method.cc
+++ b/runtime/vm/compiler/ffi/recognized_method.cc
@@ -57,6 +57,31 @@
     CLASS_LIST_FFI_NUMERIC(LOAD_STORE)
     LOAD_STORE(Pointer)
 #undef LOAD_STORE
+    case MethodRecognizer::kFfiLoadFloatUnaligned:
+    case MethodRecognizer::kFfiStoreFloatUnaligned:
+      return kFfiFloatCid;
+    case MethodRecognizer::kFfiLoadDoubleUnaligned:
+    case MethodRecognizer::kFfiStoreDoubleUnaligned:
+      return kFfiDoubleCid;
+    default:
+      UNREACHABLE();
+  }
+}
+
+AlignmentType RecognizedMethodAlignment(MethodRecognizer::Kind kind) {
+  switch (kind) {
+#define LOAD_STORE(type)                                                       \
+  case MethodRecognizer::kFfiLoad##type:                                       \
+  case MethodRecognizer::kFfiStore##type:                                      \
+    return kAlignedAccess;
+    CLASS_LIST_FFI_NUMERIC(LOAD_STORE)
+    LOAD_STORE(Pointer)
+#undef LOAD_STORE
+    case MethodRecognizer::kFfiLoadFloatUnaligned:
+    case MethodRecognizer::kFfiStoreFloatUnaligned:
+    case MethodRecognizer::kFfiLoadDoubleUnaligned:
+    case MethodRecognizer::kFfiStoreDoubleUnaligned:
+      return kUnalignedAccess;
     default:
       UNREACHABLE();
   }
diff --git a/runtime/vm/compiler/ffi/recognized_method.h b/runtime/vm/compiler/ffi/recognized_method.h
index 8cbfca8..993306e 100644
--- a/runtime/vm/compiler/ffi/recognized_method.h
+++ b/runtime/vm/compiler/ffi/recognized_method.h
@@ -11,6 +11,7 @@
 
 #include <platform/globals.h>
 
+#include "vm/compiler/backend/il.h"
 #include "vm/compiler/method_recognizer.h"
 
 namespace dart {
@@ -25,6 +26,8 @@
 // Returns the kFFi<type>Cid for the recognized load/store method [kind].
 classid_t RecognizedMethodTypeArgCid(MethodRecognizer::Kind kind);
 
+AlignmentType RecognizedMethodAlignment(MethodRecognizer::Kind kind);
+
 }  // namespace ffi
 
 }  // namespace compiler
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index a106f1e..7527b84 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -370,7 +370,8 @@
 
 Fragment BaseFlowGraphBuilder::LoadIndexed(classid_t class_id,
                                            intptr_t index_scale,
-                                           bool index_unboxed) {
+                                           bool index_unboxed,
+                                           AlignmentType alignment) {
   Value* index = Pop();
   // A C pointer if index_unboxed, otherwise a boxed Dart value.
   Value* array = Pop();
@@ -379,7 +380,7 @@
   // all cases.
   LoadIndexedInstr* instr = new (Z)
       LoadIndexedInstr(array, index, index_unboxed, index_scale, class_id,
-                       kAlignedAccess, DeoptId::kNone, InstructionSource());
+                       alignment, DeoptId::kNone, InstructionSource());
   Push(instr);
   return Fragment(instr);
 }
@@ -636,10 +637,8 @@
 
 Fragment BaseFlowGraphBuilder::StoreIndexedTypedData(classid_t class_id,
                                                      intptr_t index_scale,
-                                                     bool index_unboxed) {
-  // We use C behavior when dereferencing pointers, we assume alignment.
-  const AlignmentType alignment = kAlignedAccess;
-
+                                                     bool index_unboxed,
+                                                     AlignmentType alignment) {
   Value* value = Pop();
   Value* index = Pop();
   Value* c_pointer = Pop();
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index 50e198a..e55d218 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -167,7 +167,8 @@
   // Pass true for index_unboxed if indexing into external typed data.
   Fragment LoadIndexed(classid_t class_id,
                        intptr_t index_scale = compiler::target::kWordSize,
-                       bool index_unboxed = false);
+                       bool index_unboxed = false,
+                       AlignmentType alignment = kAlignedAccess);
 
   Fragment LoadUntagged(intptr_t offset);
   Fragment StoreUntagged(intptr_t offset);
@@ -213,7 +214,8 @@
   // Takes a [class_id] valid for StoreIndexed.
   Fragment StoreIndexedTypedData(classid_t class_id,
                                  intptr_t index_scale,
-                                 bool index_unboxed);
+                                 bool index_unboxed,
+                                 AlignmentType alignment = kAlignedAccess);
 
   // Sign-extends kUnboxedInt32 and zero-extends kUnboxedUint32.
   Fragment Box(Representation from);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 7437768..3d38bf7 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -831,7 +831,9 @@
     case MethodRecognizer::kFfiLoadUint64:
     case MethodRecognizer::kFfiLoadIntPtr:
     case MethodRecognizer::kFfiLoadFloat:
+    case MethodRecognizer::kFfiLoadFloatUnaligned:
     case MethodRecognizer::kFfiLoadDouble:
+    case MethodRecognizer::kFfiLoadDoubleUnaligned:
     case MethodRecognizer::kFfiLoadPointer:
     case MethodRecognizer::kFfiStoreInt8:
     case MethodRecognizer::kFfiStoreInt16:
@@ -843,7 +845,9 @@
     case MethodRecognizer::kFfiStoreUint64:
     case MethodRecognizer::kFfiStoreIntPtr:
     case MethodRecognizer::kFfiStoreFloat:
+    case MethodRecognizer::kFfiStoreFloatUnaligned:
     case MethodRecognizer::kFfiStoreDouble:
+    case MethodRecognizer::kFfiStoreDoubleUnaligned:
     case MethodRecognizer::kFfiStorePointer:
     case MethodRecognizer::kFfiFromAddress:
     case MethodRecognizer::kFfiGetAddress:
@@ -1288,10 +1292,14 @@
     case MethodRecognizer::kFfiLoadUint64:
     case MethodRecognizer::kFfiLoadIntPtr:
     case MethodRecognizer::kFfiLoadFloat:
+    case MethodRecognizer::kFfiLoadFloatUnaligned:
     case MethodRecognizer::kFfiLoadDouble:
+    case MethodRecognizer::kFfiLoadDoubleUnaligned:
     case MethodRecognizer::kFfiLoadPointer: {
       const classid_t ffi_type_arg_cid =
           compiler::ffi::RecognizedMethodTypeArgCid(kind);
+      const AlignmentType alignment =
+          compiler::ffi::RecognizedMethodAlignment(kind);
       const classid_t typed_data_cid =
           compiler::ffi::ElementTypedDataCid(ffi_type_arg_cid);
       const auto& native_rep = compiler::ffi::NativeType::FromTypedDataClassId(
@@ -1314,10 +1322,13 @@
       body += LoadLocal(arg_offset_not_null);
       body += UnboxTruncate(kUnboxedFfiIntPtr);
       body += LoadIndexed(typed_data_cid, /*index_scale=*/1,
-                          /*index_unboxed=*/true);
+                          /*index_unboxed=*/true, alignment);
       if (kind == MethodRecognizer::kFfiLoadFloat ||
-          kind == MethodRecognizer::kFfiLoadDouble) {
-        if (kind == MethodRecognizer::kFfiLoadFloat) {
+          kind == MethodRecognizer::kFfiLoadFloatUnaligned ||
+          kind == MethodRecognizer::kFfiLoadDouble ||
+          kind == MethodRecognizer::kFfiLoadDoubleUnaligned) {
+        if (kind == MethodRecognizer::kFfiLoadFloat ||
+            kind == MethodRecognizer::kFfiLoadFloatUnaligned) {
           body += FloatToDouble();
         }
         body += Box(kUnboxedDouble);
@@ -1369,10 +1380,14 @@
     case MethodRecognizer::kFfiStoreUint64:
     case MethodRecognizer::kFfiStoreIntPtr:
     case MethodRecognizer::kFfiStoreFloat:
+    case MethodRecognizer::kFfiStoreFloatUnaligned:
     case MethodRecognizer::kFfiStoreDouble:
+    case MethodRecognizer::kFfiStoreDoubleUnaligned:
     case MethodRecognizer::kFfiStorePointer: {
       const classid_t ffi_type_arg_cid =
           compiler::ffi::RecognizedMethodTypeArgCid(kind);
+      const AlignmentType alignment =
+          compiler::ffi::RecognizedMethodAlignment(kind);
       const classid_t typed_data_cid =
           compiler::ffi::ElementTypedDataCid(ffi_type_arg_cid);
       const auto& native_rep = compiler::ffi::NativeType::FromTypedDataClassId(
@@ -1439,16 +1454,19 @@
         body += LoadUntagged(compiler::target::Pointer::data_field_offset());
         body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
       } else if (kind == MethodRecognizer::kFfiStoreFloat ||
-                 kind == MethodRecognizer::kFfiStoreDouble) {
+                 kind == MethodRecognizer::kFfiStoreFloatUnaligned ||
+                 kind == MethodRecognizer::kFfiStoreDouble ||
+                 kind == MethodRecognizer::kFfiStoreDoubleUnaligned) {
         body += UnboxTruncate(kUnboxedDouble);
-        if (kind == MethodRecognizer::kFfiStoreFloat) {
+        if (kind == MethodRecognizer::kFfiStoreFloat ||
+            kind == MethodRecognizer::kFfiStoreFloatUnaligned) {
           body += DoubleToFloat();
         }
       } else {
         body += UnboxTruncate(native_rep.AsRepresentationOverApprox(zone_));
       }
       body += StoreIndexedTypedData(typed_data_cid, /*index_scale=*/1,
-                                    /*index_unboxed=*/true);
+                                    /*index_unboxed=*/true, alignment);
       body += Drop();  // Drop [arg_value].
       body += Drop();  // Drop [arg_offset].
       body += NullConstant();
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index c53fc34..e9b014d 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -176,7 +176,9 @@
   V(::, _loadUint64, FfiLoadUint64, 0x0505fdcc)                                \
   V(::, _loadIntPtr, FfiLoadIntPtr, 0xebd9b43e)                                \
   V(::, _loadFloat, FfiLoadFloat, 0xf8d9845d)                                  \
+  V(::, _loadFloatUnaligned, FfiLoadFloatUnaligned, 0xc8c8dfff)                \
   V(::, _loadDouble, FfiLoadDouble, 0xf70cc619)                                \
+  V(::, _loadDoubleUnaligned, FfiLoadDoubleUnaligned, 0xc99ebd39)              \
   V(::, _loadPointer, FfiLoadPointer, 0x4e79d0fc)                              \
   V(::, _storeInt8, FfiStoreInt8, 0xdf50af0c)                                  \
   V(::, _storeInt16, FfiStoreInt16, 0xd84df332)                                \
@@ -188,7 +190,9 @@
   V(::, _storeUint64, FfiStoreUint64, 0xe2d93239)                              \
   V(::, _storeIntPtr, FfiStoreIntPtr, 0x080266a8)                              \
   V(::, _storeFloat, FfiStoreFloat, 0x6484f07e)                                \
+  V(::, _storeFloatUnaligned, FfiStoreFloatUnaligned, 0x600a9203)              \
   V(::, _storeDouble, FfiStoreDouble, 0x42998c64)                              \
+  V(::, _storeDoubleUnaligned, FfiStoreDoubleUnaligned, 0x3dced75b)            \
   V(::, _storePointer, FfiStorePointer, 0xea6b7751)                            \
   V(::, _fromAddress, FfiFromAddress, 0xfd8cb1cc)                              \
   V(Pointer, get:address, FfiGetAddress, 0x7cde87be)                           \
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index 03356ae..b41e1f9 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -376,12 +376,14 @@
                                     /*null_safety=*/true);
 }
 
-// Version of Instance::NullIsAssignableTo() used when the destination type is
-// not known at compile time. Must be kept in sync.
+// Version of Instance::NullIsAssignableTo(other, inst_tav, fun_tav) used when
+// the destination type was not known at compile time. Must be kept in sync.
 //
 // Inputs:
 // - TypeTestABI::kInstanceReg: Object to check for assignability.
 // - TypeTestABI::kDstTypeReg: Destination type.
+// - TypeTestABI::kInstantiatorTypeArgumentsReg: Instantiator TAV.
+// - TypeTestABI::kFunctionTypeArgumentsReg: Function TAV.
 //
 // Non-preserved non-output scratch registers:
 // - TypeTestABI::kScratchReg (only on non-IA32 architectures)
@@ -396,85 +398,120 @@
   // The only case where the original value of kSubtypeTestCacheReg is needed
   // after the stub call is on IA32, where it's currently preserved on the stack
   // before calling the stub (as it's also CODE_REG on that architecture), so we
-  // both use it as a scratch and clobber it for the return value.
-  const Register scratch1_reg = TypeTestABI::kSubtypeTestCacheReg;
+  // both use it as a scratch to hold the current type to inspect and also
+  // clobber it for the return value.
+  const Register kCurrentTypeReg = TypeTestABI::kSubtypeTestCacheReg;
   // We reuse the first scratch register as the output register because we're
-  // always guaranteed to have a type in it (starting with kDstType), and all
-  // non-Smi ObjectPtrs are non-zero values.
-  const Register output_reg = scratch1_reg;
+  // always guaranteed to have a type in it (starting with the contents of
+  // kDstTypeReg), and all non-Smi ObjectPtrs are non-zero values.
+  const Register kOutputReg = kCurrentTypeReg;
 #if defined(TARGET_ARCH_IA32)
   // The remaining scratch registers are preserved and restored before exit on
   // IA32. Because  we have few registers to choose from (which are all used in
   // TypeTestABI), use specific TestTypeABI registers.
-  const Register scratch2_reg = TypeTestABI::kFunctionTypeArgumentsReg;
+  const Register kScratchReg = TypeTestABI::kFunctionTypeArgumentsReg;
   // Preserve non-output scratch registers.
-  __ PushRegister(scratch2_reg);
+  __ PushRegister(kScratchReg);
 #else
-  const Register scratch2_reg = TypeTestABI::kScratchReg;
+  const Register kScratchReg = TypeTestABI::kScratchReg;
 #endif
-  static_assert(scratch1_reg != scratch2_reg,
+  static_assert(kCurrentTypeReg != kScratchReg,
                 "code assumes distinct scratch registers");
 
   compiler::Label is_assignable, done;
   // Initialize the first scratch register (and thus the output register) with
   // the destination type. We do this before the check to ensure the output
   // register has a non-zero value if !null_safety and kInstanceReg is not null.
-  __ MoveRegister(scratch1_reg, TypeTestABI::kDstTypeReg);
+  __ MoveRegister(kCurrentTypeReg, TypeTestABI::kDstTypeReg);
   __ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
   if (null_safety) {
     compiler::Label check_null_assignable;
     // Skip checking the type if not null.
-    __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
+    __ BranchIf(NOT_EQUAL, &done);
     __ Bind(&check_null_assignable);
     // scratch1_reg: Current type to check.
-    EnsureIsTypeOrFunctionTypeOrTypeParameter(assembler, scratch1_reg,
-                                              scratch2_reg);
+    EnsureIsTypeOrFunctionTypeOrTypeParameter(assembler, kCurrentTypeReg,
+                                              kScratchReg);
     compiler::Label is_not_type;
-    __ CompareClassId(scratch1_reg, kTypeCid, scratch2_reg);
+    __ CompareClassId(kCurrentTypeReg, kTypeCid, kScratchReg);
     __ BranchIf(NOT_EQUAL, &is_not_type, compiler::Assembler::kNearJump);
     __ CompareTypeNullabilityWith(
-        scratch1_reg, static_cast<int8_t>(Nullability::kNonNullable));
-    __ BranchIf(NOT_EQUAL, &is_assignable, compiler::Assembler::kNearJump);
+        kCurrentTypeReg, static_cast<int8_t>(Nullability::kNonNullable));
+    __ BranchIf(NOT_EQUAL, &is_assignable);
     // FutureOr is a special case because it may have the non-nullable bit set,
     // but FutureOr<T> functions as the union of T and Future<T>, so it must be
     // unwrapped to see if T is nullable.
     __ LoadField(
-        scratch2_reg,
-        compiler::FieldAddress(scratch1_reg,
+        kScratchReg,
+        compiler::FieldAddress(kCurrentTypeReg,
                                compiler::target::Type::type_class_id_offset()));
-    __ SmiUntag(scratch2_reg);
-    __ CompareImmediate(scratch2_reg, kFutureOrCid);
-    __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
-    __ LoadField(scratch2_reg,
-                 compiler::FieldAddress(
-                     scratch1_reg, compiler::target::Type::arguments_offset()));
-    __ CompareObject(scratch2_reg, Object::null_object());
+    __ SmiUntag(kScratchReg);
+    __ CompareImmediate(kScratchReg, kFutureOrCid);
+    __ BranchIf(NOT_EQUAL, &done);
+    __ LoadField(kScratchReg, compiler::FieldAddress(
+                                  kCurrentTypeReg,
+                                  compiler::target::Type::arguments_offset()));
+    __ CompareObject(kScratchReg, Object::null_object());
     // If the arguments are null, then unwrapping gives the dynamic type,
     // which can take null.
-    __ BranchIf(EQUAL, &is_assignable, compiler::Assembler::kNearJump);
+    __ BranchIf(EQUAL, &is_assignable);
     __ LoadField(
-        scratch1_reg,
+        kCurrentTypeReg,
         compiler::FieldAddress(
-            scratch2_reg, compiler::target::TypeArguments::type_at_offset(0)));
+            kScratchReg, compiler::target::TypeArguments::type_at_offset(0)));
     __ Jump(&check_null_assignable, compiler::Assembler::kNearJump);
     __ Bind(&is_not_type);
-    // Null is assignable to a type parameter only if it is nullable.
+    // Null is assignable to a type parameter only if it is nullable or if the
+    // instantiation is nullable.
     __ LoadFieldFromOffset(
-        scratch2_reg, scratch1_reg,
+        kScratchReg, kCurrentTypeReg,
         compiler::target::TypeParameter::nullability_offset(), kByte);
-    __ CompareImmediate(scratch2_reg,
+    __ CompareImmediate(kScratchReg,
                         static_cast<int8_t>(Nullability::kNonNullable));
-    __ BranchIf(EQUAL, &done, compiler::Assembler::kNearJump);
+    __ BranchIf(NOT_EQUAL, &is_assignable);
+
+    // Don't set kScratchReg in here as on IA32, that's the function TAV reg.
+    auto handle_case = [&](Register tav) {
+      // We can reuse kCurrentTypeReg to hold the index because we no longer
+      // need the type parameter afterwards.
+      auto const kIndexReg = kCurrentTypeReg;
+      // If the TAV is null, resolving gives the (nullable) dynamic type.
+      __ CompareObject(tav, NullObject());
+      __ BranchIf(EQUAL, &is_assignable, Assembler::kNearJump);
+      // Resolve the type parameter to its instantiated type and loop.
+      __ LoadFieldFromOffset(kIndexReg, kCurrentTypeReg,
+                             target::TypeParameter::index_offset(), kTwoBytes);
+      __ LoadIndexedPayload(kCurrentTypeReg, tav,
+                            target::TypeArguments::types_offset(), kIndexReg,
+                            TIMES_WORD_SIZE);
+      __ Jump(&check_null_assignable);
+    };
+
+    Label function_type_param;
+    __ LoadFieldFromOffset(
+        kScratchReg, kCurrentTypeReg,
+        target::TypeParameter::parameterized_class_id_offset(),
+        kUnsignedTwoBytes);
+    __ CompareImmediate(kScratchReg, kFunctionCid);
+    __ BranchIf(EQUAL, &function_type_param, Assembler::kNearJump);
+    handle_case(TypeTestABI::kInstantiatorTypeArgumentsReg);
+    __ Bind(&function_type_param);
+#if defined(TARGET_ARCH_IA32)
+    // Function TAV is on top of stack because we're using that register as
+    // kScratchReg.
+    __ LoadFromStack(TypeTestABI::kFunctionTypeArgumentsReg, 0);
+#endif
+    handle_case(TypeTestABI::kFunctionTypeArgumentsReg);
   } else {
     // Null in non-null-safe mode is always assignable.
     __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
   }
   __ Bind(&is_assignable);
-  __ LoadImmediate(output_reg, 0);
+  __ LoadImmediate(kOutputReg, 0);
   __ Bind(&done);
 #if defined(TARGET_ARCH_IA32)
   // Restore preserved scratch registers.
-  __ PopRegister(scratch2_reg);
+  __ PopRegister(kScratchReg);
 #endif
   __ Ret();
 }
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 6147404..25ace1d 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -557,7 +557,7 @@
   }
 }
 
-static UNLESS_DEBUG(constexpr) const uword kReadOnlyGCBits =
+static constexpr uword kReadOnlyGCBits =
     UntaggedObject::OldBit::encode(true) |
     UntaggedObject::OldAndNotMarkedBit::encode(false) |
     UntaggedObject::OldAndNotRememberedBit::encode(true) |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index c47b47e..a0af589 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -8241,7 +8241,8 @@
     // If the argument type is the top type, no need to check.
     if (type.IsTopTypeForSubtyping()) return true;
     if (argument.IsNull()) {
-      return Instance::NullIsAssignableTo(type);
+      return Instance::NullIsAssignableTo(type, instantiator_type_args,
+                                          function_type_args);
     }
     return argument.IsAssignableTo(type, instantiator_type_args,
                                    function_type_args);
@@ -8304,6 +8305,11 @@
       if (!check_argument(argument, type, instantiator_type_arguments,
                           function_type_arguments)) {
         auto& name = String::Handle(zone, ParameterNameAt(param_index));
+        if (!type.IsInstantiated()) {
+          type = type.InstantiateFrom(instantiator_type_arguments,
+                                      function_type_arguments, kAllFree,
+                                      Heap::kNew);
+        }
         return ThrowTypeError(token_pos(), argument, type, name);
       }
       break;
@@ -18821,9 +18827,28 @@
     return NullIsAssignableTo(
         AbstractType::Handle(thread->zone(), other.UnwrapFutureOr()));
   }
+  // Since the TAVs are not available, for non-nullable type parameters
+  // this returns a conservative approximation of "not assignable" .
   return false;
 }
 
+// Must be kept in sync with GenerateNullIsAssignableToType in
+// stub_code_compiler.cc if any changes are made.
+bool Instance::NullIsAssignableTo(
+    const AbstractType& other,
+    const TypeArguments& other_instantiator_type_arguments,
+    const TypeArguments& other_function_type_arguments) {
+  // Do checks that don't require instantiation first.
+  if (NullIsAssignableTo(other)) return true;
+  if (!other.IsTypeParameter()) return false;
+  const auto& type = AbstractType::Handle(other.InstantiateFrom(
+      other_instantiator_type_arguments, other_function_type_arguments,
+      kAllFree, Heap::kNew));
+  // At runtime, uses of TypeRef should not occur.
+  ASSERT(!type.IsTypeRef());
+  return NullIsAssignableTo(type);
+}
+
 bool Instance::RuntimeTypeIsSubtypeOf(
     const AbstractType& other,
     const TypeArguments& other_instantiator_type_arguments,
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index d603fdc..0708f3d 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -7111,9 +7111,18 @@
 
   // Return true if the null instance can be assigned to a variable of [other]
   // type. Return false if null cannot be assigned or we cannot tell (if
-  // [other] is a type parameter in NNBD strong mode).
+  // [other] is a type parameter in NNBD strong mode). Only used for checks at
+  // compile time.
   static bool NullIsAssignableTo(const AbstractType& other);
 
+  // Return true if the null instance can be assigned to a variable of [other]
+  // type. Return false if null cannot be assigned. Used for checks at runtime,
+  // when the instantiator and function type argument vectors are available.
+  static bool NullIsAssignableTo(
+      const AbstractType& other,
+      const TypeArguments& other_instantiator_type_arguments,
+      const TypeArguments& other_function_type_arguments);
+
   bool IsValidNativeIndex(int index) const {
     return ((index >= 0) && (index < clazz()->untag()->num_native_fields_));
   }
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 79fa9df..a4f0b8a 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -153,7 +153,7 @@
     static constexpr intptr_t kMaxSizeTag =
         kMaxSizeTagInUnitsOfAlignment * kObjectAlignment;
 
-    static UNLESS_DEBUG(constexpr) uword encode(intptr_t size) {
+    static constexpr uword encode(intptr_t size) {
       return SizeBits::encode(SizeToTagValue(size));
     }
 
@@ -161,11 +161,11 @@
       return TagValueToSize(SizeBits::decode(tag));
     }
 
-    static UNLESS_DEBUG(constexpr) uword update(intptr_t size, uword tag) {
+    static constexpr uword update(intptr_t size, uword tag) {
       return SizeBits::update(SizeToTagValue(size), tag);
     }
 
-    static UNLESS_DEBUG(constexpr) bool SizeFits(intptr_t size) {
+    static constexpr bool SizeFits(intptr_t size) {
       DEBUG_ASSERT(Utils::IsAligned(size, kObjectAlignment));
       return (size <= kMaxSizeTag);
     }
@@ -175,8 +175,8 @@
     class SizeBits
         : public BitField<uword, intptr_t, kSizeTagPos, kSizeTagSize> {};
 
-    static UNLESS_DEBUG(constexpr) intptr_t SizeToTagValue(intptr_t size) {
-      DEBUG_ASSERT(Utils::IsAligned(size, kObjectAlignment));
+    static constexpr intptr_t SizeToTagValue(intptr_t size) {
+      assert(Utils::IsAligned(size, kObjectAlignment));
       return !SizeFits(size) ? 0 : (size >> kObjectAlignmentLog2);
     }
     static constexpr intptr_t TagValueToSize(intptr_t value) {
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index ff0ad5e..6688a92 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -125,6 +125,7 @@
   V(FfiDouble, "Double")                                                       \
   V(FfiDynamicLibrary, "DynamicLibrary")                                       \
   V(FfiElementType, "elementType")                                             \
+  V(FfiFieldPacking, "packing")                                                \
   V(FfiFieldTypes, "fieldTypes")                                               \
   V(FfiFloat, "Float")                                                         \
   V(FfiInt16, "Int16")                                                         \
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index b6091dc..aad3f5a 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -115,6 +115,10 @@
 # Snapshots that go under bin/snapshots
 _platform_sdk_snapshots = [
   [
+    "analysis_server",
+    "../utils/analysis_server",
+  ],
+  [
     "dartanalyzer",
     "../utils/dartanalyzer:generate_dartanalyzer_snapshot",
   ],
@@ -151,12 +155,6 @@
         "../utils/kernel-service:kernel-service_snapshot",
       ] ]
 }
-if (dart_target_arch != "arm") {
-  _platform_sdk_snapshots += [ [
-        "analysis_server",
-        "../utils/analysis_server",
-      ] ]
-}
 
 _full_sdk_snapshots = _platform_sdk_snapshots + [
                         [
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index df878fa..75a6e51 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -260,6 +260,14 @@
     native "Ffi_loadDouble";
 
 @pragma("vm:recognized", "other")
+double _loadFloatUnaligned(Object typedDataBase, int offsetInBytes)
+    native "Ffi_loadFloatUnaligned";
+
+@pragma("vm:recognized", "other")
+double _loadDoubleUnaligned(Object typedDataBase, int offsetInBytes)
+    native "Ffi_loadDoubleUnaligned";
+
+@pragma("vm:recognized", "other")
 Pointer<S> _loadPointer<S extends NativeType>(
     Object typedDataBase, int offsetInBytes) native "Ffi_loadPointer";
 
@@ -308,6 +316,14 @@
     native "Ffi_storeDouble";
 
 @pragma("vm:recognized", "other")
+void _storeFloatUnaligned(Object typedDataBase, int offsetInBytes, double value)
+    native "Ffi_storeFloatUnaligned";
+
+@pragma("vm:recognized", "other")
+void _storeDoubleUnaligned(Object typedDataBase, int offsetInBytes,
+    double value) native "Ffi_storeDoubleUnaligned";
+
+@pragma("vm:recognized", "other")
 void _storePointer<S extends NativeType>(Object typedDataBase,
     int offsetInBytes, Pointer<S> value) native "Ffi_storePointer";
 
diff --git a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
index 3391e88..1dd4726 100644
--- a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
@@ -16,7 +16,10 @@
   @pragma("vm:entry-point")
   final List<Object> fieldTypes;
 
-  const _FfiStructLayout(this.fieldTypes);
+  @pragma("vm:entry-point")
+  final int? packing;
+
+  const _FfiStructLayout(this.fieldTypes, this.packing);
 }
 
 @pragma("vm:entry-point")
diff --git a/sdk/lib/ffi/struct.dart b/sdk/lib/ffi/struct.dart
index 5d32708..ebe326a 100644
--- a/sdk/lib/ffi/struct.dart
+++ b/sdk/lib/ffi/struct.dart
@@ -58,3 +58,13 @@
 
   Struct._fromPointer(this._addressOf);
 }
+
+/// Annotation to specify on `Struct` subtypes to indicate that its members
+/// need to be packed.
+///
+/// Valid values for [memberAlignment] are 1, 2, 4, 8, and 16.
+class Packed {
+  final int memberAlignment;
+
+  const Packed(this.memberAlignment);
+}
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 4cdd74b..09648be 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -285,6 +285,42 @@
           passUint8Struct4BytesInlineArrayMultiDimensionalIn, 0),
       passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback),
   CallbackTest.withCheck(
+      "PassStruct3BytesPackedIntx10",
+      Pointer.fromFunction<PassStruct3BytesPackedIntx10Type>(
+          passStruct3BytesPackedIntx10, 0),
+      passStruct3BytesPackedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesPackedIntx10",
+      Pointer.fromFunction<PassStruct8BytesPackedIntx10Type>(
+          passStruct8BytesPackedIntx10, 0),
+      passStruct8BytesPackedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct9BytesPackedMixedx10DoubleInt32",
+      Pointer.fromFunction<PassStruct9BytesPackedMixedx10DoubleInt32Type>(
+          passStruct9BytesPackedMixedx10DoubleInt32, 0.0),
+      passStruct9BytesPackedMixedx10DoubleInt32AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct5BytesPackedMixed",
+      Pointer.fromFunction<PassStruct5BytesPackedMixedType>(
+          passStruct5BytesPackedMixed, 0.0),
+      passStruct5BytesPackedMixedAfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedAlignmentStruct5BytesPackedMixed",
+      Pointer.fromFunction<
+              PassStructNestedAlignmentStruct5BytesPackedMixedType>(
+          passStructNestedAlignmentStruct5BytesPackedMixed, 0.0),
+      passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct6BytesInlineArrayInt",
+      Pointer.fromFunction<PassStruct6BytesInlineArrayIntType>(
+          passStruct6BytesInlineArrayInt, 0.0),
+      passStruct6BytesInlineArrayIntAfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct15BytesInlineArrayMixed",
+      Pointer.fromFunction<PassStruct15BytesInlineArrayMixedType>(
+          passStruct15BytesInlineArrayMixed, 0.0),
+      passStruct15BytesInlineArrayMixedAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -392,6 +428,21 @@
           returnStruct1024BytesHomogeneousUint64),
       returnStruct1024BytesHomogeneousUint64AfterCallback),
   CallbackTest.withCheck(
+      "ReturnStruct3BytesPackedInt",
+      Pointer.fromFunction<ReturnStruct3BytesPackedIntType>(
+          returnStruct3BytesPackedInt),
+      returnStruct3BytesPackedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesPackedInt",
+      Pointer.fromFunction<ReturnStruct8BytesPackedIntType>(
+          returnStruct8BytesPackedInt),
+      returnStruct8BytesPackedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct9BytesPackedMixed",
+      Pointer.fromFunction<ReturnStruct9BytesPackedMixedType>(
+          returnStruct9BytesPackedMixed),
+      returnStruct9BytesPackedMixedAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStructArgumentStruct1ByteInt",
       Pointer.fromFunction<ReturnStructArgumentStruct1ByteIntType>(
           returnStructArgumentStruct1ByteInt),
@@ -6210,6 +6261,585 @@
   Expect.equals(5, result);
 }
 
+typedef PassStruct3BytesPackedIntx10Type = Int64 Function(
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a0 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a1 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a2 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a3 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a4 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a5 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a6 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a7 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a8 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a9 = Struct3BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct3BytesPackedIntx10Result = 0;
+
+int passStruct3BytesPackedIntx10CalculateResult() {
+  int result = 0;
+
+  result += passStruct3BytesPackedIntx10_a0.a0;
+  result += passStruct3BytesPackedIntx10_a0.a1;
+  result += passStruct3BytesPackedIntx10_a1.a0;
+  result += passStruct3BytesPackedIntx10_a1.a1;
+  result += passStruct3BytesPackedIntx10_a2.a0;
+  result += passStruct3BytesPackedIntx10_a2.a1;
+  result += passStruct3BytesPackedIntx10_a3.a0;
+  result += passStruct3BytesPackedIntx10_a3.a1;
+  result += passStruct3BytesPackedIntx10_a4.a0;
+  result += passStruct3BytesPackedIntx10_a4.a1;
+  result += passStruct3BytesPackedIntx10_a5.a0;
+  result += passStruct3BytesPackedIntx10_a5.a1;
+  result += passStruct3BytesPackedIntx10_a6.a0;
+  result += passStruct3BytesPackedIntx10_a6.a1;
+  result += passStruct3BytesPackedIntx10_a7.a0;
+  result += passStruct3BytesPackedIntx10_a7.a1;
+  result += passStruct3BytesPackedIntx10_a8.a0;
+  result += passStruct3BytesPackedIntx10_a8.a1;
+  result += passStruct3BytesPackedIntx10_a9.a0;
+  result += passStruct3BytesPackedIntx10_a9.a1;
+
+  passStruct3BytesPackedIntx10Result = result;
+
+  return result;
+}
+
+/// Small struct with mis-aligned member.
+int passStruct3BytesPackedIntx10(
+    Struct3BytesPackedInt a0,
+    Struct3BytesPackedInt a1,
+    Struct3BytesPackedInt a2,
+    Struct3BytesPackedInt a3,
+    Struct3BytesPackedInt a4,
+    Struct3BytesPackedInt a5,
+    Struct3BytesPackedInt a6,
+    Struct3BytesPackedInt a7,
+    Struct3BytesPackedInt a8,
+    Struct3BytesPackedInt a9) {
+  print(
+      "passStruct3BytesPackedIntx10(${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 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct3BytesPackedIntx10 throwing on purpose!");
+  }
+
+  passStruct3BytesPackedIntx10_a0 = a0;
+  passStruct3BytesPackedIntx10_a1 = a1;
+  passStruct3BytesPackedIntx10_a2 = a2;
+  passStruct3BytesPackedIntx10_a3 = a3;
+  passStruct3BytesPackedIntx10_a4 = a4;
+  passStruct3BytesPackedIntx10_a5 = a5;
+  passStruct3BytesPackedIntx10_a6 = a6;
+  passStruct3BytesPackedIntx10_a7 = a7;
+  passStruct3BytesPackedIntx10_a8 = a8;
+  passStruct3BytesPackedIntx10_a9 = a9;
+
+  final result = passStruct3BytesPackedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct3BytesPackedIntx10AfterCallback() {
+  final result = passStruct3BytesPackedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(10, result);
+}
+
+typedef PassStruct8BytesPackedIntx10Type = Int64 Function(
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a0 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a1 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a2 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a3 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a4 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a5 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a6 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a7 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a8 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a9 = Struct8BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesPackedIntx10Result = 0;
+
+int passStruct8BytesPackedIntx10CalculateResult() {
+  int result = 0;
+
+  result += passStruct8BytesPackedIntx10_a0.a0;
+  result += passStruct8BytesPackedIntx10_a0.a1;
+  result += passStruct8BytesPackedIntx10_a0.a2;
+  result += passStruct8BytesPackedIntx10_a0.a3;
+  result += passStruct8BytesPackedIntx10_a0.a4;
+  result += passStruct8BytesPackedIntx10_a1.a0;
+  result += passStruct8BytesPackedIntx10_a1.a1;
+  result += passStruct8BytesPackedIntx10_a1.a2;
+  result += passStruct8BytesPackedIntx10_a1.a3;
+  result += passStruct8BytesPackedIntx10_a1.a4;
+  result += passStruct8BytesPackedIntx10_a2.a0;
+  result += passStruct8BytesPackedIntx10_a2.a1;
+  result += passStruct8BytesPackedIntx10_a2.a2;
+  result += passStruct8BytesPackedIntx10_a2.a3;
+  result += passStruct8BytesPackedIntx10_a2.a4;
+  result += passStruct8BytesPackedIntx10_a3.a0;
+  result += passStruct8BytesPackedIntx10_a3.a1;
+  result += passStruct8BytesPackedIntx10_a3.a2;
+  result += passStruct8BytesPackedIntx10_a3.a3;
+  result += passStruct8BytesPackedIntx10_a3.a4;
+  result += passStruct8BytesPackedIntx10_a4.a0;
+  result += passStruct8BytesPackedIntx10_a4.a1;
+  result += passStruct8BytesPackedIntx10_a4.a2;
+  result += passStruct8BytesPackedIntx10_a4.a3;
+  result += passStruct8BytesPackedIntx10_a4.a4;
+  result += passStruct8BytesPackedIntx10_a5.a0;
+  result += passStruct8BytesPackedIntx10_a5.a1;
+  result += passStruct8BytesPackedIntx10_a5.a2;
+  result += passStruct8BytesPackedIntx10_a5.a3;
+  result += passStruct8BytesPackedIntx10_a5.a4;
+  result += passStruct8BytesPackedIntx10_a6.a0;
+  result += passStruct8BytesPackedIntx10_a6.a1;
+  result += passStruct8BytesPackedIntx10_a6.a2;
+  result += passStruct8BytesPackedIntx10_a6.a3;
+  result += passStruct8BytesPackedIntx10_a6.a4;
+  result += passStruct8BytesPackedIntx10_a7.a0;
+  result += passStruct8BytesPackedIntx10_a7.a1;
+  result += passStruct8BytesPackedIntx10_a7.a2;
+  result += passStruct8BytesPackedIntx10_a7.a3;
+  result += passStruct8BytesPackedIntx10_a7.a4;
+  result += passStruct8BytesPackedIntx10_a8.a0;
+  result += passStruct8BytesPackedIntx10_a8.a1;
+  result += passStruct8BytesPackedIntx10_a8.a2;
+  result += passStruct8BytesPackedIntx10_a8.a3;
+  result += passStruct8BytesPackedIntx10_a8.a4;
+  result += passStruct8BytesPackedIntx10_a9.a0;
+  result += passStruct8BytesPackedIntx10_a9.a1;
+  result += passStruct8BytesPackedIntx10_a9.a2;
+  result += passStruct8BytesPackedIntx10_a9.a3;
+  result += passStruct8BytesPackedIntx10_a9.a4;
+
+  passStruct8BytesPackedIntx10Result = result;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+int passStruct8BytesPackedIntx10(
+    Struct8BytesPackedInt a0,
+    Struct8BytesPackedInt a1,
+    Struct8BytesPackedInt a2,
+    Struct8BytesPackedInt a3,
+    Struct8BytesPackedInt a4,
+    Struct8BytesPackedInt a5,
+    Struct8BytesPackedInt a6,
+    Struct8BytesPackedInt a7,
+    Struct8BytesPackedInt a8,
+    Struct8BytesPackedInt a9) {
+  print(
+      "passStruct8BytesPackedIntx10(${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 == 42 || a0.a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct8BytesPackedIntx10 throwing on purpose!");
+  }
+
+  passStruct8BytesPackedIntx10_a0 = a0;
+  passStruct8BytesPackedIntx10_a1 = a1;
+  passStruct8BytesPackedIntx10_a2 = a2;
+  passStruct8BytesPackedIntx10_a3 = a3;
+  passStruct8BytesPackedIntx10_a4 = a4;
+  passStruct8BytesPackedIntx10_a5 = a5;
+  passStruct8BytesPackedIntx10_a6 = a6;
+  passStruct8BytesPackedIntx10_a7 = a7;
+  passStruct8BytesPackedIntx10_a8 = a8;
+  passStruct8BytesPackedIntx10_a9 = a9;
+
+  final result = passStruct8BytesPackedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesPackedIntx10AfterCallback() {
+  final result = passStruct8BytesPackedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(1275, result);
+}
+
+typedef PassStruct9BytesPackedMixedx10DoubleInt32Type = Double Function(
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Double,
+    Int32);
+
+// Global variables to be able to test inputs after callback returned.
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a0 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a1 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a2 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a3 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a4 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a5 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a6 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a7 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a8 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a9 =
+    Struct9BytesPackedMixed();
+double passStruct9BytesPackedMixedx10DoubleInt32_a10 = 0.0;
+int passStruct9BytesPackedMixedx10DoubleInt32_a11 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct9BytesPackedMixedx10DoubleInt32Result = 0.0;
+
+double passStruct9BytesPackedMixedx10DoubleInt32CalculateResult() {
+  double result = 0;
+
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a10;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a11;
+
+  passStruct9BytesPackedMixedx10DoubleInt32Result = result;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+double passStruct9BytesPackedMixedx10DoubleInt32(
+    Struct9BytesPackedMixed a0,
+    Struct9BytesPackedMixed a1,
+    Struct9BytesPackedMixed a2,
+    Struct9BytesPackedMixed a3,
+    Struct9BytesPackedMixed a4,
+    Struct9BytesPackedMixed a5,
+    Struct9BytesPackedMixed a6,
+    Struct9BytesPackedMixed a7,
+    Struct9BytesPackedMixed a8,
+    Struct9BytesPackedMixed a9,
+    double a10,
+    int a11) {
+  print(
+      "passStruct9BytesPackedMixedx10DoubleInt32(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+  // 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(
+        "PassStruct9BytesPackedMixedx10DoubleInt32 throwing on purpose!");
+  }
+
+  passStruct9BytesPackedMixedx10DoubleInt32_a0 = a0;
+  passStruct9BytesPackedMixedx10DoubleInt32_a1 = a1;
+  passStruct9BytesPackedMixedx10DoubleInt32_a2 = a2;
+  passStruct9BytesPackedMixedx10DoubleInt32_a3 = a3;
+  passStruct9BytesPackedMixedx10DoubleInt32_a4 = a4;
+  passStruct9BytesPackedMixedx10DoubleInt32_a5 = a5;
+  passStruct9BytesPackedMixedx10DoubleInt32_a6 = a6;
+  passStruct9BytesPackedMixedx10DoubleInt32_a7 = a7;
+  passStruct9BytesPackedMixedx10DoubleInt32_a8 = a8;
+  passStruct9BytesPackedMixedx10DoubleInt32_a9 = a9;
+  passStruct9BytesPackedMixedx10DoubleInt32_a10 = a10;
+  passStruct9BytesPackedMixedx10DoubleInt32_a11 = a11;
+
+  final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct9BytesPackedMixedx10DoubleInt32AfterCallback() {
+  final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(211.0, result);
+}
+
+typedef PassStruct5BytesPackedMixedType = Double Function(
+    Struct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct5BytesPackedMixed passStruct5BytesPackedMixed_a0 =
+    Struct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct5BytesPackedMixedResult = 0.0;
+
+double passStruct5BytesPackedMixedCalculateResult() {
+  double result = 0;
+
+  result += passStruct5BytesPackedMixed_a0.a0;
+  result += passStruct5BytesPackedMixed_a0.a1;
+
+  passStruct5BytesPackedMixedResult = result;
+
+  return result;
+}
+
+/// This packed struct happens to have only aligned members.
+double passStruct5BytesPackedMixed(Struct5BytesPackedMixed a0) {
+  print("passStruct5BytesPackedMixed(${a0})");
+
+  // 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("PassStruct5BytesPackedMixed throwing on purpose!");
+  }
+
+  passStruct5BytesPackedMixed_a0 = a0;
+
+  final result = passStruct5BytesPackedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct5BytesPackedMixedAfterCallback() {
+  final result = passStruct5BytesPackedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(1.0, result);
+}
+
+typedef PassStructNestedAlignmentStruct5BytesPackedMixedType = Double Function(
+    StructNestedAlignmentStruct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedAlignmentStruct5BytesPackedMixed
+    passStructNestedAlignmentStruct5BytesPackedMixed_a0 =
+    StructNestedAlignmentStruct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedAlignmentStruct5BytesPackedMixedResult = 0.0;
+
+double passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult() {
+  double result = 0;
+
+  result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a0;
+  result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a0;
+  result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a1;
+
+  passStructNestedAlignmentStruct5BytesPackedMixedResult = result;
+
+  return result;
+}
+
+/// Check alignment of packed struct in non-packed struct.
+double passStructNestedAlignmentStruct5BytesPackedMixed(
+    StructNestedAlignmentStruct5BytesPackedMixed a0) {
+  print("passStructNestedAlignmentStruct5BytesPackedMixed(${a0})");
+
+  // 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(
+        "PassStructNestedAlignmentStruct5BytesPackedMixed throwing on purpose!");
+  }
+
+  passStructNestedAlignmentStruct5BytesPackedMixed_a0 = a0;
+
+  final result =
+      passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback() {
+  final result =
+      passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(6.0, result);
+}
+
+typedef PassStruct6BytesInlineArrayIntType = Double Function(
+    Struct6BytesInlineArrayInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct6BytesInlineArrayInt passStruct6BytesInlineArrayInt_a0 =
+    Struct6BytesInlineArrayInt();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct6BytesInlineArrayIntResult = 0.0;
+
+double passStruct6BytesInlineArrayIntCalculateResult() {
+  double result = 0;
+
+  result += passStruct6BytesInlineArrayInt_a0.a0[0].a0;
+  result += passStruct6BytesInlineArrayInt_a0.a0[0].a1;
+  result += passStruct6BytesInlineArrayInt_a0.a0[1].a0;
+  result += passStruct6BytesInlineArrayInt_a0.a0[1].a1;
+
+  passStruct6BytesInlineArrayIntResult = result;
+
+  return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct6BytesInlineArrayInt(Struct6BytesInlineArrayInt a0) {
+  print("passStruct6BytesInlineArrayInt(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct6BytesInlineArrayInt throwing on purpose!");
+  }
+
+  passStruct6BytesInlineArrayInt_a0 = a0;
+
+  final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct6BytesInlineArrayIntAfterCallback() {
+  final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(2.0, result);
+}
+
+typedef PassStruct15BytesInlineArrayMixedType = Double Function(
+    Struct15BytesInlineArrayMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct15BytesInlineArrayMixed passStruct15BytesInlineArrayMixed_a0 =
+    Struct15BytesInlineArrayMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct15BytesInlineArrayMixedResult = 0.0;
+
+double passStruct15BytesInlineArrayMixedCalculateResult() {
+  double result = 0;
+
+  result += passStruct15BytesInlineArrayMixed_a0.a0[0].a0;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[0].a1;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[1].a0;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[1].a1;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[2].a0;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[2].a1;
+
+  passStruct15BytesInlineArrayMixedResult = result;
+
+  return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct15BytesInlineArrayMixed(Struct15BytesInlineArrayMixed a0) {
+  print("passStruct15BytesInlineArrayMixed(${a0})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct15BytesInlineArrayMixed throwing on purpose!");
+  }
+
+  passStruct15BytesInlineArrayMixed_a0 = a0;
+
+  final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct15BytesInlineArrayMixedAfterCallback() {
+  final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(3.0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
@@ -8283,6 +8913,182 @@
   calloc.free(returnStruct1024BytesHomogeneousUint64ResultPointer);
 }
 
+typedef ReturnStruct3BytesPackedIntType = Struct3BytesPackedInt Function(
+    Int8, Int16);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct3BytesPackedInt_a0 = 0;
+int returnStruct3BytesPackedInt_a1 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct3BytesPackedInt> returnStruct3BytesPackedIntResultPointer =
+    nullptr;
+
+Struct3BytesPackedInt returnStruct3BytesPackedIntCalculateResult() {
+  final resultPointer = calloc<Struct3BytesPackedInt>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnStruct3BytesPackedInt_a0;
+  result.a1 = returnStruct3BytesPackedInt_a1;
+
+  returnStruct3BytesPackedIntResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Small struct with mis-aligned member.
+Struct3BytesPackedInt returnStruct3BytesPackedInt(int a0, int a1) {
+  print("returnStruct3BytesPackedInt(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct3BytesPackedInt throwing on purpose!");
+  }
+
+  returnStruct3BytesPackedInt_a0 = a0;
+  returnStruct3BytesPackedInt_a1 = a1;
+
+  final result = returnStruct3BytesPackedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct3BytesPackedIntAfterCallback() {
+  calloc.free(returnStruct3BytesPackedIntResultPointer);
+
+  final result = returnStruct3BytesPackedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnStruct3BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct8BytesPackedIntType = Struct8BytesPackedInt Function(
+    Uint8, Uint32, Uint8, Uint8, Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct8BytesPackedInt_a0 = 0;
+int returnStruct8BytesPackedInt_a1 = 0;
+int returnStruct8BytesPackedInt_a2 = 0;
+int returnStruct8BytesPackedInt_a3 = 0;
+int returnStruct8BytesPackedInt_a4 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct8BytesPackedInt> returnStruct8BytesPackedIntResultPointer =
+    nullptr;
+
+Struct8BytesPackedInt returnStruct8BytesPackedIntCalculateResult() {
+  final resultPointer = calloc<Struct8BytesPackedInt>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnStruct8BytesPackedInt_a0;
+  result.a1 = returnStruct8BytesPackedInt_a1;
+  result.a2 = returnStruct8BytesPackedInt_a2;
+  result.a3 = returnStruct8BytesPackedInt_a3;
+  result.a4 = returnStruct8BytesPackedInt_a4;
+
+  returnStruct8BytesPackedIntResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+Struct8BytesPackedInt returnStruct8BytesPackedInt(
+    int a0, int a1, int a2, int a3, int a4) {
+  print("returnStruct8BytesPackedInt(${a0}, ${a1}, ${a2}, ${a3}, ${a4})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct8BytesPackedInt throwing on purpose!");
+  }
+
+  returnStruct8BytesPackedInt_a0 = a0;
+  returnStruct8BytesPackedInt_a1 = a1;
+  returnStruct8BytesPackedInt_a2 = a2;
+  returnStruct8BytesPackedInt_a3 = a3;
+  returnStruct8BytesPackedInt_a4 = a4;
+
+  final result = returnStruct8BytesPackedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesPackedIntAfterCallback() {
+  calloc.free(returnStruct8BytesPackedIntResultPointer);
+
+  final result = returnStruct8BytesPackedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnStruct8BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct9BytesPackedMixedType = Struct9BytesPackedMixed Function(
+    Uint8, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct9BytesPackedMixed_a0 = 0;
+double returnStruct9BytesPackedMixed_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct9BytesPackedMixed> returnStruct9BytesPackedMixedResultPointer =
+    nullptr;
+
+Struct9BytesPackedMixed returnStruct9BytesPackedMixedCalculateResult() {
+  final resultPointer = calloc<Struct9BytesPackedMixed>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnStruct9BytesPackedMixed_a0;
+  result.a1 = returnStruct9BytesPackedMixed_a1;
+
+  returnStruct9BytesPackedMixedResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+Struct9BytesPackedMixed returnStruct9BytesPackedMixed(int a0, double a1) {
+  print("returnStruct9BytesPackedMixed(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("ReturnStruct9BytesPackedMixed throwing on purpose!");
+  }
+
+  returnStruct9BytesPackedMixed_a0 = a0;
+  returnStruct9BytesPackedMixed_a1 = a1;
+
+  final result = returnStruct9BytesPackedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct9BytesPackedMixedAfterCallback() {
+  calloc.free(returnStruct9BytesPackedMixedResultPointer);
+
+  final result = returnStruct9BytesPackedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnStruct9BytesPackedMixedResultPointer);
+}
+
 typedef ReturnStructArgumentStruct1ByteIntType = Struct1ByteInt Function(
     Struct1ByteInt);
 
diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart
index ef59018..d52c0d3 100644
--- a/tests/ffi/function_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_test.dart
@@ -70,6 +70,13 @@
     testPassStructStruct16BytesMixed3x10();
     testPassUint8Struct32BytesInlineArrayMultiDimensionalI();
     testPassUint8Struct4BytesInlineArrayMultiDimensionalIn();
+    testPassStruct3BytesPackedIntx10();
+    testPassStruct8BytesPackedIntx10();
+    testPassStruct9BytesPackedMixedx10DoubleInt32();
+    testPassStruct5BytesPackedMixed();
+    testPassStructNestedAlignmentStruct5BytesPackedMixed();
+    testPassStruct6BytesInlineArrayInt();
+    testPassStruct15BytesInlineArrayMixed();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -92,6 +99,9 @@
     testReturnStruct32BytesHomogeneousDouble();
     testReturnStruct40BytesHomogeneousDouble();
     testReturnStruct1024BytesHomogeneousUint64();
+    testReturnStruct3BytesPackedInt();
+    testReturnStruct8BytesPackedInt();
+    testReturnStruct9BytesPackedMixed();
     testReturnStructArgumentStruct1ByteInt();
     testReturnStructArgumentInt32x8Struct1ByteInt();
     testReturnStructArgumentStruct8BytesHomogeneousFloat();
@@ -1186,6 +1196,93 @@
       ]})";
 }
 
+@Packed(1)
+class Struct3BytesPackedInt extends Struct {
+  @Int8()
+  external int a0;
+
+  @Int16()
+  external int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct3BytesPackedIntMembersAligned extends Struct {
+  @Int8()
+  external int a0;
+
+  @Int16()
+  external int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct5BytesPackedMixed extends Struct {
+  @Float()
+  external double a0;
+
+  @Uint8()
+  external int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedAlignmentStruct5BytesPackedMixed extends Struct {
+  @Uint8()
+  external int a0;
+
+  external Struct5BytesPackedMixed a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct6BytesInlineArrayInt extends Struct {
+  @Array(2)
+  external Array<Struct3BytesPackedIntMembersAligned> a0;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 2; i0 += 1) a0[i0]]})";
+}
+
+@Packed(1)
+class Struct8BytesPackedInt extends Struct {
+  @Uint8()
+  external int a0;
+
+  @Uint32()
+  external int a1;
+
+  @Uint8()
+  external int a2;
+
+  @Uint8()
+  external int a3;
+
+  @Uint8()
+  external int a4;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4})";
+}
+
+@Packed(1)
+class Struct9BytesPackedMixed extends Struct {
+  @Uint8()
+  external int a0;
+
+  @Double()
+  external double a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct15BytesInlineArrayMixed extends Struct {
+  @Array(3)
+  external Array<Struct5BytesPackedMixed> a0;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 3; i0 += 1) a0[i0]]})";
+}
+
 final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
     Int64 Function(
         Struct1ByteInt,
@@ -5423,6 +5520,402 @@
   calloc.free(a1Pointer);
 }
 
+final passStruct3BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+    Int64 Function(
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt),
+    int Function(
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt)>("PassStruct3BytesPackedIntx10");
+
+/// Small struct with mis-aligned member.
+void testPassStruct3BytesPackedIntx10() {
+  final a0Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a9 = a9Pointer.ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3;
+  a1.a1 = 4;
+  a2.a0 = -5;
+  a2.a1 = 6;
+  a3.a0 = -7;
+  a3.a1 = 8;
+  a4.a0 = -9;
+  a4.a1 = 10;
+  a5.a0 = -11;
+  a5.a1 = 12;
+  a6.a0 = -13;
+  a6.a1 = 14;
+  a7.a0 = -15;
+  a7.a1 = 16;
+  a8.a0 = -17;
+  a8.a1 = 18;
+  a9.a0 = -19;
+  a9.a1 = 20;
+
+  final result =
+      passStruct3BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.equals(10, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passStruct8BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+    Int64 Function(
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt),
+    int Function(
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt)>("PassStruct8BytesPackedIntx10");
+
+/// Struct with mis-aligned member.
+void testPassStruct8BytesPackedIntx10() {
+  final a0Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a9 = a9Pointer.ref;
+
+  a0.a0 = 1;
+  a0.a1 = 2;
+  a0.a2 = 3;
+  a0.a3 = 4;
+  a0.a4 = 5;
+  a1.a0 = 6;
+  a1.a1 = 7;
+  a1.a2 = 8;
+  a1.a3 = 9;
+  a1.a4 = 10;
+  a2.a0 = 11;
+  a2.a1 = 12;
+  a2.a2 = 13;
+  a2.a3 = 14;
+  a2.a4 = 15;
+  a3.a0 = 16;
+  a3.a1 = 17;
+  a3.a2 = 18;
+  a3.a3 = 19;
+  a3.a4 = 20;
+  a4.a0 = 21;
+  a4.a1 = 22;
+  a4.a2 = 23;
+  a4.a3 = 24;
+  a4.a4 = 25;
+  a5.a0 = 26;
+  a5.a1 = 27;
+  a5.a2 = 28;
+  a5.a3 = 29;
+  a5.a4 = 30;
+  a6.a0 = 31;
+  a6.a1 = 32;
+  a6.a2 = 33;
+  a6.a3 = 34;
+  a6.a4 = 35;
+  a7.a0 = 36;
+  a7.a1 = 37;
+  a7.a2 = 38;
+  a7.a3 = 39;
+  a7.a4 = 40;
+  a8.a0 = 41;
+  a8.a1 = 42;
+  a8.a2 = 43;
+  a8.a3 = 44;
+  a8.a4 = 45;
+  a9.a0 = 46;
+  a9.a1 = 47;
+  a9.a2 = 48;
+  a9.a3 = 49;
+  a9.a4 = 50;
+
+  final result =
+      passStruct8BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.equals(1275, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passStruct9BytesPackedMixedx10DoubleInt32 =
+    ffiTestFunctions.lookupFunction<
+        Double Function(
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Double,
+            Int32),
+        double Function(
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            double,
+            int)>("PassStruct9BytesPackedMixedx10DoubleInt32");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testPassStruct9BytesPackedMixedx10DoubleInt32() {
+  final a0Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a9 = a9Pointer.ref;
+  double a10;
+  int a11;
+
+  a0.a0 = 1;
+  a0.a1 = 2.0;
+  a1.a0 = 3;
+  a1.a1 = 4.0;
+  a2.a0 = 5;
+  a2.a1 = 6.0;
+  a3.a0 = 7;
+  a3.a1 = 8.0;
+  a4.a0 = 9;
+  a4.a1 = 10.0;
+  a5.a0 = 11;
+  a5.a1 = 12.0;
+  a6.a0 = 13;
+  a6.a1 = 14.0;
+  a7.a0 = 15;
+  a7.a1 = 16.0;
+  a8.a0 = 17;
+  a8.a1 = 18.0;
+  a9.a0 = 19;
+  a9.a1 = 20.0;
+  a10 = -21.0;
+  a11 = 22;
+
+  final result = passStruct9BytesPackedMixedx10DoubleInt32(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.approxEquals(211.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passStruct5BytesPackedMixed = ffiTestFunctions.lookupFunction<
+    Double Function(Struct5BytesPackedMixed),
+    double Function(Struct5BytesPackedMixed)>("PassStruct5BytesPackedMixed");
+
+/// This packed struct happens to have only aligned members.
+void testPassStruct5BytesPackedMixed() {
+  final a0Pointer = calloc<Struct5BytesPackedMixed>();
+  final Struct5BytesPackedMixed a0 = a0Pointer.ref;
+
+  a0.a0 = -1.0;
+  a0.a1 = 2;
+
+  final result = passStruct5BytesPackedMixed(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(1.0, result);
+
+  calloc.free(a0Pointer);
+}
+
+final passStructNestedAlignmentStruct5BytesPackedMixed =
+    ffiTestFunctions.lookupFunction<
+            Double Function(StructNestedAlignmentStruct5BytesPackedMixed),
+            double Function(StructNestedAlignmentStruct5BytesPackedMixed)>(
+        "PassStructNestedAlignmentStruct5BytesPackedMixed");
+
+/// Check alignment of packed struct in non-packed struct.
+void testPassStructNestedAlignmentStruct5BytesPackedMixed() {
+  final a0Pointer = calloc<StructNestedAlignmentStruct5BytesPackedMixed>();
+  final StructNestedAlignmentStruct5BytesPackedMixed a0 = a0Pointer.ref;
+
+  a0.a0 = 1;
+  a0.a1.a0 = 2.0;
+  a0.a1.a1 = 3;
+
+  final result = passStructNestedAlignmentStruct5BytesPackedMixed(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(6.0, result);
+
+  calloc.free(a0Pointer);
+}
+
+final passStruct6BytesInlineArrayInt = ffiTestFunctions.lookupFunction<
+    Double Function(Struct6BytesInlineArrayInt),
+    double Function(
+        Struct6BytesInlineArrayInt)>("PassStruct6BytesInlineArrayInt");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct6BytesInlineArrayInt() {
+  final a0Pointer = calloc<Struct6BytesInlineArrayInt>();
+  final Struct6BytesInlineArrayInt a0 = a0Pointer.ref;
+
+  a0.a0[0].a0 = -1;
+  a0.a0[0].a1 = 2;
+  a0.a0[1].a0 = -3;
+  a0.a0[1].a1 = 4;
+
+  final result = passStruct6BytesInlineArrayInt(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(2.0, result);
+
+  calloc.free(a0Pointer);
+}
+
+final passStruct15BytesInlineArrayMixed = ffiTestFunctions.lookupFunction<
+    Double Function(Struct15BytesInlineArrayMixed),
+    double Function(
+        Struct15BytesInlineArrayMixed)>("PassStruct15BytesInlineArrayMixed");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct15BytesInlineArrayMixed() {
+  final a0Pointer = calloc<Struct15BytesInlineArrayMixed>();
+  final Struct15BytesInlineArrayMixed a0 = a0Pointer.ref;
+
+  a0.a0[0].a0 = -1.0;
+  a0.a0[0].a1 = 2;
+  a0.a0[1].a0 = -3.0;
+  a0.a0[1].a1 = 4;
+  a0.a0[2].a0 = -5.0;
+  a0.a0[2].a1 = 6;
+
+  final result = passStruct15BytesInlineArrayMixed(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(3.0, result);
+
+  calloc.free(a0Pointer);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -6841,6 +7334,78 @@
   Expect.equals(a127, result.a127);
 }
 
+final returnStruct3BytesPackedInt = ffiTestFunctions.lookupFunction<
+    Struct3BytesPackedInt Function(Int8, Int16),
+    Struct3BytesPackedInt Function(int, int)>("ReturnStruct3BytesPackedInt");
+
+/// Small struct with mis-aligned member.
+void testReturnStruct3BytesPackedInt() {
+  int a0;
+  int a1;
+
+  a0 = -1;
+  a1 = 2;
+
+  final result = returnStruct3BytesPackedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.equals(a1, result.a1);
+}
+
+final returnStruct8BytesPackedInt = ffiTestFunctions.lookupFunction<
+    Struct8BytesPackedInt Function(Uint8, Uint32, Uint8, Uint8, Uint8),
+    Struct8BytesPackedInt Function(
+        int, int, int, int, int)>("ReturnStruct8BytesPackedInt");
+
+/// Struct with mis-aligned member.
+void testReturnStruct8BytesPackedInt() {
+  int a0;
+  int a1;
+  int a2;
+  int a3;
+  int a4;
+
+  a0 = 1;
+  a1 = 2;
+  a2 = 3;
+  a3 = 4;
+  a4 = 5;
+
+  final result = returnStruct8BytesPackedInt(a0, a1, a2, a3, a4);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.equals(a1, result.a1);
+  Expect.equals(a2, result.a2);
+  Expect.equals(a3, result.a3);
+  Expect.equals(a4, result.a4);
+}
+
+final returnStruct9BytesPackedMixed = ffiTestFunctions.lookupFunction<
+    Struct9BytesPackedMixed Function(Uint8, Double),
+    Struct9BytesPackedMixed Function(
+        int, double)>("ReturnStruct9BytesPackedMixed");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testReturnStruct9BytesPackedMixed() {
+  int a0;
+  double a1;
+
+  a0 = 1;
+  a1 = 2.0;
+
+  final result = returnStruct9BytesPackedMixed(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.approxEquals(a1, result.a1);
+}
+
 final returnStructArgumentStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Struct1ByteInt),
     Struct1ByteInt Function(
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index e94b35b..df3b44b 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -155,20 +155,24 @@
 class StructType extends CType {
   final List<Member> members;
 
+  final int? packing;
+
   /// To disambiguate same size structs.
   final String suffix;
 
   /// To override names.
   final String overrideName;
 
-  StructType(List<CType> memberTypes)
+  StructType(List<CType> memberTypes, {int? this.packing})
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "",
         this.overrideName = "";
-  StructType.disambiguate(List<CType> memberTypes, this.suffix)
+  StructType.disambiguate(List<CType> memberTypes, this.suffix,
+      {int? this.packing})
       : this.members = generateMemberNames(memberTypes),
         this.overrideName = "";
-  StructType.override(List<CType> memberTypes, this.overrideName)
+  StructType.override(List<CType> memberTypes, this.overrideName,
+      {int? this.packing})
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "";
 
@@ -183,9 +187,19 @@
       !memberTypes.map((e) => e.hasSize).contains(false) && !hasPadding;
   int get size => memberTypes.fold(0, (int acc, e) => acc + e.size);
 
-  /// Rough approximation, to not redo all ABI logic here.
-  bool get hasPadding =>
-      members.length < 2 ? false : members[0].type.size < members[1].type.size;
+  bool get hasPacking => packing != null;
+
+  bool get hasPadding {
+    if (members.length < 2) {
+      return false;
+    }
+    if (packing == 1) {
+      return false;
+    }
+
+    /// Rough approximation, to not redo all ABI logic here.
+    return members[0].type.size < members[1].type.size;
+  }
 
   bool get hasNestedStructs =>
       members.map((e) => e.type is StructType).contains(true);
@@ -217,6 +231,12 @@
     if (hasSize) {
       result += "${size}Byte" + (size != 1 ? "s" : "");
     }
+    if (hasPacking) {
+      result += "Packed";
+      if (packing! > 1) {
+        result += "$packing";
+      }
+    }
     if (hasNestedStructs) {
       result += "Nested";
     }
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index ebc6a94..1ad3c11 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -328,6 +328,36 @@
       uint32,
       """
 Test struct in multi dimensional inline array."""),
+  FunctionType(List.filled(10, struct3bytesPacked), int64, """
+Small struct with mis-aligned member."""),
+  FunctionType(List.filled(10, struct8bytesPacked), int64, """
+Struct with mis-aligned member."""),
+  FunctionType(
+      [...List.filled(10, struct9bytesPacked), double_, int32],
+      double_,
+      """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
+  FunctionType(
+      [struct5bytesPacked],
+      double_,
+      """
+This packed struct happens to have only aligned members."""),
+  FunctionType(
+      [struct6bytesPacked],
+      double_,
+      """
+Check alignment of packed struct in non-packed struct."""),
+  FunctionType(
+      [struct6bytesPacked2],
+      double_,
+      """
+Check alignment of packed struct array in non-packed struct."""),
+  FunctionType(
+      [struct15bytesPacked],
+      double_,
+      """
+Check alignment of packed struct array in non-packed struct."""),
   FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
 Smallest struct with data."""),
   FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -383,6 +413,13 @@
 Return value too big to go in FPU registers on arm64."""),
   FunctionType(struct1024bytesInt.memberTypes, struct1024bytesInt, """
 Test 1kb struct."""),
+  FunctionType(struct3bytesPacked.memberTypes, struct3bytesPacked, """
+Small struct with mis-aligned member."""),
+  FunctionType(struct8bytesPacked.memberTypes, struct8bytesPacked, """
+Struct with mis-aligned member."""),
+  FunctionType(struct9bytesPacked.memberTypes, struct9bytesPacked, """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
   FunctionType(
       [struct1byteInt],
       struct1byteInt,
@@ -529,6 +566,14 @@
   struct32bytesInlineArrayMultiDimesional,
   struct64bytesInlineArrayMultiDimesional,
   structMultiDimensionalStruct,
+  struct3bytesPacked,
+  struct3bytesPackedMembersAligned,
+  struct5bytesPacked,
+  struct6bytesPacked,
+  struct6bytesPacked2,
+  struct8bytesPacked,
+  struct9bytesPacked,
+  struct15bytesPacked,
 ];
 
 final struct1byteInt = StructType([int8]);
@@ -672,3 +717,27 @@
 final structMultiDimensionalStruct = StructType([
   FixedLengthArrayType.multi(struct1byteInt, [2, 2])
 ]);
+
+final struct3bytesPacked = StructType([int8, int16], packing: 1);
+
+final struct3bytesPackedMembersAligned =
+    StructType.disambiguate([int8, int16], "MembersAligned", packing: 1);
+
+final struct5bytesPacked = StructType([float, uint8], packing: 1);
+
+/// The float in the nested struct is not aligned.
+final struct6bytesPacked = StructType([uint8, struct5bytesPacked]);
+
+/// The second element in the array has a nested misaligned int16.
+final struct6bytesPacked2 =
+    StructType([FixedLengthArrayType(struct3bytesPackedMembersAligned, 2)]);
+
+final struct8bytesPacked =
+    StructType([uint8, uint32, uint8, uint8, uint8], packing: 1);
+
+final struct9bytesPacked = StructType([uint8, double_], packing: 1);
+
+/// The float in the nested struct is aligned in the first element in the
+/// inline array, but not in the subsequent ones.
+final struct15bytesPacked =
+    StructType([FixedLengthArrayType(struct5bytesPacked, 3)]);
diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart
index 51f51ab..c66dbea 100644
--- a/tests/ffi/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi/generator/structs_by_value_tests_generator.dart
@@ -455,6 +455,7 @@
 
 extension on StructType {
   String dartClass(bool nnbd) {
+    final packingAnnotation = hasPacking ? "@Packed(${packing})" : "";
     String dartFields = "";
     for (final member in members) {
       dartFields += "${member.dartStructField(nnbd)}\n\n";
@@ -477,6 +478,7 @@
       return "\$\{${m.name}\}";
     }).join(", ");
     return """
+    $packingAnnotation
     class $name extends Struct {
       $dartFields
 
@@ -486,14 +488,19 @@
   }
 
   String get cDefinition {
+    final packingPragmaPush =
+        hasPacking ? "#pragma pack(push, ${packing})" : "";
+    final packingPragmaPop = hasPacking ? "#pragma pack(pop)" : "";
     String cFields = "";
     for (final member in members) {
       cFields += "  ${member.cStructField}\n";
     }
     return """
+    $packingPragmaPush
     struct $name {
       $cFields
     };
+    $packingPragmaPop
 
     """;
   }
diff --git a/tests/ffi/structs_packed_test.dart b/tests/ffi/structs_packed_test.dart
new file mode 100644
index 0000000..cec7703
--- /dev/null
+++ b/tests/ffi/structs_packed_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, 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.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+
+import 'dylib_utils.dart';
+
+// Reuse struct definitions.
+import 'function_structs_by_value_generated_test.dart';
+
+void main() {
+  testSizeOfC();
+  testSizeOfDart();
+}
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+final sizeOfStruct3BytesPackedInt =
+    ffiTestFunctions.lookupFunction<Uint64 Function(), int Function()>(
+        "SizeOfStruct3BytesPackedInt");
+
+void testSizeOfC() {
+  Expect.equals(3, sizeOfStruct3BytesPackedInt());
+}
+
+void testSizeOfDart() {
+  // No packing needed to get to 3 bytes.
+  Expect.equals(3, sizeOf<Struct3BytesHomogeneousUint8>());
+
+  // Contents 3 bytes, but alignment forces it to be 4 bytes.
+  Expect.equals(4, sizeOf<Struct3BytesInt2ByteAligned>());
+
+  // Alignment gets the same content back to 3 bytes.
+  Expect.equals(3, sizeOf<Struct3BytesPackedInt>());
+}
diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart
index 746b238..e70f9963 100644
--- a/tests/ffi/vmspecific_static_checks_test.dart
+++ b/tests/ffi/vmspecific_static_checks_test.dart
@@ -703,3 +703,59 @@
 
   external Pointer<Uint8> notEmpty;
 }
+
+@Packed(1)
+class TestStruct1600 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+@Packed(1) //# 1601: compile-time error
+class TestStruct1601 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(3) //# 1602: compile-time error
+class TestStruct1602 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1603 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1603Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  external TestStruct1603 nestedNotPacked; //# 1603: compile-time error
+}
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+  external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1604Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  external TestStruct1604 nestedLooselyPacked; //# 1604: compile-time error
+}
+
+@Packed(1)
+class TestStruct1605Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  @Array(2) //# 1605: compile-time error
+  external Array<TestStruct1603> nestedNotPacked; //# 1605: compile-time error
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+  external Pointer<Uint8> notEmpty;
+
+  @Array(2) //# 1606: compile-time error
+  external Array<TestStruct1604> //# 1606: compile-time error
+      nestedLooselyPacked; //# 1606: 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 d4650fe..c063d7f 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
@@ -285,6 +285,42 @@
           passUint8Struct4BytesInlineArrayMultiDimensionalIn, 0),
       passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback),
   CallbackTest.withCheck(
+      "PassStruct3BytesPackedIntx10",
+      Pointer.fromFunction<PassStruct3BytesPackedIntx10Type>(
+          passStruct3BytesPackedIntx10, 0),
+      passStruct3BytesPackedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct8BytesPackedIntx10",
+      Pointer.fromFunction<PassStruct8BytesPackedIntx10Type>(
+          passStruct8BytesPackedIntx10, 0),
+      passStruct8BytesPackedIntx10AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct9BytesPackedMixedx10DoubleInt32",
+      Pointer.fromFunction<PassStruct9BytesPackedMixedx10DoubleInt32Type>(
+          passStruct9BytesPackedMixedx10DoubleInt32, 0.0),
+      passStruct9BytesPackedMixedx10DoubleInt32AfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct5BytesPackedMixed",
+      Pointer.fromFunction<PassStruct5BytesPackedMixedType>(
+          passStruct5BytesPackedMixed, 0.0),
+      passStruct5BytesPackedMixedAfterCallback),
+  CallbackTest.withCheck(
+      "PassStructNestedAlignmentStruct5BytesPackedMixed",
+      Pointer.fromFunction<
+              PassStructNestedAlignmentStruct5BytesPackedMixedType>(
+          passStructNestedAlignmentStruct5BytesPackedMixed, 0.0),
+      passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct6BytesInlineArrayInt",
+      Pointer.fromFunction<PassStruct6BytesInlineArrayIntType>(
+          passStruct6BytesInlineArrayInt, 0.0),
+      passStruct6BytesInlineArrayIntAfterCallback),
+  CallbackTest.withCheck(
+      "PassStruct15BytesInlineArrayMixed",
+      Pointer.fromFunction<PassStruct15BytesInlineArrayMixedType>(
+          passStruct15BytesInlineArrayMixed, 0.0),
+      passStruct15BytesInlineArrayMixedAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -392,6 +428,21 @@
           returnStruct1024BytesHomogeneousUint64),
       returnStruct1024BytesHomogeneousUint64AfterCallback),
   CallbackTest.withCheck(
+      "ReturnStruct3BytesPackedInt",
+      Pointer.fromFunction<ReturnStruct3BytesPackedIntType>(
+          returnStruct3BytesPackedInt),
+      returnStruct3BytesPackedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct8BytesPackedInt",
+      Pointer.fromFunction<ReturnStruct8BytesPackedIntType>(
+          returnStruct8BytesPackedInt),
+      returnStruct8BytesPackedIntAfterCallback),
+  CallbackTest.withCheck(
+      "ReturnStruct9BytesPackedMixed",
+      Pointer.fromFunction<ReturnStruct9BytesPackedMixedType>(
+          returnStruct9BytesPackedMixed),
+      returnStruct9BytesPackedMixedAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStructArgumentStruct1ByteInt",
       Pointer.fromFunction<ReturnStructArgumentStruct1ByteIntType>(
           returnStructArgumentStruct1ByteInt),
@@ -6410,6 +6461,613 @@
   Expect.equals(5, result);
 }
 
+typedef PassStruct3BytesPackedIntx10Type = Int64 Function(
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt,
+    Struct3BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a0 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a1 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a2 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a3 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a4 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a5 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a6 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a7 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a8 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a9 = Struct3BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct3BytesPackedIntx10Result = 0;
+
+int passStruct3BytesPackedIntx10CalculateResult() {
+  int result = 0;
+
+  result += passStruct3BytesPackedIntx10_a0.a0;
+  result += passStruct3BytesPackedIntx10_a0.a1;
+  result += passStruct3BytesPackedIntx10_a1.a0;
+  result += passStruct3BytesPackedIntx10_a1.a1;
+  result += passStruct3BytesPackedIntx10_a2.a0;
+  result += passStruct3BytesPackedIntx10_a2.a1;
+  result += passStruct3BytesPackedIntx10_a3.a0;
+  result += passStruct3BytesPackedIntx10_a3.a1;
+  result += passStruct3BytesPackedIntx10_a4.a0;
+  result += passStruct3BytesPackedIntx10_a4.a1;
+  result += passStruct3BytesPackedIntx10_a5.a0;
+  result += passStruct3BytesPackedIntx10_a5.a1;
+  result += passStruct3BytesPackedIntx10_a6.a0;
+  result += passStruct3BytesPackedIntx10_a6.a1;
+  result += passStruct3BytesPackedIntx10_a7.a0;
+  result += passStruct3BytesPackedIntx10_a7.a1;
+  result += passStruct3BytesPackedIntx10_a8.a0;
+  result += passStruct3BytesPackedIntx10_a8.a1;
+  result += passStruct3BytesPackedIntx10_a9.a0;
+  result += passStruct3BytesPackedIntx10_a9.a1;
+
+  passStruct3BytesPackedIntx10Result = result;
+
+  return result;
+}
+
+/// Small struct with mis-aligned member.
+int passStruct3BytesPackedIntx10(
+    Struct3BytesPackedInt a0,
+    Struct3BytesPackedInt a1,
+    Struct3BytesPackedInt a2,
+    Struct3BytesPackedInt a3,
+    Struct3BytesPackedInt a4,
+    Struct3BytesPackedInt a5,
+    Struct3BytesPackedInt a6,
+    Struct3BytesPackedInt a7,
+    Struct3BytesPackedInt a8,
+    Struct3BytesPackedInt a9) {
+  print(
+      "passStruct3BytesPackedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // 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("PassStruct3BytesPackedIntx10 throwing on purpose!");
+  }
+
+  passStruct3BytesPackedIntx10_a0 = a0;
+  passStruct3BytesPackedIntx10_a1 = a1;
+  passStruct3BytesPackedIntx10_a2 = a2;
+  passStruct3BytesPackedIntx10_a3 = a3;
+  passStruct3BytesPackedIntx10_a4 = a4;
+  passStruct3BytesPackedIntx10_a5 = a5;
+  passStruct3BytesPackedIntx10_a6 = a6;
+  passStruct3BytesPackedIntx10_a7 = a7;
+  passStruct3BytesPackedIntx10_a8 = a8;
+  passStruct3BytesPackedIntx10_a9 = a9;
+
+  final result = passStruct3BytesPackedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct3BytesPackedIntx10AfterCallback() {
+  final result = passStruct3BytesPackedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(10, result);
+}
+
+typedef PassStruct8BytesPackedIntx10Type = Int64 Function(
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt,
+    Struct8BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a0 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a1 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a2 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a3 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a4 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a5 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a6 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a7 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a8 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a9 = Struct8BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesPackedIntx10Result = 0;
+
+int passStruct8BytesPackedIntx10CalculateResult() {
+  int result = 0;
+
+  result += passStruct8BytesPackedIntx10_a0.a0;
+  result += passStruct8BytesPackedIntx10_a0.a1;
+  result += passStruct8BytesPackedIntx10_a0.a2;
+  result += passStruct8BytesPackedIntx10_a0.a3;
+  result += passStruct8BytesPackedIntx10_a0.a4;
+  result += passStruct8BytesPackedIntx10_a1.a0;
+  result += passStruct8BytesPackedIntx10_a1.a1;
+  result += passStruct8BytesPackedIntx10_a1.a2;
+  result += passStruct8BytesPackedIntx10_a1.a3;
+  result += passStruct8BytesPackedIntx10_a1.a4;
+  result += passStruct8BytesPackedIntx10_a2.a0;
+  result += passStruct8BytesPackedIntx10_a2.a1;
+  result += passStruct8BytesPackedIntx10_a2.a2;
+  result += passStruct8BytesPackedIntx10_a2.a3;
+  result += passStruct8BytesPackedIntx10_a2.a4;
+  result += passStruct8BytesPackedIntx10_a3.a0;
+  result += passStruct8BytesPackedIntx10_a3.a1;
+  result += passStruct8BytesPackedIntx10_a3.a2;
+  result += passStruct8BytesPackedIntx10_a3.a3;
+  result += passStruct8BytesPackedIntx10_a3.a4;
+  result += passStruct8BytesPackedIntx10_a4.a0;
+  result += passStruct8BytesPackedIntx10_a4.a1;
+  result += passStruct8BytesPackedIntx10_a4.a2;
+  result += passStruct8BytesPackedIntx10_a4.a3;
+  result += passStruct8BytesPackedIntx10_a4.a4;
+  result += passStruct8BytesPackedIntx10_a5.a0;
+  result += passStruct8BytesPackedIntx10_a5.a1;
+  result += passStruct8BytesPackedIntx10_a5.a2;
+  result += passStruct8BytesPackedIntx10_a5.a3;
+  result += passStruct8BytesPackedIntx10_a5.a4;
+  result += passStruct8BytesPackedIntx10_a6.a0;
+  result += passStruct8BytesPackedIntx10_a6.a1;
+  result += passStruct8BytesPackedIntx10_a6.a2;
+  result += passStruct8BytesPackedIntx10_a6.a3;
+  result += passStruct8BytesPackedIntx10_a6.a4;
+  result += passStruct8BytesPackedIntx10_a7.a0;
+  result += passStruct8BytesPackedIntx10_a7.a1;
+  result += passStruct8BytesPackedIntx10_a7.a2;
+  result += passStruct8BytesPackedIntx10_a7.a3;
+  result += passStruct8BytesPackedIntx10_a7.a4;
+  result += passStruct8BytesPackedIntx10_a8.a0;
+  result += passStruct8BytesPackedIntx10_a8.a1;
+  result += passStruct8BytesPackedIntx10_a8.a2;
+  result += passStruct8BytesPackedIntx10_a8.a3;
+  result += passStruct8BytesPackedIntx10_a8.a4;
+  result += passStruct8BytesPackedIntx10_a9.a0;
+  result += passStruct8BytesPackedIntx10_a9.a1;
+  result += passStruct8BytesPackedIntx10_a9.a2;
+  result += passStruct8BytesPackedIntx10_a9.a3;
+  result += passStruct8BytesPackedIntx10_a9.a4;
+
+  passStruct8BytesPackedIntx10Result = result;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+int passStruct8BytesPackedIntx10(
+    Struct8BytesPackedInt a0,
+    Struct8BytesPackedInt a1,
+    Struct8BytesPackedInt a2,
+    Struct8BytesPackedInt a3,
+    Struct8BytesPackedInt a4,
+    Struct8BytesPackedInt a5,
+    Struct8BytesPackedInt a6,
+    Struct8BytesPackedInt a7,
+    Struct8BytesPackedInt a8,
+    Struct8BytesPackedInt a9) {
+  print(
+      "passStruct8BytesPackedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+  // 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("PassStruct8BytesPackedIntx10 throwing on purpose!");
+  }
+
+  passStruct8BytesPackedIntx10_a0 = a0;
+  passStruct8BytesPackedIntx10_a1 = a1;
+  passStruct8BytesPackedIntx10_a2 = a2;
+  passStruct8BytesPackedIntx10_a3 = a3;
+  passStruct8BytesPackedIntx10_a4 = a4;
+  passStruct8BytesPackedIntx10_a5 = a5;
+  passStruct8BytesPackedIntx10_a6 = a6;
+  passStruct8BytesPackedIntx10_a7 = a7;
+  passStruct8BytesPackedIntx10_a8 = a8;
+  passStruct8BytesPackedIntx10_a9 = a9;
+
+  final result = passStruct8BytesPackedIntx10CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct8BytesPackedIntx10AfterCallback() {
+  final result = passStruct8BytesPackedIntx10CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(1275, result);
+}
+
+typedef PassStruct9BytesPackedMixedx10DoubleInt32Type = Double Function(
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Struct9BytesPackedMixed,
+    Double,
+    Int32);
+
+// Global variables to be able to test inputs after callback returned.
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a0 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a1 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a2 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a3 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a4 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a5 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a6 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a7 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a8 =
+    Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a9 =
+    Struct9BytesPackedMixed();
+double passStruct9BytesPackedMixedx10DoubleInt32_a10 = 0.0;
+int passStruct9BytesPackedMixedx10DoubleInt32_a11 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct9BytesPackedMixedx10DoubleInt32Result = 0.0;
+
+double passStruct9BytesPackedMixedx10DoubleInt32CalculateResult() {
+  double result = 0;
+
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a0;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a1;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a10;
+  result += passStruct9BytesPackedMixedx10DoubleInt32_a11;
+
+  passStruct9BytesPackedMixedx10DoubleInt32Result = result;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+double passStruct9BytesPackedMixedx10DoubleInt32(
+    Struct9BytesPackedMixed a0,
+    Struct9BytesPackedMixed a1,
+    Struct9BytesPackedMixed a2,
+    Struct9BytesPackedMixed a3,
+    Struct9BytesPackedMixed a4,
+    Struct9BytesPackedMixed a5,
+    Struct9BytesPackedMixed a6,
+    Struct9BytesPackedMixed a7,
+    Struct9BytesPackedMixed a8,
+    Struct9BytesPackedMixed a9,
+    double a10,
+    int a11) {
+  print(
+      "passStruct9BytesPackedMixedx10DoubleInt32(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+  // 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(
+        "PassStruct9BytesPackedMixedx10DoubleInt32 throwing on purpose!");
+  }
+
+  passStruct9BytesPackedMixedx10DoubleInt32_a0 = a0;
+  passStruct9BytesPackedMixedx10DoubleInt32_a1 = a1;
+  passStruct9BytesPackedMixedx10DoubleInt32_a2 = a2;
+  passStruct9BytesPackedMixedx10DoubleInt32_a3 = a3;
+  passStruct9BytesPackedMixedx10DoubleInt32_a4 = a4;
+  passStruct9BytesPackedMixedx10DoubleInt32_a5 = a5;
+  passStruct9BytesPackedMixedx10DoubleInt32_a6 = a6;
+  passStruct9BytesPackedMixedx10DoubleInt32_a7 = a7;
+  passStruct9BytesPackedMixedx10DoubleInt32_a8 = a8;
+  passStruct9BytesPackedMixedx10DoubleInt32_a9 = a9;
+  passStruct9BytesPackedMixedx10DoubleInt32_a10 = a10;
+  passStruct9BytesPackedMixedx10DoubleInt32_a11 = a11;
+
+  final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct9BytesPackedMixedx10DoubleInt32AfterCallback() {
+  final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(211.0, result);
+}
+
+typedef PassStruct5BytesPackedMixedType = Double Function(
+    Struct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct5BytesPackedMixed passStruct5BytesPackedMixed_a0 =
+    Struct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct5BytesPackedMixedResult = 0.0;
+
+double passStruct5BytesPackedMixedCalculateResult() {
+  double result = 0;
+
+  result += passStruct5BytesPackedMixed_a0.a0;
+  result += passStruct5BytesPackedMixed_a0.a1;
+
+  passStruct5BytesPackedMixedResult = result;
+
+  return result;
+}
+
+/// This packed struct happens to have only aligned members.
+double passStruct5BytesPackedMixed(Struct5BytesPackedMixed a0) {
+  print("passStruct5BytesPackedMixed(${a0})");
+
+  // 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("PassStruct5BytesPackedMixed throwing on purpose!");
+  }
+
+  passStruct5BytesPackedMixed_a0 = a0;
+
+  final result = passStruct5BytesPackedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct5BytesPackedMixedAfterCallback() {
+  final result = passStruct5BytesPackedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(1.0, result);
+}
+
+typedef PassStructNestedAlignmentStruct5BytesPackedMixedType = Double Function(
+    StructNestedAlignmentStruct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedAlignmentStruct5BytesPackedMixed
+    passStructNestedAlignmentStruct5BytesPackedMixed_a0 =
+    StructNestedAlignmentStruct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedAlignmentStruct5BytesPackedMixedResult = 0.0;
+
+double passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult() {
+  double result = 0;
+
+  result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a0;
+  result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a0;
+  result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a1;
+
+  passStructNestedAlignmentStruct5BytesPackedMixedResult = result;
+
+  return result;
+}
+
+/// Check alignment of packed struct in non-packed struct.
+double passStructNestedAlignmentStruct5BytesPackedMixed(
+    StructNestedAlignmentStruct5BytesPackedMixed a0) {
+  print("passStructNestedAlignmentStruct5BytesPackedMixed(${a0})");
+
+  // 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(
+        "PassStructNestedAlignmentStruct5BytesPackedMixed throwing on purpose!");
+  }
+
+  passStructNestedAlignmentStruct5BytesPackedMixed_a0 = a0;
+
+  final result =
+      passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback() {
+  final result =
+      passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(6.0, result);
+}
+
+typedef PassStruct6BytesInlineArrayIntType = Double Function(
+    Struct6BytesInlineArrayInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct6BytesInlineArrayInt passStruct6BytesInlineArrayInt_a0 =
+    Struct6BytesInlineArrayInt();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct6BytesInlineArrayIntResult = 0.0;
+
+double passStruct6BytesInlineArrayIntCalculateResult() {
+  double result = 0;
+
+  result += passStruct6BytesInlineArrayInt_a0.a0[0].a0;
+  result += passStruct6BytesInlineArrayInt_a0.a0[0].a1;
+  result += passStruct6BytesInlineArrayInt_a0.a0[1].a0;
+  result += passStruct6BytesInlineArrayInt_a0.a0[1].a1;
+
+  passStruct6BytesInlineArrayIntResult = result;
+
+  return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct6BytesInlineArrayInt(Struct6BytesInlineArrayInt a0) {
+  print("passStruct6BytesInlineArrayInt(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0[0].a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct6BytesInlineArrayInt throwing on purpose!");
+  }
+
+  passStruct6BytesInlineArrayInt_a0 = a0;
+
+  final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct6BytesInlineArrayIntAfterCallback() {
+  final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(2.0, result);
+}
+
+typedef PassStruct15BytesInlineArrayMixedType = Double Function(
+    Struct15BytesInlineArrayMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct15BytesInlineArrayMixed passStruct15BytesInlineArrayMixed_a0 =
+    Struct15BytesInlineArrayMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct15BytesInlineArrayMixedResult = 0.0;
+
+double passStruct15BytesInlineArrayMixedCalculateResult() {
+  double result = 0;
+
+  result += passStruct15BytesInlineArrayMixed_a0.a0[0].a0;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[0].a1;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[1].a0;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[1].a1;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[2].a0;
+  result += passStruct15BytesInlineArrayMixed_a0.a0[2].a1;
+
+  passStruct15BytesInlineArrayMixedResult = result;
+
+  return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct15BytesInlineArrayMixed(Struct15BytesInlineArrayMixed a0) {
+  print("passStruct15BytesInlineArrayMixed(${a0})");
+
+  // In legacy mode, possibly return null.
+  if (a0.a0[0].a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+    print("throwing!");
+    throw Exception("PassStruct15BytesInlineArrayMixed throwing on purpose!");
+  }
+
+  passStruct15BytesInlineArrayMixed_a0 = a0;
+
+  final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passStruct15BytesInlineArrayMixedAfterCallback() {
+  final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.approxEquals(3.0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
@@ -8571,6 +9229,194 @@
   calloc.free(returnStruct1024BytesHomogeneousUint64ResultPointer);
 }
 
+typedef ReturnStruct3BytesPackedIntType = Struct3BytesPackedInt Function(
+    Int8, Int16);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct3BytesPackedInt_a0 = 0;
+int returnStruct3BytesPackedInt_a1 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct3BytesPackedInt> returnStruct3BytesPackedIntResultPointer =
+    nullptr;
+
+Struct3BytesPackedInt returnStruct3BytesPackedIntCalculateResult() {
+  final resultPointer = calloc<Struct3BytesPackedInt>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnStruct3BytesPackedInt_a0;
+  result.a1 = returnStruct3BytesPackedInt_a1;
+
+  returnStruct3BytesPackedIntResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Small struct with mis-aligned member.
+Struct3BytesPackedInt returnStruct3BytesPackedInt(int a0, int a1) {
+  print("returnStruct3BytesPackedInt(${a0}, ${a1})");
+
+  // 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("ReturnStruct3BytesPackedInt throwing on purpose!");
+  }
+
+  returnStruct3BytesPackedInt_a0 = a0;
+  returnStruct3BytesPackedInt_a1 = a1;
+
+  final result = returnStruct3BytesPackedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct3BytesPackedIntAfterCallback() {
+  calloc.free(returnStruct3BytesPackedIntResultPointer);
+
+  final result = returnStruct3BytesPackedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnStruct3BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct8BytesPackedIntType = Struct8BytesPackedInt Function(
+    Uint8, Uint32, Uint8, Uint8, Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct8BytesPackedInt_a0 = 0;
+int returnStruct8BytesPackedInt_a1 = 0;
+int returnStruct8BytesPackedInt_a2 = 0;
+int returnStruct8BytesPackedInt_a3 = 0;
+int returnStruct8BytesPackedInt_a4 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct8BytesPackedInt> returnStruct8BytesPackedIntResultPointer =
+    nullptr;
+
+Struct8BytesPackedInt returnStruct8BytesPackedIntCalculateResult() {
+  final resultPointer = calloc<Struct8BytesPackedInt>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnStruct8BytesPackedInt_a0;
+  result.a1 = returnStruct8BytesPackedInt_a1;
+  result.a2 = returnStruct8BytesPackedInt_a2;
+  result.a3 = returnStruct8BytesPackedInt_a3;
+  result.a4 = returnStruct8BytesPackedInt_a4;
+
+  returnStruct8BytesPackedIntResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+Struct8BytesPackedInt returnStruct8BytesPackedInt(
+    int a0, int a1, int a2, int a3, int a4) {
+  print("returnStruct8BytesPackedInt(${a0}, ${a1}, ${a2}, ${a3}, ${a4})");
+
+  // 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("ReturnStruct8BytesPackedInt throwing on purpose!");
+  }
+
+  returnStruct8BytesPackedInt_a0 = a0;
+  returnStruct8BytesPackedInt_a1 = a1;
+  returnStruct8BytesPackedInt_a2 = a2;
+  returnStruct8BytesPackedInt_a3 = a3;
+  returnStruct8BytesPackedInt_a4 = a4;
+
+  final result = returnStruct8BytesPackedIntCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct8BytesPackedIntAfterCallback() {
+  calloc.free(returnStruct8BytesPackedIntResultPointer);
+
+  final result = returnStruct8BytesPackedIntCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnStruct8BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct9BytesPackedMixedType = Struct9BytesPackedMixed Function(
+    Uint8, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct9BytesPackedMixed_a0 = 0;
+double returnStruct9BytesPackedMixed_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct9BytesPackedMixed> returnStruct9BytesPackedMixedResultPointer =
+    nullptr;
+
+Struct9BytesPackedMixed returnStruct9BytesPackedMixedCalculateResult() {
+  final resultPointer = calloc<Struct9BytesPackedMixed>();
+  final result = resultPointer.ref;
+
+  result.a0 = returnStruct9BytesPackedMixed_a0;
+  result.a1 = returnStruct9BytesPackedMixed_a1;
+
+  returnStruct9BytesPackedMixedResultPointer = resultPointer;
+
+  return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+Struct9BytesPackedMixed returnStruct9BytesPackedMixed(int a0, double a1) {
+  print("returnStruct9BytesPackedMixed(${a0}, ${a1})");
+
+  // 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("ReturnStruct9BytesPackedMixed throwing on purpose!");
+  }
+
+  returnStruct9BytesPackedMixed_a0 = a0;
+  returnStruct9BytesPackedMixed_a1 = a1;
+
+  final result = returnStruct9BytesPackedMixedCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void returnStruct9BytesPackedMixedAfterCallback() {
+  calloc.free(returnStruct9BytesPackedMixedResultPointer);
+
+  final result = returnStruct9BytesPackedMixedCalculateResult();
+
+  print("after callback result = $result");
+
+  calloc.free(returnStruct9BytesPackedMixedResultPointer);
+}
+
 typedef ReturnStructArgumentStruct1ByteIntType = Struct1ByteInt Function(
     Struct1ByteInt);
 
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 91747de..48c7b59 100644
--- a/tests/ffi_2/function_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_test.dart
@@ -70,6 +70,13 @@
     testPassStructStruct16BytesMixed3x10();
     testPassUint8Struct32BytesInlineArrayMultiDimensionalI();
     testPassUint8Struct4BytesInlineArrayMultiDimensionalIn();
+    testPassStruct3BytesPackedIntx10();
+    testPassStruct8BytesPackedIntx10();
+    testPassStruct9BytesPackedMixedx10DoubleInt32();
+    testPassStruct5BytesPackedMixed();
+    testPassStructNestedAlignmentStruct5BytesPackedMixed();
+    testPassStruct6BytesInlineArrayInt();
+    testPassStruct15BytesInlineArrayMixed();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -92,6 +99,9 @@
     testReturnStruct32BytesHomogeneousDouble();
     testReturnStruct40BytesHomogeneousDouble();
     testReturnStruct1024BytesHomogeneousUint64();
+    testReturnStruct3BytesPackedInt();
+    testReturnStruct8BytesPackedInt();
+    testReturnStruct9BytesPackedMixed();
     testReturnStructArgumentStruct1ByteInt();
     testReturnStructArgumentInt32x8Struct1ByteInt();
     testReturnStructArgumentStruct8BytesHomogeneousFloat();
@@ -1186,6 +1196,93 @@
       ]})";
 }
 
+@Packed(1)
+class Struct3BytesPackedInt extends Struct {
+  @Int8()
+  int a0;
+
+  @Int16()
+  int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct3BytesPackedIntMembersAligned extends Struct {
+  @Int8()
+  int a0;
+
+  @Int16()
+  int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct5BytesPackedMixed extends Struct {
+  @Float()
+  double a0;
+
+  @Uint8()
+  int a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedAlignmentStruct5BytesPackedMixed extends Struct {
+  @Uint8()
+  int a0;
+
+  Struct5BytesPackedMixed a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct6BytesInlineArrayInt extends Struct {
+  @Array(2)
+  Array<Struct3BytesPackedIntMembersAligned> a0;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 2; i0 += 1) a0[i0]]})";
+}
+
+@Packed(1)
+class Struct8BytesPackedInt extends Struct {
+  @Uint8()
+  int a0;
+
+  @Uint32()
+  int a1;
+
+  @Uint8()
+  int a2;
+
+  @Uint8()
+  int a3;
+
+  @Uint8()
+  int a4;
+
+  String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4})";
+}
+
+@Packed(1)
+class Struct9BytesPackedMixed extends Struct {
+  @Uint8()
+  int a0;
+
+  @Double()
+  double a1;
+
+  String toString() => "(${a0}, ${a1})";
+}
+
+class Struct15BytesInlineArrayMixed extends Struct {
+  @Array(3)
+  Array<Struct5BytesPackedMixed> a0;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 3; i0 += 1) a0[i0]]})";
+}
+
 final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
     Int64 Function(
         Struct1ByteInt,
@@ -5423,6 +5520,402 @@
   calloc.free(a1Pointer);
 }
 
+final passStruct3BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+    Int64 Function(
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt),
+    int Function(
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt,
+        Struct3BytesPackedInt)>("PassStruct3BytesPackedIntx10");
+
+/// Small struct with mis-aligned member.
+void testPassStruct3BytesPackedIntx10() {
+  final a0Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Struct3BytesPackedInt>();
+  final Struct3BytesPackedInt a9 = a9Pointer.ref;
+
+  a0.a0 = -1;
+  a0.a1 = 2;
+  a1.a0 = -3;
+  a1.a1 = 4;
+  a2.a0 = -5;
+  a2.a1 = 6;
+  a3.a0 = -7;
+  a3.a1 = 8;
+  a4.a0 = -9;
+  a4.a1 = 10;
+  a5.a0 = -11;
+  a5.a1 = 12;
+  a6.a0 = -13;
+  a6.a1 = 14;
+  a7.a0 = -15;
+  a7.a1 = 16;
+  a8.a0 = -17;
+  a8.a1 = 18;
+  a9.a0 = -19;
+  a9.a1 = 20;
+
+  final result =
+      passStruct3BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.equals(10, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passStruct8BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+    Int64 Function(
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt),
+    int Function(
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt,
+        Struct8BytesPackedInt)>("PassStruct8BytesPackedIntx10");
+
+/// Struct with mis-aligned member.
+void testPassStruct8BytesPackedIntx10() {
+  final a0Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Struct8BytesPackedInt>();
+  final Struct8BytesPackedInt a9 = a9Pointer.ref;
+
+  a0.a0 = 1;
+  a0.a1 = 2;
+  a0.a2 = 3;
+  a0.a3 = 4;
+  a0.a4 = 5;
+  a1.a0 = 6;
+  a1.a1 = 7;
+  a1.a2 = 8;
+  a1.a3 = 9;
+  a1.a4 = 10;
+  a2.a0 = 11;
+  a2.a1 = 12;
+  a2.a2 = 13;
+  a2.a3 = 14;
+  a2.a4 = 15;
+  a3.a0 = 16;
+  a3.a1 = 17;
+  a3.a2 = 18;
+  a3.a3 = 19;
+  a3.a4 = 20;
+  a4.a0 = 21;
+  a4.a1 = 22;
+  a4.a2 = 23;
+  a4.a3 = 24;
+  a4.a4 = 25;
+  a5.a0 = 26;
+  a5.a1 = 27;
+  a5.a2 = 28;
+  a5.a3 = 29;
+  a5.a4 = 30;
+  a6.a0 = 31;
+  a6.a1 = 32;
+  a6.a2 = 33;
+  a6.a3 = 34;
+  a6.a4 = 35;
+  a7.a0 = 36;
+  a7.a1 = 37;
+  a7.a2 = 38;
+  a7.a3 = 39;
+  a7.a4 = 40;
+  a8.a0 = 41;
+  a8.a1 = 42;
+  a8.a2 = 43;
+  a8.a3 = 44;
+  a8.a4 = 45;
+  a9.a0 = 46;
+  a9.a1 = 47;
+  a9.a2 = 48;
+  a9.a3 = 49;
+  a9.a4 = 50;
+
+  final result =
+      passStruct8BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+  print("result = $result");
+
+  Expect.equals(1275, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passStruct9BytesPackedMixedx10DoubleInt32 =
+    ffiTestFunctions.lookupFunction<
+        Double Function(
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Double,
+            Int32),
+        double Function(
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            Struct9BytesPackedMixed,
+            double,
+            int)>("PassStruct9BytesPackedMixedx10DoubleInt32");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testPassStruct9BytesPackedMixedx10DoubleInt32() {
+  final a0Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a0 = a0Pointer.ref;
+  final a1Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a1 = a1Pointer.ref;
+  final a2Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a2 = a2Pointer.ref;
+  final a3Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a3 = a3Pointer.ref;
+  final a4Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a4 = a4Pointer.ref;
+  final a5Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a5 = a5Pointer.ref;
+  final a6Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a6 = a6Pointer.ref;
+  final a7Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a7 = a7Pointer.ref;
+  final a8Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a8 = a8Pointer.ref;
+  final a9Pointer = calloc<Struct9BytesPackedMixed>();
+  final Struct9BytesPackedMixed a9 = a9Pointer.ref;
+  double a10;
+  int a11;
+
+  a0.a0 = 1;
+  a0.a1 = 2.0;
+  a1.a0 = 3;
+  a1.a1 = 4.0;
+  a2.a0 = 5;
+  a2.a1 = 6.0;
+  a3.a0 = 7;
+  a3.a1 = 8.0;
+  a4.a0 = 9;
+  a4.a1 = 10.0;
+  a5.a0 = 11;
+  a5.a1 = 12.0;
+  a6.a0 = 13;
+  a6.a1 = 14.0;
+  a7.a0 = 15;
+  a7.a1 = 16.0;
+  a8.a0 = 17;
+  a8.a1 = 18.0;
+  a9.a0 = 19;
+  a9.a1 = 20.0;
+  a10 = -21.0;
+  a11 = 22;
+
+  final result = passStruct9BytesPackedMixedx10DoubleInt32(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.approxEquals(211.0, result);
+
+  calloc.free(a0Pointer);
+  calloc.free(a1Pointer);
+  calloc.free(a2Pointer);
+  calloc.free(a3Pointer);
+  calloc.free(a4Pointer);
+  calloc.free(a5Pointer);
+  calloc.free(a6Pointer);
+  calloc.free(a7Pointer);
+  calloc.free(a8Pointer);
+  calloc.free(a9Pointer);
+}
+
+final passStruct5BytesPackedMixed = ffiTestFunctions.lookupFunction<
+    Double Function(Struct5BytesPackedMixed),
+    double Function(Struct5BytesPackedMixed)>("PassStruct5BytesPackedMixed");
+
+/// This packed struct happens to have only aligned members.
+void testPassStruct5BytesPackedMixed() {
+  final a0Pointer = calloc<Struct5BytesPackedMixed>();
+  final Struct5BytesPackedMixed a0 = a0Pointer.ref;
+
+  a0.a0 = -1.0;
+  a0.a1 = 2;
+
+  final result = passStruct5BytesPackedMixed(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(1.0, result);
+
+  calloc.free(a0Pointer);
+}
+
+final passStructNestedAlignmentStruct5BytesPackedMixed =
+    ffiTestFunctions.lookupFunction<
+            Double Function(StructNestedAlignmentStruct5BytesPackedMixed),
+            double Function(StructNestedAlignmentStruct5BytesPackedMixed)>(
+        "PassStructNestedAlignmentStruct5BytesPackedMixed");
+
+/// Check alignment of packed struct in non-packed struct.
+void testPassStructNestedAlignmentStruct5BytesPackedMixed() {
+  final a0Pointer = calloc<StructNestedAlignmentStruct5BytesPackedMixed>();
+  final StructNestedAlignmentStruct5BytesPackedMixed a0 = a0Pointer.ref;
+
+  a0.a0 = 1;
+  a0.a1.a0 = 2.0;
+  a0.a1.a1 = 3;
+
+  final result = passStructNestedAlignmentStruct5BytesPackedMixed(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(6.0, result);
+
+  calloc.free(a0Pointer);
+}
+
+final passStruct6BytesInlineArrayInt = ffiTestFunctions.lookupFunction<
+    Double Function(Struct6BytesInlineArrayInt),
+    double Function(
+        Struct6BytesInlineArrayInt)>("PassStruct6BytesInlineArrayInt");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct6BytesInlineArrayInt() {
+  final a0Pointer = calloc<Struct6BytesInlineArrayInt>();
+  final Struct6BytesInlineArrayInt a0 = a0Pointer.ref;
+
+  a0.a0[0].a0 = -1;
+  a0.a0[0].a1 = 2;
+  a0.a0[1].a0 = -3;
+  a0.a0[1].a1 = 4;
+
+  final result = passStruct6BytesInlineArrayInt(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(2.0, result);
+
+  calloc.free(a0Pointer);
+}
+
+final passStruct15BytesInlineArrayMixed = ffiTestFunctions.lookupFunction<
+    Double Function(Struct15BytesInlineArrayMixed),
+    double Function(
+        Struct15BytesInlineArrayMixed)>("PassStruct15BytesInlineArrayMixed");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct15BytesInlineArrayMixed() {
+  final a0Pointer = calloc<Struct15BytesInlineArrayMixed>();
+  final Struct15BytesInlineArrayMixed a0 = a0Pointer.ref;
+
+  a0.a0[0].a0 = -1.0;
+  a0.a0[0].a1 = 2;
+  a0.a0[1].a0 = -3.0;
+  a0.a0[1].a1 = 4;
+  a0.a0[2].a0 = -5.0;
+  a0.a0[2].a1 = 6;
+
+  final result = passStruct15BytesInlineArrayMixed(a0);
+
+  print("result = $result");
+
+  Expect.approxEquals(3.0, result);
+
+  calloc.free(a0Pointer);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -6841,6 +7334,78 @@
   Expect.equals(a127, result.a127);
 }
 
+final returnStruct3BytesPackedInt = ffiTestFunctions.lookupFunction<
+    Struct3BytesPackedInt Function(Int8, Int16),
+    Struct3BytesPackedInt Function(int, int)>("ReturnStruct3BytesPackedInt");
+
+/// Small struct with mis-aligned member.
+void testReturnStruct3BytesPackedInt() {
+  int a0;
+  int a1;
+
+  a0 = -1;
+  a1 = 2;
+
+  final result = returnStruct3BytesPackedInt(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.equals(a1, result.a1);
+}
+
+final returnStruct8BytesPackedInt = ffiTestFunctions.lookupFunction<
+    Struct8BytesPackedInt Function(Uint8, Uint32, Uint8, Uint8, Uint8),
+    Struct8BytesPackedInt Function(
+        int, int, int, int, int)>("ReturnStruct8BytesPackedInt");
+
+/// Struct with mis-aligned member.
+void testReturnStruct8BytesPackedInt() {
+  int a0;
+  int a1;
+  int a2;
+  int a3;
+  int a4;
+
+  a0 = 1;
+  a1 = 2;
+  a2 = 3;
+  a3 = 4;
+  a4 = 5;
+
+  final result = returnStruct8BytesPackedInt(a0, a1, a2, a3, a4);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.equals(a1, result.a1);
+  Expect.equals(a2, result.a2);
+  Expect.equals(a3, result.a3);
+  Expect.equals(a4, result.a4);
+}
+
+final returnStruct9BytesPackedMixed = ffiTestFunctions.lookupFunction<
+    Struct9BytesPackedMixed Function(Uint8, Double),
+    Struct9BytesPackedMixed Function(
+        int, double)>("ReturnStruct9BytesPackedMixed");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testReturnStruct9BytesPackedMixed() {
+  int a0;
+  double a1;
+
+  a0 = 1;
+  a1 = 2.0;
+
+  final result = returnStruct9BytesPackedMixed(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(a0, result.a0);
+  Expect.approxEquals(a1, result.a1);
+}
+
 final returnStructArgumentStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Struct1ByteInt),
     Struct1ByteInt Function(
diff --git a/tests/ffi_2/generator/c_types.dart b/tests/ffi_2/generator/c_types.dart
index e94b35b..df3b44b 100644
--- a/tests/ffi_2/generator/c_types.dart
+++ b/tests/ffi_2/generator/c_types.dart
@@ -155,20 +155,24 @@
 class StructType extends CType {
   final List<Member> members;
 
+  final int? packing;
+
   /// To disambiguate same size structs.
   final String suffix;
 
   /// To override names.
   final String overrideName;
 
-  StructType(List<CType> memberTypes)
+  StructType(List<CType> memberTypes, {int? this.packing})
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "",
         this.overrideName = "";
-  StructType.disambiguate(List<CType> memberTypes, this.suffix)
+  StructType.disambiguate(List<CType> memberTypes, this.suffix,
+      {int? this.packing})
       : this.members = generateMemberNames(memberTypes),
         this.overrideName = "";
-  StructType.override(List<CType> memberTypes, this.overrideName)
+  StructType.override(List<CType> memberTypes, this.overrideName,
+      {int? this.packing})
       : this.members = generateMemberNames(memberTypes),
         this.suffix = "";
 
@@ -183,9 +187,19 @@
       !memberTypes.map((e) => e.hasSize).contains(false) && !hasPadding;
   int get size => memberTypes.fold(0, (int acc, e) => acc + e.size);
 
-  /// Rough approximation, to not redo all ABI logic here.
-  bool get hasPadding =>
-      members.length < 2 ? false : members[0].type.size < members[1].type.size;
+  bool get hasPacking => packing != null;
+
+  bool get hasPadding {
+    if (members.length < 2) {
+      return false;
+    }
+    if (packing == 1) {
+      return false;
+    }
+
+    /// Rough approximation, to not redo all ABI logic here.
+    return members[0].type.size < members[1].type.size;
+  }
 
   bool get hasNestedStructs =>
       members.map((e) => e.type is StructType).contains(true);
@@ -217,6 +231,12 @@
     if (hasSize) {
       result += "${size}Byte" + (size != 1 ? "s" : "");
     }
+    if (hasPacking) {
+      result += "Packed";
+      if (packing! > 1) {
+        result += "$packing";
+      }
+    }
     if (hasNestedStructs) {
       result += "Nested";
     }
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 d0a22b0..1ad3c11 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
@@ -313,7 +313,7 @@
   FunctionType(
       [
         uint8,
-        struct8bytesInlineArrayMultiDimesional,
+        struct32bytesInlineArrayMultiDimesional,
         uint8,
         struct8bytesInlineArrayMultiDimesional,
         uint8,
@@ -328,6 +328,36 @@
       uint32,
       """
 Test struct in multi dimensional inline array."""),
+  FunctionType(List.filled(10, struct3bytesPacked), int64, """
+Small struct with mis-aligned member."""),
+  FunctionType(List.filled(10, struct8bytesPacked), int64, """
+Struct with mis-aligned member."""),
+  FunctionType(
+      [...List.filled(10, struct9bytesPacked), double_, int32],
+      double_,
+      """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
+  FunctionType(
+      [struct5bytesPacked],
+      double_,
+      """
+This packed struct happens to have only aligned members."""),
+  FunctionType(
+      [struct6bytesPacked],
+      double_,
+      """
+Check alignment of packed struct in non-packed struct."""),
+  FunctionType(
+      [struct6bytesPacked2],
+      double_,
+      """
+Check alignment of packed struct array in non-packed struct."""),
+  FunctionType(
+      [struct15bytesPacked],
+      double_,
+      """
+Check alignment of packed struct array in non-packed struct."""),
   FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
 Smallest struct with data."""),
   FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -383,6 +413,13 @@
 Return value too big to go in FPU registers on arm64."""),
   FunctionType(struct1024bytesInt.memberTypes, struct1024bytesInt, """
 Test 1kb struct."""),
+  FunctionType(struct3bytesPacked.memberTypes, struct3bytesPacked, """
+Small struct with mis-aligned member."""),
+  FunctionType(struct8bytesPacked.memberTypes, struct8bytesPacked, """
+Struct with mis-aligned member."""),
+  FunctionType(struct9bytesPacked.memberTypes, struct9bytesPacked, """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
   FunctionType(
       [struct1byteInt],
       struct1byteInt,
@@ -529,6 +566,14 @@
   struct32bytesInlineArrayMultiDimesional,
   struct64bytesInlineArrayMultiDimesional,
   structMultiDimensionalStruct,
+  struct3bytesPacked,
+  struct3bytesPackedMembersAligned,
+  struct5bytesPacked,
+  struct6bytesPacked,
+  struct6bytesPacked2,
+  struct8bytesPacked,
+  struct9bytesPacked,
+  struct15bytesPacked,
 ];
 
 final struct1byteInt = StructType([int8]);
@@ -672,3 +717,27 @@
 final structMultiDimensionalStruct = StructType([
   FixedLengthArrayType.multi(struct1byteInt, [2, 2])
 ]);
+
+final struct3bytesPacked = StructType([int8, int16], packing: 1);
+
+final struct3bytesPackedMembersAligned =
+    StructType.disambiguate([int8, int16], "MembersAligned", packing: 1);
+
+final struct5bytesPacked = StructType([float, uint8], packing: 1);
+
+/// The float in the nested struct is not aligned.
+final struct6bytesPacked = StructType([uint8, struct5bytesPacked]);
+
+/// The second element in the array has a nested misaligned int16.
+final struct6bytesPacked2 =
+    StructType([FixedLengthArrayType(struct3bytesPackedMembersAligned, 2)]);
+
+final struct8bytesPacked =
+    StructType([uint8, uint32, uint8, uint8, uint8], packing: 1);
+
+final struct9bytesPacked = StructType([uint8, double_], packing: 1);
+
+/// The float in the nested struct is aligned in the first element in the
+/// inline array, but not in the subsequent ones.
+final struct15bytesPacked =
+    StructType([FixedLengthArrayType(struct5bytesPacked, 3)]);
diff --git a/tests/ffi_2/generator/structs_by_value_tests_generator.dart b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
index 51f51ab..c66dbea 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
@@ -455,6 +455,7 @@
 
 extension on StructType {
   String dartClass(bool nnbd) {
+    final packingAnnotation = hasPacking ? "@Packed(${packing})" : "";
     String dartFields = "";
     for (final member in members) {
       dartFields += "${member.dartStructField(nnbd)}\n\n";
@@ -477,6 +478,7 @@
       return "\$\{${m.name}\}";
     }).join(", ");
     return """
+    $packingAnnotation
     class $name extends Struct {
       $dartFields
 
@@ -486,14 +488,19 @@
   }
 
   String get cDefinition {
+    final packingPragmaPush =
+        hasPacking ? "#pragma pack(push, ${packing})" : "";
+    final packingPragmaPop = hasPacking ? "#pragma pack(pop)" : "";
     String cFields = "";
     for (final member in members) {
       cFields += "  ${member.cStructField}\n";
     }
     return """
+    $packingPragmaPush
     struct $name {
       $cFields
     };
+    $packingPragmaPop
 
     """;
   }
diff --git a/tests/ffi_2/structs_packed_test.dart b/tests/ffi_2/structs_packed_test.dart
new file mode 100644
index 0000000..cec7703
--- /dev/null
+++ b/tests/ffi_2/structs_packed_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, 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.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+
+import 'dylib_utils.dart';
+
+// Reuse struct definitions.
+import 'function_structs_by_value_generated_test.dart';
+
+void main() {
+  testSizeOfC();
+  testSizeOfDart();
+}
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+final sizeOfStruct3BytesPackedInt =
+    ffiTestFunctions.lookupFunction<Uint64 Function(), int Function()>(
+        "SizeOfStruct3BytesPackedInt");
+
+void testSizeOfC() {
+  Expect.equals(3, sizeOfStruct3BytesPackedInt());
+}
+
+void testSizeOfDart() {
+  // No packing needed to get to 3 bytes.
+  Expect.equals(3, sizeOf<Struct3BytesHomogeneousUint8>());
+
+  // Contents 3 bytes, but alignment forces it to be 4 bytes.
+  Expect.equals(4, sizeOf<Struct3BytesInt2ByteAligned>());
+
+  // Alignment gets the same content back to 3 bytes.
+  Expect.equals(3, sizeOf<Struct3BytesPackedInt>());
+}
diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart
index 978fb2d..3960181 100644
--- a/tests/ffi_2/vmspecific_static_checks_test.dart
+++ b/tests/ffi_2/vmspecific_static_checks_test.dart
@@ -701,3 +701,58 @@
 
   Pointer<Uint8> notEmpty;
 }
+
+@Packed(1)
+class TestStruct1600 extends Struct {
+  Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+@Packed(1) //# 1601: compile-time error
+class TestStruct1601 extends Struct {
+  Pointer<Uint8> notEmpty;
+}
+
+@Packed(3) //# 1602: compile-time error
+class TestStruct1602 extends Struct {
+  Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1603 extends Struct {
+  Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1603Packed extends Struct {
+  Pointer<Uint8> notEmpty;
+
+  TestStruct1603 nestedNotPacked; //# 1603: compile-time error
+}
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+  Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1604Packed extends Struct {
+  Pointer<Uint8> notEmpty;
+
+  TestStruct1604 nestedLooselyPacked; //# 1604: compile-time error
+}
+
+@Packed(1)
+class TestStruct1605Packed extends Struct {
+  Pointer<Uint8> notEmpty;
+
+  @Array(2) //# 1605: compile-time error
+  Array<TestStruct1603> nestedNotPacked; //# 1605: compile-time error
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+  Pointer<Uint8> notEmpty;
+
+  @Array(2) //# 1606: compile-time error
+  Array<TestStruct1604> nestedLooselyPacked; //# 1606: compile-time error
+}
diff --git a/tools/VERSION b/tools/VERSION
index aa81756..78dca80 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 152
+PRERELEASE 153
 PRERELEASE_PATCH 0
\ No newline at end of file