Files
sdk/runtime/vm/compiler/runtime_api.h
T
Ben Konyi fea5413d5a [ VM ] Fix hang of Flutter engine tests caused by global RuntimeEntry objects being destroyed when exit() is called when the VM hasn't finished shutting down.
This is due to how Windows destroys objects as destroyed objects have
their vtables reset to the vtable of the base class. If this happens
when an isolate hasn't finished shutting down and a virtual function
which is overriding a pure virtual function from the base class is
called, _purecall() is invoked and attempts to call exit() again which
leads to a deadlock.

See issue #35855 for more details.

Change-Id: I93a0e00549e7ee87a717a6cde01bf0848050384d
Reviewed-on: https://dart-review.googlesource.com/c/92000
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
2019-02-06 22:37:24 +00:00

388 lines
13 KiB
C++

// Copyright (c) 2019, 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.
#ifndef RUNTIME_VM_COMPILER_RUNTIME_API_H_
#define RUNTIME_VM_COMPILER_RUNTIME_API_H_
// This header defines the API that compiler can use to interact with the
// underlying Dart runtime that it is embedded into.
//
// Compiler is not allowed to directly interact with any objects - it can only
// use classes like dart::Object, dart::Code, dart::Function and similar as
// opaque handles. All interactions should be done through helper methods
// provided by this header.
//
// This header also provides ways to get word sizes, frame layout, field
// offsets for the target runtime. Note that these can be different from
// those on the host. Helpers providing access to these values live
// in compiler::target namespace.
#include "platform/globals.h"
#include "vm/allocation.h"
#include "vm/bitfield.h"
#include "vm/code_entry_kind.h"
#include "vm/frame_layout.h"
#include "vm/pointer_tagging.h"
namespace dart {
// Forward declarations.
class Class;
class Code;
class Function;
class LocalVariable;
class Object;
class RuntimeEntry;
class String;
class Zone;
namespace compiler {
class Assembler;
}
namespace compiler {
// Host word sizes.
//
// Code in the compiler namespace should not use kWordSize and derived
// constants directly because the word size on host and target might
// be different.
//
// To prevent this we introduce variables that would shadow these
// constants and introduce compilation errors when used.
//
// target::kWordSize and target::ObjectAlignment give access to
// word size and object aligment offsets for the target.
//
// Similarly kHostWordSize gives access to the host word size.
class InvalidClass {};
extern InvalidClass kWordSize;
extern InvalidClass kWordSizeLog2;
extern InvalidClass kNewObjectAlignmentOffset;
extern InvalidClass kOldObjectAlignmentOffset;
extern InvalidClass kNewObjectBitPosition;
extern InvalidClass kObjectAlignment;
extern InvalidClass kObjectAlignmentLog2;
extern InvalidClass kObjectAlignmentMask;
static constexpr intptr_t kHostWordSize = dart::kWordSize;
static constexpr intptr_t kHostWordSizeLog2 = dart::kWordSizeLog2;
//
// Object handles.
//
// Create an empty handle.
Object& NewZoneHandle(Zone* zone);
// Clone the given handle.
Object& NewZoneHandle(Zone* zone, const Object&);
// Returns true if [a] and [b] are the same object.
bool IsSameObject(const Object& a, const Object& b);
// Returns true if the given handle is a zone handle or one of the global
// cached handles.
bool IsNotTemporaryScopedHandle(const Object& obj);
// Returns true if [obj] resides in old space.
bool IsInOldSpace(const Object& obj);
// Returns true if [obj] is not a Field/ICData clone.
//
// Used to assert that we are not embedding pointers to cloned objects that are
// used by background compiler into object pools / code.
bool IsOriginalObject(const Object& object);
// Clear the given handle.
void SetToNull(Object* obj);
// Helper functions to upcast handles.
//
// Note: compiler code cannot include object.h so it cannot see that Object is
// a superclass of Code or Function - thus we have to cast these pointers using
// reinterpret_cast.
inline const Object& ToObject(const Code& handle) {
return *reinterpret_cast<const Object*>(&handle);
}
inline const Object& ToObject(const Function& handle) {
return *reinterpret_cast<const Object*>(&handle);
}
// Returns some hash value for the given object.
//
// Note: the given hash value does not necessarily match Object.get:hashCode,
// or canonical hash.
intptr_t ObjectHash(const Object& obj);
// If the given object represents a Dart integer returns true and sets [value]
// to the value of the integer.
bool HasIntegerValue(const dart::Object& obj, int64_t* value);
// Creates a random cookie to be used for masking constants embedded in the
// generated code.
int32_t CreateJitCookie();
typedef void (*RuntimeEntryCallInternal)(const dart::RuntimeEntry*,
compiler::Assembler*,
intptr_t);
class RuntimeEntry : public ValueObject {
public:
virtual ~RuntimeEntry() {}
void Call(compiler::Assembler* assembler, intptr_t argument_count) const {
ASSERT(call_ != NULL);
ASSERT(runtime_entry_ != NULL);
// We call a manually set function pointer which points to the
// implementation of call for the subclass. We do this instead of just
// defining Call in this class as a pure virtual method and providing an
// implementation in the subclass as RuntimeEntry objects are declared as
// globals which causes problems on Windows.
//
// When exit() is called on Windows, global objects start to be destroyed.
// As part of an object's destruction, the vtable is reset to that of the
// base class. Since some threads may still be running and accessing these
// now destroyed globals, an invocation to dart::RuntimeEntry::Call would
// instead invoke dart::compiler::RuntimeEntry::Call. If
// dart::compiler::RuntimeEntry::Call were a pure virtual method, _purecall
// would be invoked to handle the invalid call and attempt to call exit(),
// causing the process to hang on a lock.
//
// By removing the need to rely on a potentially invalid vtable at exit,
// we should be able to avoid hanging or crashing the process at shutdown,
// even as global objects start to be destroyed. See issue #35855.
call_(runtime_entry_, assembler, argument_count);
}
protected:
RuntimeEntry(const dart::RuntimeEntry* runtime_entry,
RuntimeEntryCallInternal call)
: runtime_entry_(runtime_entry), call_(call) {}
private:
const dart::RuntimeEntry* runtime_entry_;
RuntimeEntryCallInternal call_;
};
// Allocate a string object with the given content in the runtime heap.
const String& AllocateString(const char* buffer);
DART_NORETURN void BailoutWithBranchOffsetError();
// compiler::target namespace contains information about the target platform:
//
// - word sizes and derived constants
// - offsets of fields
// - sizes of structures
namespace target {
// Currently we define target::word to match dart::word which represents
// host word.
//
// Once refactoring of the compiler is complete we will switch target::word
// to be independent from host word.
typedef dart::word word;
typedef dart::uword uword;
static constexpr word kWordSize = dart::kWordSize;
static constexpr word kWordSizeLog2 = dart::kWordSizeLog2;
static_assert((1 << kWordSizeLog2) == kWordSize,
"kWordSizeLog2 should match kWordSize");
using ObjectAlignment = dart::ObjectAlignment<kWordSize, kWordSizeLog2>;
// Information about frame_layout that compiler should be targeting.
extern FrameLayout frame_layout;
// Returns the FP-relative index where [variable] can be found (assumes
// [variable] is not captured), in bytes.
inline int FrameOffsetInBytesForVariable(const LocalVariable* variable) {
return frame_layout.FrameSlotForVariable(variable) * kWordSize;
}
// Encode tag word for a heap allocated object with the given class id and
// size.
//
// Note: even on 64-bit platforms we only use lower 32-bits of the tag word.
uint32_t MakeTagWordForNewSpaceObject(classid_t cid, uword instance_size);
//
// Target specific information about objects.
//
// Returns true if the given object can be represented as a Smi on the
// target platform.
bool IsSmi(const dart::Object& a);
// Return raw Smi representation of the given object for the target platform.
word ToRawSmi(const dart::Object& a);
// Return raw Smi representation of the given integer value for the target
// platform.
//
// Note: method assumes that caller has validated that value is representable
// as a Smi.
word ToRawSmi(intptr_t value);
// If the given object can be loaded from the thread on the target then
// return true and set offset (if provided) to the offset from the
// thread pointer to a field that contains the object.
bool CanLoadFromThread(const dart::Object& object, word* offset = nullptr);
// On IA32 we can embed raw pointers into generated code.
#if defined(TARGET_ARCH_IA32)
// Returns true if the pointer to the given object can be directly embedded
// into the generated code (because the object is immortal and immovable).
bool CanEmbedAsRawPointerInGeneratedCode(const dart::Object& obj);
// Returns raw pointer value for the given object. Should only be invoked
// if CanEmbedAsRawPointerInGeneratedCode returns true.
word ToRawPointer(const dart::Object& a);
#endif // defined(TARGET_ARCH_IA32)
//
// Target specific offsets and constants.
//
// Currently we use the same names for classes, constants and getters to make
// migration easier.
class RawObject : public AllStatic {
public:
static const word kClassIdTagPos;
static const word kClassIdTagSize;
static const word kBarrierOverlapShift;
};
class Object : public AllStatic {
public:
// Offset of the tags word.
static word tags_offset();
};
class ObjectPool : public AllStatic {
public:
// Return offset to the element with the given [index] in the object pool.
static intptr_t element_offset(intptr_t index);
};
class Class : public AllStatic {
public:
// Return class id of the given class on the target.
static classid_t GetId(const dart::Class& handle);
// Return instance size for the given class on the target.
static uword GetInstanceSize(const dart::Class& handle);
};
class Instance : public AllStatic {
public:
static word DataOffsetFor(intptr_t cid);
};
class Double : public AllStatic {
public:
static word value_offset();
};
class Float32x4 : public AllStatic {
public:
static word value_offset();
};
class Float64x2 : public AllStatic {
public:
static word value_offset();
};
class Thread : public AllStatic {
public:
static word top_offset();
static word end_offset();
static word isolate_offset();
static word call_to_runtime_entry_point_offset();
static word null_error_shared_with_fpu_regs_entry_point_offset();
static word null_error_shared_without_fpu_regs_entry_point_offset();
static word write_barrier_mask_offset();
static word monomorphic_miss_entry_offset();
static word write_barrier_wrappers_thread_offset(intptr_t regno);
static word array_write_barrier_entry_point_offset();
static word write_barrier_entry_point_offset();
static word vm_tag_offset();
#define THREAD_XMM_CONSTANT_LIST(V) \
V(float_not) \
V(float_negate) \
V(float_absolute) \
V(float_zerow) \
V(double_negate) \
V(double_abs)
#define DECLARE_CONSTANT_OFFSET_GETTER(name) \
static word name##_address_offset();
THREAD_XMM_CONSTANT_LIST(DECLARE_CONSTANT_OFFSET_GETTER)
#undef DECLARE_CONSTANT_OFFSET_GETTER
};
class Isolate : public AllStatic {
public:
static word class_table_offset();
};
class ClassTable : public AllStatic {
public:
static word table_offset();
#if !defined(PRODUCT)
static word ClassOffsetFor(intptr_t cid);
static word StateOffsetFor(intptr_t cid);
static word TableOffsetFor(intptr_t cid);
static word CounterOffsetFor(intptr_t cid, bool is_new);
static word SizeOffsetFor(intptr_t cid, bool is_new);
#endif // !defined(PRODUCT)
static const word kSizeOfClassPairLog2;
};
#if !defined(PRODUCT)
class ClassHeapStats : public AllStatic {
public:
static word TraceAllocationMask();
static word state_offset();
static word allocated_since_gc_new_space_offset();
static word allocated_size_since_gc_new_space_offset();
};
#endif // !defined(PRODUCT)
class Instructions : public AllStatic {
public:
static const intptr_t kPolymorphicEntryOffset;
static const intptr_t kMonomorphicEntryOffset;
static intptr_t HeaderSize();
};
class Code : public AllStatic {
public:
#if defined(TARGET_ARCH_IA32)
static uword EntryPointOf(const dart::Code& code);
#endif // defined(TARGET_ARCH_IA32)
static intptr_t object_pool_offset();
static intptr_t entry_point_offset(
CodeEntryKind kind = CodeEntryKind::kNormal);
static intptr_t saved_instructions_offset();
};
class Heap : public AllStatic {
public:
// Return true if an object with the given instance size is allocatable
// in new space on the target.
static bool IsAllocatableInNewSpace(intptr_t instance_size);
};
} // namespace target
} // namespace compiler
} // namespace dart
#endif // RUNTIME_VM_COMPILER_RUNTIME_API_H_