Files
sdk/runtime/vm/stub_code.cc
T
Martin Kustermann 38c6152884 [vm/concurrency] Add IsolateGroup::RunWithStoppedMutators and use it in various places
When installing new code, we need to ensure the mutator is stopped for a
variety of reasons.

Right now we only need to distuinguish mutator and background compiler
when installing code: The mutator can install code without any
synchronization whereas the bg compiler needs to get the mutator to a safepoint.

Yet once all isolates within one isolate group share a heap, a mutator
might need to stop all other mutators before installing code (since they
operate on pages on which we flip page protection bits)

This CL adds IsolateGroup::RunWithStoppedMutators which will get other
mutators to a safepoint (if there are multiple or we are on a bg
compiler thread).

Along with this we add a read-write lock and use it for the protection
of the IsolateGroup::isolate_: While we make assumptions about the
number of isolates in a group we force all pending additions of new
isolates to wait. Later on this will also be used to allow iterating the
list of isolates during GC and prevent new isolates from being added at
the same time.

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

Change-Id: I6e761fa51d36b2f2b4b67995cac954898ce7fd69
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-android-release-arm-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-bare-linux-release-x64-try,vm-kernel-precomp-mac-debug-simarm_x64-try,vm-kernel-precomp-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116767
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2019-09-18 12:24:31 +00:00

320 lines
10 KiB
C++

// Copyright (c) 2012, 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 "vm/stub_code.h"
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/clustered_snapshot.h"
#include "vm/compiler/aot/precompiler.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/flags.h"
#include "vm/heap/safepoint.h"
#include "vm/interpreter.h"
#include "vm/object_store.h"
#include "vm/snapshot.h"
#include "vm/virtual_memory.h"
#include "vm/visitor.h"
namespace dart {
DEFINE_FLAG(bool, disassemble_stubs, false, "Disassemble generated stubs.");
DECLARE_FLAG(bool, precompiled_mode);
DECLARE_FLAG(bool, enable_interpreter);
Code* StubCode::entries_[kNumStubEntries] = {
#define STUB_CODE_DECLARE(name) nullptr,
VM_STUB_CODE_LIST(STUB_CODE_DECLARE)
#undef STUB_CODE_DECLARE
};
#if defined(DART_PRECOMPILED_RUNTIME)
void StubCode::Init() {
// Stubs will be loaded from the snapshot.
UNREACHABLE();
}
#else
#define STUB_CODE_GENERATE(name) \
entries_[k##name##Index] = Code::ReadOnlyHandle(); \
*entries_[k##name##Index] = \
Generate("_stub_" #name, &object_pool_builder, \
compiler::StubCodeCompiler::Generate##name##Stub);
#define STUB_CODE_SET_OBJECT_POOL(name) \
entries_[k##name##Index]->set_object_pool(object_pool.raw());
void StubCode::Init() {
compiler::ObjectPoolBuilder object_pool_builder;
// Generate all the stubs.
VM_STUB_CODE_LIST(STUB_CODE_GENERATE);
const ObjectPool& object_pool =
ObjectPool::Handle(ObjectPool::NewFromBuilder(object_pool_builder));
VM_STUB_CODE_LIST(STUB_CODE_SET_OBJECT_POOL)
}
#undef STUB_CODE_GENERATE
#undef STUB_CODE_SET_OBJECT_POOL
RawCode* StubCode::Generate(
const char* name,
compiler::ObjectPoolBuilder* object_pool_builder,
void (*GenerateStub)(compiler::Assembler* assembler)) {
compiler::Assembler assembler(object_pool_builder);
GenerateStub(&assembler);
const Code& code = Code::Handle(Code::FinalizeCodeAndNotify(
name, nullptr, &assembler, Code::PoolAttachment::kNotAttachPool,
/*optimized=*/false));
#ifndef PRODUCT
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
LogBlock lb;
THR_Print("Code for stub '%s': {\n", name);
DisassembleToStdout formatter;
code.Disassemble(&formatter);
THR_Print("}\n");
const ObjectPool& object_pool = ObjectPool::Handle(code.object_pool());
if (!object_pool.IsNull()) {
object_pool.DebugPrint();
}
}
#endif // !PRODUCT
return code.raw();
}
#endif // defined(DART_PRECOMPILED_RUNTIME)
#define STUB_CODE_CLEANUP(name) entries_[k##name##Index] = nullptr;
void StubCode::Cleanup() {
VM_STUB_CODE_LIST(STUB_CODE_CLEANUP);
}
#undef STUB_CODE_CLEANUP
bool StubCode::HasBeenInitialized() {
// Use AsynchronousGapMarker as canary.
return entries_[kAsynchronousGapMarkerIndex] != nullptr;
}
bool StubCode::InInvocationStub(uword pc, bool is_interpreted_frame) {
#if !defined(TARGET_ARCH_DBC)
ASSERT(HasBeenInitialized());
#if !defined(DART_PRECOMPILED_RUNTIME)
if (FLAG_enable_interpreter) {
if (is_interpreted_frame) {
// Recognize special marker set up by interpreter in entry frame.
return Interpreter::IsEntryFrameMarker(
reinterpret_cast<const KBCInstr*>(pc));
}
{
uword entry = StubCode::InvokeDartCodeFromBytecode().EntryPoint();
uword size = StubCode::InvokeDartCodeFromBytecodeSize();
if ((pc >= entry) && (pc < (entry + size))) {
return true;
}
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
uword entry = StubCode::InvokeDartCode().EntryPoint();
uword size = StubCode::InvokeDartCodeSize();
return (pc >= entry) && (pc < (entry + size));
#else
if (FLAG_enable_interpreter) {
FATAL(
"Simultaneous usage of DBC simulator "
"and interpreter not yet supported.");
}
// On DBC we use a special marker PC to signify entry frame because there is
// no such thing as invocation stub.
return (pc & 2) != 0;
#endif
}
bool StubCode::InJumpToFrameStub(uword pc) {
#if !defined(TARGET_ARCH_DBC)
ASSERT(HasBeenInitialized());
uword entry = StubCode::JumpToFrame().EntryPoint();
uword size = StubCode::JumpToFrameSize();
return (pc >= entry) && (pc < (entry + size));
#else
// This stub does not exist on DBC.
return false;
#endif
}
RawCode* StubCode::GetAllocationStubForClass(const Class& cls) {
// These stubs are not used by DBC.
#if !defined(TARGET_ARCH_DBC)
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread));
ASSERT(error.IsNull());
if (cls.id() == kArrayCid) {
return AllocateArray().raw();
}
Code& stub = Code::Handle(zone, cls.allocation_stub());
#if !defined(DART_PRECOMPILED_RUNTIME)
if (stub.IsNull()) {
compiler::ObjectPoolBuilder object_pool_builder;
Precompiler* precompiler = Precompiler::Instance();
compiler::ObjectPoolBuilder* wrapper =
FLAG_use_bare_instructions && precompiler != NULL
? precompiler->global_object_pool_builder()
: &object_pool_builder;
const auto pool_attachment =
FLAG_precompiled_mode && FLAG_use_bare_instructions
? Code::PoolAttachment::kNotAttachPool
: Code::PoolAttachment::kAttachPool;
compiler::Assembler assembler(wrapper);
const char* name = cls.ToCString();
compiler::StubCodeCompiler::GenerateAllocationStubForClass(&assembler, cls);
auto mutator_fun = [&]() {
stub = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
/*optimized=*/false,
/*stats=*/nullptr);
// Check if background compilation thread has not already added the stub.
if (cls.allocation_stub() == Code::null()) {
stub.set_owner(cls);
cls.set_allocation_stub(stub);
}
};
auto bg_compiler_fun = [&]() {
ForceGrowthSafepointOperationScope safepoint_scope(thread);
stub = cls.allocation_stub();
// Check if stub was already generated.
if (!stub.IsNull()) {
return;
}
stub = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
/*optimized=*/false, /*stats=*/nullptr);
stub.set_owner(cls);
cls.set_allocation_stub(stub);
};
// We have to ensure no mutators are running, because:
//
// a) We allocate an instructions object, which might cause us to
// temporarily flip page protections from (RX -> RW -> RX).
//
// b) To ensure only one thread succeeds installing an allocation for the
// given class.
//
thread->isolate_group()->RunWithStoppedMutators(
mutator_fun, bg_compiler_fun, /*use_force_growth=*/true);
// We notify code observers after finalizing the code in order to be
// outside a [SafepointOperationScope].
Code::NotifyCodeObservers(name, stub, /*optimized=*/false);
#ifndef PRODUCT
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
LogBlock lb;
THR_Print("Code for allocation stub '%s': {\n", name);
DisassembleToStdout formatter;
stub.Disassemble(&formatter);
THR_Print("}\n");
const ObjectPool& object_pool = ObjectPool::Handle(stub.object_pool());
if (!object_pool.IsNull()) {
object_pool.DebugPrint();
}
}
#endif // !PRODUCT
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
return stub.raw();
#endif // !DBC
UNIMPLEMENTED();
return Code::null();
}
#if !defined(TARGET_ARCH_DBC) && !defined(TARGET_ARCH_IA32)
RawCode* StubCode::GetBuildMethodExtractorStub(
compiler::ObjectPoolBuilder* pool) {
#if !defined(DART_PRECOMPILED_RUNTIME)
auto thread = Thread::Current();
auto Z = thread->zone();
auto object_store = thread->isolate()->object_store();
const auto& closure_class =
Class::ZoneHandle(Z, object_store->closure_class());
const auto& closure_allocation_stub =
Code::ZoneHandle(Z, StubCode::GetAllocationStubForClass(closure_class));
const auto& context_allocation_stub = StubCode::AllocateContext();
compiler::ObjectPoolBuilder object_pool_builder;
compiler::Assembler assembler(pool != nullptr ? pool : &object_pool_builder);
compiler::StubCodeCompiler::GenerateBuildMethodExtractorStub(
&assembler, closure_allocation_stub, context_allocation_stub);
const char* name = "BuildMethodExtractor";
const Code& stub = Code::Handle(Code::FinalizeCodeAndNotify(
name, nullptr, &assembler, Code::PoolAttachment::kNotAttachPool,
/*optimized=*/false));
if (pool == nullptr) {
stub.set_object_pool(ObjectPool::NewFromBuilder(object_pool_builder));
}
#ifndef PRODUCT
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
LogBlock lb;
THR_Print("Code for isolate stub '%s': {\n", name);
DisassembleToStdout formatter;
stub.Disassemble(&formatter);
THR_Print("}\n");
const ObjectPool& object_pool = ObjectPool::Handle(stub.object_pool());
if (!object_pool.IsNull()) {
object_pool.DebugPrint();
}
}
#endif // !PRODUCT
return stub.raw();
#else // !defined(DART_PRECOMPILED_RUNTIME)
UNIMPLEMENTED();
return nullptr;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
}
#endif // !defined(TARGET_ARCH_DBC)
const Code& StubCode::UnoptimizedStaticCallEntry(intptr_t num_args_tested) {
// These stubs are not used by DBC.
#if !defined(TARGET_ARCH_DBC)
switch (num_args_tested) {
case 0:
return ZeroArgsUnoptimizedStaticCall();
case 1:
return OneArgUnoptimizedStaticCall();
case 2:
return TwoArgsUnoptimizedStaticCall();
default:
UNIMPLEMENTED();
return Code::Handle();
}
#else
return Code::Handle();
#endif
}
const char* StubCode::NameOfStub(uword entry_point) {
#define VM_STUB_CODE_TESTER(name) \
if (entries_[k##name##Index] != nullptr && \
!entries_[k##name##Index]->IsNull() && \
entries_[k##name##Index]->EntryPoint() == entry_point) { \
return "" #name; \
}
VM_STUB_CODE_LIST(VM_STUB_CODE_TESTER);
#undef VM_STUB_CODE_TESTER
return nullptr;
}
} // namespace dart