// Copyright 2014, 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_EXAMPLES_NON_CONST_VISITOR_H_
#define VIXL_EXAMPLES_NON_CONST_VISITOR_H_

using namespace vixl::aarch64;

class SwitchAddSubRegisterSources : public DecoderVisitor {
 public:
  SwitchAddSubRegisterSources()
      : DecoderVisitor(DecoderVisitor::kNonConstVisitor) {}

  // Our visitor switches the register sources for some add and sub instructions
  // (not all add and sub instructions). Visitors are listed by the macro
  // `VISITOR_LIST` in aarch64/decoder-aarch64.h.
  virtual void VisitAddSubShifted(const Instruction* instr) VIXL_OVERRIDE {
    int rn = instr->GetRn();
    int rm = instr->GetRm();
    // Only non-const visitors are allowed to discard constness of the visited
    // instruction.
    Instruction* mutable_instr = MutableInstruction(instr);
    Instr instr_bits = mutable_instr->GetInstructionBits();

    // Switch the bitfields for the `rn` and `rm` registers.
    instr_bits &= ~(Rn_mask | Rm_mask);
    instr_bits |= (rn << Rm_offset) | (rm << Rn_offset);

    // Rewrite the instruction.
    mutable_instr->SetInstructionBits(instr_bits);
  }

// Define the remaining visitors to do nothing.
#define UNUSED_VISITOR_LIST(V)          \
  V(PCRelAddressing)                    \
  V(AddSubImmediate)                    \
  V(LogicalImmediate)                   \
  V(MoveWideImmediate)                  \
  V(Bitfield)                           \
  V(Extract)                            \
  V(UnconditionalBranch)                \
  V(UnconditionalBranchToRegister)      \
  V(CompareBranch)                      \
  V(TestBranch)                         \
  V(ConditionalBranch)                  \
  V(System)                             \
  V(Exception)                          \
  V(LoadStorePairPostIndex)             \
  V(LoadStorePairOffset)                \
  V(LoadStorePairPreIndex)              \
  V(LoadStorePairNonTemporal)           \
  V(LoadLiteral)                        \
  V(LoadStoreUnscaledOffset)            \
  V(LoadStorePostIndex)                 \
  V(LoadStorePreIndex)                  \
  V(LoadStoreRegisterOffset)            \
  V(LoadStoreUnsignedOffset)            \
  V(LoadStoreExclusive)                 \
  V(LogicalShifted)                     \
  V(AddSubExtended)                     \
  V(AddSubWithCarry)                    \
  V(ConditionalCompareRegister)         \
  V(ConditionalCompareImmediate)        \
  V(ConditionalSelect)                  \
  V(DataProcessing1Source)              \
  V(DataProcessing2Source)              \
  V(DataProcessing3Source)              \
  V(FPCompare)                          \
  V(FPConditionalCompare)               \
  V(FPConditionalSelect)                \
  V(FPImmediate)                        \
  V(FPDataProcessing1Source)            \
  V(FPDataProcessing2Source)            \
  V(FPDataProcessing3Source)            \
  V(FPIntegerConvert)                   \
  V(FPFixedPointConvert)                \
  V(Crypto2RegSHA)                      \
  V(Crypto3RegSHA)                      \
  V(CryptoAES)                          \
  V(NEON2RegMisc)                       \
  V(NEON3Different)                     \
  V(NEON3Same)                          \
  V(NEONAcrossLanes)                    \
  V(NEONByIndexedElement)               \
  V(NEONCopy)                           \
  V(NEONExtract)                        \
  V(NEONLoadStoreMultiStruct)           \
  V(NEONLoadStoreMultiStructPostIndex)  \
  V(NEONLoadStoreSingleStruct)          \
  V(NEONLoadStoreSingleStructPostIndex) \
  V(NEONModifiedImmediate)              \
  V(NEONScalar2RegMisc)                 \
  V(NEONScalar3Diff)                    \
  V(NEONScalar3Same)                    \
  V(NEONScalarByIndexedElement)         \
  V(NEONScalarCopy)                     \
  V(NEONScalarPairwise)                 \
  V(NEONScalarShiftImmediate)           \
  V(NEONShiftImmediate)                 \
  V(NEONTable)                          \
  V(NEONPerm)                           \
  V(Unallocated)                        \
  V(Unimplemented)
#define DEFINE_UNUSED_VISITOR(Name)                                  \
  virtual void Visit##Name(const Instruction* i) VIXL_OVERRIDE {     \
    USE(i); /* Prevents compiler warnings about unused variables. */ \
  }
  UNUSED_VISITOR_LIST(DEFINE_UNUSED_VISITOR)
#undef DEFINE_UNUSED_VISITOR
#undef UNUSED_VISITOR_LIST
};


void GenerateNonConstVisitorTestCode(MacroAssembler* masm);

int64_t RunNonConstVisitorTestGeneratedCode(const Instruction* start_instr);

void ModifyNonConstVisitorTestGeneratedCode(Instruction* start,
                                            Instruction* end);


#endif