// Copyright 2015, 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_AARCH32_OPERANDS_AARCH32_H_
#define VIXL_AARCH32_OPERANDS_AARCH32_H_

#include "aarch32/instructions-aarch32.h"

namespace vixl {
namespace aarch32 {

// Operand represents generic set of arguments to pass to an instruction.
//
//   Usage: <instr> <Rd> , <Operand>
//
//   where <instr> is the instruction to use (e.g., Mov(), Rsb(), etc.)
//         <Rd> is the destination register
//         <Operand> is the rest of the arguments to the instruction
//
//   <Operand> can be one of:
//
//   #<imm> - an unsigned 32-bit immediate value
//   <Rm>, <shift> <#amount> - immediate shifted register
//   <Rm>, <shift> <Rs> - register shifted register
//
class Operand {
 public:
  // { #<immediate> }
  // where <immediate> is uint32_t.
  // This is allowed to be an implicit constructor because Operand is
  // a wrapper class that doesn't normally perform any type conversion.
  Operand(uint32_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoReg),
        shift_(LSL),
        amount_(0),
        rs_(NoReg) {}
  Operand(int32_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoReg),
        shift_(LSL),
        amount_(0),
        rs_(NoReg) {}

  // rm
  // where rm is the base register
  // This is allowed to be an implicit constructor because Operand is
  // a wrapper class that doesn't normally perform any type conversion.
  Operand(Register rm)  // NOLINT(runtime/explicit)
      : imm_(0),
        rm_(rm),
        shift_(LSL),
        amount_(0),
        rs_(NoReg) {
    VIXL_ASSERT(rm_.IsValid());
  }

  // rm, <shift>
  // where rm is the base register, and
  //       <shift> is RRX
  Operand(Register rm, Shift shift)
      : imm_(0), rm_(rm), shift_(shift), amount_(0), rs_(NoReg) {
    VIXL_ASSERT(rm_.IsValid());
    VIXL_ASSERT(shift_.IsRRX());
  }

  // rm, <shift> #<amount>
  // where rm is the base register, and
  //       <shift> is one of {LSL, LSR, ASR, ROR}, and
  //       <amount> is uint6_t.
  Operand(Register rm, Shift shift, uint32_t amount)
      : imm_(0), rm_(rm), shift_(shift), amount_(amount), rs_(NoReg) {
    VIXL_ASSERT(rm_.IsValid());
    VIXL_ASSERT(!shift_.IsRRX());
#ifdef VIXL_DEBUG
    switch (shift_.GetType()) {
      case LSL:
        VIXL_ASSERT(amount_ <= 31);
        break;
      case ROR:
        VIXL_ASSERT(amount_ <= 31);
        break;
      case LSR:
      case ASR:
        VIXL_ASSERT(amount_ <= 32);
        break;
      case RRX:
      default:
        VIXL_UNREACHABLE();
        break;
    }
#endif
  }

  // rm, <shift> rs
  // where rm is the base register, and
  //       <shift> is one of {LSL, LSR, ASR, ROR}, and
  //       rs is the shifted register
  Operand(Register rm, Shift shift, Register rs)
      : imm_(0), rm_(rm), shift_(shift), amount_(0), rs_(rs) {
    VIXL_ASSERT(rm_.IsValid() && rs_.IsValid());
    VIXL_ASSERT(!shift_.IsRRX());
  }

  // Factory methods creating operands from any integral or pointer type. The
  // source must fit into 32 bits.
  template <typename T>
  static Operand From(T immediate) {
#if __cplusplus >= 201103L
    VIXL_STATIC_ASSERT_MESSAGE(std::is_integral<T>::value,
                               "An integral type is required to build an "
                               "immediate operand.");
#endif
    // Allow both a signed or unsigned 32 bit integer to be passed, but store it
    // as a uint32_t. The signedness information will be lost. We have to add a
    // static_cast to make sure the compiler does not complain about implicit 64
    // to 32 narrowing. It's perfectly acceptable for the user to pass a 64-bit
    // value, as long as it can be encoded in 32 bits.
    VIXL_ASSERT(IsInt32(immediate) || IsUint32(immediate));
    return Operand(static_cast<uint32_t>(immediate));
  }

  template <typename T>
  static Operand From(T* address) {
    uintptr_t address_as_integral = reinterpret_cast<uintptr_t>(address);
    VIXL_ASSERT(IsUint32(address_as_integral));
    return Operand(static_cast<uint32_t>(address_as_integral));
  }

  bool IsImmediate() const { return !rm_.IsValid(); }

  bool IsPlainRegister() const {
    return rm_.IsValid() && !shift_.IsRRX() && !rs_.IsValid() && (amount_ == 0);
  }

  bool IsImmediateShiftedRegister() const {
    return rm_.IsValid() && !rs_.IsValid();
  }

  bool IsRegisterShiftedRegister() const {
    return rm_.IsValid() && rs_.IsValid();
  }

  uint32_t GetImmediate() const {
    VIXL_ASSERT(IsImmediate());
    return imm_;
  }

  int32_t GetSignedImmediate() const {
    VIXL_ASSERT(IsImmediate());
    int32_t result;
    memcpy(&result, &imm_, sizeof(result));
    return result;
  }

  Register GetBaseRegister() const {
    VIXL_ASSERT(IsImmediateShiftedRegister() || IsRegisterShiftedRegister());
    return rm_;
  }

  Shift GetShift() const {
    VIXL_ASSERT(IsImmediateShiftedRegister() || IsRegisterShiftedRegister());
    return shift_;
  }

  uint32_t GetShiftAmount() const {
    VIXL_ASSERT(IsImmediateShiftedRegister());
    return amount_;
  }

  Register GetShiftRegister() const {
    VIXL_ASSERT(IsRegisterShiftedRegister());
    return rs_;
  }

  uint32_t GetTypeEncodingValue() const {
    return shift_.IsRRX() ? kRRXEncodedValue : shift_.GetValue();
  }

 private:
// Forbid implicitely creating operands around types that cannot be encoded
// into a uint32_t without loss.
#if __cplusplus >= 201103L
  Operand(int64_t) = delete;   // NOLINT(runtime/explicit)
  Operand(uint64_t) = delete;  // NOLINT(runtime/explicit)
  Operand(float) = delete;     // NOLINT(runtime/explicit)
  Operand(double) = delete;    // NOLINT(runtime/explicit)
#else
  VIXL_NO_RETURN_IN_DEBUG_MODE Operand(int64_t) {  // NOLINT(runtime/explicit)
    VIXL_UNREACHABLE();
  }
  VIXL_NO_RETURN_IN_DEBUG_MODE Operand(uint64_t) {  // NOLINT(runtime/explicit)
    VIXL_UNREACHABLE();
  }
  VIXL_NO_RETURN_IN_DEBUG_MODE Operand(float) {  // NOLINT
    VIXL_UNREACHABLE();
  }
  VIXL_NO_RETURN_IN_DEBUG_MODE Operand(double) {  // NOLINT
    VIXL_UNREACHABLE();
  }
#endif

  uint32_t imm_;
  Register rm_;
  Shift shift_;
  uint32_t amount_;
  Register rs_;
};

std::ostream& operator<<(std::ostream& os, const Operand& operand);

class NeonImmediate {
  template <typename T>
  struct DataTypeIdentity {
    T data_type_;
  };

 public:
  // { #<immediate> }
  // where <immediate> is 32 bit number.
  // This is allowed to be an implicit constructor because NeonImmediate is
  // a wrapper class that doesn't normally perform any type conversion.
  NeonImmediate(uint32_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        immediate_type_(I32) {}
  NeonImmediate(int immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        immediate_type_(I32) {}

  // { #<immediate> }
  // where <immediate> is a 64 bit number
  // This is allowed to be an implicit constructor because NeonImmediate is
  // a wrapper class that doesn't normally perform any type conversion.
  NeonImmediate(int64_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        immediate_type_(I64) {}
  NeonImmediate(uint64_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        immediate_type_(I64) {}

  // { #<immediate> }
  // where <immediate> is a non zero floating point number which can be encoded
  // as an 8 bit floating point (checked by the constructor).
  // This is allowed to be an implicit constructor because NeonImmediate is
  // a wrapper class that doesn't normally perform any type conversion.
  NeonImmediate(float immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        immediate_type_(F32) {}
  NeonImmediate(double immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        immediate_type_(F64) {}

  NeonImmediate(const NeonImmediate& src)
      : imm_(src.imm_), immediate_type_(src.immediate_type_) {}

  template <typename T>
  T GetImmediate() const {
    return GetImmediate(DataTypeIdentity<T>());
  }

  template <typename T>
  T GetImmediate(const DataTypeIdentity<T>&) const {
    VIXL_ASSERT(sizeof(T) <= sizeof(uint32_t));
    VIXL_ASSERT(CanConvert<T>());
    if (immediate_type_.Is(I64))
      return static_cast<T>(imm_.u64_ & static_cast<T>(-1));
    if (immediate_type_.Is(F64) || immediate_type_.Is(F32)) return 0;
    return static_cast<T>(imm_.u32_ & static_cast<T>(-1));
  }

  uint64_t GetImmediate(const DataTypeIdentity<uint64_t>&) const {
    VIXL_ASSERT(CanConvert<uint64_t>());
    if (immediate_type_.Is(I32)) return imm_.u32_;
    if (immediate_type_.Is(F64) || immediate_type_.Is(F32)) return 0;
    return imm_.u64_;
  }
  float GetImmediate(const DataTypeIdentity<float>&) const {
    VIXL_ASSERT(CanConvert<float>());
    if (immediate_type_.Is(F64)) return static_cast<float>(imm_.d_);
    return imm_.f_;
  }
  double GetImmediate(const DataTypeIdentity<double>&) const {
    VIXL_ASSERT(CanConvert<double>());
    if (immediate_type_.Is(F32)) return static_cast<double>(imm_.f_);
    return imm_.d_;
  }

  bool IsInteger32() const { return immediate_type_.Is(I32); }
  bool IsInteger64() const { return immediate_type_.Is(I64); }
  bool IsInteger() const { return IsInteger32() | IsInteger64(); }
  bool IsFloat() const { return immediate_type_.Is(F32); }
  bool IsDouble() const { return immediate_type_.Is(F64); }

  template <typename T>
  bool CanConvert() const {
    return CanConvert(DataTypeIdentity<T>());
  }

  template <typename T>
  bool CanConvert(const DataTypeIdentity<T>&) const {
    VIXL_ASSERT(sizeof(T) < sizeof(uint32_t));
    return (immediate_type_.Is(I32) && ((imm_.u32_ >> (8 * sizeof(T))) == 0)) ||
           (immediate_type_.Is(I64) && ((imm_.u64_ >> (8 * sizeof(T))) == 0)) ||
           (immediate_type_.Is(F32) && (imm_.f_ == 0.0f)) ||
           (immediate_type_.Is(F64) && (imm_.d_ == 0.0));
  }
  bool CanConvert(const DataTypeIdentity<uint32_t>&) const {
    return immediate_type_.Is(I32) ||
           (immediate_type_.Is(I64) && ((imm_.u64_ >> 32) == 0)) ||
           (immediate_type_.Is(F32) && (imm_.f_ == 0.0f)) ||
           (immediate_type_.Is(F64) && (imm_.d_ == 0.0));
  }
  bool CanConvert(const DataTypeIdentity<uint64_t>&) const {
    return IsInteger() || CanConvert<uint32_t>();
  }
  bool CanConvert(const DataTypeIdentity<float>&) const {
    return IsFloat() || IsDouble();
  }
  bool CanConvert(const DataTypeIdentity<double>&) const {
    return IsFloat() || IsDouble();
  }
  friend std::ostream& operator<<(std::ostream& os,
                                  const NeonImmediate& operand);

 private:
  union NeonImmediateType {
    uint64_t u64_;
    double d_;
    uint32_t u32_;
    float f_;
    NeonImmediateType(uint64_t u) : u64_(u) {}
    NeonImmediateType(int64_t u) : u64_(u) {}
    NeonImmediateType(uint32_t u) : u32_(u) {}
    NeonImmediateType(int32_t u) : u32_(u) {}
    NeonImmediateType(double d) : d_(d) {}
    NeonImmediateType(float f) : f_(f) {}
    NeonImmediateType(const NeonImmediateType& ref) : u64_(ref.u64_) {}
  } imm_;

  DataType immediate_type_;
};

std::ostream& operator<<(std::ostream& os, const NeonImmediate& operand);

class NeonOperand {
 public:
  NeonOperand(int32_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoDReg) {}
  NeonOperand(uint32_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoDReg) {}
  NeonOperand(int64_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoDReg) {}
  NeonOperand(uint64_t immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoDReg) {}
  NeonOperand(float immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoDReg) {}
  NeonOperand(double immediate)  // NOLINT(runtime/explicit)
      : imm_(immediate),
        rm_(NoDReg) {}
  NeonOperand(const NeonImmediate& imm)  // NOLINT(runtime/explicit)
      : imm_(imm),
        rm_(NoDReg) {}
  NeonOperand(const VRegister& rm)  // NOLINT(runtime/explicit)
      : imm_(0),
        rm_(rm) {
    VIXL_ASSERT(rm_.IsValid());
  }

  bool IsImmediate() const { return !rm_.IsValid(); }
  bool IsRegister() const { return rm_.IsValid(); }

  const NeonImmediate& GetNeonImmediate() const { return imm_; }

  VRegister GetRegister() const {
    VIXL_ASSERT(IsRegister());
    return rm_;
  }

 protected:
  NeonImmediate imm_;
  VRegister rm_;
};

std::ostream& operator<<(std::ostream& os, const NeonOperand& operand);

// SOperand represents either an immediate or a SRegister.
class SOperand : public NeonOperand {
 public:
  // #<immediate>
  // where <immediate> is 32bit int
  // This is allowed to be an implicit constructor because SOperand is
  // a wrapper class that doesn't normally perform any type conversion.
  SOperand(int32_t immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  SOperand(uint32_t immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  // #<immediate>
  // where <immediate> is 32bit float
  SOperand(float immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}

  SOperand(const NeonImmediate& imm)  // NOLINT(runtime/explicit)
      : NeonOperand(imm) {}

  // rm
  // This is allowed to be an implicit constructor because SOperand is
  // a wrapper class that doesn't normally perform any type conversion.
  SOperand(SRegister rm)  // NOLINT(runtime/explicit)
      : NeonOperand(rm) {}
  SRegister GetRegister() const {
    VIXL_ASSERT(IsRegister() && (rm_.GetType() == CPURegister::kSRegister));
    return SRegister(rm_.GetCode());
  }
};

// DOperand represents either an immediate or a DRegister.
std::ostream& operator<<(std::ostream& os, const SOperand& operand);

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

  // #<immediate>
  // where <immediate> is a non zero floating point number which can be encoded
  // as an 8 bit floating point (checked by the constructor).
  // This is allowed to be an implicit constructor because DOperand is
  // a wrapper class that doesn't normally perform any type conversion.
  DOperand(float immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  DOperand(double immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}

  DOperand(const NeonImmediate& imm)  // NOLINT(runtime/explicit)
      : NeonOperand(imm) {}
  // rm
  // This is allowed to be an implicit constructor because DOperand is
  // a wrapper class that doesn't normally perform any type conversion.
  DOperand(DRegister rm)  // NOLINT(runtime/explicit)
      : NeonOperand(rm) {}

  DRegister GetRegister() const {
    VIXL_ASSERT(IsRegister() && (rm_.GetType() == CPURegister::kDRegister));
    return DRegister(rm_.GetCode());
  }
};

std::ostream& operator<<(std::ostream& os, const DOperand& operand);

// QOperand represents either an immediate or a QRegister.
class QOperand : public NeonOperand {
 public:
  // #<immediate>
  // where <immediate> is uint32_t.
  // This is allowed to be an implicit constructor because QOperand is
  // a wrapper class that doesn't normally perform any type conversion.
  QOperand(int32_t immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  QOperand(uint32_t immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  QOperand(int64_t immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  QOperand(uint64_t immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  QOperand(float immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}
  QOperand(double immediate)  // NOLINT(runtime/explicit)
      : NeonOperand(immediate) {}

  QOperand(const NeonImmediate& imm)  // NOLINT(runtime/explicit)
      : NeonOperand(imm) {}

  // rm
  // This is allowed to be an implicit constructor because QOperand is
  // a wrapper class that doesn't normally perform any type conversion.
  QOperand(QRegister rm)  // NOLINT(runtime/explicit)
      : NeonOperand(rm) {
    VIXL_ASSERT(rm_.IsValid());
  }

  QRegister GetRegister() const {
    VIXL_ASSERT(IsRegister() && (rm_.GetType() == CPURegister::kQRegister));
    return QRegister(rm_.GetCode());
  }
};

std::ostream& operator<<(std::ostream& os, const QOperand& operand);

class ImmediateVFP : public EncodingValue {
  template <typename T>
  struct FloatType {
    typedef T base_type;
  };

 public:
  explicit ImmediateVFP(const NeonImmediate& neon_imm) {
    if (neon_imm.IsFloat()) {
      const float imm = neon_imm.GetImmediate<float>();
      if (VFP::IsImmFP32(imm)) {
        SetEncodingValue(VFP::FP32ToImm8(imm));
      }
    } else if (neon_imm.IsDouble()) {
      const double imm = neon_imm.GetImmediate<double>();
      if (VFP::IsImmFP64(imm)) {
        SetEncodingValue(VFP::FP32ToImm8(imm));
      }
    }
  }

  template <typename T>
  static T Decode(uint32_t v) {
    return Decode(v, FloatType<T>());
  }

  static float Decode(uint32_t imm8, const FloatType<float>&) {
    return VFP::Imm8ToFP32(imm8);
  }

  static double Decode(uint32_t imm8, const FloatType<double>&) {
    return VFP::Imm8ToFP64(imm8);
  }
};


class ImmediateVbic : public EncodingValueAndImmediate {
 public:
  ImmediateVbic(DataType dt, const NeonImmediate& neon_imm);
  static DataType DecodeDt(uint32_t cmode);
  static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate);
};

class ImmediateVand : public ImmediateVbic {
 public:
  ImmediateVand(DataType dt, const NeonImmediate neon_imm)
      : ImmediateVbic(dt, neon_imm) {
    if (IsValid()) {
      SetEncodedImmediate(~GetEncodedImmediate() & 0xff);
    }
  }
};

class ImmediateVmov : public EncodingValueAndImmediate {
 public:
  ImmediateVmov(DataType dt, const NeonImmediate& neon_imm);
  static DataType DecodeDt(uint32_t cmode);
  static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate);
};

class ImmediateVmvn : public EncodingValueAndImmediate {
 public:
  ImmediateVmvn(DataType dt, const NeonImmediate& neon_imm);
  static DataType DecodeDt(uint32_t cmode);
  static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate);
};

class ImmediateVorr : public EncodingValueAndImmediate {
 public:
  ImmediateVorr(DataType dt, const NeonImmediate& neon_imm);
  static DataType DecodeDt(uint32_t cmode);
  static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate);
};

class ImmediateVorn : public ImmediateVorr {
 public:
  ImmediateVorn(DataType dt, const NeonImmediate& neon_imm)
      : ImmediateVorr(dt, neon_imm) {
    if (IsValid()) {
      SetEncodedImmediate(~GetEncodedImmediate() & 0xff);
    }
  }
};

// MemOperand represents the addressing mode of a load or store instruction.
//
//   Usage: <instr> <Rt> , <MemOperand>
//
//   where <instr> is the instruction to use (e.g., Ldr(), Str(), etc.),
//         <Rt> is general purpose register to be transferred,
//         <MemOperand> is the rest of the arguments to the instruction
//
//   <MemOperand> can be in one of 3 addressing modes:
//
//   [ <Rn>, <offset> ]   ==  offset addressing
//   [ <Rn>, <offset> ]!  ==  pre-indexed addressing
//   [ <Rn> ], <offset>   ==  post-indexed addressing
//
//   where <offset> can be one of:
//     - an immediate constant, such as <imm8>, <imm12>
//     - an index register <Rm>
//     - a shifted index register <Rm>, <shift> #<amount>
//
//   The index register may have an associated {+/-} sign,
//   which if ommitted, defaults to + .
//
//   We have two constructors for the offset:
//
//   One with a signed value offset parameter. The value of sign_ is
//   "sign_of(constructor's offset parameter) and the value of offset_ is
//   "constructor's offset parameter".
//
//   The other with a sign and a positive value offset parameters. The value of
//   sign_ is "constructor's sign parameter" and the value of offset_ is
//   "constructor's sign parameter * constructor's offset parameter".
//
//   The value of offset_ reflects the effective offset. For an offset_ of 0,
//   sign_ can be positive or negative. Otherwise, sign_ always agrees with
//   the sign of offset_.
class MemOperand {
 public:
  // rn
  // where rn is the general purpose base register only
  explicit MemOperand(Register rn, AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(0),
        sign_(plus),
        rm_(NoReg),
        shift_(LSL),
        shift_amount_(0),
        addrmode_(addrmode | kMemOperandRegisterOnly) {
    VIXL_ASSERT(rn_.IsValid());
  }

  // rn, #<imm>
  // where rn is the general purpose base register,
  //       <imm> is a 32-bit offset to add to rn
  //
  // Note: if rn is PC, then this form is equivalent to a "label"
  // Note: the second constructor allow minus zero (-0).
  MemOperand(Register rn, int32_t offset, AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(offset),
        sign_((offset < 0) ? minus : plus),
        rm_(NoReg),
        shift_(LSL),
        shift_amount_(0),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid());
  }
  MemOperand(Register rn, Sign sign, int32_t offset, AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(sign.IsPlus() ? offset : -offset),
        sign_(sign),
        rm_(NoReg),
        shift_(LSL),
        shift_amount_(0),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid());
    // With this constructor, the sign must only be specified by "sign".
    VIXL_ASSERT(offset >= 0);
  }

  // rn, {+/-}rm
  // where rn is the general purpose base register,
  //       {+/-} is the sign of the index register,
  //       rm is the general purpose index register,
  MemOperand(Register rn, Sign sign, Register rm, AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(0),
        sign_(sign),
        rm_(rm),
        shift_(LSL),
        shift_amount_(0),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid() && rm_.IsValid());
  }

  // rn, rm
  // where rn is the general purpose base register,
  //       rm is the general purpose index register,
  MemOperand(Register rn, Register rm, AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(0),
        sign_(plus),
        rm_(rm),
        shift_(LSL),
        shift_amount_(0),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid() && rm_.IsValid());
  }

  // rn, {+/-}rm, <shift>
  // where rn is the general purpose base register,
  //       {+/-} is the sign of the index register,
  //       rm is the general purpose index register,
  //       <shift> is RRX, applied to value from rm
  MemOperand(Register rn,
             Sign sign,
             Register rm,
             Shift shift,
             AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(0),
        sign_(sign),
        rm_(rm),
        shift_(shift),
        shift_amount_(0),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid() && rm_.IsValid());
    VIXL_ASSERT(shift_.IsRRX());
  }

  // rn, rm, <shift>
  // where rn is the general purpose base register,
  //       rm is the general purpose index register,
  //       <shift> is RRX, applied to value from rm
  MemOperand(Register rn, Register rm, Shift shift, AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(0),
        sign_(plus),
        rm_(rm),
        shift_(shift),
        shift_amount_(0),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid() && rm_.IsValid());
    VIXL_ASSERT(shift_.IsRRX());
  }

  // rn, {+/-}rm, <shift> #<amount>
  // where rn is the general purpose base register,
  //       {+/-} is the sign of the index register,
  //       rm is the general purpose index register,
  //       <shift> is one of {LSL, LSR, ASR, ROR}, applied to value from rm
  //       <shift_amount> is optional size to apply to value from rm
  MemOperand(Register rn,
             Sign sign,
             Register rm,
             Shift shift,
             uint32_t shift_amount,
             AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(0),
        sign_(sign),
        rm_(rm),
        shift_(shift),
        shift_amount_(shift_amount),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid() && rm_.IsValid());
    CheckShift();
  }

  // rn, rm, <shift> #<amount>
  // where rn is the general purpose base register,
  //       rm is the general purpose index register,
  //       <shift> is one of {LSL, LSR, ASR, ROR}, applied to value from rm
  //       <shift_amount> is optional size to apply to value from rm
  MemOperand(Register rn,
             Register rm,
             Shift shift,
             uint32_t shift_amount,
             AddrMode addrmode = Offset)
      : rn_(rn),
        offset_(0),
        sign_(plus),
        rm_(rm),
        shift_(shift),
        shift_amount_(shift_amount),
        addrmode_(addrmode) {
    VIXL_ASSERT(rn_.IsValid() && rm_.IsValid());
    CheckShift();
  }

  Register GetBaseRegister() const { return rn_; }
  int32_t GetOffsetImmediate() const { return offset_; }
  bool IsOffsetImmediateWithinRange(int min,
                                    int max,
                                    int multiple_of = 1) const {
    return (offset_ >= min) && (offset_ <= max) &&
           ((offset_ % multiple_of) == 0);
  }
  Sign GetSign() const { return sign_; }
  Register GetOffsetRegister() const { return rm_; }
  Shift GetShift() const { return shift_; }
  unsigned GetShiftAmount() const { return shift_amount_; }
  AddrMode GetAddrMode() const {
    return static_cast<AddrMode>(addrmode_ & kMemOperandAddrModeMask);
  }
  bool IsRegisterOnly() const {
    return (addrmode_ & kMemOperandRegisterOnly) != 0;
  }

  bool IsImmediate() const { return !rm_.IsValid(); }
  bool IsImmediateZero() const { return !rm_.IsValid() && (offset_ == 0); }
  bool IsPlainRegister() const {
    return rm_.IsValid() && shift_.IsLSL() && (shift_amount_ == 0);
  }
  bool IsShiftedRegister() const { return rm_.IsValid(); }
  bool IsImmediateOffset() const {
    return (GetAddrMode() == Offset) && !rm_.IsValid();
  }
  bool IsImmediateZeroOffset() const {
    return (GetAddrMode() == Offset) && !rm_.IsValid() && (offset_ == 0);
  }
  bool IsRegisterOffset() const {
    return (GetAddrMode() == Offset) && rm_.IsValid() && shift_.IsLSL() &&
           (shift_amount_ == 0);
  }
  bool IsShiftedRegisterOffset() const {
    return (GetAddrMode() == Offset) && rm_.IsValid();
  }
  uint32_t GetTypeEncodingValue() const {
    return shift_.IsRRX() ? kRRXEncodedValue : shift_.GetValue();
  }
  bool IsOffset() const { return GetAddrMode() == Offset; }
  bool IsPreIndex() const { return GetAddrMode() == PreIndex; }
  bool IsPostIndex() const { return GetAddrMode() == PostIndex; }
  bool IsShiftValid() const { return shift_.IsValidAmount(shift_amount_); }

 private:
  static const int kMemOperandRegisterOnly = 0x1000;
  static const int kMemOperandAddrModeMask = 0xfff;
  void CheckShift() {
#ifdef VIXL_DEBUG
    // Disallow any zero shift other than RRX #0 and LSL #0 .
    if ((shift_amount_ == 0) && shift_.IsRRX()) return;
    if ((shift_amount_ == 0) && !shift_.IsLSL()) {
      VIXL_ABORT_WITH_MSG(
          "A shift by 0 is only accepted in "
          "the case of lsl and will be treated as "
          "no shift.\n");
    }
    switch (shift_.GetType()) {
      case LSL:
        VIXL_ASSERT(shift_amount_ <= 31);
        break;
      case ROR:
        VIXL_ASSERT(shift_amount_ <= 31);
        break;
      case LSR:
      case ASR:
        VIXL_ASSERT(shift_amount_ <= 32);
        break;
      case RRX:
      default:
        VIXL_UNREACHABLE();
        break;
    }
#endif
  }
  Register rn_;
  int32_t offset_;
  Sign sign_;
  Register rm_;
  Shift shift_;
  uint32_t shift_amount_;
  uint32_t addrmode_;
};

std::ostream& operator<<(std::ostream& os, const MemOperand& operand);

class AlignedMemOperand : public MemOperand {
 public:
  AlignedMemOperand(Register rn, Alignment align, AddrMode addrmode = Offset)
      : MemOperand(rn, addrmode), align_(align) {
    VIXL_ASSERT(addrmode != PreIndex);
  }

  AlignedMemOperand(Register rn,
                    Alignment align,
                    Register rm,
                    AddrMode addrmode)
      : MemOperand(rn, rm, addrmode), align_(align) {
    VIXL_ASSERT(addrmode != PreIndex);
  }

  Alignment GetAlignment() const { return align_; }

 private:
  Alignment align_;
};

std::ostream& operator<<(std::ostream& os, const AlignedMemOperand& operand);

}  // namespace aarch32
}  // namespace vixl

#endif  // VIXL_AARCH32_OPERANDS_AARCH32_H_