-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[vm] Records of primitives requires boxing #51637
Comments
can i work on this issue ? |
/cc @alexmarkov |
There are multiple optimizations missing here:
Currently AOT compiler can unbox record instances (of 2 fields) in return values, as returning multiple values would be probably the most common use of records. At this point we are not sure how records would be used in practice and what other scenarios would be performance critical. Do you have a real use case where performance of passing record instances as arguments would be critical? (1) and (2) is not that hard to implement. However, we should try to find a motivating example / real use case before adding these optimizations, as they would increase complexity of the implementation. (3) may severely affect AOT compilation time, so I'd like to avoid adding it unless absolutely necessary. (4) looks like a bug and I'd like to fix it. |
My primary use-case for records is to support vector value types via records + inline classes. It's much nicer to deal with compared to inline class Vec4 {
final (double, double, double, double) xyzw;
const Vec4.raw(this.xyzw);
const Vec4(double x, double y, double z, double w) : super((x, y, z, w));
double get x => xyzw.$1;
double get y => xyzw.$2;
double get z => xyzw.$3;
double get w => xyzw.$4;
Vec4 operator+(Vec4 rhs) {
var (lx, ly, lz, lw) = xyzw;
var (rx, ry, rz, rw) = rhs.xyzw;
return Vec4(lx + rx, ly + ry, lz + rz, lw + rw);
}
// ...
} Another example is 128-bit ints via inline class: inline class Int128 {
final (int, int) _lohi;
Int128.from(int value) : _lohi = (value, 0);
// ... implementations here
} I'd still expect something as large as a 4x4 matrix to be heap allocated though. But these small records are the perfect candidate for unboxing. |
…gation Types of record field accesses are based on static record types, so it is useful to keep and propagate static types even when concrete class id is known. TEST=runtime/tests/vm/dart/records_field_operations_il_test.dart Issue: #49719 Issue: #51637 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try Change-Id: I268e3d519b07e12d1e2f8929cbd704a6995e2053 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287222 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
There's some weird interactions with inlining. These two programs are not equivalent: class Box2<T> { final T x, y; const Box2(this.x, this.y); }
void main(List<String> args) {
final record = Box2(1.0, 1.0);
inline(record);
}
@pragma('vm:prefer-inline')
void inline(Box2<double> xy) {
var Box2(:x, :y) = xy;
print(x + y);
}
void main(List<String> args) {
final record = (1.0, 1.0);
inline(record);
}
@pragma('vm:prefer-inline')
void inline((double, double) xy) {
var (x, y) = xy;
print(x + y);
}
Without inlining, it unboxes x and y. But with inlining + load-store forwarding, it loses type information. Guessing it trusts the static type of the parameter when not inlined, but when inlined it only has AllocateSmallRecord to go off of? |
…the same cid Previously, a type with known cid was immediately selected, even if another type also has a known cid. Now, if cids match, the static types are also compared. This is useful for record types, because static record type is more accurate than known kRecordCid. TEST=vm/dart/records_field_operations_il_test Issue: #51637 Cq-Include-Trybots: luci.dart.try:vm-aot-linux-release-x64-try,vm-aot-linux-debug-x64-try Change-Id: I4e528d80a355a79d428bf3f03212c5a65af0b661 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/292983 Auto-Submit: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
Here is what was done so far:
|
Thanks! I accidentally stumbled upon another place this comes up, looks like type propagation does not work for records that are allocated then immediately destructured: Godbolt This causes the compiler to switch to doing table calls for |
Godbolt link
When passing a record of primitives between functions, the values are boxed. Since these are value types without identity, they could exist as an unboxed record shape until they escape via covariance, similar to how int and double primitives work today (int -> num or double -> num require boxing).
Records also lose static type information, resulting in a runtime call to
_Double operator+
instead of unboxing.The text was updated successfully, but these errors were encountered: