[vm] Add unsafe mode to VM

This CL adds experimental --experimental-unsafe-mode-use-at-your-own-risk
VM option which does the following:
* Dart 2 strong mode type and bool checks are omitted.
* VM compiler optimizations which rely on strong mode types are disabled.

Applications which do not fail any strong mode checks at run time
should behave exactly the same in strong and weak modes.
Applications with failing strong mode checks will not see corresponding
errors but VM should not crash.

This option can be used for experiments, or as a temporary remedy for
regressions caused by expensive strong mode type checks.

Change-Id: I042cbccba83c105b61b3e11c659a35c20e0329cd
Reviewed-on: https://dart-review.googlesource.com/65484
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index e4f7d2e..81b5e09 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -130,7 +130,7 @@
   // can contain [AssertAssignableInstr]s and we therefore enable this
   // optimization.
   Isolate* isolate = Isolate::Current();
-  if (isolate->type_checks() || isolate->strong()) {
+  if (isolate->argument_type_checks()) {
     StrengthenAsserts(block);
   }
 
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 588c85a..e690459 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -956,6 +956,10 @@
 
 Fragment StreamingFlowGraphBuilder::BuildArgumentTypeChecks(
     TypeChecksToBuild mode) {
+  if (FLAG_omit_strong_type_checks) {
+    return Fragment();
+  }
+
   FunctionNodeHelper function_node_helper(this);
   function_node_helper.SetNext(FunctionNodeHelper::kTypeParameters);
   const Function& dart_function = parsed_function()->function();
@@ -2717,7 +2721,8 @@
     const intptr_t kNumArgsChecked = 1;
 
     const String* mangled_name = &setter_name;
-    if (!FLAG_precompiled_mode && I->strong() && H.IsRoot(itarget_name)) {
+    if (!FLAG_precompiled_mode && I->strong() &&
+        !FLAG_omit_strong_type_checks && H.IsRoot(itarget_name)) {
       mangled_name = &String::ZoneHandle(
           Z, Function::CreateDynamicInvocationForwarderName(setter_name));
     }
@@ -3291,6 +3296,7 @@
     //     those cases require a dynamic invocation forwarder;
     //   * we assume that all closures are entered in a checked way.
     if (!FLAG_precompiled_mode && I->strong() &&
+        !FLAG_omit_strong_type_checks &&
         (name.raw() != Symbols::EqualOperator().raw()) &&
         (name.raw() != Symbols::Call().raw()) && H.IsRoot(itarget_name)) {
       mangled_name = &String::ZoneHandle(
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 73d7c38..fa2d862 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -1593,6 +1593,9 @@
   if (dst_type.IsMalformed()) {
     return ThrowTypeError();
   }
+  if (FLAG_omit_strong_type_checks) {
+    return Fragment();
+  }
   if (!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
       !dst_type.IsVoidType()) {
     LocalVariable* top_of_stack = MakeTemporary();
@@ -1605,6 +1608,9 @@
 }
 
 Fragment FlowGraphBuilder::AssertBool(TokenPosition position) {
+  if (FLAG_omit_strong_type_checks) {
+    return Fragment();
+  }
   Value* value = Pop();
   AssertBooleanInstr* instr =
       new (Z) AssertBooleanInstr(position, value, GetNextDeoptId());
@@ -1616,6 +1622,10 @@
                                             const AbstractType& dst_type,
                                             const String& dst_name,
                                             AssertAssignableInstr::Kind kind) {
+  if (FLAG_omit_strong_type_checks) {
+    return Fragment();
+  }
+
   Fragment instructions;
   Value* value = Pop();
 
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index 9083339..be3018b 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -132,6 +132,18 @@
                     precompilation,
                     "Precompilation mode");
 
+static void UnsafeModeHandler(bool value) {
+  if (value) {
+    FLAG_omit_strong_type_checks = true;
+    FLAG_use_strong_mode_types = false;
+  }
+}
+
+DEFINE_FLAG_HANDLER(UnsafeModeHandler,
+                    experimental_unsafe_mode_use_at_your_own_risk,
+                    "Omit runtime strong mode type checks and disable "
+                    "optimizations based on types.");
+
 #ifndef DART_PRECOMPILED_RUNTIME
 
 bool UseKernelFrontEndFor(ParsedFunction* parsed_function) {
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index 670fd1a..40a8d28 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -122,6 +122,7 @@
     "Max size of new gen semi space in MB")                                    \
   P(new_gen_semi_initial_size, int, (kWordSize <= 4) ? 1 : 2,                  \
     "Initial size of new gen semi space in MB")                                \
+  P(omit_strong_type_checks, bool, false, "Omit strong mode type checks.")     \
   P(optimization_counter_threshold, int, 30000,                                \
     "Function's usage-counter value before it is optimized, -1 means never")   \
   P(old_gen_heap_size, int, kDefaultMaxOldGenHeapSize,                         \
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 974c447..318ed36 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -749,7 +749,9 @@
 
   // Convenience flag tester indicating whether incoming function arguments
   // should be type checked.
-  bool argument_type_checks() { return strong() || type_checks(); }
+  bool argument_type_checks() {
+    return (strong() && !FLAG_omit_strong_type_checks) || type_checks();
+  }
 
   static void KillAllIsolates(LibMsgId msg_id);
   static void KillIfExists(Isolate* isolate, LibMsgId msg_id);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 3a1b5cb..eede837 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2428,6 +2428,9 @@
 
   bool NeedsArgumentTypeChecks(Isolate* I) const {
     if (I->strong()) {
+      if (FLAG_omit_strong_type_checks) {
+        return false;
+      }
       return IsNonImplicitClosureFunction() ||
              !(is_static() || (kind() == RawFunction::kConstructor));
     }
diff --git a/runtime/vm/scopes.h b/runtime/vm/scopes.h
index 15548dc..1383695 100644
--- a/runtime/vm/scopes.h
+++ b/runtime/vm/scopes.h
@@ -129,7 +129,9 @@
 
   // Returns true if this local variable represents a parameter that needs type
   // check when we enter the function.
-  bool needs_type_check() const { return type_check_mode_ == kDoTypeCheck; }
+  bool needs_type_check() const {
+    return (type_check_mode_ == kDoTypeCheck) && !FLAG_omit_strong_type_checks;
+  }
 
   // Returns true if this local variable represents a parameter which type is
   // guaranteed by the caller.