Skip to content

Commit 85cb56d

Browse files
johnniwintherCommit Queue
authored and
Commit Queue
committed
[cfe] Don't use late final for pattern caching
This changes the pattern matching lowering to avoid using late final variables (or lowerings thereof) and instead inline the initializer where used. This avoids generating closures that are hard for backends to reason about and optimize. Closes #52805 Closes #53804 Change-Id: Ia887446dd4d4475d05cf852c812bfe4b851de261 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/338860 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Jens Johansen <[email protected]>
1 parent f07352b commit 85cb56d

File tree

582 files changed

+11728
-10955
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

582 files changed

+11728
-10955
lines changed

pkg/front_end/lib/src/fasta/type_inference/matching_cache.dart

Lines changed: 17 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,12 +1047,6 @@ class Cache {
10471047
/// Otherwise [_isSetVariable] is unused.
10481048
VariableDeclaration? _isSetVariable;
10491049

1050-
/// If cached using late lowering, this will be the variable for the local
1051-
/// function that initializes or reads [_variable].
1052-
///
1053-
/// Otherwise [_getVariable] is unused.
1054-
VariableDeclaration? _getVariable;
1055-
10561050
/// The name used to name [_variable], [_isSetVariable] and [_getVariable].
10571051
final String _name;
10581052

@@ -1128,123 +1122,27 @@ class Cache {
11281122
.createExpression(typeEnvironment, inCacheInitializer: false);
11291123
} else {
11301124
CacheExpression cacheableExpression = _accesses[accessKey]!;
1131-
if (_accesses.length == 1) {
1125+
if (!_isLate) {
1126+
// To avoid closurizing initializers we always inline the initializer
1127+
// unless it is not a late variable.
11321128
VariableDeclaration? variable = _variable;
1133-
VariableDeclaration? isSetVariable = _isSetVariable;
11341129
if (variable == null) {
1135-
DartType type = cacheableExpression.getType(typeEnvironment);
1136-
if (_matchingCache.useLowering && _isLate) {
1137-
variable = _variable =
1138-
createUninitializedVariable(type, fileOffset: _fileOffset)
1139-
..name = _name;
1140-
_matchingCache.registerDeclaration(variable);
1141-
isSetVariable = _isSetVariable = createInitializedVariable(
1142-
createBoolLiteral(false, fileOffset: _fileOffset),
1143-
typeEnvironment.coreTypes.boolNonNullableRawType,
1144-
fileOffset: _fileOffset)
1145-
..name = '$_name#isSet';
1146-
_matchingCache.registerDeclaration(isSetVariable);
1147-
VariableDeclaration getVariable =
1148-
_getVariable = createUninitializedVariable(
1149-
new FunctionType([], type, Nullability.nonNullable),
1150-
fileOffset: _fileOffset)
1151-
..name = '$_name#func'
1152-
..isFinal = true;
1153-
1154-
Statement body;
1155-
if (_matchingCache.useVerboseEncodingForDebugging) {
1156-
body = createBlock([
1157-
createIfStatement(
1158-
createNot(createVariableGet(isSetVariable)),
1159-
createBlock([
1160-
createExpressionStatement(createStaticInvocation(
1161-
typeEnvironment.coreTypes.printProcedure,
1162-
createArguments([
1163-
createStringConcatenation([
1164-
createStringLiteral('compute $_name',
1165-
fileOffset: _fileOffset),
1166-
], fileOffset: _fileOffset)
1167-
], fileOffset: _fileOffset),
1168-
fileOffset: _fileOffset)),
1169-
createExpressionStatement(createVariableSet(isSetVariable,
1170-
createBoolLiteral(true, fileOffset: _fileOffset),
1171-
fileOffset: _fileOffset)),
1172-
createExpressionStatement(createVariableSet(
1173-
variable,
1174-
cacheableExpression.expression.createExpression(
1175-
typeEnvironment,
1176-
inCacheInitializer: true),
1177-
fileOffset: _fileOffset)),
1178-
], fileOffset: _fileOffset),
1179-
fileOffset: _fileOffset),
1180-
createExpressionStatement(createStaticInvocation(
1181-
typeEnvironment.coreTypes.printProcedure,
1182-
createArguments([
1183-
createStringConcatenation([
1184-
createStringLiteral('$_name = ',
1185-
fileOffset: _fileOffset),
1186-
createVariableGet(variable)
1187-
], fileOffset: _fileOffset)
1188-
], fileOffset: _fileOffset),
1189-
fileOffset: _fileOffset)),
1190-
createReturnStatement(createVariableGet(variable),
1191-
fileOffset: _fileOffset),
1192-
], fileOffset: _fileOffset)
1193-
..fileOffset = _fileOffset;
1194-
} else {
1195-
body = createReturnStatement(
1196-
createConditionalExpression(
1197-
createVariableGet(isSetVariable),
1198-
createVariableGet(variable),
1199-
createLetEffect(
1200-
effect: createVariableSet(isSetVariable,
1201-
createBoolLiteral(true, fileOffset: _fileOffset),
1202-
fileOffset: _fileOffset),
1203-
result: createVariableSet(
1204-
variable,
1205-
cacheableExpression.expression.createExpression(
1206-
typeEnvironment,
1207-
inCacheInitializer: true),
1208-
fileOffset: _fileOffset)),
1209-
staticType: type,
1210-
fileOffset: _fileOffset),
1211-
fileOffset: _fileOffset);
1212-
}
1213-
FunctionDeclaration functionDeclaration = new FunctionDeclaration(
1214-
getVariable, new FunctionNode(body, returnType: type))
1215-
// TODO(johnniwinther): Reinsert the file offset when the vm
1216-
// doesn't use it for function declaration identity.
1217-
/*..fileOffset = fileOffset*/;
1218-
getVariable.type = functionDeclaration.function
1219-
.computeFunctionType(Nullability.nonNullable);
1220-
_matchingCache.registerDeclaration(functionDeclaration);
1221-
} else {
1222-
variable = _variable = createVariableCache(
1223-
cacheableExpression.expression.createExpression(typeEnvironment,
1224-
inCacheInitializer: true),
1225-
cacheableExpression.getType(typeEnvironment))
1226-
..isConst = _isConst
1227-
..isLate = _isLate
1228-
..name = _name;
1229-
if (_isLate) {
1230-
// Avoid step debugging on the declaration of caching variables.
1231-
// TODO(johnniwinther): Find a more systematic way of omitting
1232-
// offsets for better step debugging.
1233-
variable.fileOffset = TreeNode.noOffset;
1234-
}
1235-
_matchingCache.registerDeclaration(variable);
1130+
variable = _variable = createVariableCache(
1131+
cacheableExpression.expression
1132+
.createExpression(typeEnvironment, inCacheInitializer: true),
1133+
cacheableExpression.getType(typeEnvironment))
1134+
..isConst = _isConst
1135+
..isLate = _isLate
1136+
..name = _name;
1137+
if (_isLate) {
1138+
// Avoid step debugging on the declaration of caching variables.
1139+
// TODO(johnniwinther): Find a more systematic way of omitting
1140+
// offsets for better step debugging.
1141+
variable.fileOffset = TreeNode.noOffset;
12361142
}
1143+
_matchingCache.registerDeclaration(variable);
12371144
}
1238-
if (_matchingCache.useLowering && _isLate) {
1239-
LocalFunctionInvocation localFunctionInvocation =
1240-
createLocalFunctionInvocation(_getVariable!,
1241-
fileOffset: _fileOffset)
1242-
..fileOffset = TreeNode.noOffset;
1243-
localFunctionInvocation.arguments.fileOffset = TreeNode.noOffset;
1244-
result = localFunctionInvocation;
1245-
} else {
1246-
result = createVariableGet(variable)..fileOffset = TreeNode.noOffset;
1247-
}
1145+
result = createVariableGet(variable)..fileOffset = TreeNode.noOffset;
12481146
} else {
12491147
assert(_isLate, "Unexpected non-late cache ${cacheKey.name}");
12501148

pkg/front_end/test/spell_checking_list_code.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ cli
247247
client's
248248
clil
249249
clock
250+
closurizing
250251
cls
251252
cm
252253
cmdline
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
use(o) {}
6+
7+
void Function() f(List<int> list) {
8+
return switch (list) {
9+
[final item] => () {
10+
use(item);
11+
},
12+
[...] => () {},
13+
};
14+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method use(dynamic o) → dynamic {}
6+
static method f(core::List<core::int> list) → () → void {
7+
return block {
8+
() → void #t1;
9+
final synthesized core::List<core::int> #0#0 = list;
10+
#L1:
11+
{
12+
{
13+
final hoisted core::int item;
14+
if(#0#0.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final dynamic #t2 = item = #0#0.{core::List::[]}(0){(core::int) → core::int} in true)) {
15+
#t1 = () → void {
16+
self::use(item);
17+
};
18+
break #L1;
19+
}
20+
}
21+
{
22+
if(true) {
23+
#t1 = () → void {};
24+
break #L1;
25+
}
26+
}
27+
}
28+
} =>#t1;
29+
}
30+
31+
constants {
32+
#C1 = 1
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method use(dynamic o) → dynamic {}
6+
static method f(core::List<core::int> list) → () → void {
7+
return block {
8+
() → void #t1;
9+
final synthesized core::List<core::int> #0#0 = list;
10+
#L1:
11+
{
12+
{
13+
final hoisted core::int item;
14+
if(#0#0.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final core::int #t2 = item = #0#0.{core::List::[]}(0){(core::int) → core::int} in true)) {
15+
#t1 = () → void {
16+
self::use(item);
17+
};
18+
break #L1;
19+
}
20+
}
21+
{
22+
if(true) {
23+
#t1 = () → void {};
24+
break #L1;
25+
}
26+
}
27+
}
28+
} =>#t1;
29+
}
30+
31+
constants {
32+
#C1 = 1
33+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
use(o) {}
2+
void Function() f(List<int> list) {}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
use(o) {}
2+
void Function() f(List<int> list) {}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
static method use(dynamic o) → dynamic {}
7+
static method f(core::List<core::int> list) → () → void {
8+
return block {
9+
() → void #t1;
10+
final synthesized core::List<core::int> #0#0 = list;
11+
#L1:
12+
{
13+
{
14+
final hoisted core::int item;
15+
if(#0#0.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final dynamic #t2 = item = #0#0.{core::List::[]}(0){(core::int) → core::int} in true)) {
16+
#t1 = () → void {
17+
self::use(item);
18+
};
19+
break #L1;
20+
}
21+
}
22+
{
23+
if(true) {
24+
#t1 = () → void {};
25+
break #L1;
26+
}
27+
}
28+
throw new _in::ReachabilityError::•("`null` encountered as case in a switch expression with a non-nullable type.");
29+
}
30+
} =>#t1;
31+
}
32+
33+
constants {
34+
#C1 = 1
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
static method use(dynamic o) → dynamic {}
7+
static method f(core::List<core::int> list) → () → void {
8+
return block {
9+
() → void #t1;
10+
final synthesized core::List<core::int> #0#0 = list;
11+
#L1:
12+
{
13+
{
14+
final hoisted core::int item;
15+
if(#0#0.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final dynamic #t2 = item = #0#0.{core::List::[]}(0){(core::int) → core::int} in true)) {
16+
#t1 = () → void {
17+
self::use(item);
18+
};
19+
break #L1;
20+
}
21+
}
22+
{
23+
if(true) {
24+
#t1 = () → void {};
25+
break #L1;
26+
}
27+
}
28+
throw new _in::ReachabilityError::•("`null` encountered as case in a switch expression with a non-nullable type.");
29+
}
30+
} =>#t1;
31+
}
32+
33+
constants {
34+
#C1 = 1
35+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method use(dynamic o) → dynamic
6+
;
7+
static method f(core::List<core::int> list) → () → void
8+
;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:_internal" as _in;
5+
6+
static method use(dynamic o) → dynamic {}
7+
static method f(core::List<core::int> list) → () → void {
8+
return block {
9+
() → void #t1;
10+
final synthesized core::List<core::int> #0#0 = list;
11+
#L1:
12+
{
13+
{
14+
final hoisted core::int item;
15+
if(#0#0.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && (let final core::int #t2 = item = #0#0.{core::List::[]}(0){(core::int) → core::int} in true)) {
16+
#t1 = () → void {
17+
self::use(item);
18+
};
19+
break #L1;
20+
}
21+
}
22+
{
23+
if(true) {
24+
#t1 = () → void {};
25+
break #L1;
26+
}
27+
}
28+
throw new _in::ReachabilityError::•("`null` encountered as case in a switch expression with a non-nullable type.");
29+
}
30+
} =>#t1;
31+
}
32+
33+
constants {
34+
#C1 = 1
35+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
class A {
6+
int? f1;
7+
double? f2;
8+
9+
@pragma('vm:never-inline')
10+
String foo() {
11+
return switch ((this.f1, this.f2)) {
12+
(final f1, _) => '$f1',
13+
(null, final f2) => '$f2',
14+
(null, null) => '?',
15+
};
16+
}
17+
}
18+
19+
void main() {
20+
print(A().foo());
21+
}

0 commit comments

Comments
 (0)