//===- InstrDocsEmitter.cpp - Opcode Documentation Generator --------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// InstrDocsEmitter generates restructured text documentation for the opcodes
// that can be used by MachineInstr. For each opcode, the documentation lists:
// * Opcode name
// * Assembly string
// * Flags (e.g. mayLoad, isBranch, ...)
// * Operands, including type and name
// * Operand constraints
// * Implicit register uses & defs
// * Predicates
//
//===----------------------------------------------------------------------===//

#include "CodeGenDAGPatterns.h"
#include "CodeGenInstruction.h"
#include "CodeGenTarget.h"
#include "TableGenBackends.h"
#include "llvm/TableGen/Record.h"
#include <string>
#include <vector>

using namespace llvm;

namespace llvm {

void writeTitle(StringRef Str, raw_ostream &OS, char Kind = '-') {
  OS << std::string(Str.size(), Kind) << "\n" << Str << "\n"
     << std::string(Str.size(), Kind) << "\n";
}

void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
  OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
}

std::string escapeForRST(StringRef Str) {
  std::string Result;
  Result.reserve(Str.size() + 4);
  for (char C : Str) {
    switch (C) {
    // We want special characters to be shown as their C escape codes.
    case '\n': Result += "\\n"; break;
    case '\t': Result += "\\t"; break;
    // Underscore at the end of a line has a special meaning in rst.
    case '_': Result += "\\_"; break;
    default: Result += C;
    }
  }
  return Result;
}

void EmitInstrDocs(RecordKeeper &RK, raw_ostream &OS) {
  CodeGenDAGPatterns CDP(RK);
  CodeGenTarget &Target = CDP.getTargetInfo();
  unsigned VariantCount = Target.getAsmParserVariantCount();

  // Page title.
  std::string Title = Target.getName();
  Title += " Instructions";
  writeTitle(Title, OS);
  OS << "\n";

  for (const CodeGenInstruction *II : Target.getInstructionsByEnumValue()) {
    Record *Inst = II->TheDef;

    // Don't print the target-independent instructions.
    if (II->Namespace == "TargetOpcode")
      continue;

    // Heading (instruction name).
    writeHeader(escapeForRST(Inst->getName()), OS, '=');
    OS << "\n";

    // Assembly string(s).
    if (!II->AsmString.empty()) {
      for (unsigned VarNum = 0; VarNum < VariantCount; ++VarNum) {
        Record *AsmVariant = Target.getAsmParserVariant(VarNum);
        OS << "Assembly string";
        if (VariantCount != 1)
          OS << " (" << AsmVariant->getValueAsString("Name") << ")";
        std::string AsmString =
            CodeGenInstruction::FlattenAsmStringVariants(II->AsmString, VarNum);
        // We trim spaces at each end of the asm string because rst needs the
        // formatting backticks to be next to a non-whitespace character.
        OS << ": ``" << escapeForRST(StringRef(AsmString).trim(" "))
           << "``\n\n";
      }
    }

    // Boolean flags.
    std::vector<const char *> FlagStrings;
#define xstr(s) str(s)
#define str(s) #s
#define FLAG(f) if (II->f) { FlagStrings.push_back(str(f)); }
    FLAG(isReturn)
    FLAG(isEHScopeReturn)
    FLAG(isBranch)
    FLAG(isIndirectBranch)
    FLAG(isCompare)
    FLAG(isMoveImm)
    FLAG(isBitcast)
    FLAG(isSelect)
    FLAG(isBarrier)
    FLAG(isCall)
    FLAG(isAdd)
    FLAG(isTrap)
    FLAG(canFoldAsLoad)
    FLAG(mayLoad)
    //FLAG(mayLoad_Unset) // Deliberately omitted.
    FLAG(mayStore)
    //FLAG(mayStore_Unset) // Deliberately omitted.
    FLAG(isPredicable)
    FLAG(isConvertibleToThreeAddress)
    FLAG(isCommutable)
    FLAG(isTerminator)
    FLAG(isReMaterializable)
    FLAG(hasDelaySlot)
    FLAG(usesCustomInserter)
    FLAG(hasPostISelHook)
    FLAG(hasCtrlDep)
    FLAG(isNotDuplicable)
    FLAG(hasSideEffects)
    //FLAG(hasSideEffects_Unset) // Deliberately omitted.
    FLAG(isAsCheapAsAMove)
    FLAG(hasExtraSrcRegAllocReq)
    FLAG(hasExtraDefRegAllocReq)
    FLAG(isCodeGenOnly)
    FLAG(isPseudo)
    FLAG(isRegSequence)
    FLAG(isExtractSubreg)
    FLAG(isInsertSubreg)
    FLAG(isConvergent)
    FLAG(hasNoSchedulingInfo)
    FLAG(variadicOpsAreDefs)
    if (!FlagStrings.empty()) {
      OS << "Flags: ";
      bool IsFirst = true;
      for (auto FlagString : FlagStrings) {
        if (!IsFirst)
          OS << ", ";
        OS << "``" << FlagString << "``";
        IsFirst = false;
      }
      OS << "\n\n";
    }

    // Operands.
    for (unsigned i = 0; i < II->Operands.size(); ++i) {
      bool IsDef = i < II->Operands.NumDefs;
      auto Op = II->Operands[i];

      if (Op.MINumOperands > 1) {
        // This operand corresponds to multiple operands on the
        // MachineInstruction, so print all of them, showing the types and
        // names of both the compound operand and the basic operands it
        // contains.
        for (unsigned SubOpIdx = 0; SubOpIdx < Op.MINumOperands; ++SubOpIdx) {
          Record *SubRec =
              cast<DefInit>(Op.MIOperandInfo->getArg(SubOpIdx))->getDef();
          StringRef SubOpName = Op.MIOperandInfo->getArgNameStr(SubOpIdx);
          StringRef SubOpTypeName = SubRec->getName();

          OS << "* " << (IsDef ? "DEF" : "USE") << " ``" << Op.Rec->getName()
             << "/" << SubOpTypeName << ":$" << Op.Name << ".";
          // Not all sub-operands are named, make up a name for these.
          if (SubOpName.empty())
            OS << "anon" << SubOpIdx;
          else
            OS << SubOpName;
          OS << "``\n\n";
        }
      } else {
        // The operand corresponds to only one MachineInstruction operand.
        OS << "* " << (IsDef ? "DEF" : "USE") << " ``" << Op.Rec->getName()
           << ":$" << Op.Name << "``\n\n";
      }
    }

    // Constraints.
    StringRef Constraints = Inst->getValueAsString("Constraints");
    if (!Constraints.empty()) {
      OS << "Constraints: ``" << Constraints << "``\n\n";
    }

    // Implicit definitions.
    if (!II->ImplicitDefs.empty()) {
      OS << "Implicit defs: ";
      bool IsFirst = true;
      for (Record *Def : II->ImplicitDefs) {
        if (!IsFirst)
          OS << ", ";
        OS << "``" << Def->getName() << "``";
        IsFirst = false;
      }
      OS << "\n\n";
    }

    // Implicit uses.
    if (!II->ImplicitUses.empty()) {
      OS << "Implicit uses: ";
      bool IsFirst = true;
      for (Record *Use : II->ImplicitUses) {
        if (!IsFirst)
          OS << ", ";
        OS << "``" << Use->getName() << "``";
        IsFirst = false;
      }
      OS << "\n\n";
    }

    // Predicates.
    std::vector<Record *> Predicates =
        II->TheDef->getValueAsListOfDefs("Predicates");
    if (!Predicates.empty()) {
      OS << "Predicates: ";
      bool IsFirst = true;
      for (Record *P : Predicates) {
        if (!IsFirst)
          OS << ", ";
        OS << "``" << P->getName() << "``";
        IsFirst = false;
      }
      OS << "\n\n";
    }
  }
}

} // end namespace llvm
