Version 2.14.0-267.0.dev

Merge commit 'f5326473c2db3f55996a645d5e7e59743766fe10' into 'dev'
diff --git a/runtime/tests/concurrency/stress_test_list.json b/runtime/tests/concurrency/stress_test_list.json
index f600f8b..68a2c00 100644
--- a/runtime/tests/concurrency/stress_test_list.json
+++ b/runtime/tests/concurrency/stress_test_list.json
@@ -402,7 +402,6 @@
     "../../../tests/corelib/regexp/repeat-match-waldemar_test.dart",
     "../../../tests/corelib/regexp/results-cache_test.dart",
     "../../../tests/corelib/regexp/stack-overflow2_test.dart",
-    "../../../tests/corelib/regexp/stack-overflow_test.dart",
     "../../../tests/corelib/regexp/standalones_test.dart",
     "../../../tests/corelib/regexp/toString_test.dart",
     "../../../tests/corelib/regexp/unicode-character-ranges_test.dart",
@@ -3306,7 +3305,6 @@
     "../../../tests/standalone/io/http_request_pipeling_test.dart",
     "../../../tests/standalone/io/http_requested_uri_test.dart",
     "../../../tests/standalone/io/http_response_deadline_test.dart",
-    "../../../tests/standalone/io/http_reuse_server_port_test.dart",
     "../../../tests/standalone/io/http_session_test.dart",
     "../../../tests/standalone/io/http_shutdown_test.dart",
     "../../../tests/standalone/io/http_stream_close_test.dart",
@@ -3823,7 +3821,6 @@
     "../../../tests/corelib_2/regexp/repeat-match-waldemar_test.dart",
     "../../../tests/corelib_2/regexp/results-cache_test.dart",
     "../../../tests/corelib_2/regexp/stack-overflow2_test.dart",
-    "../../../tests/corelib_2/regexp/stack-overflow_test.dart",
     "../../../tests/corelib_2/regexp/standalones_test.dart",
     "../../../tests/corelib_2/regexp/toString_test.dart",
     "../../../tests/corelib_2/regexp/unicode-character-ranges_test.dart",
@@ -6643,7 +6640,6 @@
     "../../../tests/standalone_2/io/http_request_pipeling_test.dart",
     "../../../tests/standalone_2/io/http_requested_uri_test.dart",
     "../../../tests/standalone_2/io/http_response_deadline_test.dart",
-    "../../../tests/standalone_2/io/http_reuse_server_port_test.dart",
     "../../../tests/standalone_2/io/http_session_test.dart",
     "../../../tests/standalone_2/io/http_shutdown_test.dart",
     "../../../tests/standalone_2/io/http_stream_close_test.dart",
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index da234e4..60fdf3a 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -791,10 +791,6 @@
   }
 }
 
-void ConstantPropagator::VisitStringInterpolate(StringInterpolateInstr* instr) {
-  SetValue(instr, non_constant_);
-}
-
 void ConstantPropagator::VisitUtf8Scan(Utf8ScanInstr* instr) {
   SetValue(instr, non_constant_);
 }
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 49131ee..b121598 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5243,8 +5243,92 @@
   return false;
 }
 
+static const String& EvaluateToString(Zone* zone, Definition* defn) {
+  if (auto konst = defn->AsConstant()) {
+    const Object& obj = konst->value();
+    if (obj.IsString()) {
+      return String::Cast(obj);
+    } else if (obj.IsSmi()) {
+      const char* cstr = obj.ToCString();
+      return String::Handle(zone, String::New(cstr, Heap::kOld));
+    } else if (obj.IsBool()) {
+      return Bool::Cast(obj).value() ? Symbols::True() : Symbols::False();
+    } else if (obj.IsNull()) {
+      return Symbols::null();
+    }
+  }
+  return String::null_string();
+}
+
+static Definition* CanonicalizeStringInterpolate(StaticCallInstr* call,
+                                                 FlowGraph* flow_graph) {
+  auto arg0 = call->ArgumentValueAt(0)->definition();
+  auto create_array = arg0->AsCreateArray();
+  if (create_array == nullptr) {
+    // Do not try to fold interpolate if array is an OSR argument.
+    ASSERT(flow_graph->IsCompiledForOsr());
+    ASSERT(arg0->IsPhi() || arg0->IsParameter());
+    return call;
+  }
+  // Check if the string interpolation has only constant inputs.
+  Value* num_elements = create_array->num_elements();
+  if (!num_elements->BindsToConstant() ||
+      !num_elements->BoundConstant().IsSmi()) {
+    return call;
+  }
+  const intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+  GrowableHandlePtrArray<const String> pieces(zone, length);
+  for (intptr_t i = 0; i < length; i++) {
+    pieces.Add(Object::null_string());
+  }
+
+  for (Value::Iterator it(create_array->input_use_list()); !it.Done();
+       it.Advance()) {
+    auto store = it.Current()->instruction()->AsStoreIndexed();
+    if (store == nullptr || !store->index()->BindsToConstant() ||
+        !store->index()->BoundConstant().IsSmi()) {
+      return call;
+    }
+    intptr_t store_index = Smi::Cast(store->index()->BoundConstant()).Value();
+    ASSERT(store_index < length);
+    const String& piece =
+        EvaluateToString(flow_graph->zone(), store->value()->definition());
+    if (!piece.IsNull()) {
+      pieces.SetAt(store_index, piece);
+    } else {
+      return call;
+    }
+  }
+
+  const String& concatenated =
+      String::ZoneHandle(zone, Symbols::FromConcatAll(thread, pieces));
+  return flow_graph->GetConstant(concatenated);
+}
+
+static Definition* CanonicalizeStringInterpolateSingle(StaticCallInstr* call,
+                                                       FlowGraph* flow_graph) {
+  auto arg0 = call->ArgumentValueAt(0)->definition();
+  const auto& result = EvaluateToString(flow_graph->zone(), arg0);
+  if (!result.IsNull()) {
+    return flow_graph->GetConstant(String::ZoneHandle(
+        flow_graph->zone(), Symbols::New(flow_graph->thread(), result)));
+  }
+  return call;
+}
+
 Definition* StaticCallInstr::Canonicalize(FlowGraph* flow_graph) {
-  if (!CompilerState::Current().is_aot()) {
+  auto& compiler_state = CompilerState::Current();
+
+  if (function().ptr() == compiler_state.StringBaseInterpolate().ptr()) {
+    return CanonicalizeStringInterpolate(this, flow_graph);
+  } else if (function().ptr() ==
+             compiler_state.StringBaseInterpolateSingle().ptr()) {
+    return CanonicalizeStringInterpolateSingle(this, flow_graph);
+  }
+
+  if (!compiler_state.is_aot()) {
     return this;
   }
 
@@ -5964,101 +6048,6 @@
   }
 }
 
-const Function& StringInterpolateInstr::CallFunction() const {
-  if (function_.IsNull()) {
-    const int kTypeArgsLen = 0;
-    const int kNumberOfArguments = 1;
-    const Array& kNoArgumentNames = Object::null_array();
-    const Class& cls =
-        Class::Handle(Library::LookupCoreClass(Symbols::StringBase()));
-    ASSERT(!cls.IsNull());
-    function_ = Resolver::ResolveStatic(
-        cls, Library::PrivateCoreLibName(Symbols::Interpolate()), kTypeArgsLen,
-        kNumberOfArguments, kNoArgumentNames);
-  }
-  ASSERT(!function_.IsNull());
-  return function_;
-}
-
-// Replace StringInterpolateInstr with a constant string if all inputs are
-// constant of [string, number, boolean, null].
-// Leave the CreateArrayInstr and StoreIndexedInstr in the stream in case
-// deoptimization occurs.
-Definition* StringInterpolateInstr::Canonicalize(FlowGraph* flow_graph) {
-  // The following graph structure is generated by the graph builder:
-  //   v2 <- CreateArray(v0)
-  //   StoreIndexed(v2, v3, v4)   -- v3:constant index, v4: value.
-  //   ..
-  //   v8 <- StringInterpolate(v2)
-
-  // Don't compile-time fold when optimizing the interpolation function itself.
-  if (flow_graph->function().ptr() == CallFunction().ptr()) {
-    return this;
-  }
-
-  CreateArrayInstr* create_array = value()->definition()->AsCreateArray();
-  if (create_array == nullptr) {
-    // Do not try to fold interpolate if array is an OSR argument.
-    ASSERT(flow_graph->IsCompiledForOsr());
-    ASSERT(value()->definition()->IsPhi() ||
-           value()->definition()->IsParameter());
-    return this;
-  }
-  // Check if the string interpolation has only constant inputs.
-  Value* num_elements = create_array->num_elements();
-  if (!num_elements->BindsToConstant() ||
-      !num_elements->BoundConstant().IsSmi()) {
-    return this;
-  }
-  const intptr_t length = Smi::Cast(num_elements->BoundConstant()).Value();
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  GrowableHandlePtrArray<const String> pieces(zone, length);
-  for (intptr_t i = 0; i < length; i++) {
-    pieces.Add(Object::null_string());
-  }
-
-  for (Value::Iterator it(create_array->input_use_list()); !it.Done();
-       it.Advance()) {
-    Instruction* curr = it.Current()->instruction();
-    if (curr == this) continue;
-
-    StoreIndexedInstr* store = curr->AsStoreIndexed();
-    if (store == nullptr || !store->index()->BindsToConstant() ||
-        !store->index()->BoundConstant().IsSmi()) {
-      return this;
-    }
-    intptr_t store_index = Smi::Cast(store->index()->BoundConstant()).Value();
-    ASSERT(store_index < length);
-    ASSERT(store != NULL);
-    if (store->value()->definition()->IsConstant()) {
-      ASSERT(store->index()->BindsToConstant());
-      const Object& obj = store->value()->definition()->AsConstant()->value();
-      // TODO(srdjan): Verify if any other types should be converted as well.
-      if (obj.IsString()) {
-        pieces.SetAt(store_index, String::Cast(obj));
-      } else if (obj.IsSmi()) {
-        const char* cstr = obj.ToCString();
-        pieces.SetAt(store_index,
-                     String::Handle(zone, String::New(cstr, Heap::kOld)));
-      } else if (obj.IsBool()) {
-        pieces.SetAt(store_index, Bool::Cast(obj).value() ? Symbols::True()
-                                                          : Symbols::False());
-      } else if (obj.IsNull()) {
-        pieces.SetAt(store_index, Symbols::null());
-      } else {
-        return this;
-      }
-    } else {
-      return this;
-    }
-  }
-
-  const String& concatenated =
-      String::ZoneHandle(zone, Symbols::FromConcatAll(thread, pieces));
-  return flow_graph->GetConstant(concatenated);
-}
-
 static AlignmentType StrengthenAlignment(intptr_t cid,
                                          AlignmentType alignment) {
   switch (cid) {
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 5f22ad8..1744e17 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -478,7 +478,6 @@
   M(Constraint, kNoGC)                                                         \
   M(StringToCharCode, kNoGC)                                                   \
   M(OneByteStringFromCharCode, kNoGC)                                          \
-  M(StringInterpolate, _)                                                      \
   M(Utf8Scan, kNoGC)                                                           \
   M(InvokeMathCFunction, kNoGC)                                                \
   M(TruncDivMod, kNoGC)                                                        \
@@ -5850,41 +5849,6 @@
   DISALLOW_COPY_AND_ASSIGN(StringToCharCodeInstr);
 };
 
-class StringInterpolateInstr : public TemplateDefinition<1, Throws> {
- public:
-  StringInterpolateInstr(Value* value,
-                         const InstructionSource& source,
-                         intptr_t deopt_id)
-      : TemplateDefinition(source, deopt_id),
-        token_pos_(source.token_pos),
-        function_(Function::ZoneHandle()) {
-    SetInputAt(0, value);
-  }
-
-  Value* value() const { return inputs_[0]; }
-  virtual TokenPosition token_pos() const { return token_pos_; }
-
-  virtual CompileType ComputeType() const;
-  // Issues a static call to Dart code which calls toString on objects.
-  virtual bool HasUnknownSideEffects() const { return true; }
-  virtual bool CanCallDart() const { return true; }
-  virtual bool ComputeCanDeoptimize() const {
-    return !CompilerState::Current().is_aot();
-  }
-
-  const Function& CallFunction() const;
-
-  virtual Definition* Canonicalize(FlowGraph* flow_graph);
-
-  DECLARE_INSTRUCTION(StringInterpolate)
-
- private:
-  const TokenPosition token_pos_;
-  Function& function_;
-
-  DISALLOW_COPY_AND_ASSIGN(StringInterpolateInstr);
-};
-
 // Scanning instruction to compute the result size and decoding parameters
 // for the UTF-8 decoder. Equivalent to:
 //
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 6b44b0d..0ac5dfd 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -1727,31 +1727,6 @@
   __ SmiTag(result);
 }
 
-LocationSummary* StringInterpolateInstr::MakeLocationSummary(Zone* zone,
-                                                             bool opt) const {
-  const intptr_t kNumInputs = 1;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* summary = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  summary->set_in(0, Location::RegisterLocation(R0));
-  summary->set_out(0, Location::RegisterLocation(R0));
-  return summary;
-}
-
-void StringInterpolateInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  const Register array = locs()->in(0).reg();
-  __ Push(array);
-  const int kTypeArgsLen = 0;
-  const int kNumberOfArguments = 1;
-  constexpr int kSizeOfArguments = 1;
-  const Array& kNoArgumentNames = Object::null_array();
-  ArgumentsInfo args_info(kTypeArgsLen, kNumberOfArguments, kSizeOfArguments,
-                          kNoArgumentNames);
-  compiler->GenerateStaticCall(deopt_id(), source(), CallFunction(), args_info,
-                               locs(), ICData::Handle(), ICData::kStatic);
-  ASSERT(locs()->out(0).reg() == R0);
-}
-
 LocationSummary* Utf8ScanInstr::MakeLocationSummary(Zone* zone,
                                                     bool opt) const {
   const intptr_t kNumInputs = 5;
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 0f09726..d67e9e9 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -1575,31 +1575,6 @@
   __ SmiTag(result);
 }
 
-LocationSummary* StringInterpolateInstr::MakeLocationSummary(Zone* zone,
-                                                             bool opt) const {
-  const intptr_t kNumInputs = 1;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* summary = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  summary->set_in(0, Location::RegisterLocation(R0));
-  summary->set_out(0, Location::RegisterLocation(R0));
-  return summary;
-}
-
-void StringInterpolateInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  const Register array = locs()->in(0).reg();
-  __ Push(array);
-  const int kTypeArgsLen = 0;
-  const int kNumberOfArguments = 1;
-  constexpr int kSizeOfArguments = 1;
-  const Array& kNoArgumentNames = Object::null_array();
-  ArgumentsInfo args_info(kTypeArgsLen, kNumberOfArguments, kSizeOfArguments,
-                          kNoArgumentNames);
-  compiler->GenerateStaticCall(deopt_id(), source(), CallFunction(), args_info,
-                               locs(), ICData::Handle(), ICData::kStatic);
-  ASSERT(locs()->out(0).reg() == R0);
-}
-
 LocationSummary* Utf8ScanInstr::MakeLocationSummary(Zone* zone,
                                                     bool opt) const {
   const intptr_t kNumInputs = 5;
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index d16dab4..f7ef9ae 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -1272,31 +1272,6 @@
   __ Bind(&done);
 }
 
-LocationSummary* StringInterpolateInstr::MakeLocationSummary(Zone* zone,
-                                                             bool opt) const {
-  const intptr_t kNumInputs = 1;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* summary = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  summary->set_in(0, Location::RegisterLocation(EAX));
-  summary->set_out(0, Location::RegisterLocation(EAX));
-  return summary;
-}
-
-void StringInterpolateInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  Register array = locs()->in(0).reg();
-  __ pushl(array);
-  const int kTypeArgsLen = 0;
-  const int kNumberOfArguments = 1;
-  constexpr int kSizeOfArguments = 1;
-  const Array& kNoArgumentNames = Object::null_array();
-  ArgumentsInfo args_info(kTypeArgsLen, kNumberOfArguments, kSizeOfArguments,
-                          kNoArgumentNames);
-  compiler->GenerateStaticCall(deopt_id(), source(), CallFunction(), args_info,
-                               locs(), ICData::Handle(), ICData::kStatic);
-  ASSERT(locs()->out(0).reg() == EAX);
-}
-
 LocationSummary* Utf8ScanInstr::MakeLocationSummary(Zone* zone,
                                                     bool opt) const {
   const intptr_t kNumInputs = 5;
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 56144e61..7882a76 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -1488,31 +1488,6 @@
   __ Bind(&done);
 }
 
-LocationSummary* StringInterpolateInstr::MakeLocationSummary(Zone* zone,
-                                                             bool opt) const {
-  const intptr_t kNumInputs = 1;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* summary = new (zone)
-      LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  summary->set_in(0, Location::RegisterLocation(RAX));
-  summary->set_out(0, Location::RegisterLocation(RAX));
-  return summary;
-}
-
-void StringInterpolateInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  Register array = locs()->in(0).reg();
-  __ pushq(array);
-  const int kTypeArgsLen = 0;
-  const int kNumberOfArguments = 1;
-  constexpr int kSizeOfArguments = 1;
-  const Array& kNoArgumentNames = Object::null_array();
-  ArgumentsInfo args_info(kTypeArgsLen, kNumberOfArguments, kSizeOfArguments,
-                          kNoArgumentNames);
-  compiler->GenerateStaticCall(deopt_id(), source(), CallFunction(), args_info,
-                               locs(), ICData::Handle(), ICData::kStatic);
-  ASSERT(locs()->out(0).reg() == RAX);
-}
-
 LocationSummary* Utf8ScanInstr::MakeLocationSummary(Zone* zone,
                                                     bool opt) const {
   const intptr_t kNumInputs = 5;
diff --git a/runtime/vm/compiler/backend/redundancy_elimination_test.cc b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
index 2e88fcc..2668f1d 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination_test.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination_test.cc
@@ -1189,13 +1189,14 @@
  49:     ParallelMove rdx <- rcx, rax <- rax
  50:     StoreIndexed(v24, v53, v580)
  51:     ParallelMove rax <- rcx
- 52:     v54 <- StringInterpolate:44(v24) T{String}
+         PushArgument(v24)
+ 52:     v54 <- StaticCall:44(_StringBase._interpolate, v24) T{String}
  53:     ParallelMove rax <- rax
  54:     Return:48(v54)
 */
 
   CreateArrayInstr* create_array = nullptr;
-  StringInterpolateInstr* string_interpolate = nullptr;
+  StaticCallInstr* string_interpolate = nullptr;
 
   ILMatcher cursor(flow_graph, entry, /*trace=*/true,
                    ParallelMovesHandling::kSkip);
@@ -1225,11 +1226,12 @@
       kMatchAndMoveStoreIndexed,
       kMatchAndMoveBox,
       kMatchAndMoveStoreIndexed,
-      {kMatchAndMoveStringInterpolate, &string_interpolate},
+      kMatchAndMovePushArgument,
+      {kMatchAndMoveStaticCall, &string_interpolate},
       kMatchReturn,
   }));
 
-  EXPECT(string_interpolate->value()->definition() == create_array);
+  EXPECT(string_interpolate->ArgumentAt(0) == create_array);
 }
 
 #if !defined(TARGET_ARCH_IA32)
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index c28b9c7..9aecb2c 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -1503,11 +1503,6 @@
   return CompileType::FromCid(kSmiCid);
 }
 
-CompileType StringInterpolateInstr::ComputeType() const {
-  // TODO(srdjan): Do better and determine if it is a one or two byte string.
-  return CompileType::String();
-}
-
 CompileType LoadStaticFieldInstr::ComputeType() const {
   const Field& field = this->field();
   bool is_nullable = CompileType::kCanBeNull;
diff --git a/runtime/vm/compiler/compiler_state.cc b/runtime/vm/compiler/compiler_state.cc
index 38d4932..b886b6f 100644
--- a/runtime/vm/compiler/compiler_state.cc
+++ b/runtime/vm/compiler/compiler_state.cc
@@ -98,4 +98,34 @@
   return *comparable_class_;
 }
 
+const Function& CompilerState::StringBaseInterpolateSingle() {
+  if (interpolate_single_ == nullptr) {
+    Thread* thread = Thread::Current();
+    Zone* zone = thread->zone();
+
+    const Class& cls =
+        Class::Handle(Library::LookupCoreClass(Symbols::StringBase()));
+    ASSERT(!cls.IsNull());
+    interpolate_single_ = &Function::ZoneHandle(
+        zone, cls.LookupFunctionAllowPrivate(Symbols::InterpolateSingle()));
+    ASSERT(!interpolate_single_->IsNull());
+  }
+  return *interpolate_single_;
+}
+
+const Function& CompilerState::StringBaseInterpolate() {
+  if (interpolate_ == nullptr) {
+    Thread* thread = Thread::Current();
+    Zone* zone = thread->zone();
+
+    const Class& cls =
+        Class::Handle(Library::LookupCoreClass(Symbols::StringBase()));
+    ASSERT(!cls.IsNull());
+    interpolate_ = &Function::ZoneHandle(
+        zone, cls.LookupFunctionAllowPrivate(Symbols::Interpolate()));
+    ASSERT(!interpolate_->IsNull());
+  }
+  return *interpolate_;
+}
+
 }  // namespace dart
diff --git a/runtime/vm/compiler/compiler_state.h b/runtime/vm/compiler/compiler_state.h
index db608f3..1520b40 100644
--- a/runtime/vm/compiler/compiler_state.h
+++ b/runtime/vm/compiler/compiler_state.h
@@ -99,6 +99,12 @@
   // Returns class Comparable<T> from dart:core.
   const Class& ComparableClass();
 
+  // Returns _StringBase._interpolate
+  const Function& StringBaseInterpolate();
+
+  // Returns _StringBase._interpolateSingle
+  const Function& StringBaseInterpolateSingle();
+
  private:
   CHA cha_;
   intptr_t deopt_id_ = 0;
@@ -118,6 +124,8 @@
   // Lookup cache for various classes (to avoid polluting object store with
   // compiler specific classes).
   const Class* comparable_class_ = nullptr;
+  const Function* interpolate_ = nullptr;
+  const Function* interpolate_single_ = nullptr;
 
   CompilerState* previous_;
 };
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index f750101..4c3a5cf 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -1149,14 +1149,6 @@
   return Fragment(call);
 }
 
-Fragment BaseFlowGraphBuilder::StringInterpolate(TokenPosition position) {
-  Value* array = Pop();
-  StringInterpolateInstr* interpolate = new (Z) StringInterpolateInstr(
-      array, InstructionSource(position), GetNextDeoptId());
-  Push(interpolate);
-  return Fragment(interpolate);
-}
-
 void BaseFlowGraphBuilder::reset_context_depth_for_deopt_id(intptr_t deopt_id) {
   if (is_recording_context_levels()) {
     for (intptr_t i = 0, n = context_level_array_->length(); i < n; i += 2) {
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index 6873a4f..edad1e4 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -411,10 +411,6 @@
                        intptr_t argument_count,
                        const Array& argument_names);
 
-  // Builds StringInterpolate instruction, an equivalent of
-  // _StringBase._interpolate call.
-  Fragment StringInterpolate(TokenPosition position);
-
   // Pops function type arguments, instantiator type arguments, dst_type, and
   // value; and type checks value against the type arguments.
   Fragment AssertAssignable(
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph_test.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph_test.cc
index 1bd0f27..f280b55 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph_test.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph_test.cc
@@ -88,7 +88,7 @@
     kMatchAndMoveCreateArray,
     {kMatchAndMoveStoreIndexed, &store1},
     {kMatchAndMoveStoreIndexed, &store2},
-    kMatchAndMoveStringInterpolate,
+    kMatchAndMoveStaticCall,
     kMoveDebugStepChecks,
     kMatchReturn,
   }));
@@ -147,7 +147,7 @@
     {kMatchAndMoveStoreIndexed, &store1},
     {kMatchAndMoveStoreIndexed, &store2},
     {kMatchAndMoveStoreIndexed, &store3},
-    kMatchAndMoveStringInterpolate,
+    kMatchAndMoveStaticCall,
     kMoveDebugStepChecks,
     kMatchReturn,
   }));
@@ -215,7 +215,7 @@
     {kMatchAndMoveStoreIndexed, &store1},
     {kMatchAndMoveStoreIndexed, &store2},
     {kMatchAndMoveStoreIndexed, &store3},
-    kMatchAndMoveStringInterpolate,
+    kMatchAndMoveStaticCall,
     kMoveDebugStepChecks,
     kMatchReturn,
   }));
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 8c9be51..c4a43ff 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -617,19 +617,18 @@
 }
 
 Fragment FlowGraphBuilder::StringInterpolateSingle(TokenPosition position) {
-  const int kTypeArgsLen = 0;
-  const int kNumberOfArguments = 1;
-  const Array& kNoArgumentNames = Object::null_array();
-  const Class& cls =
-      Class::Handle(Library::LookupCoreClass(Symbols::StringBase()));
-  ASSERT(!cls.IsNull());
-  const Function& function = Function::ZoneHandle(
-      Z, Resolver::ResolveStatic(
-             cls, Library::PrivateCoreLibName(Symbols::InterpolateSingle()),
-             kTypeArgsLen, kNumberOfArguments, kNoArgumentNames));
+  Fragment instructions;
+  instructions += StaticCall(
+      position, CompilerState::Current().StringBaseInterpolateSingle(),
+      /* argument_count = */ 1, ICData::kStatic);
+  return instructions;
+}
+
+Fragment FlowGraphBuilder::StringInterpolate(TokenPosition position) {
   Fragment instructions;
   instructions +=
-      StaticCall(position, function, /* argument_count = */ 1, ICData::kStatic);
+      StaticCall(position, CompilerState::Current().StringBaseInterpolate(),
+                 /* argument_count = */ 1, ICData::kStatic);
   return instructions;
 }
 
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 7f606e4..f9d1a6e 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -222,6 +222,7 @@
                       intptr_t type_args_len = 0,
                       bool use_unchecked_entry = false);
   Fragment StringInterpolateSingle(TokenPosition position);
+  Fragment StringInterpolate(TokenPosition position);
   Fragment ThrowTypeError();
   Fragment ThrowNoSuchMethodError(const Function& target);
   Fragment ThrowLateInitializationError(TokenPosition position,
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 3f46559..3810986 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -81,7 +81,7 @@
   V(::, _toClampedUint8, ConvertIntToClampedUint8, 0x00fc4650)                 \
   V(::, copyRangeFromUint8ListToOneByteString,                                 \
     CopyRangeFromUint8ListToOneByteString, 0x0df019c5)                         \
-  V(_StringBase, _interpolate, StringBaseInterpolate, 0xea1eac09)              \
+  V(_StringBase, _interpolate, StringBaseInterpolate, 0xea1eafca)              \
   V(_IntegerImplementation, toDouble, IntegerToDouble, 0x97728b46)             \
   V(_Double, _add, DoubleAdd, 0xea666327)                                      \
   V(_Double, _sub, DoubleSub, 0x28474c2e)                                      \
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 0ee8b47..a645736 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4520,8 +4520,8 @@
         "Class\",\"fixedId\":true,\"id\":\"\",\"name\":\"_OneByteString\",\"_"
         "vmName\":\"\",\"location\":{\"type\":\"SourceLocation\",\"script\":{"
         "\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\",\"uri\":\"dart:core-"
-        "patch\\/string_patch.dart\",\"_kind\":\"kernel\"},\"tokenPos\":32310,"
-        "\"endTokenPos\":44599},\"library\":{\"type\":\"@Library\",\"fixedId\":"
+        "patch\\/string_patch.dart\",\"_kind\":\"kernel\"},\"tokenPos\":32339,"
+        "\"endTokenPos\":44628},\"library\":{\"type\":\"@Library\",\"fixedId\":"
         "true,\"id\":\"\",\"name\":\"dart.core\",\"uri\":\"dart:core\"}},"
         "\"identityHashCode\":0,\"kind\":\"String\",\"id\":\"\",\"length\":2,"
         "\"valueAsString\":\"dw\"}",
diff --git a/sdk/lib/_internal/vm/lib/string_patch.dart b/sdk/lib/_internal/vm/lib/string_patch.dart
index be6b858..b02d273 100644
--- a/sdk/lib/_internal/vm/lib/string_patch.dart
+++ b/sdk/lib/_internal/vm/lib/string_patch.dart
@@ -846,6 +846,7 @@
    */
   @pragma("vm:recognized", "other")
   @pragma("vm:entry-point", "call")
+  @pragma("vm:never-inline")
   static String _interpolate(final List values) {
     final numValues = values.length;
     int totalLength = 0;
diff --git a/tools/VERSION b/tools/VERSION
index 1390739..1e2134a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 266
+PRERELEASE 267
 PRERELEASE_PATCH 0
\ No newline at end of file