Index: llvm/docs/TableGen/BackGuide.rst =================================================================== --- llvm/docs/TableGen/BackGuide.rst +++ llvm/docs/TableGen/BackGuide.rst @@ -690,11 +690,9 @@ ``RecordVal``, and ``Init``. -A constant and two helper functions are provided for producing the output -file. The constant ``MAX_LINE_LEN`` specifies the maximum length of output -lines. The helper function ``printLine`` prints a horizontal line comment. -The helper function ``emitSourceFileHeader`` prints the header comment that -should be included at the top of every output file. +The helper function ``emitSourceFileHeader()`` prints the header comment +that should be included at the top of every output file. A call to it is +included in the skeleton backend file ``TableGenBackendSkeleton.cpp``. Printing Error Messages ======================= @@ -780,9 +778,53 @@ fields. Note that anonymous records are named ``anonymous_0``, ``anonymous_1``, etc. - - The ``PrintDetailedRecords`` Backend ------------------------------------ -[to come] +The TableGen command option ``--print-detailed-records`` invokes a backend +that prints all the global variables, classes, and records defined in the +source files. The output looks like this. + +.. code-block:: text + + DETAILED RECORDS for file llvm-project\llvm\lib\target\arc\arc.td + + -------------------- Global Variables (5) -------------------- + + AMDGPUBufferIntrinsics = [int_amdgcn_buffer_load_format, ... + AMDGPUImageDimAtomicIntrinsics = [int_amdgcn_image_atomic_swap_1d, ... + ... + -------------------- Classes (758) -------------------- + + AMDGPUBufferLoad |IntrinsicsAMDGPU.td:879| + Template args: + LLVMType AMDGPUBufferLoad:data_ty = llvm_any_ty |IntrinsicsAMDGPU.td:879| + Superclasses: (SDPatternOperator) Intrinsic AMDGPURsrcIntrinsic + Fields: + list Properties = [SDNPMemOperand] |Intrinsics.td:348| + string LLVMName = "" |Intrinsics.td:343| + ... + -------------------- Records (12303) -------------------- + + AMDGPUSample_lz_o |IntrinsicsAMDGPU.td:560| + Defm sequence: |IntrinsicsAMDGPU.td:584| |IntrinsicsAMDGPU.td:566| + Superclasses: AMDGPUSampleVariant + Fields: + string UpperCaseMod = "_LZ_O" |IntrinsicsAMDGPU.td:542| + string LowerCaseMod = "_lz_o" |IntrinsicsAMDGPU.td:543| + ... + +* Global variables defined with outer ``defvar`` statements are shown with + their values. + +* The classes are shown with their source location, template arguments, + superclasses, and fields. + +* The records are shown with their source location, ``defm`` sequence, + superclasses, and fields. + +Superclasses are shown in the order processed, with indirect superclasses in +parentheses. Each field is shown with its value and the source location at +which it was set. +The ``defm`` sequence gives the locations of the ``defm`` statements that +were involved in generating the record, in the order they were invoked. \ No newline at end of file Index: llvm/include/llvm/Support/SourceMgr.h =================================================================== --- llvm/include/llvm/Support/SourceMgr.h +++ llvm/include/llvm/Support/SourceMgr.h @@ -172,6 +172,11 @@ std::pair getLineAndColumn(SMLoc Loc, unsigned BufferID = 0) const; + /// Get a string with the \p SMLoc filename and line number + /// formatted in the standard style. + std::string getFormattedLocationNoOffset(SMLoc Loc, + bool IncludePath = false) const; + /// Given a line and column number in a mapped buffer, turn it into an SMLoc. /// This will return a null SMLoc if the line/column location is invalid. SMLoc FindLocForLineAndColumn(unsigned BufferID, unsigned LineNo, Index: llvm/include/llvm/TableGen/Record.h =================================================================== --- llvm/include/llvm/TableGen/Record.h +++ llvm/include/llvm/TableGen/Record.h @@ -1518,7 +1518,10 @@ return SuperClasses; } - /// Append the direct super classes of this record to Classes. + /// Determine whether this record has the specified direct superclass. + bool hasDirectSuperClass(const Record *SuperClass) const; + + /// Append the direct superclasses of this record to Classes. void getDirectSuperClasses(SmallVectorImpl &Classes) const; bool isTemplateArg(Init *Name) const { @@ -1710,19 +1713,27 @@ friend class RecordRecTy; using RecordMap = std::map, std::less<>>; + using GlobalMap = std::map>; + std::string InputFilename; RecordMap Classes, Defs; FoldingSet RecordTypePool; std::map> ExtraGlobals; unsigned AnonCounter = 0; public: + /// Get the main TableGen input file's name. + const std::string getInputFilename() const { return InputFilename; } + /// Get the map of classes. const RecordMap &getClasses() const { return Classes; } /// Get the map of records (defs). const RecordMap &getDefs() const { return Defs; } + /// Get the map of global variables. + const GlobalMap &getGlobals() const { return ExtraGlobals; } + /// Get the class with the specified name. Record *getClass(StringRef Name) const { auto I = Classes.find(Name); @@ -1743,6 +1754,10 @@ return It == ExtraGlobals.end() ? nullptr : It->second; } + void saveInputFilename(std::string Filename) { + InputFilename = Filename; + } + void addClass(std::unique_ptr R) { bool Ins = Classes.insert(std::make_pair(std::string(R->getName()), std::move(R))).second; @@ -2017,6 +2032,7 @@ Init *resolve(Init *VarName) override; }; +void EmitDetailedRecords(RecordKeeper &RK, raw_ostream &OS); void EmitJSON(RecordKeeper &RK, raw_ostream &OS); } // end namespace llvm Index: llvm/lib/Support/SourceMgr.cpp =================================================================== --- llvm/lib/Support/SourceMgr.cpp +++ llvm/lib/Support/SourceMgr.cpp @@ -180,7 +180,7 @@ SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const { if (!BufferID) BufferID = FindBufferContainingLoc(Loc); - assert(BufferID && "Invalid Location!"); + assert(BufferID && "Invalid location!"); auto &SB = getBufferInfo(BufferID); const char *Ptr = Loc.getPointer(); @@ -193,6 +193,30 @@ return std::make_pair(LineNo, Ptr - BufStart - NewlineOffs); } +// FIXME: Note that the formatting of source locations is spread between +// multiple functions, some in SourceMgr and some in SMDiagnostic. A better +// solution would be a general-purpose source location formatter +// in one of those two classes, or possibly in SMLoc. + +/// Get a string with the source location formatted in the standard +/// style, but without the line offset. If \p IncludePath is true, the path +/// is included. If false, only the file name and extension are included. +std::string SourceMgr::getFormattedLocationNoOffset(SMLoc Loc, + bool IncludePath) const { + auto BufferID = FindBufferContainingLoc(Loc); + assert(BufferID && "Invalid location!"); + auto FileSpec = getBufferInfo(BufferID).Buffer->getBufferIdentifier(); + + if (IncludePath) { + return FileSpec.str() + ":" + std::to_string(FindLineNumber(Loc, BufferID)); + } else { + auto I = FileSpec.find_last_of("/\\"); + I = (I == FileSpec.size()) ? 0 : (I + 1); + return FileSpec.substr(I).str() + ":" + + std::to_string(FindLineNumber(Loc, BufferID)); + } +} + /// Given a line and column number in a mapped buffer, turn it into an SMLoc. /// This will return a null SMLoc if the line/column location is invalid. SMLoc SourceMgr::FindLocForLineAndColumn(unsigned BufferID, unsigned LineNo, Index: llvm/lib/TableGen/CMakeLists.txt =================================================================== --- llvm/lib/TableGen/CMakeLists.txt +++ llvm/lib/TableGen/CMakeLists.txt @@ -1,4 +1,5 @@ add_llvm_component_library(LLVMTableGen + DetailedRecordsBackend.cpp Error.cpp JSONBackend.cpp Main.cpp Index: llvm/lib/TableGen/DetailedRecordsBackend.cpp =================================================================== --- /dev/null +++ llvm/lib/TableGen/DetailedRecordsBackend.cpp @@ -0,0 +1,204 @@ +//===- DetailedRecordBackend.cpp - Detailed Records Report -*- 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 +// +//===----------------------------------------------------------------------===// +// +// This Tablegen backend prints a report that includes all the global +// variables, classes, and records in complete detail. It includes more +// detail than the default TableGen printer backend. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include + +#define DEBUG_TYPE "detailed-records-backend" + +#define NL "\n" + +using namespace llvm; + +namespace { + +class DetailedRecordsEmitter { +private: + RecordKeeper &Records; + +public: + DetailedRecordsEmitter(RecordKeeper &RK) : Records(RK) {} + + void run(raw_ostream &OS); + void printReportHeading(raw_ostream &OS); + void printVariables(raw_ostream &OS); + void printClasses(raw_ostream &OS); + void printRecords(raw_ostream &OS); + void printSectionHeading(std::string Title, int Count, raw_ostream &OS); + void printDefms(Record *Rec, raw_ostream &OS); + void printTemplateArgs(Record *Rec, raw_ostream &OS); + void printSuperclasses(Record *Rec, raw_ostream &OS); + void printFields(Record *Rec, raw_ostream &OS); + std::string formatLocation(const SMLoc Loc); +}; // emitter class + +} // anonymous namespace + +// Print the report. +void DetailedRecordsEmitter::run(raw_ostream &OS) { + printReportHeading(OS); + printVariables(OS); + printClasses(OS); + printRecords(OS); +} + +// Print the report heading, including the source file name. +void DetailedRecordsEmitter::printReportHeading(raw_ostream &OS) { + OS << formatv("DETAILED RECORDS for file {0}\n", Records.getInputFilename()); +} + +// Print the global variables. +void DetailedRecordsEmitter::printVariables(raw_ostream &OS) { + const auto GlobalList = Records.getGlobals(); + printSectionHeading("Global Variables", GlobalList.size(), OS); + + OS << NL; + for (const auto &Var : GlobalList) { + OS << Var.first << " = " << Var.second->getAsString() << NL; + } +} + +// Print the classes, including the template arguments, superclasses, +// and fields. +void DetailedRecordsEmitter::printClasses(raw_ostream &OS) { + const auto &ClassList = Records.getClasses(); + printSectionHeading("Classes", ClassList.size(), OS); + + for (const auto &ClassPair : ClassList) { + auto *const Class = ClassPair.second.get(); + OS << formatv("\n{0} |{1}|\n", Class->getNameInitAsString(), + SrcMgr.getFormattedLocationNoOffset(Class->getLoc().front())); + printTemplateArgs(Class, OS); + printSuperclasses(Class, OS); + printFields(Class, OS); + } +} + +// Print the records, including the defm sequences, supercasses, +// and fields. +void DetailedRecordsEmitter::printRecords(raw_ostream &OS) { + const auto &RecordList = Records.getDefs(); + printSectionHeading("Records", RecordList.size(), OS); + + for (const auto &RecPair : RecordList) { + auto *const Rec = RecPair.second.get(); + OS << formatv("\n{0} |{1}|\n", Rec->getNameInitAsString(), + SrcMgr.getFormattedLocationNoOffset(Rec->getLoc().front())); + printDefms(Rec, OS); + printSuperclasses(Rec, OS); + printFields(Rec, OS); + } +} + +// Print a section heading with the name of the section and +// the item count. +void DetailedRecordsEmitter::printSectionHeading(std::string Title, int Count, + raw_ostream &OS) { + OS << formatv("\n{0} {1} ({2}) {0}\n", "--------------------", Title, Count); +} + +// Print the record's defm source locations, if any. Note that they +// are stored in the reverse order of their invocation. +void DetailedRecordsEmitter::printDefms(Record *Rec, raw_ostream &OS) { + const auto &LocList = Rec->getLoc(); + if (LocList.size() < 2) + return; + + OS << " Defm sequence:"; + for (unsigned I = LocList.size() - 1; I >= 1; --I) { + OS << formatv(" |{0}|", SrcMgr.getFormattedLocationNoOffset(LocList[I])); + } + OS << NL; +} + +// Print the template arguments of a class. +void DetailedRecordsEmitter::printTemplateArgs(Record *Rec, + raw_ostream &OS) { + ArrayRef Args = Rec->getTemplateArgs(); + if (Args.empty()) { + OS << " Template args: (none)\n"; + return; + } + + OS << " Template args:\n"; + for (const Init *ArgName : Args) { + const RecordVal *Value = Rec->getValue(ArgName); + assert(Value && "Template argument value not found."); + OS << " "; + Value->print(OS, false); + OS << formatv(" |{0}|", SrcMgr.getFormattedLocationNoOffset(Value->getLoc())); + OS << NL; + } +} + +// Print the superclasses of a class or record. Indirect superclasses +// are enclosed in parentheses. +void DetailedRecordsEmitter::printSuperclasses(Record *Rec, raw_ostream &OS) { + ArrayRef> Superclasses = Rec->getSuperClasses(); + if (Superclasses.empty()) { + OS << " Superclasses: (none)\n"; + return; + } + + OS << " Superclasses:"; + for (const auto &SuperclassPair : Superclasses) { + auto *ClassRec = SuperclassPair.first; + if (Rec->hasDirectSuperClass(ClassRec)) + OS << formatv(" {0}", ClassRec->getNameInitAsString()); + else + OS << formatv(" ({0})", ClassRec->getNameInitAsString()); + } + OS << NL; +} + +// Print the fields of a class or record, including their source locations. +void DetailedRecordsEmitter::printFields(Record *Rec, raw_ostream &OS) { + const auto &ValueList = Rec->getValues(); + if (ValueList.empty()) { + OS << " Fields: (none)\n"; + return; + } + + OS << " Fields:\n"; + for (const RecordVal &Value : ValueList) + if (!Rec->isTemplateArg(Value.getNameInit())) { + OS << " "; + Value.print(OS, false); + OS << formatv(" |{0}|\n", + SrcMgr.getFormattedLocationNoOffset(Value.getLoc())); + } +} + +namespace llvm { + +// This function is called by TableGen after parsing the files. + +void EmitDetailedRecords(RecordKeeper &RK, raw_ostream &OS) { + // Instantiate the emitter class and invoke run(). + DetailedRecordsEmitter(RK).run(OS); +} + +} // namespace llvm Index: llvm/lib/TableGen/Main.cpp =================================================================== --- llvm/lib/TableGen/Main.cpp +++ llvm/lib/TableGen/Main.cpp @@ -90,6 +90,8 @@ return reportError(argv0, "Could not open input file '" + InputFilename + "': " + EC.message() + "\n"); + Records.saveInputFilename(InputFilename); + // Tell SrcMgr about this buffer, which is what TGParser will pick up. SrcMgr.AddNewSourceBuffer(std::move(*FileOrErr), SMLoc()); Index: llvm/lib/TableGen/Record.cpp =================================================================== --- llvm/lib/TableGen/Record.cpp +++ llvm/lib/TableGen/Record.cpp @@ -2144,12 +2144,27 @@ // this. See TGParser::ParseDef and TGParser::ParseDefm. } +// NOTE for the next two functions: +// Superclasses are in post-order, so the final one is a direct +// superclass. All of its transitive superclases immediately precede it, +// so we can step through the direct superclasses in reverse order. + +bool Record::hasDirectSuperClass(const Record *Superclass) const { + ArrayRef> SCs = getSuperClasses(); + + for (int I = SCs.size() - 1; I >= 0; --I) { + const Record *SC = SCs[I].first; + if (SC == Superclass) + return true; + I -= SC->getSuperClasses().size(); + } + + return false; +} + void Record::getDirectSuperClasses(SmallVectorImpl &Classes) const { ArrayRef> SCs = getSuperClasses(); - // Superclasses are in post-order, so the final one is a direct - // superclass. All of its transitive superclases immediately precede it, - // so we can step through the direct superclasses in reverse order. while (!SCs.empty()) { Record *SC = SCs.back().first; SCs = SCs.drop_back(1 + SC->getSuperClasses().size()); Index: llvm/lib/TableGen/TableGenBackendSkeleton.cpp =================================================================== --- llvm/lib/TableGen/TableGenBackendSkeleton.cpp +++ llvm/lib/TableGen/TableGenBackendSkeleton.cpp @@ -41,9 +41,9 @@ SkeletonEmitter(RecordKeeper &RK) : Records(RK) {} void run(raw_ostream &OS); -}; // End emitter class. +}; // emitter class -} // End anonymous namespace. +} // anonymous namespace void SkeletonEmitter::run(raw_ostream &OS) { emitSourceFileHeader("Skeleton data structures", OS); @@ -61,4 +61,4 @@ SkeletonEmitter(RK).run(OS); } -} // End llvm namespace. +} // namespace llvm Index: llvm/utils/TableGen/TableGen.cpp =================================================================== --- llvm/utils/TableGen/TableGen.cpp +++ llvm/utils/TableGen/TableGen.cpp @@ -21,6 +21,7 @@ enum ActionType { PrintRecords, + PrintDetailedRecords, DumpJSON, GenEmitter, GenRegisterInfo, @@ -75,6 +76,8 @@ cl::values( clEnumValN(PrintRecords, "print-records", "Print all records to stdout (default)"), + clEnumValN(PrintDetailedRecords, "print-detailed-records", + "Print full details of all records to stdout"), clEnumValN(DumpJSON, "dump-json", "Dump all records as machine-readable JSON"), clEnumValN(GenEmitter, "gen-emitter", "Generate machine code emitter"), @@ -152,6 +155,9 @@ case PrintRecords: OS << Records; // No argument, dump all contents break; + case PrintDetailedRecords: + EmitDetailedRecords(Records, OS); + break; case DumpJSON: EmitJSON(Records, OS); break;