Skip to content

Commit 3099911

Browse files
rakudramaCommit Bot
authored and
Commit Bot
committed
[dart2js] Add @pragma('dart2js:disable-inlining') annotation
This annotation prevents inlining at call sites within the method that is annotated. The main expected use is to control inlining in unit tests. Change-Id: Ib6652f0b4cb7a34c6bf829b1be5c09e241ffbde2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/248345 Commit-Queue: Stephen Adams <[email protected]> Reviewed-by: Sigmund Cherem <[email protected]>
1 parent 6bcb186 commit 3099911

File tree

4 files changed

+133
-18
lines changed

4 files changed

+133
-18
lines changed

pkg/compiler/lib/src/js_backend/annotations.dart

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ class PragmaAnnotation {
2222
final bool forFieldsOnly;
2323
final bool internalOnly;
2424

25+
// TODO(sra): Review [forFunctionsOnly] and [forFieldsOnly]. Fields have
26+
// implied getters and setters, so some annotations meant only for functions
27+
// could reasonable be placed on a field to apply to the getter and setter.
28+
2529
const PragmaAnnotation(this._index, this.name,
2630
{this.forFunctionsOnly = false,
2731
this.forFieldsOnly = false,
@@ -41,15 +45,20 @@ class PragmaAnnotation {
4145
static const PragmaAnnotation tryInline =
4246
PragmaAnnotation(1, 'tryInline', forFunctionsOnly: true);
4347

48+
/// Annotation on a member that tells the optimizing compiler to disable
49+
/// inlining at call sites within the member.
50+
static const PragmaAnnotation disableInlining =
51+
PragmaAnnotation(2, 'disable-inlining');
52+
4453
static const PragmaAnnotation disableFinal = PragmaAnnotation(
45-
2, 'disableFinal',
54+
3, 'disableFinal',
4655
forFunctionsOnly: true, internalOnly: true);
4756

48-
static const PragmaAnnotation noElision = PragmaAnnotation(3, 'noElision');
57+
static const PragmaAnnotation noElision = PragmaAnnotation(4, 'noElision');
4958

5059
/// Tells the optimizing compiler that the annotated method cannot throw.
5160
/// Requires @pragma('dart2js:noInline') to function correctly.
52-
static const PragmaAnnotation noThrows = PragmaAnnotation(4, 'noThrows',
61+
static const PragmaAnnotation noThrows = PragmaAnnotation(5, 'noThrows',
5362
forFunctionsOnly: true, internalOnly: true);
5463

5564
/// Tells the optimizing compiler that the annotated method has no
@@ -58,56 +67,57 @@ class PragmaAnnotation {
5867
///
5968
/// Requires @pragma('dart2js:noInline') to function correctly.
6069
static const PragmaAnnotation noSideEffects = PragmaAnnotation(
61-
5, 'noSideEffects',
70+
6, 'noSideEffects',
6271
forFunctionsOnly: true, internalOnly: true);
6372

6473
/// Use this as metadata on method declarations to disable closed world
6574
/// assumptions on parameters, effectively assuming that the runtime arguments
6675
/// could be any value. Note that the constraints due to static types still
6776
/// apply.
6877
static const PragmaAnnotation assumeDynamic = PragmaAnnotation(
69-
6, 'assumeDynamic',
78+
7, 'assumeDynamic',
7079
forFunctionsOnly: true, internalOnly: true);
7180

72-
static const PragmaAnnotation asTrust = PragmaAnnotation(7, 'as:trust',
81+
static const PragmaAnnotation asTrust = PragmaAnnotation(8, 'as:trust',
7382
forFunctionsOnly: false, internalOnly: false);
7483

75-
static const PragmaAnnotation asCheck = PragmaAnnotation(8, 'as:check',
84+
static const PragmaAnnotation asCheck = PragmaAnnotation(9, 'as:check',
7685
forFunctionsOnly: false, internalOnly: false);
7786

78-
static const PragmaAnnotation typesTrust = PragmaAnnotation(9, 'types:trust',
87+
static const PragmaAnnotation typesTrust = PragmaAnnotation(10, 'types:trust',
7988
forFunctionsOnly: false, internalOnly: false);
8089

81-
static const PragmaAnnotation typesCheck = PragmaAnnotation(10, 'types:check',
90+
static const PragmaAnnotation typesCheck = PragmaAnnotation(11, 'types:check',
8291
forFunctionsOnly: false, internalOnly: false);
8392

8493
static const PragmaAnnotation parameterTrust = PragmaAnnotation(
85-
11, 'parameter:trust',
94+
12, 'parameter:trust',
8695
forFunctionsOnly: false, internalOnly: false);
8796

8897
static const PragmaAnnotation parameterCheck = PragmaAnnotation(
89-
12, 'parameter:check',
98+
13, 'parameter:check',
9099
forFunctionsOnly: false, internalOnly: false);
91100

92101
static const PragmaAnnotation downcastTrust = PragmaAnnotation(
93-
13, 'downcast:trust',
102+
14, 'downcast:trust',
94103
forFunctionsOnly: false, internalOnly: false);
95104

96105
static const PragmaAnnotation downcastCheck = PragmaAnnotation(
97-
14, 'downcast:check',
106+
15, 'downcast:check',
98107
forFunctionsOnly: false, internalOnly: false);
99108

100109
static const PragmaAnnotation indexBoundsTrust = PragmaAnnotation(
101-
15, 'index-bounds:trust',
110+
16, 'index-bounds:trust',
102111
forFunctionsOnly: false, internalOnly: false);
103112

104113
static const PragmaAnnotation indexBoundsCheck = PragmaAnnotation(
105-
16, 'index-bounds:check',
114+
17, 'index-bounds:check',
106115
forFunctionsOnly: false, internalOnly: false);
107116

108117
static const List<PragmaAnnotation> values = [
109118
noInline,
110119
tryInline,
120+
disableInlining,
111121
disableFinal,
112122
noElision,
113123
noThrows,
@@ -273,6 +283,9 @@ abstract class AnnotationsData {
273283
/// annotation.
274284
bool hasTryInline(MemberEntity member);
275285

286+
/// Returns `true` if inlining is disabled at call sites inside [member].
287+
bool hasDisableInlining(MemberEntity member);
288+
276289
/// Returns `true` if [member] has a `@pragma('dart2js:disableFinal')`
277290
/// annotation.
278291
bool hasDisableFinal(MemberEntity member);
@@ -343,6 +356,7 @@ class AnnotationsDataImpl implements AnnotationsData {
343356
final CheckPolicy _defaultConditionCheckPolicy;
344357
final CheckPolicy _defaultExplicitCastCheckPolicy;
345358
final CheckPolicy _defaultIndexBoundsCheckPolicy;
359+
final bool _defaultDisableInlining;
346360
final Map<MemberEntity, EnumSet<PragmaAnnotation>> pragmaAnnotations;
347361

348362
AnnotationsDataImpl(CompilerOptions options, this.pragmaAnnotations)
@@ -353,7 +367,8 @@ class AnnotationsDataImpl implements AnnotationsData {
353367
this._defaultExplicitCastCheckPolicy =
354368
options.defaultExplicitCastCheckPolicy,
355369
this._defaultIndexBoundsCheckPolicy =
356-
options.defaultIndexBoundsCheckPolicy;
370+
options.defaultIndexBoundsCheckPolicy,
371+
this._defaultDisableInlining = options.disableInlining;
357372

358373
factory AnnotationsDataImpl.readFromDataSource(
359374
CompilerOptions options, DataSourceReader source) {
@@ -392,6 +407,11 @@ class AnnotationsDataImpl implements AnnotationsData {
392407
bool hasTryInline(MemberEntity member) =>
393408
_hasPragma(member, PragmaAnnotation.tryInline);
394409

410+
@override
411+
bool hasDisableInlining(MemberEntity member) =>
412+
_hasPragma(member, PragmaAnnotation.disableInlining) ||
413+
_defaultDisableInlining;
414+
395415
@override
396416
bool hasDisableFinal(MemberEntity member) =>
397417
_hasPragma(member, PragmaAnnotation.disableFinal);

pkg/compiler/lib/src/ssa/builder.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5830,6 +5830,13 @@ class KernelSsaGraphBuilder extends ir.Visitor<void> with ir.VisitorVoidMixin {
58305830
return false;
58315831
}
58325832

5833+
// Check if inlining is disabled for the current element (includes globally)
5834+
// before making decsions on the basis of the callee so that cached callee
5835+
// decisions are not a function of the call site's method.
5836+
if (closedWorld.annotationsData.hasDisableInlining(_currentFrame.member)) {
5837+
return false;
5838+
}
5839+
58335840
bool insideLoop = loopDepth > 0 || graph.calledInLoop;
58345841

58355842
// Bail out early if the inlining decision is in the cache and we can't
@@ -5840,8 +5847,6 @@ class KernelSsaGraphBuilder extends ir.Visitor<void> with ir.VisitorVoidMixin {
58405847
if (cachedCanBeInlined == false) return false;
58415848

58425849
bool meetsHardConstraints() {
5843-
if (options.disableInlining) return false;
5844-
58455850
assert(
58465851
selector != null ||
58475852
function.isStatic ||

pkg/compiler/test/annotations/data/basic.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ main() {
1111
noThrows();
1212
noSideEffects();
1313
assumeDynamic();
14+
disableInlining();
15+
several();
1416
}
1517

1618
/*member: noInline:noInline*/
@@ -38,3 +40,15 @@ noSideEffects() {}
3840
/*member: assumeDynamic:assumeDynamic*/
3941
@pragma('dart2js:assumeDynamic')
4042
assumeDynamic() {}
43+
44+
/*member: disableInlining:disable-inlining*/
45+
@pragma('dart2js:disable-inlining')
46+
disableInlining() {}
47+
48+
/*member: several:assumeDynamic,disable-inlining,noInline,noSideEffects,noThrows*/
49+
@pragma('dart2js:disable-inlining')
50+
@pragma('dart2js:noThrows')
51+
@pragma('dart2js:noSideEffects')
52+
@pragma('dart2js:assumeDynamic')
53+
@pragma('dart2js:noInline')
54+
several() {}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) 2022, 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+
/*member: foo1:ignore*/
6+
@pragma('dart2js:tryInline')
7+
int foo1() => bar1();
8+
9+
/*member: foo2:ignore*/
10+
@pragma('dart2js:tryInline')
11+
@pragma('dart2js:disable-inlining')
12+
int foo2() => bar2();
13+
14+
/*member: foo3:ignore*/
15+
@pragma('dart2js:noInline')
16+
int foo3() => bar3();
17+
18+
/*member: bar1:ignore*/
19+
int bar1() => 1;
20+
21+
/*member: bar2:ignore*/
22+
int bar2() => 2;
23+
24+
/*member: bar3:ignore*/
25+
int bar3() => 3;
26+
27+
// All calls to `barN` are inlined because this improves size and performance.
28+
/*member: test1:function() {
29+
A.use(1, 2, 3);
30+
}*/
31+
@pragma('dart2js:noInline')
32+
void test1() {
33+
use(bar1(), bar2(), bar3());
34+
}
35+
36+
// No calls to `barN` are inlined due to `disable-inlining`.
37+
/*member: test2:function() {
38+
A.use(A.bar1(), A.bar2(), A.bar3());
39+
}*/
40+
@pragma('dart2js:noInline')
41+
@pragma('dart2js:disable-inlining')
42+
void test2() {
43+
use(bar1(), bar2(), bar3());
44+
}
45+
46+
// `foo` and `bar1` are inlined. `foo2` is inlined, but the contained call to
47+
// `bar2` is not inlined due to `disable-inlining` on `foo2`.
48+
/*member: test3:function() {
49+
A.use(1, A.bar2(), A.foo3());
50+
}*/
51+
@pragma('dart2js:noInline')
52+
void test3() {
53+
use(foo1(), foo2(), foo3());
54+
}
55+
56+
// None of the `fooN` calls are inlined due to `disable-inlining`.
57+
/*member: test4:function() {
58+
A.use(A.foo1(), A.foo2(), A.foo3());
59+
}*/
60+
@pragma('dart2js:noInline')
61+
@pragma('dart2js:disable-inlining')
62+
void test4() {
63+
use(foo1(), foo2(), foo3());
64+
}
65+
66+
/*member: use:ignore*/
67+
@pragma('dart2js:noInline')
68+
void use(int a, int b, int c) {}
69+
70+
/*member: main:ignore*/
71+
main() {
72+
test1();
73+
test2();
74+
test3();
75+
test4();
76+
}

0 commit comments

Comments
 (0)