//===-- MCInstrDescView.h ---------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \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 <random>

#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 exegesis {

struct Operand; // forward declaration.

// A variable represents the value associated to an Operand or a set of Operands
// if they are tied together.
struct Variable {
  // The indices of the operands tied to this Variable.
  llvm::SmallVector<unsigned, 2> TiedOperands;
  llvm::MCOperand AssignedValue;
  // The index of this Variable in Instruction.Variables and its associated
  // Value in InstructionBuilder.VariableValues.
  unsigned Index = -1;
};

// 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 {
  unsigned Index = 0;
  bool IsDef = false;
  bool IsMem = false;
  bool IsExplicit = false;
  const RegisterAliasingTracker *Tracker = nullptr; // Set for Register Op.
  const llvm::MCOperandInfo *Info = nullptr;        // Set for Explicit Op.
  int TiedToIndex = -1;                             // Set for Reg&Explicit Op.
  const llvm::MCPhysReg *ImplicitReg = nullptr;     // Set for Implicit Op.
  int VariableIndex = -1;                           // Set for Explicit Op.
};

// A view over an MCInstrDesc offering a convenient interface to compute
// Register aliasing.
struct Instruction {
  Instruction(const llvm::MCInstrDesc &MCInstrDesc,
              const RegisterAliasingTrackerCache &ATC);

  bool hasMemoryOperands() const;

  const llvm::MCInstrDesc *Description; // Never nullptr.
  llvm::SmallVector<Operand, 8> Operands;
  llvm::SmallVector<Variable, 4> Variables;
  llvm::BitVector DefRegisters; // The union of the aliased def registers.
  llvm::BitVector UseRegisters; // The union of the aliased use registers.
};

// A builder for an Instruction holding values for each of its Variables.
struct InstructionBuilder {
  InstructionBuilder(const Instruction &Instr);

  InstructionBuilder(const InstructionBuilder &);            // default
  InstructionBuilder &operator=(const InstructionBuilder &); // default
  InstructionBuilder(InstructionBuilder &&);                 // default
  InstructionBuilder &operator=(InstructionBuilder &&);      // default

  unsigned getOpcode() const;
  llvm::MCOperand &getValueFor(const Variable &Var);
  const llvm::MCOperand &getValueFor(const Variable &Var) const;
  llvm::MCOperand &getValueFor(const Operand &Op);
  const llvm::MCOperand &getValueFor(const Operand &Op) const;
  bool hasImmediateVariables() const;

  // Assigns a Random Value to all Variables that are still Invalid.
  // Do not use any of the registers in `ForbiddenRegs`.
  void randomizeUnsetVariables(const llvm::BitVector &ForbiddenRegs);

  // Builds an llvm::MCInst from this InstructionBuilder setting its operands to
  // the corresponding variable values.
  // Precondition: All VariableValues must be set.
  llvm::MCInst build() const;

  Instruction Instr;
  llvm::SmallVector<llvm::MCOperand, 4> VariableValues;
};

// A CodeTemplate is a set of InstructionBuilders that may not be fully
// specified (i.e. some variables are not yet set).
// This allows the BenchmarkRunner to instantiate it many times with specific
// values to study their impact on instruction's performance.
struct CodeTemplate {
  CodeTemplate() = default;

  CodeTemplate(CodeTemplate &&);            // default
  CodeTemplate &operator=(CodeTemplate &&); // default
  CodeTemplate(const CodeTemplate &) = delete;
  CodeTemplate &operator=(const CodeTemplate &) = delete;

  // Some information about how this template has been created.
  std::string Info;
  // The list of the instructions for this template.
  std::vector<InstructionBuilder> Instructions;
  // If the template uses the provided scratch memory, the register in which
  // the pointer to this memory is passed in to the function.
  unsigned ScratchSpacePointerInReg = 0;
};

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

  const Operand *Op; // Pointer to an Explicit Register Operand.
  llvm::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 {
  llvm::SmallVector<RegisterOperandAssignment, 1> Defs; // Unlikely size() > 1.
  llvm::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;
  void setExplicitAliasing() const;

  const Instruction &DefInstruction;
  const Instruction &UseInstruction;
  llvm::SmallVector<AliasingRegisterOperands, 32> Configurations;
};

// A global Random Number Generator to randomize configurations.
// FIXME: Move random number generation into an object and make it seedable for
// unit tests.
std::mt19937 &randomGenerator();

// Picks a random bit among the bits set in Vector and returns its index.
// Precondition: Vector must have at least one bit set.
size_t randomBit(const llvm::BitVector &Vector);

// Picks a random configuration, then selects a random def and a random use from
// it and finally set the selected values in the provided InstructionInstances.
void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations,
                       InstructionBuilder &DefIB, InstructionBuilder &UseIB);

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

} // namespace exegesis

#endif // LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
