b00c777f79
Add `@pragma('dart2js:allow-cse')` and `@pragma('dart2js:allow-dce')`.
These annotations allow the compiler to do common subexpression
elimination and dead code elimination on calls to getters and methods.
Change-Id: Ie4091466e4782a4d28a7f9bfbfdb60a5de45a384
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/426360
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
307 lines
5.9 KiB
Dart
307 lines
5.9 KiB
Dart
// Copyright (c) 2025, 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.
|
|
|
|
/*member: main:ignore*/
|
|
void main() {
|
|
for (final functions in [
|
|
[cse_000, cse_001, cse_010, cse_011, cse_100, cse_101, cse_110, cse_111],
|
|
[dce_000, dce_001, dce_010, dce_011, dce_100, dce_101, dce_110, dce_111],
|
|
[cse_dce_000, cse_dce_001, cse_dce_010, cse_dce_011],
|
|
[cse_dce_100, cse_dce_101, cse_dce_110, cse_dce_111],
|
|
]) {
|
|
for (final f in functions) {
|
|
f(123);
|
|
f('x');
|
|
}
|
|
}
|
|
print([sink1, sink2]);
|
|
}
|
|
|
|
Object? sink1;
|
|
Object? sink2;
|
|
int gI = 0;
|
|
|
|
/// [source] is not idempotent so would normally not be eligible for CSE or DCE.
|
|
/*member: source:ignore*/
|
|
@pragma('dart2js:never-inline')
|
|
Object? source(Object? o) {
|
|
++gI;
|
|
sink1 = o;
|
|
return o is num ? gI : o;
|
|
}
|
|
|
|
/// Basic 'f' functions are the same function body that calls [source] and casts
|
|
/// the result, but with all combinations of inlining and allow-cse and
|
|
/// allow-dce annotations.
|
|
///
|
|
/// fABC:
|
|
/// A: 1: inlined (0 not inlined)
|
|
/// B: 1: has allow-dce
|
|
/// C: 1: has allow-cse
|
|
///
|
|
/// So f000 and f100 serve as a baseline for comparing the effects of the
|
|
/// allow-cse and allow-dce annotations.
|
|
///
|
|
/// These functions are used in three ways:
|
|
///
|
|
/// cse_ABC calls fABC twice and uses the results
|
|
/// dce_ABC calls fABC once and ignores the result
|
|
/// cse_dce_ABC calls fABC twice and ignores both results
|
|
|
|
@pragma('dart2js:never-inline')
|
|
/*member: f000:function(o) {
|
|
return A._asInt(A.source(o));
|
|
}*/
|
|
int f000(o) => source(o) as int;
|
|
|
|
@pragma('dart2js:never-inline')
|
|
@pragma('dart2js:allow-cse')
|
|
/*member: f001:function(o) {
|
|
return A._asInt(A.source(o));
|
|
}*/
|
|
int f001(o) => source(o) as int;
|
|
|
|
@pragma('dart2js:never-inline')
|
|
@pragma('dart2js:allow-dce')
|
|
/*member: f010:function(o) {
|
|
return A._asInt(A.source(o));
|
|
}*/
|
|
int f010(o) => source(o) as int;
|
|
|
|
@pragma('dart2js:never-inline')
|
|
@pragma('dart2js:allow-cse')
|
|
@pragma('dart2js:allow-dce')
|
|
/*member: f011:function(o) {
|
|
return A._asInt(A.source(o));
|
|
}*/
|
|
int f011(o) => source(o) as int;
|
|
|
|
@pragma('dart2js:prefer-inline')
|
|
int f100(o) => source(o) as int;
|
|
|
|
@pragma('dart2js:prefer-inline')
|
|
@pragma('dart2js:allow-cse')
|
|
/*member: f101:function(o) {
|
|
return A._asInt(A.source(o));
|
|
}*/
|
|
int f101(o) => source(o) as int;
|
|
|
|
@pragma('dart2js:prefer-inline')
|
|
@pragma('dart2js:allow-dce')
|
|
/*member: f110:function(o) {
|
|
return A._asInt(A.source(o));
|
|
}*/
|
|
int f110(o) => source(o) as int;
|
|
|
|
@pragma('dart2js:prefer-inline')
|
|
@pragma('dart2js:allow-cse')
|
|
@pragma('dart2js:allow-dce')
|
|
/*member: f111:function(o) {
|
|
return A._asInt(A.source(o));
|
|
}*/
|
|
int f111(o) => source(o) as int;
|
|
|
|
/*member: cse_000:function(x) {
|
|
$.sink1 = A.f000(x);
|
|
$.sink2 = A.f000(x);
|
|
}*/
|
|
void cse_000(x) {
|
|
// Expect two uninlined calls.
|
|
sink1 = f000(x);
|
|
sink2 = f000(x);
|
|
}
|
|
|
|
/*member: cse_001:function(x) {
|
|
$.sink2 = $.sink1 = A.f001(x);
|
|
}*/
|
|
void cse_001(x) {
|
|
// Expect one call that is reused.
|
|
sink1 = f001(x);
|
|
sink2 = f001(x);
|
|
}
|
|
|
|
/*member: cse_010:function(x) {
|
|
$.sink1 = A.f010(x);
|
|
$.sink2 = A.f010(x);
|
|
}*/
|
|
void cse_010(x) {
|
|
sink1 = f010(x);
|
|
sink2 = f010(x);
|
|
}
|
|
|
|
/*member: cse_011:function(x) {
|
|
$.sink2 = $.sink1 = A.f011(x);
|
|
}*/
|
|
void cse_011(x) {
|
|
// Expect one call that is reused.
|
|
sink1 = f011(x);
|
|
sink2 = f011(x);
|
|
}
|
|
|
|
/*member: cse_100:function(x) {
|
|
$.sink1 = A._asInt(A.source(x));
|
|
$.sink2 = A._asInt(A.source(x));
|
|
}*/
|
|
void cse_100(x) {
|
|
// Expect two inlined calls with no shared subexpressions.
|
|
sink1 = f100(x);
|
|
sink2 = f100(x);
|
|
}
|
|
|
|
/*member: cse_101:function(x) {
|
|
$.sink2 = $.sink1 = A.f101(x);
|
|
}*/
|
|
void cse_101(x) {
|
|
// Expect one call, possibly inlined.
|
|
sink1 = f101(x);
|
|
sink2 = f101(x);
|
|
}
|
|
|
|
/*member: cse_110:function(x) {
|
|
$.sink1 = A.f110(x);
|
|
$.sink2 = A.f110(x);
|
|
}*/
|
|
void cse_110(x) {
|
|
// Expect two calls; possibly inlined, with no shared subexpressions.
|
|
sink1 = f110(x);
|
|
sink2 = f110(x);
|
|
}
|
|
|
|
/*member: cse_111:function(x) {
|
|
$.sink2 = $.sink1 = A.f111(x);
|
|
}*/
|
|
void cse_111(x) {
|
|
// Expect one call.
|
|
sink1 = f111(x);
|
|
sink2 = f111(x);
|
|
}
|
|
|
|
/*member: dce_000:function(x) {
|
|
A.f000(x);
|
|
}*/
|
|
void dce_000(x) {
|
|
f000(x);
|
|
}
|
|
|
|
/*member: dce_001:function(x) {
|
|
A.f001(x);
|
|
}*/
|
|
void dce_001(x) {
|
|
f001(x);
|
|
}
|
|
|
|
/*member: dce_010:function(x) {
|
|
}*/
|
|
void dce_010(x) {
|
|
// Expect deleted call.
|
|
f010(x);
|
|
}
|
|
|
|
/*member: dce_011:function(x) {
|
|
}*/
|
|
void dce_011(x) {
|
|
// Expect deleted call.
|
|
f011(x);
|
|
}
|
|
|
|
/*member: dce_100:function(x) {
|
|
A._asInt(A.source(x));
|
|
}*/
|
|
void dce_100(x) {
|
|
// Expect inlined call.
|
|
f100(x);
|
|
}
|
|
|
|
/*member: dce_101:function(x) {
|
|
A.f101(x);
|
|
}*/
|
|
void dce_101(x) {
|
|
// Expect one call, possibly inlined.
|
|
f101(x);
|
|
}
|
|
|
|
/*member: dce_110:function(x) {
|
|
}*/
|
|
void dce_110(x) {
|
|
// Expect deleted call.
|
|
f110(x);
|
|
}
|
|
|
|
/*member: dce_111:function(x) {
|
|
}*/
|
|
void dce_111(x) {
|
|
// Expect deleted call.
|
|
f111(x);
|
|
}
|
|
|
|
/*member: cse_dce_000:function(x) {
|
|
A.f000(x);
|
|
A.f000(x);
|
|
}*/
|
|
void cse_dce_000(x) {
|
|
// Expect two calls.
|
|
f000(x);
|
|
f000(x);
|
|
}
|
|
|
|
/*member: cse_dce_001:function(x) {
|
|
A.f001(x);
|
|
}*/
|
|
void cse_dce_001(x) {
|
|
// Expect one call.
|
|
f001(x);
|
|
f001(x);
|
|
}
|
|
|
|
/*member: cse_dce_010:function(x) {
|
|
}*/
|
|
void cse_dce_010(x) {
|
|
// Expect empty body - both calls deleted.
|
|
f010(x);
|
|
f010(x);
|
|
}
|
|
|
|
/*member: cse_dce_011:function(x) {
|
|
}*/
|
|
void cse_dce_011(x) {
|
|
// Expect empty body - both calls deleted.
|
|
f011(x);
|
|
f011(x);
|
|
}
|
|
|
|
/*member: cse_dce_100:function(x) {
|
|
A._asInt(A.source(x));
|
|
A._asInt(A.source(x));
|
|
}*/
|
|
void cse_dce_100(x) {
|
|
// Expect both calls inlined.
|
|
f100(x);
|
|
f100(x);
|
|
}
|
|
|
|
/*member: cse_dce_101:function(x) {
|
|
A.f101(x);
|
|
}*/
|
|
void cse_dce_101(x) {
|
|
// Expect call or one copy of inlined function.
|
|
f101(x);
|
|
f101(x);
|
|
}
|
|
|
|
/*member: cse_dce_110:function(x) {
|
|
}*/
|
|
void cse_dce_110(x) {
|
|
// Expect empty body - both calls deleted.
|
|
f110(x);
|
|
f110(x);
|
|
}
|
|
|
|
/*member: cse_dce_111:function(x) {
|
|
}*/
|
|
void cse_dce_111(x) {
|
|
// Expect empty body - both calls deleted.
|
|
f111(x);
|
|
f111(x);
|
|
}
|