| Index: src/assembler.h
|
| diff --git a/src/assembler.h b/src/assembler.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ca35953b5e43c4432129d03ffa2cf95d3214fba9
|
| --- /dev/null
|
| +++ b/src/assembler.h
|
| @@ -0,0 +1,207 @@
|
| +// 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.
|
| +//
|
| +// Modified by the Subzero authors.
|
| +//
|
| +//===- subzero/src/assembler.h - Integrated assembler -----------*- C++ -*-===//
|
| +//
|
| +// The Subzero Code Generator
|
| +//
|
| +// This file is distributed under the University of Illinois Open Source
|
| +// License. See LICENSE.TXT for details.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +//
|
| +// This file declares the Assembler base class. Instructions are assembled
|
| +// by architecture-specific assemblers that derive from this base class.
|
| +// This base class manages buffers and fixups for emitting code, etc.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#ifndef SUBZERO_SRC_ASSEMBLER_H
|
| +#define SUBZERO_SRC_ASSEMBLER_H
|
| +
|
| +#include "IceDefs.h"
|
| +
|
| +#include "llvm/Support/Allocator.h"
|
| +
|
| +namespace Ice {
|
| +
|
| +// Forward declarations.
|
| +class Assembler;
|
| +class AssemblerFixup;
|
| +class AssemblerBuffer;
|
| +class MemoryRegion;
|
| +
|
| +// TODO(jvoung): Do we care if uword was uintptr_t or not?
|
| +
|
| +// 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:
|
| + virtual void Process(const MemoryRegion ®ion, intptr_t position) = 0;
|
| +
|
| + // It would be ideal if the destructor method could be made private,
|
| + // but the g++ compiler complains when this is subclassed.
|
| + virtual ~AssemblerFixup() { llvm_unreachable("~AssemblerFixup used"); }
|
| +
|
| +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:
|
| + AssemblerBuffer(Assembler &);
|
| + ~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> T Load(intptr_t position) const {
|
| + 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;
|
| + }
|
| +
|
| + // Emit a fixup at the current location.
|
| + void EmitFixup(AssemblerFixup *fixup) {
|
| + fixup->set_previous(fixup_);
|
| + fixup->set_position(Size());
|
| + fixup_ = fixup;
|
| + }
|
| +
|
| + // Get the size of the emitted code.
|
| + intptr_t Size() const { return cursor_ - contents_; }
|
| + uintptr_t contents() const { return contents_; }
|
| +
|
| + // Copy the assembled instructions into the specified memory block
|
| + // and apply all fixups.
|
| + void FinalizeInstructions(const MemoryRegion ®ion);
|
| +
|
| +// 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:
|
| + 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:
|
| + 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;
|
| +
|
| + uintptr_t contents_;
|
| + uintptr_t cursor_;
|
| + uintptr_t limit_;
|
| + Assembler &assembler_;
|
| + AssemblerFixup *fixup_;
|
| +#if defined(DEBUG)
|
| + bool fixups_processed_;
|
| +#endif
|
| +
|
| + uintptr_t cursor() const { return cursor_; }
|
| + uintptr_t limit() const { return limit_; }
|
| + intptr_t Capacity() const {
|
| + assert(limit_ >= contents_);
|
| + return (limit_ - contents_) + kMinimumGap;
|
| + }
|
| +
|
| + // Process the fixup chain.
|
| + void ProcessFixups(const MemoryRegion ®ion);
|
| +
|
| + // Compute the limit based on the data area and the capacity. See
|
| + // description of kMinimumGap for the reasoning behind the value.
|
| + static uintptr_t ComputeLimit(uintptr_t data, intptr_t capacity) {
|
| + return data + capacity - kMinimumGap;
|
| + }
|
| +
|
| + void ExtendCapacity();
|
| +
|
| + friend class AssemblerFixup;
|
| +};
|
| +
|
| +class Assembler {
|
| +public:
|
| + Assembler() {}
|
| + ~Assembler() {}
|
| +
|
| + // Allocate a chunk of bytes using the per-Assembler allocator.
|
| + uintptr_t AllocateBytes(size_t bytes) {
|
| + // re: Alignment -- We just need to be able to write bytes to this,
|
| + // so there's not a real requirement (e.g., not related to NaCl bundle
|
| + // alignment). When code is finalized and copied out to the .o file's
|
| + // text section, then we will care about alignment.
|
| + const size_t Alignment = 16;
|
| + return reinterpret_cast<uintptr_t>(Allocator.Allocate(bytes, Alignment));
|
| + }
|
| +
|
| + // Allocate data of type T using the per-Assembler allocator.
|
| + template <typename T> T *Allocate() { return Allocator.Allocate<T>(); }
|
| +
|
| +private:
|
| + llvm::BumpPtrAllocator Allocator;
|
| +
|
| + Assembler(const Assembler &) LLVM_DELETED_FUNCTION;
|
| + Assembler &operator=(const Assembler &) LLVM_DELETED_FUNCTION;
|
| +};
|
| +
|
| +} // end of namespace Ice
|
| +
|
| +#endif // SUBZERO_SRC_ASSEMBLER_H_
|
|
|