Skip to content

Commit

Permalink
[pkg/vm] Initial work on constant operating system fields and getters.
Browse files Browse the repository at this point in the history
* Add an `targetOS` argument to `pkg/vm`'s `compileToKernel`,
  that contains the target operating system's name.

* Add a new `--target-os` command line argument for all binaries
  that use `compileToKernel` for clients to provide the target
  operating system, if known.

* Add a new`"vm:platform:const"` annotation to certain field and
  getters in the Platform class.

  This annotation is used to annotate static getters and fields with
  initializers where the getter body or field initializer must evaluate
  to a constant value if the target operating system is known. This
  annotation may be used outside the Platform class and in user code.

  For example, this annotation can be used on a static `String` field
  that is initialized with one value if `Platform.isWindows` is true
  and to a different value if `Platform.isWindows` is false.

  Note: If the const functions experimental flag is disabled, then
  any annotated static methods can only contain a single expression
  whose value is returned. If it is enabled, then the static method
  is evaluated as if it is a const function with the special
  handling of annotated static fields and getters above.

* Create a VM constant evaluator that evaluates uses of static getters
  and fields marked with the above annotations when a target operating
  system is provided.

* Use the new VM constant evaluator in the unreachable code elimination
  transformer.

TEST=pkg/vm/test/transformations/platform_use_transformer
     pkg/vm/test/transformations/unreachable_code_elimination

Change-Id: Ie381de70486a767fd7b1d515fd9e6bb58c6bf090
Bug: #31969
Cq-Include-Trybots: luci.dart.try:pkg-linux-release-try
CoreLibraryReviewExempt: Just adding vm-specific annotations.
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274386
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
  • Loading branch information
sstrickl authored and Commit Queue committed Feb 10, 2023
1 parent 1309036 commit 2c0484c
Show file tree
Hide file tree
Showing 26 changed files with 784 additions and 30 deletions.
2 changes: 2 additions & 0 deletions pkg/dart2native/lib/dart2native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ Future<ProcessResult> generateAotKernel(
String? packages,
List<String> defines,
{String enableExperiment = '',
String? targetOS,
List<String> extraGenKernelOptions = const []}) {
return Process.run(dart, [
genKernel,
'--platform',
platformDill,
if (enableExperiment.isNotEmpty) '--enable-experiment=$enableExperiment',
if (targetOS != null) '--target-os=$targetOS',
'--aot',
'-Ddart.vm.product=true',
...(defines.map((d) => '-D$d')),
Expand Down
5 changes: 5 additions & 0 deletions pkg/dart2native/lib/generate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Future<void> generateNative({
String? outputFile,
String? debugFile,
String? packages,
String? targetOS,
required List<String> defines,
String enableExperiment = '',
bool enableAsserts = false,
Expand Down Expand Up @@ -53,6 +54,9 @@ Future<void> generateNative({
debugFile != null ? path.canonicalize(path.normalize(debugFile)) : null;

if (verbose) {
if (targetOS != null) {
print('Specializing Platform getters for target OS $targetOS.');
}
print('Compiling $sourcePath to $outputPath using format $kind:');
print('Generating AOT kernel dill.');
}
Expand All @@ -61,6 +65,7 @@ Future<void> generateNative({
final kernelResult = await generateAotKernel(Platform.executable, genKernel,
productPlatformDill, sourcePath, kernelFile, packages, defines,
enableExperiment: enableExperiment,
targetOS: targetOS,
extraGenKernelOptions: [
'--invocation-modes=compile',
'--verbosity=$verbosity',
Expand Down
5 changes: 5 additions & 0 deletions pkg/dartdev/lib/src/commands/compile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:dart2native/generate.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show Verbosity;
import 'package:path/path.dart' as path;
import 'package:vm/target_os.dart'; // For possible --target-os values.

import '../core.dart';
import '../experiments.dart';
Expand Down Expand Up @@ -281,6 +282,9 @@ Remove debugging information from the output and save it separately to the speci
hide: true,
valueHelp: 'opt1,opt2,...',
)
..addOption('target-os',
help: 'Compile to a specific target operating system.',
allowed: TargetOS.names)
..addExperimentalFlags(verbose: verbose);
}

Expand Down Expand Up @@ -327,6 +331,7 @@ Remove debugging information from the output and save it separately to the speci
verbose: verbose,
verbosity: args['verbosity'],
extraOptions: args['extra-gen-snapshot-options'],
targetOS: args['target-os'],
);
return 0;
} catch (e, st) {
Expand Down
1 change: 1 addition & 0 deletions pkg/dartdev/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies:
pub: any
telemetry: any
usage: any
vm: any
yaml: any

# Use 'any' constraints here; we get our versions from the DEPS file.
Expand Down
20 changes: 16 additions & 4 deletions pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import 'package:_fe_analyzer_shared/src/exhaustiveness/exhaustive.dart';
import 'package:_fe_analyzer_shared/src/exhaustiveness/space.dart';
import 'package:_fe_analyzer_shared/src/exhaustiveness/static_type.dart';

import '../../base/nnbd_mode.dart';
import '../fasta_codes.dart';

import 'constant_int_folder.dart';
Expand Down Expand Up @@ -177,7 +178,18 @@ void transformProcedure(
enum EvaluationMode {
weak,
agnostic,
strong,
strong;

static EvaluationMode fromNnbdMode(NnbdMode nnbdMode) {
switch (nnbdMode) {
case NnbdMode.Weak:
return EvaluationMode.weak;
case NnbdMode.Strong:
return EvaluationMode.strong;
case NnbdMode.Agnostic:
return EvaluationMode.agnostic;
}
}
}

class ConstantWeakener extends ComputeOnceConstantVisitor<Constant?> {
Expand Down Expand Up @@ -3144,7 +3156,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
/// This compute the constant as seen in the current evaluation mode even when
/// the constant is defined in a library compiled with the agnostic evaluation
/// mode.
Constant _evaluateExpressionInContext(Member member, Expression expression) {
Constant evaluateExpressionInContext(Member member, Expression expression) {
StaticTypeContext? oldStaticTypeContext = _staticTypeContext;
_staticTypeContext = new StaticTypeContext(member, typeEnvironment);
Constant constant = _evaluateSubexpression(expression);
Expand All @@ -3166,7 +3178,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
visitedLibraries.add(target.enclosingLibrary);
if (target is Field) {
if (target.isConst) {
return _evaluateExpressionInContext(target, target.initializer!);
return evaluateExpressionInContext(target, target.initializer!);
}
return createEvaluationErrorConstant(
node,
Expand Down Expand Up @@ -3241,7 +3253,7 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
Constant _getFromEnvironmentDefaultValue(Procedure target) {
VariableDeclaration variable = target.function.namedParameters
.singleWhere((v) => v.name == 'defaultValue');
return _evaluateExpressionInContext(target, variable.initializer!);
return evaluateExpressionInContext(target, variable.initializer!);
}

Constant _handleFromEnvironment(
Expand Down
14 changes: 1 addition & 13 deletions pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1618,7 +1618,6 @@ class KernelTarget extends TargetImplementation {
_getConstantEvaluationMode();

constants.EvaluationMode _getConstantEvaluationMode() {
constants.EvaluationMode evaluationMode;
// If nnbd is not enabled we will use weak evaluation mode. This is needed
// because the SDK might be agnostic and therefore needs to be weakened
// for legacy mode.
Expand All @@ -1627,18 +1626,7 @@ class KernelTarget extends TargetImplementation {
loader.nnbdMode == NnbdMode.Weak,
"Non-weak nnbd mode found without experiment enabled: "
"${loader.nnbdMode}.");
switch (loader.nnbdMode) {
case NnbdMode.Weak:
evaluationMode = constants.EvaluationMode.weak;
break;
case NnbdMode.Strong:
evaluationMode = constants.EvaluationMode.strong;
break;
case NnbdMode.Agnostic:
evaluationMode = constants.EvaluationMode.agnostic;
break;
}
return evaluationMode;
return constants.EvaluationMode.fromNnbdMode(loader.nnbdMode);
}

void verify() {
Expand Down
2 changes: 1 addition & 1 deletion pkg/front_end/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name: front_end
publish_to: none

environment:
sdk: '>=2.13.0 <3.0.0'
sdk: '>=2.17.0 <3.0.0'

# Use 'any' constraints here; we get our versions from the DEPS file.
dependencies:
Expand Down
12 changes: 12 additions & 0 deletions pkg/frontend_server/lib/frontend_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import 'package:usage/uuid/uuid.dart';

import 'package:vm/incremental_compiler.dart' show IncrementalCompiler;
import 'package:vm/kernel_front_end.dart';
import 'package:vm/target_os.dart'; // For possible --target-os values.

import 'src/javascript_bundle.dart';

Expand All @@ -51,6 +52,9 @@ ArgParser argParser = ArgParser(allowTrailingOptions: true)
..addFlag('aot',
help: 'Run compiler in AOT mode (enables whole-program transformations)',
defaultsTo: false)
..addOption('target-os',
help: 'Compile to a specific target operating system.',
allowed: TargetOS.names)
..addFlag('support-mirrors',
help: 'Whether dart:mirrors is supported. By default dart:mirrors is '
'supported when --aot and --minimal-kernel are not used.',
Expand Down Expand Up @@ -513,6 +517,13 @@ class FrontendCompiler implements CompilerInterface {
}
}

if (options['target-os'] != null) {
if (!options['aot']) {
print('Error: --target-os option must be used with --aot');
return false;
}
}

if (options['support-mirrors'] == true) {
if (options['aot']) {
print('Error: --support-mirrors option cannot be used with --aot');
Expand Down Expand Up @@ -592,6 +603,7 @@ class FrontendCompiler implements CompilerInterface {
includePlatform: options['link-platform'],
deleteToStringPackageUris: options['delete-tostring-package-uri'],
aot: options['aot'],
targetOS: options['target-os'],
useGlobalTypeFlowAnalysis: options['tfa'],
useRapidTypeAnalysis: options['rta'],
environmentDefines: environmentDefines,
Expand Down
2 changes: 2 additions & 0 deletions pkg/kernel/lib/core_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ class CoreTypes {
late final Procedure objectEquals =
index.getProcedure('dart:core', 'Object', '==');

late final Class? platformClass = index.tryGetClass('dart:io', 'Platform');

late final Class pragmaClass = index.getClass('dart:core', 'pragma');

late final Field pragmaName = index.getField('dart:core', 'pragma', 'name');
Expand Down
27 changes: 23 additions & 4 deletions pkg/vm/lib/kernel_front_end.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
import 'package:package_config/package_config.dart' show loadPackageConfigUri;

import 'http_filesystem.dart' show HttpAwareFileSystem;
import 'target_os.dart';
import 'native_assets/synthesizer.dart';
import 'target/install.dart' show installAdditionalTargets;
import 'transformations/devirtualization.dart' as devirtualization
Expand All @@ -61,6 +62,7 @@ import 'transformations/obfuscation_prohibitions_annotator.dart'
import 'transformations/call_site_annotator.dart' as call_site_annotator;
import 'transformations/unreachable_code_elimination.dart'
as unreachable_code_elimination;
import 'transformations/vm_constant_evaluator.dart' as vm_constant_evaluator;
import 'transformations/deferred_loading.dart' as deferred_loading;
import 'transformations/to_string_transformer.dart' as to_string_transformer;

Expand Down Expand Up @@ -111,6 +113,9 @@ void declareCompilerOptions(ArgParser args) {
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: true);
args.addOption('target-os',
help: 'Compile for a specific target operating system when in AOT mode.',
allowed: TargetOS.names);
args.addFlag('rta',
help: 'Use rapid type analysis for faster compilation in AOT mode.',
defaultsTo: true);
Expand Down Expand Up @@ -193,6 +198,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
final String? depfile = options['depfile'];
final String? fromDillFile = options['from-dill'];
final List<String>? fileSystemRoots = options['filesystem-root'];
final String? targetOS = options['target-os'];
final bool aot = options['aot'];
final bool tfa = options['tfa'];
final bool rta = options['rta'];
Expand Down Expand Up @@ -305,6 +311,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
useProtobufTreeShakerV2: useProtobufTreeShakerV2,
minimalKernel: minimalKernel,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
targetOS: targetOS,
fromDillFile: fromDillFile);

errorPrinter.printCompilationMessages();
Expand Down Expand Up @@ -406,6 +413,7 @@ Future<KernelCompilationResults> compileToKernel(
bool useProtobufTreeShakerV2 = false,
bool minimalKernel = false,
bool treeShakeWriteOnlyFields = false,
String? targetOS = null,
String? fromDillFile = null,
}) async {
// Replace error handler to detect if there are compilation errors.
Expand Down Expand Up @@ -450,6 +458,9 @@ Future<KernelCompilationResults> compileToKernel(
if ((aot || minimalKernel) && component != null) {
await runGlobalTransformations(target, component, useGlobalTypeFlowAnalysis,
enableAsserts, useProtobufTreeShakerV2, errorDetector,
environmentDefines: options.environmentDefines,
nnbdMode: options.nnbdMode,
targetOS: targetOS,
minimalKernel: minimalKernel,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
useRapidTypeAnalysis: useRapidTypeAnalysis);
Expand Down Expand Up @@ -512,7 +523,10 @@ Future runGlobalTransformations(
ErrorDetector errorDetector,
{bool minimalKernel = false,
bool treeShakeWriteOnlyFields = false,
bool useRapidTypeAnalysis = true}) async {
bool useRapidTypeAnalysis = true,
NnbdMode nnbdMode = NnbdMode.Weak,
Map<String, String>? environmentDefines,
String? targetOS}) async {
assert(!target.flags.supportMirrors);
if (errorDetector.hasCompilationErrors) return;

Expand All @@ -526,9 +540,14 @@ Future runGlobalTransformations(
// when building a platform dill file for VM/JIT case.
mixin_deduplication.transformComponent(component);

// Unreachable code elimination transformation should be performed
// before type flow analysis so TFA won't take unreachable code into account.
unreachable_code_elimination.transformComponent(component, enableAsserts);
// Perform unreachable code elimination, which should be performed before
// type flow analysis so TFA won't take unreachable code into account.
final os = targetOS != null ? TargetOS.fromString(targetOS)! : null;
final evaluator = vm_constant_evaluator.VMConstantEvaluator.create(
target, component, os, nnbdMode,
environmentDefines: environmentDefines, coreTypes: coreTypes);
unreachable_code_elimination.transformComponent(
component, enableAsserts, evaluator);

if (useGlobalTypeFlowAnalysis) {
globalTypeFlow.transformComponent(target, coreTypes, component,
Expand Down
29 changes: 29 additions & 0 deletions pkg/vm/lib/target_os.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2023, 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.

enum TargetOS {
android('android', '/'),
fuchsia('fuchsia', '/'),
iOS('ios', '/'),
linux('linux', '/'),
macOS('macos', '/'),
windows('windows', '\\');

final String name;
final String pathSeparator;

const TargetOS(this.name, this.pathSeparator);

static final Iterable<String> names = values.map((v) => v.name);

static TargetOS? fromString(String s) {
for (final os in values) {
if (os.name == s) return os;
}
return null;
}

@override
String toString() => name;
}
31 changes: 28 additions & 3 deletions pkg/vm/lib/transformations/unreachable_code_elimination.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,38 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:kernel/ast.dart';
import 'package:kernel/type_environment.dart' show StaticTypeContext;

import 'vm_constant_evaluator.dart' show VMConstantEvaluator;

/// Simple unreachable code elimination: removes asserts and if statements
/// with constant conditions. Does a very limited constant folding of
/// logical expressions.
Component transformComponent(Component component, bool enableAsserts) {
new SimpleUnreachableCodeElimination(enableAsserts)
///
/// Also performs some additional constant evaluation via [evaluator], which is
/// applied to certain types of expressions (currently only StaticGet).
Component transformComponent(
Component component, bool enableAsserts, VMConstantEvaluator evaluator) {
SimpleUnreachableCodeElimination(enableAsserts, evaluator)
.visitComponent(component, null);
return component;
}

class SimpleUnreachableCodeElimination extends RemovingTransformer {
final bool enableAsserts;
final VMConstantEvaluator constantEvaluator;
StaticTypeContext? _staticTypeContext;

SimpleUnreachableCodeElimination(this.enableAsserts);
SimpleUnreachableCodeElimination(this.enableAsserts, this.constantEvaluator);

@override
TreeNode defaultMember(Member node, TreeNode? removalSentinel) {
_staticTypeContext =
StaticTypeContext(node, constantEvaluator.typeEnvironment);
final result = super.defaultMember(node, removalSentinel);
_staticTypeContext = null;
return result;
}

bool _isBoolConstant(Expression node) =>
node is BoolLiteral ||
Expand Down Expand Up @@ -115,6 +133,13 @@ class SimpleUnreachableCodeElimination extends RemovingTransformer {
if (target is Field && target.isConst) {
throw 'StaticGet from const field $target should be evaluated by front-end: $node';
}
if (constantEvaluator.transformerShouldEvaluateExpression(node)) {
final context = _staticTypeContext!;
final result = constantEvaluator.evaluate(context, node);
assert(result is! UnevaluatedConstant);
return new ConstantExpression(result, node.getStaticType(context))
..fileOffset = node.fileOffset;
}
return node;
}

Expand Down
Loading

0 comments on commit 2c0484c

Please sign in to comment.