Skip to content

Commit 57a1168

Browse files
sstricklCommit Queue
authored andcommitted
[pkg/vm] Create @pragma("vm:platform-const-if", <cond>) annotation
If a static field or getter is annotated with @pragma("vm:platform-const-if", <cond>) and <cond> const evaluates to true, then uses of the static field or getter are const evaluated when a target operating system is available. If <cond> const evaluates to any other value, then the annotation is ignored. For example, when runtime-only code is guarded like the following, using Flutter's kDebugMode constant, then debug mode Flutter programs can alter the defaultTargetPlatform for testing, but in release mode, the defaultTargetPlatform getter is const evaluated and code guarded with defaultTargetPlatform checks can be eliminated if unreachable: @pragma("vm:platform-const-if", !kDebugMode) TargetPlatform get defaultTargetPlatform { ... assert(() { if (Platform.environment.containsKey('FLUTTER_TEST')) { result = TestPlatform.android; } return true; }()); if (kDebugMode && platform.debugDefaultTargetPlatformOverride != null) { result = platform.debugDefaultTargetPlatformOverride; } ... } TEST=pkg/vm/test/transformations/vm_constant_evaluator Change-Id: I55b88502a908c56cf42a761dd06741f15c8a23d9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/333220 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
1 parent 91d16af commit 57a1168

File tree

34 files changed

+4181
-167
lines changed

34 files changed

+4181
-167
lines changed

pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -763,8 +763,12 @@ class ConstantsTransformer extends RemovingTransformer {
763763
if (shouldInline(target.initializer!)) {
764764
return evaluateAndTransformWithContext(node, node);
765765
}
766-
} else if (target is Procedure && target.kind == ProcedureKind.Method) {
767-
return evaluateAndTransformWithContext(node, node);
766+
} else if (target is Procedure) {
767+
if (target.kind == ProcedureKind.Method) {
768+
return evaluateAndTransformWithContext(node, node);
769+
} else if (target.kind == ProcedureKind.Getter && enableConstFunctions) {
770+
return evaluateAndTransformWithContext(node, node);
771+
}
768772
}
769773
return super.visitStaticGet(node, removalSentinel);
770774
}
@@ -2443,7 +2447,8 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
24432447
final EvaluationMode evaluationMode;
24442448

24452449
final bool enableTripleShift;
2446-
final bool enableConstFunctions;
2450+
final bool enableAsserts;
2451+
bool enableConstFunctions;
24472452
bool inExtensionTypeConstConstructor = false;
24482453

24492454
final Map<Constant, Constant> canonicalizationCache;
@@ -2488,6 +2493,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
24882493
this._environmentDefines, this.typeEnvironment, this.errorReporter,
24892494
{this.enableTripleShift = false,
24902495
this.enableConstFunctions = false,
2496+
this.enableAsserts = true,
24912497
this.errorOnUnevaluatedConstant = false,
24922498
this.evaluationMode = EvaluationMode.weak})
24932499
: numberSemantics = backend.numberSemantics,
@@ -3692,6 +3698,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
36923698
/// Returns [null] on success and an error-"constant" on failure, as such the
36933699
/// return value should be checked.
36943700
AbortConstant? checkAssert(AssertStatement statement) {
3701+
if (!enableAsserts) return null;
36953702
final Constant condition = _evaluateSubexpression(statement.condition);
36963703
if (condition is AbortConstant) return condition;
36973704

@@ -4577,27 +4584,23 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
45774584

45784585
@override
45794586
Constant visitStaticGet(StaticGet node) {
4580-
return withNewEnvironment(() {
4581-
final Member target = node.target;
4582-
visitedLibraries.add(target.enclosingLibrary);
4583-
if (target is Field) {
4584-
if (target.isConst) {
4585-
return evaluateExpressionInContext(target, target.initializer!);
4586-
}
4587-
return createEvaluationErrorConstant(
4588-
node,
4589-
templateConstEvalInvalidStaticInvocation
4590-
.withArguments(target.name.text));
4591-
} else if (target is Procedure && target.kind == ProcedureKind.Method) {
4587+
final Member target = node.target;
4588+
visitedLibraries.add(target.enclosingLibrary);
4589+
if (target is Field && target.isConst) {
4590+
return withNewEnvironment(
4591+
() => evaluateExpressionInContext(target, target.initializer!));
4592+
} else if (target is Procedure) {
4593+
if (target.kind == ProcedureKind.Method) {
45924594
// TODO(johnniwinther): Remove this. This should never occur.
45934595
return canonicalize(new StaticTearOffConstant(target));
4594-
} else {
4595-
return createEvaluationErrorConstant(
4596-
node,
4597-
templateConstEvalInvalidStaticInvocation
4598-
.withArguments(target.name.text));
4596+
} else if (target.kind == ProcedureKind.Getter && enableConstFunctions) {
4597+
return _handleFunctionInvocation(target.function, [], [], {});
45994598
}
4600-
});
4599+
}
4600+
return createEvaluationErrorConstant(
4601+
node,
4602+
templateConstEvalInvalidStaticInvocation
4603+
.withArguments(target.name.text));
46014604
}
46024605

46034606
@override
@@ -5617,6 +5620,7 @@ class StatementConstantEvaluator implements StatementVisitor<ExecutionStatus> {
56175620

56185621
@override
56195622
ExecutionStatus visitAssertBlock(AssertBlock node) {
5623+
if (!exprEvaluator.enableAsserts) return const ProceedStatus();
56205624
throw new UnsupportedError(
56215625
'Statement constant evaluation does not support ${node.runtimeType}.');
56225626
}

pkg/vm/lib/kernel_front_end.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,9 @@ Future runGlobalTransformations(
577577
final os = targetOS != null ? TargetOS.fromString(targetOS)! : null;
578578
final evaluator = vm_constant_evaluator.VMConstantEvaluator.create(
579579
target, component, os, nnbdMode,
580-
environmentDefines: environmentDefines, coreTypes: coreTypes);
580+
enableAsserts: enableAsserts,
581+
environmentDefines: environmentDefines,
582+
coreTypes: coreTypes);
581583
unreachable_code_elimination.transformComponent(
582584
target, component, evaluator, enableAsserts);
583585

pkg/vm/lib/transformations/pragma.dart

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,66 @@ const kResultTypeUsesPassedTypeArguments =
1515
const kVmRecognizedPragmaName = "vm:recognized";
1616
const kVmDisableUnboxedParametersPragmaName = "vm:disable-unboxed-parameters";
1717
const kVmKeepNamePragmaName = "vm:keep-name";
18+
const kVmPlatformConstPragmaName = "vm:platform-const";
19+
const kVmPlatformConstIfPragmaName = "vm:platform-const-if";
1820

1921
// Pragmas recognized by dart2wasm
2022
const kWasmEntryPointPragmaName = "wasm:entry-point";
2123
const kWasmExportPragmaName = "wasm:export";
2224

23-
abstract class ParsedPragma {
24-
const ParsedPragma();
25-
}
25+
abstract class ParsedPragma {}
2626

2727
enum PragmaEntryPointType { Default, GetterOnly, SetterOnly, CallOnly }
2828

2929
enum PragmaRecognizedType { AsmIntrinsic, GraphIntrinsic, Other }
3030

31-
class ParsedEntryPointPragma extends ParsedPragma {
31+
class ParsedEntryPointPragma implements ParsedPragma {
3232
final PragmaEntryPointType type;
3333
const ParsedEntryPointPragma(this.type);
3434
}
3535

36-
class ParsedResultTypeByTypePragma extends ParsedPragma {
36+
class ParsedResultTypeByTypePragma implements ParsedPragma {
3737
final DartType type;
3838
final bool resultTypeUsesPassedTypeArguments;
3939
const ParsedResultTypeByTypePragma(
4040
this.type, this.resultTypeUsesPassedTypeArguments);
4141
}
4242

43-
class ParsedResultTypeByPathPragma extends ParsedPragma {
43+
class ParsedResultTypeByPathPragma implements ParsedPragma {
4444
final String path;
4545
const ParsedResultTypeByPathPragma(this.path);
4646
}
4747

48-
class ParsedNonNullableResultType extends ParsedPragma {
48+
class ParsedNonNullableResultType implements ParsedPragma {
4949
const ParsedNonNullableResultType();
5050
}
5151

52-
class ParsedRecognized extends ParsedPragma {
52+
class ParsedRecognized implements ParsedPragma {
5353
final PragmaRecognizedType type;
5454
const ParsedRecognized(this.type);
5555
}
5656

57-
class ParsedDisableUnboxedParameters extends ParsedPragma {
57+
class ParsedDisableUnboxedParameters implements ParsedPragma {
5858
const ParsedDisableUnboxedParameters();
5959
}
6060

61-
class ParsedKeepNamePragma extends ParsedPragma {
61+
class ParsedKeepNamePragma implements ParsedPragma {
6262
const ParsedKeepNamePragma();
6363
}
6464

65+
class ParsedPlatformConstPragma implements ParsedPragma {
66+
const ParsedPlatformConstPragma();
67+
}
68+
6569
abstract class PragmaAnnotationParser {
6670
/// May return 'null' if the annotation does not represent a recognized
6771
/// @pragma.
6872
ParsedPragma? parsePragma(Expression annotation);
73+
74+
Iterable<R> parsedPragmas<R extends ParsedPragma>(Iterable<Expression> node);
6975
}
7076

71-
class ConstantPragmaAnnotationParser extends PragmaAnnotationParser {
77+
class ConstantPragmaAnnotationParser implements PragmaAnnotationParser {
7278
final CoreTypes coreTypes;
7379
final Target target;
7480

@@ -166,6 +172,14 @@ class ConstantPragmaAnnotationParser extends PragmaAnnotationParser {
166172
return const ParsedDisableUnboxedParameters();
167173
case kVmKeepNamePragmaName:
168174
return ParsedKeepNamePragma();
175+
case kVmPlatformConstPragmaName:
176+
return ParsedPlatformConstPragma();
177+
case kVmPlatformConstIfPragmaName:
178+
if (options is! BoolConstant) {
179+
throw "ERROR: Non-boolean option to '$kVmPlatformConstIfPragmaName' "
180+
"pragma: $options";
181+
}
182+
return options.value ? ParsedPlatformConstPragma() : null;
169183
case kWasmEntryPointPragmaName:
170184
return ParsedEntryPointPragma(PragmaEntryPointType.Default);
171185
case kWasmExportPragmaName:
@@ -175,4 +189,8 @@ class ConstantPragmaAnnotationParser extends PragmaAnnotationParser {
175189
return null;
176190
}
177191
}
192+
193+
Iterable<R> parsedPragmas<R extends ParsedPragma>(
194+
Iterable<Expression> annotations) =>
195+
annotations.map(parsePragma).whereType<R>();
178196
}

pkg/vm/lib/transformations/unreachable_code_elimination.dart

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,10 @@ class SimpleUnreachableCodeElimination extends RemovingTransformer {
4848
return constant is BoolConstant ? constant.value : null;
4949
}
5050

51-
Expression _makeConstantExpression(Constant constant, Expression node) {
52-
if (constant is UnevaluatedConstant &&
53-
constant.expression is InvalidExpression) {
54-
return constant.expression;
55-
}
56-
ConstantExpression constantExpression = new ConstantExpression(
57-
constant, node.getStaticType(_staticTypeContext!))
58-
..fileOffset = node.fileOffset;
59-
if (node is FileUriExpression) {
60-
return new FileUriConstantExpression(constantExpression.constant,
61-
type: constantExpression.type, fileUri: node.fileUri)
62-
..fileOffset = node.fileOffset;
63-
}
64-
return constantExpression;
65-
}
66-
6751
Expression _createBoolConstantExpression(bool value, Expression node) =>
68-
_makeConstantExpression(
69-
constantEvaluator.canonicalize(BoolConstant(value)), node);
52+
ConstantExpression(constantEvaluator.makeBoolConstant(value),
53+
node.getStaticType(_staticTypeContext!))
54+
..fileOffset = node.fileOffset;
7055

7156
Statement _makeEmptyBlockIfEmptyStatement(Statement node, TreeNode parent) =>
7257
node is EmptyStatement ? (Block(<Statement>[])..parent = parent) : node;
@@ -219,11 +204,21 @@ class SimpleUnreachableCodeElimination extends RemovingTransformer {
219204
if (target is Field && target.isConst) {
220205
throw 'StaticGet from const field $target should be evaluated by front-end: $node';
221206
}
222-
if (!constantEvaluator.transformerShouldEvaluateExpression(node)) {
223-
return node;
207+
208+
if (!constantEvaluator.hasTargetOS ||
209+
!constantEvaluator.isPlatformConst(target)) {
210+
return super.visitStaticGet(node, removalSentinel);
224211
}
212+
225213
final result = constantEvaluator.evaluate(_staticTypeContext!, node);
226-
return _makeConstantExpression(result, node);
214+
215+
if (result is UnevaluatedConstant &&
216+
result.expression is InvalidExpression) {
217+
return result.expression;
218+
}
219+
220+
final type = node.getStaticType(_staticTypeContext!);
221+
return ConstantExpression(result, type)..fileOffset = node.fileOffset;
227222
}
228223

229224
@override

0 commit comments

Comments
 (0)