Version 2.13.0-131.0.dev
Merge commit 'f13285482df8f30462410851a6b90f0cb14bb8f3' into 'dev'
diff --git a/WATCHLISTS b/WATCHLISTS
index 195162c..23100a3 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -73,7 +73,6 @@
},
'WATCHLISTS': {
- 'build': [ 'keertip@google.com' ],
'dart2js': [ 'dart2js-team+reviews@google.com' ],
'dartdevc': [ 'dart-dc-team+reviews@google.com' ],
'front_end': [ 'dart-fe-team+reviews@google.com' ],
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
index 25287e5..d8b48dc 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -164,7 +164,9 @@
/// offset is within the given [node], or `null` if the context does not
/// impose any type.
DartType computeContextType(AstNode node, int offset) {
- var type = node.accept(_ContextTypeVisitor(typeProvider, offset));
+ var type = node
+ .accept(_ContextTypeVisitor(typeProvider, offset))
+ ?.resolveToBound(typeProvider.objectType);
if (type == null || type.isDynamic) {
return null;
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
index e3eee98..2ffa0b5 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
@@ -47,7 +47,7 @@
}
// Determine the target expression's type.
- var type = expression.staticType;
+ var type = expression.staticType?.resolveToBound(request.objectType);
if (type == null || type.isDynamic) {
// If the expression does not provide a good type, then attempt to get a
// better type from the element.
diff --git a/pkg/analysis_server/test/completion_test.dart b/pkg/analysis_server/test/completion_test.dart
index 26306df..6a3b7d3 100644
--- a/pkg/analysis_server/test/completion_test.dart
+++ b/pkg/analysis_server/test/completion_test.dart
@@ -2351,9 +2351,7 @@
<String>['1+HttpServer', '1-HttpClient'],
failingTests: '1');
- buildTests(
- 'test038',
- '''
+ buildTests('test038', '''
class X {
x(){}
}
@@ -2367,9 +2365,7 @@
ay.!1y;
az.!2x;
}
-}''',
- <String>['1+y', '1-x', '2+x', '2-y'],
- failingTests: '2');
+}''', <String>['1+y', '1-x', '2+x', '2-y']);
// test analysis of untyped fields and top-level vars
buildTests(
diff --git a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
index 894b3d0..982f671 100644
--- a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
@@ -3067,6 +3067,21 @@
assertNotSuggested('==');
}
+ Future<void> test_methodInvocation_typeParameter() async {
+ addTestSource('''
+class A {
+ void a() {}
+}
+class C<T extends A> {
+ void c(T t) {
+ t.^;
+ }
+}
+''');
+ await computeSuggestions();
+ assertSuggestMethod('a', 'A', 'void');
+ }
+
Future<void> test_mixin() async {
addTestSource(r'''
class A {
diff --git a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
index 2a6276b..c2a5bb6 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
@@ -357,6 +357,31 @@
''', 'String');
}
+ Future<void> test_argumentList_typeParameter_resolved() async {
+ await assertContextType('''
+class A {}
+class B {}
+class C<T extends A> {
+ void m(T t) {}
+}
+void f(C<B> c) {
+ c.m(^);
+}
+''', 'B');
+ }
+
+ Future<void> test_argumentList_typeParameter_unresolved() async {
+ await assertContextType('''
+class A {}
+class C<T extends A> {
+ void m1(T t) {}
+ void m2() {
+ m1(^);
+ }
+}
+''', 'A');
+ }
+
Future<void> test_assertInitializer_with_identifier() async {
await assertContextType('''
class C {
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 498c87c..07ede06b 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -44,12 +44,6 @@
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:meta/meta.dart';
-/// TODO(scheglov) We could use generalized Function in
-/// [AnalysisDriverTestView], but this breaks `AnalysisContext` and code
-/// generation. So, for now let's work around them, and rewrite generators to
-/// [AnalysisDriver].
-typedef WorkToWaitAfterComputingResult = Future<void> Function(String path);
-
/// This class computes [AnalysisResult]s for Dart files.
///
/// Let the set of "explicitly analyzed files" denote the set of paths that have
@@ -528,7 +522,7 @@
}
_discoverAvailableFiles();
_scheduler.notify(this);
- return _discoverAvailableFilesTask!.completer!.future;
+ return _discoverAvailableFilesTask!.completer.future;
}
@override
@@ -2214,28 +2208,31 @@
final AnalysisDriver driver;
- bool isCompleted = false;
- Completer<void>? completer = Completer<void>();
+ final Completer<void> completer = Completer<void>();
Iterator<Folder>? folderIterator;
- List<String>? files = [];
+
+ final List<String> files = [];
+
int fileIndex = 0;
_DiscoverAvailableFilesTask(this.driver);
+ bool get isCompleted => completer.isCompleted;
+
/// Perform the next piece of work, and set [isCompleted] to `true` to
/// indicate that the task is done, or keeps it `false` to indicate that the
/// task should continue to be run.
void perform() {
if (folderIterator == null) {
- files!.addAll(driver.addedFiles);
+ files.addAll(driver.addedFiles);
// Discover SDK libraries.
var dartSdk = driver._sourceFactory.dartSdk;
if (dartSdk != null) {
for (var sdkLibrary in dartSdk.sdkLibraries) {
var file = dartSdk.mapDartUri(sdkLibrary.shortName)!.fullName;
- files!.add(file);
+ files.add(file);
}
}
@@ -2262,22 +2259,18 @@
}
// Get know files one by one.
- while (fileIndex < files!.length) {
+ while (fileIndex < files.length) {
if (timer.elapsedMilliseconds > _MS_WORK_INTERVAL) {
return;
}
- var file = files![fileIndex++];
+ var file = files[fileIndex++];
driver._fsState.getFileForPath(file);
}
// The task is done, clean up.
folderIterator = null;
- files = null;
-
- // Complete and clean up.
- isCompleted = true;
- completer!.complete();
- completer = null;
+ files.clear();
+ completer.complete();
}
void _appendFilesRecursively(Folder folder) {
@@ -2287,7 +2280,7 @@
if (child is File) {
var path = child.path;
if (file_paths.isDart(pathContext, path)) {
- files!.add(path);
+ files.add(path);
}
} else if (child is Folder) {
_appendFilesRecursively(child);
diff --git a/pkg/analyzer/lib/src/dart/ast/extensions.dart b/pkg/analyzer/lib/src/dart/ast/extensions.dart
index 7451b12..03225d1 100644
--- a/pkg/analyzer/lib/src/dart/ast/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/ast/extensions.dart
@@ -143,3 +143,18 @@
return cast<FormalParameterImpl>();
}
}
+
+extension TypeAnnotationExtension on TypeAnnotation {
+ /// Return the static type of this type annotation.
+ ///
+ /// This accessor should be used on expressions that are expected to
+ /// be already resolved. Every such expression must have the type set,
+ /// at least `dynamic`.
+ DartType get typeOrThrow {
+ var type = this.type;
+ if (type == null) {
+ throw StateError('No type: $this');
+ }
+ return type;
+ }
+}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index e0f5421..5413fb9 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -17,6 +17,7 @@
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/constant/compute.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
import 'package:analyzer/src/dart/constant/value.dart';
@@ -3873,7 +3874,7 @@
if (linkedNode != null) {
var linkedNode = this.linkedNode as ExtensionDeclaration;
linkedContext!.applyResolution(linkedNode);
- return _extendedType = linkedNode.extendedType.type!;
+ return _extendedType = linkedNode.extendedType.typeOrThrow;
}
return _extendedType!;
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index 25bd1d7..93cc5b6 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -199,7 +199,7 @@
if (typeArguments != null) {
var arguments = typeArguments.arguments;
if (arguments.length == typeParameters.length) {
- typeArgumentTypes = arguments.map((a) => a.type!).toList();
+ typeArgumentTypes = arguments.map((a) => a.typeOrThrow).toList();
} else {
typeArgumentTypes = _listOfDynamic(typeParameters);
}
@@ -392,7 +392,7 @@
if (typeParameters.isEmpty) {
return const <DartType>[];
}
- return arguments.map((a) => a.type!).toList();
+ return arguments.map((a) => a.typeOrThrow).toList();
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_EXTENSION,
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 658adba..2478a00 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -9,6 +9,7 @@
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
import 'package:analyzer/src/generated/migration.dart';
@@ -86,7 +87,7 @@
var expression = node.expression;
var typeAnnotation = node.type;
- flow!.asExpression_end(expression, typeAnnotation.type!);
+ flow!.asExpression_end(expression, typeAnnotation.typeOrThrow);
}
void assignmentExpression(AssignmentExpression node) {
@@ -200,7 +201,7 @@
node,
expression,
node.notOperator != null,
- typeAnnotation.type!,
+ typeAnnotation.typeOrThrow,
);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
index 4dfeb7d..24ce9b9 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
@@ -7,6 +7,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
@@ -153,7 +154,7 @@
var typeArguments = node.typeArguments;
if (typeArguments != null) {
node.typeArgumentTypes = typeArguments.arguments
- .map((typeArgument) => typeArgument.type!)
+ .map((typeArgument) => typeArgument.typeOrThrow)
.toList();
} else {
node.typeArgumentTypes = const <DartType>[];
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
index 0b960b5..8239ccd 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -144,7 +144,8 @@
// for FunctionExpressionInvocation(s), so set it here, if not inferred.
if (node is FunctionExpressionInvocationImpl) {
if (typeArguments != null) {
- var typeArgs = typeArguments.arguments.map((n) => n.type!).toList();
+ var typeArgs =
+ typeArguments.arguments.map((n) => n.typeOrThrow).toList();
node.typeArgumentTypes = typeArgs;
} else {
node.typeArgumentTypes = const <DartType>[];
@@ -420,7 +421,7 @@
);
} else {
typeArguments = typeArgumentList.arguments
- .map((typeArgument) => typeArgument.type!)
+ .map((typeArgument) => typeArgument.typeOrThrow)
.toList(growable: true);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index 77429cb..c2d6338 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -789,7 +789,8 @@
var typeArgumentList = _invocation!.typeArguments;
if (typeArgumentList != null) {
var arguments = typeArgumentList.arguments;
- _invocation!.typeArgumentTypes = arguments.map((n) => n.type!).toList();
+ _invocation!.typeArgumentTypes =
+ arguments.map((n) => n.typeOrThrow).toList();
} else {
_invocation!.typeArgumentTypes = [];
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index 49c88e6..666cce9 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -13,6 +13,7 @@
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/scope.dart';
import 'package:analyzer/src/dart/element/type.dart';
@@ -180,7 +181,7 @@
element.type = type;
exceptionNode.staticType = type;
} else {
- element.type = exceptionTypeNode.type!;
+ element.type = exceptionTypeNode.typeOrThrow;
exceptionNode.staticType = exceptionTypeNode.type;
}
@@ -321,7 +322,7 @@
element.type = _dynamicType;
} else {
node.type!.accept(this);
- element.type = node.type!.type!;
+ element.type = node.type!.typeOrThrow;
}
_setCodeRange(element, node);
@@ -1192,7 +1193,7 @@
visitTypeName(typeName);
_typeNameResolver.classHierarchy_typeName = null;
- DartType type = typeName.type!;
+ DartType type = typeName.typeOrThrow;
if (type is InterfaceType) {
ClassElement element = type.element;
if (element.isEnum || element.isMixin && asClass) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
index 9c2328a..c4c1dfe 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
@@ -11,6 +11,7 @@
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
@@ -140,7 +141,7 @@
return List.generate(
parameterCount,
- (i) => arguments[i].type!,
+ (i) => arguments[i].typeOrThrow,
);
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
index 624a27b..ee009b4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
@@ -79,7 +79,7 @@
var typeArguments = node.typeArguments?.arguments;
if (typeArguments != null) {
if (typeArguments.length == 1) {
- DartType elementType = typeArguments[0].type!;
+ DartType elementType = typeArguments[0].typeOrThrow;
if (!elementType.isDynamic) {
listType = _typeProvider.listType(elementType);
}
@@ -109,7 +109,7 @@
var literalResolution = _computeSetOrMapResolution(node);
if (literalResolution.kind == _LiteralResolutionKind.set) {
if (typeArguments != null && typeArguments.length == 1) {
- var elementType = typeArguments[0].type!;
+ var elementType = typeArguments[0].typeOrThrow;
literalType = _typeProvider.setType(elementType);
} else {
literalType =
@@ -117,8 +117,8 @@
}
} else if (literalResolution.kind == _LiteralResolutionKind.map) {
if (typeArguments != null && typeArguments.length == 2) {
- var keyType = typeArguments[0].type!;
- var valueType = typeArguments[1].type!;
+ var keyType = typeArguments[0].typeOrThrow;
+ var valueType = typeArguments[1].typeOrThrow;
literalType = _typeProvider.mapType(keyType, valueType);
} else {
literalType =
@@ -313,10 +313,12 @@
if (arguments != null) {
if (arguments.length == 1) {
return _LiteralResolution(_LiteralResolutionKind.set,
- _typeProvider.setType(arguments[0].type!));
+ _typeProvider.setType(arguments[0].typeOrThrow));
} else if (arguments.length == 2) {
- return _LiteralResolution(_LiteralResolutionKind.map,
- _typeProvider.mapType(arguments[0].type!, arguments[1].type!));
+ return _LiteralResolution(
+ _LiteralResolutionKind.map,
+ _typeProvider.mapType(
+ arguments[0].typeOrThrow, arguments[1].typeOrThrow));
}
}
return _LiteralResolution(_LiteralResolutionKind.ambiguous, null);
@@ -669,7 +671,7 @@
if (typeArguments != null) {
DartType elementType = _dynamicType;
if (typeArguments.length == 1) {
- elementType = typeArguments[0].type!;
+ elementType = typeArguments[0].typeOrThrow;
}
_recordStaticType(
node,
@@ -706,7 +708,7 @@
if (typeArguments != null) {
if (typeArguments.length == 1) {
node.becomeSet();
- var elementType = typeArguments[0].type!;
+ var elementType = typeArguments[0].typeOrThrow;
_recordStaticType(
node,
_typeProvider.setElement.instantiate(
@@ -717,8 +719,8 @@
return;
} else if (typeArguments.length == 2) {
node.becomeMap();
- var keyType = typeArguments[0].type!;
- var valueType = typeArguments[1].type!;
+ var keyType = typeArguments[0].typeOrThrow;
+ var valueType = typeArguments[1].typeOrThrow;
_recordStaticType(
node,
_typeProvider.mapElement.instantiate(
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index e68cf25..388384f 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -1207,7 +1207,7 @@
return;
}
- if (_typeSystem.isPotentiallyNullable(type.type!)) {
+ if (_typeSystem.isPotentiallyNullable(type.typeOrThrow)) {
_errorReporter.reportErrorForNode(
HintCode.NULLABLE_TYPE_IN_CATCH_CLAUSE,
type,
@@ -1596,7 +1596,7 @@
/// on [node]. See [HintCode.UNNECESSARY_CAST].
static bool isUnnecessaryCast(AsExpression node, TypeSystemImpl typeSystem) {
var leftType = node.expression.typeOrThrow;
- var rightType = node.type.type!;
+ var rightType = node.type.typeOrThrow;
// `dynamicValue as SomeType` is a valid use case.
if (leftType.isDynamic) {
diff --git a/pkg/analyzer/lib/src/error/inheritance_override.dart b/pkg/analyzer/lib/src/error/inheritance_override.dart
index 4b4e865..e9fbe20 100644
--- a/pkg/analyzer/lib/src/error/inheritance_override.dart
+++ b/pkg/analyzer/lib/src/error/inheritance_override.dart
@@ -10,6 +10,7 @@
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
@@ -330,7 +331,7 @@
return false;
}
- DartType type = typeName.type!;
+ DartType type = typeName.typeOrThrow;
if (type is InterfaceType &&
typeProvider.nonSubtypableClasses.contains(type.element)) {
reporter.reportErrorForNode(errorCode, typeName, [type]);
diff --git a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
index 6d4276d..9e5545c 100644
--- a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
+++ b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
@@ -175,7 +175,7 @@
return;
}
if (_isMissingTypeArguments(
- node, node.type!, node.name.staticElement, null)) {
+ node, node.typeOrThrow, node.name.staticElement, null)) {
AstNode unwrappedParent = parentEscapingTypeArguments(node);
if (unwrappedParent is AsExpression || unwrappedParent is IsExpression) {
// Do not report a "Strict raw type" error in this case; too noisy.
@@ -190,7 +190,7 @@
/// Verify that the type arguments in the given [typeName] are all within
/// their bounds.
void _checkForTypeArgumentNotMatchingBounds(TypeName typeName) {
- var type = typeName.type!;
+ var type = typeName.typeOrThrow;
List<TypeParameterElement> typeParameters;
List<DartType> typeArguments;
@@ -336,7 +336,7 @@
var instantiatedType = node.staticInvokeType;
if (genericType is FunctionType && instantiatedType is FunctionType) {
var fnTypeParams = genericType.typeFormals;
- var typeArgs = typeArgumentList.map((t) => t.type!).toList();
+ var typeArgs = typeArgumentList.map((t) => t.typeOrThrow).toList();
// If the amount mismatches, clean up the lists to be substitutable. The
// mismatch in size is reported elsewhere, but we must successfully
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 7b163a73..921c948 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/resolver/method_invocation_resolver.dart';
@@ -245,7 +246,7 @@
@override
void visitConstructorName(covariant ConstructorNameImpl node) {
- DartType type = node.type.type!;
+ DartType type = node.type.typeOrThrow;
if (type.isDynamic) {
// Nothing to do.
} else if (type is InterfaceType) {
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 4a304c1..071f068 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -870,7 +870,7 @@
void visitInstanceCreationExpression(InstanceCreationExpression node) {
ConstructorName constructorName = node.constructorName;
TypeName typeName = constructorName.type;
- DartType type = typeName.type!;
+ DartType type = typeName.typeOrThrow;
if (type is InterfaceType) {
_checkForConstOrNewWithAbstractClass(node, typeName, type);
_checkForConstOrNewWithEnum(node, typeName, type);
@@ -1400,7 +1400,7 @@
mixinNameIndex < withClause.mixinTypes.length;
mixinNameIndex++) {
TypeName mixinName = withClause.mixinTypes[mixinNameIndex];
- DartType mixinType = mixinName.type!;
+ DartType mixinType = mixinName.typeOrThrow;
if (mixinType is InterfaceType) {
mixinTypeIndex++;
if (_checkForExtendsOrImplementsDisallowedClass(
@@ -1450,7 +1450,7 @@
// If the element is null, we check for the
// REDIRECT_TO_MISSING_CONSTRUCTOR case
TypeName constructorTypeName = redirectedConstructor.type;
- DartType redirectedType = constructorTypeName.type!;
+ DartType redirectedType = constructorTypeName.typeOrThrow;
if (redirectedType.element != null && !redirectedType.isDynamic) {
// Prepare the constructor name
String constructorStrName = constructorTypeName.name.name;
@@ -2199,7 +2199,7 @@
if (constructorName.staticElement != null) {
return;
}
- DartType type = typeName.type!;
+ DartType type = typeName.typeOrThrow;
if (type is InterfaceType) {
ClassElement element = type.element;
if (element.isEnum) {
@@ -2492,7 +2492,8 @@
return false;
}
return typeName.type is InterfaceType &&
- _typeProvider.nonSubtypableClasses.contains(typeName.type!.element);
+ _typeProvider.nonSubtypableClasses
+ .contains(typeName.typeOrThrow.element);
}
void _checkForExtensionDeclaresMemberOfObject(MethodDeclaration node) {
@@ -2687,7 +2688,7 @@
if (_featureSet?.isEnabled(Feature.generic_metadata) ?? false) {
return;
}
- DartType type = node.type!;
+ DartType type = node.typeOrThrow;
if (type is FunctionType && type.typeFormals.isNotEmpty) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
@@ -2758,7 +2759,7 @@
(node is TypeName && node.typeArguments != null)) {
return;
}
- DartType type = node.type!;
+ DartType type = node.typeOrThrow;
if (type is ParameterizedType &&
type.typeArguments.isNotEmpty &&
type.typeArguments.any((t) => t.isDynamic)) {
@@ -3419,7 +3420,7 @@
}
for (TypeName mixinType in withClause.mixinTypes) {
- DartType type = mixinType.type!;
+ DartType type = mixinType.typeOrThrow;
if (type is InterfaceType) {
LibraryElement library = type.element.library;
if (library != _currentLibrary) {
@@ -3486,7 +3487,7 @@
if (constructorName.staticElement != null) {
return;
}
- DartType type = typeName.type!;
+ DartType type = typeName.typeOrThrow;
if (type is InterfaceType) {
ClassElement element = type.element;
if (element.isEnum || element.isMixin) {
@@ -3632,7 +3633,7 @@
// check return type
var annotation = declaration.returnType;
if (annotation != null) {
- DartType type = annotation.type!;
+ DartType type = annotation.typeOrThrow;
if (!type.isVoid) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.NON_VOID_RETURN_FOR_OPERATOR, annotation);
@@ -3646,7 +3647,7 @@
/// See [StaticWarningCode.NON_VOID_RETURN_FOR_SETTER].
void _checkForNonVoidReturnTypeForSetter(TypeAnnotation? typeName) {
if (typeName != null) {
- DartType type = typeName.type!;
+ DartType type = typeName.typeOrThrow;
if (!type.isVoid) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.NON_VOID_RETURN_FOR_SETTER, typeName);
@@ -3720,7 +3721,7 @@
if (node.type == null) {
return;
}
- var type = node.type!.type!;
+ var type = node.type!.typeOrThrow;
if (!_typeSystem.isPotentiallyNonNullable(type)) {
return;
@@ -3747,7 +3748,7 @@
}
bool problemReported = false;
for (TypeName typeName in onClause.superclassConstraints) {
- DartType type = typeName.type!;
+ DartType type = typeName.typeOrThrow;
if (type is InterfaceType) {
if (_checkForExtendsOrImplementsDisallowedClass(
typeName,
@@ -4723,7 +4724,7 @@
continue;
}
var methodTypeParameterVariance = Variance.invariant.combine(
- Variance(typeParameter, methodTypeParameter.bound!.type!),
+ Variance(typeParameter, methodTypeParameter.bound!.typeOrThrow),
);
_checkForWrongVariancePosition(
methodTypeParameterVariance, typeParameter, methodTypeParameter);
@@ -4748,7 +4749,7 @@
var returnType = method.returnType;
if (returnType != null) {
var methodReturnTypeVariance =
- Variance(typeParameter, returnType.type!);
+ Variance(typeParameter, returnType.typeOrThrow);
_checkForWrongVariancePosition(
methodReturnTypeVariance, typeParameter, returnType);
}
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 99f293a..d65781b 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -546,7 +546,7 @@
_errorReporter.reportErrorForNode(
FfiCode.MISSING_FIELD_TYPE_IN_STRUCT, fields.variables[0].name);
} else {
- DartType declaredType = fieldType.type!;
+ DartType declaredType = fieldType.typeOrThrow;
if (declaredType.isDartCoreInt) {
_validateAnnotations(fieldType, annotations, _PrimitiveDartType.int);
} else if (declaredType.isDartCoreDouble) {
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 45632ba..fb518cd 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -198,7 +198,7 @@
void visitInstanceCreationExpression(
covariant InstanceCreationExpressionImpl node) {
_inferInstanceCreationExpression(node);
- recordStaticType(node, node.constructorName.type.type!);
+ recordStaticType(node, node.constructorName.type.typeOrThrow);
}
/// <blockquote>
diff --git a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
index de3e0a0..965b30f 100644
--- a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
+++ b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
@@ -259,7 +259,7 @@
}
} else if (condition is IsExpression) {
if (condition.notOperator == null) {
- _promote(condition.expression, condition.type.type!);
+ _promote(condition.expression, condition.type.typeOrThrow);
}
} else if (condition is ParenthesizedExpression) {
_promoteTypes(condition.expression);
diff --git a/pkg/analyzer/lib/src/summary2/default_types_builder.dart b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
index 4401ee5..6138667 100644
--- a/pkg/analyzer/lib/src/summary2/default_types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
@@ -6,6 +6,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/replacement_visitor.dart';
import 'package:analyzer/src/dart/element/type.dart';
@@ -73,7 +74,7 @@
var cycles = _findRawTypePathsToDeclaration(
parameter,
- boundNode.type!,
+ boundNode.typeOrThrow,
declarationElement,
Set<Element>.identity(),
);
@@ -244,7 +245,7 @@
if (bound != null) {
var tails = _findRawTypePathsToDeclaration(
parameterNode,
- bound.type!,
+ bound.typeOrThrow,
end,
visited,
);
diff --git a/pkg/analyzer/lib/src/summary2/function_type_builder.dart b/pkg/analyzer/lib/src/summary2/function_type_builder.dart
index bdbc0ba..faeefe2 100644
--- a/pkg/analyzer/lib/src/summary2/function_type_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/function_type_builder.dart
@@ -168,7 +168,7 @@
if (node == null) {
return _dynamicType;
} else {
- return node.type!;
+ return node.typeOrThrow;
}
}
diff --git a/pkg/analyzer/lib/src/summary2/named_type_builder.dart b/pkg/analyzer/lib/src/summary2/named_type_builder.dart
index 47ea3f0..56056ce 100644
--- a/pkg/analyzer/lib/src/summary2/named_type_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/named_type_builder.dart
@@ -57,7 +57,7 @@
List<DartType> arguments;
var argumentList = node.typeArguments;
if (argumentList != null) {
- arguments = argumentList.arguments.map((n) => n.type!).toList();
+ arguments = argumentList.arguments.map((n) => n.typeOrThrow).toList();
} else {
arguments = <DartType>[];
}
@@ -154,13 +154,13 @@
DartType _buildAliasedType(TypeAnnotation? node) {
if (_isNonFunctionTypeAliasesEnabled) {
if (node != null) {
- return _buildType(node.type!);
+ return _buildType(node.typeOrThrow);
} else {
return _dynamicType;
}
} else {
if (node is GenericFunctionType) {
- return _buildType(node.type!);
+ return _buildType(node.typeOrThrow);
} else {
return FunctionTypeImpl(
typeFormals: const <TypeParameterElement>[],
@@ -233,7 +233,7 @@
if (node == null) {
return _dynamicType;
} else {
- return _buildType(node.type!);
+ return _buildType(node.typeOrThrow);
}
}
diff --git a/pkg/analyzer/lib/src/summary2/types_builder.dart b/pkg/analyzer/lib/src/summary2/types_builder.dart
index f049832d..af20893 100644
--- a/pkg/analyzer/lib/src/summary2/types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/types_builder.dart
@@ -264,7 +264,7 @@
}
InterfaceType _inferSingle(TypeNameImpl mixinNode) {
- var mixinType = _interfaceType(mixinNode.type!);
+ var mixinType = _interfaceType(mixinNode.typeOrThrow);
if (mixinNode.typeArguments != null) {
return mixinType;
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 0c4053d..02e11bc 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -376,7 +376,7 @@
if (typeArgumentList != null) {
var typeArguments = typeArgumentList.arguments;
if (typeArguments.isNotEmpty) {
- type = typeArguments[0].type!;
+ type = typeArguments[0].typeOrThrow;
}
} else {
DartType staticType = node.typeOrThrow;
@@ -447,10 +447,10 @@
if (typeArgumentsList != null) {
NodeList<TypeAnnotation> typeArguments = typeArgumentsList.arguments;
if (typeArguments.isNotEmpty) {
- keyType = typeArguments[0].type!;
+ keyType = typeArguments[0].typeOrThrow;
}
if (typeArguments.length > 1) {
- valueType = typeArguments[1].type!;
+ valueType = typeArguments[1].typeOrThrow;
}
} else {
DartType staticType = node.typeOrThrow;
@@ -467,7 +467,7 @@
if (typeArgumentsList != null) {
NodeList<TypeAnnotation> typeArguments = typeArgumentsList.arguments;
if (typeArguments.isNotEmpty) {
- type = typeArguments[0].type!;
+ type = typeArguments[0].typeOrThrow;
}
} else {
DartType staticType = node.typeOrThrow;
@@ -525,7 +525,7 @@
var initializer = variable.initializer;
if (initializer != null) {
checkForCast(initializer,
- from: initializer.typeOrThrow, to: type.type!);
+ from: initializer.typeOrThrow, to: type.typeOrThrow);
}
}
}
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 9a88a32..aa67fe2 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -372,7 +372,7 @@
var exp = stmt.expression as InstanceCreationExpression;
ClassElement elementB = AstFinder.getClass(unit, "B").declaredElement!;
ClassElement elementA = AstFinder.getClass(unit, "A").declaredElement!;
- expect(exp.constructorName.type.type!.element, elementB);
+ expect(exp.constructorName.type.typeOrThrow.element, elementB);
_isInstantiationOf(_hasElement(elementB))([
_isType(elementA.typeParameters[0]
.instantiate(nullabilitySuffix: NullabilitySuffix.star))
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index e4a7455..9ddda1e 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -13,6 +13,7 @@
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/dart/analysis/status.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/error/codes.dart';
@@ -1404,7 +1405,7 @@
var f = result.unit!.declarations[0] as FunctionDeclaration;
assertType(f.declaredElement!.type, 'int Function()');
- assertType(f.returnType!.type!, 'int');
+ assertType(f.returnType!.typeOrThrow, 'int');
// The same result is also received through the stream.
await waitForIdleWithoutExceptions();
@@ -3131,7 +3132,7 @@
var f = result.unit!.declarations[0] as FunctionDeclaration;
assertType(f.declaredElement!.type, 'int Function()');
- assertType(f.returnType!.type!, 'int');
+ assertType(f.returnType!.typeOrThrow, 'int');
}
test_results_priorityFirst() async {
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index 89086df..1e0723b 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -2,6 +2,7 @@
// 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.
+import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/test_utilities/find_element.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -615,7 +616,7 @@
assertTypeName(typeName, element, 'int* Function(bool*)*');
assertTypeAlias(
- typeName.type!,
+ typeName.typeOrThrow,
element: element,
typeArguments: [],
);
@@ -639,7 +640,7 @@
assertTypeName(typeName, element, 'dynamic Function(bool*)*');
assertTypeAlias(
- typeName.type!,
+ typeName.typeOrThrow,
element: element,
typeArguments: ['dynamic'],
);
@@ -663,7 +664,7 @@
assertTypeName(typeName, element, 'num* Function(bool*)*');
assertTypeAlias(
- typeName.type!,
+ typeName.typeOrThrow,
element: element,
typeArguments: ['num*'],
);
@@ -687,7 +688,7 @@
assertTypeName(typeName, element, 'int* Function(bool*)*');
assertTypeAlias(
- typeName.type!,
+ typeName.typeOrThrow,
element: element,
typeArguments: ['int*'],
);
diff --git a/pkg/dartdev/lib/src/analysis_server.dart b/pkg/dartdev/lib/src/analysis_server.dart
index 0f616f8..4102f12 100644
--- a/pkg/dartdev/lib/src/analysis_server.dart
+++ b/pkg/dartdev/lib/src/analysis_server.dart
@@ -16,10 +16,10 @@
/// A class to provide an API wrapper around an analysis server process.
class AnalysisServer {
- AnalysisServer(this.sdkPath, this.directory);
+ AnalysisServer(this.sdkPath, this.analysisRoot);
final Directory sdkPath;
- final Directory directory;
+ final FileSystemEntity analysisRoot;
Process _process;
@@ -98,8 +98,8 @@
//
// The call to absolute.resolveSymbolicLinksSync() canonicalizes the path to
// be passed to the analysis server.
- var dirPath = trimEnd(
- directory.absolute.resolveSymbolicLinksSync(),
+ var analysisRootPath = trimEnd(
+ analysisRoot.absolute.resolveSymbolicLinksSync(),
path.context.separator,
);
@@ -115,7 +115,7 @@
// ignore: unawaited_futures
_sendCommand('analysis.setAnalysisRoots', params: <String, dynamic>{
- 'included': [dirPath],
+ 'included': [analysisRootPath],
'excluded': <String>[]
});
}
diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart
index cbb4270..4bdcd62 100644
--- a/pkg/dartdev/lib/src/commands/analyze.dart
+++ b/pkg/dartdev/lib/src/commands/analyze.dart
@@ -68,15 +68,26 @@
@override
FutureOr<int> run() async {
if (argResults.rest.length > 1) {
- usageException('Only one directory is expected.');
+ usageException('Only one directory or file is expected.');
}
- // find directory from argResults.rest
- var dir = argResults.rest.isEmpty
- ? io.Directory.current
- : io.Directory(argResults.rest.single);
- if (!dir.existsSync()) {
- usageException("Directory doesn't exist: ${dir.path}");
+ // find target from argResults.rest
+ io.FileSystemEntity target;
+ io.Directory relativeToDir;
+ if (argResults.rest.isEmpty) {
+ target = io.Directory.current;
+ relativeToDir = target;
+ } else {
+ var targetPath = argResults.rest.single;
+ if (io.Directory(targetPath).existsSync()) {
+ target = io.Directory(targetPath);
+ relativeToDir = target;
+ } else if (io.File(targetPath).existsSync()) {
+ target = io.File(targetPath);
+ relativeToDir = target.parent;
+ } else {
+ usageException("Directory or file doesn't exist: $targetPath");
+ }
}
final List<AnalysisError> errors = <AnalysisError>[];
@@ -85,11 +96,11 @@
var progress = machineFormat
? null
- : log.progress('Analyzing ${path.basename(dir.path)}');
+ : log.progress('Analyzing ${path.basename(target.path)}');
final AnalysisServer server = AnalysisServer(
io.Directory(sdk.sdkPath),
- dir,
+ target,
);
server.onErrors.listen((FileAnalysisErrors fileErrors) {
@@ -128,7 +139,8 @@
if (machineFormat) {
emitMachineFormat(log, errors);
} else {
- emitDefaultFormat(log, errors, relativeToDir: dir, verbose: verbose);
+ emitDefaultFormat(log, errors,
+ relativeToDir: relativeToDir, verbose: verbose);
}
bool hasErrors = false;
@@ -180,7 +192,7 @@
if (error.isError) {
severity = ansi.error(severity);
}
- var filePath = _relativePath(error.file, relativeToDir?.path);
+ var filePath = _relativePath(error.file, relativeToDir);
var codeRef = error.code;
// If we're in verbose mode, write any error urls instead of error codes.
if (error.url != null && verbose) {
@@ -207,7 +219,7 @@
// Add any context messages as bullet list items.
for (var message in error.contextMessages) {
- var contextPath = _relativePath(error.file, relativeToDir?.path);
+ var contextPath = _relativePath(error.file, relativeToDir);
var messageSentenceFragment = trimEnd(message.message, '.');
log.stdout('$_bodyIndent'
@@ -222,8 +234,9 @@
log.stdout('$errorCount ${pluralize('issue', errorCount)} found.');
}
- /// Return a relative path if it is a shorter reference than the given path.
- static String _relativePath(String givenPath, String fromPath) {
+ /// Return a relative path if it is a shorter reference than the given dir.
+ static String _relativePath(String givenPath, io.Directory fromDir) {
+ String fromPath = fromDir?.absolute?.resolveSymbolicLinksSync();
String relative = path.relative(givenPath, from: fromPath);
return relative.length <= givenPath.length ? relative : givenPath;
}
diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart
index 6d0709d..8a23601 100644
--- a/pkg/dartdev/test/commands/analyze_test.dart
+++ b/pkg/dartdev/test/commands/analyze_test.dart
@@ -78,7 +78,7 @@
expect(result.exitCode, 64);
expect(result.stdout, isEmpty);
- expect(result.stderr, contains('Only one directory is expected.'));
+ expect(result.stderr, contains('Only one directory or file is expected.'));
expect(result.stderr, contains(_analyzeUsageText));
});
@@ -88,7 +88,8 @@
expect(result.exitCode, 64);
expect(result.stdout, isEmpty);
- expect(result.stderr, contains("Directory doesn't exist: /no/such/dir1/"));
+ expect(result.stderr,
+ contains("Directory or file doesn't exist: /no/such/dir1/"));
expect(result.stderr, contains(_analyzeUsageText));
});
@@ -102,34 +103,59 @@
expect(result.stdout, contains('No issues found!'));
});
- test('no errors', () {
- p = project(mainSrc: 'int get foo => 1;\n');
- var result = p.runSync(['analyze', p.dirPath]);
+ group('single directory', () {
+ test('no errors', () {
+ p = project(mainSrc: 'int get foo => 1;\n');
+ var result = p.runSync(['analyze', p.dirPath]);
- expect(result.exitCode, 0);
- expect(result.stderr, isEmpty);
- expect(result.stdout, contains('No issues found!'));
+ expect(result.exitCode, 0);
+ expect(result.stderr, isEmpty);
+ expect(result.stdout, contains('No issues found!'));
+ });
+
+ test('one error', () {
+ p = project(mainSrc: "int get foo => 'str';\n");
+ var result = p.runSync(['analyze', p.dirPath]);
+
+ expect(result.exitCode, 3);
+ expect(result.stderr, isEmpty);
+ expect(result.stdout, contains('A value of type '));
+ expect(result.stdout, contains('lib/main.dart:1:16 '));
+ expect(result.stdout, contains('return_of_invalid_type'));
+ expect(result.stdout, contains('1 issue found.'));
+ });
+
+ test('two errors', () {
+ p = project(mainSrc: "int get foo => 'str';\nint get bar => 'str';\n");
+ var result = p.runSync(['analyze', p.dirPath]);
+
+ expect(result.exitCode, 3);
+ expect(result.stderr, isEmpty);
+ expect(result.stdout, contains('2 issues found.'));
+ });
});
- test('one error', () {
- p = project(mainSrc: "int get foo => 'str';\n");
- var result = p.runSync(['analyze', p.dirPath]);
+ group('single file', () {
+ test('no errors', () {
+ p = project(mainSrc: 'int get foo => 1;\n');
+ var result = p.runSync(['analyze', p.mainPath]);
- expect(result.exitCode, 3);
- expect(result.stderr, isEmpty);
- expect(result.stdout, contains('A value of type '));
- expect(result.stdout, contains('lib/main.dart:1:16 '));
- expect(result.stdout, contains('return_of_invalid_type'));
- expect(result.stdout, contains('1 issue found.'));
- });
+ expect(result.exitCode, 0);
+ expect(result.stderr, isEmpty);
+ expect(result.stdout, contains('No issues found!'));
+ });
- test('two errors', () {
- p = project(mainSrc: "int get foo => 'str';\nint get bar => 'str';\n");
- var result = p.runSync(['analyze', p.dirPath]);
+ test('one error', () {
+ p = project(mainSrc: "int get foo => 'str';\n");
+ var result = p.runSync(['analyze', p.mainPath]);
- expect(result.exitCode, 3);
- expect(result.stderr, isEmpty);
- expect(result.stdout, contains('2 issues found.'));
+ expect(result.exitCode, 3);
+ expect(result.stderr, isEmpty);
+ expect(result.stdout, contains('A value of type '));
+ expect(result.stdout, contains('main.dart:1:16 '));
+ expect(result.stdout, contains('return_of_invalid_type'));
+ expect(result.stdout, contains('1 issue found.'));
+ });
});
test('warning --fatal-warnings', () {
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index 117aec2..1f3db17 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -38,6 +38,8 @@
String get dirPath => dir.path;
+ String get mainPath => path.join(dirPath, relativeFilePath);
+
final String name;
String get relativeFilePath => 'lib/main.dart';
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 2fba597..1089515 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -543,7 +543,7 @@
if (node.function != null) {
node.function = transform(node.function)..parent = node;
}
- constantEvaluator.env.updateVariableValue(
+ constantEvaluator.env.addVariableValue(
node.variable, new IntermediateValue(node.function));
} else {
return super.visitFunctionDeclaration(node, removalSentinel);
@@ -559,7 +559,7 @@
if (node.initializer != null) {
if (node.isConst) {
final Constant constant = evaluateWithContext(node, node.initializer);
- constantEvaluator.env.updateVariableValue(node, constant);
+ constantEvaluator.env.addVariableValue(node, constant);
node.initializer = makeConstantExpression(constant, node.initializer)
..parent = node;
@@ -1669,14 +1669,14 @@
// TODO(johnniwinther): This should call [_evaluateSubexpression].
: _evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
for (final VariableDeclaration parameter in function.namedParameters) {
final Constant value = namedArguments[parameter.name] ??
// TODO(johnniwinther): This should call [_evaluateSubexpression].
_evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
// Step 2) Run all initializers (including super calls) with environment
@@ -1697,7 +1697,7 @@
final VariableDeclaration variable = init.variable;
Constant constant = _evaluateSubexpression(variable.initializer);
if (constant is AbortConstant) return constant;
- env.updateVariableValue(variable, constant);
+ env.addVariableValue(variable, constant);
} else if (init is SuperInitializer) {
AbortConstant error = checkConstructorConst(init, constructor);
if (error != null) return error;
@@ -2412,7 +2412,7 @@
Constant visitLet(Let node) {
Constant value = _evaluateSubexpression(node.variable.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(node.variable, value);
+ env.addVariableValue(node.variable, value);
return _evaluateSubexpression(node.body);
}
@@ -2445,6 +2445,19 @@
node, 'Variable get of a non-const variable.');
}
+ @override
+ Constant visitVariableSet(VariableSet node) {
+ if (enableConstFunctions) {
+ final VariableDeclaration variable = node.variable;
+ Constant value = _evaluateSubexpression(node.value);
+ if (value is AbortConstant) return value;
+ return env.updateVariableValue(variable, value) ??
+ createInvalidExpressionConstant(
+ node, 'Variable set of an unknown value.');
+ }
+ return defaultExpression(node);
+ }
+
/// Computes the constant for [expression] defined in the context of [member].
///
/// This compute the constant as seen in the current evaluation mode even when
@@ -2772,14 +2785,14 @@
// TODO(johnniwinther): This should call [_evaluateSubexpression].
: _evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
for (final VariableDeclaration parameter in function.namedParameters) {
final Constant value = namedArguments[parameter.name] ??
// TODO(johnniwinther): This should call [_evaluateSubexpression].
_evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
return execute(function.body);
});
@@ -3295,9 +3308,6 @@
@override
Constant visitThrow(Throw node) => defaultExpression(node);
-
- @override
- Constant visitVariableSet(VariableSet node) => defaultExpression(node);
}
class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
@@ -3330,6 +3340,13 @@
}
@override
+ ExecutionStatus visitExpressionStatement(ExpressionStatement node) {
+ Constant value = evaluate(node.expression);
+ if (value is AbortConstant) return new ReturnStatus(value);
+ return const ProceedStatus();
+ }
+
+ @override
ExecutionStatus visitReturnStatement(ReturnStatement node) =>
new ReturnStatus(evaluate(node.expression));
@@ -3337,7 +3354,7 @@
ExecutionStatus visitVariableDeclaration(VariableDeclaration node) {
Constant value = evaluate(node.initializer);
if (value is AbortConstant) return new ReturnStatus(value);
- exprEvaluator.env.updateVariableValue(node, value);
+ exprEvaluator.env.addVariableValue(node, value);
return const ProceedStatus();
}
}
@@ -3426,17 +3443,21 @@
_typeVariables[parameter] = value;
}
- void updateVariableValue(VariableDeclaration variable, Constant value) {
- if (!_variables.containsKey(variable)) {
- _variables[variable] = new EvaluationReference(value);
- } else {
- _variables[variable].value = value;
- }
+ void addVariableValue(VariableDeclaration variable, Constant value) {
+ _variables[variable] = new EvaluationReference(value);
if (value is UnevaluatedConstant) {
_unreadUnevaluatedVariables.add(variable);
}
}
+ Constant updateVariableValue(VariableDeclaration variable, Constant value) {
+ if (_variables.containsKey(variable)) {
+ _variables[variable].value = value;
+ return value;
+ }
+ return _parent?.updateVariableValue(variable, value);
+ }
+
Constant lookupVariable(VariableDeclaration variable) {
Constant value = _variables[variable]?.value;
if (value is UnevaluatedConstant) {
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart
new file mode 100644
index 0000000..3a3fc6f
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2021, 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.
+
+// Tests variable assignments for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+int varAssignmentTest(int a) {
+ int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+
+int function() {
+ int varAssignmentTest2() {
+ int x = 2;
+ x += 1;
+ return x;
+ }
+
+ const var2 = varAssignmentTest2();
+ return var2;
+}
+
+const var3 = varAssignmentTest3(1);
+int varAssignmentTest3(int a) {
+ int x = 4;
+ x = a + 1;
+ return x;
+}
+
+void main() {
+ Expect.equals(var1, 3);
+ Expect.equals(function(), 3);
+ Expect.equals(var3, 2);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect
new file mode 100644
index 0000000..801ab46
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+int varAssignmentTest(int a) {}
+int function() {}
+const var3 = varAssignmentTest3(1);
+int varAssignmentTest3(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..5f57ea4
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+const var3 = varAssignmentTest3(1);
+int function() {}
+int varAssignmentTest(int a) {}
+int varAssignmentTest3(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect
new file mode 100644
index 0000000..71847ee
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::varAssignmentTest(1);
+static const field core::int var3 = self::varAssignmentTest3(1);
+static method varAssignmentTest(core::int a) → core::int
+ ;
+static method function() → core::int
+ ;
+static method varAssignmentTest3(core::int a) → core::int
+ ;
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 139e557..2c45796c 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -10,7 +10,11 @@
import 'package:args/args.dart';
import 'package:dev_compiler/dev_compiler.dart'
- show DevCompilerTarget, ExpressionCompiler, parseModuleFormat;
+ show
+ DevCompilerTarget,
+ ExpressionCompiler,
+ parseModuleFormat,
+ ProgramCompiler;
// front_end/src imports below that require lint `ignore_for_file`
// are a temporary state of things until frontend team builds better api
@@ -631,7 +635,7 @@
final sourceMapsFileSink = sourceMapsFile.openWrite();
final metadataFileSink =
emitDebugMetadata ? metadataFile.openWrite() : null;
- await _bundler.compile(
+ final kernel2JsCompilers = await _bundler.compile(
results.classHierarchy,
results.coreTypes,
results.loadedLibraries,
@@ -639,6 +643,7 @@
manifestFileSink,
sourceMapsFileSink,
metadataFileSink);
+ cachedProgramCompilers.addAll(kernel2JsCompilers);
await Future.wait([
sourceFileSink.close(),
manifestFileSink.close(),
@@ -806,6 +811,12 @@
}
}
+ /// Program compilers per module.
+ ///
+ /// Produced suring initial compilation of the module to JavaScript,
+ /// cached to be used for expression compilation in [compileExpressionToJs].
+ final Map<String, ProgramCompiler> cachedProgramCompilers = {};
+
@override
Future<Null> compileExpressionToJs(
String libraryUri,
@@ -822,7 +833,7 @@
reportError('JavaScript bundler is null');
return;
}
- if (!_bundler.compilers.containsKey(moduleName)) {
+ if (!cachedProgramCompilers.containsKey(moduleName)) {
reportError('Cannot find kernel2js compiler for $moduleName.');
return;
}
@@ -833,13 +844,13 @@
_processedOptions.ticker
.logMs('Compiling expression to JavaScript in $moduleName');
- var kernel2jsCompiler = _bundler.compilers[moduleName];
+ final kernel2jsCompiler = cachedProgramCompilers[moduleName];
Component component = _generator.lastKnownGoodComponent;
component.computeCanonicalNames();
_processedOptions.ticker.logMs('Computed component');
- var expressionCompiler = new ExpressionCompiler(
+ final expressionCompiler = new ExpressionCompiler(
_compilerOptions,
parseModuleFormat(_options['dartdevc-module-format'] as String),
errors,
@@ -848,10 +859,10 @@
component,
);
- var procedure = await expressionCompiler.compileExpressionToJs(
+ final procedure = await expressionCompiler.compileExpressionToJs(
libraryUri, line, column, jsFrameValues, expression);
- var result = errors.length > 0 ? errors[0] : procedure;
+ final result = errors.length > 0 ? errors[0] : procedure;
// TODO(annagrin): kernelBinaryFilename is too specific
// rename to _outputFileName?
diff --git a/pkg/frontend_server/lib/src/javascript_bundle.dart b/pkg/frontend_server/lib/src/javascript_bundle.dart
index 346b4d8..9829c57 100644
--- a/pkg/frontend_server/lib/src/javascript_bundle.dart
+++ b/pkg/frontend_server/lib/src/javascript_bundle.dart
@@ -29,8 +29,7 @@
this.emitDebugMetadata = false,
this.soundNullSafety = false,
String moduleFormat})
- : compilers = <String, ProgramCompiler>{},
- _moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
+ : _moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
_summaries = <Component>[];
_summaryUris = <Uri>[];
_moduleImportForSummary = <Uri, String>{};
@@ -64,7 +63,6 @@
final PackageConfig _packageConfig;
final bool useDebuggerModuleNames;
final bool emitDebugMetadata;
- final Map<String, ProgramCompiler> compilers;
final ModuleFormat _moduleFormat;
final bool soundNullSafety;
@@ -75,7 +73,7 @@
Map<Uri, Component> _uriToComponent;
/// Compile each component into a single JavaScript module.
- Future<void> compile(
+ Future<Map<String, ProgramCompiler>> compile(
ClassHierarchy classHierarchy,
CoreTypes coreTypes,
Set<Library> loadedLibraries,
@@ -88,6 +86,7 @@
var metadataOffset = 0;
final manifest = <String, Map<String, List<int>>>{};
final Set<Uri> visited = <Uri>{};
+ final Map<String, ProgramCompiler> kernel2JsCompilers = {};
final importToSummary = Map<Library, Component>.identity();
final summaryToModule = Map<Component, String>.identity();
@@ -150,9 +149,8 @@
// so it can map dart symbols to js symbols
// [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
- // program compiler is used by ExpressionCompiler to evaluate expressions
- // on demand
- compilers[moduleName] = compiler;
+ // Save program compiler to reuse for expression evaluation.
+ kernel2JsCompilers[moduleName] = compiler;
final moduleUrl = urlForComponentUri(moduleUri);
String sourceMapBase;
@@ -201,6 +199,8 @@
};
}
manifestSink.add(utf8.encode(json.encode(manifest)));
+
+ return kernel2JsCompilers;
}
}
diff --git a/pkg/frontend_server/test/src/javascript_bundle_test.dart b/pkg/frontend_server/test/src/javascript_bundle_test.dart
index 26a7c21..02cd813 100644
--- a/pkg/frontend_server/test/src/javascript_bundle_test.dart
+++ b/pkg/frontend_server/test/src/javascript_bundle_test.dart
@@ -106,8 +106,14 @@
final metadataSink = _MemorySink();
final coreTypes = CoreTypes(testComponent);
- await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
- coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
+ final compilers = await javaScriptBundler.compile(
+ ClassHierarchy(testComponent, coreTypes),
+ coreTypes,
+ {},
+ codeSink,
+ manifestSink,
+ sourcemapSink,
+ metadataSink);
final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
final String code = utf8.decode(codeSink.buffer);
@@ -122,6 +128,9 @@
// verify source map url is correct.
expect(code, contains('sourceMappingURL=c.dart.lib.js.map'));
+
+ // verify program compilers are created.
+ expect(compilers.keys, equals([urlForComponentUri(library.importUri)]));
});
test('converts package: uris into /packages/ uris', () async {
diff --git a/pkg/test_runner/lib/src/options.dart b/pkg/test_runner/lib/src/options.dart
index af07581a..1d2b814 100644
--- a/pkg/test_runner/lib/src/options.dart
+++ b/pkg/test_runner/lib/src/options.dart
@@ -833,7 +833,7 @@
// Expand architectures.
var architectures = data["arch"] as String;
if (architectures == "all") {
- architectures = "ia32,x64,simarm,simarm64";
+ architectures = "ia32,x64,x64c,simarm,simarm64,simarm64c";
}
for (var architectureName in architectures.split(",")) {
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 4413911..00b1716 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -2703,7 +2703,7 @@
FieldRef? decl;
/// [value] can be one of [InstanceRef] or [Sentinel].
- dynamic? value;
+ dynamic value;
BoundField({
required this.decl,
@@ -2747,7 +2747,7 @@
String? name;
/// [value] can be one of [InstanceRef], [TypeArgumentsRef] or [Sentinel].
- dynamic? value;
+ dynamic value;
/// The token position where this variable was declared.
int? declarationTokenPos;
@@ -2822,7 +2822,7 @@
/// a breakpoint is not resolved.
///
/// [location] can be one of [SourceLocation] or [UnresolvedSourceLocation].
- dynamic? location;
+ dynamic location;
Breakpoint({
required this.breakpointNumber,
@@ -3331,7 +3331,7 @@
json == null ? null : ContextElement._fromJson(json);
/// [value] can be one of [InstanceRef] or [Sentinel].
- dynamic? value;
+ dynamic value;
ContextElement({
required this.value,
@@ -4052,7 +4052,7 @@
///
/// [staticValue] can be one of [InstanceRef] or [Sentinel].
@optional
- dynamic? staticValue;
+ dynamic staticValue;
/// The location of this field in the source code.
@optional
@@ -4082,7 +4082,7 @@
isFinal = json['final'] ?? false;
isStatic = json['static'] ?? false;
staticValue = createServiceObject(
- json['staticValue'], const ['InstanceRef', 'Sentinel']) as dynamic?;
+ json['staticValue'], const ['InstanceRef', 'Sentinel']) as dynamic;
location = createServiceObject(json['location'], const ['SourceLocation'])
as SourceLocation?;
}
@@ -4276,7 +4276,7 @@
/// The owner of this function, which can be a Library, Class, or a Function.
///
/// [owner] can be one of [LibraryRef], [ClassRef] or [FuncRef].
- dynamic? owner;
+ dynamic owner;
/// Is this function static?
bool? isStatic;
@@ -4338,7 +4338,7 @@
/// The owner of this function, which can be a Library, Class, or a Function.
///
/// [owner] can be one of [LibraryRef], [ClassRef] or [FuncRef].
- dynamic? owner;
+ dynamic owner;
/// Is this function static?
bool? isStatic;
@@ -5788,10 +5788,10 @@
json == null ? null : MapAssociation._fromJson(json);
/// [key] can be one of [InstanceRef] or [Sentinel].
- dynamic? key;
+ dynamic key;
/// [value] can be one of [InstanceRef] or [Sentinel].
- dynamic? value;
+ dynamic value;
MapAssociation({
required this.key,
@@ -6241,7 +6241,7 @@
String? resolvedUrl;
/// The function captured during profiling.
- dynamic? function;
+ dynamic function;
ProfileFunction({
required this.kind,
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index 774f271..b57d248 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -1200,7 +1200,7 @@
gen.write('Future<${returnType.name}> ${name}(');
bool startedOptional = false;
gen.write(args.map((MethodArg arg) {
- String? typeName;
+ String typeName;
if (api.isEnumName(arg.type.name)) {
if (arg.type.isArray) {
typeName = typeName = '/*${arg.type}*/ List<String>';
@@ -1268,7 +1268,7 @@
}
}
- String? get name {
+ String get name {
if (types.isEmpty) return '';
if (types.length == 1) return types.first.ref;
if (isReturnType) return 'Response';
@@ -1286,7 +1286,7 @@
bool get isArray => types.length == 1 && types.first.isArray;
- void generate(DartGenerator gen) => gen.write(name!);
+ void generate(DartGenerator gen) => gen.write(name);
}
class TypeRef {
@@ -1296,7 +1296,7 @@
TypeRef(this.name);
- String? get ref {
+ String get ref {
if (arrayDepth == 2) {
return 'List<List<${name}>>';
} else if (arrayDepth == 1) {
@@ -1304,7 +1304,7 @@
} else if (genericTypes != null) {
return '$name<${genericTypes!.join(', ')}>';
} else {
- return name!.startsWith('_') ? name!.substring(1) : name;
+ return name!.startsWith('_') ? name!.substring(1) : name!;
}
}
@@ -1340,7 +1340,7 @@
name == 'double' ||
name == 'ByteData');
- String toString() => ref!;
+ String toString() => ref;
}
class MethodArg extends Member {
@@ -1639,9 +1639,11 @@
}
} else {
String typesList = _typeRefListToString(field.type.types);
+ String nullable =
+ field.optional && field.type.name != 'dynamic' ? '?' : '';
gen.writeln("${field.generatableName} = "
"createServiceObject(json['${field.name}']${field.optional ? '' : '!'}, "
- "$typesList) as ${field.type.name}${field.optional ? '?' : ''};");
+ "$typesList) as ${field.type.name}$nullable;");
}
});
if (fields.isNotEmpty) {
@@ -1851,8 +1853,8 @@
gen.writeln('}');
} else {
String assertMethodName = 'assert' +
- type.name!.substring(0, 1).toUpperCase() +
- type.name!.substring(1);
+ type.name.substring(0, 1).toUpperCase() +
+ type.name.substring(1);
gen.writeln('$assertMethodName(obj.${field.generatableName}!);');
}
}
@@ -1943,7 +1945,9 @@
{
String? typeName =
api.isEnumName(type.name) ? '/*${type.name}*/ String' : type.name;
- typeName = '$typeName?';
+ if (typeName != 'dynamic') {
+ typeName = '$typeName?';
+ }
gen.writeStatement('${typeName} ${generatableName};');
if (parent.fields.any((field) => field.hasDocs)) gen.writeln();
}
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index b303c7d..d390576 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -572,6 +572,8 @@
for (auto gap : gaps_) {
s->WriteUnsigned(gap);
}
+ target_memory_size_ +=
+ compiler::target::Array::InstanceSize(table_length_);
}
}
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 3176eea..059575b 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -94,28 +94,16 @@
void AsmIntrinsifier::Integer_add(Assembler* assembler, Label* normal_ir_body) {
TestBothArgumentsSmis(assembler, normal_ir_body); // Checks two smis.
-#if !defined(DART_COMPRESSED_POINTERS)
- __ adds(R0, R0, Operand(R1)); // Add.
+ __ adds(R0, R0, Operand(R1), kObjectBytes); // Add.
__ b(normal_ir_body, VS); // Fall-through on overflow.
-#else
- __ addsw(R0, R0, Operand(R1)); // Add (32-bit).
- __ b(normal_ir_body, VS); // Fall-through on overflow.
- __ sxtw(R0, R0); // Sign extend.
-#endif
__ ret();
__ Bind(normal_ir_body);
}
void AsmIntrinsifier::Integer_sub(Assembler* assembler, Label* normal_ir_body) {
TestBothArgumentsSmis(assembler, normal_ir_body);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subs(R0, R1, Operand(R0)); // Subtract.
- __ b(normal_ir_body, VS); // Fall-through on overflow.
-#else
- __ subsw(R0, R1, Operand(R0)); // Subtract (32-bit).
- __ b(normal_ir_body, VS); // Fall-through on overflow.
- __ sxtw(R0, R0); // Sign extend.
-#endif
+ __ subs(R0, R1, Operand(R0), compiler::kObjectBytes); // Subtract.
+ __ b(normal_ir_body, VS); // Fall-through on overflow.
__ ret();
__ Bind(normal_ir_body);
}
@@ -160,16 +148,16 @@
ASSERT(left == result);
// Check for quick zero results.
- __ CompareRegisters(left, ZR);
+ __ CompareObjectRegisters(left, ZR);
__ b(&return_zero, EQ);
- __ CompareRegisters(left, right);
+ __ CompareObjectRegisters(left, right);
__ b(&return_zero, EQ);
// Check if result should be left.
- __ CompareRegisters(left, ZR);
+ __ CompareObjectRegisters(left, ZR);
__ b(&modulo, LT);
// left is positive.
- __ CompareRegisters(left, right);
+ __ CompareObjectRegisters(left, right);
// left is less than right, result is left.
__ b(&modulo, GT);
__ mov(R0, left);
@@ -184,8 +172,9 @@
__ SmiUntag(left);
__ SmiUntag(right);
- __ sdiv(tmp, left, right);
- __ msub(result, right, tmp, left); // result <- left - right * tmp
+ __ sdiv(tmp, left, right, kObjectBytes);
+ __ msub(result, right, tmp, left,
+ kObjectBytes); // result <- left - right * tmp
}
// Implementation:
@@ -207,21 +196,21 @@
// R1: Tagged left (dividend).
// R0: Tagged right (divisor).
// Check if modulo by zero -> exception thrown in main function.
- __ CompareRegisters(R0, ZR);
+ __ CompareObjectRegisters(R0, ZR);
__ b(normal_ir_body, EQ);
EmitRemainderOperation(assembler);
// Untagged right in R0. Untagged remainder result in R1.
- __ CompareRegisters(R1, ZR);
+ __ CompareObjectRegisters(R1, ZR);
__ b(&neg_remainder, LT);
__ SmiTag(R0, R1); // Tag and move result to R0.
__ ret();
__ Bind(&neg_remainder);
// Result is negative, adjust it.
- __ CompareRegisters(R0, ZR);
- __ sub(TMP, R1, Operand(R0));
- __ add(TMP2, R1, Operand(R0));
+ __ CompareObjectRegisters(R0, ZR);
+ __ sub(TMP, R1, Operand(R0), kObjectBytes);
+ __ add(TMP2, R1, Operand(R0), kObjectBytes);
__ csel(R0, TMP2, TMP, GE);
__ SmiTag(R0);
__ ret();
@@ -234,20 +223,20 @@
// Check to see if we have integer division
TestBothArgumentsSmis(assembler, normal_ir_body);
- __ CompareRegisters(R0, ZR);
+ __ CompareObjectRegisters(R0, ZR);
__ b(normal_ir_body, EQ); // If b is 0, fall through.
__ SmiUntag(R0);
__ SmiUntag(R1);
- __ sdiv(R0, R1, R0);
+ __ sdiv(R0, R1, R0, kObjectBytes);
// Check the corner case of dividing the 'MIN_SMI' with -1, in which case we
// cannot tag the result.
#if !defined(DART_COMPRESSED_POINTERS)
__ CompareImmediate(R0, 0x4000000000000000);
#else
- __ CompareImmediate(R0, 0x40000000);
+ __ CompareImmediate(R0, 0x40000000, compiler::kFourBytes);
#endif
__ b(normal_ir_body, EQ);
__ SmiTag(R0); // Not equal. Okay to tag and return.
@@ -259,14 +248,8 @@
Label* normal_ir_body) {
__ ldr(R0, Address(SP, +0 * target::kWordSize)); // Grab first argument.
__ BranchIfNotSmi(R0, normal_ir_body);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ negs(R0, R0);
+ __ negs(R0, R0, kObjectBytes);
__ b(normal_ir_body, VS);
-#else
- __ negsw(R0, R0);
- __ b(normal_ir_body, VS);
- __ sxtw(R0, R0);
-#endif
__ ret();
__ Bind(normal_ir_body);
}
@@ -304,25 +287,20 @@
const Register result = R0;
TestBothArgumentsSmis(assembler, normal_ir_body);
- __ CompareImmediate(right, target::ToRawSmi(target::kSmiBits));
+ __ CompareImmediate(right, target::ToRawSmi(target::kSmiBits),
+ compiler::kObjectBytes);
__ b(normal_ir_body, CS);
// Left is not a constant.
// Check if count too large for handling it inlined.
__ SmiUntag(TMP, right); // SmiUntag right into TMP.
// Overflow test (preserve left, right, and TMP);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ lslv(temp, left, TMP);
- __ asrv(TMP2, temp, TMP);
- __ CompareRegisters(left, TMP2);
-#else
- __ lslvw(temp, left, TMP);
- __ asrvw(TMP2, temp, TMP);
- __ cmpw(left, Operand(TMP2));
-#endif
+ __ lslv(temp, left, TMP, kObjectBytes);
+ __ asrv(TMP2, temp, TMP, kObjectBytes);
+ __ cmp(left, Operand(TMP2), kObjectBytes);
__ b(normal_ir_body, NE); // Overflow.
// Shift for result now we know there is no overflow.
- __ lslv(result, left, TMP);
+ __ lslv(result, left, TMP, kObjectBytes);
__ ret();
__ Bind(normal_ir_body);
}
@@ -333,7 +311,7 @@
Label true_label;
TestBothArgumentsSmis(assembler, normal_ir_body);
// R0 contains the right argument, R1 the left.
- __ CompareRegisters(R1, R0);
+ __ CompareObjectRegisters(R1, R0);
__ LoadObject(R0, CastHandle<Object>(FalseObject()));
__ LoadObject(TMP, CastHandle<Object>(TrueObject()));
__ csel(R0, TMP, R0, true_condition);
@@ -369,7 +347,7 @@
// For integer receiver '===' check first.
__ ldr(R0, Address(SP, 0 * target::kWordSize));
__ ldr(R1, Address(SP, 1 * target::kWordSize));
- __ cmp(R0, Operand(R1));
+ __ CompareObjectRegisters(R0, R1);
__ b(&true_label, EQ);
__ orr(R2, R0, Operand(R1));
@@ -424,15 +402,19 @@
// Fall through if shift amount is negative.
__ SmiUntag(R0);
- __ CompareRegisters(R0, ZR);
+ __ CompareObjectRegisters(R0, ZR);
__ b(normal_ir_body, LT);
- // If shift amount is bigger than 63, set to 63.
+ // If shift amount is bigger than 63/31, set to 63/31.
+#if !defined(DART_COMPRESSED_POINTERS)
__ LoadImmediate(TMP, 0x3F);
+#else
+ __ LoadImmediate(TMP, 0x1F);
+#endif
__ CompareRegisters(R0, TMP);
__ csel(R0, TMP, R0, GT);
__ SmiUntag(R1);
- __ asrv(R0, R1, R0);
+ __ asrv(R0, R1, R0, kObjectBytes);
__ SmiTag(R0);
__ ret();
__ Bind(normal_ir_body);
@@ -451,9 +433,15 @@
__ ldr(R0, Address(SP, 0 * target::kWordSize));
__ SmiUntag(R0);
// XOR with sign bit to complement bits if value is negative.
+#if !defined(DART_COMPRESSED_POINTERS)
__ eor(R0, R0, Operand(R0, ASR, 63));
__ clz(R0, R0);
__ LoadImmediate(R1, 64);
+#else
+ __ eorw(R0, R0, Operand(R0, ASR, 31));
+ __ clzw(R0, R0);
+ __ LoadImmediate(R1, 32);
+#endif
__ sub(R0, R1, Operand(R0));
__ SmiTag(R0);
__ ret();
@@ -1349,14 +1337,14 @@
__ fcmpd(V0, V0);
__ b(normal_ir_body, VS);
-#if !defined(DART_COMPRESSED_POINTERS)
__ fcvtzdsx(R0, V0);
+
+#if !defined(DART_COMPRESSED_POINTERS)
// Overflow is signaled with minint.
// Check for overflow and that it fits into Smi.
__ CompareImmediate(R0, 0xC000000000000000);
__ b(normal_ir_body, MI);
#else
- __ fcvtzdsw(R0, V0);
// Overflow is signaled with minint.
// Check for overflow and that it fits into Smi.
__ AsrImmediate(TMP, R0, 30);
@@ -1397,14 +1385,8 @@
// overflow is signalled by fcvt through clamping R0 to either
// INT64_MAX or INT64_MIN (saturation).
ASSERT(kSmiTag == 0 && kSmiTagShift == 1);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ adds(R0, R0, Operand(R0));
+ __ adds(R0, R0, Operand(R0), kObjectBytes);
__ b(normal_ir_body, VS);
-#else
- __ addsw(R0, R0, Operand(R0));
- __ b(normal_ir_body, VS);
- __ sxtw(R0, R0); // Sign extend.
-#endif
// Compare the two double values. If they are equal, we return the
// Smi tagged result immediately as the hash code.
@@ -1476,7 +1458,7 @@
Label* normal_ir_body) {
__ ldr(R0, Address(SP, 0 * target::kWordSize));
__ ldr(R1, Address(SP, 1 * target::kWordSize));
- __ cmp(R0, Operand(R1));
+ __ CompareObjectRegisters(R0, R1);
__ LoadObject(R0, CastHandle<Object>(FalseObject()));
__ LoadObject(TMP, CastHandle<Object>(TrueObject()));
__ csel(R0, TMP, R0, EQ);
@@ -1704,7 +1686,7 @@
Label equal, not_equal, equiv_cids, check_legacy;
__ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
- __ cmp(R1, Operand(R2));
+ __ CompareObjectRegisters(R1, R2);
__ b(&equal, EQ);
// R1 might not be a Type object, so check that first (R2 should be though,
@@ -1766,7 +1748,7 @@
void AsmIntrinsifier::FunctionType_equality(Assembler* assembler,
Label* normal_ir_body) {
__ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
- __ cmp(R1, Operand(R2));
+ __ CompareObjectRegisters(R1, R2);
__ b(normal_ir_body, NE);
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1955,7 +1937,7 @@
Label* normal_ir_body) {
__ ldr(R0, Address(SP, 0 * target::kWordSize));
__ ldr(R0, FieldAddress(R0, target::String::length_offset()));
- __ cmp(R0, Operand(target::ToRawSmi(0)));
+ __ cmp(R0, Operand(target::ToRawSmi(0)), kObjectBytes);
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
__ LoadObject(TMP, CastHandle<Object>(FalseObject()));
__ csel(R0, TMP, R0, NE);
@@ -2234,7 +2216,7 @@
__ ldr(R1, Address(SP, 0 * target::kWordSize)); // Other.
// Are identical?
- __ cmp(R0, Operand(R1));
+ __ CompareObjectRegisters(R0, R1);
__ b(&is_true, EQ);
// Is other OneByteString?
@@ -2245,7 +2227,7 @@
// Have same length?
__ ldr(R2, FieldAddress(R0, target::String::length_offset()));
__ ldr(R3, FieldAddress(R1, target::String::length_offset()));
- __ cmp(R2, Operand(R3));
+ __ CompareObjectRegisters(R2, R3);
__ b(&is_false, NE);
// Check contents, no fall-through possible.
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index 3254745..a2b59ef 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -93,14 +93,8 @@
void AsmIntrinsifier::Integer_add(Assembler* assembler, Label* normal_ir_body) {
TestBothArgumentsSmis(assembler, normal_ir_body);
// RAX contains right argument.
-#if !defined(DART_COMPRESSED_POINTERS)
- __ addq(RAX, Address(RSP, +2 * target::kWordSize));
+ __ OBJ(add)(RAX, Address(RSP, +2 * target::kWordSize));
__ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
- __ addl(RAX, Address(RSP, +2 * target::kWordSize));
- __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
- __ movsxd(RAX, RAX);
-#endif
// Result is in RAX.
__ ret();
__ Bind(normal_ir_body);
@@ -111,14 +105,8 @@
// RAX contains right argument, which is the actual subtrahend of subtraction.
__ movq(RCX, RAX);
__ movq(RAX, Address(RSP, +2 * target::kWordSize));
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subq(RAX, RCX);
+ __ OBJ(sub)(RAX, RCX);
__ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
- __ subl(RAX, RCX);
- __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
- __ movsxd(RAX, RAX);
-#endif
// Result is in RAX.
__ ret();
__ Bind(normal_ir_body);
@@ -129,14 +117,8 @@
// RAX is the right argument.
ASSERT(kSmiTag == 0); // Adjust code below if not the case.
__ SmiUntag(RAX);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ imulq(RAX, Address(RSP, +2 * target::kWordSize));
+ __ OBJ(imul)(RAX, Address(RSP, +2 * target::kWordSize));
__ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
- __ imull(RAX, Address(RSP, +2 * target::kWordSize));
- __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
- __ movsxd(RAX, RAX);
-#endif
// Result is in RAX.
__ ret();
__ Bind(normal_ir_body);
@@ -156,16 +138,16 @@
static void EmitRemainderOperation(Assembler* assembler) {
Label return_zero, try_modulo, not_32bit, done;
// Check for quick zero results.
- __ cmpq(RAX, Immediate(0));
+ __ OBJ(cmp)(RAX, Immediate(0));
__ j(EQUAL, &return_zero, Assembler::kNearJump);
- __ cmpq(RAX, RCX);
+ __ OBJ(cmp)(RAX, RCX);
__ j(EQUAL, &return_zero, Assembler::kNearJump);
// Check if result equals left.
- __ cmpq(RAX, Immediate(0));
+ __ OBJ(cmp)(RAX, Immediate(0));
__ j(LESS, &try_modulo, Assembler::kNearJump);
// left is positive.
- __ cmpq(RAX, RCX);
+ __ OBJ(cmp)(RAX, RCX);
__ j(GREATER, &try_modulo, Assembler::kNearJump);
// left is less than right, result is left (RAX).
__ ret();
@@ -229,11 +211,11 @@
__ movq(RCX, Address(RSP, +1 * target::kWordSize));
// RAX: Tagged left (dividend).
// RCX: Tagged right (divisor).
- __ cmpq(RCX, Immediate(0));
+ __ OBJ(cmp)(RCX, Immediate(0));
__ j(EQUAL, normal_ir_body);
EmitRemainderOperation(assembler);
// Untagged remainder result in RAX.
- __ cmpq(RAX, Immediate(0));
+ __ OBJ(cmp)(RAX, Immediate(0));
__ j(LESS, &negative_result, Assembler::kNearJump);
__ SmiTag(RAX);
__ ret();
@@ -242,14 +224,14 @@
Label subtract;
// RAX: Untagged result.
// RCX: Untagged right.
- __ cmpq(RCX, Immediate(0));
+ __ OBJ(cmp)(RCX, Immediate(0));
__ j(LESS, &subtract, Assembler::kNearJump);
- __ addq(RAX, RCX);
+ __ OBJ(add)(RAX, RCX);
__ SmiTag(RAX);
__ ret();
__ Bind(&subtract);
- __ subq(RAX, RCX);
+ __ OBJ(sub)(RAX, RCX);
__ SmiTag(RAX);
__ ret();
@@ -261,7 +243,7 @@
Label not_32bit;
TestBothArgumentsSmis(assembler, normal_ir_body);
// RAX: right argument (divisor)
- __ cmpq(RAX, Immediate(0));
+ __ OBJ(cmp)(RAX, Immediate(0));
__ j(EQUAL, normal_ir_body, Assembler::kNearJump);
__ movq(RCX, RAX);
__ movq(RAX,
@@ -325,14 +307,8 @@
__ movq(RAX, Address(RSP, +1 * target::kWordSize));
__ testq(RAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, normal_ir_body, Assembler::kNearJump); // Non-smi value.
-#if !defined(DART_COMPRESSED_POINTERS)
- __ negq(RAX);
+ __ OBJ(neg)(RAX);
__ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
- __ negl(RAX);
- __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
- __ movsxd(RAX, RAX);
-#endif
// Result is in RAX.
__ ret();
__ Bind(normal_ir_body);
@@ -374,7 +350,7 @@
Label overflow;
TestBothArgumentsSmis(assembler, normal_ir_body);
// Shift value is in RAX. Compare with tagged Smi.
- __ cmpq(RAX, Immediate(target::ToRawSmi(target::kSmiBits)));
+ __ OBJ(cmp)(RAX, Immediate(target::ToRawSmi(target::kSmiBits)));
__ j(ABOVE_EQUAL, normal_ir_body, Assembler::kNearJump);
__ SmiUntag(RAX);
@@ -383,18 +359,12 @@
// Overflow test - all the shifted-out bits must be same as the sign bit.
__ movq(RDI, RAX);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ shlq(RAX, RCX);
- __ sarq(RAX, RCX);
-#else
- __ shll(RAX, RCX);
- __ sarl(RAX, RCX);
- __ movsxd(RAX, RAX);
-#endif
- __ cmpq(RAX, RDI);
+ __ OBJ(shl)(RAX, RCX);
+ __ OBJ(sar)(RAX, RCX);
+ __ OBJ(cmp)(RAX, RDI);
__ j(NOT_EQUAL, &overflow, Assembler::kNearJump);
- __ shlq(RAX, RCX); // Shift for result now we know there is no overflow.
+ __ OBJ(shl)(RAX, RCX); // Shift for result now we know there is no overflow.
// RAX is a correctly tagged Smi.
__ ret();
@@ -411,7 +381,7 @@
Label true_label;
TestBothArgumentsSmis(assembler, normal_ir_body);
// RAX contains the right argument.
- __ cmpq(Address(RSP, +2 * target::kWordSize), RAX);
+ __ OBJ(cmp)(Address(RSP, +2 * target::kWordSize), RAX);
__ j(true_condition, &true_label, Assembler::kNearJump);
__ LoadObject(RAX, CastHandle<Object>(FalseObject()));
__ ret();
@@ -452,7 +422,7 @@
// For integer receiver '===' check first.
__ movq(RAX, Address(RSP, +kArgumentOffset * target::kWordSize));
__ movq(RCX, Address(RSP, +kReceiverOffset * target::kWordSize));
- __ cmpq(RAX, RCX);
+ __ OBJ(cmp)(RAX, RCX);
__ j(EQUAL, &true_label, Assembler::kNearJump);
__ orq(RAX, RCX);
__ testq(RAX, Immediate(kSmiTagMask));
@@ -504,22 +474,26 @@
void AsmIntrinsifier::Integer_sar(Assembler* assembler, Label* normal_ir_body) {
Label shift_count_ok;
TestBothArgumentsSmis(assembler, normal_ir_body);
+#if !defined(DART_COMPRESSED_POINTERS)
const Immediate& count_limit = Immediate(0x3F);
+#else
+ const Immediate& count_limit = Immediate(0x1F);
+#endif
// Check that the count is not larger than what the hardware can handle.
// For shifting right a Smi the result is the same for all numbers
// >= count_limit.
__ SmiUntag(RAX);
// Negative counts throw exception.
- __ cmpq(RAX, Immediate(0));
+ __ OBJ(cmp)(RAX, Immediate(0));
__ j(LESS, normal_ir_body, Assembler::kNearJump);
- __ cmpq(RAX, count_limit);
+ __ OBJ(cmp)(RAX, count_limit);
__ j(LESS_EQUAL, &shift_count_ok, Assembler::kNearJump);
__ movq(RAX, count_limit);
__ Bind(&shift_count_ok);
__ movq(RCX, RAX); // Shift amount must be in RCX.
__ movq(RAX, Address(RSP, +2 * target::kWordSize)); // Value.
__ SmiUntag(RAX); // Value.
- __ sarq(RAX, RCX);
+ __ OBJ(sar)(RAX, RCX);
__ SmiTag(RAX);
__ ret();
__ Bind(normal_ir_body);
@@ -529,7 +503,7 @@
void AsmIntrinsifier::Smi_bitNegate(Assembler* assembler,
Label* normal_ir_body) {
__ movq(RAX, Address(RSP, +1 * target::kWordSize)); // Index.
- __ notq(RAX);
+ __ OBJ(not )(RAX);
__ andq(RAX, Immediate(~kSmiTagMask)); // Remove inverted smi-tag.
__ ret();
}
@@ -538,10 +512,13 @@
Label* normal_ir_body) {
ASSERT(kSmiTagShift == 1);
__ movq(RAX, Address(RSP, +1 * target::kWordSize)); // Index.
+#if defined(DART_COMPRESSED_POINTERS)
+ __ movsxd(RAX, RAX);
+#endif
// XOR with sign bit to complement bits if value is negative.
__ movq(RCX, RAX);
__ sarq(RCX, Immediate(63)); // All 0 or all 1.
- __ xorq(RAX, RCX);
+ __ OBJ (xor)(RAX, RCX);
// BSR does not write the destination register if source is zero. Put a 1 in
// the Smi tag bit to ensure BSR writes to destination register.
__ orq(RAX, Immediate(kSmiTagMask));
@@ -1111,7 +1088,7 @@
__ ret();
__ Bind(&is_smi);
__ SmiUntag(RAX);
- __ cvtsi2sdq(XMM1, RAX);
+ __ OBJ(cvtsi2sd)(XMM1, RAX);
__ jmp(&double_op);
__ Bind(normal_ir_body);
}
@@ -1177,7 +1154,7 @@
__ ret();
__ Bind(&is_smi);
__ SmiUntag(RAX);
- __ cvtsi2sdq(XMM1, RAX);
+ __ OBJ(cvtsi2sd)(XMM1, RAX);
__ jmp(&double_op);
__ Bind(normal_ir_body);
}
@@ -1206,7 +1183,7 @@
__ j(NOT_ZERO, normal_ir_body);
// Is Smi.
__ SmiUntag(RAX);
- __ cvtsi2sdq(XMM1, RAX);
+ __ OBJ(cvtsi2sd)(XMM1, RAX);
__ movq(RAX, Address(RSP, +2 * target::kWordSize));
__ movsd(XMM0, FieldAddress(RAX, target::Double::value_offset()));
__ mulsd(XMM0, XMM1);
@@ -1227,11 +1204,7 @@
__ j(NOT_ZERO, normal_ir_body);
// Is Smi.
__ SmiUntag(RAX);
-#if !defined(DART_COMPRESSED_POINTER)
- __ cvtsi2sdq(XMM0, RAX);
-#else
- __ cvtsi2sdl(XMM0, RAX);
-#endif
+ __ OBJ(cvtsi2sd)(XMM0, RAX);
const Class& double_class = DoubleClass();
__ TryAllocate(double_class, normal_ir_body, Assembler::kFarJump,
RAX, // Result register.
@@ -1303,26 +1276,13 @@
Label* normal_ir_body) {
__ movq(RAX, Address(RSP, +1 * target::kWordSize));
__ movsd(XMM0, FieldAddress(RAX, target::Double::value_offset()));
-#if !defined(DART_COMPRESSED_POINTERS)
- __ cvttsd2siq(RAX, XMM0);
-#else
- __ cvttsd2sil(RAX, XMM0);
-#endif
+ __ OBJ(cvttsd2si)(RAX, XMM0);
// Overflow is signalled with minint.
// Check for overflow and that it fits into Smi.
__ movq(RCX, RAX);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ shlq(RCX, Immediate(1));
-#else
- __ shll(RCX, Immediate(1));
-#endif
+ __ OBJ(shl)(RCX, Immediate(1));
__ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#if !defined(DART_COMPRESSED_POINTERS)
__ SmiTag(RAX);
-#else
- ASSERT((kSmiTagShift == 1) && (kSmiTag == 0));
- __ movsxd(RAX, RCX);
-#endif
__ ret();
__ Bind(normal_ir_body);
}
@@ -1335,26 +1295,15 @@
// back to a double in XMM1.
__ movq(RCX, Address(RSP, +1 * target::kWordSize));
__ movsd(XMM0, FieldAddress(RCX, target::Double::value_offset()));
-#if !defined(DART_COMPRESSED_POINTERS)
- __ cvttsd2siq(RAX, XMM0);
- __ cvtsi2sdq(XMM1, RAX);
-#else
- __ cvttsd2sil(RAX, XMM0);
- __ cvtsi2sdl(XMM1, RAX);
-#endif
+ __ OBJ(cvttsd2si)(RAX, XMM0);
+ __ OBJ(cvtsi2sd)(XMM1, RAX);
// Tag the int as a Smi, making sure that it fits; this checks for
// overflow and NaN in the conversion from double to int. Conversion
// overflow from cvttsd2si is signalled with an INT64_MIN value.
ASSERT(kSmiTag == 0 && kSmiTagShift == 1);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ addq(RAX, RAX);
+ __ OBJ(add)(RAX, RAX);
__ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
- __ addl(RAX, RAX);
- __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
- __ movsxd(RAX, RAX);
-#endif
// Compare the two double values. If they are equal, we return the
// Smi tagged result immediately as the hash code.
@@ -1392,7 +1341,7 @@
__ ret();
__ Bind(&is_smi);
__ SmiUntag(RAX);
- __ cvtsi2sdq(XMM1, RAX);
+ __ OBJ(cvtsi2sd)(XMM1, RAX);
__ jmp(&double_op);
__ Bind(normal_ir_body);
}
@@ -1437,7 +1386,7 @@
const intptr_t kArgumentOffset = 1;
__ movq(RAX, Address(RSP, +kArgumentOffset * target::kWordSize));
- __ cmpq(RAX, Address(RSP, +kReceiverOffset * target::kWordSize));
+ __ OBJ(cmp)(RAX, Address(RSP, +kReceiverOffset * target::kWordSize));
__ j(EQUAL, &is_true, Assembler::kNearJump);
__ LoadObject(RAX, CastHandle<Object>(FalseObject()));
__ ret();
@@ -1665,7 +1614,7 @@
__ movq(RCX, Address(RSP, +1 * target::kWordSize));
__ movq(RDX, Address(RSP, +2 * target::kWordSize));
- __ cmpq(RCX, RDX);
+ __ OBJ(cmp)(RCX, RDX);
__ j(EQUAL, &equal);
// RCX might not be a Type object, so check that first (RDX should be though,
@@ -1729,7 +1678,7 @@
Label* normal_ir_body) {
__ movq(RCX, Address(RSP, +1 * target::kWordSize));
__ movq(RDX, Address(RSP, +2 * target::kWordSize));
- __ cmpq(RCX, RDX);
+ __ OBJ(cmp)(RCX, RDX);
__ j(NOT_EQUAL, normal_ir_body);
__ LoadObject(RAX, CastHandle<Object>(TrueObject()));
@@ -1763,8 +1712,11 @@
intptr_t other_cid,
Label* return_true,
Label* return_false) {
+ __ SmiUntag(RBX);
__ movq(R8, FieldAddress(RAX, target::String::length_offset()));
+ __ SmiUntag(R8);
__ movq(R9, FieldAddress(RCX, target::String::length_offset()));
+ __ SmiUntag(R9);
// if (other.length == 0) return true;
__ testq(R9, R9);
@@ -1780,8 +1732,6 @@
__ cmpq(R11, R8);
__ j(GREATER, return_false);
- __ SmiUntag(RBX); // start
- __ SmiUntag(R9); // other.length
__ LoadImmediate(R11, Immediate(0)); // i = 0
// do
@@ -1911,7 +1861,7 @@
// Get length.
__ movq(RAX, Address(RSP, +1 * target::kWordSize)); // String object.
__ movq(RAX, FieldAddress(RAX, target::String::length_offset()));
- __ cmpq(RAX, Immediate(target::ToRawSmi(0)));
+ __ OBJ(cmp)(RAX, Immediate(target::ToRawSmi(0)));
__ j(EQUAL, &is_true, Assembler::kNearJump);
__ LoadObject(RAX, CastHandle<Object>(FalseObject()));
__ ret();
@@ -2195,7 +2145,7 @@
__ movq(RCX, Address(RSP, +1 * target::kWordSize)); // Other.
// Are identical?
- __ cmpq(RAX, RCX);
+ __ OBJ(cmp)(RAX, RCX);
__ j(EQUAL, &is_true, Assembler::kNearJump);
// Is other target::OneByteString?
@@ -2206,7 +2156,7 @@
// Have same length?
__ movq(RDI, FieldAddress(RAX, target::String::length_offset()));
- __ cmpq(RDI, FieldAddress(RCX, target::String::length_offset()));
+ __ OBJ(cmp)(RDI, FieldAddress(RCX, target::String::length_offset()));
__ j(NOT_EQUAL, &is_false, Assembler::kNearJump);
// Check contents, no fall-through possible.
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index 91c5e85..ff0820b 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -529,16 +529,16 @@
ASSERT(IsOriginalObject(object));
word offset = 0;
if (IsSameObject(compiler::NullObject(), object)) {
- CompareRegisters(reg, NULL_REG);
+ CompareObjectRegisters(reg, NULL_REG);
} else if (target::CanLoadFromThread(object, &offset)) {
ldr(TMP, Address(THR, offset));
- CompareRegisters(reg, TMP);
+ CompareObjectRegisters(reg, TMP);
} else if (CanLoadFromObjectPool(object)) {
LoadObject(TMP, object);
- CompareRegisters(reg, TMP);
+ CompareObjectRegisters(reg, TMP);
} else {
ASSERT(target::IsSmi(object));
- CompareImmediate(reg, target::ToRawSmi(object));
+ CompareImmediate(reg, target::ToRawSmi(object), kObjectBytes);
}
}
@@ -667,7 +667,12 @@
Call(FieldAddress(CODE_REG, target::Code::entry_point_offset(entry_kind)));
}
-void Assembler::AddImmediate(Register dest, Register rn, int64_t imm) {
+void Assembler::AddImmediate(Register dest,
+ Register rn,
+ int64_t imm,
+ OperandSize sz) {
+ ASSERT(sz == kEightBytes || sz == kFourBytes);
+ int width = sz == kEightBytes ? kXRegSizeInBits : kWRegSizeInBits;
Operand op;
if (imm == 0) {
if (dest != rn) {
@@ -675,16 +680,15 @@
}
return;
}
- if (Operand::CanHold(imm, kXRegSizeInBits, &op) == Operand::Immediate) {
- add(dest, rn, op);
- } else if (Operand::CanHold(-imm, kXRegSizeInBits, &op) ==
- Operand::Immediate) {
- sub(dest, rn, op);
+ if (Operand::CanHold(imm, width, &op) == Operand::Immediate) {
+ add(dest, rn, op, sz);
+ } else if (Operand::CanHold(-imm, width, &op) == Operand::Immediate) {
+ sub(dest, rn, op, sz);
} else {
// TODO(zra): Try adding top 12 bits, then bottom 12 bits.
ASSERT(rn != TMP2);
LoadImmediate(TMP2, imm);
- add(dest, rn, Operand(TMP2));
+ add(dest, rn, Operand(TMP2), sz);
}
}
@@ -783,7 +787,7 @@
tsti(rn, Immediate(imm), sz);
} else {
LoadImmediate(TMP, imm);
- tst(rn, Operand(TMP));
+ tst(rn, Operand(TMP), sz);
}
}
@@ -926,17 +930,8 @@
#if !defined(DART_COMPRESSED_POINTERS)
ldr(dest, slot);
#else
- Label done;
- ldr(dest, slot, kFourBytes); // Sign-extension.
- if (can_value_be_smi == kValueCanBeSmi) {
- BranchIfSmi(dest, &done);
- }
+ ldr(dest, slot, kUnsignedFourBytes); // Zero-extension.
add(dest, dest, Operand(HEAP_BASE));
- Bind(&done);
-
- // After further Smi changes:
- // ldr(dest, slot, kUnsignedFourBytes); // Zero-extension.
- // add(dest, dest, Operand(HEAP_BASE));
#endif
}
@@ -1899,15 +1894,33 @@
const intptr_t boxing_shift = index_unboxed ? 0 : -kSmiTagShift;
const intptr_t shift = Utils::ShiftForPowerOfTwo(index_scale) + boxing_shift;
const int32_t offset = HeapDataOffset(is_external, cid);
+#if !defined(DART_COMPRESSED_POINTERS)
+ const bool index_is_32bit = false;
+#else
+ const bool index_is_32bit = !index_unboxed;
+#endif
ASSERT(array != temp);
ASSERT(index != temp);
if ((offset == 0) && (shift == 0)) {
- return Address(array, index, UXTX, Address::Unscaled);
+ if (index_is_32bit) {
+ return Address(array, index, SXTW, Address::Unscaled);
+ } else {
+ return Address(array, index, UXTX, Address::Unscaled);
+ }
} else if (shift < 0) {
ASSERT(shift == -1);
- add(temp, array, Operand(index, ASR, 1));
+ if (index_is_32bit) {
+ AsrImmediate(temp, index, 1, kFourBytes);
+ add(temp, array, Operand(temp, SXTW, 0));
+ } else {
+ add(temp, array, Operand(index, ASR, 1));
+ }
} else {
- add(temp, array, Operand(index, LSL, shift));
+ if (index_is_32bit) {
+ add(temp, array, Operand(index, SXTW, shift));
+ } else {
+ add(temp, array, Operand(index, LSL, shift));
+ }
}
ASSERT(Address::CanHoldOffset(offset, Address::Offset, size));
return Address(temp, offset, Address::Offset, size);
@@ -1924,13 +1937,31 @@
const intptr_t boxing_shift = index_unboxed ? 0 : -kSmiTagShift;
const intptr_t shift = Utils::ShiftForPowerOfTwo(index_scale) + boxing_shift;
const int32_t offset = HeapDataOffset(is_external, cid);
+#if !defined(DART_COMPRESSED_POINTERS)
+ const bool index_is_32bit = false;
+#else
+ const bool index_is_32bit = !index_unboxed;
+#endif
if (shift == 0) {
- add(address, array, Operand(index));
+ if (index_is_32bit) {
+ add(address, array, Operand(index, SXTW, 0));
+ } else {
+ add(address, array, Operand(index));
+ }
} else if (shift < 0) {
ASSERT(shift == -1);
- add(address, array, Operand(index, ASR, 1));
+ if (index_is_32bit) {
+ sxtw(index, index);
+ add(address, array, Operand(index, ASR, 1));
+ } else {
+ add(address, array, Operand(index, ASR, 1));
+ }
} else {
- add(address, array, Operand(index, LSL, shift));
+ if (index_is_32bit) {
+ add(address, array, Operand(index, SXTW, shift));
+ } else {
+ add(address, array, Operand(index, LSL, shift));
+ }
}
if (offset != 0) {
AddImmediate(address, offset);
@@ -2046,7 +2077,7 @@
}
}
-bool Assembler::CanGenerateXCbzTbz(Register rn, Condition cond) {
+bool Assembler::CanGenerateCbzTbz(Register rn, Condition cond) {
if (rn == CSP) {
return false;
}
@@ -2063,9 +2094,12 @@
}
}
-void Assembler::GenerateXCbzTbz(Register rn, Condition cond, Label* label) {
- constexpr int32_t bit_no = 63;
- constexpr OperandSize sz = kEightBytes;
+void Assembler::GenerateCbzTbz(Register rn,
+ Condition cond,
+ Label* label,
+ OperandSize sz) {
+ ASSERT((sz == kEightBytes) || (sz == kFourBytes));
+ const int32_t sign_bit = sz == kEightBytes ? 63 : 31;
ASSERT(rn != CSP);
switch (cond) {
case EQ: // equal
@@ -2076,11 +2110,11 @@
return;
case MI: // minus/negative
case LT: // signed less than
- tbnz(label, rn, bit_no);
+ tbnz(label, rn, sign_bit);
return;
case PL: // plus/positive or zero
case GE: // signed greater than or equal
- tbz(label, rn, bit_no);
+ tbz(label, rn, sign_bit);
return;
default:
// Only conditions above allow single instruction emission.
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 14ac5aa..0417af4 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -897,29 +897,47 @@
}
// Misc. arithmetic.
- void udiv(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(UDIV, rd, rn, rm, kEightBytes);
+ void udiv(Register rd,
+ Register rn,
+ Register rm,
+ OperandSize sz = kEightBytes) {
+ EmitMiscDP2Source(UDIV, rd, rn, rm, sz);
}
- void sdiv(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(SDIV, rd, rn, rm, kEightBytes);
+ void sdiv(Register rd,
+ Register rn,
+ Register rm,
+ OperandSize sz = kEightBytes) {
+ EmitMiscDP2Source(SDIV, rd, rn, rm, sz);
}
- void lslv(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(LSLV, rd, rn, rm, kEightBytes);
+ void lslv(Register rd,
+ Register rn,
+ Register rm,
+ OperandSize sz = kEightBytes) {
+ EmitMiscDP2Source(LSLV, rd, rn, rm, sz);
}
- void lsrv(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(LSRV, rd, rn, rm, kEightBytes);
+ void lsrv(Register rd,
+ Register rn,
+ Register rm,
+ OperandSize sz = kEightBytes) {
+ EmitMiscDP2Source(LSRV, rd, rn, rm, sz);
}
- void asrv(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(ASRV, rd, rn, rm, kEightBytes);
+ void asrv(Register rd,
+ Register rn,
+ Register rm,
+ OperandSize sz = kEightBytes) {
+ EmitMiscDP2Source(ASRV, rd, rn, rm, sz);
+ }
+ void sdivw(Register rd, Register rn, Register rm) {
+ sdiv(rd, rn, rm, kFourBytes);
}
void lslvw(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(LSLV, rd, rn, rm, kFourBytes);
+ lslv(rd, rn, rm, kFourBytes);
}
void lsrvw(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(LSRV, rd, rn, rm, kFourBytes);
+ lsrv(rd, rn, rm, kFourBytes);
}
void asrvw(Register rd, Register rn, Register rm) {
- EmitMiscDP2Source(ASRV, rd, rn, rm, kFourBytes);
+ asrv(rd, rn, rm, kFourBytes);
}
void madd(Register rd,
Register rn,
@@ -1115,6 +1133,11 @@
}
}
+ void CompareObjectRegisters(Register rn, Register rm) {
+ ASSERT(rn != CSP);
+ cmp(rn, Operand(rm), kObjectBytes);
+ }
+
// Conditional branch.
void b(Label* label, Condition cond = AL) {
EmitConditionalBranch(BCOND, cond, label);
@@ -1149,10 +1172,13 @@
EmitCompareAndBranch(CBNZ, rt, label, sz);
}
- // Generate 64-bit compare with zero and branch when condition allows to use
- // a single instruction: cbz/cbnz/tbz/tbnz.
- bool CanGenerateXCbzTbz(Register rn, Condition cond);
- void GenerateXCbzTbz(Register rn, Condition cond, Label* label);
+ // Generate 64/32-bit compare with zero and branch when condition allows to
+ // use a single instruction: cbz/cbnz/tbz/tbnz.
+ bool CanGenerateCbzTbz(Register rn, Condition cond);
+ void GenerateCbzTbz(Register rn,
+ Condition cond,
+ Label* label,
+ OperandSize sz = kEightBytes);
// Test bit and branch if zero.
void tbz(Label* label, Register rt, intptr_t bit_number) {
@@ -1442,8 +1468,10 @@
void mvn(Register rd, Register rm) { orn(rd, ZR, Operand(rm)); }
void mvnw(Register rd, Register rm) { ornw(rd, ZR, Operand(rm)); }
void neg(Register rd, Register rm) { sub(rd, ZR, Operand(rm)); }
- void negs(Register rd, Register rm) { subs(rd, ZR, Operand(rm)); }
- void negsw(Register rd, Register rm) { subsw(rd, ZR, Operand(rm)); }
+ void negs(Register rd, Register rm, OperandSize sz = kEightBytes) {
+ subs(rd, ZR, Operand(rm), sz);
+ }
+ void negsw(Register rd, Register rm) { negs(rd, rm, kFourBytes); }
void mul(Register rd, Register rn, Register rm) {
madd(rd, rn, rm, ZR, kEightBytes);
}
@@ -1538,9 +1566,11 @@
void VRecps(VRegister vd, VRegister vn);
void VRSqrts(VRegister vd, VRegister vn);
- void SmiUntag(Register reg) { AsrImmediate(reg, reg, kSmiTagSize); }
+ void SmiUntag(Register reg) {
+ sbfm(reg, reg, kSmiTagSize, target::kSmiBits + 1);
+ }
void SmiUntag(Register dst, Register src) {
- AsrImmediate(dst, src, kSmiTagSize);
+ sbfm(dst, src, kSmiTagSize, target::kSmiBits + 1);
}
void SmiTag(Register reg) { LslImmediate(reg, reg, kSmiTagSize); }
void SmiTag(Register dst, Register src) {
@@ -1617,7 +1647,10 @@
// the object pool when possible. Unless you are sure that the untagged object
// pool pointer is in another register, or that it is not available at all,
// PP should be passed for pp. `dest` can be TMP2, `rn` cannot.
- void AddImmediate(Register dest, Register rn, int64_t imm);
+ void AddImmediate(Register dest,
+ Register rn,
+ int64_t imm,
+ OperandSize sz = kEightBytes);
void AddImmediateSetFlags(Register dest,
Register rn,
int64_t imm,
diff --git a/runtime/vm/compiler/assembler/assembler_base.h b/runtime/vm/compiler/assembler/assembler_base.h
index a650f43..1ce7b58 100644
--- a/runtime/vm/compiler/assembler/assembler_base.h
+++ b/runtime/vm/compiler/assembler/assembler_base.h
@@ -191,6 +191,12 @@
kRegList,
// 64-bit ARM specific constants.
kQWord,
+
+#if defined(TARGET_ARCH_IS_64_BIT) && !defined(DART_COMPRESSED_POINTERS)
+ kObjectBytes = kEightBytes,
+#else
+ kObjectBytes = kFourBytes,
+#endif
};
// Forward declarations.
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index 7a616d9..d1ab4a7 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -533,22 +533,36 @@
EmitSimple(0xDD, 0xC0 + value);
}
-void Assembler::CompareImmediate(Register reg, const Immediate& imm) {
- if (imm.is_int32()) {
- cmpq(reg, imm);
+void Assembler::CompareImmediate(Register reg,
+ const Immediate& imm,
+ OperandSize width) {
+ if (width == kEightBytes) {
+ if (imm.is_int32()) {
+ cmpq(reg, imm);
+ } else {
+ ASSERT(reg != TMP);
+ LoadImmediate(TMP, imm);
+ cmpq(reg, TMP);
+ }
} else {
- ASSERT(reg != TMP);
- LoadImmediate(TMP, imm);
- cmpq(reg, TMP);
+ ASSERT(width == kFourBytes);
+ cmpl(reg, imm);
}
}
-void Assembler::CompareImmediate(const Address& address, const Immediate& imm) {
- if (imm.is_int32()) {
- cmpq(address, imm);
+void Assembler::CompareImmediate(const Address& address,
+ const Immediate& imm,
+ OperandSize width) {
+ if (width == kEightBytes) {
+ if (imm.is_int32()) {
+ cmpq(address, imm);
+ } else {
+ LoadImmediate(TMP, imm);
+ cmpq(address, TMP);
+ }
} else {
- LoadImmediate(TMP, imm);
- cmpq(address, TMP);
+ ASSERT(width == kFourBytes);
+ cmpl(address, imm);
}
}
@@ -607,13 +621,20 @@
}
}
-void Assembler::TestImmediate(Register dst, const Immediate& imm) {
- if (imm.is_int32() || imm.is_uint32()) {
- testq(dst, imm);
+void Assembler::TestImmediate(Register dst,
+ const Immediate& imm,
+ OperandSize width) {
+ if (width == kEightBytes) {
+ if (imm.is_int32() || imm.is_uint32()) {
+ testq(dst, imm);
+ } else {
+ ASSERT(dst != TMP);
+ LoadImmediate(TMP, imm);
+ testq(dst, TMP);
+ }
} else {
- ASSERT(dst != TMP);
- LoadImmediate(TMP, imm);
- testq(dst, TMP);
+ ASSERT(width == kFourBytes);
+ testl(dst, imm);
}
}
@@ -1303,15 +1324,15 @@
intptr_t offset_from_thread;
if (target::CanLoadFromThread(object, &offset_from_thread)) {
- cmpq(reg, Address(THR, offset_from_thread));
+ OBJ(cmp)(reg, Address(THR, offset_from_thread));
} else if (CanLoadFromObjectPool(object)) {
const intptr_t idx = object_pool_builder().FindObject(
object, ObjectPoolBuilderEntry::kNotPatchable);
const int32_t offset = target::ObjectPool::element_offset(idx);
- cmpq(reg, Address(PP, offset - kHeapObjectTag));
+ OBJ(cmp)(reg, Address(PP, offset - kHeapObjectTag));
} else {
ASSERT(target::IsSmi(object));
- CompareImmediate(reg, Immediate(target::ToRawSmi(object)));
+ CompareImmediate(reg, Immediate(target::ToRawSmi(object)), kObjectBytes);
}
}
@@ -1345,17 +1366,8 @@
#if !defined(DART_COMPRESSED_POINTERS)
movq(dest, slot);
#else
- Label done;
- movsxd(dest, slot); // (movslq) Sign-extension.
- if (can_value_be_smi == kValueCanBeSmi) {
- BranchIfSmi(dest, &done, kNearJump);
- }
+ movl(dest, slot); // Zero-extension.
addq(dest, Address(THR, target::Thread::heap_base_offset()));
- Bind(&done);
-
- // After further Smi changes:
- // movl(dest, slot); // Zero-extension.
- // addq(dest, Address(THR, target::Thread::heap_base_offset());
#endif
}
@@ -1888,7 +1900,11 @@
j(NOT_EQUAL, &miss, Assembler::kNearJump);
addl(FieldAddress(RBX, count_offset), Immediate(target::ToRawSmi(1)));
xorq(R10, R10); // GC-safe for OptimizeInvokedFunction.
+#if !defined(DART_COMPRESSED_POINTERS)
nop(1);
+#else
+ nop(2);
+#endif
// Fall through to unchecked entry.
ASSERT_EQUAL(CodeSize() - start,
@@ -1921,7 +1937,11 @@
// Ensure the unchecked entry is 2-byte aligned (so GC can see them if we
// store them in ICData / MegamorphicCache arrays).
+#if !defined(DART_COMPRESSED_POINTERS)
nop(1);
+#else
+ nop(2);
+#endif
// Fall through to unchecked entry.
ASSERT_EQUAL(CodeSize() - start,
@@ -2230,6 +2250,7 @@
void Assembler::SmiUntagOrCheckClass(Register object,
intptr_t class_id,
Label* is_smi) {
+#if !defined(DART_COMPRESSED_POINTERS)
ASSERT(kSmiTagShift == 1);
ASSERT(target::UntaggedObject::kClassIdTagPos == 16);
ASSERT(target::UntaggedObject::kClassIdTagSize == 16);
@@ -2244,6 +2265,11 @@
// factor in the addressing mode to compensate for this.
movzxw(TMP, Address(object, TIMES_2, class_id_offset));
cmpl(TMP, Immediate(class_id));
+#else
+ // Cannot speculatively untag compressed Smis because it erases upper address
+ // bits.
+ UNREACHABLE();
+#endif
}
void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index cffa9e9..82d7b89 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -286,6 +286,12 @@
}
};
+#if !defined(DART_COMPRESSED_POINTERS)
+#define OBJ(op) op##q
+#else
+#define OBJ(op) op##l
+#endif
+
class Assembler : public AssemblerBase {
public:
explicit Assembler(ObjectPoolBuilder* object_pool_builder,
@@ -544,10 +550,16 @@
};
void roundsd(XmmRegister dst, XmmRegister src, RoundingMode mode);
- void CompareImmediate(Register reg, const Immediate& imm);
- void CompareImmediate(const Address& address, const Immediate& imm);
- void CompareImmediate(Register reg, int32_t immediate) {
- return CompareImmediate(reg, Immediate(immediate));
+ void CompareImmediate(Register reg,
+ const Immediate& imm,
+ OperandSize width = kEightBytes);
+ void CompareImmediate(const Address& address,
+ const Immediate& imm,
+ OperandSize width = kEightBytes);
+ void CompareImmediate(Register reg,
+ int32_t immediate,
+ OperandSize width = kEightBytes) {
+ return CompareImmediate(reg, Immediate(immediate), width);
}
void testl(Register reg, const Immediate& imm) { testq(reg, imm); }
@@ -555,7 +567,9 @@
void testb(const Address& address, Register reg);
void testq(Register reg, const Immediate& imm);
- void TestImmediate(Register dst, const Immediate& imm);
+ void TestImmediate(Register dst,
+ const Immediate& imm,
+ OperandSize width = kEightBytes);
void AndImmediate(Register dst, const Immediate& imm);
void OrImmediate(Register dst, const Immediate& imm);
@@ -678,6 +692,7 @@
// Methods for High-level operations and implemented on all architectures.
void Ret() { ret(); }
void CompareRegisters(Register a, Register b);
+ void CompareObjectRegisters(Register a, Register b) { OBJ(cmp)(a, b); }
void BranchIf(Condition condition,
Label* label,
JumpDistance distance = kFarJump) {
@@ -864,9 +879,21 @@
void SmiUntagOrCheckClass(Register object, intptr_t class_id, Label* smi);
// Misc. functionality.
- void SmiTag(Register reg) { addq(reg, reg); }
+ void SmiTag(Register reg) { OBJ(add)(reg, reg); }
- void SmiUntag(Register reg) { sarq(reg, Immediate(kSmiTagSize)); }
+ void SmiUntag(Register reg) { OBJ(sar)(reg, Immediate(kSmiTagSize)); }
+
+ void SmiUntagAndSignExtend(Register reg) {
+#if !defined(DART_COMPRESSED_POINTERS)
+ sarq(reg, Immediate(kSmiTagSize));
+#else
+ // This is shorter than
+ // shlq reg, 32
+ // sraq reg, 33
+ sarl(reg, Immediate(1));
+ movsxd(reg, reg);
+#endif
+ }
void BranchIfNotSmi(Register reg,
Label* label,
@@ -950,7 +977,7 @@
}
void CompareWithFieldValue(Register value, FieldAddress address) {
- cmpq(value, address);
+ OBJ(cmp)(value, address);
}
void CompareTypeNullabilityWith(Register type, int8_t value) {
diff --git a/runtime/vm/compiler/assembler/assembler_x64_test.cc b/runtime/vm/compiler/assembler/assembler_x64_test.cc
index 39d492d..9704c22 100644
--- a/runtime/vm/compiler/assembler/assembler_x64_test.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64_test.cc
@@ -4971,6 +4971,7 @@
ASSEMBLER_TEST_RUN(TestObjectCompare, test) {
bool res = test->InvokeWithCodeAndThread<bool>();
EXPECT_EQ(true, res);
+#if !defined(DART_COMPRESSED_POINTERS)
EXPECT_DISASSEMBLY_NOT_WINDOWS(
"push rbp\n"
"movq rbp,rsp\n"
@@ -5014,6 +5015,51 @@
"movq rsp,rbp\n"
"pop rbp\n"
"ret\n");
+#else
+ EXPECT_DISASSEMBLY_NOT_WINDOWS(
+ "push rbp\n"
+ "movq rbp,rsp\n"
+ "push r12\n"
+ "push pp\n"
+ "push thr\n"
+ "movq r12,[rdi+0x8]\n"
+ "movq thr,rsi\n"
+ "movq pp,[r12+0x27]\n"
+ "movq rax,[pp+0xf]\n"
+ "cmpl rax,[pp+0xf]\n"
+ "jnz 0x................\n"
+ "movq rcx,[pp+0xf]\n"
+ "cmpl rcx,[pp+0xf]\n"
+ "jnz 0x................\n"
+ "movl rcx,0x1e\n"
+ "cmpl rcx,0x1e\n"
+ "jnz 0x................\n"
+ "push rax\n"
+ "movq r11,[pp+0xf]\n"
+ "movq [rsp],r11\n"
+ "pop rcx\n"
+ "cmpl rcx,[pp+0xf]\n"
+ "jnz 0x................\n"
+ "push rax\n"
+ "movq [rsp],0x1e\n"
+ "pop rcx\n"
+ "cmpl rcx,0x1e\n"
+ "jnz 0x................\n"
+ "movl rax,1\n"
+ "pop thr\n"
+ "pop pp\n"
+ "pop r12\n"
+ "movq rsp,rbp\n"
+ "pop rbp\n"
+ "ret\n"
+ "movl rax,0\n"
+ "pop thr\n"
+ "pop pp\n"
+ "pop r12\n"
+ "movq rsp,rbp\n"
+ "pop rbp\n"
+ "ret\n");
+#endif
}
ASSEMBLER_TEST_GENERATE(TestNop, assembler) {
diff --git a/runtime/vm/compiler/backend/constant_propagator_test.cc b/runtime/vm/compiler/backend/constant_propagator_test.cc
index 3eb3218..5265ea0 100644
--- a/runtime/vm/compiler/backend/constant_propagator_test.cc
+++ b/runtime/vm/compiler/backend/constant_propagator_test.cc
@@ -51,7 +51,7 @@
{
BlockBuilder builder(H.flow_graph(), b2);
- v1 = H.Phi(b2, {{b1, v0}, {b3, FlowGraphBuilderHelper::kPhiSelfReference}});
+ v1 = H.Phi(b2, {{b1, v0}, {b3, &v1}});
builder.AddPhi(v1);
auto v2 = builder.AddDefinition(
new EqualityCompareInstr(InstructionSource(), Token::kEQ, new Value(v1),
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 69b2bd0..639a7cd 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -1901,7 +1901,7 @@
}
}
-static void UnboxPhi(PhiInstr* phi) {
+static void UnboxPhi(PhiInstr* phi, bool is_aot) {
Representation unboxed = phi->representation();
switch (phi->Type()->ToCid()) {
@@ -2004,10 +2004,22 @@
}
}
+#if defined(TARGET_ARCH_IS_64_BIT)
+ // In AOT mode on 64-bit platforms always unbox integer typed phis (similar
+ // to how we treat doubles and other boxed numeric types).
+ // In JIT mode only unbox phis which are not fully known to be Smi.
+ if ((unboxed == kTagged) && phi->Type()->IsInt() &&
+ (is_aot || phi->Type()->ToCid() != kSmiCid)) {
+ unboxed = kUnboxedInt64;
+ }
+#endif
+
phi->set_representation(unboxed);
}
void FlowGraph::SelectRepresentations() {
+ const auto is_aot = CompilerState::Current().is_aot();
+
// First we decide for each phi if it is beneficial to unbox it. If so, we
// change it's `phi->representation()`
for (BlockIterator block_it = reverse_postorder_iterator(); !block_it.Done();
@@ -2016,7 +2028,7 @@
if (join_entry != NULL) {
for (PhiIterator it(join_entry); !it.Done(); it.Advance()) {
PhiInstr* phi = it.Current();
- UnboxPhi(phi);
+ UnboxPhi(phi, is_aot);
}
}
}
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index 89f2a24..6b3f7c3 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -708,7 +708,7 @@
if (obj.IsSmi() && (Smi::Cast(obj).Value() == 0)) {
ASSERT(!needs_number_check);
- __ testq(reg, reg);
+ __ OBJ(test)(reg, reg);
return EQUAL;
}
diff --git a/runtime/vm/compiler/backend/flow_graph_test.cc b/runtime/vm/compiler/backend/flow_graph_test.cc
new file mode 100644
index 0000000..55323bb
--- /dev/null
+++ b/runtime/vm/compiler/backend/flow_graph_test.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2012, 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.
+
+#include "vm/compiler/backend/flow_graph.h"
+
+#include <vector>
+
+#include "platform/utils.h"
+#include "vm/compiler/backend/block_builder.h"
+#include "vm/compiler/backend/il_printer.h"
+#include "vm/compiler/backend/il_test_helper.h"
+#include "vm/compiler/backend/type_propagator.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#if defined(TARGET_ARCH_IS_64_BIT)
+ISOLATE_UNIT_TEST_CASE(FlowGraph_UnboxInt64Phi) {
+ using compiler::BlockBuilder;
+
+ CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
+
+ FlowGraphBuilderHelper H;
+
+ // Add a variable into the scope which would provide static type for the
+ // parameter.
+ LocalVariable* v0_var =
+ new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
+ String::Handle(Symbols::New(thread, "v0")),
+ AbstractType::ZoneHandle(Type::IntType()),
+ new CompileType(CompileType::Int()));
+ v0_var->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
+ H.flow_graph()->parsed_function().scope()->AddVariable(v0_var);
+
+ auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
+ auto loop_header = H.JoinEntry();
+ auto loop_body = H.TargetEntry();
+ auto loop_exit = H.TargetEntry();
+
+ Definition* v0;
+ PhiInstr* loop_var;
+ Definition* add1;
+ ReturnInstr* ret;
+
+ {
+ BlockBuilder builder(H.flow_graph(), normal_entry);
+ v0 = builder.AddParameter(0, 0, /*with_frame=*/true, kTagged);
+ builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
+ }
+
+ {
+ BlockBuilder builder(H.flow_graph(), loop_header);
+ loop_var = H.Phi(loop_header, {{normal_entry, v0}, {loop_body, &add1}});
+ builder.AddPhi(loop_var);
+ builder.AddBranch(new RelationalOpInstr(
+ InstructionSource(), Token::kLT, new Value(loop_var),
+ new Value(H.IntConstant(50)), kMintCid,
+ S.GetNextDeoptId(), Instruction::kNotSpeculative),
+ loop_body, loop_exit);
+ }
+
+ {
+ BlockBuilder builder(H.flow_graph(), loop_body);
+ add1 = builder.AddDefinition(new BinaryInt64OpInstr(
+ Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
+ S.GetNextDeoptId(), Instruction::kNotSpeculative));
+ builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
+ }
+
+ {
+ BlockBuilder builder(H.flow_graph(), loop_exit);
+ ret = builder.AddReturn(new Value(loop_var));
+ }
+
+ H.FinishGraph();
+
+ FlowGraphTypePropagator::Propagate(H.flow_graph());
+ H.flow_graph()->SelectRepresentations();
+
+ EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
+}
+#endif // defined(TARGET_ARCH_IS_64_BIT)
+
+} // namespace dart
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index d3c8abb..0e88877 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -93,14 +93,22 @@
case kTagged:
case kUnboxedInt64: {
const auto out = locs()->out(0).reg();
+#if !defined(DART_COMPRESSED_POINTERS)
__ add(out, base_reg(), compiler::Operand(index, LSL, 2));
+#else
+ __ add(out, base_reg(), compiler::Operand(index, SXTW, 2));
+#endif
__ LoadFromOffset(out, out, offset());
break;
}
case kUnboxedDouble: {
const auto tmp = locs()->temp(0).reg();
const auto out = locs()->out(0).fpu_reg();
+#if !defined(DART_COMPRESSED_POINTERS)
__ add(tmp, base_reg(), compiler::Operand(index, LSL, 2));
+#else
+ __ add(tmp, base_reg(), compiler::Operand(index, SXTW, 2));
+#endif
__ LoadDFromOffset(out, tmp, offset());
break;
}
@@ -114,7 +122,11 @@
(NoLocation, Register index, Register value)) {
ASSERT(instr->RequiredInputRepresentation(
StoreIndexedUnsafeInstr::kIndexPos) == kTagged); // It is a Smi.
+#if !defined(DART_COMPRESSED_POINTERS)
__ add(TMP, instr->base_reg(), compiler::Operand(index, LSL, 2));
+#else
+ __ add(TMP, instr->base_reg(), compiler::Operand(index, SXTW, 2));
+#endif
__ str(value, compiler::Address(TMP, instr->offset()));
ASSERT(kSmiTag == 0);
@@ -810,21 +822,22 @@
Register rn,
Condition cond,
BranchLabels labels) {
- return !AreLabelsNull(labels) && __ CanGenerateXCbzTbz(rn, cond);
+ return !AreLabelsNull(labels) && __ CanGenerateCbzTbz(rn, cond);
}
static void EmitCbzTbz(Register reg,
FlowGraphCompiler* compiler,
Condition true_condition,
- BranchLabels labels) {
+ BranchLabels labels,
+ compiler::OperandSize sz) {
ASSERT(CanUseCbzTbzForComparison(compiler, reg, true_condition, labels));
if (labels.fall_through == labels.false_label) {
// If the next block is the false successor we will fall through to it.
- __ GenerateXCbzTbz(reg, true_condition, labels.true_label);
+ __ GenerateCbzTbz(reg, true_condition, labels.true_label, sz);
} else {
// If the next block is not the false successor we will branch to it.
Condition false_condition = InvertCondition(true_condition);
- __ GenerateXCbzTbz(reg, false_condition, labels.false_label);
+ __ GenerateCbzTbz(reg, false_condition, labels.false_label, sz);
// Fall through or jump to the true successor.
if (labels.fall_through != labels.true_label) {
@@ -833,6 +846,44 @@
}
}
+static Condition EmitSmiComparisonOp(FlowGraphCompiler* compiler,
+ LocationSummary* locs,
+ Token::Kind kind,
+ BranchLabels labels) {
+ Location left = locs->in(0);
+ Location right = locs->in(1);
+ ASSERT(!left.IsConstant() || !right.IsConstant());
+
+ Condition true_condition = TokenKindToSmiCondition(kind);
+ if (left.IsConstant() || right.IsConstant()) {
+ // Ensure constant is on the right.
+ ConstantInstr* constant = nullptr;
+ if (left.IsConstant()) {
+ constant = left.constant_instruction();
+ Location tmp = right;
+ right = left;
+ left = tmp;
+ true_condition = FlipCondition(true_condition);
+ } else {
+ constant = right.constant_instruction();
+ }
+
+ ASSERT(constant->representation() == kTagged);
+ int64_t value;
+ if (compiler::HasIntegerValue(constant->value(), &value) && (value == 0) &&
+ CanUseCbzTbzForComparison(compiler, left.reg(), true_condition,
+ labels)) {
+ EmitCbzTbz(left.reg(), compiler, true_condition, labels,
+ compiler::kObjectBytes);
+ return kInvalidCondition;
+ }
+ __ CompareObject(left.reg(), right.constant());
+ } else {
+ __ CompareObjectRegisters(left.reg(), right.reg());
+ }
+ return true_condition;
+}
+
// Similar to ComparisonInstr::EmitComparisonCode, may either:
// - emit comparison code and return a valid condition in which case the
// caller is expected to emit a branch to the true label based on that
@@ -867,13 +918,13 @@
RELEASE_ASSERT(ok);
if (value == 0 && CanUseCbzTbzForComparison(compiler, left.reg(),
true_condition, labels)) {
- EmitCbzTbz(left.reg(), compiler, true_condition, labels);
+ EmitCbzTbz(left.reg(), compiler, true_condition, labels,
+ compiler::kEightBytes);
return kInvalidCondition;
}
__ CompareImmediate(left.reg(), value);
} else {
- ASSERT(constant->representation() == kTagged);
- __ CompareObject(left.reg(), right.constant());
+ UNREACHABLE();
}
} else {
__ CompareRegisters(left.reg(), right.reg());
@@ -949,7 +1000,9 @@
Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
BranchLabels labels) {
- if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
+ if (operation_cid() == kSmiCid) {
+ return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
+ } else if (operation_cid() == kMintCid) {
return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
} else {
ASSERT(operation_cid() == kDoubleCid);
@@ -976,9 +1029,9 @@
if (right.IsConstant()) {
ASSERT(right.constant().IsSmi());
const int64_t imm = static_cast<int64_t>(right.constant().ptr());
- __ TestImmediate(left, imm);
+ __ TestImmediate(left, imm, compiler::kObjectBytes);
} else {
- __ tst(left, compiler::Operand(right.reg()));
+ __ tst(left, compiler::Operand(right.reg()), compiler::kObjectBytes);
}
Condition true_condition = (kind() == Token::kNE) ? NE : EQ;
return true_condition;
@@ -1070,7 +1123,9 @@
Condition RelationalOpInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
BranchLabels labels) {
- if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
+ if (operation_cid() == kSmiCid) {
+ return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
+ } else if (operation_cid() == kMintCid) {
return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
} else {
ASSERT(operation_cid() == kDoubleCid);
@@ -3383,20 +3438,12 @@
ASSERT((0 < value) && (value < kCountLimit));
if (shift_left->can_overflow()) {
// Check for overflow (preserve left).
-#if !defined(DART_COMPRESSED_POINTERS)
- __ LslImmediate(TMP, left, value);
- __ cmp(left, compiler::Operand(TMP, ASR, value));
-#else
- __ LslImmediate(TMP, left, value, compiler::kFourBytes);
- __ cmpw(left, compiler::Operand(TMP, ASR, value));
-#endif
+ __ LslImmediate(TMP, left, value, compiler::kObjectBytes);
+ __ cmp(left, compiler::Operand(TMP, ASR, value), compiler::kObjectBytes);
__ b(deopt, NE); // Overflow.
}
// Shift for result now we know there is no overflow.
- __ LslImmediate(result, left, value);
-#if defined(DART_COMPRESSED_POINTERS)
- __ sxtw(result, result);
-#endif
+ __ LslImmediate(result, left, value, compiler::kObjectBytes);
return;
}
@@ -3410,7 +3457,7 @@
if (obj.IsSmi()) {
const intptr_t left_int = Smi::Cast(obj).Value();
if (left_int == 0) {
- __ CompareRegisters(right, ZR);
+ __ CompareObjectRegisters(right, ZR);
__ b(deopt, MI);
__ mov(result, ZR);
return;
@@ -3420,14 +3467,11 @@
const bool right_needs_check =
!RangeUtils::IsWithin(right_range, 0, max_right - 1);
if (right_needs_check) {
- __ CompareImmediate(right, static_cast<int64_t>(Smi::New(max_right)));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(max_right)));
__ b(deopt, CS);
}
__ SmiUntag(TMP, right);
- __ lslv(result, left, TMP);
-#if defined(DART_COMPRESSED_POINTERS)
- __ sxtw(result, result);
-#endif
+ __ lslv(result, left, TMP, compiler::kObjectBytes);
}
return;
}
@@ -3438,23 +3482,23 @@
if (right_needs_check) {
if (!RangeUtils::IsPositive(right_range)) {
ASSERT(shift_left->CanDeoptimize());
- __ CompareRegisters(right, ZR);
+ __ CompareObjectRegisters(right, ZR);
__ b(deopt, MI);
}
- __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
__ csel(result, ZR, result, CS);
__ SmiUntag(TMP, right);
- __ lslv(TMP, left, TMP);
+ __ lslv(TMP, left, TMP, compiler::kObjectBytes);
__ csel(result, TMP, result, CC);
} else {
__ SmiUntag(TMP, right);
- __ lslv(result, left, TMP);
+ __ lslv(result, left, TMP, compiler::kObjectBytes);
}
} else {
if (right_needs_check) {
ASSERT(shift_left->CanDeoptimize());
- __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
__ b(deopt, CS);
}
// Left is not a constant.
@@ -3462,18 +3506,12 @@
__ SmiUntag(TMP, right);
// Overflow test (preserve left, right, and TMP);
const Register temp = locs.temp(0).reg();
-#if !defined(DART_COMPRESSED_POINTERS)
- __ lslv(temp, left, TMP);
- __ asrv(TMP2, temp, TMP);
- __ CompareRegisters(left, TMP2);
-#else
- __ lslvw(temp, left, TMP);
- __ asrvw(TMP2, temp, TMP);
- __ cmpw(left, compiler::Operand(TMP2));
-#endif
+ __ lslv(temp, left, TMP, compiler::kObjectBytes);
+ __ asrv(TMP2, temp, TMP, compiler::kObjectBytes);
+ __ cmp(left, compiler::Operand(TMP2), compiler::kObjectBytes);
__ b(deopt, NE); // Overflow.
// Shift for result now we know there is no overflow.
- __ lslv(result, left, TMP);
+ __ lslv(result, left, TMP, compiler::kObjectBytes);
}
}
@@ -3554,21 +3592,11 @@
switch (op_kind()) {
case Token::kADD:
-#if !defined(DART_COMPRESSED_POINTERS)
- __ adds(result, left, compiler::Operand(right));
-#else
- __ addsw(result, left, compiler::Operand(right));
- __ sxtw(result, result);
-#endif
+ __ adds(result, left, compiler::Operand(right), compiler::kObjectBytes);
__ b(slow_path->entry_label(), VS);
break;
case Token::kSUB:
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subs(result, left, compiler::Operand(right));
-#else
- __ subsw(result, left, compiler::Operand(right));
- __ sxtw(result, result);
-#endif
+ __ subs(result, left, compiler::Operand(right), compiler::kObjectBytes);
__ b(slow_path->entry_label(), VS);
break;
case Token::kMUL:
@@ -3600,37 +3628,36 @@
case Token::kSHL:
ASSERT(result != left);
ASSERT(result != right);
- __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
__ b(slow_path->entry_label(), CS);
__ SmiUntag(TMP, right);
- __ lslv(result, left, TMP);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ asrv(TMP2, result, TMP);
- __ CompareRegisters(left, TMP2);
-#else
- __ asrvw(TMP2, result, TMP);
- __ cmp(left, compiler::Operand(TMP2, SXTW, 0));
-#endif
+ __ lslv(result, left, TMP, compiler::kObjectBytes);
+ __ asrv(TMP2, result, TMP, compiler::kObjectBytes);
+ __ cmp(left, compiler::Operand(TMP2), compiler::kObjectBytes);
__ b(slow_path->entry_label(), NE); // Overflow.
break;
case Token::kSHR:
+ ASSERT(result != left);
+ ASSERT(result != right);
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
+ __ b(slow_path->entry_label(), CS);
+
+ __ SmiUntag(result, right);
+ __ SmiUntag(TMP, left);
+ __ asrv(result, TMP, result, compiler::kObjectBytes);
+ __ SmiTag(result);
+ break;
case Token::kUSHR:
ASSERT(result != left);
ASSERT(result != right);
- __ CompareImmediate(right, static_cast<int64_t>(Smi::New(kBitsPerInt64)));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
__ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
__ SmiUntag(result, right);
__ SmiUntag(TMP, left);
- if (op_kind() == Token::kSHR) {
- __ asrv(result, TMP, result);
- __ SmiTag(result);
- } else {
- ASSERT(op_kind() == Token::kUSHR);
- __ lsrv(result, TMP, result);
- __ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
- }
+ __ lsrv(result, TMP, result);
+ __ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
break;
default:
UNIMPLEMENTED();
@@ -3722,7 +3749,7 @@
Condition CheckedSmiComparisonInstr::EmitComparisonCode(
FlowGraphCompiler* compiler,
BranchLabels labels) {
- return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
+ return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
}
#define EMIT_SMI_CHECK \
@@ -3848,37 +3875,21 @@
switch (op_kind()) {
case Token::kADD: {
if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ AddImmediate(result, left, imm);
+ __ AddImmediate(result, left, imm, compiler::kObjectBytes);
} else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ AddImmediateSetFlags(result, left, imm);
+ __ AddImmediateSetFlags(result, left, imm, compiler::kObjectBytes);
__ b(deopt, VS);
-#else
- __ AddImmediateSetFlags(result, left, imm, compiler::kFourBytes);
- __ b(deopt, VS);
- __ sxtw(result, result);
-#endif
}
break;
}
case Token::kSUB: {
if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
__ AddImmediate(result, left, -imm);
} else {
// Negating imm and using AddImmediateSetFlags would not detect the
// overflow when imm == kMinInt64.
-#if !defined(DART_COMPRESSED_POINTERS)
- __ SubImmediateSetFlags(result, left, imm);
+ __ SubImmediateSetFlags(result, left, imm, compiler::kObjectBytes);
__ b(deopt, VS);
-#else
- __ SubImmediateSetFlags(result, left, imm, compiler::kFourBytes);
- __ b(deopt, VS);
- __ sxtw(result, result);
-#endif
}
break;
}
@@ -3911,14 +3922,22 @@
const intptr_t shift_count =
Utils::ShiftForPowerOfTwo(Utils::Abs(value)) + kSmiTagSize;
ASSERT(kSmiTagSize == 1);
+#if !defined(DART_COMPRESSED_POINTERS)
__ AsrImmediate(TMP, left, 63);
+#else
+ __ AsrImmediate(TMP, left, 31, compiler::kFourBytes);
+#endif
ASSERT(shift_count > 1); // 1, -1 case handled above.
const Register temp = TMP2;
+#if !defined(DART_COMPRESSED_POINTERS)
__ add(temp, left, compiler::Operand(TMP, LSR, 64 - shift_count));
+#else
+ __ addw(temp, left, compiler::Operand(TMP, LSR, 32 - shift_count));
+#endif
ASSERT(shift_count > 0);
- __ AsrImmediate(result, temp, shift_count);
+ __ AsrImmediate(result, temp, shift_count, compiler::kObjectBytes);
if (value < 0) {
- __ sub(result, ZR, compiler::Operand(result));
+ __ sub(result, ZR, compiler::Operand(result), compiler::kObjectBytes);
}
__ SmiTag(result);
break;
@@ -3936,12 +3955,18 @@
__ XorImmediate(result, left, imm);
break;
case Token::kSHR: {
- // Asr operation masks the count to 6 bits.
+ // Asr operation masks the count to 6/5 bits.
+#if !defined(DART_COMPRESSED_POINTERS)
const intptr_t kCountLimit = 0x3F;
+#else
+ const intptr_t kCountLimit = 0x1F;
+#endif
intptr_t value = Smi::Cast(constant).Value();
__ AsrImmediate(result, left,
- Utils::Minimum(value + kSmiTagSize, kCountLimit));
+ Utils::Minimum(value + kSmiTagSize, kCountLimit),
+ compiler::kObjectBytes);
__ SmiTag(result);
+ // BOGUS: this could be one sbfiz
break;
}
case Token::kUSHR: {
@@ -3971,31 +3996,19 @@
switch (op_kind()) {
case Token::kADD: {
if (deopt == NULL) {
- __ add(result, left, compiler::Operand(right));
+ __ add(result, left, compiler::Operand(right), compiler::kObjectBytes);
} else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ adds(result, left, compiler::Operand(right));
+ __ adds(result, left, compiler::Operand(right), compiler::kObjectBytes);
__ b(deopt, VS);
-#else
- __ addsw(result, left, compiler::Operand(right));
- __ b(deopt, VS);
- __ sxtw(result, result);
-#endif
}
break;
}
case Token::kSUB: {
if (deopt == NULL) {
- __ sub(result, left, compiler::Operand(right));
+ __ sub(result, left, compiler::Operand(right), compiler::kObjectBytes);
} else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subs(result, left, compiler::Operand(right));
+ __ subs(result, left, compiler::Operand(right), compiler::kObjectBytes);
__ b(deopt, VS);
-#else
- __ subsw(result, left, compiler::Operand(right));
- __ b(deopt, VS);
- __ sxtw(result, result);
-#endif
}
break;
}
@@ -4037,20 +4050,20 @@
case Token::kTRUNCDIV: {
if (RangeUtils::CanBeZero(right_range())) {
// Handle divide by zero in runtime.
- __ cbz(deopt, right);
+ __ cbz(deopt, right, compiler::kObjectBytes);
}
const Register temp = TMP2;
__ SmiUntag(temp, left);
__ SmiUntag(TMP, right);
- __ sdiv(result, temp, TMP);
+ __ sdiv(result, temp, TMP, compiler::kObjectBytes);
if (RangeUtils::Overlaps(right_range(), -1, -1)) {
// Check the corner case of dividing the 'MIN_SMI' with -1, in which
// case we cannot tag the result.
#if !defined(DART_COMPRESSED_POINTERS)
__ CompareImmediate(result, 0x4000000000000000LL);
#else
- __ CompareImmediate(result, 0x40000000LL);
+ __ CompareImmediate(result, 0x40000000LL, compiler::kFourBytes);
#endif
__ b(deopt, EQ);
}
@@ -4060,16 +4073,17 @@
case Token::kMOD: {
if (RangeUtils::CanBeZero(right_range())) {
// Handle divide by zero in runtime.
- __ cbz(deopt, right);
+ __ cbz(deopt, right, compiler::kObjectBytes);
}
const Register temp = TMP2;
__ SmiUntag(temp, left);
__ SmiUntag(TMP, right);
- __ sdiv(result, temp, TMP);
+ __ sdiv(result, temp, TMP, compiler::kObjectBytes);
__ SmiUntag(TMP, right);
- __ msub(result, TMP, result, temp); // result <- left - right * result
+ __ msub(result, TMP, result, temp,
+ compiler::kObjectBytes); // result <- left - right * result
__ SmiTag(result);
// res = left % right;
// if (res < 0) {
@@ -4080,12 +4094,12 @@
// }
// }
compiler::Label done;
- __ CompareRegisters(result, ZR);
+ __ CompareObjectRegisters(result, ZR);
__ b(&done, GE);
// Result is negative, adjust it.
- __ CompareRegisters(right, ZR);
- __ sub(TMP, result, compiler::Operand(right));
- __ add(result, result, compiler::Operand(right));
+ __ CompareObjectRegisters(right, ZR);
+ __ sub(TMP, result, compiler::Operand(right), compiler::kObjectBytes);
+ __ add(result, result, compiler::Operand(right), compiler::kObjectBytes);
__ csel(result, TMP, result, LT);
__ Bind(&done);
break;
@@ -4095,16 +4109,20 @@
__ tbnz(deopt, right, compiler::target::kSmiBits + kSmiTagSize);
}
__ SmiUntag(TMP, right);
- // asrv operation masks the count to 6 bits.
+ // asrv[w] operation masks the count to 6/5 bits.
+#if !defined(DART_COMPRESSED_POINTERS)
const intptr_t kCountLimit = 0x3F;
+#else
+ const intptr_t kCountLimit = 0x1F;
+#endif
if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
__ LoadImmediate(TMP2, kCountLimit);
- __ CompareRegisters(TMP, TMP2);
+ __ CompareObjectRegisters(TMP, TMP2);
__ csel(TMP, TMP2, TMP, GT);
}
const Register temp = locs()->temp(0).reg();
__ SmiUntag(temp, left);
- __ asrv(result, temp, TMP);
+ __ asrv(result, temp, TMP, compiler::kObjectBytes);
__ SmiTag(result);
break;
}
@@ -4291,7 +4309,11 @@
case kUnboxedDouble: {
const VRegister result = locs()->out(0).fpu_reg();
__ SmiUntag(TMP, box);
+#if !defined(DART_COMPRESSED_POINTERS)
__ scvtfdx(result, TMP);
+#else
+ __ scvtfdw(result, TMP);
+#endif
break;
}
@@ -4400,7 +4422,8 @@
ASSERT(from_representation() == kUnboxedUint32);
// A 32 bit positive Smi has one tag bit and one unused sign bit,
// leaving only 30 bits for the payload.
- __ ubfiz(out, value, kSmiTagSize, compiler::target::kSmiBits);
+ // __ ubfiz(out, value, kSmiTagSize, compiler::target::kSmiBits);
+ __ LslImmediate(out, value, kSmiTagSize, compiler::kFourBytes);
if (ValueFitsSmi()) {
return;
}
@@ -4414,7 +4437,7 @@
if (from_representation() == kUnboxedInt32) {
__ sxtw(temp, value); // Sign-extend.
} else {
- __ ubfiz(temp, value, 0, 32); // Zero extend word.
+ __ uxtw(temp, value); // Zero-extend.
}
__ StoreToOffset(temp, out, Mint::value_offset() - kHeapObjectTag);
__ Bind(&done);
@@ -4474,9 +4497,7 @@
// which causes the overflow flag to be set.
__ b(&done, NO_OVERFLOW);
#else
- __ LslImmediate(out, in, kSmiTagSize,
- compiler::kFourBytes); // SmiTag (32-bit);
- __ sxtw(out, out); // Sign-extend.
+ __ sbfiz(out, in, kSmiTagSize, 31); // SmiTag + sign-extend.
__ cmp(in, compiler::Operand(out, ASR, kSmiTagSize));
__ b(&done, EQ);
#endif
@@ -4910,7 +4931,7 @@
const Register vs[] = {v0, v1, v2, v3};
for (intptr_t i = 0; i < 4; i++) {
- __ CompareRegisters(vs[i], TMP2);
+ __ CompareObjectRegisters(vs[i], TMP2);
__ csel(TMP, temp, ZR, EQ);
__ vinsw(result, i, TMP);
}
@@ -5207,7 +5228,7 @@
const Register left = locs()->in(0).reg();
const Register right = locs()->in(1).reg();
const Register result = locs()->out(0).reg();
- __ CompareRegisters(left, right);
+ __ CompareObjectRegisters(left, right);
ASSERT(result == left);
if (is_min) {
__ csel(result, right, left, GT);
@@ -5236,12 +5257,7 @@
case Token::kNEGATE: {
compiler::Label* deopt =
compiler->AddDeoptStub(deopt_id(), ICData::kDeoptUnaryOp);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subs(result, ZR, compiler::Operand(value));
-#else
- __ subsw(result, ZR, compiler::Operand(value));
- __ sxtw(result, result);
-#endif
+ __ subs(result, ZR, compiler::Operand(value), compiler::kObjectBytes);
__ b(deopt, VS);
break;
}
@@ -5407,13 +5423,13 @@
__ fcmpd(value, value);
__ b(deopt, VS);
-#if !defined(DART_COMPRESSED_POINTERS)
__ fcvtzdsx(result, value);
+
+#if !defined(DART_COMPRESSED_POINTERS)
// Check for overflow and that it fits into Smi.
__ CompareImmediate(result, 0xC000000000000000);
__ b(deopt, MI);
#else
- __ fcvtzdsw(result, value);
// Check for overflow and that it fits into Smi.
__ AsrImmediate(TMP, result, 30);
__ cmp(TMP, compiler::Operand(result, ASR, 63));
@@ -5678,25 +5694,25 @@
const Register result_mod = pair->At(1).reg();
if (RangeUtils::CanBeZero(divisor_range())) {
// Handle divide by zero in runtime.
- __ CompareRegisters(right, ZR);
+ __ CompareObjectRegisters(right, ZR);
__ b(deopt, EQ);
}
__ SmiUntag(result_mod, left);
__ SmiUntag(TMP, right);
- __ sdiv(result_div, result_mod, TMP);
-
// Check the corner case of dividing the 'MIN_SMI' with -1, in which
// case we cannot tag the result.
#if !defined(DART_COMPRESSED_POINTERS)
+ __ sdiv(result_div, result_mod, TMP);
__ CompareImmediate(result_div, 0x4000000000000000);
#else
- __ CompareImmediate(result_div, 0x40000000);
+ __ sdivw(result_div, result_mod, TMP);
+ __ CompareImmediate(result_div, 0x40000000, compiler::kFourBytes);
#endif
__ b(deopt, EQ);
// result_mod <- left - right * result_div.
- __ msub(result_mod, TMP, result_div, result_mod);
+ __ msub(result_mod, TMP, result_div, result_mod, compiler::kObjectBytes);
__ SmiTag(result_div);
__ SmiTag(result_mod);
// Correct MOD result:
@@ -5709,12 +5725,12 @@
// }
// }
compiler::Label done;
- __ CompareRegisters(result_mod, ZR);
+ __ CompareObjectRegisters(result_mod, ZR);
__ b(&done, GE);
// Result is negative, adjust it.
- __ CompareRegisters(right, ZR);
- __ sub(TMP2, result_mod, compiler::Operand(right));
- __ add(TMP, result_mod, compiler::Operand(right));
+ __ CompareObjectRegisters(right, ZR);
+ __ sub(TMP2, result_mod, compiler::Operand(right), compiler::kObjectBytes);
+ __ add(TMP, result_mod, compiler::Operand(right), compiler::kObjectBytes);
__ csel(result_mod, TMP, TMP2, GE);
__ Bind(&done);
}
@@ -5898,7 +5914,7 @@
if (index_loc.IsConstant()) {
const Register length = length_loc.reg();
const Smi& index = Smi::Cast(index_loc.constant());
- __ CompareImmediate(length, static_cast<int64_t>(index.ptr()));
+ __ CompareObject(length, index);
__ b(deopt, LS);
} else if (length_loc.IsConstant()) {
const Smi& length = Smi::Cast(length_loc.constant());
@@ -5907,10 +5923,10 @@
__ BranchIfNotSmi(index, deopt);
}
if (length.Value() == Smi::kMaxValue) {
- __ tst(index, compiler::Operand(index));
+ __ tst(index, compiler::Operand(index), compiler::kObjectBytes);
__ b(deopt, MI);
} else {
- __ CompareImmediate(index, static_cast<int64_t>(length.ptr()));
+ __ CompareObject(index, length);
__ b(deopt, CS);
}
} else {
@@ -5919,7 +5935,7 @@
if (index_cid != kSmiCid) {
__ BranchIfNotSmi(index, deopt);
}
- __ CompareRegisters(index, length);
+ __ CompareObjectRegisters(index, length);
__ b(deopt, CS);
}
}
@@ -6759,7 +6775,6 @@
}
case kUnboxedInt64: {
ASSERT(to() == kUnboxedDouble);
-
const Register from_reg = locs()->in(0).reg();
const FpuRegister to_reg = locs()->out(0).fpu_reg();
__ fmovdr(to_reg, from_reg);
@@ -6895,7 +6910,7 @@
if (!needs_number_check() && compiler::target::IsSmi(obj) &&
compiler::target::ToRawSmi(obj) == 0 &&
CanUseCbzTbzForComparison(compiler, reg, orig_cond, labels)) {
- EmitCbzTbz(reg, compiler, orig_cond, labels);
+ EmitCbzTbz(reg, compiler, orig_cond, labels, compiler::kObjectBytes);
return kInvalidCondition;
} else {
return compiler->EmitEqualityRegConstCompare(reg, obj, needs_number_check(),
diff --git a/runtime/vm/compiler/backend/il_test_helper.cc b/runtime/vm/compiler/backend/il_test_helper.cc
index ab32cd4..b860562 100644
--- a/runtime/vm/compiler/backend/il_test_helper.cc
+++ b/runtime/vm/compiler/backend/il_test_helper.cc
@@ -21,8 +21,6 @@
namespace dart {
-Definition* const FlowGraphBuilderHelper::kPhiSelfReference = nullptr;
-
LibraryPtr LoadTestScript(const char* script,
Dart_NativeEntryResolver resolver,
const char* lib_uri) {
diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h
index e34a5fe..7ef8db3 100644
--- a/runtime/vm/compiler/backend/il_test_helper.h
+++ b/runtime/vm/compiler/backend/il_test_helper.h
@@ -5,6 +5,7 @@
#ifndef RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
#define RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
+#include <type_traits>
#include <utility>
#include <vector>
@@ -277,20 +278,48 @@
return flow_graph_.GetConstant(Double::Handle(Double::NewCanonical(value)));
}
- static Definition* const kPhiSelfReference;
+ enum class IncomingDefKind {
+ kImmediate,
+ kDelayed,
+ };
+
+ class IncomingDef {
+ public:
+ IncomingDef(BlockEntryInstr* from, Definition* defn)
+ : kind_(IncomingDefKind::kImmediate), from_(from), defn_(defn) {}
+
+ template <typename T,
+ typename = typename std::enable_if<
+ std::is_base_of<Definition, T>::value>::type>
+ IncomingDef(BlockEntryInstr* from, T** defn_source)
+ : kind_(IncomingDefKind::kDelayed),
+ from_(from),
+ defn_source_(reinterpret_cast<Definition**>(defn_source)) {}
+
+ BlockEntryInstr* from() const { return from_; }
+ Definition* defn() const {
+ return kind_ == IncomingDefKind::kImmediate ? defn_ : *defn_source_;
+ }
+
+ private:
+ IncomingDefKind kind_;
+ BlockEntryInstr* from_;
+ union {
+ Definition* defn_;
+ Definition** defn_source_;
+ };
+ };
PhiInstr* Phi(JoinEntryInstr* join,
- std::initializer_list<std::pair<BlockEntryInstr*, Definition*>>
- incoming) {
+ std::initializer_list<IncomingDef> incoming) {
auto phi = new PhiInstr(join, incoming.size());
for (size_t i = 0; i < incoming.size(); i++) {
auto input = new Value(flow_graph_.constant_dead());
phi->SetInputAt(i, input);
input->definition()->AddInputUse(input);
}
- for (auto pair : incoming) {
- pending_phis_.Add({phi, pair.first,
- pair.second == kPhiSelfReference ? phi : pair.second});
+ for (auto def : incoming) {
+ pending_phis_.Add({phi, def});
}
return phi;
}
@@ -303,9 +332,9 @@
for (auto& pending : pending_phis_) {
auto join = pending.phi->block();
EXPECT(pending.phi->InputCount() == join->PredecessorCount());
- auto pred_index = join->IndexOfPredecessor(pending.pred);
+ auto pred_index = join->IndexOfPredecessor(pending.incoming.from());
EXPECT(pred_index != -1);
- pending.phi->InputAt(pred_index)->BindTo(pending.defn);
+ pending.phi->InputAt(pred_index)->BindTo(pending.incoming.defn());
}
}
@@ -347,8 +376,7 @@
struct PendingPhiInput {
PhiInstr* phi;
- BlockEntryInstr* pred;
- Definition* defn;
+ IncomingDef incoming;
};
GrowableArray<PendingPhiInput> pending_phis_;
};
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 971d6cc..681164a 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -88,6 +88,14 @@
ASSERT(kSmiTagSize == 1);
const Register index = locs()->in(0).reg();
+#if defined(DART_COMPRESSED_POINTERS)
+ // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
+ // to clear the upper bits as this instructions uses negative indices as part
+ // of FP-relative loads.
+ // TODO(compressed-pointers): Can we guarentee the index is already
+ // sign-extended if always comes for an args-descriptor load?
+ __ movsxd(index, index);
+#endif
switch (representation()) {
case kTagged:
@@ -111,6 +119,14 @@
(NoLocation, Register index, Register value)) {
ASSERT(instr->RequiredInputRepresentation(
StoreIndexedUnsafeInstr::kIndexPos) == kTagged); // It is a Smi.
+#if defined(DART_COMPRESSED_POINTERS)
+ // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
+ // to clear the upper bits as this instructions uses negative indices as part
+ // of FP-relative stores.
+ // TODO(compressed-pointers): Can we guarentee the index is already
+ // sign-extended if always comes for an args-descriptor load?
+ __ movsxd(index, index);
+#endif
__ movq(compiler::Address(instr->base_reg(), index, TIMES_4, instr->offset()),
value);
@@ -788,6 +804,44 @@
}
}
+static Condition EmitSmiComparisonOp(FlowGraphCompiler* compiler,
+ const LocationSummary& locs,
+ Token::Kind kind) {
+ Location left = locs.in(0);
+ Location right = locs.in(1);
+ ASSERT(!left.IsConstant() || !right.IsConstant());
+
+ Condition true_condition = TokenKindToIntCondition(kind);
+ if (left.IsConstant() || right.IsConstant()) {
+ // Ensure constant is on the right.
+ ConstantInstr* constant = NULL;
+ if (left.IsConstant()) {
+ constant = left.constant_instruction();
+ Location tmp = right;
+ right = left;
+ left = tmp;
+ true_condition = FlipCondition(true_condition);
+ } else {
+ constant = right.constant_instruction();
+ }
+
+ if (RepresentationUtils::IsUnboxedInteger(constant->representation())) {
+ int64_t value;
+ const bool ok = compiler::HasIntegerValue(constant->value(), &value);
+ RELEASE_ASSERT(ok);
+ __ OBJ(cmp)(left.reg(), compiler::Immediate(value));
+ } else {
+ ASSERT(constant->representation() == kTagged);
+ __ CompareObject(left.reg(), right.constant());
+ }
+ } else if (right.IsStackSlot()) {
+ __ OBJ(cmp)(left.reg(), LocationToStackSlotAddress(right));
+ } else {
+ __ OBJ(cmp)(left.reg(), right.reg());
+ }
+ return true_condition;
+}
+
static Condition EmitInt64ComparisonOp(FlowGraphCompiler* compiler,
const LocationSummary& locs,
Token::Kind kind) {
@@ -815,8 +869,7 @@
RELEASE_ASSERT(ok);
__ cmpq(left.reg(), compiler::Immediate(value));
} else {
- ASSERT(constant->representation() == kTagged);
- __ CompareObject(left.reg(), right.constant());
+ UNREACHABLE();
}
} else if (right.IsStackSlot()) {
__ cmpq(left.reg(), LocationToStackSlotAddress(right));
@@ -864,7 +917,9 @@
Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
BranchLabels labels) {
- if ((operation_cid() == kSmiCid) || (operation_cid() == kMintCid)) {
+ if (operation_cid() == kSmiCid) {
+ return EmitSmiComparisonOp(compiler, *locs(), kind());
+ } else if (operation_cid() == kMintCid) {
return EmitInt64ComparisonOp(compiler, *locs(), kind());
} else {
ASSERT(operation_cid() == kDoubleCid);
@@ -917,9 +972,10 @@
if (right.IsConstant()) {
ASSERT(right.constant().IsSmi());
const int64_t imm = static_cast<int64_t>(right.constant().ptr());
- __ TestImmediate(left_reg, compiler::Immediate(imm));
+ __ TestImmediate(left_reg, compiler::Immediate(imm),
+ compiler::kObjectBytes);
} else {
- __ testq(left_reg, right.reg());
+ __ OBJ(test)(left_reg, right.reg());
}
Condition true_condition = (kind() == Token::kNE) ? NOT_ZERO : ZERO;
return true_condition;
@@ -1010,7 +1066,9 @@
Condition RelationalOpInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
BranchLabels labels) {
- if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
+ if (operation_cid() == kSmiCid) {
+ return EmitSmiComparisonOp(compiler, *locs(), kind());
+ } else if (operation_cid() == kMintCid) {
return EmitInt64ComparisonOp(compiler, *locs(), kind());
} else {
ASSERT(operation_cid() == kDoubleCid);
@@ -1614,7 +1672,7 @@
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
locs->set_in(0, Location::RequiresRegister());
// For tagged index with index_scale=1 as well as untagged index with
- // index_scale=16 we need a writable register due to assdressing mode
+ // index_scale=16 we need a writable register due to addressing mode
// restrictions on X64.
const bool need_writable_index_register =
(index_scale() == 1 && !index_unboxed_) ||
@@ -1648,6 +1706,16 @@
// X64 does not support addressing mode using TIMES_16.
__ SmiTag(index.reg());
index_scale >>= 1;
+ } else if (!index_unboxed_) {
+#if defined(DART_COMPRESSED_POINTERS)
+ // The upper half of a compressed Smi contains undefined bits, but no x64
+ // addressing mode will ignore these bits. Assume that the index is
+ // non-negative and clear the upper bits, which is shorter than
+ // sign-extension (movsxd). Note: we don't bother to ensure index is a
+ // writable input because any other instructions using it must also not
+ // rely on the upper bits.
+ __ orl(index.reg(), index.reg());
+#endif
}
} else {
ASSERT(index.IsConstant());
@@ -1754,6 +1822,16 @@
if ((index_scale() == 1)) {
__ SmiUntag(index.reg());
+ } else {
+#if defined(DART_COMPRESSED_POINTERS)
+ // The upper half of a compressed Smi contains undefined bits, but no x64
+ // addressing mode will ignore these bits. Assume that the index is
+ // non-negative and clear the upper bits, which is shorter than
+ // sign-extension (movsxd). Note: we don't bother to ensure index is a
+ // writable input because any other instructions using it must also not
+ // rely on the upper bits.
+ __ orl(index.reg(), index.reg());
+#endif
}
Register result = locs()->out(0).reg();
switch (class_id()) {
@@ -1851,7 +1929,7 @@
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
locs->set_in(0, Location::RequiresRegister());
// For tagged index with index_scale=1 as well as untagged index with
- // index_scale=16 we need a writable register due to assdressing mode
+ // index_scale=16 we need a writable register due to addressing mode
// restrictions on X64.
const bool need_writable_index_register =
(index_scale() == 1 && !index_unboxed_) ||
@@ -1923,6 +2001,16 @@
// X64 does not support addressing mode using TIMES_16.
__ SmiTag(index.reg());
index_scale >>= 1;
+ } else if (!index_unboxed_) {
+#if defined(DART_COMPRESSED_POINTERS)
+ // The upper half of a compressed Smi contains undefined bits, but no x64
+ // addressing mode will ignore these bits. Assume that the index is
+ // non-negative and clear the upper bits, which is shorter than
+ // sign-extension (movsxd). Note: we don't bother to ensure index is a
+ // writable input because any other instructions using it must also not
+ // rely on the upper bits.
+ __ orl(index.reg(), index.reg());
+#endif
}
} else {
ASSERT(index.IsConstant());
@@ -3401,37 +3489,20 @@
if (shift_left->can_overflow()) {
if (value == 1) {
// Use overflow flag.
-#if !defined(DART_COMPRESSED_POINTERS)
- __ shlq(left, compiler::Immediate(1));
+ __ OBJ(shl)(left, compiler::Immediate(1));
__ j(OVERFLOW, deopt);
-#else
- __ shll(left, compiler::Immediate(1));
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
-#endif
return;
}
// Check for overflow.
Register temp = locs.temp(0).reg();
- __ movq(temp, left);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ shlq(left, compiler::Immediate(value));
- __ sarq(left, compiler::Immediate(value));
-#else
- __ shll(left, compiler::Immediate(value));
- __ sarl(left, compiler::Immediate(value));
- __ movsxd(left, left);
-#endif
- __ cmpq(left, temp);
+ __ OBJ(mov)(temp, left);
+ __ OBJ(shl)(left, compiler::Immediate(value));
+ __ OBJ(sar)(left, compiler::Immediate(value));
+ __ OBJ(cmp)(left, temp);
__ j(NOT_EQUAL, deopt); // Overflow.
}
// Shift for result now we know there is no overflow.
- __ shlq(left, compiler::Immediate(value));
-#if defined(DART_COMPRESSED_POINTERS)
- if (shift_left->is_truncating()) {
- __ movsxd(left, left);
- }
-#endif
+ __ OBJ(shl)(left, compiler::Immediate(value));
return;
}
@@ -3445,7 +3516,8 @@
if (obj.IsSmi()) {
const intptr_t left_int = Smi::Cast(obj).Value();
if (left_int == 0) {
- __ CompareImmediate(right, compiler::Immediate(0));
+ __ CompareImmediate(right, compiler::Immediate(0),
+ compiler::kObjectBytes);
__ j(NEGATIVE, deopt);
return;
}
@@ -3453,20 +3525,11 @@
const bool right_needs_check =
!RangeUtils::IsWithin(right_range, 0, max_right - 1);
if (right_needs_check) {
- __ CompareImmediate(
- right,
- compiler::Immediate(static_cast<int64_t>(Smi::New(max_right))));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(max_right)));
__ j(ABOVE_EQUAL, deopt);
}
__ SmiUntag(right);
- __ shlq(left, right);
-#if defined(DART_COMPRESSED_POINTERS)
- if (shift_left->is_truncating()) {
- __ movsxd(left, left);
- }
-#endif
- } else {
- __ int3(); //???
+ __ OBJ(shl)(left, right);
}
return;
}
@@ -3480,59 +3543,41 @@
(right_range == NULL) || !right_range->IsPositive();
if (right_may_be_negative) {
ASSERT(shift_left->CanDeoptimize());
- __ CompareImmediate(right, compiler::Immediate(0));
+ __ CompareImmediate(right, compiler::Immediate(0),
+ compiler::kObjectBytes);
__ j(NEGATIVE, deopt);
}
compiler::Label done, is_not_zero;
- __ CompareImmediate(
- right,
- compiler::Immediate(static_cast<int64_t>(Smi::New(Smi::kBits))));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
__ j(BELOW, &is_not_zero, compiler::Assembler::kNearJump);
__ xorq(left, left);
__ jmp(&done, compiler::Assembler::kNearJump);
__ Bind(&is_not_zero);
__ SmiUntag(right);
- __ shlq(left, right);
+ __ OBJ(shl)(left, right);
__ Bind(&done);
} else {
__ SmiUntag(right);
- __ shlq(left, right);
+ __ OBJ(shl)(left, right);
}
-#if defined(DART_COMPRESSED_POINTERS)
- if (shift_left->is_truncating()) {
- __ movsxd(left, left);
- }
-#endif
} else {
if (right_needs_check) {
ASSERT(shift_left->CanDeoptimize());
- __ CompareImmediate(
- right,
- compiler::Immediate(static_cast<int64_t>(Smi::New(Smi::kBits))));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
__ j(ABOVE_EQUAL, deopt);
}
// Left is not a constant.
Register temp = locs.temp(0).reg();
// Check if count too large for handling it inlined.
-#if !defined(DART_COMPRESSED_POINTERS)
- __ movq(temp, left);
-#else
- __ movl(temp, left);
-#endif
+ __ OBJ(mov)(temp, left);
__ SmiUntag(right);
// Overflow test (preserve temp and right);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ shlq(left, right);
- __ sarq(left, right);
- __ cmpq(left, temp);
-#else
- __ shll(temp, right);
- __ sarl(temp, right);
- __ cmpl(temp, left);
-#endif
+ __ OBJ(shl)(left, right);
+ __ OBJ(sar)(left, right);
+ __ OBJ(cmp)(left, temp);
__ j(NOT_EQUAL, deopt); // Overflow.
// Shift for result now we know there is no overflow.
- __ shlq(left, right);
+ __ OBJ(shl)(left, right);
ASSERT(!shift_left->is_truncating());
}
}
@@ -3637,37 +3682,19 @@
switch (op_kind()) {
case Token::kADD:
__ movq(result, left);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ addq(result, right);
+ __ OBJ(add)(result, right);
__ j(OVERFLOW, slow_path->entry_label());
-#else
- __ addl(result, right);
- __ j(OVERFLOW, slow_path->entry_label());
- __ movsxd(result, result);
-#endif
break;
case Token::kSUB:
__ movq(result, left);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subq(result, right);
+ __ OBJ(sub)(result, right);
__ j(OVERFLOW, slow_path->entry_label());
-#else
- __ subl(result, right);
- __ j(OVERFLOW, slow_path->entry_label());
- __ movsxd(result, result);
-#endif
break;
case Token::kMUL:
__ movq(result, left);
__ SmiUntag(result);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ imulq(result, right);
+ __ OBJ(imul)(result, right);
__ j(OVERFLOW, slow_path->entry_label());
-#else
- __ imull(result, right);
- __ j(OVERFLOW, slow_path->entry_label());
- __ movsxd(result, result);
-#endif
break;
case Token::kBIT_OR:
ASSERT(left == result);
@@ -3684,54 +3711,50 @@
case Token::kSHL:
ASSERT(result != right);
ASSERT(locs()->temp(0).reg() == RCX);
- __ cmpq(right, compiler::Immediate(Smi::RawValue(Smi::kBits)));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
__ j(ABOVE_EQUAL, slow_path->entry_label());
__ movq(RCX, right);
__ SmiUntag(RCX);
__ movq(result, left);
-#if !defined(DART_COMPRESSED_POINTERS)
__ shlq(result, RCX);
__ movq(TMP, result);
- __ sarq(TMP, RCX);
- __ cmpq(TMP, left);
+ __ OBJ(sar)(TMP, RCX);
+ __ OBJ(cmp)(TMP, left);
__ j(NOT_EQUAL, slow_path->entry_label());
-#else
- __ shll(result, RCX);
- __ movq(TMP, result);
- __ sarl(TMP, RCX);
- __ cmpl(TMP, left);
- __ j(NOT_EQUAL, slow_path->entry_label());
- __ movsxd(result, result);
-#endif
break;
case Token::kSHR:
- case Token::kUSHR: {
- compiler::Label shift_count_ok;
ASSERT(result != right);
ASSERT(locs()->temp(0).reg() == RCX);
- __ cmpq(right, compiler::Immediate(Smi::RawValue(kBitsPerInt64)));
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
__ j(ABOVE_EQUAL, slow_path->entry_label());
__ movq(RCX, right);
__ SmiUntag(RCX);
__ movq(result, left);
__ SmiUntag(result);
- if (op_kind() == Token::kSHR) {
- __ sarq(result, RCX);
- __ SmiTag(result);
- } else {
- ASSERT(op_kind() == Token::kUSHR);
- __ shrq(result, RCX);
- __ SmiTag(result);
- __ j(OVERFLOW, slow_path->entry_label());
+ __ OBJ(sar)(result, RCX);
+ __ SmiTag(result);
+ break;
+ case Token::kUSHR: {
+ ASSERT(result != right);
+ ASSERT(locs()->temp(0).reg() == RCX);
+ __ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
+ __ j(ABOVE_EQUAL, slow_path->entry_label());
+
+ __ movq(RCX, right);
+ __ SmiUntag(RCX);
+ __ movq(result, left);
+ __ SmiUntagAndSignExtend(result);
+ __ shrq(result, RCX);
+ __ shlq(result, compiler::Immediate(1)); // SmiTag, keep hi bits.
+ __ j(OVERFLOW, slow_path->entry_label());
#if defined(DART_COMPRESSED_POINTERS)
- const Register temp = locs()->temp(0).reg();
- __ movsxd(temp, result);
- __ cmpq(temp, result);
- __ j(NOT_EQUAL, slow_path->entry_label());
+ const Register temp = locs()->temp(0).reg();
+ __ movsxd(temp, result);
+ __ cmpq(temp, result);
+ __ j(NOT_EQUAL, slow_path->entry_label());
#endif // defined(DART_COMPRESSED_POINTERS)
- }
break;
}
default:
@@ -3826,7 +3849,7 @@
Condition CheckedSmiComparisonInstr::EmitComparisonCode(
FlowGraphCompiler* compiler,
BranchLabels labels) {
- return EmitInt64ComparisonOp(compiler, *locs(), kind());
+ return EmitSmiComparisonOp(compiler, *locs(), kind());
}
#define EMIT_SMI_CHECK \
@@ -4030,57 +4053,21 @@
const int64_t imm = static_cast<int64_t>(constant.ptr());
switch (op_kind()) {
case Token::kADD: {
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ AddImmediate(left, compiler::Immediate(imm));
- } else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ AddImmediate(left, compiler::Immediate(imm));
- __ j(OVERFLOW, deopt);
-#else
- __ AddImmediate(left, compiler::Immediate(imm), compiler::kFourBytes);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
-#endif
- }
+ __ AddImmediate(left, compiler::Immediate(imm), compiler::kObjectBytes);
+ if (deopt != NULL) __ j(OVERFLOW, deopt);
break;
}
case Token::kSUB: {
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ SubImmediate(left, compiler::Immediate(imm));
- } else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ SubImmediate(left, compiler::Immediate(imm));
- __ j(OVERFLOW, deopt);
-#else
- __ SubImmediate(left, compiler::Immediate(imm), compiler::kFourBytes);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
-#endif
- }
+ __ SubImmediate(left, compiler::Immediate(imm), compiler::kObjectBytes);
+ if (deopt != NULL) __ j(OVERFLOW, deopt);
break;
}
case Token::kMUL: {
// Keep left value tagged and untag right value.
const intptr_t value = Smi::Cast(constant).Value();
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ MulImmediate(left, compiler::Immediate(value));
- } else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ MulImmediate(left, compiler::Immediate(value));
- __ j(OVERFLOW, deopt);
-#else
- __ MulImmediate(left, compiler::Immediate(value),
- compiler::kFourBytes);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
-#endif
- }
+ __ MulImmediate(left, compiler::Immediate(value),
+ compiler::kObjectBytes);
+ if (deopt != NULL) __ j(OVERFLOW, deopt);
break;
}
case Token::kTRUNCDIV: {
@@ -4095,16 +4082,19 @@
#if !defined(DART_COMPRESSED_POINTERS)
__ sarq(temp, compiler::Immediate(63));
#else
- // Assumes Smis are sign extended.
- __ sarq(temp, compiler::Immediate(31));
+ __ sarl(temp, compiler::Immediate(31));
#endif
ASSERT(shift_count > 1); // 1, -1 case handled above.
+#if !defined(DART_COMPRESSED_POINTERS)
__ shrq(temp, compiler::Immediate(64 - shift_count));
- __ addq(left, temp);
+#else
+ __ shrl(temp, compiler::Immediate(32 - shift_count));
+#endif
+ __ OBJ(add)(left, temp);
ASSERT(shift_count > 0);
- __ sarq(left, compiler::Immediate(shift_count));
+ __ OBJ(sar)(left, compiler::Immediate(shift_count));
if (value < 0) {
- __ negq(left);
+ __ OBJ(neg)(left);
}
__ SmiTag(left);
break;
@@ -4126,15 +4116,15 @@
}
case Token::kSHR: {
- // sarq operation masks the count to 6 bits.
+ // sarq/l operation masks the count to 6/5 bits.
#if !defined(DART_COMPRESSED_POINTERS)
const intptr_t kCountLimit = 0x3F;
#else
const intptr_t kCountLimit = 0x1F;
#endif
const intptr_t value = Smi::Cast(constant).Value();
- __ sarq(left, compiler::Immediate(
- Utils::Minimum(value + kSmiTagSize, kCountLimit)));
+ __ OBJ(sar)(left, compiler::Immediate(Utils::Minimum(
+ value + kSmiTagSize, kCountLimit)));
__ SmiTag(left);
break;
}
@@ -4146,9 +4136,9 @@
const intptr_t kCountLimit = 0x3F;
const intptr_t value = Smi::Cast(constant).Value();
ASSERT((value >= 0) && (value <= kCountLimit));
- __ SmiUntag(left);
+ __ SmiUntagAndSignExtend(left);
__ shrq(left, compiler::Immediate(value));
- __ SmiTag(left);
+ __ shlq(left, compiler::Immediate(1)); // SmiTag, keep hi bits.
if (deopt != nullptr) {
__ j(OVERFLOW, deopt);
#if defined(DART_COMPRESSED_POINTERS)
@@ -4172,55 +4162,19 @@
const compiler::Address& right = LocationToStackSlotAddress(locs()->in(1));
switch (op_kind()) {
case Token::kADD: {
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ addq(left, right);
- } else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ addq(left, right);
- __ j(OVERFLOW, deopt);
-#else
- __ addl(left, right);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
-#endif
- }
+ __ OBJ(add)(left, right);
+ if (deopt != NULL) __ j(OVERFLOW, deopt);
break;
}
case Token::kSUB: {
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ subq(left, right);
- } else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subq(left, right);
- __ j(OVERFLOW, deopt);
-#else
- __ subl(left, right);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
-#endif
- }
+ __ OBJ(sub)(left, right);
+ if (deopt != NULL) __ j(OVERFLOW, deopt);
break;
}
case Token::kMUL: {
__ SmiUntag(left);
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ imulq(left, right);
- } else {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ imulq(left, right);
- __ j(OVERFLOW, deopt);
-#else
- __ imull(left, right);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
-#endif
- }
+ __ OBJ(imul)(left, right);
+ if (deopt != NULL) __ j(OVERFLOW, deopt);
break;
}
case Token::kBIT_AND: {
@@ -4249,55 +4203,19 @@
Register right = locs()->in(1).reg();
switch (op_kind()) {
case Token::kADD: {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ addq(left, right);
+ __ OBJ(add)(left, right);
if (deopt != NULL) __ j(OVERFLOW, deopt);
-#else
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ addq(left, right);
- } else {
- __ addl(left, right);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
- }
-#endif
break;
}
case Token::kSUB: {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ subq(left, right);
+ __ OBJ(sub)(left, right);
if (deopt != NULL) __ j(OVERFLOW, deopt);
-#else
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ subq(left, right);
- } else {
- __ subl(left, right);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
- }
-#endif
break;
}
case Token::kMUL: {
__ SmiUntag(left);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ imulq(left, right);
+ __ OBJ(imul)(left, right);
if (deopt != NULL) __ j(OVERFLOW, deopt);
-#else
- if (deopt == NULL) {
- // When we can't overflow, prefer 64-bit op to 32-bit op followed by
- // sign extension.
- __ imulq(left, right);
- } else {
- __ imull(left, right);
- __ j(OVERFLOW, deopt);
- __ movsxd(left, left);
- }
-#endif
break;
}
case Token::kBIT_AND: {
@@ -4325,7 +4243,7 @@
ASSERT(result == RAX);
if (RangeUtils::CanBeZero(right_range())) {
// Handle divide by zero in runtime.
- __ testq(right, right);
+ __ OBJ(test)(right, right);
__ j(ZERO, deopt);
}
#if !defined(DART_COMPRESSED_POINTERS)
@@ -4390,7 +4308,7 @@
ASSERT(result == RDX);
if (RangeUtils::CanBeZero(right_range())) {
// Handle divide by zero in runtime.
- __ testq(right, right);
+ __ OBJ(test)(right, right);
__ j(ZERO, deopt);
}
#if !defined(DART_COMPRESSED_POINTERS)
@@ -4434,23 +4352,23 @@
// }
// }
compiler::Label all_done;
- __ cmpq(result, compiler::Immediate(0));
+ __ OBJ(cmp)(result, compiler::Immediate(0));
__ j(GREATER_EQUAL, &all_done, compiler::Assembler::kNearJump);
// Result is negative, adjust it.
if (RangeUtils::Overlaps(right_range(), -1, 1)) {
compiler::Label subtract;
- __ cmpq(right, compiler::Immediate(0));
+ __ OBJ(cmp)(right, compiler::Immediate(0));
__ j(LESS, &subtract, compiler::Assembler::kNearJump);
- __ addq(result, right);
+ __ OBJ(add)(result, right);
__ jmp(&all_done, compiler::Assembler::kNearJump);
__ Bind(&subtract);
- __ subq(result, right);
+ __ OBJ(sub)(result, right);
} else if (right_range()->IsPositive()) {
// Right is positive.
- __ addq(result, right);
+ __ OBJ(add)(result, right);
} else {
// Right is negative.
- __ subq(result, right);
+ __ OBJ(sub)(result, right);
}
__ Bind(&all_done);
__ SmiTag(result);
@@ -4458,12 +4376,17 @@
}
case Token::kSHR: {
if (CanDeoptimize()) {
- __ CompareImmediate(right, compiler::Immediate(0));
+ __ CompareImmediate(right, compiler::Immediate(0),
+ compiler::kObjectBytes);
__ j(LESS, deopt);
}
__ SmiUntag(right);
- // sarq operation masks the count to 6 bits.
+ // sarq/l operation masks the count to 6/5 bits.
+#if !defined(DART_COMPRESSED_POINTERS)
const intptr_t kCountLimit = 0x3F;
+#else
+ const intptr_t kCountLimit = 0x1F;
+#endif
if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
__ CompareImmediate(right, compiler::Immediate(kCountLimit));
compiler::Label count_ok;
@@ -4473,13 +4396,14 @@
}
ASSERT(right == RCX); // Count must be in RCX
__ SmiUntag(left);
- __ sarq(left, right);
+ __ OBJ(sar)(left, right);
__ SmiTag(left);
break;
}
case Token::kUSHR: {
if (deopt != nullptr) {
- __ CompareImmediate(right, compiler::Immediate(0));
+ __ CompareImmediate(right, compiler::Immediate(0),
+ compiler::kObjectBytes);
__ j(LESS, deopt);
}
__ SmiUntag(right);
@@ -4488,7 +4412,8 @@
COMPILE_ASSERT(kCountLimit + 1 == kBitsPerInt64);
compiler::Label done;
if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
- __ CompareImmediate(right, compiler::Immediate(kCountLimit));
+ __ CompareImmediate(right, compiler::Immediate(kCountLimit),
+ compiler::kObjectBytes);
compiler::Label count_ok;
__ j(LESS_EQUAL, &count_ok, compiler::Assembler::kNearJump);
__ xorq(left, left);
@@ -4496,9 +4421,9 @@
__ Bind(&count_ok);
}
ASSERT(right == RCX); // Count must be in RCX
- __ SmiUntag(left);
+ __ SmiUntagAndSignExtend(left);
__ shrq(left, right);
- __ SmiTag(left);
+ __ shlq(left, compiler::Immediate(1)); // SmiTag, keep hi bits.
if (deopt != nullptr) {
__ j(OVERFLOW, deopt);
#if defined(DART_COMPRESSED_POINTERS)
@@ -4674,14 +4599,14 @@
case kUnboxedInt64: {
const Register result = locs()->out(0).reg();
ASSERT(result == box);
- __ SmiUntag(box);
+ __ SmiUntagAndSignExtend(box);
break;
}
case kUnboxedDouble: {
const FpuRegister result = locs()->out(0).fpu_reg();
__ SmiUntag(box);
- __ cvtsi2sdq(result, box);
+ __ OBJ(cvtsi2sd)(result, box);
break;
}
@@ -4696,9 +4621,22 @@
const Register result = locs()->out(0).reg();
ASSERT(value == result);
compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
__ SmiUntag(value);
__ j(NOT_CARRY, &done, compiler::Assembler::kNearJump);
__ movsxw(result, compiler::Address(value, TIMES_2, Mint::value_offset()));
+#else
+ // Cannot speculatively untag because it erases the upper bits needed to
+ // dereference when it is a Mint.
+ // TODO(compressed-pointers): It would probably be cheaper to make
+ // result != value with compressed pointers.
+ compiler::Label not_smi;
+ __ BranchIfNotSmi(value, ¬_smi, compiler::Assembler::kNearJump);
+ __ SmiUntagAndSignExtend(value);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(¬_smi);
+ __ movsxw(result, compiler::FieldAddress(value, Mint::value_offset()));
+#endif
__ Bind(&done);
}
@@ -4707,10 +4645,22 @@
const Register result = locs()->out(0).reg();
ASSERT(value == result);
compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
__ SmiUntag(value);
__ j(NOT_CARRY, &done, compiler::Assembler::kNearJump);
__ movq(value, compiler::Address(value, TIMES_2, Mint::value_offset()));
__ Bind(&done);
+#else
+ // Cannot speculatively untag because it erases the upper bits needed to
+ // dereference when it is a Mint.
+ compiler::Label not_smi;
+ __ BranchIfNotSmi(value, ¬_smi, compiler::Assembler::kNearJump);
+ __ SmiUntagAndSignExtend(value);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(¬_smi);
+ __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
+ __ Bind(&done);
+#endif
}
LocationSummary* UnboxInteger32Instr::MakeLocationSummary(Zone* zone,
@@ -4746,23 +4696,42 @@
// or mint value.
ASSERT(is_truncating());
compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
__ SmiUntag(value);
__ j(NOT_CARRY, &done, compiler::Assembler::kNearJump);
__ movq(value, compiler::Address(value, TIMES_2, Mint::value_offset()));
- __ Bind(&done);
-#if defined(DART_COMPRESSED_POINTERS)
- if (is_truncating()) {
- __ movsxd(value, value);
- }
+#else
+ // Cannot speculatively untag because it erases the upper bits needed to
+ // dereference when it is a Mint.
+ compiler::Label not_smi;
+ __ BranchIfNotSmi(value, ¬_smi, compiler::Assembler::kNearJump);
+ __ SmiUntagAndSignExtend(value);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(¬_smi);
+ __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
#endif
+ __ Bind(&done);
return;
} else {
compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
// Optimistically untag value.
__ SmiUntagOrCheckClass(value, kMintCid, &done);
__ j(NOT_EQUAL, deopt);
// Undo untagging by multiplying value with 2.
__ movq(value, compiler::Address(value, TIMES_2, Mint::value_offset()));
+#else
+ // Cannot speculatively untag because it erases the upper bits needed to
+ // dereference when it is a Mint.
+ compiler::Label not_smi;
+ __ BranchIfNotSmi(value, ¬_smi, compiler::Assembler::kNearJump);
+ __ SmiUntagAndSignExtend(value);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(¬_smi);
+ __ CompareClassId(value, kMintCid);
+ __ j(NOT_EQUAL, deopt);
+ __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
+#endif
__ Bind(&done);
}
@@ -5523,12 +5492,7 @@
case Token::kNEGATE: {
compiler::Label* deopt =
compiler->AddDeoptStub(deopt_id(), ICData::kDeoptUnaryOp);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ negq(value);
-#else
- __ negl(value);
- __ movsxd(value, value);
-#endif
+ __ OBJ(neg)(value);
__ j(OVERFLOW, deopt);
break;
}
@@ -5639,7 +5603,7 @@
Register left = locs()->in(0).reg();
Register right = locs()->in(1).reg();
Register result = locs()->out(0).reg();
- __ cmpq(left, right);
+ __ OBJ(cmp)(left, right);
ASSERT(result == left);
if (is_min) {
__ cmovgeq(result, right);
@@ -5680,11 +5644,7 @@
Register value = locs()->in(0).reg();
FpuRegister result = locs()->out(0).fpu_reg();
__ SmiUntag(value);
-#if !defined(DART_COMPRESSED_POINTERS)
- __ cvtsi2sdq(result, value);
-#else
- __ cvtsi2sdl(result, value);
-#endif
+ __ OBJ(cvtsi2sd)(result, value);
}
DEFINE_BACKEND(Int64ToDouble, (FpuRegister result, Register value)) {
@@ -5713,25 +5673,14 @@
ASSERT(result != temp);
__ movsd(value_double,
compiler::FieldAddress(value_obj, Double::value_offset()));
-#if !defined(DART_COMPRESSED_POINTERS)
- __ cvttsd2siq(result, value_double);
-#else
- __ cvttsd2sil(result, value_double);
-#endif
+ __ OBJ(cvttsd2si)(result, value_double);
// Overflow is signalled with minint.
compiler::Label do_call, done;
// Check for overflow and that it fits into Smi.
-#if !defined(DART_COMPRESSED_POINTERS)
__ movq(temp, result);
- __ shlq(temp, compiler::Immediate(1));
+ __ OBJ(shl)(temp, compiler::Immediate(1));
__ j(OVERFLOW, &do_call, compiler::Assembler::kNearJump);
__ SmiTag(result);
-#else
- __ movl(temp, result);
- __ shll(temp, compiler::Immediate(1));
- __ j(OVERFLOW, &do_call, compiler::Assembler::kNearJump);
- __ movsxd(result, temp);
-#endif
__ jmp(&done);
__ Bind(&do_call);
__ pushq(value_obj);
@@ -5770,26 +5719,14 @@
XmmRegister value = locs()->in(0).fpu_reg();
Register temp = locs()->temp(0).reg();
-#if !defined(DART_COMPRESSED_POINTERS)
- __ cvttsd2siq(result, value);
-#else
- __ cvttsd2sil(result, value);
-#endif
+ __ OBJ(cvttsd2si)(result, value);
// Overflow is signalled with minint.
compiler::Label do_call, done;
// Check for overflow and that it fits into Smi.
-#if !defined(DART_COMPRESSED_POINTERS)
__ movq(temp, result);
- __ shlq(temp, compiler::Immediate(1));
+ __ OBJ(shl)(temp, compiler::Immediate(1));
__ j(OVERFLOW, deopt);
__ SmiTag(result);
-#else
- __ movl(temp, result);
- __ shll(temp, compiler::Immediate(1));
- __ j(OVERFLOW, deopt);
- ASSERT(kSmiTagShift == 1 && kSmiTag == 0);
- __ movsxd(result, temp);
-#endif
}
LocationSummary* DoubleToDoubleInstr::MakeLocationSummary(Zone* zone,
@@ -6113,7 +6050,7 @@
ASSERT(result2 == RDX);
if (RangeUtils::CanBeZero(divisor_range())) {
// Handle divide by zero in runtime.
- __ testq(right, right);
+ __ OBJ(test)(right, right);
__ j(ZERO, deopt);
}
#if !defined(DART_COMPRESSED_POINTERS)
@@ -6397,8 +6334,7 @@
if (index_loc.IsConstant()) {
Register length = length_loc.reg();
const Smi& index = Smi::Cast(index_loc.constant());
- __ CompareImmediate(length,
- compiler::Immediate(static_cast<int64_t>(index.ptr())));
+ __ CompareObject(length, index);
__ j(BELOW_EQUAL, deopt);
} else if (length_loc.IsConstant()) {
const Smi& length = Smi::Cast(length_loc.constant());
@@ -6407,11 +6343,10 @@
__ BranchIfNotSmi(index, deopt);
}
if (length.Value() == Smi::kMaxValue) {
- __ testq(index, index);
+ __ OBJ(test)(index, index);
__ j(NEGATIVE, deopt);
} else {
- __ CompareImmediate(
- index, compiler::Immediate(static_cast<int64_t>(length.ptr())));
+ __ CompareObject(index, length);
__ j(ABOVE_EQUAL, deopt);
}
} else {
@@ -6420,7 +6355,7 @@
if (index_cid != kSmiCid) {
__ BranchIfNotSmi(index, deopt);
}
- __ cmpq(index, length);
+ __ OBJ(cmp)(index, length);
__ j(ABOVE_EQUAL, deopt);
}
}
diff --git a/runtime/vm/compiler/backend/inliner_test.cc b/runtime/vm/compiler/backend/inliner_test.cc
index 9a53a7b..26eb9e5 100644
--- a/runtime/vm/compiler/backend/inliner_test.cc
+++ b/runtime/vm/compiler/backend/inliner_test.cc
@@ -222,8 +222,8 @@
RELEASE_ASSERT(cursor.TryMatch({
kMoveGlob,
kMatchAndMoveCreateArray,
- kMatchAndMoveUnboxInt64,
{kMoveAny, &unbox1},
+ kMatchAndMoveUnboxInt64,
{kMoveAny, &unbox2},
kMatchAndMoveGoto,
diff --git a/runtime/vm/compiler/backend/loops_test.cc b/runtime/vm/compiler/backend/loops_test.cc
index 8f1201e..1722f3b 100644
--- a/runtime/vm/compiler/backend/loops_test.cc
+++ b/runtime/vm/compiler/backend/loops_test.cc
@@ -68,9 +68,13 @@
Invoke(root_library, "main");
std::initializer_list<CompilerPass::Id> passes = {
- CompilerPass::kComputeSSA, CompilerPass::kTypePropagation,
- CompilerPass::kApplyICData, CompilerPass::kSelectRepresentations,
- CompilerPass::kTypePropagation, CompilerPass::kCanonicalize,
+ CompilerPass::kComputeSSA,
+ CompilerPass::kTypePropagation,
+ CompilerPass::kApplyICData,
+ CompilerPass::kTypePropagation,
+ CompilerPass::kSelectRepresentations,
+ CompilerPass::kTypePropagation,
+ CompilerPass::kCanonicalize,
};
const auto& function = Function::Handle(GetFunction(root_library, "foo"));
TestPipeline pipeline(function, CompilerPass::kJIT);
@@ -300,7 +304,7 @@
const char* expected =
" [0\n"
" WRAP(99, LIN(0 + 1 * i))\n" // phi (w)
- " LIN(0 + 1 * i) 100\n" // phi
+ " LIN(0 + 1 * i) 100\n" // phi (i)
" WRAP(102, LIN(3 + 1 * i))\n" // a
" WRAP(94, LIN(-5 + 1 * i))\n" // b
" WRAP(693, LIN(0 + 7 * i))\n" // c
@@ -383,11 +387,15 @@
const char* expected =
" [0\n"
" LIN(9223372036854775806 + 1 * i)\n" // phi
+#if !defined(TARGET_ARCH_IS_64_BIT)
" LIN(9223372036854775806 + 1 * i)\n" // (un)boxing
" LIN(9223372036854775806 + 1 * i)\n"
" LIN(9223372036854775806 + 1 * i)\n"
+#endif // !defined(TARGET_ARCH_IS_64_BIT)
" LIN(9223372036854775807 + 1 * i)\n" // add
+#if !defined(TARGET_ARCH_IS_64_BIT)
" LIN(9223372036854775807 + 1 * i)\n" // unbox
+#endif // !defined(TARGET_ARCH_IS_64_BIT)
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
@@ -426,11 +434,15 @@
const char* expected =
" [0\n"
" LIN(-9223372036854775807 + -1 * i)\n" // phi
+#if !defined(TARGET_ARCH_IS_64_BIT)
" LIN(-9223372036854775807 + -1 * i)\n" // (un)boxing
" LIN(-9223372036854775807 + -1 * i)\n"
" LIN(-9223372036854775807 + -1 * i)\n"
+#endif // !defined(TARGET_ARCH_IS_64_BIT)
" LIN(-9223372036854775808 + -1 * i)\n" // sub
+#if !defined(TARGET_ARCH_IS_64_BIT)
" LIN(-9223372036854775808 + -1 * i)\n" // unbox
+#endif // !defined(TARGET_ARCH_IS_64_BIT)
" ]\n";
EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
}
diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni
index 8cc4b39..9135f50 100644
--- a/runtime/vm/compiler/compiler_sources.gni
+++ b/runtime/vm/compiler/compiler_sources.gni
@@ -160,6 +160,7 @@
"assembler/disassembler_test.cc",
"backend/bce_test.cc",
"backend/constant_propagator_test.cc",
+ "backend/flow_graph_test.cc",
"backend/il_test.cc",
"backend/il_test_helper.h",
"backend/il_test_helper.cc",
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 022ba2c..c3c6fbc 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -1134,7 +1134,7 @@
// Check length >= 0 && length <= kMaxNewSpaceElements
const intptr_t max_len =
target::ToRawSmi(target::Array::kMaxNewSpaceElements);
- __ CompareImmediate(R2, max_len);
+ __ CompareImmediate(R2, max_len, kObjectBytes);
__ b(&slow_case, HI);
const intptr_t cid = kArrayCid;
@@ -1149,7 +1149,7 @@
target::Array::header_size() +
target::ObjectAlignment::kObjectAlignment - 1;
__ LoadImmediate(R3, fixed_size_plus_alignment_padding);
- __ add(R3, R3, Operand(R2, LSL, 2)); // R2 is Smi.
+ __ add(R3, R3, Operand(R2, LSL, 2), kObjectBytes); // R2 is Smi.
ASSERT(kSmiTagShift == 1);
__ andi(R3, R3,
Immediate(~(target::ObjectAlignment::kObjectAlignment - 1)));
@@ -2177,25 +2177,19 @@
__ BranchIfNotSmi(TMP, not_smi_or_overflow);
switch (kind) {
case Token::kADD: {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ adds(R0, R1, Operand(R0)); // Add.
+ __ adds(R0, R1, Operand(R0), kObjectBytes); // Add.
__ b(not_smi_or_overflow, VS); // Branch if overflow.
-#else
- __ addsw(R0, R1, Operand(R0)); // Add (32-bit).
- __ b(not_smi_or_overflow, VS); // Branch if overflow.
- __ sxtw(R0, R0); // Sign extend.
-#endif
break;
}
case Token::kLT: {
- __ CompareRegisters(R0, R1);
+ __ CompareObjectRegisters(R0, R1);
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
__ LoadObject(R1, CastHandle<Object>(FalseObject()));
__ csel(R0, R0, R1, LT);
break;
}
case Token::kEQ: {
- __ CompareRegisters(R0, R1);
+ __ CompareObjectRegisters(R0, R1);
__ LoadObject(R0, CastHandle<Object>(TrueObject()));
__ LoadObject(R1, CastHandle<Object>(FalseObject()));
__ csel(R0, R0, R1, EQ);
@@ -3136,7 +3130,8 @@
// Double values bitwise compare.
__ LoadFieldFromOffset(left, left, target::Double::value_offset());
__ LoadFieldFromOffset(right, right, target::Double::value_offset());
- __ b(&reference_compare);
+ __ CompareRegisters(left, right);
+ __ b(&done);
__ Bind(&check_mint);
__ CompareClassId(left, kMintCid);
@@ -3145,9 +3140,11 @@
__ b(&done, NE);
__ LoadFieldFromOffset(left, left, target::Mint::value_offset());
__ LoadFieldFromOffset(right, right, target::Mint::value_offset());
+ __ CompareRegisters(left, right);
+ __ b(&done);
__ Bind(&reference_compare);
- __ CompareRegisters(left, right);
+ __ CompareObjectRegisters(left, right);
__ Bind(&done);
}
@@ -3584,7 +3581,7 @@
__ SmiUntag(R2);
/* Check for length >= 0 && length <= max_len. */
/* R2: untagged array length. */
- __ CompareImmediate(R2, max_len);
+ __ CompareImmediate(R2, max_len, kObjectBytes);
__ b(&call_runtime, HI);
__ LslImmediate(R2, R2, scale_shift);
const intptr_t fixed_size_plus_alignment_padding =
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 93bd854c6..db74b1f 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -1067,7 +1067,7 @@
// Check length >= 0 && length <= kMaxNewSpaceElements
const Immediate& max_len =
Immediate(target::ToRawSmi(target::Array::kMaxNewSpaceElements));
- __ cmpq(RDI, max_len);
+ __ OBJ(cmp)(RDI, max_len);
__ j(ABOVE, &slow_case);
// Check for allocation tracing.
@@ -1078,7 +1078,7 @@
target::Array::header_size() +
target::ObjectAlignment::kObjectAlignment - 1;
// RDI is a Smi.
- __ leaq(RDI, Address(RDI, TIMES_4, fixed_size_plus_alignment_padding));
+ __ OBJ(lea)(RDI, Address(RDI, TIMES_4, fixed_size_plus_alignment_padding));
ASSERT(kSmiTagShift == 1);
__ andq(RDI, Immediate(-target::ObjectAlignment::kObjectAlignment));
@@ -2106,18 +2106,12 @@
__ j(NOT_ZERO, not_smi_or_overflow);
switch (kind) {
case Token::kADD: {
-#if !defined(DART_COMPRESSED_POINTERS)
- __ addq(RAX, RCX);
+ __ OBJ(add)(RAX, RCX);
__ j(OVERFLOW, not_smi_or_overflow);
-#else
- __ addl(RAX, RCX);
- __ j(OVERFLOW, not_smi_or_overflow);
- __ movsxd(RAX, RAX);
-#endif
break;
}
case Token::kLT: {
- __ cmpq(RAX, RCX);
+ __ OBJ(cmp)(RAX, RCX);
__ setcc(GREATER_EQUAL, ByteRegisterOf(RAX));
__ movzxb(RAX, RAX); // RAX := RAX < RCX ? 0 : 1
__ movq(RAX,
@@ -2127,7 +2121,7 @@
break;
}
case Token::kEQ: {
- __ cmpq(RAX, RCX);
+ __ OBJ(cmp)(RAX, RCX);
__ setcc(NOT_EQUAL, ByteRegisterOf(RAX));
__ movzxb(RAX, RAX); // RAX := RAX == RCX ? 0 : 1
__ movq(RAX,
@@ -3102,7 +3096,7 @@
__ jmp(&done, Assembler::kFarJump);
__ Bind(&reference_compare);
- __ cmpq(left, right);
+ __ CompareObjectRegisters(left, right);
__ Bind(&done);
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index cdda785..535c62e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -8552,9 +8552,8 @@
Heap::Space space = Heap::kNew) const;
static int64_t GetInt64Value(const IntegerPtr obj) {
- intptr_t raw_value = static_cast<intptr_t>(obj);
- if ((raw_value & kSmiTagMask) == kSmiTag) {
- return (raw_value >> kSmiTagShift);
+ if (obj->IsSmi()) {
+ return RawSmiValue(static_cast<const SmiPtr>(obj));
} else {
ASSERT(obj->IsMint());
return static_cast<const MintPtr>(obj)->untag()->value_;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index ebd6127..67656c6 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -2878,7 +2878,7 @@
EXPECT(Smi::Cast(result).Value() == kSmiTestValue);
}
-#if defined(ARCH_IS_64_BIT)
+#if defined(ARCH_IS_64_BIT) && !defined(DART_COMPRESSED_POINTERS)
// Test for Embedded Smi object in the instructions.
ISOLATE_UNIT_TEST_CASE(EmbedSmiIn64BitCode) {
extern void GenerateEmbedSmiInCode(compiler::Assembler * assembler,
@@ -2898,7 +2898,7 @@
Object::Handle(DartEntry::InvokeFunction(function, Array::empty_array()));
EXPECT(Smi::Cast(result).Value() == kSmiTestValue);
}
-#endif // ARCH_IS_64_BIT
+#endif // ARCH_IS_64_BIT && !DART_COMPRESSED_POINTERS
ISOLATE_UNIT_TEST_CASE(ExceptionHandlers) {
const int kNumEntries = 4;
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index b3ee8a3..4f29d72 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -1128,7 +1128,14 @@
// First check if it is a Smi (i.e not a heap object).
if (!rawobj->IsHeapObject()) {
+#if !defined(DART_COMPRESSED_POINTERS)
Write<int64_t>(static_cast<intptr_t>(rawobj));
+#else
+ // One might expect this to be unnecessary because the reader will just
+ // ignore the upper bits, but the upper bits affect the variable-length
+ // encoding and can change lower bits in the variable-length reader.
+ Write<int64_t>(static_cast<intptr_t>(rawobj) << 32 >> 32);
+#endif
return true;
}
diff --git a/runtime/vm/tagged_pointer.h b/runtime/vm/tagged_pointer.h
index 63b446f..6302427 100644
--- a/runtime/vm/tagged_pointer.h
+++ b/runtime/vm/tagged_pointer.h
@@ -349,7 +349,12 @@
#undef DEFINE_TAGGED_POINTER
inline intptr_t RawSmiValue(const SmiPtr raw_value) {
+#if !defined(DART_COMPRESSED_POINTERS)
const intptr_t value = static_cast<intptr_t>(raw_value);
+#else
+ const intptr_t value = static_cast<intptr_t>(static_cast<int32_t>(
+ static_cast<uint32_t>(static_cast<uintptr_t>(raw_value))));
+#endif
ASSERT((value & kSmiTagMask) == kSmiTag);
return (value >> kSmiTagShift);
}
diff --git a/tests/language/const_functions/const_functions_variable_assignments_test.dart b/tests/language/const_functions/const_functions_variable_assignments_test.dart
new file mode 100644
index 0000000..f2c47cb
--- /dev/null
+++ b/tests/language/const_functions/const_functions_variable_assignments_test.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2021, 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.
+
+// Tests variable assignments for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+// ^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int varAssignmentTest(int a) {
+ int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+
+int function() {
+ int varAssignmentTest2() {
+ int x = 2;
+ x += 1;
+ return x;
+ }
+
+ const var2 = varAssignmentTest2();
+ // ^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+ return var2;
+}
+
+const var3 = varAssignmentTest3(1);
+// ^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int varAssignmentTest3(int a) {
+ int x = 4;
+ x = a + 1;
+ return x;
+}
+
+void main() {
+ Expect.equals(var1, 3);
+ Expect.equals(function(), 3);
+ Expect.equals(var3, 2);
+}
diff --git a/tools/VERSION b/tools/VERSION
index b6cf424..5b87bd5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 130
+PRERELEASE 131
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/lib/src/firestore.dart b/tools/bots/lib/src/firestore.dart
index 166173c..80b2315 100644
--- a/tools/bots/lib/src/firestore.dart
+++ b/tools/bots/lib/src/firestore.dart
@@ -21,8 +21,11 @@
/// tools/bots.
class FirestoreDatabase {
final http.Client _client = http.Client();
- final String _authToken;
- final String _project;
+ final Map<String, String> _headers;
+ final Uri _documentsUrl;
+ final Uri _queryUrl;
+ final Uri _beginTransactionUrl;
+ final Uri _commitUrl;
/// The current transaction ID in base64 (or `null`)
String _currentTransaction;
@@ -34,27 +37,30 @@
.replaceAll("+", "%2B");
}
- FirestoreDatabase(this._project, this._authToken);
+ FirestoreDatabase._(this._headers, this._documentsUrl, this._queryUrl,
+ this._beginTransactionUrl, this._commitUrl);
- static const apiUrl = 'https://firestore.googleapis.com/v1beta1';
-
- String get projectUrl => '$apiUrl/projects/$_project';
-
- String get documentsUrl => '$projectUrl/databases/(default)/documents';
-
- String get queryUrl => '$documentsUrl:runQuery';
-
- Map<String, String> get _headers {
- return {
- 'Authorization': 'Bearer $_authToken',
+ factory FirestoreDatabase(String project, String authToken) {
+ var databaseUrl = _apiUrl.resolve('projects/$project/databases/(default)/');
+ var documentsUrl = databaseUrl.resolve('documents/');
+ var queryUrl = databaseUrl.resolveUri(Uri(path: 'documents:runQuery'));
+ var beginTransactionUrl =
+ databaseUrl.resolveUri(Uri(path: 'documents:beginTransaction'));
+ var commitUrl = databaseUrl.resolveUri(Uri(path: 'documents:commit'));
+ var headers = {
+ 'Authorization': 'Bearer $authToken',
'Accept': 'application/json',
'Content-Type': 'application/json'
};
+ return FirestoreDatabase._(
+ headers, documentsUrl, queryUrl, beginTransactionUrl, commitUrl);
}
+ static final _apiUrl = Uri.https('firestore.googleapis.com', 'v1beta1/');
+
Future<List /*!*/ > runQuery(Query query) async {
var body = jsonEncode(query.data);
- var response = await _client.post(queryUrl, headers: _headers, body: body);
+ var response = await _client.post(_queryUrl, headers: _headers, body: body);
if (response.statusCode == HttpStatus.ok) {
return jsonDecode(response.body);
} else {
@@ -63,10 +69,11 @@
}
Future<Map> getDocument(String collectionName, String documentName) async {
- var url = '$documentsUrl/$collectionName/$documentName';
- if (_currentTransaction != null) {
- url = '$url?transaction=${_escapedCurrentTransaction}';
- }
+ var url = _documentsUrl.resolveUri(Uri(
+ path: '$collectionName/$documentName',
+ query: _currentTransaction == null
+ ? null
+ : 'transaction=${_escapedCurrentTransaction}'));
var response = await _client.get(url, headers: _headers);
if (response.statusCode == HttpStatus.ok) {
var document = jsonDecode(response.body);
@@ -80,7 +87,8 @@
}
Future<Object> updateField(Map document, String field) async {
- var url = '$apiUrl/${document["name"]}?updateMask.fieldPaths=$field';
+ var url =
+ _apiUrl.resolve('${document["name"]}?updateMask.fieldPaths=$field');
var response =
await _client.patch(url, headers: _headers, body: jsonEncode(document));
if (response.statusCode == HttpStatus.ok) {
@@ -94,9 +102,9 @@
if (_currentTransaction != null) {
throw Exception('Error: nested transactions');
}
- var url = '$documentsUrl:beginTransaction';
var body = '{"options": {}}';
- var response = await _client.post(url, headers: _headers, body: body);
+ var response =
+ await _client.post(_beginTransactionUrl, headers: _headers, body: body);
if (response.statusCode == HttpStatus.ok) {
var result = jsonDecode(response.body);
_currentTransaction = result['transaction'] as String;
@@ -116,8 +124,8 @@
"writes": writes.map((write) => write.data).toList(),
"transaction": "$_currentTransaction"
});
- var url = '$documentsUrl:commit';
- var response = await _client.post(url, headers: _headers, body: body);
+ var response =
+ await _client.post(_commitUrl, headers: _headers, body: body);
_currentTransaction = null;
if (response.statusCode == HttpStatus.conflict) {
// This HTTP status code corresponds to the ABORTED error code, see