[parser] Optimize mayFollowTypeArgs

Doing benchmarks before and after I get the below.
Notice that this is on tokens/ms, i.e. higher is better.

JIT:

pkg/analyzer/lib/src/dart/ast/ast.dart:

```
Difference at 95.0% confidence
        1.27922 +/- 0.393002
        6.47159% +/- 1.9882%
        (Student's t, pooled s = 0.418268)
```

pkg/front_end/lib/src/type_inference/inference_visitor.dart:

```
Difference at 95.0% confidence
        0.968609 +/- 0.287367
        6.8824% +/- 2.04187%
        (Student's t, pooled s = 0.305841)
```

In both cases it processes a little over 6% more tokens per ms.

AOT:

pkg/analyzer/lib/src/dart/ast/ast.dart:

```
Difference at 95.0% confidence
        2.39988 +/- 0.429312
        9.50898% +/- 1.70105%
        (Student's t, pooled s = 0.456911)
```

pkg/front_end/lib/src/type_inference/inference_visitor.dart:

```
Difference at 95.0% confidence
        2.28604 +/- 0.279366
        12.9521% +/- 1.58282%
        (Student's t, pooled s = 0.297326)
```

It processes between 9-13% more tokens per ms.


Additionally, for AOT, I ran it through the benchmarker and got this:

pkg/analyzer/lib/src/dart/ast/ast.dart:

```
msec task-clock:u: -8.5488% +/- 2.1840% (-275.52 +/- 70.39) (3222.95 -> 2947.42)
cycles:u: -8.6402% +/- 2.1619% (-1210657004.60 +/- 302921394.06) (14011942063.40 -> 12801285058.80)
instructions:u: -9.0870% +/- 0.0000% (-2746030853.20 +/- 876.33) (30219334757.30 -> 27473303904.10)
branch-misses:u: -13.3846% +/- 10.1850% (-5542357.40 +/- 4217459.09) (41408381.20 -> 35866023.80)
seconds time elapsed: -8.5442% +/- 2.1816% (-0.28 +/- 0.07) (3.22 -> 2.95)
seconds user: -8.5614% +/- 2.2098% (-0.27 +/- 0.07) (3.20 -> 2.93)
```

pkg/front_end/lib/src/type_inference/inference_visitor.dart

```
msec task-clock:u: -11.6033% +/- 0.9079% (-402.51 +/- 31.50) (3468.96 -> 3066.45)
cycles:u: -11.6826% +/- 0.9053% (-1765194611.60 +/- 136793475.00) (15109578869.80 -> 13344384258.20)
instructions:u: -11.4435% +/- 0.0000% (-3905886611.70 +/- 1150.33) (34131775222.10 -> 30225888610.40)
branch-misses:u: -17.5244% +/- 4.6453% (-8196278.70 +/- 2172645.25) (46770630.60 -> 38574351.90)
seconds time elapsed: -11.6004% +/- 0.9056% (-0.40 +/- 0.03) (3.47 -> 3.07)
seconds user: -11.6215% +/- 0.9879% (-0.40 +/- 0.03) (3.45 -> 3.05)
```

I.e. in both cases it's 8-11% less work (instructions, time etc).

Compiling the CFE with the CFE before and after (AOT) I get this change:

```
msec task-clock:u: -0.4356% +/- 0.3110% (-23.07 +/- 16.47) (5295.49 -> 5272.42)
page-faults:u: 0.1135% +/- 0.0534% (123.64 +/- 58.22) (108956.82 -> 109080.46)
cycles:u: -0.5004% +/- 0.3175% (-109702416.52 +/- 69596415.67) (21921571730.18 -> 21811869313.66)
instructions:u: -0.3994% +/- 0.0005% (-104979706.80 +/- 138268.02) (26284569470.80 -> 26179589764.00)
branch-misses:u: -1.6117% +/- 1.4644% (-1285239.90 +/- 1167813.84) (79746730.52 -> 78461490.62)
seconds time elapsed: -0.4291% +/- 0.3090% (-0.02 +/- 0.02) (5.30 -> 5.28)
seconds user: -0.3992% +/- 0.3801% (-0.02 +/- 0.02) (5.04 -> 5.02)
```

So this improvement to the parser reduces the runtime of compiling by ~0.4%.

Change-Id: Ia89d02e8cce2655fb23ffe18ecbab63521ce75e3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/439721
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Jens Johansen
2025-07-30 07:05:12 -07:00
committed by Commit Queue
parent 9c5eea2b1a
commit 06402ab1ca
@@ -470,7 +470,8 @@ TypeParamOrArgInfo computeTypeParamOrArg(
/// possible other constructs will pass (e.g., 'a < C, D > 3').
TypeParamOrArgInfo computeMethodTypeArguments(Token token) {
TypeParamOrArgInfo typeArg = computeTypeParamOrArg(token);
return mayFollowTypeArgs(typeArg.skip(token).next!) && !typeArg.recovered
return _mayFollowTypeArgs(typeArg.skip(token).next!.typeIndex) &&
!typeArg.recovered
? typeArg
: noTypeParamOrArg;
}
@@ -480,8 +481,10 @@ TypeParamOrArgInfo computeMethodTypeArguments(Token token) {
/// pattern.
const Set<String> illegalPatternIdentifiers = {'when', 'as'};
/// Indicates whether the given [token] is allowed to follow a list of type
/// arguments used as a selector after an expression.
/// Indicates whether the given [tokenTypeIndex] is allowed to follow a list of
/// type arguments used as a selector after an expression.
///
/// Get the index from a token via `Token.typeIndex`.
///
/// This is used for disambiguating constructs like `f(a<b,c>(d))` and
/// `f(a<b,c>-d)`. In the case of `f(a<b,c>(d))`, `true` will be returned,
@@ -490,19 +493,71 @@ const Set<String> illegalPatternIdentifiers = {'when', 'as'};
/// function `a`). In the case of `f(a<b,c>-d)`, `false` will be returned,
/// indicating that the `<` and `>` should be interpreted as operators (so two
/// arguments are being passed to `f`: `a < b` and `c > -d`).
bool mayFollowTypeArgs(Token token) {
const Set<String> continuationTokens = {'(', '.', '==', '!='};
const Set<String> stopTokens = {')', ']', '}', ';', ':', ','};
const Set<String> tokensThatMayFollowTypeArg = {
...continuationTokens,
...stopTokens,
};
if (token.isA(TokenType.EOF)) {
// The spec doesn't have anything to say about this case, since an
// expression can't occur at the end of a file, but for testing it's to our
// advantage to allow EOF after type arguments, so that an isolated `f<x>`
// can be parsed as an expression.
return true;
}
return tokensThatMayFollowTypeArg.contains(token.lexeme);
///
// DartDocTest(() {
// for (int i = 0; i < 256; i++) {
// if (_mayFollowTypeArgs(i) !=
// _mayFollowTypeArgs_helper_for_testing(i)) {
// return false;
// }
// }
// return true;
// }(), true);
@pragma("vm:prefer-inline")
bool _mayFollowTypeArgs(int tokenTypeIndex) {
// Table has size 256 to avoid bounds checks as this is called with
// `Token.typeIndex` which is know to be in [0-255].
const List<bool> table = [
// format hack.
true, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, true, false, false, false, false, false,
true, true, false, false, true, true, true, false,
true, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, true, false, false, false,
true, false, false, false, false, false, false, false,
false, true, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
// format hack.
];
return table[tokenTypeIndex];
}
// ignore: unused_element
bool _mayFollowTypeArgs_helper_for_testing(int tokenTypeIndex) {
return tokenTypeIndex == TokenType.OPEN_PAREN.index ||
tokenTypeIndex == TokenType.PERIOD.index ||
tokenTypeIndex == TokenType.EQ_EQ.index ||
tokenTypeIndex == TokenType.BANG_EQ.index ||
tokenTypeIndex == TokenType.CLOSE_PAREN.index ||
tokenTypeIndex == TokenType.CLOSE_SQUARE_BRACKET.index ||
tokenTypeIndex == TokenType.CLOSE_CURLY_BRACKET.index ||
tokenTypeIndex == TokenType.SEMICOLON.index ||
tokenTypeIndex == TokenType.COLON.index ||
tokenTypeIndex == TokenType.COMMA.index ||
tokenTypeIndex == TokenType.EOF.index;
}