mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 20:41:45 +00:00
ee0f608ce4
This version is Clang/GCC only and does not support Windows because it uses computed goto's. Only unoptimized mode is supported. Architecture is described in constants_dbc.h and stack_frame_dbc.h. R=fschneider@google.com, zra@google.com Review URL: https://codereview.chromium.org/1858283002 .
350 lines
10 KiB
C++
350 lines
10 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.
|
|
|
|
#ifndef VM_ASSEMBLER_H_
|
|
#define VM_ASSEMBLER_H_
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/allocation.h"
|
|
#include "vm/globals.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/object.h"
|
|
|
|
namespace dart {
|
|
|
|
#if defined(TARGET_ARCH_ARM) || \
|
|
defined(TARGET_ARCH_ARM64) || \
|
|
defined(TARGET_ARCH_MIPS)
|
|
DECLARE_FLAG(bool, use_far_branches);
|
|
#endif
|
|
|
|
// Forward declarations.
|
|
class Assembler;
|
|
class AssemblerFixup;
|
|
class AssemblerBuffer;
|
|
class MemoryRegion;
|
|
|
|
|
|
// External labels keep a function pointer to allow them
|
|
// to be called from code generated by the assembler.
|
|
class ExternalLabel : public ValueObject {
|
|
public:
|
|
explicit ExternalLabel(uword address) : address_(address) {}
|
|
|
|
bool is_resolved() const { return address_ != 0; }
|
|
uword address() const {
|
|
ASSERT(is_resolved());
|
|
return address_;
|
|
}
|
|
|
|
private:
|
|
const uword address_;
|
|
};
|
|
|
|
|
|
// Assembler fixups are positions in generated code that hold relocation
|
|
// information that needs to be processed before finalizing the code
|
|
// into executable memory.
|
|
class AssemblerFixup : public ZoneAllocated {
|
|
public:
|
|
virtual void Process(const MemoryRegion& region, intptr_t position) = 0;
|
|
|
|
virtual bool IsPointerOffset() const = 0;
|
|
|
|
// It would be ideal if the destructor method could be made private,
|
|
// but the g++ compiler complains when this is subclassed.
|
|
virtual ~AssemblerFixup() { UNREACHABLE(); }
|
|
|
|
private:
|
|
AssemblerFixup* previous_;
|
|
intptr_t position_;
|
|
|
|
AssemblerFixup* previous() const { return previous_; }
|
|
void set_previous(AssemblerFixup* previous) { previous_ = previous; }
|
|
|
|
intptr_t position() const { return position_; }
|
|
void set_position(intptr_t position) { position_ = position; }
|
|
|
|
friend class AssemblerBuffer;
|
|
};
|
|
|
|
|
|
// Assembler buffers are used to emit binary code. They grow on demand.
|
|
class AssemblerBuffer : public ValueObject {
|
|
public:
|
|
AssemblerBuffer();
|
|
~AssemblerBuffer();
|
|
|
|
// Basic support for emitting, loading, and storing.
|
|
template<typename T> void Emit(T value) {
|
|
ASSERT(HasEnsuredCapacity());
|
|
*reinterpret_cast<T*>(cursor_) = value;
|
|
cursor_ += sizeof(T);
|
|
}
|
|
|
|
template<typename T> void Remit() {
|
|
ASSERT(Size() >= static_cast<intptr_t>(sizeof(T)));
|
|
cursor_ -= sizeof(T);
|
|
}
|
|
|
|
// Return address to code at |position| bytes.
|
|
uword Address(intptr_t position) {
|
|
return contents_ + position;
|
|
}
|
|
|
|
template<typename T> T Load(intptr_t position) {
|
|
ASSERT(position >= 0 &&
|
|
position <= (Size() - static_cast<intptr_t>(sizeof(T))));
|
|
return *reinterpret_cast<T*>(contents_ + position);
|
|
}
|
|
|
|
template<typename T> void Store(intptr_t position, T value) {
|
|
ASSERT(position >= 0 &&
|
|
position <= (Size() - static_cast<intptr_t>(sizeof(T))));
|
|
*reinterpret_cast<T*>(contents_ + position) = value;
|
|
}
|
|
|
|
const ZoneGrowableArray<intptr_t>& pointer_offsets() const {
|
|
#if defined(DEBUG)
|
|
ASSERT(fixups_processed_);
|
|
#endif
|
|
return *pointer_offsets_;
|
|
}
|
|
|
|
// Emit an object pointer directly in the code.
|
|
void EmitObject(const Object& object);
|
|
|
|
// Emit a fixup at the current location.
|
|
void EmitFixup(AssemblerFixup* fixup) {
|
|
fixup->set_previous(fixup_);
|
|
fixup->set_position(Size());
|
|
fixup_ = fixup;
|
|
}
|
|
|
|
// Count the fixups that produce a pointer offset, without processing
|
|
// the fixups.
|
|
intptr_t CountPointerOffsets() const;
|
|
|
|
// Get the size of the emitted code.
|
|
intptr_t Size() const { return cursor_ - contents_; }
|
|
uword contents() const { return contents_; }
|
|
|
|
// Copy the assembled instructions into the specified memory block
|
|
// and apply all fixups.
|
|
void FinalizeInstructions(const MemoryRegion& region);
|
|
|
|
// To emit an instruction to the assembler buffer, the EnsureCapacity helper
|
|
// must be used to guarantee that the underlying data area is big enough to
|
|
// hold the emitted instruction. Usage:
|
|
//
|
|
// AssemblerBuffer buffer;
|
|
// AssemblerBuffer::EnsureCapacity ensured(&buffer);
|
|
// ... emit bytes for single instruction ...
|
|
|
|
#if defined(DEBUG)
|
|
class EnsureCapacity : public ValueObject {
|
|
public:
|
|
explicit EnsureCapacity(AssemblerBuffer* buffer);
|
|
~EnsureCapacity();
|
|
|
|
private:
|
|
AssemblerBuffer* buffer_;
|
|
intptr_t gap_;
|
|
|
|
intptr_t ComputeGap() { return buffer_->Capacity() - buffer_->Size(); }
|
|
};
|
|
|
|
bool has_ensured_capacity_;
|
|
bool HasEnsuredCapacity() const { return has_ensured_capacity_; }
|
|
#else
|
|
class EnsureCapacity : public ValueObject {
|
|
public:
|
|
explicit EnsureCapacity(AssemblerBuffer* buffer) {
|
|
if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity();
|
|
}
|
|
};
|
|
|
|
// When building the C++ tests, assertion code is enabled. To allow
|
|
// asserting that the user of the assembler buffer has ensured the
|
|
// capacity needed for emitting, we add a dummy method in non-debug mode.
|
|
bool HasEnsuredCapacity() const { return true; }
|
|
#endif
|
|
|
|
// Returns the position in the instruction stream.
|
|
intptr_t GetPosition() const { return cursor_ - contents_; }
|
|
|
|
private:
|
|
// The limit is set to kMinimumGap bytes before the end of the data area.
|
|
// This leaves enough space for the longest possible instruction and allows
|
|
// for a single, fast space check per instruction.
|
|
static const intptr_t kMinimumGap = 32;
|
|
|
|
uword contents_;
|
|
uword cursor_;
|
|
uword limit_;
|
|
AssemblerFixup* fixup_;
|
|
ZoneGrowableArray<intptr_t>* pointer_offsets_;
|
|
#if defined(DEBUG)
|
|
bool fixups_processed_;
|
|
#endif
|
|
|
|
uword cursor() const { return cursor_; }
|
|
uword limit() const { return limit_; }
|
|
intptr_t Capacity() const {
|
|
ASSERT(limit_ >= contents_);
|
|
return (limit_ - contents_) + kMinimumGap;
|
|
}
|
|
|
|
// Process the fixup chain.
|
|
void ProcessFixups(const MemoryRegion& region);
|
|
|
|
// Compute the limit based on the data area and the capacity. See
|
|
// description of kMinimumGap for the reasoning behind the value.
|
|
static uword ComputeLimit(uword data, intptr_t capacity) {
|
|
return data + capacity - kMinimumGap;
|
|
}
|
|
|
|
void ExtendCapacity();
|
|
|
|
friend class AssemblerFixup;
|
|
};
|
|
|
|
|
|
struct ObjectPoolWrapperEntry {
|
|
ObjectPoolWrapperEntry()
|
|
: raw_value_(), type_(), equivalence_() { }
|
|
explicit ObjectPoolWrapperEntry(const Object* obj)
|
|
: obj_(obj), type_(ObjectPool::kTaggedObject), equivalence_(obj) { }
|
|
explicit ObjectPoolWrapperEntry(const Object* obj, const Object* eqv)
|
|
: obj_(obj), type_(ObjectPool::kTaggedObject), equivalence_(eqv) { }
|
|
ObjectPoolWrapperEntry(uword value, ObjectPool::EntryType info)
|
|
: raw_value_(value), type_(info), equivalence_() { }
|
|
|
|
union {
|
|
const Object* obj_;
|
|
uword raw_value_;
|
|
};
|
|
ObjectPool::EntryType type_;
|
|
const Object* equivalence_;
|
|
};
|
|
|
|
|
|
// Pair type parameter for DirectChainedHashMap used for the constant pool.
|
|
class ObjIndexPair {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef ObjectPoolWrapperEntry Key;
|
|
typedef intptr_t Value;
|
|
typedef ObjIndexPair Pair;
|
|
|
|
static const intptr_t kNoIndex = -1;
|
|
|
|
ObjIndexPair() : key_(static_cast<uword>(NULL), ObjectPool::kTaggedObject),
|
|
value_(kNoIndex) { }
|
|
|
|
ObjIndexPair(Key key, Value value) : value_(value) {
|
|
key_.type_ = key.type_;
|
|
if (key.type_ == ObjectPool::kTaggedObject) {
|
|
key_.obj_ = key.obj_;
|
|
key_.equivalence_ = key.equivalence_;
|
|
} else {
|
|
key_.raw_value_ = key.raw_value_;
|
|
}
|
|
}
|
|
|
|
static Key KeyOf(Pair kv) { return kv.key_; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv.value_; }
|
|
|
|
static intptr_t Hashcode(Key key) {
|
|
if (key.type_ != ObjectPool::kTaggedObject) {
|
|
return key.raw_value_;
|
|
}
|
|
if (key.obj_->IsSmi()) {
|
|
return Smi::Cast(*key.obj_).Value();
|
|
}
|
|
// TODO(asiva) For now we assert that the object is from Old space
|
|
// and use the address of the raw object, once the weak_entry_table code
|
|
// in heap allows for multiple thread access we should switch this code
|
|
// to create a temporary raw obj => id mapping and use that.
|
|
ASSERT(key.obj_->IsOld());
|
|
return reinterpret_cast<intptr_t>(key.obj_->raw());
|
|
}
|
|
|
|
static inline bool IsKeyEqual(Pair kv, Key key) {
|
|
if (kv.key_.type_ != key.type_) return false;
|
|
if (kv.key_.type_ == ObjectPool::kTaggedObject) {
|
|
return (kv.key_.obj_->raw() == key.obj_->raw()) &&
|
|
(kv.key_.equivalence_->raw() == key.equivalence_->raw());
|
|
}
|
|
return kv.key_.raw_value_ == key.raw_value_;
|
|
}
|
|
|
|
private:
|
|
Key key_;
|
|
Value value_;
|
|
};
|
|
|
|
|
|
enum Patchability {
|
|
kPatchable,
|
|
kNotPatchable,
|
|
};
|
|
|
|
|
|
class ObjectPoolWrapper : public ValueObject {
|
|
public:
|
|
intptr_t AddObject(const Object& obj,
|
|
Patchability patchable = kNotPatchable);
|
|
intptr_t AddImmediate(uword imm);
|
|
|
|
intptr_t FindObject(const Object& obj,
|
|
Patchability patchable = kNotPatchable);
|
|
intptr_t FindObject(const Object& obj,
|
|
const Object& equivalence);
|
|
intptr_t FindImmediate(uword imm);
|
|
intptr_t FindNativeEntry(const ExternalLabel* label,
|
|
Patchability patchable);
|
|
|
|
RawObjectPool* MakeObjectPool();
|
|
|
|
private:
|
|
intptr_t AddObject(ObjectPoolWrapperEntry entry, Patchability patchable);
|
|
intptr_t FindObject(ObjectPoolWrapperEntry entry, Patchability patchable);
|
|
|
|
// Objects and jump targets.
|
|
GrowableArray<ObjectPoolWrapperEntry> object_pool_;
|
|
|
|
// Hashmap for fast lookup in object pool.
|
|
DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
|
|
};
|
|
|
|
|
|
enum RestorePP {
|
|
kRestoreCallerPP,
|
|
kKeepCalleePP
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
#include "vm/assembler_ia32.h"
|
|
#elif defined(TARGET_ARCH_X64)
|
|
#include "vm/assembler_x64.h"
|
|
#elif defined(TARGET_ARCH_ARM)
|
|
#include "vm/assembler_arm.h"
|
|
#elif defined(TARGET_ARCH_ARM64)
|
|
#include "vm/assembler_arm64.h"
|
|
#elif defined(TARGET_ARCH_MIPS)
|
|
#include "vm/assembler_mips.h"
|
|
#elif defined(TARGET_ARCH_DBC)
|
|
#include "vm/assembler_dbc.h"
|
|
#else
|
|
#error Unknown architecture.
|
|
#endif
|
|
|
|
#endif // VM_ASSEMBLER_H_
|