summaryrefslogtreecommitdiff
path: root/llvm/utils/TableGen/InstrDocsEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/utils/TableGen/InstrDocsEmitter.cpp')
-rw-r--r--llvm/utils/TableGen/InstrDocsEmitter.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/llvm/utils/TableGen/InstrDocsEmitter.cpp b/llvm/utils/TableGen/InstrDocsEmitter.cpp
new file mode 100644
index 000000000000..45fa936b9574
--- /dev/null
+++ b/llvm/utils/TableGen/InstrDocsEmitter.cpp
@@ -0,0 +1,234 @@
+//===- 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