a62e924859
There might be a race between background compiler and mutator where mutator changes guarded state of the field after Slot was created from it. A situation is possible where we have a clone of a field with its guarded state set to unknown, however Slot::Get for this field returns a Slot created from the previous clone of the same field with a known guarded state. In this case we must add *old* clone from which the Slot was created to guarded fields and not the new clone, because new clone has no guarded state to begin with and thus ParsedFunction::AddToGuardedFields(...) would simply ignore it. Such slots with inconsistent guarded state that are not in the current list of guarded fields arise due to unsuccessful inlining attempts. If we built and discard the graph, then guarded fields associated with that graph are also discarded. However the slot itself stays behind in the compilation global cache. TEST=vm/cc/SlotFromGuardedField Bug: b/121271056 Change-Id: Ib6ee5ec4922c033c3a71bdc46da74ead47b1edd0 Reviewed-on: https://dart-review.googlesource.com/c/88575 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
105 lines
4.4 KiB
C++
105 lines
4.4 KiB
C++
// Copyright (c) 2019, 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.
|
|
|
|
#include "include/dart_api.h"
|
|
|
|
#include "platform/globals.h"
|
|
|
|
#include "vm/compiler/backend/slot.h"
|
|
#include "vm/compiler/compiler_state.h"
|
|
#include "vm/object.h"
|
|
#include "vm/parser.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/unit_test.h"
|
|
|
|
namespace dart {
|
|
|
|
// This is a regression test for b/121271056: there might be a race between
|
|
// background compiler and mutator where mutator changes guarded state of
|
|
// the field after Slot was created from it. A situation is possible where we
|
|
// have a clone of a field with its guarded state set to unknown, however
|
|
// Slot::Get for this field returns a Slot created from the previous clone of
|
|
// the same field with a known guarded state. In this case we must add *old*
|
|
// clone from which the Slot was created to guarded fields and not the new
|
|
// clone, because new clone has no guarded state to begin with and thus
|
|
// ParsedFunction::AddToGuardedFields(...) would simply ignore it.
|
|
// Such slots with inconsistent guarded state that are not in the current
|
|
// list of guarded fields arise due to unsuccessful inlining attempts.
|
|
// If we built and discard the graph, then guarded fields associated with
|
|
// that graph are also discarded. However the slot itself stays behind in
|
|
// the global cache.
|
|
// Adding old clone would lead to correct rejection of the compilation
|
|
// attempt because Slot type information is different from the current guarded
|
|
// state of the field.
|
|
TEST_CASE(SlotFromGuardedField) {
|
|
if (!FLAG_use_field_guards) {
|
|
return;
|
|
}
|
|
|
|
TransitionNativeToVM transition(thread);
|
|
Zone* zone = thread->zone();
|
|
|
|
// Setup: create dummy class, function and a field.
|
|
const Class& dummy_class = Class::Handle(Class::New(
|
|
Library::Handle(), String::Handle(Symbols::New(thread, "DummyClass")),
|
|
Script::Handle(), TokenPosition::kNoSource));
|
|
dummy_class.set_is_synthesized_class();
|
|
|
|
const Function& dummy_function = Function::ZoneHandle(
|
|
Function::New(String::Handle(Symbols::New(thread, "foo")),
|
|
RawFunction::kRegularFunction, false, false, false, false,
|
|
false, dummy_class, TokenPosition::kMinSource));
|
|
|
|
const Field& field = Field::Handle(
|
|
Field::New(String::Handle(Symbols::New(thread, "field")),
|
|
/*is_static=*/false, /*is_final=*/false, /*is_const=*/false,
|
|
/*is_reflectable=*/true, dummy_class, Object::dynamic_type(),
|
|
TokenPosition::kMinSource, TokenPosition::kMinSource));
|
|
|
|
// Set non-trivial guarded state on the field.
|
|
field.set_guarded_cid(kSmiCid);
|
|
field.set_is_nullable(false);
|
|
|
|
// Enter compiler state.
|
|
CompilerState compiler_state(thread);
|
|
|
|
const Field& field_clone_1 = Field::ZoneHandle(field.CloneFromOriginal());
|
|
const Field& field_clone_2 = Field::ZoneHandle(field.CloneFromOriginal());
|
|
|
|
// Check that Slot::Get() returns correctly canonicalized and configured
|
|
// slot that matches properties of the field.
|
|
ParsedFunction* parsed_function =
|
|
new (zone) ParsedFunction(thread, dummy_function);
|
|
const Slot& slot1 = Slot::Get(field_clone_1, parsed_function);
|
|
const Slot& slot2 = Slot::Get(field_clone_2, parsed_function);
|
|
EXPECT_EQ(&slot1, &slot2);
|
|
EXPECT(slot1.is_guarded_field());
|
|
EXPECT(!slot1.is_nullable());
|
|
EXPECT_EQ(kSmiCid, slot1.nullable_cid());
|
|
|
|
// Check that the field was added (once) to the list of guarded fields.
|
|
EXPECT_EQ(1, parsed_function->guarded_fields()->length());
|
|
EXPECT_EQ(parsed_function->guarded_fields()->At(0)->raw(),
|
|
field_clone_1.raw());
|
|
|
|
// Change the guarded state of the field to "unknown" - emulating concurrent
|
|
// modification of the guarded state in mutator) and create a new clone of
|
|
// the field.
|
|
field.set_guarded_cid(kDynamicCid);
|
|
field.set_is_nullable(true);
|
|
const Field& field_clone_3 = Field::ZoneHandle(field.CloneFromOriginal());
|
|
|
|
// Slot::Get must return the same slot and add the field from which it
|
|
// was created to the guarded fields list.
|
|
ParsedFunction* parsed_function2 =
|
|
new (zone) ParsedFunction(thread, dummy_function);
|
|
const Slot& slot3 = Slot::Get(field_clone_3, parsed_function2);
|
|
EXPECT_EQ(&slot1, &slot3);
|
|
EXPECT_EQ(1, parsed_function2->guarded_fields()->length());
|
|
EXPECT_EQ(parsed_function2->guarded_fields()->At(0)->raw(),
|
|
field_clone_1.raw());
|
|
}
|
|
|
|
} // namespace dart
|