| // 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) { |
| #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); |
| |
| 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) { |
| 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_method_extractor_code, BuildMethodExtractor) |
| #undef MATCH |
| return nullptr; |
| } |
| |
| } // namespace dart |