Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  Operands-vixl.h

  Sprache: C
 

// Copyright 2016, VIXL authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of ARM Limited nor the names of its contributors may be
//     used to endorse or promote products derived from this software without
//     specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef VIXL_A64_OPERANDS_A64_H_
#define VIXL_A64_OPERANDS_A64_H_

#include "jit/arm64/vixl/Instructions-vixl.h"
#include "jit/arm64/vixl/Registers-vixl.h"

#include "jit/shared/Assembler-shared.h"

namespace vixl {

// Lists of registers.
class CPURegList {
 public:
  explicit CPURegList(CPURegister reg1,
                      CPURegister reg2 = NoCPUReg,
                      CPURegister reg3 = NoCPUReg,
                      CPURegister reg4 = NoCPUReg)
      : list_(reg1.GetBit() | reg2.GetBit() | reg3.GetBit() | reg4.GetBit()),
        size_(reg1.GetSizeInBits()),
        type_(reg1.GetType()) {
    VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4));
    VIXL_ASSERT(IsValid());
  }

  CPURegList(CPURegister::RegisterType type, unsigned size, RegList list)
      : list_(list), size_(size), type_(type) {
    VIXL_ASSERT(IsValid());
  }

  CPURegList(CPURegister::RegisterType type,
             unsigned size,
             unsigned first_reg,
             unsigned last_reg)
      : size_(size), type_(type) {
    VIXL_ASSERT(
        ((type == CPURegister::kRegister) && (last_reg < kNumberOfRegisters)) ||
        ((type == CPURegister::kVRegister) &&
         (last_reg < kNumberOfVRegisters)));
    VIXL_ASSERT(last_reg >= first_reg);
    list_ = (UINT64_C(1) << (last_reg + 1)) - 1;
    list_ &= ~((UINT64_C(1) << first_reg) - 1);
    VIXL_ASSERT(IsValid());
  }

  // Construct an empty CPURegList with the specified size and type. If `size`
  // is CPURegister::kUnknownSize and the register type requires a size, a valid
  // but unspecified default will be picked.
  static CPURegList Empty(CPURegister::RegisterType type,
                          unsigned size = CPURegister::kUnknownSize) {
    return CPURegList(type, GetDefaultSizeFor(type, size), 0);
  }

  // Construct a CPURegList with all possible registers with the specified size
  // and type. If `size` is CPURegister::kUnknownSize and the register type
  // requires a size, a valid but unspecified default will be picked.
  static CPURegList All(CPURegister::RegisterType type,
                        unsigned size = CPURegister::kUnknownSize) {
    unsigned number_of_registers = (CPURegister::GetMaxCodeFor(type) + 1);
    RegList list = (static_cast<RegList>(1) << number_of_registers) - 1;
    if (type == CPURegister::kRegister) {
      // GetMaxCodeFor(kRegister) ignores SP, so explicitly include it.
      list |= (static_cast<RegList>(1) << kSPRegInternalCode);
    }
    return CPURegList(type, GetDefaultSizeFor(type, size), list);
  }

  CPURegister::RegisterType GetType() const {
    VIXL_ASSERT(IsValid());
    return type_;
  }
  CPURegister::RegisterType type() const {
    return GetType();
  }

  CPURegister::RegisterBank GetBank() const {
    return CPURegister::GetBankFor(GetType());
  }

  // Combine another CPURegList into this one. Registers that already exist in
  // this list are left unchanged. The type and size of the registers in the
  // 'other' list must match those in this list.
  void Combine(const CPURegList& other) {
    VIXL_ASSERT(IsValid());
    VIXL_ASSERT(other.GetType() == type_);
    VIXL_ASSERT(other.GetRegisterSizeInBits() == size_);
    list_ |= other.GetList();
  }

  // Remove every register in the other CPURegList from this one. Registers that
  // do not exist in this list are ignored. The type and size of the registers
  // in the 'other' list must match those in this list.
  void Remove(const CPURegList& other) {
    VIXL_ASSERT(IsValid());
    VIXL_ASSERT(other.GetType() == type_);
    VIXL_ASSERT(other.GetRegisterSizeInBits() == size_);
    list_ &= ~other.GetList();
  }

  // Variants of Combine and Remove which take a single register.
  void Combine(const CPURegister& other) {
    VIXL_ASSERT(other.GetType() == type_);
    VIXL_ASSERT(other.GetSizeInBits() == size_);
    Combine(other.GetCode());
  }

  void Remove(const CPURegister& other) {
    VIXL_ASSERT(other.GetType() == type_);
    VIXL_ASSERT(other.GetSizeInBits() == size_);
    Remove(other.GetCode());
  }

  // Variants of Combine and Remove which take a single register by its code;
  // the type and size of the register is inferred from this list.
  void Combine(int code) {
    VIXL_ASSERT(IsValid());
    VIXL_ASSERT(CPURegister(code, size_, type_).IsValid());
    list_ |= (UINT64_C(1) << code);
  }

  void Remove(int code) {
    VIXL_ASSERT(IsValid());
    VIXL_ASSERT(CPURegister(code, size_, type_).IsValid());
    list_ &= ~(UINT64_C(1) << code);
  }

  static CPURegList Union(const CPURegList& list_1, const CPURegList& list_2) {
    VIXL_ASSERT(list_1.type_ == list_2.type_);
    VIXL_ASSERT(list_1.size_ == list_2.size_);
    return CPURegList(list_1.type_, list_1.size_, list_1.list_ | list_2.list_);
  }
  static CPURegList Union(const CPURegList& list_1,
                          const CPURegList& list_2,
                          const CPURegList& list_3);
  static CPURegList Union(const CPURegList& list_1,
                          const CPURegList& list_2,
                          const CPURegList& list_3,
                          const CPURegList& list_4);

  static CPURegList Intersection(const CPURegList& list_1,
                                 const CPURegList& list_2) {
    VIXL_ASSERT(list_1.type_ == list_2.type_);
    VIXL_ASSERT(list_1.size_ == list_2.size_);
    return CPURegList(list_1.type_, list_1.size_, list_1.list_ & list_2.list_);
  }
  static CPURegList Intersection(const CPURegList& list_1,
                                 const CPURegList& list_2,
                                 const CPURegList& list_3);
  static CPURegList Intersection(const CPURegList& list_1,
                                 const CPURegList& list_2,
                                 const CPURegList& list_3,
                                 const CPURegList& list_4);

  bool Overlaps(const CPURegList& other) const {
    return (type_ == other.type_) && ((list_ & other.list_) != 0);
  }

  RegList GetList() const {
    VIXL_ASSERT(IsValid());
    return list_;
  }
  RegList list() const { return GetList(); }

  void SetList(RegList new_list) {
    VIXL_ASSERT(IsValid());
    list_ = new_list;
  }
  void set_list(RegList new_list) {
    return SetList(new_list);
  }

  // Remove all callee-saved registers from the list. This can be useful when
  // preparing registers for an AAPCS64 function call, for example.
  void RemoveCalleeSaved();

  // Find the register in this list that appears in `mask` with the lowest or
  // highest code, remove it from the list and return it as a CPURegister. If
  // the list is empty, leave it unchanged and return NoCPUReg.
  CPURegister PopLowestIndex(RegList mask = ~static_cast<RegList>(0));
  CPURegister PopHighestIndex(RegList mask = ~static_cast<RegList>(0));

  // AAPCS64 callee-saved registers.
  static CPURegList GetCalleeSaved(unsigned size = kXRegSize);
  static CPURegList GetCalleeSavedV(unsigned size = kDRegSize);

  // AAPCS64 caller-saved registers. Note that this includes lr.
  // TODO(all): Determine how we handle d8-d15 being callee-saved, but the top
  // 64-bits being caller-saved.
  static CPURegList GetCallerSaved(unsigned size = kXRegSize);
  static CPURegList GetCallerSavedV(unsigned size = kDRegSize);

  bool IsEmpty() const {
    VIXL_ASSERT(IsValid());
    return list_ == 0;
  }

  bool IncludesAliasOf(const CPURegister& other) const {
    VIXL_ASSERT(IsValid());
    return (GetBank() == other.GetBank()) && IncludesAliasOf(other.GetCode());
  }

  bool IncludesAliasOf(int code) const {
    VIXL_ASSERT(IsValid());
    return (((static_cast<RegList>(1) << code) & list_) != 0);
  }

  int GetCount() const {
    VIXL_ASSERT(IsValid());
    return CountSetBits(list_);
  }
  int Count() const { return GetCount(); }

  int GetRegisterSizeInBits() const {
    VIXL_ASSERT(IsValid());
    return size_;
  }
  int RegisterSizeInBits() const {
    return GetRegisterSizeInBits();
  }

  int GetRegisterSizeInBytes() const {
    int size_in_bits = GetRegisterSizeInBits();
    VIXL_ASSERT((size_in_bits % 8) == 0);
    return size_in_bits / 8;
  }
  int RegisterSizeInBytes() const {
    return GetRegisterSizeInBytes();
  }

  unsigned GetTotalSizeInBytes() const {
    VIXL_ASSERT(IsValid());
    return GetRegisterSizeInBytes() * GetCount();
  }
  unsigned TotalSizeInBytes() const {
    return GetTotalSizeInBytes();
  }

 private:
  // If `size` is CPURegister::kUnknownSize and the type requires a known size,
  // then return an arbitrary-but-valid size.
  //
  // Otherwise, the size is checked for validity and returned unchanged.
  static unsigned GetDefaultSizeFor(CPURegister::RegisterType type,
                                    unsigned size) {
    if (size == CPURegister::kUnknownSize) {
      if (type == CPURegister::kRegister) size = kXRegSize;
      if (type == CPURegister::kVRegister) size = kQRegSize;
      // All other types require kUnknownSize.
    }
    VIXL_ASSERT(CPURegister(0, size, type).IsValid());
    return size;
  }

  RegList list_;
  int size_;
  CPURegister::RegisterType type_;

  bool IsValid() const;
};


// AAPCS64 callee-saved registers.
extern const CPURegList kCalleeSaved;
extern const CPURegList kCalleeSavedV;


// AAPCS64 caller-saved registers. Note that this includes lr.
extern const CPURegList kCallerSaved;
extern const CPURegList kCallerSavedV;

class IntegerOperand;

// Operand.
class Operand {
 public:
  // #<immediate>
  // where <immediate> is int64_t.
  // This is allowed to be an implicit constructor because Operand is
  // a wrapper class that doesn't normally perform any type conversion.
  Operand(int64_t immediate);  // NOLINT(runtime/explicit)

  Operand(IntegerOperand immediate);  // NOLINT(runtime/explicit)

  // rm, {<shift> #<shift_amount>}
  // where <shift> is one of {LSL, LSR, ASR, ROR}.
  //       <shift_amount> is uint6_t.
  // This is allowed to be an implicit constructor because Operand is
  // a wrapper class that doesn't normally perform any type conversion.
  Operand(Register reg,
          Shift shift = LSL,
          unsigned shift_amount = 0);  // NOLINT(runtime/explicit)

  // rm, {<extend> {#<shift_amount>}}
  // where <extend> is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}.
  //       <shift_amount> is uint2_t.
  explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0);

  bool IsImmediate() const;
  bool IsPlainRegister() const;
  bool IsShiftedRegister() const;
  bool IsExtendedRegister() const;
  bool IsZero() const;

  // This returns an LSL shift (<= 4) operand as an equivalent extend operand,
  // which helps in the encoding of instructions that use the stack pointer.
  Operand ToExtendedRegister() const;

  int64_t GetImmediate() const {
    VIXL_ASSERT(IsImmediate());
    return immediate_;
  }
  int64_t immediate() const {
    return GetImmediate();
  }

  int64_t GetEquivalentImmediate() const {
    return IsZero() ? 0 : GetImmediate();
  }

  Register GetRegister() const {
    VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister());
    return reg_;
  }
  Register reg() const { return GetRegister(); }
  Register GetBaseRegister() const { return GetRegister(); }

  CPURegister maybeReg() const {
    if (IsShiftedRegister() || IsExtendedRegister())
      return reg_;
    return NoCPUReg;
  }

  Shift GetShift() const {
    VIXL_ASSERT(IsShiftedRegister());
    return shift_;
  }
  Shift shift() const { return GetShift(); }

  Extend GetExtend() const {
    VIXL_ASSERT(IsExtendedRegister());
    return extend_;
  }
  Extend extend() const { return GetExtend(); }

  unsigned GetShiftAmount() const {
    VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister());
    return shift_amount_;
  }
  unsigned shift_amount() const {
    return GetShiftAmount();
  }

 private:
  int64_t immediate_;
  Register reg_;
  Shift shift_;
  Extend extend_;
  unsigned shift_amount_;
};


// MemOperand represents the addressing mode of a load or store instruction.
// In assembly syntax, MemOperands are normally denoted by one or more elements
// inside or around square brackets.
class MemOperand {
 public:
  // Creates an invalid `MemOperand`.
  MemOperand();
  explicit MemOperand(Register base,
                      int64_t offset = 0,
                      AddrMode addrmode = Offset);
  MemOperand(Register base,
             Register regoffset,
             Shift shift = LSL,
             unsigned shift_amount = 0);
  MemOperand(Register base,
             Register regoffset,
             Extend extend,
             unsigned shift_amount = 0);
  MemOperand(Register base, const Operand& offset, AddrMode addrmode = Offset);

  // Adapter constructors using C++11 delegating.
  explicit MemOperand(js::jit::Address addr)
    : MemOperand(IsHiddenSP(addr.base) ? sp : Register(AsRegister(addr.base), 64),
                 (ptrdiff_t)addr.offset) {
    VIXL_ASSERT(addr.base.code != js::jit::Registers::xzr);
  }

  const Register& GetBaseRegister() const { return base_; }
  const Register& base() const { return base_; }

  // If the MemOperand has a register offset, return it. (This also applies to
  // pre- and post-index modes.) Otherwise, return NoReg.
  const Register& GetRegisterOffset() const { return regoffset_; }
  const Register& regoffset() const { return regoffset_; }

  // If the MemOperand has an immediate offset, return it. (This also applies to
  // pre- and post-index modes.) Otherwise, return 0.
  int64_t GetOffset() const { return offset_; }
  int64_t offset() const { return offset_; }

  AddrMode GetAddrMode() const { return addrmode_; }
  AddrMode addrmode() const { return addrmode_; }
  Shift GetShift() const { return shift_; }
  Shift shift() const { return shift_; }
  Extend GetExtend() const { return extend_; }
  Extend extend() const { return extend_; }

  unsigned GetShiftAmount() const {
    // Extend modes can also encode a shift for some instructions.
    VIXL_ASSERT((GetShift() != NO_SHIFT) || (GetExtend() != NO_EXTEND));
    return shift_amount_;
  }
  unsigned shift_amount() const { return shift_amount_; }

  // True for MemOperands which represent something like [x0].
  // Currently, this will also return true for [x0, #0], because MemOperand has
  // no way to distinguish the two.
  bool IsPlainRegister() const;

  // True for MemOperands which represent something like [x0], or for compound
  // MemOperands which are functionally equivalent, such as [x0, #0], [x0, xzr]
  // or [x0, wzr, UXTW #3].
  bool IsEquivalentToPlainRegister() const;

  // True for immediate-offset (but not indexed) MemOperands.
  bool IsImmediateOffset() const;
  // True for register-offset (but not indexed) MemOperands.
  bool IsRegisterOffset() const;
  // True for immediate or register pre-indexed MemOperands.
  bool IsPreIndex() const;
  // True for immediate or register post-indexed MemOperands.
  bool IsPostIndex() const;
  // True for immediate pre-indexed MemOperands, [reg, #imm]!
  bool IsImmediatePreIndex() const;
  // True for immediate post-indexed MemOperands, [reg], #imm
  bool IsImmediatePostIndex() const;

  void AddOffset(int64_t offset);

  bool IsValid() const {
    return base_.IsValid() &&
           ((addrmode_ == Offset) || (addrmode_ == PreIndex) ||
            (addrmode_ == PostIndex)) &&
           ((shift_ == NO_SHIFT) || (extend_ == NO_EXTEND)) &&
           ((offset_ == 0) || !regoffset_.IsValid());
  }

  bool Equals(const MemOperand& other) const {
    return base_.Is(other.base_) && regoffset_.Is(other.regoffset_) &&
           (offset_ == other.offset_) && (addrmode_ == other.addrmode_) &&
           (shift_ == other.shift_) && (extend_ == other.extend_) &&
           (shift_amount_ == other.shift_amount_);
  }

 private:
  Register base_;
  Register regoffset_;
  int64_t offset_;
  AddrMode addrmode_;
  Shift shift_;
  Extend extend_;
  unsigned shift_amount_;
};

// SVE supports memory operands which don't make sense to the core ISA, such as
// scatter-gather forms, in which either the base or offset registers are
// vectors. This class exists to avoid complicating core-ISA code with
// SVE-specific behaviour.
//
// Note that SVE does not support any pre- or post-index modes.
class SVEMemOperand {
 public:
  // "vector-plus-immediate", like [z0.s, #21]
  explicit SVEMemOperand(ZRegister base, uint64_t offset = 0)
      : base_(base),
        regoffset_(NoReg),
        offset_(RawbitsToInt64(offset)),
        mod_(NO_SVE_OFFSET_MODIFIER),
        shift_amount_(0) {
    VIXL_ASSERT(IsVectorPlusImmediate());
    VIXL_ASSERT(IsValid());
  }

  // "scalar-plus-immediate", like [x0], [x0, #42] or [x0, #42, MUL_VL]
  // The only supported modifiers are NO_SVE_OFFSET_MODIFIER or SVE_MUL_VL.
  //
  // Note that VIXL cannot currently distinguish between `SVEMemOperand(x0)` and
  // `SVEMemOperand(x0, 0)`. This is only significant in scalar-plus-scalar
  // instructions where xm defaults to xzr. However, users should not rely on
  // `SVEMemOperand(x0, 0)` being accepted in such cases.
  explicit SVEMemOperand(Register base,
                         uint64_t offset = 0,
                         SVEOffsetModifier mod = NO_SVE_OFFSET_MODIFIER)
      : base_(base),
        regoffset_(NoReg),
        offset_(RawbitsToInt64(offset)),
        mod_(mod),
        shift_amount_(0) {
    VIXL_ASSERT(IsScalarPlusImmediate());
    VIXL_ASSERT(IsValid());
  }

  // "scalar-plus-scalar", like [x0, x1]
  // "scalar-plus-vector", like [x0, z1.d]
  SVEMemOperand(Register base, CPURegister offset)
      : base_(base),
        regoffset_(offset),
        offset_(0),
        mod_(NO_SVE_OFFSET_MODIFIER),
        shift_amount_(0) {
    VIXL_ASSERT(IsScalarPlusScalar() || IsScalarPlusVector());
    if (offset.IsZero()) VIXL_ASSERT(IsEquivalentToScalar());
    VIXL_ASSERT(IsValid());
  }

  // "scalar-plus-vector", like [x0, z1.d, UXTW]
  // The type of `mod` can be any `SVEOffsetModifier` (other than LSL), or a
  // corresponding `Extend` value.
  template <typename M>
  SVEMemOperand(Register base, ZRegister offset, M mod)
      : base_(base),
        regoffset_(offset),
        offset_(0),
        mod_(GetSVEOffsetModifierFor(mod)),
        shift_amount_(0) {
    VIXL_ASSERT(mod_ != SVE_LSL);  // LSL requires an explicit shift amount.
    VIXL_ASSERT(IsScalarPlusVector());
    VIXL_ASSERT(IsValid());
  }

  // "scalar-plus-scalar", like [x0, x1, LSL #1]
  // "scalar-plus-vector", like [x0, z1.d, LSL #2]
  // The type of `mod` can be any `SVEOffsetModifier`, or a corresponding
  // `Shift` or `Extend` value.
  template <typename M>
  SVEMemOperand(Register base, CPURegister offset, M mod, unsigned shift_amount)
      : base_(base),
        regoffset_(offset),
        offset_(0),
        mod_(GetSVEOffsetModifierFor(mod)),
        shift_amount_(shift_amount) {
    VIXL_ASSERT(IsValid());
  }

  // "vector-plus-scalar", like [z0.d, x0]
  SVEMemOperand(ZRegister base, Register offset)
      : base_(base),
        regoffset_(offset),
        offset_(0),
        mod_(NO_SVE_OFFSET_MODIFIER),
        shift_amount_(0) {
    VIXL_ASSERT(IsValid());
    VIXL_ASSERT(IsVectorPlusScalar());
  }

  // "vector-plus-vector", like [z0.d, z1.d, UXTW]
  template <typename M = SVEOffsetModifier>
  SVEMemOperand(ZRegister base,
                ZRegister offset,
                M mod = NO_SVE_OFFSET_MODIFIER,
                unsigned shift_amount = 0)
      : base_(base),
        regoffset_(offset),
        offset_(0),
        mod_(GetSVEOffsetModifierFor(mod)),
        shift_amount_(shift_amount) {
    VIXL_ASSERT(IsValid());
    VIXL_ASSERT(IsVectorPlusVector());
  }

  // True for SVEMemOperands which represent something like [x0].
  // This will also return true for [x0, #0], because there is no way
  // to distinguish the two.
  bool IsPlainScalar() const {
    return IsScalarPlusImmediate() && (offset_ == 0);
  }

  // True for SVEMemOperands which represent something like [x0], or for
  // compound SVEMemOperands which are functionally equivalent, such as
  // [x0, #0], [x0, xzr] or [x0, wzr, UXTW #3].
  bool IsEquivalentToScalar() const;

  // True for SVEMemOperands like [x0], [x0, #0], false for [x0, xzr] and
  // similar.
  bool IsPlainRegister() const;

  bool IsScalarPlusImmediate() const {
    return base_.IsX() && regoffset_.IsNone() &&
           ((mod_ == NO_SVE_OFFSET_MODIFIER) || IsMulVl());
  }

  bool IsScalarPlusScalar() const {
    // SVE offers no extend modes for scalar-plus-scalar, so both registers must
    // be X registers.
    return base_.IsX() && regoffset_.IsX() &&
           ((mod_ == NO_SVE_OFFSET_MODIFIER) || (mod_ == SVE_LSL));
  }

  bool IsScalarPlusVector() const {
    // The modifier can be LSL or an an extend mode (UXTW or SXTW) here. Unlike
    // in the core ISA, these extend modes do not imply an S-sized lane, so the
    // modifier is independent from the lane size. The architecture describes
    // [US]XTW with a D-sized lane as an "unpacked" offset.
    return base_.IsX() && regoffset_.IsZRegister() &&
           (regoffset_.IsLaneSizeS() || regoffset_.IsLaneSizeD()) && !IsMulVl();
  }

  bool IsVectorPlusImmediate() const {
    return base_.IsZRegister() &&
           (base_.IsLaneSizeS() || base_.IsLaneSizeD()) &&
           regoffset_.IsNone() && (mod_ == NO_SVE_OFFSET_MODIFIER);
  }

  bool IsVectorPlusScalar() const {
    return base_.IsZRegister() && regoffset_.IsX() &&
           (base_.IsLaneSizeS() || base_.IsLaneSizeD());
  }

  bool IsVectorPlusVector() const {
    return base_.IsZRegister() && regoffset_.IsZRegister() && (offset_ == 0) &&
           AreSameFormat(base_, regoffset_) &&
           (base_.IsLaneSizeS() || base_.IsLaneSizeD());
  }

  bool IsContiguous() const { return !IsScatterGather(); }
  bool IsScatterGather() const {
    return base_.IsZRegister() || regoffset_.IsZRegister();
  }

  // TODO: If necessary, add helpers like `HasScalarBase()`.

  Register GetScalarBase() const {
    VIXL_ASSERT(base_.IsX());
    return Register(base_);
  }

  ZRegister GetVectorBase() const {
    VIXL_ASSERT(base_.IsZRegister());
    VIXL_ASSERT(base_.HasLaneSize());
    return ZRegister(base_);
  }

  Register GetScalarOffset() const {
    VIXL_ASSERT(regoffset_.IsRegister());
    return Register(regoffset_);
  }

  ZRegister GetVectorOffset() const {
    VIXL_ASSERT(regoffset_.IsZRegister());
    VIXL_ASSERT(regoffset_.HasLaneSize());
    return ZRegister(regoffset_);
  }

  int64_t GetImmediateOffset() const {
    VIXL_ASSERT(regoffset_.IsNone());
    return offset_;
  }

  SVEOffsetModifier GetOffsetModifier() const { return mod_; }
  unsigned GetShiftAmount() const { return shift_amount_; }

  bool IsEquivalentToLSL(unsigned amount) const {
    if (shift_amount_ != amount) return false;
    if (amount == 0) {
      // No-shift is equivalent to "LSL #0".
      return ((mod_ == SVE_LSL) || (mod_ == NO_SVE_OFFSET_MODIFIER));
    }
    return mod_ == SVE_LSL;
  }

  bool IsMulVl() const { return mod_ == SVE_MUL_VL; }

  bool IsValid() const;

 private:
  // Allow standard `Shift` and `Extend` arguments to be used.
  SVEOffsetModifier GetSVEOffsetModifierFor(Shift shift) {
    if (shift == LSL) return SVE_LSL;
    if (shift == NO_SHIFT) return NO_SVE_OFFSET_MODIFIER;
    // SVE does not accept any other shift.
    VIXL_UNIMPLEMENTED();
    return NO_SVE_OFFSET_MODIFIER;
  }

  SVEOffsetModifier GetSVEOffsetModifierFor(Extend extend = NO_EXTEND) {
    if (extend == UXTW) return SVE_UXTW;
    if (extend == SXTW) return SVE_SXTW;
    if (extend == NO_EXTEND) return NO_SVE_OFFSET_MODIFIER;
    // SVE does not accept any other extend mode.
    VIXL_UNIMPLEMENTED();
    return NO_SVE_OFFSET_MODIFIER;
  }

  SVEOffsetModifier GetSVEOffsetModifierFor(SVEOffsetModifier mod) {
    return mod;
  }

  CPURegister base_;
  CPURegister regoffset_;
  int64_t offset_;
  SVEOffsetModifier mod_;
  unsigned shift_amount_;
};

// Represent a signed or unsigned integer operand.
//
// This is designed to make instructions which naturally accept a _signed_
// immediate easier to implement and use, when we also want users to be able to
// specify raw-bits values (such as with hexadecimal constants). The advantage
// of this class over a simple uint64_t (with implicit C++ sign-extension) is
// that this class can strictly check the range of allowed values. With a simple
// uint64_t, it is impossible to distinguish -1 from UINT64_MAX.
//
// For example, these instructions are equivalent:
//
//     __ Insr(z0.VnB(), -1);
//     __ Insr(z0.VnB(), 0xff);
//
// ... as are these:
//
//     __ Insr(z0.VnD(), -1);
//     __ Insr(z0.VnD(), 0xffffffffffffffff);
//
// ... but this is invalid:
//
//     __ Insr(z0.VnB(), 0xffffffffffffffff);  // Too big for B-sized lanes.
class IntegerOperand {
 public:
#define VIXL_INT_TYPES(V) \
  V(char) V(short) V(int) V(long) V(long long)  // NOLINT(google-runtime-int)
#define VIXL_DECL_INT_OVERLOADS(T)                                        \
  /* These are allowed to be implicit constructors because this is a */   \
  /* wrapper class that doesn't normally perform any type conversion. */  \
  IntegerOperand(signed T immediate) /* NOLINT(runtime/explicit) */       \
      : raw_bits_(immediate),        /* Allow implicit sign-extension. */ \
        is_negative_(immediate < 0) {}                                    \
  IntegerOperand(unsigned T immediate) /* NOLINT(runtime/explicit) */     \
      : raw_bits_(immediate), is_negative_(false) {}
  VIXL_INT_TYPES(VIXL_DECL_INT_OVERLOADS)
#undef VIXL_DECL_INT_OVERLOADS
#undef VIXL_INT_TYPES

  // TODO: `Operand` can currently only hold an int64_t, so some large, unsigned
  // values will be misrepresented here.
  explicit IntegerOperand(const Operand& operand)
      : raw_bits_(operand.GetEquivalentImmediate()),
        is_negative_(operand.GetEquivalentImmediate() < 0) {}

  bool IsIntN(unsigned n) const {
    return is_negative_ ? vixl::IsIntN(n, RawbitsToInt64(raw_bits_))
                        : vixl::IsIntN(n, raw_bits_);
  }
  bool IsUintN(unsigned n) const {
    return !is_negative_ && vixl::IsUintN(n, raw_bits_);
  }

  bool IsUint8() const { return IsUintN(8); }
  bool IsUint16() const { return IsUintN(16); }
  bool IsUint32() const { return IsUintN(32); }
  bool IsUint64() const { return IsUintN(64); }

  bool IsInt8() const { return IsIntN(8); }
  bool IsInt16() const { return IsIntN(16); }
  bool IsInt32() const { return IsIntN(32); }
  bool IsInt64() const { return IsIntN(64); }

  bool FitsInBits(unsigned n) const {
    return is_negative_ ? IsIntN(n) : IsUintN(n);
  }
  bool FitsInLane(const CPURegister& zd) const {
    return FitsInBits(zd.GetLaneSizeInBits());
  }
  bool FitsInSignedLane(const CPURegister& zd) const {
    return IsIntN(zd.GetLaneSizeInBits());
  }
  bool FitsInUnsignedLane(const CPURegister& zd) const {
    return IsUintN(zd.GetLaneSizeInBits());
  }

  // Cast a value in the range [INT<n>_MIN, UINT<n>_MAX] to an unsigned integer
  // in the range [0, UINT<n>_MAX] (using two's complement mapping).
  uint64_t AsUintN(unsigned n) const {
    VIXL_ASSERT(FitsInBits(n));
    return raw_bits_ & GetUintMask(n);
  }

  uint8_t AsUint8() const { return static_cast<uint8_t>(AsUintN(8)); }
  uint16_t AsUint16() const { return static_cast<uint16_t>(AsUintN(16)); }
  uint32_t AsUint32() const { return static_cast<uint32_t>(AsUintN(32)); }
  uint64_t AsUint64() const { return AsUintN(64); }

  // Cast a value in the range [INT<n>_MIN, UINT<n>_MAX] to a signed integer in
  // the range [INT<n>_MIN, INT<n>_MAX] (using two's complement mapping).
  int64_t AsIntN(unsigned n) const {
    VIXL_ASSERT(FitsInBits(n));
    return ExtractSignedBitfield64(n - 10, raw_bits_);
  }

  int8_t AsInt8() const { return static_cast<int8_t>(AsIntN(8)); }
  int16_t AsInt16() const { return static_cast<int16_t>(AsIntN(16)); }
  int32_t AsInt32() const { return static_cast<int32_t>(AsIntN(32)); }
  int64_t AsInt64() const { return AsIntN(64); }

  // Several instructions encode a signed int<N>_t, which is then (optionally)
  // left-shifted and sign-extended to a Z register lane with a size which may
  // be larger than N. This helper tries to find an int<N>_t such that the
  // IntegerOperand's arithmetic value is reproduced in each lane.
  //
  // This is the mechanism that allows `Insr(z0.VnB(), 0xff)` to be treated as
  // `Insr(z0.VnB(), -1)`.
  template <unsigned N, unsigned kShift, typename T>
  bool TryEncodeAsShiftedIntNForLane(const CPURegister& zd, T* imm) const {
    VIXL_STATIC_ASSERT(std::numeric_limits<T>::digits > N);
    VIXL_ASSERT(FitsInLane(zd));
    if ((raw_bits_ & GetUintMask(kShift)) != 0return false;

    // Reverse the specified left-shift.
    IntegerOperand unshifted(*this);
    unshifted.ArithmeticShiftRight(kShift);

    if (unshifted.IsIntN(N)) {
      // This is trivial, since sign-extension produces the same arithmetic
      // value irrespective of the destination size.
      *imm = static_cast<T>(unshifted.AsIntN(N));
      return true;
    }

    // Otherwise, we might be able to use the sign-extension to produce the
    // desired bit pattern. We can only do this for values in the range
    // [INT<N>_MAX + 1, UINT<N>_MAX], where the highest set bit is the sign bit.
    //
    // The lane size has to be adjusted to compensate for `kShift`, since the
    // high bits will be dropped when the encoded value is left-shifted.
    if (unshifted.IsUintN(zd.GetLaneSizeInBits() - kShift)) {
      int64_t encoded = unshifted.AsIntN(zd.GetLaneSizeInBits() - kShift);
      if (vixl::IsIntN(N, encoded)) {
        *imm = static_cast<T>(encoded);
        return true;
      }
    }
    return false;
  }

  // As above, but `kShift` is written to the `*shift` parameter on success, so
  // that it is easy to chain calls like this:
  //
  //     if (imm.TryEncodeAsShiftedIntNForLane<8, 0>(zd, &imm8, &shift) ||
  //         imm.TryEncodeAsShiftedIntNForLane<8, 8>(zd, &imm8, &shift)) {
  //       insn(zd, imm8, shift)
  //     }
  template <unsigned N, unsigned kShift, typename T, typename S>
  bool TryEncodeAsShiftedIntNForLane(const CPURegister& zd,
                                     T* imm,
                                     S* shift) const {
    if (TryEncodeAsShiftedIntNForLane<N, kShift>(zd, imm)) {
      *shift = kShift;
      return true;
    }
    return false;
  }

  // As above, but assume that `kShift` is 0.
  template <unsigned N, typename T>
  bool TryEncodeAsIntNForLane(const CPURegister& zd, T* imm) const {
    return TryEncodeAsShiftedIntNForLane<N, 0>(zd, imm);
  }

  // As above, but for unsigned fields. This is usually a simple operation, but
  // is provided for symmetry.
  template <unsigned N, unsigned kShift, typename T>
  bool TryEncodeAsShiftedUintNForLane(const CPURegister& zd, T* imm) const {
    VIXL_STATIC_ASSERT(std::numeric_limits<T>::digits > N);
    VIXL_ASSERT(FitsInLane(zd));

    // TODO: Should we convert -1 to 0xff here?
    if (is_negative_) return false;
    USE(zd);

    if ((raw_bits_ & GetUintMask(kShift)) != 0return false;

    if (vixl::IsUintN(N, raw_bits_ >> kShift)) {
      *imm = static_cast<T>(raw_bits_ >> kShift);
      return true;
    }
    return false;
  }

  template <unsigned N, unsigned kShift, typename T, typename S>
  bool TryEncodeAsShiftedUintNForLane(const CPURegister& zd,
                                      T* imm,
                                      S* shift) const {
    if (TryEncodeAsShiftedUintNForLane<N, kShift>(zd, imm)) {
      *shift = kShift;
      return true;
    }
    return false;
  }

  bool IsZero() const { return raw_bits_ == 0; }
  bool IsNegative() const { return is_negative_; }
  bool IsPositiveOrZero() const { return !is_negative_; }

  uint64_t GetMagnitude() const {
    return is_negative_ ? UnsignedNegate(raw_bits_) : raw_bits_;
  }

 private:
  // Shift the arithmetic value right, with sign extension if is_negative_.
  void ArithmeticShiftRight(int shift) {
    VIXL_ASSERT((shift >= 0) && (shift < 64));
    if (shift == 0return;
    if (is_negative_) {
      raw_bits_ = ExtractSignedBitfield64(63, shift, raw_bits_);
    } else {
      raw_bits_ >>= shift;
    }
  }

  uint64_t raw_bits_;
  bool is_negative_;
};

// This an abstraction that can represent a register or memory location. The
// `MacroAssembler` provides helpers to move data between generic operands.
class GenericOperand {
 public:
  GenericOperand() { VIXL_ASSERT(!IsValid()); }
  GenericOperand(const CPURegister& reg);  // NOLINT(runtime/explicit)
  GenericOperand(const MemOperand& mem_op,
                 size_t mem_op_size = 0);  // NOLINT(runtime/explicit)

  bool IsValid() const { return cpu_register_.IsValid() != mem_op_.IsValid(); }

  bool Equals(const GenericOperand& other) const;

  bool IsCPURegister() const {
    VIXL_ASSERT(IsValid());
    return cpu_register_.IsValid();
  }

  bool IsRegister() const {
    return IsCPURegister() && cpu_register_.IsRegister();
  }

  bool IsVRegister() const {
    return IsCPURegister() && cpu_register_.IsVRegister();
  }

  bool IsSameCPURegisterType(const GenericOperand& other) {
    return IsCPURegister() && other.IsCPURegister() &&
           GetCPURegister().IsSameType(other.GetCPURegister());
  }

  bool IsMemOperand() const {
    VIXL_ASSERT(IsValid());
    return mem_op_.IsValid();
  }

  CPURegister GetCPURegister() const {
    VIXL_ASSERT(IsCPURegister());
    return cpu_register_;
  }

  MemOperand GetMemOperand() const {
    VIXL_ASSERT(IsMemOperand());
    return mem_op_;
  }

  size_t GetMemOperandSizeInBytes() const {
    VIXL_ASSERT(IsMemOperand());
    return mem_op_size_;
  }

  size_t GetSizeInBytes() const {
    return IsCPURegister() ? cpu_register_.GetSizeInBytes()
                           : GetMemOperandSizeInBytes();
  }

  size_t GetSizeInBits() const { return GetSizeInBytes() * kBitsPerByte; }

 private:
  CPURegister cpu_register_;
  MemOperand mem_op_;
  // The size of the memory region pointed to, in bytes.
  // We only support sizes up to X/D register sizes.
  size_t mem_op_size_;
};
}  // namespace vixl

#endif  // VIXL_A64_OPERANDS_A64_H_

Messung V0.5 in Prozent
C=80 H=100 G=90

¤ Dauer der Verarbeitung: 0.12 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....
    

Besucher

Besucher

Statistik
#Sources=277311
#Domains=752002