38c6152884
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>
279 lines
8.8 KiB
C++
279 lines
8.8 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/globals.h" // Needed here to get TARGET_ARCH_IA32.
|
|
#if defined(TARGET_ARCH_IA32)
|
|
|
|
#include "vm/code_patcher.h"
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/backend/flow_graph_compiler.h"
|
|
#include "vm/cpu.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/instructions.h"
|
|
#include "vm/object.h"
|
|
#include "vm/raw_object.h"
|
|
|
|
namespace dart {
|
|
|
|
// The expected pattern of a Dart unoptimized call (static and instance):
|
|
// mov ECX, ic-data
|
|
// mov EDI, target-code-object
|
|
// call target_address (stub)
|
|
// <- return address
|
|
class UnoptimizedCall : public ValueObject {
|
|
public:
|
|
explicit UnoptimizedCall(uword return_address)
|
|
: start_(return_address - kPatternSize) {
|
|
ASSERT(IsValid());
|
|
}
|
|
|
|
RawObject* ic_data() const {
|
|
return *reinterpret_cast<RawObject**>(start_ + 1);
|
|
}
|
|
|
|
static const int kMovInstructionSize = 5;
|
|
static const int kCallInstructionSize = 3;
|
|
static const int kPatternSize =
|
|
2 * kMovInstructionSize + kCallInstructionSize;
|
|
|
|
private:
|
|
bool IsValid() {
|
|
uint8_t* code_bytes = reinterpret_cast<uint8_t*>(start_);
|
|
return (code_bytes[0] == 0xB9) &&
|
|
(code_bytes[2 * kMovInstructionSize] == 0xFF);
|
|
}
|
|
|
|
uword return_address() const { return start_ + kPatternSize; }
|
|
|
|
uword call_address() const { return start_ + 2 * kMovInstructionSize; }
|
|
|
|
protected:
|
|
uword start_;
|
|
|
|
private:
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(UnoptimizedCall);
|
|
};
|
|
|
|
class NativeCall : public UnoptimizedCall {
|
|
public:
|
|
explicit NativeCall(uword return_address) : UnoptimizedCall(return_address) {}
|
|
|
|
NativeFunction native_function() const {
|
|
return *reinterpret_cast<NativeFunction*>(start_ + 1);
|
|
}
|
|
|
|
void set_native_function(NativeFunction func) const {
|
|
Thread::Current()->isolate_group()->RunWithStoppedMutators([&]() {
|
|
WritableInstructionsScope writable(start_ + 1, sizeof(func));
|
|
*reinterpret_cast<NativeFunction*>(start_ + 1) = func;
|
|
});
|
|
}
|
|
|
|
private:
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(NativeCall);
|
|
};
|
|
|
|
// b9xxxxxxxx mov ecx,<data>
|
|
// bfyyyyyyyy mov edi,<target>
|
|
// ff5707 call [edi+<monomorphic-entry-offset>]
|
|
class InstanceCall : public UnoptimizedCall {
|
|
public:
|
|
explicit InstanceCall(uword return_address)
|
|
: UnoptimizedCall(return_address) {
|
|
#if defined(DEBUG)
|
|
Object& test_data = Object::Handle(data());
|
|
ASSERT(test_data.IsArray() || test_data.IsICData() ||
|
|
test_data.IsMegamorphicCache());
|
|
if (test_data.IsICData()) {
|
|
ASSERT(ICData::Cast(test_data).NumArgsTested() > 0);
|
|
}
|
|
#endif // DEBUG
|
|
}
|
|
|
|
RawObject* data() const { return *reinterpret_cast<RawObject**>(start_ + 1); }
|
|
void set_data(const Object& data) const {
|
|
uword* cache_addr = reinterpret_cast<uword*>(start_ + 1);
|
|
uword imm = reinterpret_cast<uword>(data.raw());
|
|
*cache_addr = imm;
|
|
}
|
|
|
|
RawCode* target() const {
|
|
const uword imm = *reinterpret_cast<uword*>(start_ + 6);
|
|
return reinterpret_cast<RawCode*>(imm);
|
|
}
|
|
void set_target(const Code& target) const {
|
|
uword* target_addr = reinterpret_cast<uword*>(start_ + 6);
|
|
uword imm = reinterpret_cast<uword>(target.raw());
|
|
*target_addr = imm;
|
|
}
|
|
|
|
private:
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(InstanceCall);
|
|
};
|
|
|
|
class UnoptimizedStaticCall : public UnoptimizedCall {
|
|
public:
|
|
explicit UnoptimizedStaticCall(uword return_address)
|
|
: UnoptimizedCall(return_address) {
|
|
#if defined(DEBUG)
|
|
ICData& test_ic_data = ICData::Handle();
|
|
test_ic_data ^= ic_data();
|
|
ASSERT(test_ic_data.NumArgsTested() >= 0);
|
|
#endif // DEBUG
|
|
}
|
|
|
|
private:
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(UnoptimizedStaticCall);
|
|
};
|
|
|
|
// The expected pattern of a dart static call:
|
|
// mov EDX, arguments_descriptor_array (optional in polymorphic calls)
|
|
// mov EDI, Immediate(code_object)
|
|
// call [EDI + entry_point_offset]
|
|
// <- return address
|
|
class StaticCall : public ValueObject {
|
|
public:
|
|
explicit StaticCall(uword return_address)
|
|
: start_(return_address - (kMovInstructionSize + kCallInstructionSize)) {
|
|
ASSERT(IsValid());
|
|
}
|
|
|
|
bool IsValid() {
|
|
uint8_t* code_bytes = reinterpret_cast<uint8_t*>(start_);
|
|
return (code_bytes[0] == 0xBF) && (code_bytes[5] == 0xFF);
|
|
}
|
|
|
|
RawCode* target() const {
|
|
const uword imm = *reinterpret_cast<uword*>(start_ + 1);
|
|
return reinterpret_cast<RawCode*>(imm);
|
|
}
|
|
|
|
void set_target(const Code& target) const {
|
|
uword* target_addr = reinterpret_cast<uword*>(start_ + 1);
|
|
uword imm = reinterpret_cast<uword>(target.raw());
|
|
*target_addr = imm;
|
|
CPU::FlushICache(start_ + 1, sizeof(imm));
|
|
}
|
|
|
|
static const int kMovInstructionSize = 5;
|
|
static const int kCallInstructionSize = 3;
|
|
|
|
private:
|
|
uword return_address() const {
|
|
return start_ + kMovInstructionSize + kCallInstructionSize;
|
|
}
|
|
|
|
uword call_address() const { return start_ + kMovInstructionSize; }
|
|
|
|
uword start_;
|
|
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(StaticCall);
|
|
};
|
|
|
|
RawCode* CodePatcher::GetStaticCallTargetAt(uword return_address,
|
|
const Code& code) {
|
|
ASSERT(code.ContainsInstructionAt(return_address));
|
|
StaticCall call(return_address);
|
|
return call.target();
|
|
}
|
|
|
|
void CodePatcher::PatchStaticCallAt(uword return_address,
|
|
const Code& code,
|
|
const Code& new_target) {
|
|
auto thread = Thread::Current();
|
|
auto zone = thread->zone();
|
|
const Instructions& instrs = Instructions::Handle(zone, code.instructions());
|
|
thread->isolate_group()->RunWithStoppedMutators([&]() {
|
|
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
|
|
ASSERT(code.ContainsInstructionAt(return_address));
|
|
StaticCall call(return_address);
|
|
call.set_target(new_target);
|
|
});
|
|
}
|
|
|
|
void CodePatcher::InsertDeoptimizationCallAt(uword start) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
RawCode* CodePatcher::GetInstanceCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
Object* data) {
|
|
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
|
InstanceCall call(return_address);
|
|
if (data != NULL) {
|
|
*data = call.data();
|
|
}
|
|
return call.target();
|
|
}
|
|
|
|
void CodePatcher::PatchInstanceCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
const Object& data,
|
|
const Code& target) {
|
|
auto thread = Thread::Current();
|
|
auto zone = thread->zone();
|
|
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
|
const Instructions& instrs =
|
|
Instructions::Handle(zone, caller_code.instructions());
|
|
thread->isolate_group()->RunWithStoppedMutators([&]() {
|
|
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
|
|
InstanceCall call(return_address);
|
|
call.set_data(data);
|
|
call.set_target(target);
|
|
});
|
|
}
|
|
|
|
RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
ICData* ic_data_result) {
|
|
ASSERT(caller_code.ContainsInstructionAt(return_address));
|
|
UnoptimizedStaticCall static_call(return_address);
|
|
ICData& ic_data = ICData::Handle();
|
|
ic_data ^= static_call.ic_data();
|
|
if (ic_data_result != NULL) {
|
|
*ic_data_result = ic_data.raw();
|
|
}
|
|
return ic_data.GetTargetAt(0);
|
|
}
|
|
|
|
void CodePatcher::PatchSwitchableCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
const Object& data,
|
|
const Code& target) {
|
|
// Switchable instance calls only generated for precompilation.
|
|
UNREACHABLE();
|
|
}
|
|
|
|
RawCode* CodePatcher::GetSwitchableCallTargetAt(uword return_address,
|
|
const Code& caller_code) {
|
|
// Switchable instance calls only generated for precompilation.
|
|
UNREACHABLE();
|
|
return Code::null();
|
|
}
|
|
|
|
RawObject* CodePatcher::GetSwitchableCallDataAt(uword return_address,
|
|
const Code& caller_code) {
|
|
// Switchable instance calls only generated for precompilation.
|
|
UNREACHABLE();
|
|
return Object::null();
|
|
}
|
|
|
|
void CodePatcher::PatchNativeCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
NativeFunction target,
|
|
const Code& trampoline) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
RawCode* CodePatcher::GetNativeCallAt(uword return_address,
|
|
const Code& caller_code,
|
|
NativeFunction* target) {
|
|
UNREACHABLE();
|
|
return NULL;
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // defined TARGET_ARCH_IA32
|