0920ac883c
Closures created via Closure::New() set the delayed type arguments to
the null type argument vector for non-generic closure functions and to
the empty type argument vector for generic closure functions. However,
closures created via the method extractor stub would always have the
empty type argument vector for delayed type arguments. Make the two
consistent by creating two method extractor stubs, one for generic
extracted methods and one for non-generic ones.
With this change, Closure::IsGeneric simply compares the delayed type
arguments against the empty type argument vector. This change also makes
method extraction for non-generic functions slightly faster, since the
delayed type arguments field is not changed post-allocation.
The old version of Closure::IsGeneric was the only use of
Closure::NumTypeParameters, so remove the latter method.
Related cleanups:
* Use similar checks for Class::IsGeneric and FunctionType::IsGeneric,
since they only have non-null type_parameters fields when generic.
For Class::IsGeneric, this avoids having to fetch the current thread
if the class declaration has been loaded.
* Refactor methods that read packed fields in FunctionType into two
methods, a static method that takes a FunctionTypePtr and an instance
method that calls the static method. Replace the methods in Function
that also directly read FunctionType packed fields with delegations to
the static methods.
* Rework Closure_equals to avoid unneeded handle allocation in certain
cases for non-equal closures.
* Replace cases that allocates a handle for the closure function only
for retrieving the signature with calls to Closure::signature().
TEST=language_2/closure/tearoff_dynamic_test
language_2/regress/regress45890_test
Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-debug-x64-try,vm-kernel-linux-debug-x64c-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-nnbd-linux-release-simarm-try,vm-kernel-nnbd-linux-release-simarm64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-nnbd-linux-release-simarm64-try,vm-kernel-linux-debug-simarm64c-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-nnbd-linux-debug-simarm_x64-try
Change-Id: I29c8859c3350ed7b3f1a8f71d82a4393496b7c2b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206820
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
379 lines
14 KiB
C++
379 lines
14 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/assembler/disassembler.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/heap/safepoint.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/snapshot.h"
|
|
#include "vm/virtual_memory.h"
|
|
#include "vm/visitor.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
#include "vm/compiler/aot/precompiler.h"
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(bool, disassemble_stubs, false, "Disassemble generated stubs.");
|
|
DECLARE_FLAG(bool, precompiled_mode);
|
|
|
|
StubCode::StubCodeEntry StubCode::entries_[kNumStubEntries] = {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
#define STUB_CODE_DECLARE(name) {nullptr, #name},
|
|
#else
|
|
#define STUB_CODE_DECLARE(name) \
|
|
{nullptr, #name, compiler::StubCodeCompiler::Generate##name##Stub},
|
|
#endif
|
|
VM_STUB_CODE_LIST(STUB_CODE_DECLARE)
|
|
#undef STUB_CODE_DECLARE
|
|
};
|
|
AcqRelAtomic<bool> StubCode::initialized_ = {false};
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
void StubCode::Init() {
|
|
// Stubs will be loaded from the snapshot.
|
|
UNREACHABLE();
|
|
}
|
|
|
|
#else
|
|
|
|
void StubCode::Init() {
|
|
compiler::ObjectPoolBuilder object_pool_builder;
|
|
|
|
// Generate all the stubs.
|
|
for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
|
|
entries_[i].code = Code::ReadOnlyHandle();
|
|
*(entries_[i].code) =
|
|
Generate(entries_[i].name, &object_pool_builder, entries_[i].generator);
|
|
}
|
|
|
|
const ObjectPool& object_pool =
|
|
ObjectPool::Handle(ObjectPool::NewFromBuilder(object_pool_builder));
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
|
|
entries_[i].code->set_object_pool(object_pool.ptr());
|
|
}
|
|
|
|
InitializationDone();
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
{
|
|
// Set Function owner for UnknownDartCode stub so it pretends to
|
|
// be a Dart code.
|
|
Zone* zone = Thread::Current()->zone();
|
|
const auto& signature = FunctionType::Handle(zone, FunctionType::New());
|
|
auto& owner = Object::Handle(zone);
|
|
owner = Object::void_class();
|
|
ASSERT(!owner.IsNull());
|
|
owner = Function::New(signature, Object::null_string(),
|
|
UntaggedFunction::kRegularFunction,
|
|
/*is_static=*/true,
|
|
/*is_const=*/false,
|
|
/*is_abstract=*/false,
|
|
/*is_external=*/false,
|
|
/*is_native=*/false, owner, TokenPosition::kNoSource);
|
|
StubCode::UnknownDartCode().set_owner(owner);
|
|
StubCode::UnknownDartCode().set_exception_handlers(
|
|
Object::empty_exception_handlers());
|
|
StubCode::UnknownDartCode().set_pc_descriptors(Object::empty_descriptors());
|
|
ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
|
|
}
|
|
#endif // defined(DART_PRECOMPILER)
|
|
}
|
|
|
|
#undef STUB_CODE_GENERATE
|
|
#undef STUB_CODE_SET_OBJECT_POOL
|
|
|
|
CodePtr StubCode::Generate(
|
|
const char* name,
|
|
compiler::ObjectPoolBuilder* object_pool_builder,
|
|
void (*GenerateStub)(compiler::Assembler* assembler)) {
|
|
auto thread = Thread::Current();
|
|
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
|
|
|
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) {
|
|
Disassembler::DisassembleStub(name, code);
|
|
}
|
|
#endif // !PRODUCT
|
|
return code.ptr();
|
|
}
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
void StubCode::Cleanup() {
|
|
initialized_.store(false, std::memory_order_release);
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
|
|
entries_[i].code = nullptr;
|
|
}
|
|
}
|
|
|
|
bool StubCode::InInvocationStub(uword pc) {
|
|
ASSERT(HasBeenInitialized());
|
|
uword entry = StubCode::InvokeDartCode().EntryPoint();
|
|
uword size = StubCode::InvokeDartCodeSize();
|
|
return (pc >= entry) && (pc < (entry + size));
|
|
}
|
|
|
|
bool StubCode::InJumpToFrameStub(uword pc) {
|
|
ASSERT(HasBeenInitialized());
|
|
uword entry = StubCode::JumpToFrame().EntryPoint();
|
|
uword size = StubCode::JumpToFrameSize();
|
|
return (pc >= entry) && (pc < (entry + size));
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ArrayPtr compiler::StubCodeCompiler::BuildStaticCallsTable(
|
|
Zone* zone,
|
|
compiler::UnresolvedPcRelativeCalls* unresolved_calls) {
|
|
if (unresolved_calls->length() == 0) {
|
|
return Array::null();
|
|
}
|
|
const intptr_t array_length =
|
|
unresolved_calls->length() * Code::kSCallTableEntryLength;
|
|
const auto& static_calls_table =
|
|
Array::Handle(zone, Array::New(array_length, Heap::kOld));
|
|
StaticCallsTable entries(static_calls_table);
|
|
auto& kind_type_and_offset = Smi::Handle(zone);
|
|
for (intptr_t i = 0; i < unresolved_calls->length(); i++) {
|
|
auto& unresolved_call = (*unresolved_calls)[i];
|
|
auto call_kind = unresolved_call->is_tail_call() ? Code::kPcRelativeTailCall
|
|
: Code::kPcRelativeCall;
|
|
kind_type_and_offset =
|
|
Smi::New(Code::KindField::encode(call_kind) |
|
|
Code::EntryPointField::encode(Code::kDefaultEntry) |
|
|
Code::OffsetField::encode(unresolved_call->offset()));
|
|
auto view = entries[i];
|
|
view.Set<Code::kSCallTableKindAndOffset>(kind_type_and_offset);
|
|
view.Set<Code::kSCallTableCodeOrTypeTarget>(unresolved_call->target());
|
|
}
|
|
return static_calls_table.ptr();
|
|
}
|
|
|
|
CodePtr StubCode::GetAllocationStubForClass(const Class& cls) {
|
|
Thread* thread = Thread::Current();
|
|
auto object_store = thread->isolate_group()->object_store();
|
|
Zone* zone = thread->zone();
|
|
const Error& error =
|
|
Error::Handle(zone, cls.EnsureIsAllocateFinalized(thread));
|
|
ASSERT(error.IsNull());
|
|
switch (cls.id()) {
|
|
case kArrayCid:
|
|
return object_store->allocate_array_stub();
|
|
case kContextCid:
|
|
return object_store->allocate_context_stub();
|
|
case kUnhandledExceptionCid:
|
|
return object_store->allocate_unhandled_exception_stub();
|
|
case kMintCid:
|
|
return object_store->allocate_mint_stub();
|
|
case kDoubleCid:
|
|
return object_store->allocate_double_stub();
|
|
case kFloat32x4Cid:
|
|
return object_store->allocate_float32x4_stub();
|
|
case kFloat64x2Cid:
|
|
return object_store->allocate_float64x2_stub();
|
|
case kInt32x4Cid:
|
|
return object_store->allocate_int32x4_stub();
|
|
case kClosureCid:
|
|
return object_store->allocate_closure_stub();
|
|
}
|
|
Code& stub = Code::Handle(zone, cls.allocation_stub());
|
|
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;
|
|
|
|
auto zone = thread->zone();
|
|
auto object_store = thread->isolate_group()->object_store();
|
|
auto& allocate_object_stub = Code::ZoneHandle(zone);
|
|
auto& allocate_object_parametrized_stub = Code::ZoneHandle(zone);
|
|
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
|
allocate_object_stub = object_store->allocate_object_stub();
|
|
allocate_object_parametrized_stub =
|
|
object_store->allocate_object_parametrized_stub();
|
|
}
|
|
|
|
compiler::Assembler assembler(wrapper);
|
|
compiler::UnresolvedPcRelativeCalls unresolved_calls;
|
|
const char* name = cls.ToCString();
|
|
compiler::StubCodeCompiler::GenerateAllocationStubForClass(
|
|
&assembler, &unresolved_calls, cls, allocate_object_stub,
|
|
allocate_object_parametrized_stub);
|
|
|
|
const auto& static_calls_table =
|
|
Array::Handle(zone, compiler::StubCodeCompiler::BuildStaticCallsTable(
|
|
zone, &unresolved_calls));
|
|
|
|
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
|
|
|
auto mutator_fun = [&]() {
|
|
stub = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
|
|
/*optimized=*/false,
|
|
/*stats=*/nullptr);
|
|
// Check if some other thread has not already added the stub.
|
|
if (cls.allocation_stub() == Code::null()) {
|
|
stub.set_owner(cls);
|
|
if (!static_calls_table.IsNull()) {
|
|
stub.set_static_calls_target_table(static_calls_table);
|
|
}
|
|
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).
|
|
thread->isolate_group()->RunWithStoppedMutators(mutator_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) {
|
|
Disassembler::DisassembleStub(name, stub);
|
|
}
|
|
#endif // !PRODUCT
|
|
}
|
|
return stub.ptr();
|
|
}
|
|
|
|
CodePtr StubCode::GetAllocationStubForTypedData(classid_t class_id) {
|
|
auto object_store = Thread::Current()->isolate_group()->object_store();
|
|
switch (class_id) {
|
|
case kTypedDataInt8ArrayCid:
|
|
return object_store->allocate_int8_array_stub();
|
|
case kTypedDataUint8ArrayCid:
|
|
return object_store->allocate_uint8_array_stub();
|
|
case kTypedDataUint8ClampedArrayCid:
|
|
return object_store->allocate_uint8_clamped_array_stub();
|
|
case kTypedDataInt16ArrayCid:
|
|
return object_store->allocate_int16_array_stub();
|
|
case kTypedDataUint16ArrayCid:
|
|
return object_store->allocate_uint16_array_stub();
|
|
case kTypedDataInt32ArrayCid:
|
|
return object_store->allocate_int32_array_stub();
|
|
case kTypedDataUint32ArrayCid:
|
|
return object_store->allocate_uint32_array_stub();
|
|
case kTypedDataInt64ArrayCid:
|
|
return object_store->allocate_int64_array_stub();
|
|
case kTypedDataUint64ArrayCid:
|
|
return object_store->allocate_uint64_array_stub();
|
|
case kTypedDataFloat32ArrayCid:
|
|
return object_store->allocate_float32_array_stub();
|
|
case kTypedDataFloat64ArrayCid:
|
|
return object_store->allocate_float64_array_stub();
|
|
case kTypedDataFloat32x4ArrayCid:
|
|
return object_store->allocate_float32x4_array_stub();
|
|
case kTypedDataInt32x4ArrayCid:
|
|
return object_store->allocate_int32x4_array_stub();
|
|
case kTypedDataFloat64x2ArrayCid:
|
|
return object_store->allocate_float64x2_array_stub();
|
|
}
|
|
UNREACHABLE();
|
|
return Code::null();
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#if !defined(TARGET_ARCH_IA32)
|
|
CodePtr StubCode::GetBuildMethodExtractorStub(compiler::ObjectPoolBuilder* pool,
|
|
bool generic) {
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
auto thread = Thread::Current();
|
|
auto Z = thread->zone();
|
|
auto object_store = thread->isolate_group()->object_store();
|
|
|
|
const auto& closure_allocation_stub =
|
|
Code::ZoneHandle(Z, object_store->allocate_closure_stub());
|
|
const auto& context_allocation_stub =
|
|
Code::ZoneHandle(Z, object_store->allocate_context_stub());
|
|
|
|
compiler::ObjectPoolBuilder object_pool_builder;
|
|
compiler::Assembler assembler(pool != nullptr ? pool : &object_pool_builder);
|
|
compiler::StubCodeCompiler::GenerateBuildMethodExtractorStub(
|
|
&assembler, closure_allocation_stub, context_allocation_stub, generic);
|
|
|
|
const char* name = generic ? "BuildGenericMethodExtractor"
|
|
: "BuildNonGenericMethodExtractor";
|
|
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) {
|
|
Disassembler::DisassembleStub(name, stub);
|
|
}
|
|
#endif // !PRODUCT
|
|
return stub.ptr();
|
|
#else // !defined(DART_PRECOMPILED_RUNTIME)
|
|
UNIMPLEMENTED();
|
|
return nullptr;
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
#endif // !defined(TARGET_ARCH_IA32)
|
|
|
|
const Code& StubCode::UnoptimizedStaticCallEntry(intptr_t num_args_tested) {
|
|
switch (num_args_tested) {
|
|
case 0:
|
|
return ZeroArgsUnoptimizedStaticCall();
|
|
case 1:
|
|
return OneArgUnoptimizedStaticCall();
|
|
case 2:
|
|
return TwoArgsUnoptimizedStaticCall();
|
|
default:
|
|
UNIMPLEMENTED();
|
|
return Code::Handle();
|
|
}
|
|
}
|
|
|
|
const char* StubCode::NameOfStub(uword entry_point) {
|
|
for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
|
|
if ((entries_[i].code != nullptr) && !entries_[i].code->IsNull() &&
|
|
(entries_[i].code->EntryPoint() == entry_point)) {
|
|
return entries_[i].name;
|
|
}
|
|
}
|
|
|
|
auto object_store = IsolateGroup::Current()->object_store();
|
|
|
|
#define MATCH(member, name) \
|
|
if (object_store->member() != Code::null() && \
|
|
entry_point == Code::EntryPointOf(object_store->member())) { \
|
|
return "_iso_stub_" #name "Stub"; \
|
|
}
|
|
OBJECT_STORE_STUB_CODE_LIST(MATCH)
|
|
MATCH(build_generic_method_extractor_code, BuildGenericMethodExtractor)
|
|
MATCH(build_nongeneric_method_extractor_code, BuildNonGenericMethodExtractor)
|
|
#undef MATCH
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace dart
|