-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Currently, for each mixin application front-end generates synthetic (anonymous) mixin application class.
Moreover, mixin_full_resolution
transformation (pkg/kernel/lib/transformations/mixin_full_resolution.dart) copies members from mixin to each mixin application class.
For example:
class B {}
class M {
void foo() => print('foo');
}
class A extends B with M {}
Kernel:
class B extends core::Object {
synthetic constructor •() → foo::B
: super core::Object::•()
;
}
class M extends core::Object {
synthetic constructor •() → foo::M
: super core::Object::•()
;
method foo() → void
return core::print("foo");
}
abstract class _A&B&M extends foo::B implements foo::M /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •() → foo::_A&B&M
: super foo::B::•()
;
method foo() → void
return core::print("foo");
}
class A extends foo::_A&B&M {
synthetic constructor •() → foo::A
: super foo::_A&B&M::•()
;
}
This results in a large number of extra synthetic classes which increase AOT snapshot size, memory footprint of an application and compilation time.
There are a few mitigations for this problem in Dart VM/AOT:
mixin_deduplication
transformation (pkg/vm/lib/transformations/mixin_deduplication.dart) which removes duplicate mixin application classes if sameB with M
mixin application is used multiple times. But it cannot eliminate duplication betweenB1 with M
andB2 with M
.- VM deduplicates methods with the same generated machine code, but that doesn't work if generated code is different, for example due to different object pool indices used for caches in distinct copies of mixin methods.
These optimizations help but there is still redundancy left even after all these deduplications.
Instead, we can redesign mixins in the VM, removing mixin application classes and copying of mixin methods entirely.
In such case, super calls will not be resolved at compile time. Instead, they will be dispatched at run time.
For example, we can treat super calls super.foo
from class C
similarly to interface calls, just with a special selector super:foo@C
. Actual classes will have targets for these super:
selectors in the dispatch table depending on their superclasses and mixins. Many of those super calls can be devirtualized and converted into direct calls (for example, if a class is not used as a mixin, or if mixin is always used with the same superclass).
Such implementation of mixins should result in a smaller AOT snapshot size, lower memory footprint and faster compilation, with the expense of slightly more expensive super calls. Hopefully the performance difference would be negligible.