[dart2js] Constant-fold const record accesses

I noticed that dart2js generated poor code for `(a, b) = (1, 2)`.
This is due to an oversight in not constant-folding record field loads.

Change-Id: Iac110d8c3373d7673c21494fc467f7fcb400de1d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/371505
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
This commit is contained in:
Stephen Adams 2024-06-17 19:47:34 +00:00 committed by Commit Queue
parent 8c918ffda3
commit 77496338dc
3 changed files with 103 additions and 1 deletions

View file

@ -122,7 +122,7 @@ class CommonMasks with AbstractValueDomain {
// subtypes of Record or (2) several live subtypes of Record. Everything
// 'works' for the similar interface `Function` because there are multiple
// live subclasses of `Closure`.
late final TypeMask recordType = dynamicType;
late final TypeMask recordType = nonNullType;
@override
late final TypeMask listType =

View file

@ -1559,6 +1559,25 @@ class SsaInstructionSimplifier extends HBaseVisitor<HInstruction>
return _graph.addConstant(value, _closedWorld);
}
}
if (constant is RecordConstantValue) {
final recordData = _closedWorld.recordData;
final shape = constant.shape;
final representation = recordData.representationForShape(shape);
if (representation != null) {
// The `representation` does not have a method to convert a field into
// a record-index, so look at all the possible access paths to find
// one that matches the field. Although this is 'slow' (1) only short
// records have direct fields (longer ones use arrays), and (2) we
// should always find a matching path, so the search will not be
// repeated in later phases.
for (int i = 0; i < constant.shape.fieldCount; i++) {
final path = recordData.pathForAccess(shape, i);
if (path.field == node.element && path.index == null) {
return _graph.addConstant(constant.values[i], _closedWorld);
}
}
}
}
}
return node;
@ -1650,6 +1669,38 @@ class SsaInstructionSimplifier extends HBaseVisitor<HInstruction>
return _graph.addConstant(foldedValue, _closedWorld);
}
}
} else if (receiver is HFieldGet) {
// Match the access path `(constant_record._values)[i]` for 'long' records
// where the record fields are stored in an Array (the `_RecordN` family
// of representations).
final receiver2 = receiver.receiver;
if (receiver2 is HConstant) {
final constant = receiver2.constant;
if (constant is RecordConstantValue) {
HInstruction index = node.index;
if (index is HConstant) {
final constantIndex = index.constant;
if (constantIndex is IntConstantValue && constantIndex.isUInt31()) {
int indexValue = constantIndex.intValue.toInt();
final recordData = _closedWorld.recordData;
final shape = constant.shape;
final representation = recordData.representationForShape(shape);
if (representation != null) {
// We assume that the record index is going to be the same as
// the HIndex index. If not (for example, we put the shape in
// the first slot of the array, offsetting the record field
// indexes), the codegen test will fail.
final path = recordData.pathForAccess(shape, indexValue);
if (path.field == receiver.element &&
path.index == indexValue) {
return _graph.addConstant(
constant.values[indexValue], _closedWorld);
}
}
}
}
}
}
}
return node;
}

View file

@ -0,0 +1,51 @@
// Copyright (c) 2024, 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.
const r3 = (1, 2, 3);
const r9 = (1, 2, 3, 4, 5, 6, 7, 8, 9);
@pragma('dart2js:never-inline')
/*member: r3a:function() {
return 123;
}*/
r3a() {
int a, b, c;
(a, b, c) = r3;
return a * 100 + b * 10 + c;
}
@pragma('dart2js:never-inline')
/*member: r3b:function() {
return 123;
}*/
r3b() {
int a, b, c;
(Z: a, A: b, P: c) = (A: 2, P: 3, Z: 1);
return a * 100 + b * 10 + c;
}
@pragma('dart2js:never-inline')
/*member: r9a:function() {
return 123456789;
}*/
r9a() {
int a, b, c, d, e, f, g, h, i;
(a, b, c, d, e, f, g, h, i) = r9;
return a * 100000000 +
b * 10000000 +
c * 1000000 +
d * 100000 +
e * 10000 +
f * 1000 +
g * 100 +
h * 10 +
i;
}
/*member: main:ignore*/
main() {
r3a();
r3b();
r9a();
}