Index: llvm/trunk/utils/TableGen/CMakeLists.txt =================================================================== --- llvm/trunk/utils/TableGen/CMakeLists.txt +++ llvm/trunk/utils/TableGen/CMakeLists.txt @@ -26,6 +26,7 @@ GlobalISelEmitter.cpp InfoByHwMode.cpp InstrInfoEmitter.cpp + InstrDocsEmitter.cpp IntrinsicEmitter.cpp OptParserEmitter.cpp PseudoLoweringEmitter.cpp Index: llvm/trunk/utils/TableGen/InstrDocsEmitter.cpp =================================================================== --- llvm/trunk/utils/TableGen/InstrDocsEmitter.cpp +++ llvm/trunk/utils/TableGen/InstrDocsEmitter.cpp @@ -0,0 +1,232 @@ +//===- InstrDocsEmitter.cpp - Opcode Documentation Generator --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// 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 +#include + +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 FlagStrings; +#define xstr(s) str(s) +#define str(s) #s +#define FLAG(f) if (II->f) { FlagStrings.push_back(str(f)); } + FLAG(isReturn) + FLAG(isBranch) + FLAG(isIndirectBranch) + FLAG(isCompare) + FLAG(isMoveImm) + FLAG(isBitcast) + FLAG(isSelect) + FLAG(isBarrier) + FLAG(isCall) + FLAG(isAdd) + 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) + 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(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 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 llvm namespace Index: llvm/trunk/utils/TableGen/TableGen.cpp =================================================================== --- llvm/trunk/utils/TableGen/TableGen.cpp +++ llvm/trunk/utils/TableGen/TableGen.cpp @@ -28,6 +28,7 @@ GenEmitter, GenRegisterInfo, GenInstrInfo, + GenInstrDocs, GenAsmWriter, GenAsmMatcher, GenDisassembler, @@ -62,6 +63,8 @@ "Generate registers and register classes info"), clEnumValN(GenInstrInfo, "gen-instr-info", "Generate instruction descriptions"), + clEnumValN(GenInstrDocs, "gen-instr-docs", + "Generate instruction documentation"), clEnumValN(GenCallingConv, "gen-callingconv", "Generate calling convention descriptions"), clEnumValN(GenAsmWriter, "gen-asm-writer", @@ -124,6 +127,9 @@ case GenInstrInfo: EmitInstrInfo(Records, OS); break; + case GenInstrDocs: + EmitInstrDocs(Records, OS); + break; case GenCallingConv: EmitCallingConv(Records, OS); break; Index: llvm/trunk/utils/TableGen/TableGenBackends.h =================================================================== --- llvm/trunk/utils/TableGen/TableGenBackends.h +++ llvm/trunk/utils/TableGen/TableGenBackends.h @@ -72,6 +72,7 @@ void EmitDisassembler(RecordKeeper &RK, raw_ostream &OS); void EmitFastISel(RecordKeeper &RK, raw_ostream &OS); void EmitInstrInfo(RecordKeeper &RK, raw_ostream &OS); +void EmitInstrDocs(RecordKeeper &RK, raw_ostream &OS); void EmitPseudoLowering(RecordKeeper &RK, raw_ostream &OS); void EmitRegisterInfo(RecordKeeper &RK, raw_ostream &OS); void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS);