| // 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_unsafe(); |
| |
| const FunctionType& signature = FunctionType::ZoneHandle(FunctionType::New()); |
| const Function& dummy_function = Function::ZoneHandle( |
| Function::New(signature, String::Handle(Symbols::New(thread, "foo")), |
| UntaggedFunction::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, /*is_late=*/false, dummy_class, |
| Object::dynamic_type(), TokenPosition::kMinSource, |
| TokenPosition::kMinSource)); |
| |
| // Set non-trivial guarded state on the field. |
| field.set_guarded_cid_unsafe(kSmiCid); |
| field.set_is_nullable_unsafe(false); |
| |
| // Enter compiler state. |
| CompilerState compiler_state(thread, /*is_aot=*/false, |
| /*is_optimizing=*/true); |
| |
| 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)->ptr(), |
| field_clone_1.ptr()); |
| |
| // 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_unsafe(kDynamicCid); |
| field.set_is_nullable_unsafe(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)->ptr(), |
| field_clone_1.ptr()); |
| } |
| |
| } // namespace dart |