[vm/bytecode] Eliminate AssertBoolean instructions if condition is non-nullable
Number of AssertBoolean instructions in the 100Mb dynamic trace of
Richards benchmark in pure interpreted mode (platform with bytecode):
Before: 1812005
After: 251451
Change-Id: I9fe9d37499d73ffa7636c9ca5fb9c2c18dbc1397
Reviewed-on: https://dart-review.googlesource.com/c/84643
Auto-Submit: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Zach Anderson <zra@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index d2a0a23..3f07302 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -27,6 +27,7 @@
import 'dbc.dart';
import 'exceptions.dart';
import 'local_vars.dart' show LocalVariables;
+import 'nullability_detector.dart' show NullabilityDetector;
import 'recognized_methods.dart' show RecognizedMethods;
import '../constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
import '../metadata/bytecode.dart';
@@ -80,6 +81,7 @@
final ErrorReporter errorReporter;
final BytecodeMetadataRepository metadata = new BytecodeMetadataRepository();
final RecognizedMethods recognizedMethods;
+ NullabilityDetector nullabilityDetector;
Class enclosingClass;
Member enclosingMember;
@@ -119,6 +121,7 @@
this.useFutureBytecodeFormat,
this.errorReporter)
: recognizedMethods = new RecognizedMethods(typeEnvironment) {
+ nullabilityDetector = new NullabilityDetector(recognizedMethods);
component.addMetadataRepository(metadata);
}
@@ -661,7 +664,9 @@
negated = true;
}
_generateNode(condition);
- asm.emitAssertBoolean(0);
+ if (nullabilityDetector.isNullable(condition)) {
+ asm.emitAssertBoolean(0);
+ }
return negated;
}
diff --git a/pkg/vm/lib/bytecode/nullability_detector.dart b/pkg/vm/lib/bytecode/nullability_detector.dart
new file mode 100644
index 0000000..961dfef
--- /dev/null
+++ b/pkg/vm/lib/bytecode/nullability_detector.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2018, 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.
+
+library vm.bytecode.nullability_detector;
+
+import 'package:kernel/ast.dart';
+import 'dbc.dart';
+import 'recognized_methods.dart' show RecognizedMethods;
+
+class NullabilityDetector {
+ final _IsNullableVisitor _isNullableVisitor;
+
+ NullabilityDetector(RecognizedMethods recognizedMethods)
+ : _isNullableVisitor = new _IsNullableVisitor(recognizedMethods);
+
+ bool isNullable(Expression expr) => expr.accept(_isNullableVisitor);
+}
+
+class _IsNullableVisitor extends ExpressionVisitor<bool> {
+ final RecognizedMethods recognizedMethods;
+
+ _IsNullableVisitor(this.recognizedMethods);
+
+ @override
+ bool defaultExpression(Expression node) => true;
+
+ @override
+ bool visitNullLiteral(NullLiteral node) => true;
+
+ // All basic literals except NullLiteral are non-nullable.
+ @override
+ bool defaultBasicLiteral(BasicLiteral node) => false;
+
+ @override
+ bool visitVariableGet(VariableGet node) {
+ final v = node.variable;
+ if ((v.isConst || v.isFinal) && v.initializer != null) {
+ return v.initializer.accept(this);
+ }
+ return true;
+ }
+
+ @override
+ bool visitVariableSet(VariableSet node) => node.value.accept(this);
+
+ @override
+ bool visitMethodInvocation(MethodInvocation node) {
+ final Opcode opcode = recognizedMethods.specializedBytecodeFor(node);
+ if (opcode != null) {
+ return !_nonNullableBytecodeInstructions.contains(opcode);
+ }
+ return true;
+ }
+
+ @override
+ bool visitConstructorInvocation(ConstructorInvocation node) => false;
+
+ @override
+ bool visitNot(Not node) => false;
+
+ @override
+ bool visitLogicalExpression(LogicalExpression node) => false;
+
+ @override
+ bool visitConditionalExpression(ConditionalExpression node) =>
+ node.then.accept(this) || node.otherwise.accept(this);
+
+ @override
+ bool visitStringConcatenation(StringConcatenation node) => false;
+
+ @override
+ bool visitIsExpression(IsExpression node) => false;
+
+ @override
+ bool visitAsExpression(AsExpression node) => node.operand.accept(this);
+
+ @override
+ bool visitSymbolLiteral(SymbolLiteral node) => false;
+
+ @override
+ bool visitTypeLiteral(TypeLiteral node) => false;
+
+ @override
+ bool visitThisExpression(ThisExpression node) => false;
+
+ @override
+ bool visitRethrow(Rethrow node) => false;
+
+ @override
+ bool visitThrow(Throw node) => false;
+
+ @override
+ bool visitListLiteral(ListLiteral node) => false;
+
+ @override
+ bool visitMapLiteral(MapLiteral node) => false;
+
+ @override
+ bool visitFunctionExpression(FunctionExpression node) => false;
+
+ @override
+ bool visitConstantExpression(ConstantExpression node) =>
+ node.constant is NullConstant;
+
+ @override
+ bool visitLet(Let node) => node.body.accept(this);
+
+ @override
+ bool visitInstantiation(Instantiation node) => false;
+}
+
+final _nonNullableBytecodeInstructions = new Set<Opcode>.from([
+ Opcode.kBooleanNegateTOS,
+ Opcode.kEqualsNull,
+ Opcode.kNegateInt,
+ Opcode.kAddInt,
+ Opcode.kSubInt,
+ Opcode.kMulInt,
+ Opcode.kTruncDivInt,
+ Opcode.kModInt,
+ Opcode.kBitAndInt,
+ Opcode.kBitOrInt,
+ Opcode.kBitXorInt,
+ Opcode.kShlInt,
+ Opcode.kShrInt,
+ Opcode.kCompareIntEq,
+ Opcode.kCompareIntGt,
+ Opcode.kCompareIntLt,
+ Opcode.kCompareIntGe,
+ Opcode.kCompareIntLe,
+]);
diff --git a/pkg/vm/testcases/bytecode/async.dart.expect b/pkg/vm/testcases/bytecode/async.dart.expect
index 174ed5c..200441b 100644
--- a/pkg/vm/testcases/bytecode/async.dart.expect
+++ b/pkg/vm/testcases/bytecode/async.dart.expect
@@ -903,7 +903,6 @@
LoadContextVar 0
PushInt 10
CompareIntLt
- AssertBoolean 0
JumpIfFalse L2
Push r4
LoadContextParent
@@ -1038,7 +1037,6 @@
Push r8
PushInt 10
CompareIntLt
- AssertBoolean 0
JumpIfFalse L7
Push r4
Push r4
@@ -1405,7 +1403,6 @@
LoadContextVar 1
PushConstant CP#12
InstanceCall 2, CP#13
- AssertBoolean 0
JumpIfFalse L4
Push r4
LoadContextParent
diff --git a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
index 785494f..774c558 100644
--- a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
+++ b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
@@ -119,7 +119,6 @@
PushConstant CP#0
PushStatic CP#0
EqualsNull
- AssertBoolean 0
JumpIfFalse L1
Allocate CP#1
StoreLocal r1
@@ -294,12 +293,10 @@
PushConstant CP#0
PushStatic CP#0
EqualsNull
- AssertBoolean 0
JumpIfFalse L1
PushConstant CP#1
PushStatic CP#1
EqualsNull
- AssertBoolean 0
BooleanNegateTOS
PopLocal r0
Jump L2
@@ -308,7 +305,6 @@
PopLocal r0
L2:
Push r0
- AssertBoolean 0
JumpIfFalse L3
PushConstant CP#1
PushStatic CP#1
@@ -493,7 +489,6 @@
PopLocal r1
L2:
Push r1
- AssertBoolean 0
JumpIfTrue L3
PushConstant CP#0
PushStatic CP#0
@@ -507,7 +502,6 @@
PopLocal r0
L4:
Push r0
- AssertBoolean 0
JumpIfFalse L5
PushConstant CP#0
PushStatic CP#0
@@ -575,7 +569,6 @@
PushConstant CP#0
PushStatic CP#0
EqualsNull
- AssertBoolean 0
JumpIfFalse L1
PushConstant CP#1
PushStatic CP#1
diff --git a/pkg/vm/testcases/bytecode/closures.dart.expect b/pkg/vm/testcases/bytecode/closures.dart.expect
index b76eb92..654a544 100644
--- a/pkg/vm/testcases/bytecode/closures.dart.expect
+++ b/pkg/vm/testcases/bytecode/closures.dart.expect
@@ -683,7 +683,6 @@
LoadContextVar 1
PushInt 5
CompareIntGt
- AssertBoolean 0
JumpIfFalse L1
Push r0
PushInt 4
@@ -831,7 +830,6 @@
LoadContextVar 0
PushInt 10
CompareIntLt
- AssertBoolean 0
JumpIfFalse L1
Push r2
Allocate CP#8
diff --git a/pkg/vm/testcases/bytecode/loops.dart.expect b/pkg/vm/testcases/bytecode/loops.dart.expect
index 3d0fc28..77b4601 100644
--- a/pkg/vm/testcases/bytecode/loops.dart.expect
+++ b/pkg/vm/testcases/bytecode/loops.dart.expect
@@ -16,7 +16,6 @@
Push FP[-5]
InstanceCall 1, CP#1
CompareIntLt
- AssertBoolean 0
JumpIfFalse L1
Push r0
Push FP[-5]
@@ -60,13 +59,11 @@
Push r1
PushInt 0
CompareIntGe
- AssertBoolean 0
JumpIfFalse L1
Push r1
Push FP[-5]
InstanceCall 1, CP#1
CompareIntGe
- AssertBoolean 0
JumpIfFalse L2
Jump L1
L2:
@@ -118,12 +115,10 @@
Push FP[-5]
InstanceCall 1, CP#1
CompareIntLt
- AssertBoolean 0
JumpIfFalse L1
Push r1
PushInt 0
CompareIntLt
- AssertBoolean 0
JumpIfFalse L2
Jump L3
L2:
@@ -176,7 +171,6 @@
Push FP[-5]
InstanceCall 1, CP#1
CompareIntLt
- AssertBoolean 0
JumpIfFalse L1
Push r0
Push FP[-5]
@@ -234,7 +228,6 @@
Push FP[-5]
InstanceCall 1, CP#3
CompareIntLt
- AssertBoolean 0
JumpIfTrue L1
Push r0
ReturnTOS
diff --git a/pkg/vm/testcases/bytecode/try_blocks.dart.expect b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
index 2bee951..130ebb2 100644
--- a/pkg/vm/testcases/bytecode/try_blocks.dart.expect
+++ b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
@@ -623,13 +623,11 @@
Push r0
PushInt 10
CompareIntLt
- AssertBoolean 0
JumpIfFalse L1
Try #0 start:
Push r0
PushInt 5
CompareIntGt
- AssertBoolean 0
JumpIfFalse L2
Jump L3
L2:
@@ -668,7 +666,7 @@
ReturnTOS
}
ExceptionsTable {
- try-index 0, outer -1, start 10, end 17, handler 17, needs-stack-trace, types [CP#0]
+ try-index 0, outer -1, start 9, end 15, handler 15, needs-stack-trace, types [CP#0]
}
ConstantPool {
[0] = Type dynamic
diff --git a/pkg/vm/testcases/bytecode/type_ops.dart.expect b/pkg/vm/testcases/bytecode/type_ops.dart.expect
index 6a04383..292a062 100644
--- a/pkg/vm/testcases/bytecode/type_ops.dart.expect
+++ b/pkg/vm/testcases/bytecode/type_ops.dart.expect
@@ -107,7 +107,6 @@
PushNull
PushConstant CP#1
InstanceCall 4, CP#3
- AssertBoolean 0
JumpIfFalse L1
PushConstant CP#4
PushConstant CP#6
@@ -120,7 +119,6 @@
PushNull
PushConstant CP#7
InstanceCall 4, CP#8
- AssertBoolean 0
JumpIfFalse L2
PushConstant CP#9
PushConstant CP#10
@@ -177,7 +175,6 @@
Push r0
PushConstant CP#0
InstanceCall 4, CP#2
- AssertBoolean 0
JumpIfFalse L1
PushConstant CP#3
PushConstant CP#5
@@ -190,7 +187,6 @@
Push r0
PushConstant CP#7
InstanceCall 4, CP#8
- AssertBoolean 0
JumpIfFalse L2
PushConstant CP#9
PushConstant CP#10
@@ -334,7 +330,6 @@
Push FP[-5]
PushConstant CP#0
InstanceCall 2, CP#2
- AssertBoolean 0
JumpIfFalse L1
PushConstant CP#3
PushConstant CP#5
@@ -346,7 +341,6 @@
PushNull
PushConstant CP#6
InstanceCall 4, CP#8
- AssertBoolean 0
JumpIfFalse L2
PushConstant CP#9
PushConstant CP#10