aboutsummaryrefslogtreecommitdiff
path: root/disas/libvixl/a64/assembler-a64.h
diff options
context:
space:
mode:
Diffstat (limited to 'disas/libvixl/a64/assembler-a64.h')
-rw-r--r--disas/libvixl/a64/assembler-a64.h385
1 files changed, 249 insertions, 136 deletions
diff --git a/disas/libvixl/a64/assembler-a64.h b/disas/libvixl/a64/assembler-a64.h
index cc0b758..16a704b 100644
--- a/disas/libvixl/a64/assembler-a64.h
+++ b/disas/libvixl/a64/assembler-a64.h
@@ -32,6 +32,7 @@
#include "globals.h"
#include "utils.h"
+#include "code-buffer.h"
#include "a64/instructions-a64.h"
namespace vixl {
@@ -168,6 +169,11 @@ class CPURegister {
return type_ == kFPRegister;
}
+ bool IsW() const { return IsValidRegister() && Is32Bits(); }
+ bool IsX() const { return IsValidRegister() && Is64Bits(); }
+ bool IsS() const { return IsValidFPRegister() && Is32Bits(); }
+ bool IsD() const { return IsValidFPRegister() && Is64Bits(); }
+
const Register& W() const;
const Register& X() const;
const FPRegister& S() const;
@@ -191,12 +197,12 @@ class CPURegister {
class Register : public CPURegister {
public:
- explicit Register() : CPURegister() {}
+ Register() : CPURegister() {}
inline explicit Register(const CPURegister& other)
: CPURegister(other.code(), other.size(), other.type()) {
VIXL_ASSERT(IsValidRegister());
}
- explicit Register(unsigned code, unsigned size)
+ Register(unsigned code, unsigned size)
: CPURegister(code, size, kRegister) {}
bool IsValid() const {
@@ -536,7 +542,7 @@ class Operand {
class MemOperand {
public:
explicit MemOperand(Register base,
- ptrdiff_t offset = 0,
+ int64_t offset = 0,
AddrMode addrmode = Offset);
explicit MemOperand(Register base,
Register regoffset,
@@ -552,7 +558,7 @@ class MemOperand {
const Register& base() const { return base_; }
const Register& regoffset() const { return regoffset_; }
- ptrdiff_t offset() const { return offset_; }
+ int64_t offset() const { return offset_; }
AddrMode addrmode() const { return addrmode_; }
Shift shift() const { return shift_; }
Extend extend() const { return extend_; }
@@ -565,7 +571,7 @@ class MemOperand {
private:
Register base_;
Register regoffset_;
- ptrdiff_t offset_;
+ int64_t offset_;
AddrMode addrmode_;
Shift shift_;
Extend extend_;
@@ -680,32 +686,80 @@ class Label {
};
-// TODO: Obtain better values for these, based on real-world data.
-const int kLiteralPoolCheckInterval = 4 * KBytes;
-const int kRecommendedLiteralPoolRange = 2 * kLiteralPoolCheckInterval;
+// A literal is a 32-bit or 64-bit piece of data stored in the instruction
+// stream and loaded through a pc relative load. The same literal can be
+// referred to by multiple instructions but a literal can only reside at one
+// place in memory. A literal can be used by a load before or after being
+// placed in memory.
+//
+// Internally an offset of 0 is associated with a literal which has been
+// neither used nor placed. Then two possibilities arise:
+// 1) the label is placed, the offset (stored as offset + 1) is used to
+// resolve any subsequent load using the label.
+// 2) the label is not placed and offset is the offset of the last load using
+// the literal (stored as -offset -1). If multiple loads refer to this
+// literal then the last load holds the offset of the preceding load and
+// all loads form a chain. Once the offset is placed all the loads in the
+// chain are resolved and future loads fall back to possibility 1.
+class RawLiteral {
+ public:
+ RawLiteral() : size_(0), offset_(0), raw_value_(0) {}
+ size_t size() {
+ VIXL_STATIC_ASSERT(kDRegSizeInBytes == kXRegSizeInBytes);
+ VIXL_STATIC_ASSERT(kSRegSizeInBytes == kWRegSizeInBytes);
+ VIXL_ASSERT((size_ == kXRegSizeInBytes) || (size_ == kWRegSizeInBytes));
+ return size_;
+ }
+ uint64_t raw_value64() {
+ VIXL_ASSERT(size_ == kXRegSizeInBytes);
+ return raw_value_;
+ }
+ uint32_t raw_value32() {
+ VIXL_ASSERT(size_ == kWRegSizeInBytes);
+ VIXL_ASSERT(is_uint32(raw_value_) || is_int32(raw_value_));
+ return static_cast<uint32_t>(raw_value_);
+ }
+ bool IsUsed() { return offset_ < 0; }
+ bool IsPlaced() { return offset_ > 0; }
-// Control whether a branch over the literal pool should also be emitted. This
-// is needed if the literal pool has to be emitted in the middle of the JITted
-// code.
-enum LiteralPoolEmitOption {
- JumpRequired,
- NoJumpRequired
-};
+ protected:
+ ptrdiff_t offset() {
+ VIXL_ASSERT(IsPlaced());
+ return offset_ - 1;
+ }
+ void set_offset(ptrdiff_t offset) {
+ VIXL_ASSERT(offset >= 0);
+ VIXL_ASSERT(IsWordAligned(offset));
+ VIXL_ASSERT(!IsPlaced());
+ offset_ = offset + 1;
+ }
+ ptrdiff_t last_use() {
+ VIXL_ASSERT(IsUsed());
+ return -offset_ - 1;
+ }
+ void set_last_use(ptrdiff_t offset) {
+ VIXL_ASSERT(offset >= 0);
+ VIXL_ASSERT(IsWordAligned(offset));
+ VIXL_ASSERT(!IsPlaced());
+ offset_ = -offset - 1;
+ }
+ size_t size_;
+ ptrdiff_t offset_;
+ uint64_t raw_value_;
-// Literal pool entry.
-class Literal {
- public:
- Literal(Instruction* pc, uint64_t imm, unsigned size)
- : pc_(pc), value_(imm), size_(size) {}
+ friend class Assembler;
+};
- private:
- Instruction* pc_;
- int64_t value_;
- unsigned size_;
- friend class Assembler;
+template <typename T>
+class Literal : public RawLiteral {
+ public:
+ explicit Literal(T value) {
+ size_ = sizeof(value);
+ memcpy(&raw_value_, &value, sizeof(value));
+ }
};
@@ -750,7 +804,9 @@ enum LoadStoreScalingOption {
// Assembler.
class Assembler {
public:
- Assembler(byte* buffer, unsigned buffer_size,
+ Assembler(size_t capacity,
+ PositionIndependentCodeOption pic = PositionIndependentCode);
+ Assembler(byte* buffer, size_t capacity,
PositionIndependentCodeOption pic = PositionIndependentCode);
// The destructor asserts that one of the following is true:
@@ -763,9 +819,6 @@ class Assembler {
// Start generating code from the beginning of the buffer, discarding any code
// and data that has already been emitted into the buffer.
- //
- // In order to avoid any accidental transfer of state, Reset ASSERTs that the
- // constant pool is not blocked.
void Reset();
// Finalize a code buffer of generated instructions. This function must be
@@ -776,13 +829,47 @@ class Assembler {
// Bind a label to the current PC.
void bind(Label* label);
+ // Bind a label to a specified offset from the start of the buffer.
+ void BindToOffset(Label* label, ptrdiff_t offset);
+
+ // Place a literal at the current PC.
+ void place(RawLiteral* literal);
+
+ ptrdiff_t CursorOffset() const {
+ return buffer_->CursorOffset();
+ }
+
+ ptrdiff_t BufferEndOffset() const {
+ return static_cast<ptrdiff_t>(buffer_->capacity());
+ }
+
+ // Return the address of an offset in the buffer.
+ template <typename T>
+ inline T GetOffsetAddress(ptrdiff_t offset) {
+ VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t));
+ return buffer_->GetOffsetAddress<T>(offset);
+ }
+
// Return the address of a bound label.
template <typename T>
inline T GetLabelAddress(const Label * label) {
VIXL_ASSERT(label->IsBound());
VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t));
- VIXL_STATIC_ASSERT(sizeof(*buffer_) == 1);
- return reinterpret_cast<T>(buffer_ + label->location());
+ return GetOffsetAddress<T>(label->location());
+ }
+
+ // Return the address of the cursor.
+ template <typename T>
+ inline T GetCursorAddress() {
+ VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t));
+ return GetOffsetAddress<T>(CursorOffset());
+ }
+
+ // Return the address of the start of the buffer.
+ template <typename T>
+ inline T GetStartAddress() {
+ VIXL_STATIC_ASSERT(sizeof(T) >= sizeof(uintptr_t));
+ return GetOffsetAddress<T>(0);
}
// Instruction set functions.
@@ -1324,14 +1411,17 @@ class Assembler {
void stnp(const CPURegister& rt, const CPURegister& rt2,
const MemOperand& dst);
- // Load literal to register.
- void ldr(const Register& rt, uint64_t imm);
+ // Load integer or FP register from literal pool.
+ void ldr(const CPURegister& rt, RawLiteral* literal);
+
+ // Load word with sign extension from literal pool.
+ void ldrsw(const Register& rt, RawLiteral* literal);
- // Load double precision floating point literal to FP register.
- void ldr(const FPRegister& ft, double imm);
+ // Load integer or FP register from pc + imm19 << 2.
+ void ldr(const CPURegister& rt, int imm19);
- // Load single precision floating point literal to FP register.
- void ldr(const FPRegister& ft, float imm);
+ // Load word with sign extension from pc + imm19 << 2.
+ void ldrsw(const Register& rt, int imm19);
// Store exclusive byte.
void stxrb(const Register& rs, const Register& rt, const MemOperand& dst);
@@ -1618,25 +1708,26 @@ class Assembler {
inline void dci(Instr raw_inst) { Emit(raw_inst); }
// Emit 32 bits of data into the instruction stream.
- inline void dc32(uint32_t data) { EmitData(&data, sizeof(data)); }
+ inline void dc32(uint32_t data) {
+ VIXL_ASSERT(buffer_monitor_ > 0);
+ buffer_->Emit32(data);
+ }
// Emit 64 bits of data into the instruction stream.
- inline void dc64(uint64_t data) { EmitData(&data, sizeof(data)); }
+ inline void dc64(uint64_t data) {
+ VIXL_ASSERT(buffer_monitor_ > 0);
+ buffer_->Emit64(data);
+ }
// Copy a string into the instruction stream, including the terminating NULL
- // character. The instruction pointer (pc_) is then aligned correctly for
+ // character. The instruction pointer is then aligned correctly for
// subsequent instructions.
- void EmitStringData(const char * string) {
+ void EmitString(const char * string) {
VIXL_ASSERT(string != NULL);
+ VIXL_ASSERT(buffer_monitor_ > 0);
- size_t len = strlen(string) + 1;
- EmitData(string, len);
-
- // Pad with NULL characters until pc_ is aligned.
- const char pad[] = {'\0', '\0', '\0', '\0'};
- VIXL_STATIC_ASSERT(sizeof(pad) == kInstructionSize);
- Instruction* next_pc = AlignUp(pc_, kInstructionSize);
- EmitData(&pad, next_pc - pc_);
+ buffer_->EmitString(string);
+ buffer_->Align();
}
// Code generation helpers.
@@ -1912,43 +2003,39 @@ class Assembler {
return scale << FPScale_offset;
}
- // Size of the code generated in bytes
- size_t SizeOfCodeGenerated() const {
- VIXL_ASSERT((pc_ >= buffer_) && (pc_ < (buffer_ + buffer_size_)));
- return pc_ - buffer_;
- }
-
// Size of the code generated since label to the current position.
size_t SizeOfCodeGeneratedSince(Label* label) const {
- size_t pc_offset = SizeOfCodeGenerated();
-
VIXL_ASSERT(label->IsBound());
- VIXL_ASSERT(pc_offset >= static_cast<size_t>(label->location()));
- VIXL_ASSERT(pc_offset < buffer_size_);
-
- return pc_offset - label->location();
+ return buffer_->OffsetFrom(label->location());
}
+ size_t BufferCapacity() const { return buffer_->capacity(); }
- inline void BlockLiteralPool() {
- literal_pool_monitor_++;
- }
+ size_t RemainingBufferSpace() const { return buffer_->RemainingBytes(); }
- inline void ReleaseLiteralPool() {
- if (--literal_pool_monitor_ == 0) {
- // Has the literal pool been blocked for too long?
- VIXL_ASSERT(literals_.empty() ||
- (pc_ < (literals_.back()->pc_ + kMaxLoadLiteralRange)));
+ void EnsureSpaceFor(size_t amount) {
+ if (buffer_->RemainingBytes() < amount) {
+ size_t capacity = buffer_->capacity();
+ size_t size = buffer_->CursorOffset();
+ do {
+ // TODO(all): refine.
+ capacity *= 2;
+ } while ((capacity - size) < amount);
+ buffer_->Grow(capacity);
}
}
- inline bool IsLiteralPoolBlocked() {
- return literal_pool_monitor_ != 0;
+#ifdef DEBUG
+ void AcquireBuffer() {
+ VIXL_ASSERT(buffer_monitor_ >= 0);
+ buffer_monitor_++;
}
- void CheckLiteralPool(LiteralPoolEmitOption option = JumpRequired);
- void EmitLiteralPool(LiteralPoolEmitOption option = NoJumpRequired);
- size_t LiteralPoolSize();
+ void ReleaseBuffer() {
+ buffer_monitor_--;
+ VIXL_ASSERT(buffer_monitor_ >= 0);
+ }
+#endif
inline PositionIndependentCodeOption pic() {
return pic_;
@@ -1959,22 +2046,30 @@ class Assembler {
(pic() == PositionDependentCode);
}
- protected:
- inline const Register& AppropriateZeroRegFor(const CPURegister& reg) const {
+ static inline const Register& AppropriateZeroRegFor(const CPURegister& reg) {
return reg.Is64Bits() ? xzr : wzr;
}
+ protected:
void LoadStore(const CPURegister& rt,
const MemOperand& addr,
LoadStoreOp op,
LoadStoreScalingOption option = PreferScaledOffset);
- static bool IsImmLSUnscaled(ptrdiff_t offset);
- static bool IsImmLSScaled(ptrdiff_t offset, LSDataSize size);
+ static bool IsImmLSUnscaled(int64_t offset);
+ static bool IsImmLSScaled(int64_t offset, LSDataSize size);
+
+ void LoadStorePair(const CPURegister& rt,
+ const CPURegister& rt2,
+ const MemOperand& addr,
+ LoadStorePairOp op);
+ static bool IsImmLSPair(int64_t offset, LSDataSize size);
+ // TODO(all): The third parameter should be passed by reference but gcc 4.8.2
+ // reports a bogus uninitialised warning then.
void Logical(const Register& rd,
const Register& rn,
- const Operand& operand,
+ const Operand operand,
LogicalOp op);
void LogicalImmediate(const Register& rd,
const Register& rn,
@@ -2035,6 +2130,7 @@ class Assembler {
const CPURegister& rt, const CPURegister& rt2);
static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor(
const CPURegister& rt, const CPURegister& rt2);
+ static LoadLiteralOp LoadLiteralOpFor(const CPURegister& rt);
private:
@@ -2053,10 +2149,6 @@ class Assembler {
const Operand& operand,
FlagsUpdate S,
Instr op);
- void LoadStorePair(const CPURegister& rt,
- const CPURegister& rt2,
- const MemOperand& addr,
- LoadStorePairOp op);
void LoadStorePairNonTemporal(const CPURegister& rt,
const CPURegister& rt2,
const MemOperand& addr,
@@ -2088,8 +2180,6 @@ class Assembler {
const FPRegister& fa,
FPDataProcessing3SourceOp op);
- void RecordLiteral(int64_t imm, unsigned size);
-
// Link the current (not-yet-emitted) instruction to the specified label, then
// return an offset to be encoded in the instruction. If the label is not yet
// bound, an offset of 0 is returned.
@@ -2098,79 +2188,102 @@ class Assembler {
ptrdiff_t LinkAndGetPageOffsetTo(Label * label);
// A common implementation for the LinkAndGet<Type>OffsetTo helpers.
- template <int element_size>
+ template <int element_shift>
ptrdiff_t LinkAndGetOffsetTo(Label* label);
- // Emit the instruction at pc_.
+ // Literal load offset are in words (32-bit).
+ ptrdiff_t LinkAndGetWordOffsetTo(RawLiteral* literal);
+
+ // Emit the instruction in buffer_.
void Emit(Instr instruction) {
- VIXL_STATIC_ASSERT(sizeof(*pc_) == 1);
VIXL_STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
- VIXL_ASSERT((pc_ + sizeof(instruction)) <= (buffer_ + buffer_size_));
-
-#ifdef DEBUG
- finalized_ = false;
-#endif
-
- memcpy(pc_, &instruction, sizeof(instruction));
- pc_ += sizeof(instruction);
- CheckBufferSpace();
+ VIXL_ASSERT(buffer_monitor_ > 0);
+ buffer_->Emit32(instruction);
}
- // Emit data inline in the instruction stream.
- void EmitData(void const * data, unsigned size) {
- VIXL_STATIC_ASSERT(sizeof(*pc_) == 1);
- VIXL_ASSERT((pc_ + size) <= (buffer_ + buffer_size_));
+ // Buffer where the code is emitted.
+ CodeBuffer* buffer_;
+ PositionIndependentCodeOption pic_;
#ifdef DEBUG
- finalized_ = false;
+ int64_t buffer_monitor_;
#endif
+};
- // TODO: Record this 'instruction' as data, so that it can be disassembled
- // correctly.
- memcpy(pc_, data, size);
- pc_ += size;
- CheckBufferSpace();
- }
-
- inline void CheckBufferSpace() {
- VIXL_ASSERT(pc_ < (buffer_ + buffer_size_));
- if (pc_ > next_literal_pool_check_) {
- CheckLiteralPool();
- }
- }
-
- // The buffer into which code and relocation info are generated.
- Instruction* buffer_;
- // Buffer size, in bytes.
- size_t buffer_size_;
- Instruction* pc_;
- std::list<Literal*> literals_;
- Instruction* next_literal_pool_check_;
- unsigned literal_pool_monitor_;
- PositionIndependentCodeOption pic_;
+// All Assembler emits MUST acquire/release the underlying code buffer. The
+// helper scope below will do so and optionally ensure the buffer is big enough
+// to receive the emit. It is possible to request the scope not to perform any
+// checks (kNoCheck) if for example it is known in advance the buffer size is
+// adequate or there is some other size checking mechanism in place.
+class CodeBufferCheckScope {
+ public:
+ // Tell whether or not the scope needs to ensure the associated CodeBuffer
+ // has enough space for the requested size.
+ enum CheckPolicy {
+ kNoCheck,
+ kCheck
+ };
- friend class Label;
- friend class BlockLiteralPoolScope;
+ // Tell whether or not the scope should assert the amount of code emitted
+ // within the scope is consistent with the requested amount.
+ enum AssertPolicy {
+ kNoAssert, // No assert required.
+ kExactSize, // The code emitted must be exactly size bytes.
+ kMaximumSize // The code emitted must be at most size bytes.
+ };
+ CodeBufferCheckScope(Assembler* assm,
+ size_t size,
+ CheckPolicy check_policy = kCheck,
+ AssertPolicy assert_policy = kMaximumSize)
+ : assm_(assm) {
+ if (check_policy == kCheck) assm->EnsureSpaceFor(size);
#ifdef DEBUG
- bool finalized_;
+ assm->bind(&start_);
+ size_ = size;
+ assert_policy_ = assert_policy;
+ assm->AcquireBuffer();
+#else
+ USE(assert_policy);
#endif
-};
+ }
-class BlockLiteralPoolScope {
- public:
- explicit BlockLiteralPoolScope(Assembler* assm) : assm_(assm) {
- assm_->BlockLiteralPool();
+ // This is a shortcut for CodeBufferCheckScope(assm, 0, kNoCheck, kNoAssert).
+ explicit CodeBufferCheckScope(Assembler* assm) : assm_(assm) {
+#ifdef DEBUG
+ size_ = 0;
+ assert_policy_ = kNoAssert;
+ assm->AcquireBuffer();
+#endif
}
- ~BlockLiteralPoolScope() {
- assm_->ReleaseLiteralPool();
+ ~CodeBufferCheckScope() {
+#ifdef DEBUG
+ assm_->ReleaseBuffer();
+ switch (assert_policy_) {
+ case kNoAssert: break;
+ case kExactSize:
+ VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) == size_);
+ break;
+ case kMaximumSize:
+ VIXL_ASSERT(assm_->SizeOfCodeGeneratedSince(&start_) <= size_);
+ break;
+ default:
+ VIXL_UNREACHABLE();
+ }
+#endif
}
- private:
+ protected:
Assembler* assm_;
+#ifdef DEBUG
+ Label start_;
+ size_t size_;
+ AssertPolicy assert_policy_;
+#endif
};
+
} // namespace vixl
#endif // VIXL_A64_ASSEMBLER_A64_H_