| Index: runtime/vm/assembler_arm64.h
|
| ===================================================================
|
| --- runtime/vm/assembler_arm64.h (revision 0)
|
| +++ runtime/vm/assembler_arm64.h (revision 0)
|
| @@ -0,0 +1,357 @@
|
| +// Copyright (c) 2014, 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_ARM64_H_
|
| +#define VM_ASSEMBLER_ARM64_H_
|
| +
|
| +#ifndef VM_ASSEMBLER_H_
|
| +#error Do not include assembler_arm64.h directly; use assembler.h instead.
|
| +#endif
|
| +
|
| +#include "platform/assert.h"
|
| +#include "platform/utils.h"
|
| +#include "vm/constants_arm64.h"
|
| +#include "vm/object.h"
|
| +#include "vm/simulator.h"
|
| +
|
| +namespace dart {
|
| +
|
| +// Forward declarations.
|
| +class RuntimeEntry;
|
| +
|
| +// TODO(zra): Label, Address, and FieldAddress are copied from ARM,
|
| +// they must be adapted to ARM64.
|
| +class Label : public ValueObject {
|
| + public:
|
| + Label() : position_(0) { }
|
| +
|
| + ~Label() {
|
| + // Assert if label is being destroyed with unresolved branches pending.
|
| + ASSERT(!IsLinked());
|
| + }
|
| +
|
| + // Returns the position for bound and linked labels. Cannot be used
|
| + // for unused labels.
|
| + intptr_t Position() const {
|
| + ASSERT(!IsUnused());
|
| + return IsBound() ? -position_ - kWordSize : position_ - kWordSize;
|
| + }
|
| +
|
| + bool IsBound() const { return position_ < 0; }
|
| + bool IsUnused() const { return position_ == 0; }
|
| + bool IsLinked() const { return position_ > 0; }
|
| +
|
| + private:
|
| + intptr_t position_;
|
| +
|
| + void Reinitialize() {
|
| + position_ = 0;
|
| + }
|
| +
|
| + void BindTo(intptr_t position) {
|
| + ASSERT(!IsBound());
|
| + position_ = -position - kWordSize;
|
| + ASSERT(IsBound());
|
| + }
|
| +
|
| + void LinkTo(intptr_t position) {
|
| + ASSERT(!IsBound());
|
| + position_ = position + kWordSize;
|
| + ASSERT(IsLinked());
|
| + }
|
| +
|
| + friend class Assembler;
|
| + DISALLOW_COPY_AND_ASSIGN(Label);
|
| +};
|
| +
|
| +
|
| +class Address : public ValueObject {
|
| + public:
|
| + Address(const Address& other)
|
| + : ValueObject(), encoding_(other.encoding_) {
|
| + }
|
| +
|
| + Address& operator=(const Address& other) {
|
| + encoding_ = other.encoding_;
|
| + return *this;
|
| + }
|
| +
|
| + Address(Register rn, int32_t offset = 0) {
|
| + ASSERT(Utils::IsAbsoluteUint(12, offset));
|
| + encoding_ = -1;
|
| + }
|
| +
|
| + private:
|
| + uint32_t encoding() const { return encoding_; }
|
| +
|
| + uint32_t encoding_;
|
| +
|
| + friend class Assembler;
|
| +};
|
| +
|
| +
|
| +class FieldAddress : public Address {
|
| + public:
|
| + FieldAddress(Register base, int32_t disp)
|
| + : Address(base, disp - kHeapObjectTag) { }
|
| +
|
| + FieldAddress(const FieldAddress& other) : Address(other) { }
|
| +
|
| + FieldAddress& operator=(const FieldAddress& other) {
|
| + Address::operator=(other);
|
| + return *this;
|
| + }
|
| +};
|
| +
|
| +
|
| +class Operand : public ValueObject {
|
| + public:
|
| + // Data-processing operand - Uninitialized.
|
| + Operand() : encoding_(-1), type_(Unknown) { }
|
| +
|
| + // Data-processing operands - Copy constructor.
|
| + Operand(const Operand& other)
|
| + : ValueObject(), encoding_(other.encoding_), type_(other.type_) { }
|
| +
|
| + explicit Operand(Register rm) {
|
| + ASSERT((rm != R31) && (rm != SP));
|
| + const Register crm = ConcreteRegister(rm);
|
| + encoding_ = (static_cast<int32_t>(crm) << kRmShift);
|
| + }
|
| +
|
| + Operand(Register rm, Shift shift, int32_t imm) {
|
| + ASSERT(Utils::IsUint(6, imm));
|
| + ASSERT((rm != R31) && (rm != SP));
|
| + const Register crm = ConcreteRegister(rm);
|
| + encoding_ =
|
| + (imm << kImm6Shift) |
|
| + (static_cast<int32_t>(crm) << kRmShift) |
|
| + (static_cast<int32_t>(shift) << kShiftTypeShift);
|
| + type_ = Shifted;
|
| + }
|
| +
|
| + Operand(Register rm, Extend extend, int32_t imm) {
|
| + ASSERT(Utils::IsUint(3, imm));
|
| + ASSERT((rm != R31) && (rm != SP));
|
| + const Register crm = ConcreteRegister(rm);
|
| + encoding_ =
|
| + B21 |
|
| + (static_cast<int32_t>(crm) << kRmShift) |
|
| + (static_cast<int32_t>(extend) << kExtendTypeShift) |
|
| + ((imm & 0x7) << kImm3Shift);
|
| + type_ = Extended;
|
| + }
|
| +
|
| + explicit Operand(int32_t imm) {
|
| + if (Utils::IsUint(12, imm)) {
|
| + encoding_ = imm << kImm12Shift;
|
| + } else {
|
| + // imm only has bits in [12, 24) set.
|
| + ASSERT(((imm & 0xfff) == 0) && (Utils::IsUint(12, imm >> 12)));
|
| + encoding_ = B22 | ((imm >> 12) << kImm12Shift);
|
| + }
|
| + type_ = Immediate;
|
| + }
|
| +
|
| + // TODO(zra): Add bitfield immediate operand
|
| + // Operand(int32_t n, int32_t imms, int32_t immr);
|
| +
|
| + enum OperandType {
|
| + Shifted,
|
| + Extended,
|
| + Immediate,
|
| + BitfieldImm,
|
| + Unknown,
|
| + };
|
| +
|
| + private:
|
| + uint32_t encoding() const {
|
| + return encoding_;
|
| + }
|
| + OperandType type() const {
|
| + return type_;
|
| + }
|
| +
|
| + uint32_t encoding_;
|
| + OperandType type_;
|
| +
|
| + friend class Assembler;
|
| +};
|
| +
|
| +
|
| +class Assembler : public ValueObject {
|
| + public:
|
| + explicit Assembler(bool use_far_branches = false)
|
| + : buffer_(),
|
| + object_pool_(GrowableObjectArray::Handle()),
|
| + prologue_offset_(-1),
|
| + use_far_branches_(use_far_branches),
|
| + comments_() { }
|
| + ~Assembler() { }
|
| +
|
| + void PopRegister(Register r) {
|
| + UNIMPLEMENTED();
|
| + }
|
| +
|
| + void Drop(intptr_t stack_elements) {
|
| + UNIMPLEMENTED();
|
| + }
|
| +
|
| + void Bind(Label* label) {
|
| + UNIMPLEMENTED();
|
| + }
|
| +
|
| + // Misc. functionality
|
| + intptr_t CodeSize() const { return buffer_.Size(); }
|
| + intptr_t prologue_offset() const { return prologue_offset_; }
|
| +
|
| + // Count the fixups that produce a pointer offset, without processing
|
| + // the fixups. On ARM64 there are no pointers in code.
|
| + intptr_t CountPointerOffsets() const { return 0; }
|
| +
|
| + const ZoneGrowableArray<intptr_t>& GetPointerOffsets() const {
|
| + ASSERT(buffer_.pointer_offsets().length() == 0); // No pointers in code.
|
| + return buffer_.pointer_offsets();
|
| + }
|
| + const GrowableObjectArray& object_pool() const { return object_pool_; }
|
| +
|
| + bool use_far_branches() const {
|
| + return FLAG_use_far_branches || use_far_branches_;
|
| + }
|
| +
|
| + void set_use_far_branches(bool b) {
|
| + ASSERT(buffer_.Size() == 0);
|
| + use_far_branches_ = b;
|
| + }
|
| +
|
| + void FinalizeInstructions(const MemoryRegion& region) {
|
| + buffer_.FinalizeInstructions(region);
|
| + }
|
| +
|
| + // Debugging and bringup support.
|
| + void Stop(const char* message);
|
| + void Unimplemented(const char* message);
|
| + void Untested(const char* message);
|
| + void Unreachable(const char* message);
|
| +
|
| + static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
| +
|
| + void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
| +
|
| + const Code::Comments& GetCodeComments() const;
|
| +
|
| + static const char* RegisterName(Register reg);
|
| +
|
| + static const char* FpuRegisterName(FpuRegister reg);
|
| +
|
| + // TODO(zra): Make sure this is right.
|
| + // Instruction pattern from entrypoint is used in Dart frame prologs
|
| + // to set up the frame and save a PC which can be used to figure out the
|
| + // RawInstruction object corresponding to the code running in the frame.
|
| + static const intptr_t kEntryPointToPcMarkerOffset = 0;
|
| +
|
| + // Emit data (e.g encoded instruction or immediate) in instruction stream.
|
| + void Emit(int32_t value);
|
| +
|
| + // On some other platforms, we draw a distinction between safe and unsafe
|
| + // smis.
|
| + static bool IsSafe(const Object& object) { return true; }
|
| + static bool IsSafeSmi(const Object& object) { return object.IsSmi(); }
|
| +
|
| + void add(Register rd, Register rn, Operand o) {
|
| + AddSubHelper(kDoubleWord, false, false, rd, rn, o);
|
| + }
|
| + void addw(Register rd, Register rn, Operand o) {
|
| + AddSubHelper(kWord, false, false, rd, rn, o);
|
| + }
|
| +
|
| + // Function return.
|
| + void ret(Register rn = R30) {
|
| + EmitUnconditionalBranchRegOp(RET, rn);
|
| + }
|
| +
|
| + private:
|
| + AssemblerBuffer buffer_; // Contains position independent code.
|
| + GrowableObjectArray& object_pool_; // Objects and patchable jump targets.
|
| + int32_t prologue_offset_;
|
| +
|
| + bool use_far_branches_;
|
| +
|
| + class CodeComment : public ZoneAllocated {
|
| + public:
|
| + CodeComment(intptr_t pc_offset, const String& comment)
|
| + : pc_offset_(pc_offset), comment_(comment) { }
|
| +
|
| + intptr_t pc_offset() const { return pc_offset_; }
|
| + const String& comment() const { return comment_; }
|
| +
|
| + private:
|
| + intptr_t pc_offset_;
|
| + const String& comment_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(CodeComment);
|
| + };
|
| +
|
| + GrowableArray<CodeComment*> comments_;
|
| +
|
| + void AddSubHelper(OperandSize os, bool set_flags, bool subtract,
|
| + Register rd, Register rn, Operand o) {
|
| + ASSERT((rd != R31) && (rn != R31));
|
| + const Register crd = ConcreteRegister(rd);
|
| + const Register crn = ConcreteRegister(rn);
|
| + if (o.type() == Operand::Immediate) {
|
| + ASSERT((rd != ZR) && (rn != ZR));
|
| + EmitAddSubImmOp(subtract ? SUBI : ADDI, crd, crn, o, os, set_flags);
|
| + } else {
|
| + if (o.type() == Operand::Shifted) {
|
| + ASSERT((rd != SP) && (rn != SP));
|
| + EmitAddSubShiftExtOp(subtract ? SUB : ADD, crd, crn, o, os, set_flags);
|
| + } else {
|
| + ASSERT(o.type() == Operand::Extended);
|
| + ASSERT((rd != SP) && (rn != ZR));
|
| + EmitAddSubShiftExtOp(subtract ? SUB : ADD, crd, crn, o, os, set_flags);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void EmitAddSubImmOp(AddSubImmOp op, Register rd, Register rn,
|
| + Operand o, OperandSize os, bool set_flags) {
|
| + ASSERT((os == kDoubleWord) || (os == kWord));
|
| + const int32_t size = (os == kDoubleWord) ? B31 : 0;
|
| + const int32_t s = set_flags ? B29 : 0;
|
| + const int32_t encoding =
|
| + op | size | s |
|
| + (static_cast<int32_t>(rd) << kRdShift) |
|
| + (static_cast<int32_t>(rn) << kRnShift) |
|
| + o.encoding();
|
| + Emit(encoding);
|
| + }
|
| +
|
| + void EmitAddSubShiftExtOp(AddSubShiftExtOp op,
|
| + Register rd, Register rn, Operand o,
|
| + OperandSize os, bool set_flags) {
|
| + ASSERT((os == kDoubleWord) || (os == kWord));
|
| + const int32_t size = (os == kDoubleWord) ? B31 : 0;
|
| + const int32_t s = set_flags ? B29 : 0;
|
| + const int32_t encoding =
|
| + op | size | s |
|
| + (static_cast<int32_t>(rd) << kRdShift) |
|
| + (static_cast<int32_t>(rn) << kRnShift) |
|
| + o.encoding();
|
| + Emit(encoding);
|
| + }
|
| +
|
| + void EmitUnconditionalBranchRegOp(UnconditionalBranchRegOp op, Register rn) {
|
| + const int32_t encoding =
|
| + op | (static_cast<int32_t>(rn) << kRnShift);
|
| + Emit(encoding);
|
| + }
|
| +
|
| + DISALLOW_ALLOCATION();
|
| + DISALLOW_COPY_AND_ASSIGN(Assembler);
|
| +};
|
| +
|
| +} // namespace dart
|
| +
|
| +#endif // VM_ASSEMBLER_ARM64_H_
|
|
|