[vm/compiler] Allow lazy deopts to continue in unoptimized code at beginning of IR instruction

If an IR instruction lazy-deopts it usually continues in unoptimized
code in the same IR instruction after-call.

Though in certain situations we want to continue before-call in
unoptimized code.

Two cases relevant in this CL:

  * An instruction gets LICMed: If it lazy-deopts it will continue
    at the Goto instruction outside the loop.

  * A recognized method which got it's InstanceCall replaced by several
    IR instructions. If any of them (except the last one) lazy-deopts
    it should re-try the call in unoptimized code (e.g. []=)

In order to faciliate this we add a bit to the [Environment] which
encodes whether the continuation point in unoptimized code is
before-call - if so, we issue corresponding metadata.

Issue https://github.com/dart-lang/sdk/issues/45213
Issue https://github.com/dart-lang/sdk/issues/46070

TEST=runtime/tests/vm/dart{,_2}/regress_46070_test.dart

Change-Id: Ib824081768a2fd6293751a8fe09753e0d8155c87
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200644
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Martin Kustermann
2021-06-01 12:59:23 +00:00
committed by commit-bot@chromium.org
parent 7fb2e50d74
commit a44d1eefe6
7 changed files with 81 additions and 4 deletions
@@ -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;
}
@@ -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;
}
@@ -501,10 +501,20 @@ void FlowGraphCompiler::EmitCallsiteMetadata(const InstructionSource& source,
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,
+1
View File
@@ -5742,6 +5742,7 @@ Environment* Environment::DeepCopy(Zone* zone, intptr_t length) const {
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);
+16 -2
View File
@@ -9423,6 +9423,14 @@ class Environment : public ZoneAllocated {
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 @@ class Environment : public ZoneAllocated {
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 @@ class Environment : public ZoneAllocated {
: 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 @@ class Environment : public ZoneAllocated {
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;
+5
View File
@@ -2657,6 +2657,11 @@ static bool InlineSetIndexed(FlowGraph* flow_graph,
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();
}
}
@@ -1368,6 +1368,9 @@ void LICM::Hoist(ForwardInstructionIterator* it,
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);
}