Skip to content

[vm/ffi] Support treeshaking of FFI structs #38721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sjindel-google opened this issue Oct 4, 2019 · 17 comments
Closed

[vm/ffi] Support treeshaking of FFI structs #38721

sjindel-google opened this issue Oct 4, 2019 · 17 comments
Assignees
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi

Comments

@sjindel-google
Copy link
Contributor

sjindel-google commented Oct 4, 2019

Update 2021-03-10: All API changes are done.

Everything except struct constructors is shaken out. Struct constructors are still retained, because the FFI trampolines creating structs (in FFI call returns, or FFI callback arguments) is not hooked up to the TFA.

Update 2021-01-05: We should make the necessary API changes introducing an Allocator interface before Flutter 2.0, the actual implementation of tree-shaking can be done later.

===============================================================================

The way FFI structs are implemented involves adding entry-point annotations to members of all generated struct classes.

This prevents any FFI structs from being tree-shaken. We should fix this.

@sjindel-google sjindel-google added area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi labels Oct 4, 2019
@mkustermann
Copy link
Member

This problem is of a more general kind: The runtime might use some members of the Dart code based on some condition. In our specific case using MyStruct as a type in the program implies we might need to access MyStruct.sizeof and MyStruct.fromPointer in the runtime (and therefore need to retain it).

One way we could do this is to support conditional entry points:

class MyStruct extends Struct<MyStruct> {
  @prama("vm:entry-point-if", EntryPointCondition.ifUsedAsType(MyStruct))
  static final field int #sizeOf = ...;

  @prama("vm:entry-point-if", EntryPointCondition.ifUsedAsType(MyStruct))
  constructor fromPointer(dynamic )
  ...
}

TFA as well as the VM's AOT compiler have a notion of a class being retained for it's type. We could build a mapping and every time a class is retained for its type we can enqueue conditional entry points.

@alexmarkov @dcharkes

@dcharkes dcharkes self-assigned this Oct 24, 2019
@dcharkes
Copy link
Contributor

Addressing #38648, together with disallowing .ref invocations on Pointer<Struct> would remove the need for a pragma on the constructor.

However, we still need it on #sizeOf. Which we can address with @mkustermann's suggestion.

We could keep the unoptimized .ref calls on Pointer<Struct> as well if we implement that solution anyway.

@alexmarkov
Copy link
Contributor

@mkustermann I think it would be hard to support entry points which depend on the outcome of tree shaking due to circular nature of such entry points. We need to take entry points into account during type flow analysis, while notion of classes retained for types exists only at later tree shaking step which happens after analysis is finished.

For conditional entry points we would need to get back to analysis and probably iterate between analysis and tree shaking until reaching fixed point:

  1. Entry points =>
  2. Type flow analysis =>
  3. Tree shaking pass 1 =>
  4. Set of retained classes/types/etc =>
  5. Refined set of entry points, need to go back to step 1.

@dcharkes Could you elaborate why we need these static #sizeOf fields at the first place? Can they be calculated at compile time and their result passed to corresponding native methods, so native methods won't need to access these fields?

If it's not possible, can we convert these static #sizeOf fields to an instance method? In such case it would be executed only if an instance of a class is created. Then, we can simply add entry point pragma on that method in base class Struct and it would be tree shaken if no instances are created, and included only for those classes which were instantiated.

At last, we can have a custom logic in NativeCodeOracle (it could be triggered by pragma annotations or it could be entirely customized). That logic would do something special to model what happens when certain native methods are detected as called by the analysis. If #sizeOf field is used by certain native methods, NativeCodeOracle could model calls to getters of #sizeOf for certain set of classes (are they passed as type arguments to those native methods?).

@dcharkes
Copy link
Contributor

@alexmarkov we use it for being able to call sizeOf<T extends NativeType>(). They cannot be instance methods, because we need to know the size before we create objects. For example in allocate<T extends NativeType>() (source).

The sizeOf is mostly statically computed. The size depends on the ABI, and kernel is ABI agnostic, so it's #sizeOf => [size1, size2, size3][_abi()]; Maybe we can have a better way of passing on Map<Tuple<Type,int>,int> structSizes from the CFE to the VM. Though even if we use a different data structure, the question is also how to prune those data structures.

are they passed as type arguments to those native methods?

Yes, but sometimes generically, as in allocate<T> { sizeOf<T>() ... }.

It is also quite common that the only way objects are created in code using dart:ffi is through natives:

class Foo extends Struct { ... }

T allocateStruct<T extends Struct>(){
  Pointer<T> pointer = allocate<T>();
  T t = pointer.ref; // native entry of `.ref` manufactures an object, it calls the hidden constructor.
  return t;
}

main() {
  Foo f = allocateStruct<Foo>();
  print(f);
}

We would need to disallow generically calling .ref as suggested above, to be able to tree shake.

However, when adding structs by value (#36730) we would introduce the same problem again, because then the return type of a native function could be that struct.

Currently, already Pointer can be returned from a trampoline. So we had to prevent Pointer from being tree shaken to prevent the VM from crashing in that case.

But this seems to be a general pattern, that the FFI can create objects without the constructors in the user code.

@alexmarkov
Copy link
Contributor

Can we require type argument of sizeOf<T extends NativeType>() be known at compile time? Then, we can replace sizeOf<Foo>() calls with Foo.#sizeOf in the FFI transformation, which would make reference to Foo.#sizeOf expressed in Dart code and such reference would be handled well by tree shaker without extra pragmas.

Similarly, if we can require T in allocate<T>() to be a type known at compile time, then we can replace allocate<Foo>() calls with a pattern of code which would create an instance of Foo in Dart (e.g. new Foo.#hidden), calling native method only to allocate raw memory.

Generally speaking, an ability to create instances of arbitrary classes in native methods is not working well with tree shaking. This is one of the reasons why dart:mirrors are not compatible with closed-world AOT compilation / tree shaking. To work around this, we can come up with some approximations, like "include all classes which extend Struct" (used currently), or "include all classes which extend Struct and mentioned in types in reachable code" (what @mkustermann suggested). But these are still just conservative approximations. If possible, I would suggest to avoid creation of instances of arbitrary classes in native methods.

@dcharkes
Copy link
Contributor

allocate is a user-defined function. If we require the T in sizeOf<T> to be known statically, the signature would change. This would be less ergonomic.

However, it would also properly make reasoning about size orthogonal to allocating the memory.

@mkustermann and I discussed a design a while back that would separate allocation and sizing:

class FooStruct extends Struct {
  // Struct fields.

  Pointer<FooStruct> allocate(Allocator a, int count) {
    a.allocate(sizeOf<FooStruct>() * count).cast().
  }
}

In this design allocate has no type argument at all, it always returns Pointer<Void> to the amount of bytes.

@ds84182
Copy link
Contributor

ds84182 commented Jan 2, 2020

@dcharkes

I like the Struct.allocate approach, since it resembles C++ allocators (and somewhat resembles C++ placement new). Another good example is how Rust handles this with the Alloc trait and Layouts: https://doc.rust-lang.org/alloc/alloc/trait.Alloc.html

Essentially your API surface for allocation becomes:

// Represents how much memory to allocate, and the alignment requirements.
// Abstracts over computing things like repeating size (for arrays).
class Layout {
  final int size, alignment;
  const Layout(this.size, this.alignment);
  // Assuming alignment is a power of 2.
  // Equivalent to alloc::Layout::pad_to_align() in Rust
  int get alignedSize => (size + (alignment - 1)) & ~(alignment - 1);
  // Equivalent to alloc::Layout::repeat() in Rust
  Layout repeat(int count) => Layout(alignedSize * count, alignment);
}

abstract class Allocator {
  // Equivalent to alloc::Alloc::alloc in Rust
  Pointer<Uint8> allocate(Layout layout);
  // Equivalent to alloc::Alloc::dealloc in Rust
  void free(Pointer<Uint8> ptr, Layout layout);
}

// Specialized to a constant T.#layout if T is statically known.
Layout layoutOf<T extends Struct>() => ...;

class MyStruct extends Struct {
  // Generated static method
  Pointer<MyStruct> allocate(Allocator alloc, int count) => alloc.allocate(layoutOf<MyStruct>().repeat(count)).cast();
}

Then package:ffi can expose:

class _Malloc extends Allocator { ... }

// Can also be final
const Allocator malloc = _Malloc();

And allocation becomes:

MyStruct.allocate(malloc); // (with or without optional count)

You can also introduce the concept of global allocators, but I feel that explicit allocators are a cleaner design. There are many situations where you'd want to avoid allocating from heap (like a bump allocation scheme for temporary objects, or the stack) and having the concept of allocators in dart:ffi gives a pretty nice starting point for library authors to jump off of.

@dcharkes
Copy link
Contributor

dcharkes commented Jan 3, 2020

That's an excellent design. It would also work well with the Pool resource management code samples/ffi/resource_management.

We still have to refine the design to support that.

Option 1: Merge Allocator and ResourceManager

We could split up the Allocator to not have free if it's managed:

abstract class Allocator {
  // Equivalent to alloc::Alloc::alloc in Rust
  Pointer<Uint8> allocate(Layout layout);
}

abstract class UnmanagedAllocator extends Allocator {
  // Equivalent to alloc::Alloc::dealloc in Rust
  void free(Pointer<Uint8> ptr, Layout layout);
}

abstract class ManagedAllocator extends Allocator {
  // ..
}

However, this conflates allocating/freeing with resource management in the same class hierarchy. One could have a malloc Allocator, but also another one such as OPENSSL_malloc and want to use both with a Pool or with manual management. That would create 4 implementations in this situation.

Pro:

  • simple
  • concise

Con:

  • No separation of concerns.

Option 2: Make the ResourceManager take an Allocator as argument

Alternatively, we should make the ResourceManager a separate class that takes an Allocator as argument.

Pro:

  • Better separation of concerns.

Cons:

  • More verbose, instantiating 2 classes.
  • Should the MyStruct.allocate then take a ResourceManager as argument?

@ds84182
Copy link
Contributor

ds84182 commented Jan 3, 2020

I think it's perfectly fine to have a single Allocator class with allocate and free. At the end of the day, free is just a hint to the allocator that the piece of memory referenced by ptr isn't going to be used by the application anymore. If free does nothing, the application will continue to function, but it may run out of memory after a while.

For example, this bump allocation library https://crates.io/crates/bump_alloc, which does nothing on free. https://github.com/sheredom/bump_alloc/blob/master/src/lib.rs#L106

As far as Struct is concerned, it just needs something that is "layout in, pointer out". Other usages of Allocator (like a resizable vector) need a free, and maybe even a reallocate. Pool can totally extend Allocator, it just has to have an empty implementation of free.

@jonasfj
Copy link
Member

jonasfj commented Jan 3, 2020

Quick note: I've found Pool from samples/ffi/resource_management to be very useful. It might be interesting to give it a pool.move(ptr) -> ptr method that move a pointer outside the Pool.

This have been useful for me when I needed something like:

try {
  ptr = pool.allocate();
  ...;
  return pool.move(ptr); // remove ptr from pool
} finally {
  pool.releaseAll();
}

This avoid having to write } catch (_) { free(ptr); rethrow; }

@dcharkes dcharkes added this to the January 2021 milestone Dec 16, 2020
dart-bot pushed a commit that referenced this issue Jan 5, 2021
Now that we have nested structs, objects a subtype of `Struct` can be
backed by either a `Pointer` or a `TypedData`. Having this accessor is
misleading.

Instead of passing a struct around (which could be backed by either),
the `Pointer<T extends Struct>` should be passed around and `.ref`
should be used everywhere when access to the backing pointer is
required.

Issue: #40667

Related issues:
* Optimize .ref #38648
* Support tree shaking of structs #38721

Change-Id: I3c73423480b91c00639df886bf1d6ac2e444beab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177581
Reviewed-by: Martin Kustermann <[email protected]>
Commit-Queue: Daco Harkes <[email protected]>
@dcharkes
Copy link
Contributor

dcharkes commented Jan 5, 2021

As discussed in today's meeting we should separate allocation from sizeof calculation (similar to C code) for code size / tree shake reasons and performance reasons. We could use an API such as

// In "dart:ffi"
abstract class Allocator {
  Pointer<Void> allocate(int numBytes);
  void free(Pointer<Void> pointer);
}

// In "dart:ffi"
extern Pointer<T> allocate<T extends Struct>(Allocator allocator);

// In "package:ffi/ffi.dart"
class MallocAllocator extends Allocator { ... }
class ZoneAllocator extends Allocator { ... }

final malloc = MallocAllocator();

// User code:
void main() {
  Pointer<Foo> foop = allocate<Foo>(malloc);
  ...
  free<Foo>(foop, malloc);
}
// User code (lowered to kernel):
void main() {
  Pointer<Foo> foop = malloc.allocate(sizeOf<Foo>() /* <-- or rather it's lowered form */).cast<Foo>();
  ...
  malloc.free(foop.cast<Void>());
}

So we'll

  • Make CL to demonstrate these changes
  • Make breaking change request with this information and motivation for it (treeshake-ability, performance)
  • Discuss API in next meeting
  • Possible make old API deprecated before Flutter 2.0 / FFI 1.0

Originally posted by @mkustermann in #44454 (comment)

@ds84182
Copy link
Contributor

ds84182 commented Jan 7, 2021

It's also useful to pass alignment into the allocator. Right now, if you want to write a proper bump pointer allocator, small types (for ex, Uint8) will have size overhead of the largest supported type alignment (8 bytes). Additionally a type like Float32x4 or Int32x4 would have different alignment requirements due to SIMD loads and stores.

@dcharkes
Copy link
Contributor

dcharkes commented Jan 7, 2021

It's also useful to pass alignment into the allocator. Right now, if you want to write a proper bump pointer allocator, small types (for ex, Uint8) will have size overhead of the largest supported type alignment (8 bytes). Additionally a type like Float32x4 or Int32x4 would have different alignment requirements due to SIMD loads and stores.

Then we would 'invoke' both sizeOf<T>() and alignmentOf<T>() (the latter one we still have to introduce #39964) in allocate.

If we would omit an alignment argument now, we would break all existing subtypes of Allocator later when we introduce it. So we should add it to the API.

@mkustermann
Copy link
Member

To make this forwards compatible, we might want to make the Allocator class take the alignment:

abstract class Allocator {
  Pointer<Void> allocate(int numBytes, int alignment);
  void free(Pointer<Void> pointer);
}

@franklinyow franklinyow added the P1 A high priority bug; for example, a single project is unusable or has many test failures label Jan 12, 2021
dart-bot pushed a commit that referenced this issue Jan 13, 2021
Introduces the Allocator API in `dart:ffi`.

This CL does not yet roll `package:ffi` to use `Allocator`, because that
breaks the checked in Dart in Fluter in g3. Instead, this coppies
`_MallocAllocator` from `package:ffi` into the ffi tests for testing.

This CL does not yet migrate off `allocate` and `free` in the SDK. That
is done in a dependent CL.

Issue: #44621
Issue: #38721

TEST=tests/ffi/allocator_test.dart
TEST=tests/ffi/calloc_test.dart
TEST=tests/ffi/vmspecific_static_checks_test.dart

Change-Id: I173e213a750b8b3f594bb8d4fc72575f2b6b91f7
Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,analyzer-nnbd-linux-release-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,benchmark-linux-try,dart-sdk-linux-try,pkg-linux-release-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-win-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177705
Reviewed-by: Clement Skau <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 14, 2021
New API landed in: https://dart-review.googlesource.com/c/sdk/+/177705

Issue: #44621
Issue: #38721

Change-Id: Id45274313edbb3842438b66b9c0917a86884c8ed
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178993
Reviewed-by: Aske Simon Christensen <[email protected]>
Commit-Queue: Daco Harkes <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 14, 2021
This CL does not yet roll `package:ffi` to use `Allocator`, because that
breaks the checked in Dart in Flutter in g3. Instead, this uses the copy
of `_CallocAllocator` from `package:ffi` in `calloc.dart` in tests/ffi.

New API landed in: https://dart-review.googlesource.com/c/sdk/+/177705

Issue: #44621
Issue: #38721

Change-Id: Iedfc4a11d4606915a324c824372bca643016f5a3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178994
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 14, 2021
This CL does not yet roll `package:ffi` to use `Allocator`, because that
breaks the checked in Dart in Flutter in g3. Instead, this copies
`_CallocAllocator` from `package:ffi` into the benchmarks. (We need a
copy per benchmark such that file-copying before running benchmarks
works properly.) The copies can be deleted when we can update
`package:ffi` in the DEPS file to contain `_CallocAllocator`.

New API landed in: https://dart-review.googlesource.com/c/sdk/+/177705

Issue: #44621
Issue: #38721

Change-Id: I546de7ec65ceb6f05644a5f269b83f64656892e5
Cq-Include-Trybots: luci.dart.try:benchmark-linux-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178995
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 14, 2021
This CL does not yet roll `package:ffi` to use `Allocator`, because that
breaks the checked in Dart in Flutter in g3. Instead, this uses the copy
of `_CallocAllocator` from `package:ffi` in `calloc.dart`.

New API landed in: https://dart-review.googlesource.com/c/sdk/+/177705

Issue: #44621
Issue: #38721

Change-Id: I10e53a363cd87c4c11e7042989e6957b82ae2a09
Cq-Include-Trybots: luci.dart.try:vm-kernel-win-debug-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-mac-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178992
Reviewed-by: Aske Simon Christensen <[email protected]>
Commit-Queue: Daco Harkes <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 14, 2021
This CL does not yet roll `package:ffi` to use `Allocator`, because that
breaks the checked in Dart in Flutter in g3. Instead, this copies
`_CallocAllocator` from `package:ffi` into the samples.

New API landed in: https://dart-review.googlesource.com/c/sdk/+/177705

Issue: #44621
Issue: #38721

Change-Id: I83da349c2e52d7f079aa1569b4726318fee24c9d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177706
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 20, 2021
This can only be landed when `Allocator` and `Opaque` have rolled into
Flutter/engine, that into Flutter/flutter, and that into g3.
flutter/flutter/commit/a706cd211240f27be3b61f06d70f958c7a4156fe

Deletes all the copies of `_CallocAllocator` and uses the one from
`package:ffi` instead.

Issue: #44622
Issue: #43974
Issue: #44621
Issue: #38721

Change-Id: I50b3b4c31a2b839b35e3e057bd54f463b90bc55e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179540
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 20, 2021
This CL changes the semantics of `Pointer<T extends Struct>.ref` to use
the compile-time `T` rather than the runtime `T`.

This enables tree shaking of subtypes of Struct and optimizing `.ref`.

Issue: #38721

TEST=tests/ffi/vmspecific_static_checks_test.dart
TEST=tests/ffi/*struct*test_.dart

Change-Id: I3f5b08c08ec0799ef8aab3c4177e2ac70d25501c
Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,analyzer-nnbd-linux-release-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,benchmark-linux-try,dart-sdk-linux-try,pkg-linux-release-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177862
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 22, 2021
The analyzer and CFE now report a warning on calls to
`Pointer<T extends Struct>.ref` and `Pointer<T extends Struct>.[]`
where `T` is a generic.

Adapted from https://dart-review.googlesource.com/c/sdk/+/180190 to
only deprecate but not error out on generic calls to `.ref` and `[]`.

Issue: #38721

TEST=tests/ffi/vmspecific_static_checks_test.dart

Change-Id: I81353089d59f093730d63792e9dbcd0b2ff0c432
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180365
Reviewed-by: Vyacheslav Egorov <[email protected]>
dart-bot pushed a commit that referenced this issue Jan 22, 2021
The analyzer and CFE now report a warning on calls to
`Pointer<T extends NativeType>.elementAt()` and
`sizeOf<T extends NativeType>()` where `T` is a generic.

Adapted from https://dart-review.googlesource.com/c/sdk/+/178200 to
only deprecate but not error out on generic calls.

Does not roll forward `package:ffi` to a version with the `Allocator`
but keeps the generic `sizeOf` invocation. This causes extra warnings
in the pkg/front_end testcases.

Issue: #38721

TEST=tests/ffi/data_test.dart
TEST=tests/ffi/sizeof_test.dart
TEST=tests/ffi/structs_test.dart
TEST=tests/ffi/vmspecific_static_checks_test.dart

Change-Id: I8f41c4dc04fc44e7e6c540ba87a3f41604130fe9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180560
Reviewed-by: Vyacheslav Egorov <[email protected]>
@dcharkes
Copy link
Contributor

Landing the breaking change (#44621) is part of the milestone, not this. Removing milestone.

@dcharkes dcharkes removed this from the January Beta Release (2.12) milestone Jan 22, 2021
@dcharkes dcharkes removed the P1 A high priority bug; for example, a single project is unusable or has many test failures label Jan 22, 2021
@dcharkes dcharkes added this to the February Beta Release milestone Jan 22, 2021
dart-bot pushed a commit that referenced this issue Feb 2, 2021
This rewrites `sizeOf` calls in the CFE to skip the runtime entry when
the type argument is constant.

The runtime entry is still used when the type argument is generic.
Forcing the type argument to be constant and removing the runtime entry
will be done in follow up CLs.

Bug: #44621
Bug: #38721

TEST=tests/ffi/sizeof_test.dart

Change-Id: I17d14432e6ab22810729be6b5c2939a033d382c5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182262
Reviewed-by: Clement Skau <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 2, 2021
This rewrites `elementAt` calls in the CFE to skip the runtime entry
for `sizeOf` when the type argument is constant.

The runtime entry is still used when the type argument is generic.
Forcing the type argument to be constant and removing the runtime entry
will be done in follow up CLs.

Bug: #44621
Bug: #38721

TEST=tests/ffi/data_test.dart

Change-Id: I480db43e7c115c24bd45f0ddab0cfea7eb8cfa58
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182263
Reviewed-by: Clement Skau <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 2, 2021
This rewrites `Allocator.call` calls in the CFE to skip the runtime
entry for `sizeOf` when the type argument is constant.

The runtime entry is still used when the type argument is generic.
Forcing the type argument to be constant and removing the runtime entry
will be done in follow up CLs.

Bug: #44621
Bug: #38721

TEST=test/ffi (almost all of them)

Change-Id: I5e855fa2b63a5c1b7fa70dbaa1b89c122a82da6e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182264
Reviewed-by: Clement Skau <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 2, 2021
This rewrites `Pointer<Struct>.ref` and `Pointer<Struct>[]` calls in
the CFE to skip the runtime entry when the type argument is constant.

The runtime entry is still used when the type argument is generic.
Forcing the type argument to be constant and removing the runtime entry
will be done in follow up CLs.

Bug: #38721
Bug: #44621

Removing the runtime entry speeds up `.ref` significantly.
before: FfiStruct.FieldLoadStore(RunTime): 18868.140186915887 us.
after: FfiStruct.FieldLoadStore(RunTime): 270.5877976190476 us.
Measurements from Linux x64 in JIT mode.

Closes: #38648

TEST=tests/ffi/structs_test.dart

Change-Id: I82abd930b5a9c5c78a8999c2bc49802d67d37534
Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try,analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,dart-sdk-linux-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182265
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Clement Skau <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 3, 2021
… type"

This CL changes the semantics of `Pointer<T extends Struct>.ref` to use
the compile-time `T` rather than the runtime `T`.

This enables tree shaking of subtypes of Struct and optimizing `.ref`.

Bug: #38721

TEST=tests/ffi/vmspecific_static_checks_test.dart
TEST=tests/ffi/*struct*test_.dart

Change-Id: Ie19bc3259d1cb721d0ce56d68e82d09dc3a4ad0e
Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try,analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,dart-sdk-linux-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180190
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 4, 2021
After https://dart-review.googlesource.com/c/sdk/+/180190 the runtime
entry has become dead code.

This CL keeps the runtime entry itself but makes it unreachable as was
the suggestion on previous a CL removing runtime entries:
https://dart-review.googlesource.com/c/sdk/+/169406

Bug: #38648
Bug: #38721

TEST=tests/ffi/vmspecific_static_checks_test.dart
TEST=tests/ffi/*struct*test_.dart

Change-Id: I84c5c925215b9dbd999826fb390df91d8050e1dd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182627
Reviewed-by: Aske Simon Christensen <[email protected]>
Commit-Queue: Daco Harkes <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 10, 2021
This can only be landed when `Allocator`, `Opaque`, and `AllocatorAlloc`
have rolled into Flutter/engine, that into Flutter/flutter, and into g3.

Deletes all the copies of `_CallocAllocator` and uses the one from
`package:ffi` instead.

Bug: #44622
Bug: #43974
Bug: #44621
Bug: #38721

Change-Id: I486034b379b5a63cad4aefd503ccd0f2ce5dd45e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180188
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 17, 2021
This CL changes the semantics of
`Pointer<T extends NativeType>.elementAt` and
`sizeOf<T extends NativeType>` to use the compile-time `T` rather than
the runtime `T`.

Issue: #38721

TEST=tests/ffi/data_test.dart
TEST=tests/ffi/sizeof_test.dart
TEST=tests/ffi/structs_test.dart
TEST=tests/ffi/vmspecific_static_checks_test.dart

Change-Id: Ifb25a4bd66d50a385d3db6dec9213b96dff21722
Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,analyzer-nnbd-linux-release-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,benchmark-linux-try,dart-sdk-linux-try,pkg-linux-release-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178200
Reviewed-by: Aske Simon Christensen <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 17, 2021
After changing `elementAt` and `sizeOf` to only accept static types
(https://dart-review.googlesource.com/c/sdk/+/178200) and rewrite all
the calls with static types in the CFE
(https://dart-review.googlesource.com/c/sdk/+/182262), the runtime entry
can now be removed.

One less place where the runtime relies on `Struct` subtypes not being
tree shaken.
Issue: #38721

Because we're no longer using `NativeType`s in a RTE to calculate their
size, these do no longer need to be available in the precompiled
runtime.
Closes: #42809

TEST=tests/ffi(_2)/*

Change-Id: I8682a3bb4d2dc3ee71531cf71909e47489e96f12
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-ffi-android-debug-arm64-try,vm-ffi-android-debug-arm-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/185085
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Clement Skau <[email protected]>
dart-bot pushed a commit that referenced this issue Feb 18, 2021
Removes the entry-point on the `#sizeOf` field of `Struct` subtypes,
because we are no longer using it in the runtime.

Does not remove the entry-point from the constructor yet, because FFI
trampolines can instantiate these objects. Updated the TODOs to reflect
this.

Bug: #38721

TEST=tests/ffi(_2)/* in precompiled mode.

Change-Id: If3889e782b8fe34ef1c34cf8af83da041b4d2ef5
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/185540
Reviewed-by: Aske Simon Christensen <[email protected]>
Commit-Queue: Daco Harkes <[email protected]>
@dcharkes
Copy link
Contributor

Update: Everything except struct constructors is shaken out. Struct constructors are still retained, because the FFI trampolines creating structs (in FFI call returns, or FFI callback arguments) is not hooked up to the TFA yet.

@a-siva
Copy link
Contributor

a-siva commented Mar 22, 2021

Per discussion in the VM sync meeting this morning this last piece is not critical for the March milestone and hence removing the milestone tag.

@a-siva a-siva removed this from the March Beta Release milestone Mar 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi
Projects
None yet
Development

No branches or pull requests

8 participants