7ff2dd4117
The call sequence is very similar to a classic IC call, except the guarded class and the target are loaded indirectly from the constant pool instead of as immediates. In the monomorphic case, we call directly to the expected target with a class check in the callee. In the unlinked, polymorphic and megamorphic cases, we call a stub; these case are now call-through instead of call-and-return. Every code, except stubs involved in switchable calls, includes the class check sequence at the beginning. So we now distinguish between a checked and an unchecked entry point. Generated code except the switchable call continues to use the unchecked entry point. PC offsets are calculated relative to the beginning of the instruction stream, rather than either entry point. BUG= R=fschneider@google.com Review URL: https://codereview.chromium.org/2226893002 .
260 lines
7.7 KiB
C++
260 lines
7.7 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/assembler.h"
|
|
#include "vm/disassembler.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/safepoint.h"
|
|
#include "vm/snapshot.h"
|
|
#include "vm/virtual_memory.h"
|
|
#include "vm/visitor.h"
|
|
#include "vm/clustered_snapshot.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(bool, disassemble_stubs, false, "Disassemble generated stubs.");
|
|
|
|
#define STUB_CODE_DECLARE(name) \
|
|
StubEntry* StubCode::name##_entry_ = NULL;
|
|
VM_STUB_CODE_LIST(STUB_CODE_DECLARE);
|
|
#undef STUB_CODE_DECLARE
|
|
|
|
|
|
StubEntry::StubEntry(const Code& code)
|
|
: code_(code.raw()),
|
|
entry_point_(code.UncheckedEntryPoint()),
|
|
checked_entry_point_(code.CheckedEntryPoint()),
|
|
size_(code.Size()),
|
|
label_(code.UncheckedEntryPoint()) {
|
|
}
|
|
|
|
|
|
// Visit all object pointers.
|
|
void StubEntry::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
ASSERT(visitor != NULL);
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&code_));
|
|
}
|
|
|
|
|
|
#define STUB_CODE_GENERATE(name) \
|
|
code ^= Generate("_stub_"#name, StubCode::Generate##name##Stub); \
|
|
name##_entry_ = new StubEntry(code);
|
|
|
|
|
|
void StubCode::InitOnce() {
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
// Generate all the stubs.
|
|
Code& code = Code::Handle();
|
|
VM_STUB_CODE_LIST(STUB_CODE_GENERATE);
|
|
#else
|
|
UNREACHABLE();
|
|
#endif // DART_PRECOMPILED_RUNTIME
|
|
}
|
|
|
|
|
|
#undef STUB_CODE_GENERATE
|
|
|
|
|
|
void StubCode::Push(Serializer* serializer) {
|
|
#define WRITE_STUB(name) \
|
|
serializer->Push(StubCode::name##_entry()->code());
|
|
VM_STUB_CODE_LIST(WRITE_STUB);
|
|
#undef WRITE_STUB
|
|
}
|
|
|
|
|
|
void StubCode::WriteRef(Serializer* serializer) {
|
|
#define WRITE_STUB(name) \
|
|
serializer->WriteRef(StubCode::name##_entry()->code());
|
|
VM_STUB_CODE_LIST(WRITE_STUB);
|
|
#undef WRITE_STUB
|
|
}
|
|
|
|
|
|
void StubCode::ReadRef(Deserializer* deserializer) {
|
|
Code& code = Code::Handle();
|
|
#define READ_STUB(name) \
|
|
code ^= deserializer->ReadRef(); \
|
|
name##_entry_ = new StubEntry(code);
|
|
VM_STUB_CODE_LIST(READ_STUB);
|
|
#undef READ_STUB
|
|
}
|
|
|
|
|
|
|
|
void StubCode::Init(Isolate* isolate) { }
|
|
|
|
|
|
void StubCode::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
}
|
|
|
|
|
|
bool StubCode::HasBeenInitialized() {
|
|
#if !defined(TARGET_ARCH_DBC)
|
|
// Use JumpToExceptionHandler and InvokeDart as canaries.
|
|
const StubEntry* entry_1 = StubCode::JumpToExceptionHandler_entry();
|
|
const StubEntry* entry_2 = StubCode::InvokeDartCode_entry();
|
|
return (entry_1 != NULL) && (entry_2 != NULL);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
bool StubCode::InInvocationStub(uword pc) {
|
|
#if !defined(TARGET_ARCH_DBC)
|
|
ASSERT(HasBeenInitialized());
|
|
uword entry = StubCode::InvokeDartCode_entry()->EntryPoint();
|
|
uword size = StubCode::InvokeDartCodeSize();
|
|
return (pc >= entry) && (pc < (entry + size));
|
|
#else
|
|
// 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::InJumpToExceptionHandlerStub(uword pc) {
|
|
#if !defined(TARGET_ARCH_DBC)
|
|
ASSERT(HasBeenInitialized());
|
|
uword entry = StubCode::JumpToExceptionHandler_entry()->EntryPoint();
|
|
uword size = StubCode::JumpToExceptionHandlerSize();
|
|
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_entry()->code();
|
|
}
|
|
Code& stub = Code::Handle(zone, cls.allocation_stub());
|
|
if (stub.IsNull()) {
|
|
Assembler assembler;
|
|
const char* name = cls.ToCString();
|
|
StubCode::GenerateAllocationStubForClass(&assembler, cls);
|
|
|
|
if (thread->IsMutatorThread()) {
|
|
stub ^= Code::FinalizeCode(name, &assembler, false /* optimized */);
|
|
// 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);
|
|
}
|
|
} else {
|
|
// This part of stub code generation must be at a safepoint.
|
|
// Stop mutator thread before creating the instruction object and
|
|
// installing code.
|
|
// Mutator thread may not run code while we are creating the
|
|
// instruction object, since the creation of instruction object
|
|
// changes code page access permissions (makes them temporary not
|
|
// executable).
|
|
{
|
|
SafepointOperationScope safepoint_scope(thread);
|
|
stub = cls.allocation_stub();
|
|
// Check if stub was already generated.
|
|
if (!stub.IsNull()) {
|
|
return stub.raw();
|
|
}
|
|
// Do not Garbage collect during this stage and instead allow the
|
|
// heap to grow.
|
|
NoHeapGrowthControlScope no_growth_control;
|
|
stub ^= Code::FinalizeCode(name, &assembler, false /* optimized */);
|
|
stub.set_owner(cls);
|
|
cls.set_allocation_stub(stub);
|
|
}
|
|
Isolate* isolate = thread->isolate();
|
|
if (isolate->heap()->NeedsGarbageCollection()) {
|
|
isolate->heap()->CollectAllGarbage();
|
|
}
|
|
}
|
|
#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());
|
|
object_pool.DebugPrint();
|
|
}
|
|
#endif // !PRODUCT
|
|
}
|
|
return stub.raw();
|
|
#endif // !DBC
|
|
UNIMPLEMENTED();
|
|
return Code::null();
|
|
}
|
|
|
|
|
|
const StubEntry* 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_entry();
|
|
case 1:
|
|
return OneArgUnoptimizedStaticCall_entry();
|
|
case 2:
|
|
return TwoArgsUnoptimizedStaticCall_entry();
|
|
default:
|
|
UNIMPLEMENTED();
|
|
return NULL;
|
|
}
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
RawCode* StubCode::Generate(const char* name,
|
|
void (*GenerateStub)(Assembler* assembler)) {
|
|
Assembler assembler;
|
|
GenerateStub(&assembler);
|
|
const Code& code = Code::Handle(
|
|
Code::FinalizeCode(name, &assembler, false /* optimized */));
|
|
#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());
|
|
object_pool.DebugPrint();
|
|
}
|
|
#endif // !PRODUCT
|
|
return code.raw();
|
|
}
|
|
|
|
|
|
const char* StubCode::NameOfStub(uword entry_point) {
|
|
#define VM_STUB_CODE_TESTER(name) \
|
|
if ((name##_entry() != NULL) && \
|
|
(entry_point == name##_entry()->EntryPoint())) { \
|
|
return ""#name; \
|
|
}
|
|
VM_STUB_CODE_LIST(VM_STUB_CODE_TESTER);
|
|
#undef VM_STUB_CODE_TESTER
|
|
return NULL;
|
|
}
|
|
|
|
} // namespace dart
|