Repository navigation
[vm] Prepare mixins for zero-cost use cases. #64235
Description
This issue proposes two new pragmas so we can more reliably rely on mixin monomorphization.
Vectorization use cases suffer from a lot of duplication, mainly:
- Iteration over a particular block size (e.g., do we unroll the SIMD loop by 1, 4, 8 or 16?)
- The strategy that is used over a particular block size (e.g., do we test for equality or non-equality? Do we use &, | or ^?)
This seems like a small issue, but consider that we can, at the very least, very easily vectorize the following (once all the APIs exist, see: #64170):
- {
Int32,Uint32,Int64,Int16,Uint16,Int8,Uint8,Uint8Clamped}List.indexOf - {
Int32,Uint32,Int64,Int16,Uint16,Int8,Uint8,Uint8Clamped}List.contains - {
Int32,Uint32,Int64,Int16,Uint16,Int8,Uint8,Uint8Clamped}List.lastIndexOf
A correct 16x unrolled iteration is difficult to understand at first glance, so being able to reuse it across dozens of implementations would be very helpful.
Traditional abstraction approaches (e.g., abstract classes with an interface, passing closures around) don't reliably guarantee monomorphization and prevent inlining from running, which is critical here as we can't afford to pay for the abstraction. Furthermore, they don't support composition, that is, we can't easily implement and decouple the strategy from the block size. This is where we can use mixins.
Mixins are already implemented in a monomorph fashion where their application copies the contents of the mixin over. However this is not happening reliably. Also, implemented abstract mixin members are not inlined by default. Implementations need to provide a prefer inline pragma for them to be inlined.
To solve those problems I propose the following two pragmas:
vm:prefer-monomorph-mixinvm:prefer-inline-overrides
vm:prefer-monomorph-mixin
While mixins are already implemented in a way where they are monomorphized, this has apparently lead to code-size issues where a kernel transformation was introduced that deduplicates mixin applications. The purpose of this pragma is to opt out of that mixin deduplication process.
vm:prefer-inline was not chosen because it is already heavily overloaded. Note that this new pragma explicitly doesn't mention the mixin deduplication, because the intent is that future kernel transformers or whatever should attempt to honor this mixin if they could prevent monomorphization from happening.
vm:prefer-inline-overrides
Strategy specializations would still need to annotate their abstract implementations with @pragma("vm:prefer-inline"). This is tedious and an interface-level policy that doesn't belong in an implementation. To remove that, an abstract member annotated with the vm:prefer-inline-overrides pragma opts that member in to a new kernel transformation that propagates vm:prefer-inline to all its implementations.
vm:prefer-inline was explicitly rejected for this purpose because existing code might already use that pragma on abstract members, and we don't want to mess with those.
Concretely, here's what it would look like:
@pragma('vm:prefer-monomorph-mixin')
mixin K {
@pragma('vm:prefer-inline-overrides')
int probe(int x);
int use(int x) => probe(x) + probe(x + 1);
}
class A with K {
@override
int probe(int x) => x + 1;
}
class B with K {
@override
int probe(int x) => x * 2;
}@mkustermann raised some concerns in reviews where I introduced mixins to the typed_data library. It turned out not to be an issue today, because the mixin_deduplication transformer uses simple heuristics for deduplication, but it is fragile and future changes to that transformer or the typed_data library could lead to regressions. These pragmas would help there as well.
cc @mraleph @lrhn @mkustermann @rakudrama thoughts?
Activity
jrdevadattan commentedon Sep 17, 2026
I can help with a small first slice of this VM/compiler design work. The issue is really about making reusable vectorization strategies monomorphic enough for inlining, so I would begin by documenting the exact abstraction pattern needed for the typed-data
indexOf/contains/lastIndexOfcases and then prototype one pragma path against a minimal benchmark. I would keep the first change focused on proving the compiler can specialize the mixin-style structure without paying closure/interface dispatch costs.modulovalue commentedon Sep 17, 2026
Thanks @jrdevadattan! I'm already on it. No need for help here, but I appreciate it!
jrdevadattan commentedon Sep 18, 2026
Thanks for the heads-up, @modulovalue. I’ll stay out of your way on this one, and I appreciate you clarifying.
lrhn commentedon Oct 14, 2026
This annotation would be better placed on the mixin application, not the declaration.
The same mixin may be used in different places with different requirements.
Also, I'd prefer that the annotation is only respected if it occurs in platform code, otherwise I fear users will start sprinkling it around like fairy-dust "for speed", and end up costing themselves more in size increase.
If there are multiple mixin applications in the same class declaration, and they don't all need to be specialized, then
you can always introduce private mixin applications:
would be
modulovalue commentedon Oct 14, 2026
I suspect you come from the typed_data mixin CL where your point (to prefer a use site annotation) definitely can make sense.
Personally I feel like inlining/expanding/monomorphization should be able to be an interface level decision because it has a big effect on performance (and the main purpose of those mixins is performance), in other words, I think users should not be able to accidentally forget "enabling" an essential property of the abstraction they are trying to use.
Thinking of the more distant future, maybe we should support multiple with clauses and annotations on them:
I see your fear, but given the fact that mixins are not that widespread, and from what I've seen, prefer-inline did not turn into fairy-dust, I feel like we should be safe (Also, since agentic workflows are so widespread today, from my experience, we can safely assume that this is not going to turn into fairy dust. Modern models appear to be smart enough to not do that, especially if we provide documentation with clear guidelines).
That being said, I've put this on the back burner for now until we have more places to use these features in.