Version 2.14.0-169.0.dev

Merge commit 'af957d95786b33901ea7d0260ab286a964fe8318' into 'dev'
diff --git a/DEPS b/DEPS
index accfc1b..f0269ec 100644
--- a/DEPS
+++ b/DEPS
@@ -142,7 +142,7 @@
   "pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
   "process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
   "protobuf_rev": "0d03fd588df69e9863e2a2efc0059dee8f18d5b2",
-  "pub_rev": "def32ceb1d660552eaec24839d377199aea5a569",
+  "pub_rev": "11c2a0978e66fbc2c182dc6a8174db1a3651276c",
   "pub_semver_rev": "f50d80ef10c4b2fa5f4c8878036a4d9342c0cc82",
   "resource_rev": "6b79867d0becf5395e5819a75720963b8298e9a7",
   "root_certificates_rev": "7e5ec82c99677a2e5b95ce296c4d68b0d3378ed8",
diff --git a/runtime/tests/vm/dart/deopt/restart_call_on_deopt_regress_46070_test.dart b/runtime/tests/vm/dart/deopt/restart_call_on_deopt_regress_46070_test.dart
new file mode 100644
index 0000000..959c792
--- /dev/null
+++ b/runtime/tests/vm/dart/deopt/restart_call_on_deopt_regress_46070_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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.
+
+// VMOptions=--deterministic --deoptimize-on-runtime-call-every=3 --optimization-counter-threshold=10
+
+main() {
+  final l = <int>[1, 2, 3, 4, 5];
+  for (int i = 0; i < 1000; ++i) {
+    if (sumIt(l) != 15) throw 'failed';
+  }
+}
+
+@pragma('vm:never-inline')
+int sumIt(dynamic arg) {
+  int sum = 0;
+  for (int i = 0; i < 5; ++i) {
+    final l = arg as List<int>;
+    sum += l[i];
+  }
+  return sum;
+}
diff --git a/runtime/tests/vm/dart_2/deopt/restart_call_on_deopt_regress_46070_test.dart b/runtime/tests/vm/dart_2/deopt/restart_call_on_deopt_regress_46070_test.dart
new file mode 100644
index 0000000..959c792
--- /dev/null
+++ b/runtime/tests/vm/dart_2/deopt/restart_call_on_deopt_regress_46070_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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.
+
+// VMOptions=--deterministic --deoptimize-on-runtime-call-every=3 --optimization-counter-threshold=10
+
+main() {
+  final l = <int>[1, 2, 3, 4, 5];
+  for (int i = 0; i < 1000; ++i) {
+    if (sumIt(l) != 15) throw 'failed';
+  }
+}
+
+@pragma('vm:never-inline')
+int sumIt(dynamic arg) {
+  int sum = 0;
+  for (int i = 0; i < 5; ++i) {
+    final l = arg as List<int>;
+    sum += l[i];
+  }
+  return sum;
+}
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index bc3bf363..0094d89 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -501,10 +501,20 @@
   if ((deopt_id != DeoptId::kNone) && !FLAG_precompiled_mode) {
     // Marks either the continuation point in unoptimized code or the
     // deoptimization point in optimized code, after call.
-    const intptr_t deopt_id_after = DeoptId::ToDeoptAfter(deopt_id);
     if (is_optimizing()) {
-      AddDeoptIndexAtCall(deopt_id_after, env);
+      ASSERT(env != nullptr);
+      // Note that we may lazy-deopt to the same IR instruction in unoptimized
+      // code or to another IR instruction (e.g. if LICM hoisted an instruction
+      // it will lazy-deopt to a Goto).
+      // If we happen to deopt to the beginning of an instruction in unoptimized
+      // code, we'll use the before deopt-id, otherwise the after deopt-id.
+      const intptr_t dest_deopt_id = env->LazyDeoptToBeforeDeoptId()
+                                         ? deopt_id
+                                         : DeoptId::ToDeoptAfter(deopt_id);
+      AddDeoptIndexAtCall(dest_deopt_id, env);
     } else {
+      ASSERT(env == nullptr);
+      const intptr_t deopt_id_after = DeoptId::ToDeoptAfter(deopt_id);
       // Add deoptimization continuation point after the call and before the
       // arguments are removed.
       AddCurrentDescriptor(UntaggedPcDescriptors::kDeopt, deopt_id_after,
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 55da50a..0986d22 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5742,6 +5742,7 @@
       length, fixed_parameter_count_, LazyDeoptPruneCount(), parsed_function_,
       (outer_ == NULL) ? NULL : outer_->DeepCopy(zone));
   copy->SetDeoptId(DeoptIdBits::decode(bitfield_));
+  copy->SetLazyDeoptToBeforeDeoptId(LazyDeoptToBeforeDeoptId());
   if (locations_ != NULL) {
     Location* new_locations = zone->Alloc<Location>(length);
     copy->set_locations(new_locations);
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index a0b3cd1..b41c986 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -9423,6 +9423,14 @@
     return LazyDeoptPruningBits::decode(bitfield_);
   }
 
+  bool LazyDeoptToBeforeDeoptId() const {
+    return LazyDeoptToBeforeDeoptId::decode(bitfield_);
+  }
+
+  void MarkAsLazyDeoptToBeforeDeoptId() {
+    bitfield_ = LazyDeoptToBeforeDeoptId::update(true, bitfield_);
+  }
+
   Environment* GetLazyDeoptEnv(Zone* zone) {
     const intptr_t num_args_to_prune = LazyDeoptPruneCount();
     if (num_args_to_prune == 0) return this;
@@ -9501,11 +9509,13 @@
   friend class FlowGraphDeserializer;   // For constructor and deopt_id_.
 
   class LazyDeoptPruningBits : public BitField<uintptr_t, uintptr_t, 0, 8> {};
+  class LazyDeoptToBeforeDeoptId
+      : public BitField<uintptr_t, bool, LazyDeoptPruningBits::kNextBit, 1> {};
   class DeoptIdBits
       : public BitField<uintptr_t,
                         intptr_t,
-                        LazyDeoptPruningBits::kNextBit,
-                        kBitsPerWord - LazyDeoptPruningBits::kNextBit,
+                        LazyDeoptToBeforeDeoptId::kNextBit,
+                        kBitsPerWord - LazyDeoptToBeforeDeoptId::kNextBit,
                         /*sign_extend=*/true> {};
 
   Environment(intptr_t length,
@@ -9516,6 +9526,7 @@
       : values_(length),
         fixed_parameter_count_(fixed_parameter_count),
         bitfield_(DeoptIdBits::encode(DeoptId::kNone) |
+                  LazyDeoptToBeforeDeoptId::encode(false) |
                   LazyDeoptPruningBits::encode(lazy_deopt_pruning_count)),
         parsed_function_(parsed_function),
         outer_(outer) {}
@@ -9526,6 +9537,9 @@
   void SetLazyDeoptPruneCount(intptr_t value) {
     bitfield_ = LazyDeoptPruningBits::update(value, bitfield_);
   }
+  void SetLazyDeoptToBeforeDeoptId(bool value) {
+    bitfield_ = LazyDeoptToBeforeDeoptId::update(value, bitfield_);
+  }
 
   GrowableArray<Value*> values_;
   Location* locations_ = nullptr;
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index 5c8dcc8..d576993 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -2657,6 +2657,11 @@
           Symbols::Value(), call->deopt_id());
       cursor = flow_graph->AppendTo(cursor, assert_value, call->env(),
                                     FlowGraph::kValue);
+      // The environment is that of the InstanceCall([]=, ..., <env>).
+      // A lazy-deopt of the inserted AssertAssignable must continue in
+      // unoptimzed code.
+      // => We will re-try this []= call in unoptimized code.
+      assert_value->env()->MarkAsLazyDeoptToBeforeDeoptId();
     }
   }
 
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 5cbc45e..bda4322 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1368,6 +1368,9 @@
   GotoInstr* last = pre_header->last_instruction()->AsGoto();
   // Using kind kEffect will not assign a fresh ssa temporary index.
   flow_graph()->InsertBefore(last, current, last->env(), FlowGraph::kEffect);
+  // If the hoisted instruction lazy-deopts, it should continue at the start of
+  // the Goto (of which we copy the deopt-id from).
+  current->env()->MarkAsLazyDeoptToBeforeDeoptId();
   current->CopyDeoptIdFrom(*last);
 }
 
diff --git a/tools/VERSION b/tools/VERSION
index 2d5a96a..be9c9e0 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 168
+PRERELEASE 169
 PRERELEASE_PATCH 0
\ No newline at end of file