Version 2.12.0-57.0.dev

Merge commit 'b0ccaf75a3e189bf566efcb6bc9872ea0ab75120' into 'dev'
diff --git a/pkg/analysis_server/test/analysis/update_content_test.dart b/pkg/analysis_server/test/analysis/update_content_test.dart
index d8894c8..f000bb7 100644
--- a/pkg/analysis_server/test/analysis/update_content_test.dart
+++ b/pkg/analysis_server/test/analysis/update_content_test.dart
@@ -8,7 +8,6 @@
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
-import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 59aaebb..e72baca 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -9,7 +9,6 @@
 import 'package:analysis_server/src/services/completion/dart/contribution_sorter.dart';
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
-import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
diff --git a/pkg/analysis_server/test/edit/assists_test.dart b/pkg/analysis_server/test/edit/assists_test.dart
index 438a81e..3ab55f3 100644
--- a/pkg/analysis_server/test/edit/assists_test.dart
+++ b/pkg/analysis_server/test/edit/assists_test.dart
@@ -8,7 +8,6 @@
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
-import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
diff --git a/pkg/analysis_server/test/edit/fixes_test.dart b/pkg/analysis_server/test/edit/fixes_test.dart
index 19588f5..660af49 100644
--- a/pkg/analysis_server/test/edit/fixes_test.dart
+++ b/pkg/analysis_server/test/edit/fixes_test.dart
@@ -8,7 +8,6 @@
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
-import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 64e8480..006ce0b 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -23,7 +23,6 @@
 import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
-import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
 import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
diff --git a/pkg/analysis_server/test/plugin/protocol_dart_test.dart b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
index 2fdf1c5..d693387 100644
--- a/pkg/analysis_server/test/plugin/protocol_dart_test.dart
+++ b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
@@ -4,14 +4,8 @@
 
 import 'package:analysis_server/plugin/protocol/protocol_dart.dart';
 import 'package:analyzer/dart/ast/ast.dart' as engine;
-import 'package:analyzer/dart/ast/visitor.dart' as engine;
 import 'package:analyzer/dart/element/element.dart' as engine;
-import 'package:analyzer/dart/element/type.dart' as engine;
-import 'package:analyzer/error/error.dart' as engine;
-import 'package:analyzer/src/dart/ast/utilities.dart' as engine;
 import 'package:analyzer/src/dart/element/element.dart' as engine;
-import 'package:analyzer/src/error/codes.dart' as engine;
-import 'package:analyzer/src/generated/source.dart' as engine;
 import 'package:analyzer/src/generated/testing/element_search.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
diff --git a/pkg/analysis_server/test/protocol_server_test.dart b/pkg/analysis_server/test/protocol_server_test.dart
index 0ce5609..b355d68 100644
--- a/pkg/analysis_server/test/protocol_server_test.dart
+++ b/pkg/analysis_server/test/protocol_server_test.dart
@@ -9,9 +9,7 @@
 import 'package:analysis_server/src/services/search/search_engine.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart' as engine;
 import 'package:analyzer/dart/element/element.dart' as engine;
-import 'package:analyzer/dart/element/type.dart' as engine;
 import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart' as engine;
 import 'package:analyzer/src/dart/analysis/results.dart' as engine;
diff --git a/pkg/analysis_server/test/src/domain_abstract_test.dart b/pkg/analysis_server/test/src/domain_abstract_test.dart
index eb1fdb9..46c9aa6 100644
--- a/pkg/analysis_server/test/src/domain_abstract_test.dart
+++ b/pkg/analysis_server/test/src/domain_abstract_test.dart
@@ -6,7 +6,6 @@
 import 'package:analysis_server/src/plugin/plugin_manager.dart';
 import 'package:analysis_server/src/protocol_server.dart' hide Element;
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
-import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
diff --git a/pkg/analysis_server/test/src/plugin/notification_manager_test.dart b/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
index 88fd799..3157c0c 100644
--- a/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
+++ b/pkg/analysis_server/test/src/plugin/notification_manager_test.dart
@@ -7,9 +7,7 @@
 import 'package:analysis_server/src/channel/channel.dart';
 import 'package:analysis_server/src/plugin/notification_manager.dart';
 import 'package:analyzer/file_system/memory_file_system.dart';
-import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
-import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
diff --git a/pkg/analysis_server/test/src/plugin/request_converter_test.dart b/pkg/analysis_server/test/src/plugin/request_converter_test.dart
index 78d9858..7ba3049 100644
--- a/pkg/analysis_server/test/src/plugin/request_converter_test.dart
+++ b/pkg/analysis_server/test/src/plugin/request_converter_test.dart
@@ -4,7 +4,6 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart' as server;
 import 'package:analysis_server/src/plugin/request_converter.dart';
-import 'package:analysis_server/src/protocol/protocol_internal.dart' as server;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:test/test.dart';
diff --git a/pkg/analysis_server/test/src/plugin/result_converter_test.dart b/pkg/analysis_server/test/src/plugin/result_converter_test.dart
index d45118b..220ca9a 100644
--- a/pkg/analysis_server/test/src/plugin/result_converter_test.dart
+++ b/pkg/analysis_server/test/src/plugin/result_converter_test.dart
@@ -4,7 +4,6 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart' as server;
 import 'package:analysis_server/src/plugin/result_converter.dart';
-import 'package:analysis_server/src/protocol/protocol_internal.dart' as server;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:test/test.dart';
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index e2229b4..a97d843 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -156,8 +156,10 @@
   final Analytics analytics;
 
   @override
-  final ArgParser argParser =
-      ArgParser(usageLineLength: dartdevUsageLineLength);
+  final ArgParser argParser = ArgParser(
+    usageLineLength: dartdevUsageLineLength,
+    allowTrailingOptions: false,
+  );
 
   static const String dartdevDescription =
       'A command-line utility for Dart development';
diff --git a/pkg/dartdev/lib/src/commands/fix.dart b/pkg/dartdev/lib/src/commands/fix.dart
index 11dbee4..9f4d350 100644
--- a/pkg/dartdev/lib/src/commands/fix.dart
+++ b/pkg/dartdev/lib/src/commands/fix.dart
@@ -6,6 +6,7 @@
 import 'dart:io' as io;
 
 import 'package:analysis_server_client/protocol.dart' hide AnalysisError;
+import 'package:intl/intl.dart';
 import 'package:path/path.dart' as path;
 
 import '../analysis_server.dart';
@@ -26,8 +27,8 @@
 
   @override
   FutureOr<int> run() async {
-    log.stdout('\n*** The `fix` command is provisional and subject to change '
-        'or removal in future releases. ***\n');
+    log.stdout('\n${log.ansi.emphasized('Note:')} The `fix` command is '
+        'provisional and subject to change or removal in future releases.\n');
 
     var dryRun = argResults['dry-run'];
     if (argResults.rest.length - (dryRun ? 1 : 0) > 1) {
@@ -43,8 +44,9 @@
 
     var modeText = dryRun ? ' (dry run)' : '';
 
+    final projectName = path.basename(path.canonicalize(dir.path));
     var progress = log.progress(
-        'Computing fixes in ${path.basename(path.canonicalize(dir.path))}$modeText');
+        'Computing fixes in ${log.ansi.emphasized(projectName)}$modeText');
 
     var server = AnalysisServer(
       io.Directory(sdk.sdkPath),
@@ -88,22 +90,30 @@
           });
         });
 
-        log.stdout(
-            '\n$fixCount proposed ${_pluralFix(fixCount)} in $fileCount ${pluralize("file", fileCount)}.\n');
+        log.stdout('');
+
+        final bullet = log.ansi.bullet;
 
         for (var detail in details) {
           log.stdout(path.relative(detail.path, from: dir.path));
-          for (var fix in detail.fixes) {
-            log.stdout(
-                '  ${fix.code} • ${fix.occurrences} ${_pluralFix(fix.occurrences)}');
+          final fixes = detail.fixes.toList();
+          fixes.sort((a, b) => a.code.compareTo(b.code));
+          for (var fix in fixes) {
+            log.stdout('  ${fix.code} $bullet '
+                '${_format(fix.occurrences)} ${_pluralFix(fix.occurrences)}');
           }
+          log.stdout('');
         }
+
+        log.stdout('${_format(fixCount)} proposed ${_pluralFix(fixCount)} '
+            'in ${_format(fileCount)} ${pluralize("file", fileCount)}.');
       } else {
         progress = log.progress('Applying fixes');
         var fileCount = await _applyFixes(edits);
         progress.finish(showTiming: true);
         if (fileCount > 0) {
-          log.stdout('Fixed $fileCount ${pluralize("file", fileCount)}.');
+          log.stdout(
+              'Fixed ${_format(fileCount)} ${pluralize("file", fileCount)}.');
         }
       }
     }
@@ -125,4 +135,8 @@
   }
 
   String _pluralFix(int count) => count == 1 ? 'fix' : 'fixes';
+
+  static final NumberFormat _numberFormat = NumberFormat.decimalPattern();
+
+  static String _format(int value) => _numberFormat.format(value);
 }
diff --git a/pkg/dartdev/pubspec.yaml b/pkg/dartdev/pubspec.yaml
index aea30a3..d6347db 100644
--- a/pkg/dartdev/pubspec.yaml
+++ b/pkg/dartdev/pubspec.yaml
@@ -15,6 +15,7 @@
   dart2native:
     path: ../dart2native
   dart_style: any
+  intl: any
   meta:
     path: ../meta
   nnbd_migration:
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index 101edbf..ff885d6 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:io';
 
+import 'package:cli_util/cli_logging.dart';
 import 'package:test/test.dart';
 
 import '../utils.dart';
@@ -15,6 +16,8 @@
 void defineFix() {
   TestProject p;
 
+  final bullet = Logger.standard().ansi.bullet;
+
   setUp(() => p = null);
 
   tearDown(() => p?.dispose());
@@ -67,8 +70,8 @@
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('3 proposed fixes in 1 file.'));
     expect(result.stdout, contains('lib${Platform.pathSeparator}main.dart'));
-    expect(result.stdout, contains('  annotate_overrides • 1 fix'));
-    expect(result.stdout, contains('  prefer_single_quotes • 2 fixes'));
+    expect(result.stdout, contains('  annotate_overrides $bullet 1 fix'));
+    expect(result.stdout, contains('  prefer_single_quotes $bullet 2 fixes'));
   });
 
   test('.', () {
diff --git a/pkg/dartdev/test/no_such_file_test.dart b/pkg/dartdev/test/no_such_file_test.dart
new file mode 100644
index 0000000..1641dfc
--- /dev/null
+++ b/pkg/dartdev/test/no_such_file_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+import 'utils.dart';
+
+void main() {
+  TestProject p;
+
+  tearDown(() => p?.dispose());
+
+  test('Ensure parsing fails after encountering invalid file', () {
+    // Regression test for https://github.com/dart-lang/sdk/issues/43991
+    p = project();
+    final noArgsResult = p.runSync('foo.dart', []);
+    expect(noArgsResult.stderr, isNotEmpty);
+    expect(noArgsResult.stdout, isEmpty);
+    expect(noArgsResult.exitCode, 64);
+
+    final argsResult = p.runSync('foo.dart', ['--bar']);
+    expect(argsResult.stderr, noArgsResult.stderr);
+    expect(argsResult.stdout, isEmpty);
+    expect(argsResult.exitCode, 64);
+  });
+
+  test('Providing --snapshot VM option with invalid script fails gracefully',
+      () {
+    // Regression test for https://github.com/dart-lang/sdk/issues/43785
+    p = project();
+    final result = p.runSync('--snapshot=abc', ['foo.dart']);
+    expect(result.stderr, isNotEmpty);
+    expect(result.stderr,
+        contains("Error when reading 'foo.dart': No such file or directory"));
+    expect(result.stdout, isEmpty);
+    expect(result.exitCode, 254);
+  });
+}
diff --git a/pkg/dartdev/test/test_all.dart b/pkg/dartdev/test/test_all.dart
index 291bd05..2576ac9 100644
--- a/pkg/dartdev/test/test_all.dart
+++ b/pkg/dartdev/test/test_all.dart
@@ -18,6 +18,7 @@
 import 'commands/test_test.dart' as test;
 import 'core_test.dart' as core;
 import 'experiments_test.dart' as experiments;
+import 'no_such_file_test.dart' as no_such_file;
 import 'sdk_test.dart' as sdk;
 import 'smoke/implicit_smoke_test.dart' as implicit_smoke;
 import 'smoke/invalid_smoke_test.dart' as invalid_smoke;
@@ -37,6 +38,7 @@
     implicit_smoke.main();
     invalid_smoke.main();
     migrate.main();
+    no_such_file.main();
     pub.main();
     run.main();
     compile.main();
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
index 92cca7c..eb746a7 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
@@ -12,7 +12,6 @@
 import 'package:_fe_analyzer_shared/src/messages/codes.dart' show Message, Code;
 
 import 'package:dev_compiler/dev_compiler.dart';
-import 'package:dev_compiler/src/compiler/js_names.dart' as js_ast;
 import 'package:dev_compiler/src/js_ast/js_ast.dart' as js_ast;
 import 'package:dev_compiler/src/kernel/compiler.dart';
 
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index eb2f225..d22ded9 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -151,8 +151,7 @@
       ChangedStructureNotifier changedStructureNotifier}) {
     for (var library in libraries) {
       _CovarianceTransformer(library).transform();
-      JsInteropChecks(
-              coreTypes,
+      JsInteropChecks(coreTypes,
               diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>)
           .visitLibrary(library);
     }
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 3304154..57d6a01 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -610,8 +610,8 @@
 }
 
 // A struct designed to exercise all kinds of alignment rules.
-// Note that offset32A (System V ia32) aligns doubles on 4 bytes while offset32B
-// (Arm 32 bit and MSVC ia32) aligns on 8 bytes.
+// Note that offset32A (System V ia32, iOS arm) aligns doubles on 4 bytes while
+// offset32B (Arm 32 bit and MSVC ia32) aligns on 8 bytes.
 // TODO(37271): Support nested structs.
 // TODO(37470): Add uncommon primitive data types when we want to support them.
 struct VeryLargeStruct {
diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
index 452346f..2e962c1 100644
--- a/runtime/bin/main_options.cc
+++ b/runtime/bin/main_options.cc
@@ -478,6 +478,7 @@
     bool is_potential_file_path = true;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
     if (Options::disable_dart_dev() ||
+        (Options::snapshot_filename() != nullptr) ||
         (is_potential_file_path && !enable_vm_service_)) {
       *script_name = Utils::StrDup(argv[i]);
       run_script = true;
diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni
index c4572df..ad10b9c 100644
--- a/runtime/vm/compiler/compiler_sources.gni
+++ b/runtime/vm/compiler/compiler_sources.gni
@@ -179,6 +179,7 @@
   "backend/typed_data_aot_test.cc",
   "backend/yield_position_test.cc",
   "cha_test.cc",
+  "ffi/native_type_vm_test.cc",
   "frontend/kernel_binary_flowgraph_test.cc",
   "frontend/multiple_entrypoints_test.cc",
   "write_barrier_elimination_test.cc",
diff --git a/runtime/vm/compiler/ffi/native_location.h b/runtime/vm/compiler/ffi/native_location.h
index 3437e5c..614798a 100644
--- a/runtime/vm/compiler/ffi/native_location.h
+++ b/runtime/vm/compiler/ffi/native_location.h
@@ -11,6 +11,7 @@
 
 #include "platform/assert.h"
 #include "vm/compiler/ffi/native_type.h"
+#include "vm/compiler/runtime_api.h"
 #include "vm/constants.h"
 #include "vm/growable_array.h"
 
@@ -24,10 +25,6 @@
 
 namespace compiler {
 
-namespace target {
-extern const int kWordSize;
-}
-
 namespace ffi {
 
 class NativeRegistersLocation;
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index db06400..20c33dd 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -14,6 +14,10 @@
 #include "vm/compiler/backend/locations.h"
 #endif  // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
 
+#if !defined(FFI_UNIT_TESTS)
+#include "vm/symbols.h"
+#endif
+
 namespace dart {
 
 namespace compiler {
@@ -25,6 +29,11 @@
   return static_cast<const NativePrimitiveType&>(*this);
 }
 
+const NativeCompoundType& NativeType::AsCompound() const {
+  ASSERT(IsCompound());
+  return static_cast<const NativeCompoundType&>(*this);
+}
+
 bool NativePrimitiveType::IsInt() const {
   switch (representation_) {
     case kInt8:
@@ -127,6 +136,57 @@
   }
 }
 
+static bool ContainsHomogenuousFloatsInternal(const NativeTypes& types);
+
+// Keep consistent with
+// pkg/vm/lib/transformations/ffi_definitions.dart:_calculateSizeAndOffsets.
+NativeCompoundType& NativeCompoundType::FromNativeTypes(
+    Zone* zone,
+    const NativeTypes& members) {
+  intptr_t offset = 0;
+
+  const intptr_t kAtLeast1ByteAligned = 1;
+  // If this struct is nested in another struct, it should be aligned to the
+  // largest alignment of its members.
+  intptr_t alignment_field = kAtLeast1ByteAligned;
+  // If this struct is passed on the stack, it should be aligned to the largest
+  // alignment of its members when passing those members on the stack.
+  intptr_t alignment_stack = kAtLeast1ByteAligned;
+#if defined(TARGET_OS_MACOS_IOS) && defined(TARGET_ARCH_ARM64)
+  // On iOS64 stack values can be less aligned than wordSize, which deviates
+  // from the arm64 ABI.
+  ASSERT(CallingConventions::kArgumentStackAlignment == kAlignedToValueSize);
+  // Because the arm64 ABI aligns primitives to word size on the stack, every
+  // struct will be automatically aligned to word size. iOS64 does not align
+  // the primitives to word size, so we set structs to align to word size for
+  // iOS64.
+  // However, homogenous structs are treated differently. They are aligned to
+  // their member alignment. (Which is 4 in case of a homogenous float).
+  // Source: manual testing.
+  if (!ContainsHomogenuousFloatsInternal(members)) {
+    alignment_stack = compiler::target::kWordSize;
+  }
+#endif
+
+  auto& member_offsets =
+      *new (zone) ZoneGrowableArray<intptr_t>(zone, members.length());
+  for (intptr_t i = 0; i < members.length(); i++) {
+    const NativeType& member = *members[i];
+    const intptr_t member_size = member.SizeInBytes();
+    const intptr_t member_align_field = member.AlignmentInBytesField();
+    const intptr_t member_align_stack = member.AlignmentInBytesStack();
+    offset = Utils::RoundUp(offset, member_align_field);
+    member_offsets.Add(offset);
+    offset += member_size;
+    alignment_field = Utils::Maximum(alignment_field, member_align_field);
+    alignment_stack = Utils::Maximum(alignment_stack, member_align_stack);
+  }
+  const intptr_t size = Utils::RoundUp(offset, alignment_field);
+
+  return *new (zone) NativeCompoundType(members, member_offsets, size,
+                                        alignment_field, alignment_stack);
+}
+
 #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
 bool NativePrimitiveType::IsExpressibleAsRepresentation() const {
   switch (representation_) {
@@ -139,7 +199,7 @@
     case kInt32:
     case kUint32:
     case kInt64:
-    case kUint64:
+    case kUint64:  // We don't actually have a kUnboxedUint64.
     case kFloat:
     case kDouble:
       return true;
@@ -179,6 +239,23 @@
   return other.AsPrimitive().representation_ == representation_;
 }
 
+bool NativeCompoundType::Equals(const NativeType& other) const {
+  if (!other.IsCompound()) {
+    return false;
+  }
+  const auto& other_compound = other.AsCompound();
+  const auto& other_members = other_compound.members_;
+  if (other_members.length() != members_.length()) {
+    return false;
+  }
+  for (intptr_t i = 0; i < members_.length(); i++) {
+    if (!members_[i]->Equals(*other_members[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
 static PrimitiveType split_fundamental(PrimitiveType in) {
   switch (in) {
     case kInt16:
@@ -243,16 +320,52 @@
   }
 }
 
+static bool IsPredefinedFfiCid(classid_t class_id) {
+  switch (class_id) {
+#define CASE_FFI_CID_TRUE(name)                                                \
+  case kFfi##name##Cid:                                                        \
+    return true;
+    CLASS_LIST_FFI(CASE_FFI_CID_TRUE)
+    default:
+      return false;
+  }
+  UNREACHABLE();
+}
+
 NativeType& NativeType::FromTypedDataClassId(Zone* zone, classid_t class_id) {
-  // TODO(36730): Support composites.
+  ASSERT(IsPredefinedFfiCid(class_id));
   const auto fundamental_rep = TypeRepresentation(class_id);
   return *new (zone) NativePrimitiveType(fundamental_rep);
 }
 
 #if !defined(FFI_UNIT_TESTS)
 NativeType& NativeType::FromAbstractType(Zone* zone, const AbstractType& type) {
-  // TODO(36730): Support composites.
-  return NativeType::FromTypedDataClassId(zone, type.type_class_id());
+  const classid_t class_id = type.type_class_id();
+  if (IsPredefinedFfiCid(class_id)) {
+    return NativeType::FromTypedDataClassId(zone, class_id);
+  }
+
+  // User-defined structs.
+  const auto& cls = Class::Handle(zone, type.type_class());
+
+  auto& options = Object::Handle(zone);
+  Library::FindPragma(dart::Thread::Current(), /*only_core=*/false, cls,
+                      Symbols::vm_ffi_struct_fields(), &options);
+  ASSERT(!options.IsNull());
+  ASSERT(options.IsArray());
+
+  const auto& field_types = Array::Cast(options);
+  auto& field_type = AbstractType::Handle(zone);
+  auto& field_native_types = *new (zone) ZoneGrowableArray<const NativeType*>(
+      zone, field_types.Length());
+  for (intptr_t i = 0; i < field_types.Length(); i++) {
+    field_type ^= field_types.At(i);
+    const NativeType& field_native_type =
+        NativeType::FromAbstractType(zone, field_type);
+    field_native_types.Add(&field_native_type);
+  }
+
+  return NativeCompoundType::FromNativeTypes(zone, field_native_types);
 }
 #endif
 
@@ -281,15 +394,15 @@
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
 
-const char* NativeType::ToCString(Zone* zone) const {
+const char* NativeType::ToCString(Zone* zone, bool multi_line) const {
   ZoneTextBuffer textBuffer(zone);
-  PrintTo(&textBuffer);
+  PrintTo(&textBuffer, multi_line);
   return textBuffer.buffer();
 }
 
 #if !defined(FFI_UNIT_TESTS)
-const char* NativeType::ToCString() const {
-  return ToCString(Thread::Current()->zone());
+const char* NativeType::ToCString(bool multi_line) const {
+  return ToCString(Thread::Current()->zone(), multi_line);
 }
 #endif
 
@@ -324,11 +437,11 @@
   }
 }
 
-void NativeType::PrintTo(BaseTextBuffer* f) const {
+void NativeType::PrintTo(BaseTextBuffer* f, bool multi_line) const {
   f->AddString("I");
 }
 
-void NativePrimitiveType::PrintTo(BaseTextBuffer* f) const {
+void NativePrimitiveType::PrintTo(BaseTextBuffer* f, bool multi_line) const {
   f->Printf("%s", PrimitiveTypeToCString(representation_));
 }
 
@@ -338,6 +451,35 @@
   return textBuffer.buffer();
 }
 
+void NativeCompoundType::PrintTo(BaseTextBuffer* f, bool multi_line) const {
+  f->AddString("Compound(");
+  f->Printf("size: %" Pd ", ", SizeInBytes());
+  f->Printf("field alignment: %" Pd ", ", AlignmentInBytesField());
+  f->Printf("stack alignment: %" Pd ", ", AlignmentInBytesStack());
+  f->AddString("members: {");
+  if (multi_line) {
+    f->AddString("\n  ");
+  }
+  for (intptr_t i = 0; i < members_.length(); i++) {
+    if (i > 0) {
+      if (multi_line) {
+        f->AddString(",\n  ");
+      } else {
+        f->AddString(", ");
+      }
+    }
+    f->Printf("%" Pd ": ", member_offsets_[i]);
+    members_[i]->PrintTo(f);
+  }
+  if (multi_line) {
+    f->AddString("\n");
+  }
+  f->AddString("})");
+  if (multi_line) {
+    f->AddString("\n");
+  }
+}
+
 #if !defined(FFI_UNIT_TESTS)
 const char* NativeFunctionType::ToCString() const {
   return ToCString(Thread::Current()->zone());
@@ -356,6 +498,88 @@
   return_type_.PrintTo(f);
 }
 
+bool NativeCompoundType::ContainsOnlyFloats(intptr_t offset_in_bytes,
+                                            intptr_t size_in_bytes) const {
+  ASSERT(size_in_bytes >= 0);
+  const intptr_t first_byte = offset_in_bytes;
+  const intptr_t last_byte = offset_in_bytes + size_in_bytes - 1;
+  for (intptr_t i = 0; i < members_.length(); i++) {
+    const intptr_t member_first_byte = member_offsets_[i];
+    const intptr_t member_last_byte =
+        member_first_byte + members_[i]->SizeInBytes() - 1;
+    if ((first_byte <= member_first_byte && member_first_byte <= last_byte) ||
+        (first_byte <= member_last_byte && member_last_byte <= last_byte)) {
+      if (members_[i]->IsPrimitive() && !members_[i]->IsFloat()) {
+        return false;
+      }
+      if (members_[i]->IsCompound()) {
+        const auto& nested = members_[i]->AsCompound();
+        const bool nested_only_floats = nested.ContainsOnlyFloats(
+            offset_in_bytes - member_first_byte, size_in_bytes);
+        if (!nested_only_floats) {
+          return false;
+        }
+      }
+    }
+    if (member_first_byte > last_byte) {
+      // None of the remaining members fits the range.
+      break;
+    }
+  }
+  return true;
+}
+
+intptr_t NativeCompoundType::NumberOfWordSizeChunksOnlyFloat() const {
+  // O(n^2) implementation, but only invoked for small structs.
+  ASSERT(SizeInBytes() <= 16);
+  const intptr_t size = SizeInBytes();
+  intptr_t float_only_chunks = 0;
+  for (intptr_t offset = 0; offset < size;
+       offset += compiler::target::kWordSize) {
+    if (ContainsOnlyFloats(
+            offset, Utils::Minimum<intptr_t>(size - offset,
+                                             compiler::target::kWordSize))) {
+      float_only_chunks++;
+    }
+  }
+  return float_only_chunks;
+}
+
+intptr_t NativeCompoundType::NumberOfWordSizeChunksNotOnlyFloat() const {
+  const intptr_t total_chunks =
+      Utils::RoundUp(SizeInBytes(), compiler::target::kWordSize) /
+      compiler::target::kWordSize;
+  return total_chunks - NumberOfWordSizeChunksOnlyFloat();
+}
+
+static void ContainsHomogenuousFloatsRecursive(const NativeTypes& types,
+                                               bool* only_float,
+                                               bool* only_double) {
+  for (intptr_t i = 0; i < types.length(); i++) {
+    const auto& member_type = types.At(i);
+    if (member_type->IsPrimitive()) {
+      PrimitiveType type = member_type->AsPrimitive().representation();
+      *only_float = *only_float && (type == kFloat);
+      *only_double = *only_double && (type == kDouble);
+    }
+    if (member_type->IsCompound()) {
+      ContainsHomogenuousFloatsRecursive(member_type->AsCompound().members(),
+                                         only_float, only_double);
+    }
+  }
+}
+
+static bool ContainsHomogenuousFloatsInternal(const NativeTypes& types) {
+  bool only_float = true;
+  bool only_double = true;
+  ContainsHomogenuousFloatsRecursive(types, &only_float, &only_double);
+  return (only_double || only_float) && types.length() > 0;
+}
+
+bool NativeCompoundType::ContainsHomogenuousFloats() const {
+  return ContainsHomogenuousFloatsInternal(this->members());
+}
+
 const NativeType& NativeType::WidenTo4Bytes(Zone* zone) const {
   if (IsInt() && SizeInBytes() <= 2) {
     if (IsSigned()) {
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 3c11341..eaf4129 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -26,6 +26,7 @@
 namespace ffi {
 
 class NativePrimitiveType;
+class NativeCompoundType;
 
 // NativeTypes are the types used in calling convention specifications:
 // integers, floats, and composites.
@@ -40,17 +41,15 @@
 //
 // Instead, NativeTypes support representations not supported in Dart's unboxed
 // Representations, such as:
-// * Primitive types:
+// * Primitive types (https://en.cppreference.com/w/cpp/language/types):
 //   * int8_t
 //   * int16_t
 //   * uint8_t
 //   * uint16t
 //   * void
-// * Compound types:
+// * Compound types (https://en.cppreference.com/w/cpp/language/type):
 //   * Struct
 //   * Union
-//
-// TODO(36730): Add composites.
 class NativeType : public ZoneAllocated {
  public:
 #if !defined(FFI_UNIT_TESTS)
@@ -65,6 +64,8 @@
 
   virtual bool IsPrimitive() const { return false; }
   const NativePrimitiveType& AsPrimitive() const;
+  virtual bool IsCompound() const { return false; }
+  const NativeCompoundType& AsCompound() const;
 
   virtual bool IsInt() const { return false; }
   virtual bool IsFloat() const { return false; }
@@ -106,10 +107,10 @@
   // Otherwise, return original representation.
   const NativeType& WidenTo4Bytes(Zone* zone) const;
 
-  virtual void PrintTo(BaseTextBuffer* f) const;
-  const char* ToCString(Zone* zone) const;
+  virtual void PrintTo(BaseTextBuffer* f, bool multi_line = false) const;
+  const char* ToCString(Zone* zone, bool multi_line = false) const;
 #if !defined(FFI_UNIT_TESTS)
-  const char* ToCString() const;
+  const char* ToCString(bool multi_line = false) const;
 #endif
 
   virtual ~NativeType() {}
@@ -166,7 +167,7 @@
   virtual bool Equals(const NativeType& other) const;
   virtual NativePrimitiveType& Split(Zone* zone, intptr_t part) const;
 
-  virtual void PrintTo(BaseTextBuffer* f) const;
+  virtual void PrintTo(BaseTextBuffer* f, bool multi_line = false) const;
 
   virtual ~NativePrimitiveType() {}
 
@@ -176,6 +177,72 @@
 
 using NativeTypes = ZoneGrowableArray<const NativeType*>;
 
+// Struct
+//
+// TODO(dartbug.com/38491): Support unions.
+// TODO(dartbug.com/37271): Support nested compound types.
+// TODO(dartbug.com/35763): Support inline fixed-length arrays.
+class NativeCompoundType : public NativeType {
+ public:
+  static NativeCompoundType& FromNativeTypes(Zone* zone,
+                                             const NativeTypes& members);
+
+  const NativeTypes& members() const { return members_; }
+  const ZoneGrowableArray<intptr_t>& member_offsets() const {
+    return member_offsets_;
+  }
+
+  virtual bool IsCompound() const { return true; }
+
+  virtual intptr_t SizeInBytes() const { return size_; }
+  virtual intptr_t AlignmentInBytesField() const { return alignment_field_; }
+  virtual intptr_t AlignmentInBytesStack() const { return alignment_stack_; }
+
+  virtual bool Equals(const NativeType& other) const;
+
+  virtual void PrintTo(BaseTextBuffer* f, bool multi_line = false) const;
+
+  // Whether a range within a struct contains only floats.
+  //
+  // Useful for determining whether struct is passed in FP registers on x64.
+  bool ContainsOnlyFloats(intptr_t offset_in_bytes,
+                          intptr_t size_in_bytes) const;
+
+  // Returns how many word-sized chuncks _only_ contain floats.
+  //
+  // Useful for determining whether struct is passed in FP registers on x64.
+  intptr_t NumberOfWordSizeChunksOnlyFloat() const;
+
+  // Returns how many word-sized chunks do not _only_ contain floats.
+  //
+  // Useful for determining whether struct is passed in FP registers on x64.
+  intptr_t NumberOfWordSizeChunksNotOnlyFloat() const;
+
+  // Whether this type has only same-size floating point members.
+  //
+  // Useful for determining whether struct is passed in FP registers in hardfp
+  // and arm64.
+  bool ContainsHomogenuousFloats() const;
+
+ private:
+  NativeCompoundType(const NativeTypes& members,
+                     const ZoneGrowableArray<intptr_t>& member_offsets,
+                     intptr_t size,
+                     intptr_t alignment_field,
+                     intptr_t alignment_stack)
+      : members_(members),
+        member_offsets_(member_offsets),
+        size_(size),
+        alignment_field_(alignment_field),
+        alignment_stack_(alignment_stack) {}
+
+  const NativeTypes& members_;
+  const ZoneGrowableArray<intptr_t>& member_offsets_;
+  const intptr_t size_;
+  const intptr_t alignment_field_;
+  const intptr_t alignment_stack_;
+};
+
 class NativeFunctionType : public ZoneAllocated {
  public:
   NativeFunctionType(const NativeTypes& argument_types,
diff --git a/runtime/vm/compiler/ffi/native_type_test.cc b/runtime/vm/compiler/ffi/native_type_test.cc
index abf275d..66fa987 100644
--- a/runtime/vm/compiler/ffi/native_type_test.cc
+++ b/runtime/vm/compiler/ffi/native_type_test.cc
@@ -4,12 +4,44 @@
 
 #include "vm/compiler/ffi/unit_test.h"
 
+#include "platform/syslog.h"
 #include "vm/compiler/ffi/native_type.h"
+#include "vm/compiler/runtime_api.h"
 
 namespace dart {
 namespace compiler {
 namespace ffi {
 
+const NativeCompoundType& RunStructTest(dart::Zone* zone,
+                                        const char* name,
+                                        const NativeTypes& member_types) {
+  const auto& struct_type =
+      NativeCompoundType::FromNativeTypes(zone, member_types);
+
+  const char* test_result = struct_type.ToCString(zone, /*multi_line=*/true);
+
+  const int kFilePathLength = 100;
+  char expectation_file_path[kFilePathLength];
+  Utils::SNPrint(expectation_file_path, kFilePathLength,
+                 "runtime/vm/compiler/ffi/unit_tests/%s/%s_%s.expect", name,
+                 kArch, kOs);
+
+  if (TestCaseBase::update_expectations) {
+    Syslog::Print("Updating %s\n", expectation_file_path);
+    WriteToFile(expectation_file_path, test_result);
+  }
+
+  char* expectation_file_contents = nullptr;
+  ReadFromFile(expectation_file_path, &expectation_file_contents);
+  EXPECT_NOTNULL(expectation_file_contents);
+  if (expectation_file_contents != nullptr) {
+    EXPECT_STREQ(expectation_file_contents, test_result);
+    free(expectation_file_contents);
+  }
+
+  return struct_type;
+}
+
 UNIT_TEST_CASE_WITH_ZONE(NativeType) {
   const auto& native_type = *new (Z) NativePrimitiveType(kInt8);
 
@@ -20,6 +52,107 @@
   EXPECT_STREQ("int8", native_type.ToCString(Z));
 }
 
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_int8x10) {
+  const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
+
+  auto& members = *new (Z) NativeTypes(Z, 10);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+  members.Add(&int8type);
+
+  const auto& struct_type = RunStructTest(Z, "struct_int8x10", members);
+
+  EXPECT(!struct_type.ContainsHomogenuousFloats());
+  EXPECT(!struct_type.ContainsOnlyFloats(0, 8));
+  EXPECT_EQ(0, struct_type.NumberOfWordSizeChunksOnlyFloat());
+  EXPECT_EQ(
+      Utils::RoundUp(struct_type.SizeInBytes(), compiler::target::kWordSize) /
+          compiler::target::kWordSize,
+      struct_type.NumberOfWordSizeChunksNotOnlyFloat());
+}
+
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_floatx4) {
+  const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
+
+  auto& members = *new (Z) NativeTypes(Z, 4);
+  members.Add(&float_type);
+  members.Add(&float_type);
+  members.Add(&float_type);
+  members.Add(&float_type);
+
+  const auto& struct_type = RunStructTest(Z, "struct_floatx4", members);
+
+  // This is a homogenous float in the arm and arm64 ABIs.
+  //
+  // On Arm64 iOS stack alignment of homogenous floats is not word size see
+  // runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_ios.expect.
+  EXPECT(struct_type.ContainsHomogenuousFloats());
+
+  // On x64, 8-byte parts of the chunks contain only floats and will be passed
+  // in FPU registers.
+  EXPECT(struct_type.ContainsOnlyFloats(0, 8));
+  EXPECT(struct_type.ContainsOnlyFloats(8, 8));
+  EXPECT_EQ(struct_type.SizeInBytes() / compiler::target::kWordSize,
+            struct_type.NumberOfWordSizeChunksOnlyFloat());
+  EXPECT_EQ(0, struct_type.NumberOfWordSizeChunksNotOnlyFloat());
+}
+
+// A struct designed to exercise all kinds of alignment rules.
+// Note that offset32A (System V ia32, iOS arm) aligns doubles on 4 bytes while
+// offset32B (Arm 32 bit and MSVC ia32) aligns on 8 bytes.
+// TODO(37271): Support nested structs.
+// TODO(37470): Add uncommon primitive data types when we want to support them.
+struct VeryLargeStruct {
+  //                             size32 size64 offset32A offset32B offset64
+  int8_t a;                   // 1              0         0         0
+  int16_t b;                  // 2              2         2         2
+  int32_t c;                  // 4              4         4         4
+  int64_t d;                  // 8              8         8         8
+  uint8_t e;                  // 1             16        16        16
+  uint16_t f;                 // 2             18        18        18
+  uint32_t g;                 // 4             20        20        20
+  uint64_t h;                 // 8             24        24        24
+  intptr_t i;                 // 4      8      32        32        32
+  double j;                   // 8             36        40        40
+  float k;                    // 4             44        48        48
+  VeryLargeStruct* parent;    // 4      8      48        52        56
+  intptr_t numChildren;       // 4      8      52        56        64
+  VeryLargeStruct* children;  // 4      8      56        60        72
+  int8_t smallLastField;      // 1             60        64        80
+                              // sizeof        64        72        88
+};
+
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_VeryLargeStruct) {
+  const auto& intptr_type = *new (Z) NativePrimitiveType(
+      compiler::target::kWordSize == 4 ? kInt32 : kInt64);
+
+  auto& members = *new (Z) NativeTypes(Z, 15);
+  members.Add(new (Z) NativePrimitiveType(kInt8));
+  members.Add(new (Z) NativePrimitiveType(kInt16));
+  members.Add(new (Z) NativePrimitiveType(kInt32));
+  members.Add(new (Z) NativePrimitiveType(kInt64));
+  members.Add(new (Z) NativePrimitiveType(kUint8));
+  members.Add(new (Z) NativePrimitiveType(kUint16));
+  members.Add(new (Z) NativePrimitiveType(kUint32));
+  members.Add(new (Z) NativePrimitiveType(kUint64));
+  members.Add(&intptr_type);
+  members.Add(new (Z) NativePrimitiveType(kDouble));
+  members.Add(new (Z) NativePrimitiveType(kFloat));
+  members.Add(&intptr_type);
+  members.Add(&intptr_type);
+  members.Add(&intptr_type);
+  members.Add(new (Z) NativePrimitiveType(kInt8));
+
+  RunStructTest(Z, "struct_VeryLargeStruct", members);
+}
+
 }  // namespace ffi
 }  // namespace compiler
 }  // namespace dart
diff --git a/runtime/vm/compiler/ffi/native_type_vm_test.cc b/runtime/vm/compiler/ffi/native_type_vm_test.cc
new file mode 100644
index 0000000..52f0835
--- /dev/null
+++ b/runtime/vm/compiler/ffi/native_type_vm_test.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "vm/compiler/ffi/native_type.h"
+
+#include "vm/compiler/backend/il_test_helper.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+namespace compiler {
+namespace ffi {
+
+ISOLATE_UNIT_TEST_CASE(Ffi_NativeType_Primitive_FromAbstractType) {
+  Zone* Z = thread->zone();
+
+  const auto& ffi_library = Library::Handle(Library::FfiLibrary());
+  const auto& int8_class = Class::Handle(GetClass(ffi_library, "Int8"));
+  const auto& int8_type = Type::Handle(int8_class.DeclarationType());
+  const auto& native_type = NativeType::FromAbstractType(Z, int8_type);
+
+  EXPECT_EQ(1, native_type.SizeInBytes());
+  EXPECT_STREQ("int8", native_type.ToCString());
+  EXPECT(native_type.IsInt());
+  EXPECT(native_type.IsPrimitive());
+}
+
+// Test that we construct `NativeType` correctly from `Type`.
+ISOLATE_UNIT_TEST_CASE(Ffi_NativeType_Struct_FromAbstractType) {
+  Zone* Z = thread->zone();
+
+  const char* kScript =
+      R"(
+      import 'dart:ffi';
+
+      class MyStruct extends Struct {
+        @Int8()
+        external int a0;
+
+        external Pointer<Int8> a1;
+      }
+      )";
+
+  const auto& root_library = Library::Handle(LoadTestScript(kScript));
+  const auto& struct_class = Class::Handle(GetClass(root_library, "MyStruct"));
+  const auto& struct_type = Type::Handle(struct_class.DeclarationType());
+
+  const auto& native_type =
+      NativeType::FromAbstractType(Z, struct_type).AsCompound();
+
+  EXPECT_EQ(2, native_type.members().length());
+
+  const auto& int8_type = *new (Z) NativePrimitiveType(kInt8);
+  EXPECT(int8_type.Equals(*native_type.members()[0]));
+
+  EXPECT_EQ(compiler::target::kWordSize,
+            native_type.members()[1]->SizeInBytes());
+}
+
+}  // namespace ffi
+}  // namespace compiler
+}  // namespace dart
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_android.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_android.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_ios.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_ios.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_linux.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_linux.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_macos.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm64_macos.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_android.expect
new file mode 100644
index 0000000..124e769
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_android.expect
@@ -0,0 +1,17 @@
+Compound(size: 72, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int32,
+  40: double,
+  48: float,
+  52: int32,
+  56: int32,
+  60: int32,
+  64: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_ios.expect
new file mode 100644
index 0000000..44136f8
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_ios.expect
@@ -0,0 +1,17 @@
+Compound(size: 64, field alignment: 4, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int32,
+  36: double,
+  44: float,
+  48: int32,
+  52: int32,
+  56: int32,
+  60: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_linux.expect
new file mode 100644
index 0000000..124e769
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/arm_linux.expect
@@ -0,0 +1,17 @@
+Compound(size: 72, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int32,
+  40: double,
+  48: float,
+  52: int32,
+  56: int32,
+  60: int32,
+  64: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_android.expect
new file mode 100644
index 0000000..24fa174
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_android.expect
@@ -0,0 +1,17 @@
+Compound(size: 64, field alignment: 4, stack alignment: 4, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int32,
+  36: double,
+  44: float,
+  48: int32,
+  52: int32,
+  56: int32,
+  60: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_linux.expect
new file mode 100644
index 0000000..24fa174
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_linux.expect
@@ -0,0 +1,17 @@
+Compound(size: 64, field alignment: 4, stack alignment: 4, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int32,
+  36: double,
+  44: float,
+  48: int32,
+  52: int32,
+  56: int32,
+  60: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_win.expect
new file mode 100644
index 0000000..e0bd22c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/ia32_win.expect
@@ -0,0 +1,17 @@
+Compound(size: 72, field alignment: 8, stack alignment: 4, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int32,
+  40: double,
+  48: float,
+  52: int32,
+  56: int32,
+  60: int32,
+  64: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_ios.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_ios.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_linux.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_linux.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_macos.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_macos.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_win.expect
new file mode 100644
index 0000000..ca69b6c
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_VeryLargeStruct/x64_win.expect
@@ -0,0 +1,17 @@
+Compound(size: 88, field alignment: 8, stack alignment: 8, members: {
+  0: int8,
+  2: int16,
+  4: int32,
+  8: int64,
+  16: uint8,
+  18: uint16,
+  20: uint32,
+  24: uint64,
+  32: int64,
+  40: double,
+  48: float,
+  56: int64,
+  64: int64,
+  72: int64,
+  80: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_android.expect
new file mode 100644
index 0000000..025609f
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_android.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 8, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_ios.expect
new file mode 100644
index 0000000..db47a46
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_ios.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 4, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_linux.expect
new file mode 100644
index 0000000..025609f
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_linux.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 8, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_macos.expect
new file mode 100644
index 0000000..025609f
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_macos.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 8, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_android.expect
new file mode 100644
index 0000000..db47a46
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_android.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 4, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_ios.expect
new file mode 100644
index 0000000..db47a46
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_ios.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 4, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_linux.expect
new file mode 100644
index 0000000..db47a46
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm_linux.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 4, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_android.expect
new file mode 100644
index 0000000..db47a46
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_android.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 4, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_linux.expect
new file mode 100644
index 0000000..db47a46
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_linux.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 4, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_win.expect
new file mode 100644
index 0000000..db47a46
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/ia32_win.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 4, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_ios.expect
new file mode 100644
index 0000000..025609f
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_ios.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 8, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_linux.expect
new file mode 100644
index 0000000..025609f
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_linux.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 8, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_macos.expect
new file mode 100644
index 0000000..025609f
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_macos.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 8, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_win.expect
new file mode 100644
index 0000000..025609f
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatx4/x64_win.expect
@@ -0,0 +1,6 @@
+Compound(size: 16, field alignment: 4, stack alignment: 8, members: {
+  0: float,
+  4: float,
+  8: float,
+  12: float
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_android.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_android.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_ios.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_ios.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_linux.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_linux.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_macos.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm64_macos.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_android.expect
new file mode 100644
index 0000000..c7c2467
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_android.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 4, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_ios.expect
new file mode 100644
index 0000000..c7c2467
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_ios.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 4, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_linux.expect
new file mode 100644
index 0000000..c7c2467
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/arm_linux.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 4, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_android.expect
new file mode 100644
index 0000000..c7c2467
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_android.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 4, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_linux.expect
new file mode 100644
index 0000000..c7c2467
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_linux.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 4, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_win.expect
new file mode 100644
index 0000000..c7c2467
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/ia32_win.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 4, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_ios.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_ios.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_linux.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_linux.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_macos.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_macos.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_win.expect
new file mode 100644
index 0000000..146893d
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8x10/x64_win.expect
@@ -0,0 +1,12 @@
+Compound(size: 10, field alignment: 1, stack alignment: 8, members: {
+  0: int8,
+  1: int8,
+  2: int8,
+  3: int8,
+  4: int8,
+  5: int8,
+  6: int8,
+  7: int8,
+  8: int8,
+  9: int8
+})
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index 1827e82..eba92da 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -285,7 +285,7 @@
       kAlignedToWordSize;
 
   // How fields in composites are aligned.
-#if defined(_WIN32)
+#if defined(TARGET_OS_WINDOWS)
   static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;
 #else
   static constexpr AlignmentStrategy kFieldAlignment =
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 0a40d7d..a1db78b 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -493,7 +493,8 @@
   V(vm_non_nullable_result_type, "vm:non-nullable-result-type")                \
   V(vm_recognized, "vm:recognized")                                            \
   V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn")            \
-  V(vm_procedure_attributes_metadata, "vm.procedure-attributes.metadata")
+  V(vm_procedure_attributes_metadata, "vm.procedure-attributes.metadata")      \
+  V(vm_ffi_struct_fields, "vm:ffi:struct-fields")
 
 // Contains a list of frequently used strings in a canonicalized form. This
 // list is kept in the vm_isolate in order to share the copy across isolates
diff --git a/tools/VERSION b/tools/VERSION
index 3545891..5a845c02 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 56
+PRERELEASE 57
 PRERELEASE_PATCH 0
\ No newline at end of file