[vm/kernel/bytecode] Generate bytecode for assertions, bool checks

In addition:

* Use InstanceCall1 instead of InstanceCall2 for now as InstanceCall2
  requires ICData objects with 2 checked arguments.

* Remove try-catch around bytecode generation as all operations are
  supported and silent fallback is no longer needed.

Change-Id: Iead5a4997450cc47ef3bb3221647e733b7a72b62
Reviewed-on: https://dart-review.googlesource.com/62443
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/bytecode/assembler.dart b/pkg/vm/lib/bytecode/assembler.dart
index d5fa43b..3ccf658 100644
--- a/pkg/vm/lib/bytecode/assembler.dart
+++ b/pkg/vm/lib/bytecode/assembler.dart
@@ -67,6 +67,10 @@
     bytecode.addAll(_encodeBufferOut);
   }
 
+  int _getOpcodeAt(int pos) {
+    return bytecode[pos]; // TODO(alexmarkov): Take endianness into account.
+  }
+
   void _setWord(int pos, int word) {
     _encodeBufferIn[0] = word; // TODO(alexmarkov): Which endianness to use?
     bytecode.setRange(pos, pos + _encodeBufferOut.length, _encodeBufferOut);
@@ -166,8 +170,14 @@
     emitWord(_encodeT(Opcode.kJump, label.jumpOperand(offset)));
   }
 
+  void emitJumpIfNoAsserts(Label label) {
+    emitWord(_encodeT(Opcode.kJumpIfNoAsserts, label.jumpOperand(offset)));
+  }
+
   void patchJump(int pos, int rt) {
-    _setWord(pos, _encodeT(Opcode.kJump, rt));
+    final Opcode opcode = Opcode.values[_getOpcodeAt(pos)];
+    assert(isJump(opcode));
+    _setWord(pos, _encodeT(opcode, rt));
   }
 
   void emitReturn(int ra) {
diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart
index c2e6216..a3b002b 100644
--- a/pkg/vm/lib/bytecode/dbc.dart
+++ b/pkg/vm/lib/bytecode/dbc.dart
@@ -18,6 +18,10 @@
 // 3. NativeCall instruction is modified to have 'D' format and take 1 argument:
 //    D = index of NativeEntry constant pool entry
 //
+// 4. JumpIfNoAsserts instruction is added. This instruction jumps to the given
+//    target if assertions are not enabled. It has the same format as Jump
+//    instruction.
+//
 
 enum Opcode {
   kTrap,
@@ -29,6 +33,7 @@
   kDropR,
   kDrop,
   kJump,
+  kJumpIfNoAsserts,
   kReturn,
   kReturnTOS,
   kMove,
@@ -266,6 +271,8 @@
       Encoding.kA, const [Operand.imm, Operand.none, Operand.none]),
   Opcode.kJump: const Format(
       Encoding.kT, const [Operand.tgt, Operand.none, Operand.none]),
+  Opcode.kJumpIfNoAsserts: const Format(
+      Encoding.kT, const [Operand.tgt, Operand.none, Operand.none]),
   Opcode.kReturn: const Format(
       Encoding.kA, const [Operand.reg, Operand.none, Operand.none]),
   Opcode.kReturnTOS: const Format(
@@ -651,3 +658,5 @@
   exception,
   stackTrace,
 }
+
+bool isJump(Opcode opcode) => BytecodeFormats[opcode].encoding == Encoding.kT;
diff --git a/pkg/vm/lib/bytecode/disassembler.dart b/pkg/vm/lib/bytecode/disassembler.dart
index 279d492..be1e109 100644
--- a/pkg/vm/lib/bytecode/disassembler.dart
+++ b/pkg/vm/lib/bytecode/disassembler.dart
@@ -95,7 +95,7 @@
   void _scanForJumpTargets() {
     for (int i = 0; i < _instructions.length; i++) {
       final instr = _instructions[i];
-      if (instr.opcode == Opcode.kJump) {
+      if (isJump(instr.opcode)) {
         final target = i + instr.operands[0];
         assert(0 <= target && target < _instructions.length);
         if (!_labels.containsKey(target)) {
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 986874d..c3c76cf 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -30,8 +30,6 @@
 /// Flag to toggle generation of bytecode in platform kernel files.
 const bool isKernelBytecodeEnabledForPlatform = isKernelBytecodeEnabled;
 
-const bool isTraceEnabled = false;
-
 void generateBytecode(Component component,
     {bool strongMode: true, bool dropAST: false}) {
   final coreTypes = new CoreTypes(component);
@@ -107,42 +105,36 @@
     if (node.isAbstract) {
       return;
     }
-    try {
-      if (node is Field) {
-        if (node.isStatic && node.initializer != null) {
-          start(node);
-          if (node.isConst) {
-            _genPushConstExpr(node.initializer);
-          } else {
-            node.initializer.accept(this);
-          }
-          _genReturnTOS();
-          end(node);
-        }
-      } else if ((node is Procedure && !node.isRedirectingFactoryConstructor) ||
-          (node is Constructor)) {
+    if (node is Field) {
+      if (node.isStatic && node.initializer != null) {
         start(node);
-        if (node is Constructor) {
-          _genConstructorInitializers(node);
-        }
-        if (node.isExternal) {
-          final String nativeName = getExternalName(node);
-          if (nativeName == null) {
-            return;
-          }
-          _genNativeCall(nativeName);
+        if (node.isConst) {
+          _genPushConstExpr(node.initializer);
         } else {
-          node.function?.body?.accept(this);
-          // TODO(alexmarkov): figure out when 'return null' should be generated.
-          _genPushNull();
+          node.initializer.accept(this);
         }
         _genReturnTOS();
         end(node);
       }
-    } on UnsupportedOperationError catch (e) {
-      if (isTraceEnabled) {
-        print('Unable to generate bytecode for $node: $e');
+    } else if ((node is Procedure && !node.isRedirectingFactoryConstructor) ||
+        (node is Constructor)) {
+      start(node);
+      if (node is Constructor) {
+        _genConstructorInitializers(node);
       }
+      if (node.isExternal) {
+        final String nativeName = getExternalName(node);
+        if (nativeName == null) {
+          return;
+        }
+        _genNativeCall(nativeName);
+      } else {
+        node.function?.body?.accept(this);
+        // TODO(alexmarkov): figure out when 'return null' should be generated.
+        _genPushNull();
+      }
+      _genReturnTOS();
+      end(node);
     }
   }
 
@@ -230,6 +222,10 @@
   Procedure get futureValue =>
       _futureValue ??= libraryIndex.getMember('dart:async', 'Future', 'value');
 
+  Procedure _throwNewAssertionError;
+  Procedure get throwNewAssertionError => _throwNewAssertionError ??=
+      libraryIndex.getMember('dart:core', '_AssertionError', '_throwNew');
+
   void _genConstructorInitializers(Constructor node) {
     bool isRedirecting =
         node.initializers.any((init) => init is RedirectingInitializer);
@@ -491,14 +487,14 @@
   }
 
   /// Generates bool condition. Returns `true` if condition is negated.
-  bool _genCondition(Node condition) {
+  bool _genCondition(Expression condition) {
     bool negated = false;
     if (condition is Not) {
       condition = (condition as Not).operand;
       negated = true;
     }
     condition.accept(this);
-    // TODO(alexmarkov): bool check
+    asm.emitAssertBoolean(0);
     return negated;
   }
 
@@ -628,10 +624,6 @@
     metadata.mapping[node] =
         new BytecodeMetadata(cp, asm.bytecode, asm.exceptionsTable, closures);
 
-    if (isTraceEnabled) {
-      print('Generated bytecode for $node');
-    }
-
     enclosingClass = null;
     enclosingMember = null;
     classTypeParameters = null;
@@ -1611,7 +1603,24 @@
 
   @override
   visitAssertStatement(AssertStatement node) {
-    // TODO(alexmarkov): support asserts
+    final Label done = new Label();
+    asm.emitJumpIfNoAsserts(done);
+
+    final bool negated = _genCondition(node.condition);
+    _genJumpIfTrue(negated, done);
+
+    _genPushInt(node.conditionStartOffset);
+    _genPushInt(node.conditionEndOffset);
+
+    if (node.message != null) {
+      node.message.accept(this);
+    } else {
+      _genPushNull();
+    }
+
+    _genStaticCall(throwNewAssertionError, new ConstantArgDesc(3), 3);
+
+    asm.bind(done);
   }
 
   @override
@@ -1623,7 +1632,14 @@
 
   @override
   visitAssertBlock(AssertBlock node) {
-    // TODO(alexmarkov): support asserts
+    final Label done = new Label();
+    asm.emitJumpIfNoAsserts(done);
+
+    _enterScope(node);
+    visitList(node.statements, this);
+    _leaveScope();
+
+    asm.bind(done);
   }
 
   @override
@@ -1852,7 +1868,9 @@
         for (var expr in switchCase.expressions) {
           asm.emitPush(temp);
           _genPushConstExpr(expr);
-          asm.emitInstanceCall2(
+          // TODO(alexmarkov): generate InstanceCall2 once we have a way to
+          // mark ICData as having 2 checked arguments.
+          asm.emitInstanceCall1(
               2,
               cp.add(new ConstantICData(
                   InvocationKind.method, new Name('=='), equalsArgDesc)));
diff --git a/pkg/vm/testcases/bytecode/asserts.dart b/pkg/vm/testcases/bytecode/asserts.dart
new file mode 100644
index 0000000..2778c8d
--- /dev/null
+++ b/pkg/vm/testcases/bytecode/asserts.dart
@@ -0,0 +1,13 @@
+// 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.
+
+void test1(bool condition) {
+  assert(condition);
+}
+
+void test2(bool condition(), String message()) {
+  assert(condition(), message());
+}
+
+main() {}
diff --git a/pkg/vm/testcases/bytecode/asserts.dart.expect b/pkg/vm/testcases/bytecode/asserts.dart.expect
new file mode 100644
index 0000000..6d2946c
--- /dev/null
+++ b/pkg/vm/testcases/bytecode/asserts.dart.expect
@@ -0,0 +1,80 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+
+[@vm.bytecode=
+Bytecode {
+  Entry                0
+  CheckStack
+  JumpIfNoAsserts      L1
+  Push                 FP[-5]
+  AssertBoolean        0
+  PushConstant         CP#0
+  IfEqStrictTOS
+  Jump                 L1
+  PushConstant         CP#1
+  PushConstant         CP#2
+  PushConstant         CP#3
+  PushConstant         CP#5
+  IndirectStaticCall   3, CP#4
+L1:
+  PushConstant         CP#3
+  ReturnTOS
+}
+ConstantPool {
+  [0] = Bool true
+  [1] = Int 255
+  [2] = Int 264
+  [3] = Null
+  [4] = ArgDesc num-args 3, num-type-args 0, names []
+  [5] = StaticICData target 'dart.core::_AssertionError::_throwNew', arg-desc CP#4
+}
+]static method test1(core::bool condition) → void {
+  assert(condition);
+}
+[@vm.bytecode=
+Bytecode {
+  Entry                0
+  CheckStack
+  JumpIfNoAsserts      L1
+  Push                 FP[-6]
+  InstanceCall1        1, CP#1
+  AssertBoolean        0
+  PushConstant         CP#2
+  IfEqStrictTOS
+  Jump                 L1
+  PushConstant         CP#3
+  PushConstant         CP#4
+  Push                 FP[-5]
+  InstanceCall1        1, CP#5
+  PushConstant         CP#7
+  IndirectStaticCall   3, CP#6
+L1:
+  PushConstant         CP#8
+  ReturnTOS
+}
+ConstantPool {
+  [0] = ArgDesc num-args 1, num-type-args 0, names []
+  [1] = ICData target-name 'call', arg-desc CP#0
+  [2] = Bool true
+  [3] = Int 328
+  [4] = Int 339
+  [5] = ICData target-name 'call', arg-desc CP#0
+  [6] = ArgDesc num-args 3, num-type-args 0, names []
+  [7] = StaticICData target 'dart.core::_AssertionError::_throwNew', arg-desc CP#6
+  [8] = Null
+}
+]static method test2(() → core::bool condition, () → core::String message) → void {
+  assert(condition.call(), message.call());
+}
+[@vm.bytecode=
+Bytecode {
+  Entry                0
+  CheckStack
+  PushConstant         CP#0
+  ReturnTOS
+}
+ConstantPool {
+  [0] = Null
+}
+]static method main() → dynamic {}
diff --git a/pkg/vm/testcases/bytecode/async.dart b/pkg/vm/testcases/bytecode/async.dart
index e3900c7..cf1d164 100644
--- a/pkg/vm/testcases/bytecode/async.dart
+++ b/pkg/vm/testcases/bytecode/async.dart
@@ -56,4 +56,9 @@
   return nested;
 }
 
+Future<int> testAssert(Future<int> a) async {
+  assert((await a) == 42);
+  return 7;
+}
+
 main() {}
diff --git a/pkg/vm/testcases/bytecode/async.dart.expect b/pkg/vm/testcases/bytecode/async.dart.expect
index 60a3f58..981b101 100644
--- a/pkg/vm/testcases/bytecode/async.dart.expect
+++ b/pkg/vm/testcases/bytecode/async.dart.expect
@@ -714,6 +714,7 @@
   LoadFieldTOS         CP#1
   PushConstant         CP#17
   InstanceCall1        2, CP#19
+  AssertBoolean        0
   PushConstant         CP#20
   IfNeStrictTOS
   Jump                 L2
@@ -884,6 +885,7 @@
   Push                 r8
   PushConstant         CP#17
   InstanceCall1        2, CP#34
+  AssertBoolean        0
   PushConstant         CP#20
   IfNeStrictTOS
   Jump                 L7
@@ -1309,6 +1311,7 @@
   PushConstant         CP#8
   PushConstant         CP#27
   InstanceCall1        4, CP#28
+  AssertBoolean        0
   PushConstant         CP#29
   IfNeStrictTOS
   Jump                 L4
@@ -2172,6 +2175,292 @@
 }
 [@vm.bytecode=
 Bytecode {
+  Entry                4
+  CheckStack
+  AllocateContext      1
+  StoreLocal           r1
+  Push                 r1
+  Push                 r0
+  StoreFieldTOS        CP#0
+  PopLocal             r0
+  Push                 r0
+  Push                 FP[-5]
+  StoreFieldTOS        CP#1
+  AllocateContext      8
+  StoreLocal           r1
+  Push                 r1
+  Push                 r0
+  StoreFieldTOS        CP#0
+  PopLocal             r0
+  Push                 r0
+  PushConstant         CP#2
+  PushConstant         CP#4
+  IndirectStaticCall   1, CP#3
+  StoreFieldTOS        CP#5
+  Push                 r0
+  PushConstant         CP#6
+  StoreFieldTOS        CP#7
+  PushConstant         CP#6
+  PopLocal             r2
+  Push                 r0
+  PushConstant         CP#6
+  StoreFieldTOS        CP#8
+  Push                 r0
+  PushConstant         CP#6
+  StoreFieldTOS        CP#9
+  Push                 r0
+  PushConstant         CP#10
+  StoreFieldTOS        CP#1
+  Push                 r0
+  PushConstant         CP#6
+  StoreFieldTOS        CP#11
+  Push                 r0
+  PushConstant         CP#6
+  StoreFieldTOS        CP#12
+  Push                 r0
+  Allocate             CP#32
+  StoreLocal           r3
+  Push                 r3
+  PushConstant         CP#6
+  StoreFieldTOS        CP#33
+  Push                 r3
+  PushConstant         CP#6
+  StoreFieldTOS        CP#34
+  Push                 r3
+  PushConstant         CP#13
+  StoreFieldTOS        CP#35
+  Push                 r3
+  Push                 r0
+  StoreFieldTOS        CP#14
+  StoreFieldTOS        CP#16
+  Push                 r0
+  LoadFieldTOS         CP#16
+  PushConstant         CP#36
+  IndirectStaticCall   1, CP#3
+  StoreLocal           r2
+  Drop1
+  Push                 r0
+  Push                 r0
+  LoadFieldTOS         CP#16
+  PushConstant         CP#37
+  IndirectStaticCall   1, CP#3
+  StoreLocal           r3
+  StoreFieldTOS        CP#8
+  Push                 r3
+  Drop1
+  Push                 r0
+  Push                 r0
+  LoadFieldTOS         CP#16
+  PushConstant         CP#38
+  IndirectStaticCall   1, CP#3
+  StoreLocal           r3
+  StoreFieldTOS        CP#9
+  Push                 r3
+  Drop1
+  PushConstant         CP#39
+  Push                 r0
+  LoadFieldTOS         CP#16
+  PushConstant         CP#40
+  IndirectStaticCall   2, CP#20
+  Drop1
+  Push                 r0
+  LoadFieldTOS         CP#5
+  InstanceCall1        1, CP#41
+  ReturnTOS
+  Push                 r0
+  LoadFieldTOS         CP#0
+  PopLocal             r0
+  PushConstant         CP#6
+  ReturnTOS
+}
+ConstantPool {
+  [0] = ContextOffset parent
+  [1] = ContextOffset var [0]
+  [2] = TypeArgumentsForInstanceAllocation dart.async::Completer [dart.core::int]
+  [3] = ArgDesc num-args 1, num-type-args 0, names []
+  [4] = StaticICData target 'dart.async::Completer::sync', arg-desc CP#3
+  [5] = ContextOffset var [7]
+  [6] = Null
+  [7] = ContextOffset var [6]
+  [8] = ContextOffset var [3]
+  [9] = ContextOffset var [4]
+  [10] = Int 0
+  [11] = ContextOffset var [1]
+  [12] = ContextOffset var [2]
+  [13] = ClosureFunction :async_op ([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding ;
+  [14] = FieldOffset dart.core::_Closure::_context
+  [15] = Int 1
+  [16] = ContextOffset var [5]
+  [17] = ArgDesc num-args 4, num-type-args 0, names []
+  [18] = StaticICData target 'dart.async::_awaitHelper', arg-desc CP#17
+  [19] = Int 42
+  [20] = ArgDesc num-args 2, num-type-args 0, names []
+  [21] = ICData target-name '==', arg-desc CP#20
+  [22] = Bool true
+  [23] = Int 1161
+  [24] = Int 1176
+  [25] = ArgDesc num-args 3, num-type-args 0, names []
+  [26] = StaticICData target 'dart.core::_AssertionError::_throwNew', arg-desc CP#25
+  [27] = Int 7
+  [28] = ICData target-name 'complete', arg-desc CP#20
+  [29] = Type dynamic
+  [30] = ICData target-name 'completeError', arg-desc CP#25
+  [31] = EndClosureFunctionScope
+  [32] = Class dart.core::_Closure
+  [33] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
+  [34] = FieldOffset dart.core::_Closure::_function_type_arguments
+  [35] = FieldOffset dart.core::_Closure::_function
+  [36] = StaticICData target 'dart.async::_asyncStackTraceHelper', arg-desc CP#3
+  [37] = StaticICData target 'dart.async::_asyncThenWrapperHelper', arg-desc CP#3
+  [38] = StaticICData target 'dart.async::_asyncErrorWrapperHelper', arg-desc CP#3
+  [39] = TypeArgumentsForInstanceAllocation dart.async::Future [dynamic]
+  [40] = StaticICData target 'dart.async::Future::microtask', arg-desc CP#20
+  [41] = ICData get target-name 'future', arg-desc CP#3
+}
+Closure CP#13 {
+  EntryOptional        1, 3, 0
+  LoadConstant         r1, CP#6
+  LoadConstant         r2, CP#6
+  LoadConstant         r3, CP#6
+  Frame                6
+  CheckStack
+  Push                 r0
+  LoadFieldTOS         CP#14
+  PopLocal             r4
+  Push                 r4
+  LoadFieldTOS         CP#1
+  StoreLocal           r5
+  PushConstant         CP#10
+  IfNeStrictNumTOS
+  Jump                 L1
+  Push                 r4
+  Push                 r4
+  StoreFieldTOS        CP#12
+Try #0 start:
+  JumpIfNoAsserts      L2
+  Push                 r4
+  PushConstant         CP#15
+  StoreFieldTOS        CP#1
+  Push                 r4
+  Push                 r4
+  StoreFieldTOS        CP#11
+  Push                 r4
+  LoadFieldTOS         CP#0
+  LoadFieldTOS         CP#1
+  Push                 r4
+  LoadFieldTOS         CP#8
+  Push                 r4
+  LoadFieldTOS         CP#9
+  Push                 r4
+  LoadFieldTOS         CP#16
+  PushConstant         CP#18
+  IndirectStaticCall   4, CP#17
+  PopLocal             r8
+  PushConstant         CP#6
+  ReturnTOS
+L6:
+  IfEqNull             r2
+  Jump                 L3
+  Push                 r2
+  Push                 r3
+  Throw                1
+L3:
+  JumpIfNoAsserts      L2
+  Push                 r1
+  PushConstant         CP#19
+  InstanceCall1        2, CP#21
+  AssertBoolean        0
+  PushConstant         CP#22
+  IfEqStrictTOS
+  Jump                 L2
+  PushConstant         CP#23
+  PushConstant         CP#24
+  PushConstant         CP#6
+  PushConstant         CP#26
+  IndirectStaticCall   3, CP#25
+L2:
+  Push                 r4
+  PushConstant         CP#27
+  StoreLocal           r8
+  StoreFieldTOS        CP#7
+  Push                 r8
+  Drop1
+  Jump                 L4
+L4:
+  Push                 r4
+  LoadFieldTOS         CP#5
+  Push                 r4
+  LoadFieldTOS         CP#7
+  InstanceCall1        2, CP#28
+  Drop1
+  PushConstant         CP#6
+  ReturnTOS
+  Jump                 L5
+Try #0 end:
+Try #0 handler:
+  Push                 r0
+  LoadFieldTOS         CP#14
+  PopLocal             r4
+  Push                 r4
+  LoadFieldTOS         CP#12
+  PopLocal             r4
+  MoveSpecial          r6, exception
+  MoveSpecial          r7, stackTrace
+  Push                 r6
+  PopLocal             r8
+  Push                 r7
+  PopLocal             r9
+  Push                 r4
+  LoadFieldTOS         CP#5
+  Push                 r8
+  Push                 r9
+  InstanceCall1        3, CP#30
+  Drop1
+  Jump                 L5
+L5:
+  PushConstant         CP#6
+  ReturnTOS
+L1:
+  Push                 r4
+  LoadFieldTOS         CP#11
+  PopLocal             r4
+  Jump                 L6
+
+}
+]static method testAssert(asy::Future<core::int> a) → asy::Future<core::int> /* originally async */ {
+  final asy::Completer<core::int> :async_completer = asy::Completer::sync<core::int>();
+  asy::FutureOr<core::int> :return_value;
+  dynamic :async_stack_trace;
+  dynamic :async_op_then;
+  dynamic :async_op_error;
+  dynamic :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  dynamic :saved_try_context_var0;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L6:
+      {
+        assert {
+          [yield] let dynamic #t8 = asy::_awaitHelper(a, :async_op_then, :async_op_error, :async_op) in null;
+          assert(:result.{core::num::==}(42));
+        }
+        :return_value = 7;
+        break #L6;
+      }
+      :async_completer.{asy::Completer::complete}(:return_value);
+      return;
+    }
+    on dynamic catch(dynamic :exception, dynamic :stack_trace) {
+      :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
+    }
+  :async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  asy::Future::microtask<dynamic>(:async_op);
+  return :async_completer.{asy::Completer::future};
+}
+[@vm.bytecode=
+Bytecode {
   Entry                0
   CheckStack
   PushConstant         CP#0
diff --git a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
index 8df59b8..d135ff4 100644
--- a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
+++ b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
@@ -146,6 +146,7 @@
   PushStatic           CP#0
   PushConstant         CP#1
   InstanceCall1        2, CP#3
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L1
@@ -460,6 +461,7 @@
   PushStatic           CP#0
   PushConstant         CP#1
   InstanceCall1        2, CP#3
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L1
@@ -467,6 +469,7 @@
   PushStatic           CP#5
   PushConstant         CP#1
   InstanceCall1        2, CP#6
+  AssertBoolean        0
   BooleanNegateTOS
   PopLocal             r0
   Jump                 L2
@@ -475,6 +478,7 @@
   PopLocal             r0
 L2:
   Push                 r0
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L3
@@ -654,6 +658,7 @@
   PushStatic           CP#0
   PushConstant         CP#1
   InstanceCall1        2, CP#3
+  AssertBoolean        0
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
@@ -661,6 +666,7 @@
   PushStatic           CP#0
   PushConstant         CP#5
   InstanceCall1        2, CP#6
+  AssertBoolean        0
   PopLocal             r1
   Jump                 L2
 L1:
@@ -668,6 +674,7 @@
   PopLocal             r1
 L2:
   Push                 r1
+  AssertBoolean        0
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L3
@@ -675,6 +682,7 @@
   PushStatic           CP#0
   PushConstant         CP#7
   InstanceCall1        2, CP#8
+  AssertBoolean        0
   PopLocal             r0
   Jump                 L4
 L3:
@@ -682,6 +690,7 @@
   PopLocal             r0
 L4:
   Push                 r0
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L5
diff --git a/pkg/vm/testcases/bytecode/closures.dart.expect b/pkg/vm/testcases/bytecode/closures.dart.expect
index 2bece87..af1ed56 100644
--- a/pkg/vm/testcases/bytecode/closures.dart.expect
+++ b/pkg/vm/testcases/bytecode/closures.dart.expect
@@ -730,6 +730,7 @@
   LoadFieldTOS         CP#1
   PushConstant         CP#13
   InstanceCall1        2, CP#14
+  AssertBoolean        0
   PushConstant         CP#15
   IfNeStrictTOS
   Jump                 L1
@@ -894,6 +895,7 @@
   LoadFieldTOS         CP#2
   PushConstant         CP#7
   InstanceCall1        2, CP#9
+  AssertBoolean        0
   PushConstant         CP#10
   IfNeStrictTOS
   Jump                 L1
diff --git a/pkg/vm/testcases/bytecode/loops.dart.expect b/pkg/vm/testcases/bytecode/loops.dart.expect
index f2bf88a..fe91f5f 100644
--- a/pkg/vm/testcases/bytecode/loops.dart.expect
+++ b/pkg/vm/testcases/bytecode/loops.dart.expect
@@ -16,6 +16,7 @@
   Push                 FP[-5]
   InstanceCall1        1, CP#2
   InstanceCall1        2, CP#4
+  AssertBoolean        0
   PushConstant         CP#5
   IfNeStrictTOS
   Jump                 L1
@@ -71,6 +72,7 @@
   Push                 r1
   PushConstant         CP#0
   InstanceCall1        2, CP#2
+  AssertBoolean        0
   PushConstant         CP#3
   IfNeStrictTOS
   Jump                 L1
@@ -78,6 +80,7 @@
   Push                 FP[-5]
   InstanceCall1        1, CP#5
   InstanceCall1        2, CP#6
+  AssertBoolean        0
   PushConstant         CP#3
   IfNeStrictTOS
   Jump                 L2
@@ -142,12 +145,14 @@
   Push                 FP[-5]
   InstanceCall1        1, CP#4
   InstanceCall1        2, CP#6
+  AssertBoolean        0
   PushConstant         CP#7
   IfNeStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#0
   InstanceCall1        2, CP#8
+  AssertBoolean        0
   PushConstant         CP#7
   IfNeStrictTOS
   Jump                 L2
@@ -215,6 +220,7 @@
   Push                 FP[-5]
   InstanceCall1        1, CP#2
   InstanceCall1        2, CP#4
+  AssertBoolean        0
   PushConstant         CP#5
   IfNeStrictTOS
   Jump                 L1
@@ -286,6 +292,7 @@
   Push                 FP[-5]
   InstanceCall1        1, CP#7
   InstanceCall1        2, CP#8
+  AssertBoolean        0
   PushConstant         CP#9
   IfEqStrictTOS
   Jump                 L1
diff --git a/pkg/vm/testcases/bytecode/switch.dart.expect b/pkg/vm/testcases/bytecode/switch.dart.expect
index cf87dfd..c82563b 100644
--- a/pkg/vm/testcases/bytecode/switch.dart.expect
+++ b/pkg/vm/testcases/bytecode/switch.dart.expect
@@ -12,19 +12,19 @@
   PopLocal             r1
   Push                 r1
   PushConstant         CP#2
-  InstanceCall2        2, CP#3
+  InstanceCall1        2, CP#3
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#5
-  InstanceCall2        2, CP#6
+  InstanceCall1        2, CP#6
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L2
   Push                 r1
   PushConstant         CP#7
-  InstanceCall2        2, CP#8
+  InstanceCall1        2, CP#8
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L3
@@ -99,37 +99,37 @@
   PopLocal             r1
   Push                 r1
   PushConstant         CP#2
-  InstanceCall2        2, CP#3
+  InstanceCall1        2, CP#3
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#5
-  InstanceCall2        2, CP#6
+  InstanceCall1        2, CP#6
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#7
-  InstanceCall2        2, CP#8
+  InstanceCall1        2, CP#8
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#9
-  InstanceCall2        2, CP#10
+  InstanceCall1        2, CP#10
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L2
   Push                 r1
   PushConstant         CP#11
-  InstanceCall2        2, CP#12
+  InstanceCall1        2, CP#12
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L2
   Push                 r1
   PushConstant         CP#13
-  InstanceCall2        2, CP#14
+  InstanceCall1        2, CP#14
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L2
@@ -212,37 +212,37 @@
   PopLocal             r1
   Push                 r1
   PushConstant         CP#2
-  InstanceCall2        2, CP#3
+  InstanceCall1        2, CP#3
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#5
-  InstanceCall2        2, CP#6
+  InstanceCall1        2, CP#6
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#7
-  InstanceCall2        2, CP#8
+  InstanceCall1        2, CP#8
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L1
   Push                 r1
   PushConstant         CP#9
-  InstanceCall2        2, CP#10
+  InstanceCall1        2, CP#10
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L2
   Push                 r1
   PushConstant         CP#11
-  InstanceCall2        2, CP#12
+  InstanceCall1        2, CP#12
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L2
   Push                 r1
   PushConstant         CP#13
-  InstanceCall2        2, CP#14
+  InstanceCall1        2, CP#14
   PushConstant         CP#4
   IfEqStrictTOS
   Jump                 L2
diff --git a/pkg/vm/testcases/bytecode/try_blocks.dart.expect b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
index f3ac1e4..bf677f1 100644
--- a/pkg/vm/testcases/bytecode/try_blocks.dart.expect
+++ b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
@@ -571,6 +571,7 @@
   IndirectStaticCall   1, CP#1
   Drop1
   Push                 FP[-5]
+  AssertBoolean        0
   PushConstant         CP#6
   IfNeStrictTOS
   Jump                 L2
@@ -617,9 +618,9 @@
   ReturnTOS
 }
 ExceptionsTable {
-  try-index 0, outer -1, start 2, end 35, handler 35, needs-stack-trace, types [CP#3]
+  try-index 0, outer -1, start 2, end 36, handler 36, needs-stack-trace, types [CP#3]
   try-index 1, outer 0, start 2, end 7, handler 7, needs-stack-trace, types [CP#3]
-  try-index 2, outer 0, start 11, end 24, handler 24, types [CP#3]
+  try-index 2, outer 0, start 11, end 25, handler 25, types [CP#3]
 }
 ConstantPool {
   [0] = String 'try 1 > try 2'
@@ -669,6 +670,7 @@
   Push                 r0
   PushConstant         CP#1
   InstanceCall1        2, CP#3
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L1
@@ -676,6 +678,7 @@
   Push                 r0
   PushConstant         CP#5
   InstanceCall1        2, CP#6
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L2
@@ -715,7 +718,7 @@
   ReturnTOS
 }
 ExceptionsTable {
-  try-index 0, outer -1, start 11, end 19, handler 19, needs-stack-trace, types [CP#7]
+  try-index 0, outer -1, start 12, end 21, handler 21, needs-stack-trace, types [CP#7]
 }
 ConstantPool {
   [0] = Int 0
@@ -765,13 +768,13 @@
   PopLocal             r2
   Push                 r2
   PushConstant         CP#3
-  InstanceCall2        2, CP#4
+  InstanceCall1        2, CP#4
   PushConstant         CP#5
   IfEqStrictTOS
   Jump                 L1
   Push                 r2
   PushConstant         CP#6
-  InstanceCall2        2, CP#7
+  InstanceCall1        2, CP#7
   PushConstant         CP#5
   IfEqStrictTOS
   Jump                 L2
diff --git a/pkg/vm/testcases/bytecode/type_ops.dart.expect b/pkg/vm/testcases/bytecode/type_ops.dart.expect
index cf66e57..9a60881 100644
--- a/pkg/vm/testcases/bytecode/type_ops.dart.expect
+++ b/pkg/vm/testcases/bytecode/type_ops.dart.expect
@@ -110,6 +110,7 @@
   PushConstant         CP#1
   PushConstant         CP#2
   InstanceCall1        4, CP#4
+  AssertBoolean        0
   PushConstant         CP#5
   IfNeStrictTOS
   Jump                 L1
@@ -124,6 +125,7 @@
   PushConstant         CP#1
   PushConstant         CP#9
   InstanceCall1        4, CP#10
+  AssertBoolean        0
   PushConstant         CP#5
   IfNeStrictTOS
   Jump                 L2
@@ -186,6 +188,7 @@
   Push                 FP[-7]
   PushConstant         CP#1
   InstanceCall1        4, CP#3
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L1
@@ -200,6 +203,7 @@
   Push                 FP[-7]
   PushConstant         CP#9
   InstanceCall1        4, CP#10
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L2
@@ -368,6 +372,7 @@
   PushConstant         CP#0
   PushConstant         CP#1
   InstanceCall1        4, CP#3
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L1
@@ -381,6 +386,7 @@
   PushConstant         CP#0
   PushConstant         CP#8
   InstanceCall1        4, CP#9
+  AssertBoolean        0
   PushConstant         CP#4
   IfNeStrictTOS
   Jump                 L2
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index 312ca7f..4b8e028 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -113,6 +113,11 @@
 //    Jump to the given target. Target is specified as offset from the PC of the
 //    jump instruction.
 //
+//  - JumpIfNoAsserts target
+//
+//    Jump to the given target if assertions are not enabled.
+//    Target is specified as offset from the PC of the jump instruction.
+//
 //  - Return R; ReturnTOS
 //
 //    Return to the caller using either a value from the given register or a
@@ -772,6 +777,7 @@
   V(DropR,                                 A, num, ___, ___)                   \
   V(Drop,                                  A, num, ___, ___)                   \
   V(Jump,                                  T, tgt, ___, ___)                   \
+  V(JumpIfNoAsserts,                       T, tgt, ___, ___)                   \
   V(Return,                                A, reg, ___, ___)                   \
   V(ReturnTOS,                             0, ___, ___, ___)                   \
   V(Move,                                A_X, reg, xeg, ___)                   \
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index 1e31f0f..f320a65 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -4164,6 +4164,15 @@
   }
 
   {
+    BYTECODE(JumpIfNoAsserts, 0);
+    if (!thread->isolate()->asserts()) {
+      const int32_t target = static_cast<int32_t>(op) >> 8;
+      pc += (target - 1);
+    }
+    DISPATCH();
+  }
+
+  {
     BYTECODE(LoadClassId, A_D);
     const uint16_t object_reg = rD;
     RawObject* obj = static_cast<RawObject*>(FP[object_reg]);