[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