//===-- MCInstrDescView.h ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Provide views around LLVM structures to represents an instruction instance,
/// as well as its implicit and explicit arguments in a uniform way.
/// Arguments that are explicit and independant (non tied) also have a Variable
/// associated to them so the instruction can be fully defined by reading its
/// Variables.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
#define LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H

#include <memory>
#include <random>
#include <unordered_map>

#include "RegisterAliasing.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"

namespace llvm {
namespace exegesis {

// A variable represents the value associated to an Operand or a set of Operands
// if they are tied together.
struct Variable {
  // Returns the index of this Variable inside Instruction's Variable.
  unsigned getIndex() const;

  // Returns the index of the Operand linked to this Variable.
  unsigned getPrimaryOperandIndex() const;

  // Returns whether this Variable has more than one Operand linked to it.
  bool hasTiedOperands() const;

  // The indices of the operands tied to this Variable.
  SmallVector<unsigned, 2> TiedOperands;

  // The index of this Variable in Instruction.Variables and its associated
  // Value in InstructionBuilder.VariableValues.
  Optional<uint8_t> Index;
};

// MCOperandInfo can only represents Explicit operands. This object gives a
// uniform view of Implicit and Explicit Operands.
// - Index: can be used to refer to MCInstrDesc::operands for Explicit operands.
// - Tracker: is set for Register Operands and is used to keep track of possible
// registers and the registers reachable from them (aliasing registers).
// - Info: a shortcut for MCInstrDesc::operands()[Index].
// - TiedToIndex: the index of the Operand holding the value or -1.
// - ImplicitReg: a pointer to the register value when Operand is Implicit,
// nullptr otherwise.
// - VariableIndex: the index of the Variable holding the value for this Operand
// or -1 if this operand is implicit.
struct Operand {
  bool isExplicit() const;
  bool isImplicit() const;
  bool isImplicitReg() const;
  bool isDef() const;
  bool isUse() const;
  bool isReg() const;
  bool isTied() const;
  bool isVariable() const;
  bool isMemory() const;
  bool isImmediate() const;
  unsigned getIndex() const;
  unsigned getTiedToIndex() const;
  unsigned getVariableIndex() const;
  unsigned getImplicitReg() const;
  const RegisterAliasingTracker &getRegisterAliasing() const;
  const MCOperandInfo &getExplicitOperandInfo() const;

  // Please use the accessors above and not the following fields.
  Optional<uint8_t> Index;
  bool IsDef = false;
  const RegisterAliasingTracker *Tracker = nullptr; // Set for Register Op.
  const MCOperandInfo *Info = nullptr;              // Set for Explicit Op.
  Optional<uint8_t> TiedToIndex;                    // Set for Reg&Explicit Op.
  const MCPhysReg *ImplicitReg = nullptr;           // Set for Implicit Op.
  Optional<uint8_t> VariableIndex;                  // Set for Explicit Op.
};

/// A cache of BitVector to reuse between Instructions.
/// The cache will only be exercised during Instruction initialization.
/// For X86, this is ~160 unique vectors for all of the ~15K Instructions.
struct BitVectorCache {
  // Finds or allocates the provided BitVector in the cache and retrieves it's
  // unique instance.
  const BitVector *getUnique(BitVector &&BV) const;

private:
  mutable std::vector<std::unique_ptr<BitVector>> Cache;
};

// A view over an MCInstrDesc offering a convenient interface to compute
// Register aliasing.
struct Instruction {
  // Create an instruction for a particular Opcode.
  static std::unique_ptr<Instruction>
  create(const MCInstrInfo &InstrInfo, const RegisterAliasingTrackerCache &RATC,
         const BitVectorCache &BVC, unsigned Opcode);

  // Prevent copy or move, instructions are allocated once and cached.
  Instruction(const Instruction &) = delete;
  Instruction(Instruction &&) = delete;
  Instruction &operator=(const Instruction &) = delete;
  Instruction &operator=(Instruction &&) = delete;

  // Returns the Operand linked to this Variable.
  // In case the Variable is tied, the primary (i.e. Def) Operand is returned.
  const Operand &getPrimaryOperand(const Variable &Var) const;

  // Whether this instruction is self aliasing through its tied registers.
  // Repeating this instruction is guaranteed to executes sequentially.
  bool hasTiedRegisters() const;

  // Whether this instruction is self aliasing through its implicit registers.
  // Repeating this instruction is guaranteed to executes sequentially.
  bool hasAliasingImplicitRegisters() const;

  // Whether this instruction is self aliasing through some registers.
  // Repeating this instruction may execute sequentially by picking aliasing
  // Use and Def registers. It may also execute in parallel by picking non
  // aliasing Use and Def registers.
  bool hasAliasingRegisters(const BitVector &ForbiddenRegisters) const;

  // Whether this instruction's registers alias with OtherInstr's registers.
  bool hasAliasingRegistersThrough(const Instruction &OtherInstr,
                                   const BitVector &ForbiddenRegisters) const;

  // Returns whether this instruction has Memory Operands.
  // Repeating this instruction executes sequentially with an instruction that
  // reads or write the same memory region.
  bool hasMemoryOperands() const;

  // Returns whether this instruction as at least one use or one def.
  // Repeating this instruction may execute sequentially by adding an
  // instruction that aliases one of these.
  bool hasOneUseOrOneDef() const;

  // Convenient function to help with debugging.
  void dump(const MCRegisterInfo &RegInfo,
            const RegisterAliasingTrackerCache &RATC,
            raw_ostream &Stream) const;

  const MCInstrDesc &Description;
  const StringRef Name; // The name of this instruction.
  const SmallVector<Operand, 8> Operands;
  const SmallVector<Variable, 4> Variables;
  const BitVector &ImplDefRegs; // The set of aliased implicit def registers.
  const BitVector &ImplUseRegs; // The set of aliased implicit use registers.
  const BitVector &AllDefRegs;  // The set of all aliased def registers.
  const BitVector &AllUseRegs;  // The set of all aliased use registers.
private:
  Instruction(const MCInstrDesc *Description, StringRef Name,
              SmallVector<Operand, 8> Operands,
              SmallVector<Variable, 4> Variables, const BitVector *ImplDefRegs,
              const BitVector *ImplUseRegs, const BitVector *AllDefRegs,
              const BitVector *AllUseRegs);
};

// Instructions are expensive to instantiate. This class provides a cache of
// Instructions with lazy construction.
struct InstructionsCache {
  InstructionsCache(const MCInstrInfo &InstrInfo,
                    const RegisterAliasingTrackerCache &RATC);

  // Returns the Instruction object corresponding to this Opcode.
  const Instruction &getInstr(unsigned Opcode) const;

private:
  const MCInstrInfo &InstrInfo;
  const RegisterAliasingTrackerCache &RATC;
  mutable std::unordered_map<unsigned, std::unique_ptr<Instruction>>
      Instructions;
  const BitVectorCache BVC;
};

// Represents the assignment of a Register to an Operand.
struct RegisterOperandAssignment {
  RegisterOperandAssignment(const Operand *Operand, MCPhysReg Reg)
      : Op(Operand), Reg(Reg) {}

  const Operand *Op; // Pointer to an Explicit Register Operand.
  MCPhysReg Reg;

  bool operator==(const RegisterOperandAssignment &other) const;
};

// Represents a set of Operands that would alias through the use of some
// Registers.
// There are two reasons why operands would alias:
// - The registers assigned to each of the operands are the same or alias each
//   other (e.g. AX/AL)
// - The operands are tied.
struct AliasingRegisterOperands {
  SmallVector<RegisterOperandAssignment, 1> Defs; // Unlikely size() > 1.
  SmallVector<RegisterOperandAssignment, 2> Uses;

  // True is Defs and Use contain an Implicit Operand.
  bool hasImplicitAliasing() const;

  bool operator==(const AliasingRegisterOperands &other) const;
};

// Returns all possible configurations leading Def registers of DefInstruction
// to alias with Use registers of UseInstruction.
struct AliasingConfigurations {
  AliasingConfigurations(const Instruction &DefInstruction,
                         const Instruction &UseInstruction);

  bool empty() const; // True if no aliasing configuration is found.
  bool hasImplicitAliasing() const;

  SmallVector<AliasingRegisterOperands, 32> Configurations;
};

// Writes MCInst to OS.
// This is not assembly but the internal LLVM's name for instructions and
// registers.
void DumpMCInst(const MCRegisterInfo &MCRegisterInfo,
                const MCInstrInfo &MCInstrInfo, const MCInst &MCInst,
                raw_ostream &OS);

} // namespace exegesis
} // namespace llvm

#endif // LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
