// 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 void Emit(T value) { ASSERT(HasEnsuredCapacity()); *reinterpret_cast(cursor_) = value; cursor_ += sizeof(T); } template void Remit() { ASSERT(Size() >= static_cast(sizeof(T))); cursor_ -= sizeof(T); } template T Load(intptr_t position) { ASSERT(position >= 0 && position <= (Size() - static_cast(sizeof(T)))); return *reinterpret_cast(contents_ + position); } template void Store(intptr_t position, T value) { ASSERT(position >= 0 && position <= (Size() - static_cast(sizeof(T)))); *reinterpret_cast(contents_ + position) = value; } const ZoneGrowableArray& 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* 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; }; // Pair type parameter for DirectChainedHashMap used for the constant pool. class ObjIndexPair { public: // Typedefs needed for the DirectChainedHashMap template. typedef const Object* Key; typedef intptr_t Value; typedef ObjIndexPair Pair; static const intptr_t kNoIndex = -1; ObjIndexPair() : key_(NULL), value_(kNoIndex) { } ObjIndexPair(Key key, Value value) : key_(key->IsNotTemporaryScopedHandle() ? key : &Object::ZoneHandle(key->raw())), value_(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->IsSmi()) { return Smi::Cast(*key).Value(); } if (key->IsDouble()) { return static_cast( bit_cast( static_cast(Double::Cast(*key).value()))); } if (key->IsMint()) { return static_cast(Mint::Cast(*key).value()); } if (key->IsString()) { return String::Cast(*key).Hash(); } // TODO(fschneider): Add hash function for other classes commonly used as // compile-time constants. return key->GetClassId(); } static inline bool IsKeyEqual(Pair kv, Key key) { return kv.key_->raw() == key->raw(); } private: Key key_; Value value_; }; enum Patchability { kPatchable, kNotPatchable, }; class ObjectPool : public ValueObject { public: ObjectPool() : object_pool_(GrowableObjectArray::Handle()) { } intptr_t AddObject(const Object& obj, Patchability patchable); intptr_t AddExternalLabel(const ExternalLabel* label, Patchability patchable); intptr_t FindObject(const Object& obj, Patchability patchable); intptr_t FindExternalLabel(const ExternalLabel* label, Patchability patchable); const GrowableObjectArray& data() const { return object_pool_; } private: // Objects and jump targets. GrowableObjectArray& object_pool_; // Patchability of pool entries. GrowableArray patchable_pool_entries_; // Hashmap for fast lookup in object pool. DirectChainedHashMap object_pool_index_table_; }; } // 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" #else #error Unknown architecture. #endif #endif // VM_ASSEMBLER_H_