diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
commit | b60736ec1405bb0a8dd40989f67ef4c93da068ab (patch) | |
tree | 5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /llvm/lib/TableGen | |
parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) | |
download | src-b60736ec1405bb0a8dd40989f67ef4c93da068ab.tar.gz src-b60736ec1405bb0a8dd40989f67ef4c93da068ab.zip |
Diffstat (limited to 'llvm/lib/TableGen')
-rw-r--r-- | llvm/lib/TableGen/DetailedRecordsBackend.cpp | 203 | ||||
-rw-r--r-- | llvm/lib/TableGen/Error.cpp | 85 | ||||
-rw-r--r-- | llvm/lib/TableGen/JSONBackend.cpp | 5 | ||||
-rw-r--r-- | llvm/lib/TableGen/Main.cpp | 43 | ||||
-rw-r--r-- | llvm/lib/TableGen/Record.cpp | 519 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGLexer.cpp | 53 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGLexer.h | 28 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGParser.cpp | 722 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGParser.h | 5 | ||||
-rw-r--r-- | llvm/lib/TableGen/TableGenBackendSkeleton.cpp | 64 |
10 files changed, 1342 insertions, 385 deletions
diff --git a/llvm/lib/TableGen/DetailedRecordsBackend.cpp b/llvm/lib/TableGen/DetailedRecordsBackend.cpp new file mode 100644 index 000000000000..2c3c3358b347 --- /dev/null +++ b/llvm/lib/TableGen/DetailedRecordsBackend.cpp @@ -0,0 +1,203 @@ +//===- 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 <algorithm> +#include <set> +#include <string> +#include <vector> + +#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); +}; // 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<Init *> 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<std::pair<Record *, SMRange>> 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 diff --git a/llvm/lib/TableGen/Error.cpp b/llvm/lib/TableGen/Error.cpp index 54b063cb4f8d..eed4de67942a 100644 --- a/llvm/lib/TableGen/Error.cpp +++ b/llvm/lib/TableGen/Error.cpp @@ -11,11 +11,12 @@ // //===----------------------------------------------------------------------===// -#include "llvm/TableGen/Error.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Signals.h" #include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" #include <cstdlib> namespace llvm { @@ -39,12 +40,54 @@ static void PrintMessage(ArrayRef<SMLoc> Loc, SourceMgr::DiagKind Kind, "instantiated from multiclass"); } -void PrintNote(const Twine &Msg) { WithColor::note() << Msg << "\n"; } +// Functions to print notes. + +void PrintNote(const Twine &Msg) { + WithColor::note() << Msg << "\n"; +} void PrintNote(ArrayRef<SMLoc> NoteLoc, const Twine &Msg) { PrintMessage(NoteLoc, SourceMgr::DK_Note, Msg); } +// Functions to print fatal notes. + +void PrintFatalNote(const Twine &Msg) { + PrintNote(Msg); + // The following call runs the file cleanup handlers. + sys::RunInterruptHandlers(); + std::exit(1); +} + +void PrintFatalNote(ArrayRef<SMLoc> NoteLoc, const Twine &Msg) { + PrintNote(NoteLoc, Msg); + // The following call runs the file cleanup handlers. + sys::RunInterruptHandlers(); + std::exit(1); +} + +// This method takes a Record and uses the source location +// stored in it. +void PrintFatalNote(const Record *Rec, const Twine &Msg) { + PrintNote(Rec->getLoc(), Msg); + // The following call runs the file cleanup handlers. + sys::RunInterruptHandlers(); + std::exit(1); +} + +// This method takes a RecordVal and uses the source location +// stored in it. +void PrintFatalNote(const RecordVal *RecVal, const Twine &Msg) { + PrintNote(RecVal->getLoc(), Msg); + // The following call runs the file cleanup handlers. + sys::RunInterruptHandlers(); + std::exit(1); +} + +// Functions to print warnings. + +void PrintWarning(const Twine &Msg) { WithColor::warning() << Msg << "\n"; } + void PrintWarning(ArrayRef<SMLoc> WarningLoc, const Twine &Msg) { PrintMessage(WarningLoc, SourceMgr::DK_Warning, Msg); } @@ -53,7 +96,9 @@ void PrintWarning(const char *Loc, const Twine &Msg) { SrcMgr.PrintMessage(SMLoc::getFromPointer(Loc), SourceMgr::DK_Warning, Msg); } -void PrintWarning(const Twine &Msg) { WithColor::warning() << Msg << "\n"; } +// Functions to print errors. + +void PrintError(const Twine &Msg) { WithColor::error() << Msg << "\n"; } void PrintError(ArrayRef<SMLoc> ErrorLoc, const Twine &Msg) { PrintMessage(ErrorLoc, SourceMgr::DK_Error, Msg); @@ -63,7 +108,19 @@ void PrintError(const char *Loc, const Twine &Msg) { SrcMgr.PrintMessage(SMLoc::getFromPointer(Loc), SourceMgr::DK_Error, Msg); } -void PrintError(const Twine &Msg) { WithColor::error() << Msg << "\n"; } +// This method takes a Record and uses the source location +// stored in it. +void PrintError(const Record *Rec, const Twine &Msg) { + PrintMessage(Rec->getLoc(), SourceMgr::DK_Error, Msg); +} + +// This method takes a RecordVal and uses the source location +// stored in it. +void PrintError(const RecordVal *RecVal, const Twine &Msg) { + PrintMessage(RecVal->getLoc(), SourceMgr::DK_Error, Msg); +} + +// Functions to print fatal errors. void PrintFatalError(const Twine &Msg) { PrintError(Msg); @@ -79,4 +136,22 @@ void PrintFatalError(ArrayRef<SMLoc> ErrorLoc, const Twine &Msg) { std::exit(1); } +// This method takes a Record and uses the source location +// stored in it. +void PrintFatalError(const Record *Rec, const Twine &Msg) { + PrintError(Rec->getLoc(), Msg); + // The following call runs the file cleanup handlers. + sys::RunInterruptHandlers(); + std::exit(1); +} + +// This method takes a RecordVal and uses the source location +// stored in it. +void PrintFatalError(const RecordVal *RecVal, const Twine &Msg) { + PrintError(RecVal->getLoc(), Msg); + // The following call runs the file cleanup handlers. + sys::RunInterruptHandlers(); + std::exit(1); +} + } // end namespace llvm diff --git a/llvm/lib/TableGen/JSONBackend.cpp b/llvm/lib/TableGen/JSONBackend.cpp index 196644cda667..8ddfd9f04524 100644 --- a/llvm/lib/TableGen/JSONBackend.cpp +++ b/llvm/lib/TableGen/JSONBackend.cpp @@ -29,7 +29,6 @@ private: RecordKeeper &Records; json::Value translateInit(const Init &I); - json::Array listSuperclasses(const Record &R); public: JSONEmitter(RecordKeeper &R); @@ -59,8 +58,6 @@ json::Value JSONEmitter::translateInit(const Init &I) { return Int->getValue(); } else if (auto *Str = dyn_cast<StringInit>(&I)) { return Str->getValue(); - } else if (auto *Code = dyn_cast<CodeInit>(&I)) { - return Code->getValue(); } else if (auto *List = dyn_cast<ListInit>(&I)) { json::Array array; for (auto val : *List) @@ -147,7 +144,7 @@ void JSONEmitter::run(raw_ostream &OS) { for (const RecordVal &RV : Def.getValues()) { if (!Def.isTemplateArg(RV.getNameInit())) { auto Name = RV.getNameInitAsString(); - if (RV.getPrefix()) + if (RV.isNonconcreteOK()) fields.push_back(Name); obj[Name] = translateInit(*RV.getValue()); } diff --git a/llvm/lib/TableGen/Main.cpp b/llvm/lib/TableGen/Main.cpp index 77f1b61cf930..0ace5363dd05 100644 --- a/llvm/lib/TableGen/Main.cpp +++ b/llvm/lib/TableGen/Main.cpp @@ -52,6 +52,9 @@ MacroNames("D", cl::desc("Name of the macro to be defined"), static cl::opt<bool> WriteIfChanged("write-if-changed", cl::desc("Only write output if it changed")); +static cl::opt<bool> +TimePhases("time-phases", cl::desc("Time phases of parser and backend")); + static int reportError(const char *ProgName, Twine Msg) { errs() << ProgName << ": " << Msg; errs().flush(); @@ -83,13 +86,20 @@ static int createDependencyFile(const TGParser &Parser, const char *argv0) { int llvm::TableGenMain(const char *argv0, TableGenMainFn *MainFn) { RecordKeeper Records; + if (TimePhases) + Records.startPhaseTiming(); + // Parse the input file. + + Records.startTimer("Parse, build records"); ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = MemoryBuffer::getFileOrSTDIN(InputFilename); if (std::error_code EC = FileOrErr.getError()) 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()); @@ -101,11 +111,15 @@ int llvm::TableGenMain(const char *argv0, TableGenMainFn *MainFn) { if (Parser.ParseFile()) return 1; + Records.stopTimer(); // Write output to memory. + Records.startBackendTimer("Backend overall"); std::string OutString; raw_string_ostream Out(OutString); - if (MainFn(Out, Records)) + unsigned status = MainFn(Out, Records); + Records.stopBackendTimer(); + if (status) return 1; // Always write the depfile, even if the main output hasn't changed. @@ -117,26 +131,31 @@ int llvm::TableGenMain(const char *argv0, TableGenMainFn *MainFn) { return Ret; } + Records.startTimer("Write output"); + bool WriteFile = true; if (WriteIfChanged) { // Only updates the real output file if there are any differences. // This prevents recompilation of all the files depending on it if there // aren't any. if (auto ExistingOrErr = MemoryBuffer::getFile(OutputFilename)) if (std::move(ExistingOrErr.get())->getBuffer() == Out.str()) - return 0; + WriteFile = false; } - - std::error_code EC; - ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None); - if (EC) - return reportError(argv0, "error opening " + OutputFilename + ":" + - EC.message() + "\n"); - OutFile.os() << Out.str(); + if (WriteFile) { + std::error_code EC; + ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None); + if (EC) + return reportError(argv0, "error opening " + OutputFilename + ": " + + EC.message() + "\n"); + OutFile.os() << Out.str(); + if (ErrorsPrinted == 0) + OutFile.keep(); + } + + Records.stopTimer(); + Records.stopPhaseTiming(); if (ErrorsPrinted > 0) return reportError(argv0, Twine(ErrorsPrinted) + " errors.\n"); - - // Declare success. - OutFile.keep(); return 0; } diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index d3db004196b8..b6c6b8f031d3 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -43,15 +43,11 @@ using namespace llvm; static BumpPtrAllocator Allocator; -STATISTIC(CodeInitsConstructed, - "The total number of unique CodeInits constructed"); - //===----------------------------------------------------------------------===// // Type implementations //===----------------------------------------------------------------------===// BitRecTy BitRecTy::Shared; -CodeRecTy CodeRecTy::Shared; IntRecTy IntRecTy::Shared; StringRecTy StringRecTy::Shared; DagRecTy DagRecTy::Shared; @@ -113,27 +109,22 @@ bool IntRecTy::typeIsConvertibleTo(const RecTy *RHS) const { return kind==BitRecTyKind || kind==BitsRecTyKind || kind==IntRecTyKind; } -bool CodeRecTy::typeIsConvertibleTo(const RecTy *RHS) const { - RecTyKind Kind = RHS->getRecTyKind(); - return Kind == CodeRecTyKind || Kind == StringRecTyKind; -} - std::string StringRecTy::getAsString() const { return "string"; } bool StringRecTy::typeIsConvertibleTo(const RecTy *RHS) const { RecTyKind Kind = RHS->getRecTyKind(); - return Kind == StringRecTyKind || Kind == CodeRecTyKind; + return Kind == StringRecTyKind; } std::string ListRecTy::getAsString() const { - return "list<" + Ty->getAsString() + ">"; + return "list<" + ElementTy->getAsString() + ">"; } bool ListRecTy::typeIsConvertibleTo(const RecTy *RHS) const { if (const auto *ListTy = dyn_cast<ListRecTy>(RHS)) - return Ty->typeIsConvertibleTo(ListTy->getElementType()); + return ElementTy->typeIsConvertibleTo(ListTy->getElementType()); return false; } @@ -241,13 +232,10 @@ bool RecordRecTy::typeIsA(const RecTy *RHS) const { static RecordRecTy *resolveRecordTypes(RecordRecTy *T1, RecordRecTy *T2) { SmallVector<Record *, 4> CommonSuperClasses; - SmallVector<Record *, 4> Stack; - - Stack.insert(Stack.end(), T1->classes_begin(), T1->classes_end()); + SmallVector<Record *, 4> Stack(T1->classes_begin(), T1->classes_end()); while (!Stack.empty()) { - Record *R = Stack.back(); - Stack.pop_back(); + Record *R = Stack.pop_back_val(); if (T2->isSubClassOf(R)) { CommonSuperClasses.push_back(R); @@ -514,45 +502,26 @@ IntInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const { return BitsInit::get(NewBits); } -CodeInit *CodeInit::get(StringRef V, const SMLoc &Loc) { - static StringSet<BumpPtrAllocator &> ThePool(Allocator); - - CodeInitsConstructed++; - - // Unlike StringMap, StringSet doesn't accept empty keys. - if (V.empty()) - return new (Allocator) CodeInit("", Loc); - - // Location tracking prevents us from de-duping CodeInits as we're never - // called with the same string and same location twice. However, we can at - // least de-dupe the strings for a modest saving. - auto &Entry = *ThePool.insert(V).first; - return new(Allocator) CodeInit(Entry.getKey(), Loc); -} - -StringInit *StringInit::get(StringRef V) { - static StringMap<StringInit*, BumpPtrAllocator &> ThePool(Allocator); +StringInit *StringInit::get(StringRef V, StringFormat Fmt) { + static StringMap<StringInit*, BumpPtrAllocator &> StringPool(Allocator); + static StringMap<StringInit*, BumpPtrAllocator &> CodePool(Allocator); - auto &Entry = *ThePool.insert(std::make_pair(V, nullptr)).first; - if (!Entry.second) - Entry.second = new(Allocator) StringInit(Entry.getKey()); - return Entry.second; + if (Fmt == SF_String) { + auto &Entry = *StringPool.insert(std::make_pair(V, nullptr)).first; + if (!Entry.second) + Entry.second = new (Allocator) StringInit(Entry.getKey(), Fmt); + return Entry.second; + } else { + auto &Entry = *CodePool.insert(std::make_pair(V, nullptr)).first; + if (!Entry.second) + Entry.second = new (Allocator) StringInit(Entry.getKey(), Fmt); + return Entry.second; + } } Init *StringInit::convertInitializerTo(RecTy *Ty) const { if (isa<StringRecTy>(Ty)) return const_cast<StringInit *>(this); - if (isa<CodeRecTy>(Ty)) - return CodeInit::get(getValue(), SMLoc()); - - return nullptr; -} - -Init *CodeInit::convertInitializerTo(RecTy *Ty) const { - if (isa<CodeRecTy>(Ty)) - return const_cast<CodeInit *>(this); - if (isa<StringRecTy>(Ty)) - return StringInit::get(getValue()); return nullptr; } @@ -719,8 +688,10 @@ Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { if (DefInit *LHSd = dyn_cast<DefInit>(LHS)) return StringInit::get(LHSd->getAsString()); - if (IntInit *LHSi = dyn_cast<IntInit>(LHS)) + if (IntInit *LHSi = + dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get()))) return StringInit::get(LHSi->getAsString()); + } else if (isa<RecordRecTy>(getType())) { if (StringInit *Name = dyn_cast<StringInit>(LHS)) { if (!CurRec && !IsFinal) @@ -761,6 +732,12 @@ Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { return NewInit; break; + case NOT: + if (IntInit *LHSi = + dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get()))) + return IntInit::get(LHSi->getValue() ? 0 : 1); + break; + case HEAD: if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) { assert(!LHSl->empty() && "Empty list in head"); @@ -780,16 +757,22 @@ Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { case SIZE: if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) return IntInit::get(LHSl->size()); + if (DagInit *LHSd = dyn_cast<DagInit>(LHS)) + return IntInit::get(LHSd->arg_size()); + if (StringInit *LHSs = dyn_cast<StringInit>(LHS)) + return IntInit::get(LHSs->getValue().size()); break; case EMPTY: if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) return IntInit::get(LHSl->empty()); + if (DagInit *LHSd = dyn_cast<DagInit>(LHS)) + return IntInit::get(LHSd->arg_empty()); if (StringInit *LHSs = dyn_cast<StringInit>(LHS)) return IntInit::get(LHSs->getValue().empty()); break; - case GETOP: + case GETDAGOP: if (DagInit *Dag = dyn_cast<DagInit>(LHS)) { DefInit *DI = DefInit::get(Dag->getOperatorAsDef({})); if (!DI->getType()->typeIsA(getType())) { @@ -820,11 +803,12 @@ std::string UnOpInit::getAsString() const { std::string Result; switch (getOpcode()) { case CAST: Result = "!cast<" + getType()->getAsString() + ">"; break; + case NOT: Result = "!not"; break; case HEAD: Result = "!head"; break; case TAIL: Result = "!tail"; break; case SIZE: Result = "!size"; break; case EMPTY: Result = "!empty"; break; - case GETOP: Result = "!getop"; break; + case GETDAGOP: Result = "!getdagop"; break; } return Result + "(" + LHS->getAsString() + ")"; } @@ -862,7 +846,53 @@ static StringInit *ConcatStringInits(const StringInit *I0, const StringInit *I1) { SmallString<80> Concat(I0->getValue()); Concat.append(I1->getValue()); - return StringInit::get(Concat); + return StringInit::get(Concat, + StringInit::determineFormat(I0->getFormat(), + I1->getFormat())); +} + +static StringInit *interleaveStringList(const ListInit *List, + const StringInit *Delim) { + if (List->size() == 0) + return StringInit::get(""); + StringInit *Element = dyn_cast<StringInit>(List->getElement(0)); + if (!Element) + return nullptr; + SmallString<80> Result(Element->getValue()); + StringInit::StringFormat Fmt = StringInit::SF_String; + + for (unsigned I = 1, E = List->size(); I < E; ++I) { + Result.append(Delim->getValue()); + StringInit *Element = dyn_cast<StringInit>(List->getElement(I)); + if (!Element) + return nullptr; + Result.append(Element->getValue()); + Fmt = StringInit::determineFormat(Fmt, Element->getFormat()); + } + return StringInit::get(Result, Fmt); +} + +static StringInit *interleaveIntList(const ListInit *List, + const StringInit *Delim) { + if (List->size() == 0) + return StringInit::get(""); + IntInit *Element = + dyn_cast_or_null<IntInit>(List->getElement(0) + ->convertInitializerTo(IntRecTy::get())); + if (!Element) + return nullptr; + SmallString<80> Result(Element->getAsString()); + + for (unsigned I = 1, E = List->size(); I < E; ++I) { + Result.append(Delim->getValue()); + IntInit *Element = + dyn_cast_or_null<IntInit>(List->getElement(I) + ->convertInitializerTo(IntRecTy::get())); + if (!Element) + return nullptr; + Result.append(Element->getAsString()); + } + return StringInit::get(Result); } Init *BinOpInit::getStrConcat(Init *I0, Init *I1) { @@ -876,8 +906,8 @@ Init *BinOpInit::getStrConcat(Init *I0, Init *I1) { static ListInit *ConcatListInits(const ListInit *LHS, const ListInit *RHS) { SmallVector<Init *, 8> Args; - Args.insert(Args.end(), LHS->begin(), LHS->end()); - Args.insert(Args.end(), RHS->begin(), RHS->end()); + llvm::append_range(Args, *LHS); + llvm::append_range(Args, *RHS); return ListInit::get(Args, LHS->getElementType()); } @@ -891,10 +921,6 @@ Init *BinOpInit::getListConcat(TypedInit *LHS, Init *RHS) { return BinOpInit::get(BinOpInit::LISTCONCAT, LHS, RHS, LHS->getType()); } -Init *BinOpInit::getListSplat(TypedInit *LHS, Init *RHS) { - return BinOpInit::get(BinOpInit::LISTSPLAT, LHS, RHS, LHS->getType()); -} - Init *BinOpInit::Fold(Record *CurRec) const { switch (getOpcode()) { case CONCAT: { @@ -934,8 +960,8 @@ Init *BinOpInit::Fold(Record *CurRec) const { ListInit *RHSs = dyn_cast<ListInit>(RHS); if (LHSs && RHSs) { SmallVector<Init *, 8> Args; - Args.insert(Args.end(), LHSs->begin(), LHSs->end()); - Args.insert(Args.end(), RHSs->begin(), RHSs->end()); + llvm::append_range(Args, *LHSs); + llvm::append_range(Args, *RHSs); return ListInit::get(Args, LHSs->getElementType()); } break; @@ -956,47 +982,76 @@ Init *BinOpInit::Fold(Record *CurRec) const { return ConcatStringInits(LHSs, RHSs); break; } + case INTERLEAVE: { + ListInit *List = dyn_cast<ListInit>(LHS); + StringInit *Delim = dyn_cast<StringInit>(RHS); + if (List && Delim) { + StringInit *Result; + if (isa<StringRecTy>(List->getElementType())) + Result = interleaveStringList(List, Delim); + else + Result = interleaveIntList(List, Delim); + if (Result) + return Result; + } + break; + } case EQ: case NE: case LE: case LT: case GE: case GT: { - // try to fold eq comparison for 'bit' and 'int', otherwise fallback - // to string objects. - IntInit *L = + // First see if we have two bit, bits, or int. + IntInit *LHSi = dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get())); - IntInit *R = + IntInit *RHSi = dyn_cast_or_null<IntInit>(RHS->convertInitializerTo(IntRecTy::get())); - if (L && R) { + if (LHSi && RHSi) { bool Result; switch (getOpcode()) { - case EQ: Result = L->getValue() == R->getValue(); break; - case NE: Result = L->getValue() != R->getValue(); break; - case LE: Result = L->getValue() <= R->getValue(); break; - case LT: Result = L->getValue() < R->getValue(); break; - case GE: Result = L->getValue() >= R->getValue(); break; - case GT: Result = L->getValue() > R->getValue(); break; + case EQ: Result = LHSi->getValue() == RHSi->getValue(); break; + case NE: Result = LHSi->getValue() != RHSi->getValue(); break; + case LE: Result = LHSi->getValue() <= RHSi->getValue(); break; + case LT: Result = LHSi->getValue() < RHSi->getValue(); break; + case GE: Result = LHSi->getValue() >= RHSi->getValue(); break; + case GT: Result = LHSi->getValue() > RHSi->getValue(); break; default: llvm_unreachable("unhandled comparison"); } return BitInit::get(Result); } - if (getOpcode() == EQ || getOpcode() == NE) { - StringInit *LHSs = dyn_cast<StringInit>(LHS); - StringInit *RHSs = dyn_cast<StringInit>(RHS); + // Next try strings. + StringInit *LHSs = dyn_cast<StringInit>(LHS); + StringInit *RHSs = dyn_cast<StringInit>(RHS); - // Make sure we've resolved - if (LHSs && RHSs) { - bool Equal = LHSs->getValue() == RHSs->getValue(); - return BitInit::get(getOpcode() == EQ ? Equal : !Equal); + if (LHSs && RHSs) { + bool Result; + switch (getOpcode()) { + case EQ: Result = LHSs->getValue() == RHSs->getValue(); break; + case NE: Result = LHSs->getValue() != RHSs->getValue(); break; + case LE: Result = LHSs->getValue() <= RHSs->getValue(); break; + case LT: Result = LHSs->getValue() < RHSs->getValue(); break; + case GE: Result = LHSs->getValue() >= RHSs->getValue(); break; + case GT: Result = LHSs->getValue() > RHSs->getValue(); break; + default: llvm_unreachable("unhandled comparison"); } + return BitInit::get(Result); + } + + // Finally, !eq and !ne can be used with records. + if (getOpcode() == EQ || getOpcode() == NE) { + DefInit *LHSd = dyn_cast<DefInit>(LHS); + DefInit *RHSd = dyn_cast<DefInit>(RHS); + if (LHSd && RHSd) + return BitInit::get((getOpcode() == EQ) ? LHSd == RHSd + : LHSd != RHSd); } break; } - case SETOP: { + case SETDAGOP: { DagInit *Dag = dyn_cast<DagInit>(LHS); DefInit *Op = dyn_cast<DefInit>(RHS); if (Dag && Op) { @@ -1011,9 +1066,11 @@ Init *BinOpInit::Fold(Record *CurRec) const { break; } case ADD: + case SUB: case MUL: case AND: case OR: + case XOR: case SHL: case SRA: case SRL: { @@ -1026,10 +1083,12 @@ Init *BinOpInit::Fold(Record *CurRec) const { int64_t Result; switch (getOpcode()) { default: llvm_unreachable("Bad opcode!"); - case ADD: Result = LHSv + RHSv; break; - case MUL: Result = LHSv * RHSv; break; - case AND: Result = LHSv & RHSv; break; - case OR: Result = LHSv | RHSv; break; + case ADD: Result = LHSv + RHSv; break; + case SUB: Result = LHSv - RHSv; break; + case MUL: Result = LHSv * RHSv; break; + case AND: Result = LHSv & RHSv; break; + case OR: Result = LHSv | RHSv; break; + case XOR: Result = LHSv ^ RHSv; break; case SHL: Result = (uint64_t)LHSv << (uint64_t)RHSv; break; case SRA: Result = LHSv >> RHSv; break; case SRL: Result = (uint64_t)LHSv >> (uint64_t)RHSv; break; @@ -1057,9 +1116,11 @@ std::string BinOpInit::getAsString() const { switch (getOpcode()) { case CONCAT: Result = "!con"; break; case ADD: Result = "!add"; break; + case SUB: Result = "!sub"; break; case MUL: Result = "!mul"; break; case AND: Result = "!and"; break; case OR: Result = "!or"; break; + case XOR: Result = "!xor"; break; case SHL: Result = "!shl"; break; case SRA: Result = "!sra"; break; case SRL: Result = "!srl"; break; @@ -1072,7 +1133,8 @@ std::string BinOpInit::getAsString() const { case LISTCONCAT: Result = "!listconcat"; break; case LISTSPLAT: Result = "!listsplat"; break; case STRCONCAT: Result = "!strconcat"; break; - case SETOP: Result = "!setop"; break; + case INTERLEAVE: Result = "!interleave"; break; + case SETDAGOP: Result = "!setdagop"; break; } return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")"; } @@ -1107,7 +1169,7 @@ void TernOpInit::Profile(FoldingSetNodeID &ID) const { ProfileTernOpInit(ID, getOpcode(), getLHS(), getMHS(), getRHS(), getType()); } -static Init *ForeachApply(Init *LHS, Init *MHSe, Init *RHS, Record *CurRec) { +static Init *ItemApply(Init *LHS, Init *MHSe, Init *RHS, Record *CurRec) { MapResolver R(CurRec); R.set(LHS, MHSe); return RHS->resolveReferences(R); @@ -1116,7 +1178,7 @@ static Init *ForeachApply(Init *LHS, Init *MHSe, Init *RHS, Record *CurRec) { static Init *ForeachDagApply(Init *LHS, DagInit *MHSd, Init *RHS, Record *CurRec) { bool Change = false; - Init *Val = ForeachApply(LHS, MHSd->getOperator(), RHS, CurRec); + Init *Val = ItemApply(LHS, MHSd->getOperator(), RHS, CurRec); if (Val != MHSd->getOperator()) Change = true; @@ -1129,7 +1191,7 @@ static Init *ForeachDagApply(Init *LHS, DagInit *MHSd, Init *RHS, if (DagInit *Argd = dyn_cast<DagInit>(Arg)) NewArg = ForeachDagApply(LHS, Argd, RHS, CurRec); else - NewArg = ForeachApply(LHS, Arg, RHS, CurRec); + NewArg = ItemApply(LHS, Arg, RHS, CurRec); NewArgs.push_back(std::make_pair(NewArg, ArgName)); if (Arg != NewArg) @@ -1151,7 +1213,7 @@ static Init *ForeachHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type, SmallVector<Init *, 8> NewList(MHSl->begin(), MHSl->end()); for (Init *&Item : NewList) { - Init *NewItem = ForeachApply(LHS, Item, RHS, CurRec); + Init *NewItem = ItemApply(LHS, Item, RHS, CurRec); if (NewItem != Item) Item = NewItem; } @@ -1161,6 +1223,31 @@ static Init *ForeachHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type, return nullptr; } +// Evaluates RHS for all elements of MHS, using LHS as a temp variable. +// Creates a new list with the elements that evaluated to true. +static Init *FilterHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type, + Record *CurRec) { + if (ListInit *MHSl = dyn_cast<ListInit>(MHS)) { + SmallVector<Init *, 8> NewList; + + for (Init *Item : MHSl->getValues()) { + Init *Include = ItemApply(LHS, Item, RHS, CurRec); + if (!Include) + return nullptr; + if (IntInit *IncludeInt = dyn_cast_or_null<IntInit>( + Include->convertInitializerTo(IntRecTy::get()))) { + if (IncludeInt->getValue()) + NewList.push_back(Item); + } else { + return nullptr; + } + } + return ListInit::get(NewList, cast<ListRecTy>(Type)->getElementType()); + } + + return nullptr; +} + Init *TernOpInit::Fold(Record *CurRec) const { switch (getOpcode()) { case SUBST: { @@ -1213,6 +1300,12 @@ Init *TernOpInit::Fold(Record *CurRec) const { break; } + case FILTER: { + if (Init *Result = FilterHelper(LHS, MHS, RHS, getType(), CurRec)) + return Result; + break; + } + case IF: { if (IntInit *LHSi = dyn_cast_or_null<IntInit>( LHS->convertInitializerTo(IntRecTy::get()))) { @@ -1246,6 +1339,27 @@ Init *TernOpInit::Fold(Record *CurRec) const { } break; } + + case SUBSTR: { + StringInit *LHSs = dyn_cast<StringInit>(LHS); + IntInit *MHSi = dyn_cast<IntInit>(MHS); + IntInit *RHSi = dyn_cast<IntInit>(RHS); + if (LHSs && MHSi && RHSi) { + int64_t StringSize = LHSs->getValue().size(); + int64_t Start = MHSi->getValue(); + int64_t Length = RHSi->getValue(); + if (Start < 0 || Start > StringSize) + PrintError(CurRec->getLoc(), + Twine("!substr start position is out of range 0...") + + std::to_string(StringSize) + ": " + + std::to_string(Start)); + if (Length < 0) + PrintError(CurRec->getLoc(), "!substr length must be nonnegative"); + return StringInit::get(LHSs->getValue().substr(Start, Length), + LHSs->getFormat()); + } + break; + } } return const_cast<TernOpInit *>(this); @@ -1267,7 +1381,7 @@ Init *TernOpInit::resolveReferences(Resolver &R) const { Init *mhs = MHS->resolveReferences(R); Init *rhs; - if (getOpcode() == FOREACH) { + if (getOpcode() == FOREACH || getOpcode() == FILTER) { ShadowResolver SR(R); SR.addShadow(lhs); rhs = RHS->resolveReferences(SR); @@ -1285,10 +1399,12 @@ std::string TernOpInit::getAsString() const { std::string Result; bool UnquotedLHS = false; switch (getOpcode()) { - case SUBST: Result = "!subst"; break; + case DAG: Result = "!dag"; break; + case FILTER: Result = "!filter"; UnquotedLHS = true; break; case FOREACH: Result = "!foreach"; UnquotedLHS = true; break; case IF: Result = "!if"; break; - case DAG: Result = "!dag"; break; + case SUBST: Result = "!subst"; break; + case SUBSTR: Result = "!substr"; break; } return (Result + "(" + (UnquotedLHS ? LHS->getAsUnquotedString() : LHS->getAsString()) + @@ -2026,8 +2142,16 @@ std::string DagInit::getAsString() const { // Other implementations //===----------------------------------------------------------------------===// -RecordVal::RecordVal(Init *N, RecTy *T, bool P) - : Name(N), TyAndPrefix(T, P) { +RecordVal::RecordVal(Init *N, RecTy *T, FieldKind K) + : Name(N), TyAndKind(T, K) { + setValue(UnsetInit::get()); + assert(Value && "Cannot create unset value for current type!"); +} + +// This constructor accepts the same arguments as the above, but also +// a source location. +RecordVal::RecordVal(Init *N, SMLoc Loc, RecTy *T, FieldKind K) + : Name(N), Loc(Loc), TyAndKind(T, K) { setValue(UnsetInit::get()); assert(Value && "Cannot create unset value for current type!"); } @@ -2036,6 +2160,21 @@ StringRef RecordVal::getName() const { return cast<StringInit>(getNameInit())->getValue(); } +std::string RecordVal::getPrintType() const { + if (getType() == StringRecTy::get()) { + if (auto *StrInit = dyn_cast<StringInit>(Value)) { + if (StrInit->hasCodeFormat()) + return "code"; + else + return "string"; + } else { + return "string"; + } + } else { + return TyAndKind.getPointer()->getAsString(); + } +} + bool RecordVal::setValue(Init *V) { if (V) { Value = V->getCastTo(getType()); @@ -2046,8 +2185,33 @@ bool RecordVal::setValue(Init *V) { if (!isa<BitsInit>(Value)) { SmallVector<Init *, 64> Bits; Bits.reserve(BTy->getNumBits()); - for (unsigned i = 0, e = BTy->getNumBits(); i < e; ++i) - Bits.push_back(Value->getBit(i)); + for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I) + Bits.push_back(Value->getBit(I)); + Value = BitsInit::get(Bits); + } + } + } + return Value == nullptr; + } + Value = nullptr; + return false; +} + +// This version of setValue takes a source location and resets the +// location in the RecordVal. +bool RecordVal::setValue(Init *V, SMLoc NewLoc) { + Loc = NewLoc; + if (V) { + Value = V->getCastTo(getType()); + if (Value) { + assert(!isa<TypedInit>(Value) || + cast<TypedInit>(Value)->getType()->typeIsA(getType())); + if (BitsRecTy *BTy = dyn_cast<BitsRecTy>(getType())) { + if (!isa<BitsInit>(Value)) { + SmallVector<Init *, 64> Bits; + Bits.reserve(BTy->getNumBits()); + for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I) + Bits.push_back(Value->getBit(I)); Value = BitsInit::get(Bits); } } @@ -2058,13 +2222,14 @@ bool RecordVal::setValue(Init *V) { return false; } +#include "llvm/TableGen/Record.h" #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; } #endif void RecordVal::print(raw_ostream &OS, bool PrintSem) const { - if (getPrefix()) OS << "field "; - OS << *getType() << " " << getNameInitAsString(); + if (isNonconcreteOK()) OS << "field "; + OS << getPrintType() << " " << getNameInitAsString(); if (getValue()) OS << " = " << *getValue(); @@ -2089,9 +2254,9 @@ RecordRecTy *Record::getType() { } DefInit *Record::getDefInit() { - if (!TheInit) - TheInit = new(Allocator) DefInit(this); - return TheInit; + if (!CorrespondingDefInit) + CorrespondingDefInit = new (Allocator) DefInit(this); + return CorrespondingDefInit; } void Record::setName(Init *NewName) { @@ -2110,11 +2275,28 @@ void Record::setName(Init *NewName) { // 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<std::pair<Record *, SMRange>> 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<Record *> &Classes) const { ArrayRef<std::pair<Record *, SMRange>> SCs = getSuperClasses(); + while (!SCs.empty()) { - // Superclasses are in reverse preorder, so 'back' is a direct superclass, - // and its transitive superclasses are directly preceding it. Record *SC = SCs.back().first; SCs = SCs.drop_back(1 + SC->getSuperClasses().size()); Classes.push_back(SC); @@ -2187,15 +2369,23 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const Record &R) { OS << "\n"; for (const RecordVal &Val : R.getValues()) - if (Val.getPrefix() && !R.isTemplateArg(Val.getNameInit())) + if (Val.isNonconcreteOK() && !R.isTemplateArg(Val.getNameInit())) OS << Val; for (const RecordVal &Val : R.getValues()) - if (!Val.getPrefix() && !R.isTemplateArg(Val.getNameInit())) + if (!Val.isNonconcreteOK() && !R.isTemplateArg(Val.getNameInit())) OS << Val; return OS << "}\n"; } +SMLoc Record::getFieldLoc(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + return R->getLoc(); +} + Init *Record::getValueInit(StringRef FieldName) const { const RecordVal *R = getValue(FieldName); if (!R || !R->getValue()) @@ -2205,18 +2395,27 @@ Init *Record::getValueInit(StringRef FieldName) const { } StringRef Record::getValueAsString(StringRef FieldName) const { - const RecordVal *R = getValue(FieldName); - if (!R || !R->getValue()) + llvm::Optional<StringRef> S = getValueAsOptionalString(FieldName); + if (!S.hasValue()) PrintFatalError(getLoc(), "Record `" + getName() + "' does not have a field named `" + FieldName + "'!\n"); + return S.getValue(); +} + +llvm::Optional<StringRef> +Record::getValueAsOptionalString(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + return llvm::Optional<StringRef>(); + if (isa<UnsetInit>(R->getValue())) + return llvm::Optional<StringRef>(); if (StringInit *SI = dyn_cast<StringInit>(R->getValue())) return SI->getValue(); - if (CodeInit *CI = dyn_cast<CodeInit>(R->getValue())) - return CI->getValue(); - PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + - FieldName + "' does not have a string initializer!"); + PrintFatalError(getLoc(), + "Record `" + getName() + "', ` field `" + FieldName + + "' exists but does not have a string initializer!"); } BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const { @@ -2227,8 +2426,8 @@ BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const { if (BitsInit *BI = dyn_cast<BitsInit>(R->getValue())) return BI; - PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + - FieldName + "' does not have a BitsInit initializer!"); + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName + + "' exists but does not have a bits value"); } ListInit *Record::getValueAsListInit(StringRef FieldName) const { @@ -2239,8 +2438,8 @@ ListInit *Record::getValueAsListInit(StringRef FieldName) const { if (ListInit *LI = dyn_cast<ListInit>(R->getValue())) return LI; - PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + - FieldName + "' does not have a list initializer!"); + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName + + "' exists but does not have a list value"); } std::vector<Record*> @@ -2267,7 +2466,7 @@ int64_t Record::getValueAsInt(StringRef FieldName) const { return II->getValue(); PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" + FieldName + - "' does not have an int initializer: " + + "' exists but does not have an int value: " + R->getValue()->getAsString()); } @@ -2281,7 +2480,7 @@ Record::getValueAsListOfInts(StringRef FieldName) const { else PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" + FieldName + - "' does not have a list of ints initializer: " + + "' exists but does not have a list of ints value: " + I->getAsString()); } return Ints; @@ -2294,12 +2493,10 @@ Record::getValueAsListOfStrings(StringRef FieldName) const { for (Init *I : List->getValues()) { if (StringInit *SI = dyn_cast<StringInit>(I)) Strings.push_back(SI->getValue()); - else if (CodeInit *CI = dyn_cast<CodeInit>(I)) - Strings.push_back(CI->getValue()); else PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" + FieldName + - "' does not have a list of strings initializer: " + + "' exists but does not have a list of strings value: " + I->getAsString()); } return Strings; @@ -2394,16 +2591,78 @@ Init *RecordKeeper::getNewAnonymousName() { return StringInit::get("anonymous_" + utostr(AnonCounter++)); } -std::vector<Record *> -RecordKeeper::getAllDerivedDefinitions(StringRef ClassName) const { - Record *Class = getClass(ClassName); - if (!Class) - PrintFatalError("ERROR: Couldn't find the `" + ClassName + "' class!\n"); +// These functions implement the phase timing facility. Starting a timer +// when one is already running stops the running one. - std::vector<Record*> Defs; - for (const auto &D : getDefs()) - if (D.second->isSubClassOf(Class)) - Defs.push_back(D.second.get()); +void RecordKeeper::startTimer(StringRef Name) { + if (TimingGroup) { + if (LastTimer && LastTimer->isRunning()) { + LastTimer->stopTimer(); + if (BackendTimer) { + LastTimer->clear(); + BackendTimer = false; + } + } + + LastTimer = new Timer("", Name, *TimingGroup); + LastTimer->startTimer(); + } +} + +void RecordKeeper::stopTimer() { + if (TimingGroup) { + assert(LastTimer && "No phase timer was started"); + LastTimer->stopTimer(); + } +} + +void RecordKeeper::startBackendTimer(StringRef Name) { + if (TimingGroup) { + startTimer(Name); + BackendTimer = true; + } +} + +void RecordKeeper::stopBackendTimer() { + if (TimingGroup) { + if (BackendTimer) { + stopTimer(); + BackendTimer = false; + } + } +} + +// We cache the record vectors for single classes. Many backends request +// the same vectors multiple times. +std::vector<Record *> RecordKeeper::getAllDerivedDefinitions( + StringRef ClassName) const { + + auto Pair = ClassRecordsMap.try_emplace(ClassName); + if (Pair.second) + Pair.first->second = getAllDerivedDefinitions(makeArrayRef(ClassName)); + + return Pair.first->second; +} + +std::vector<Record *> RecordKeeper::getAllDerivedDefinitions( + ArrayRef<StringRef> ClassNames) const { + SmallVector<Record *, 2> ClassRecs; + std::vector<Record *> Defs; + + assert(ClassNames.size() > 0 && "At least one class must be passed."); + for (const auto &ClassName : ClassNames) { + Record *Class = getClass(ClassName); + if (!Class) + PrintFatalError("The class '" + ClassName + "' is not defined\n"); + ClassRecs.push_back(Class); + } + + for (const auto &OneDef : getDefs()) { + if (all_of(ClassRecs, [&OneDef](const Record *Class) { + return OneDef.second->isSubClassOf(Class); + })) + Defs.push_back(OneDef.second.get()); + } return Defs; } diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp index 9e6cc947925d..94c79102c7cd 100644 --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -150,7 +150,7 @@ tgtok::TokKind TGLexer::LexToken(bool FileOrLineStart) { case EOF: // Lex next token, if we just left an include file. // Note that leaving an include file means that the next - // symbol is located at the end of 'include "..."' + // symbol is located at the end of the 'include "..."' // construct, so LexToken() is called with default // false parameter. if (processEOF()) @@ -161,7 +161,6 @@ tgtok::TokKind TGLexer::LexToken(bool FileOrLineStart) { case ':': return tgtok::colon; case ';': return tgtok::semi; - case '.': return tgtok::period; case ',': return tgtok::comma; case '<': return tgtok::less; case '>': return tgtok::greater; @@ -181,6 +180,19 @@ tgtok::TokKind TGLexer::LexToken(bool FileOrLineStart) { return tgtok::paste; + // The period is a separate case so we can recognize the "..." + // range punctuator. + case '.': + if (peekNextChar(0) == '.') { + ++CurPtr; // Eat second dot. + if (peekNextChar(0) == '.') { + ++CurPtr; // Eat third dot. + return tgtok::dotdotdot; + } + return ReturnError(TokStart, "Invalid '..' punctuation"); + } + return tgtok::dot; + case '\r': PrintFatalError("getNextChar() must never return '\r'"); return tgtok::Error; @@ -326,14 +338,9 @@ tgtok::TokKind TGLexer::LexIdentifier() { while (isalpha(*CurPtr) || isdigit(*CurPtr) || *CurPtr == '_') ++CurPtr; - // Check to see if this identifier is a keyword. + // Check to see if this identifier is a reserved keyword. StringRef Str(IdentStart, CurPtr-IdentStart); - if (Str == "include") { - if (LexInclude()) return tgtok::Error; - return Lex(); - } - tgtok::TokKind Kind = StringSwitch<tgtok::TokKind>(Str) .Case("int", tgtok::Int) .Case("bit", tgtok::Bit) @@ -344,6 +351,8 @@ tgtok::TokKind TGLexer::LexIdentifier() { .Case("dag", tgtok::Dag) .Case("class", tgtok::Class) .Case("def", tgtok::Def) + .Case("true", tgtok::TrueVal) + .Case("false", tgtok::FalseVal) .Case("foreach", tgtok::Foreach) .Case("defm", tgtok::Defm) .Case("defset", tgtok::Defset) @@ -352,13 +361,25 @@ tgtok::TokKind TGLexer::LexIdentifier() { .Case("let", tgtok::Let) .Case("in", tgtok::In) .Case("defvar", tgtok::Defvar) + .Case("include", tgtok::Include) .Case("if", tgtok::If) .Case("then", tgtok::Then) .Case("else", tgtok::ElseKW) + .Case("assert", tgtok::Assert) .Default(tgtok::Id); - if (Kind == tgtok::Id) - CurStrVal.assign(Str.begin(), Str.end()); + // A couple of tokens require special processing. + switch (Kind) { + case tgtok::Include: + if (LexInclude()) return tgtok::Error; + return Lex(); + case tgtok::Id: + CurStrVal.assign(Str.begin(), Str.end()); + break; + default: + break; + } + return Kind; } @@ -520,7 +541,7 @@ tgtok::TokKind TGLexer::LexBracket() { } } - return ReturnError(CodeStart-2, "Unterminated Code Block"); + return ReturnError(CodeStart - 2, "Unterminated code block"); } /// LexExclaim - Lex '!' and '![a-zA-Z]+'. @@ -550,9 +571,12 @@ tgtok::TokKind TGLexer::LexExclaim() { .Case("con", tgtok::XConcat) .Case("dag", tgtok::XDag) .Case("add", tgtok::XADD) + .Case("sub", tgtok::XSUB) .Case("mul", tgtok::XMUL) + .Case("not", tgtok::XNOT) .Case("and", tgtok::XAND) .Case("or", tgtok::XOR) + .Case("xor", tgtok::XXOR) .Case("shl", tgtok::XSHL) .Case("sra", tgtok::XSRA) .Case("srl", tgtok::XSRL) @@ -561,11 +585,14 @@ tgtok::TokKind TGLexer::LexExclaim() { .Case("subst", tgtok::XSubst) .Case("foldl", tgtok::XFoldl) .Case("foreach", tgtok::XForEach) + .Case("filter", tgtok::XFilter) .Case("listconcat", tgtok::XListConcat) .Case("listsplat", tgtok::XListSplat) .Case("strconcat", tgtok::XStrConcat) - .Case("setop", tgtok::XSetOp) - .Case("getop", tgtok::XGetOp) + .Case("interleave", tgtok::XInterleave) + .Case("substr", tgtok::XSubstr) + .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated. + .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated. .Default(tgtok::Error); return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator"); diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h index 5b3b0a44e3ef..2f322f705e0d 100644 --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -40,19 +40,25 @@ namespace tgtok { l_paren, r_paren, // ( ) less, greater, // < > colon, semi, // : ; - comma, period, // , . + comma, dot, // , . equal, question, // = ? paste, // # + dotdotdot, // ... - // Keywords. ('ElseKW' is named to distinguish it from the existing 'Else' - // that means the preprocessor #else.) - Bit, Bits, Class, Code, Dag, Def, Foreach, Defm, Field, In, Int, Let, List, - MultiClass, String, Defset, Defvar, If, Then, ElseKW, + // Reserved keywords. ('ElseKW' is named to distinguish it from the + // existing 'Else' that means the preprocessor #else.) + Assert, Bit, Bits, Class, Code, Dag, Def, Defm, Defset, Defvar, ElseKW, + FalseKW, Field, Foreach, If, In, Include, Int, Let, List, MultiClass, + String, Then, TrueKW, - // !keywords. - XConcat, XADD, XMUL, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XListSplat, - XStrConcat, XCast, XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, - XIf, XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetOp, XGetOp, + // Bang operators. + XConcat, XADD, XSUB, XMUL, XNOT, XAND, XOR, XXOR, XSRA, XSRL, XSHL, + XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XCast, + XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf, + XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp, + + // Boolean literals. + TrueVal, FalseVal, // Integer value. IntVal, @@ -80,8 +86,8 @@ class TGLexer { // Information about the current token. const char *TokStart = nullptr; tgtok::TokKind CurCode = tgtok::TokKind::Eof; - std::string CurStrVal; // This is valid for ID, STRVAL, VARNAME, CODEFRAGMENT - int64_t CurIntVal = 0; // This is valid for INTVAL. + std::string CurStrVal; // This is valid for Id, StrVal, VarName, CodeFragment + int64_t CurIntVal = 0; // This is valid for IntVal. /// CurBuffer - This is the current buffer index we're lexing from as managed /// by the SourceMgr object. diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp index 47f471ae2c4b..24949f0b2b4d 100644 --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -25,6 +25,7 @@ #include <algorithm> #include <cassert> #include <cstdint> +#include <limits> using namespace llvm; @@ -94,7 +95,7 @@ static void checkConcrete(Record &R) { // done merely because existing targets have legitimate cases of // non-concrete variables in helper defs. Ideally, we'd introduce a // 'maybe' or 'optional' modifier instead of this. - if (RV.getPrefix()) + if (RV.isNonconcreteOK()) continue; if (Init *V = RV.getValue()) { @@ -209,16 +210,16 @@ bool TGParser::SetValue(Record *CurRec, SMLoc Loc, Init *ValName, V = BitsInit::get(NewBits); } - if (RV->setValue(V)) { + if (RV->setValue(V, Loc)) { std::string InitType; if (BitsInit *BI = dyn_cast<BitsInit>(V)) InitType = (Twine("' of type bit initializer with length ") + Twine(BI->getNumBits())).str(); else if (TypedInit *TI = dyn_cast<TypedInit>(V)) InitType = (Twine("' of type '") + TI->getType()->getAsString()).str(); - return Error(Loc, "Value '" + ValName->getAsUnquotedString() + + return Error(Loc, "Field '" + ValName->getAsUnquotedString() + "' of type '" + RV->getType()->getAsString() + - "' is incompatible with initializer '" + + "' is incompatible with value '" + V->getAsString() + InitType + "'"); } return false; @@ -451,6 +452,8 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) { Rec->resolveReferences(); checkConcrete(*Rec); + CheckRecordAsserts(*Rec); + if (!isa<StringInit>(Rec->getNameInit())) { PrintError(Rec->getLoc(), Twine("record name '") + Rec->getNameInit()->getAsString() + @@ -481,11 +484,12 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) { // Parser Code //===----------------------------------------------------------------------===// -/// isObjectStart - Return true if this is a valid first token for an Object. +/// isObjectStart - Return true if this is a valid first token for a statement. static bool isObjectStart(tgtok::TokKind K) { - return K == tgtok::Class || K == tgtok::Def || K == tgtok::Defm || - K == tgtok::Let || K == tgtok::MultiClass || K == tgtok::Foreach || - K == tgtok::Defset || K == tgtok::Defvar || K == tgtok::If; + return K == tgtok::Assert || K == tgtok::Class || K == tgtok::Def || + K == tgtok::Defm || K == tgtok::Defset || K == tgtok::Defvar || + K == tgtok::Foreach || K == tgtok::If || K == tgtok::Let || + K == tgtok::MultiClass; } bool TGParser::consume(tgtok::TokKind K) { @@ -671,8 +675,10 @@ ParseSubMultiClassReference(MultiClass *CurMC) { /// ParseRangePiece - Parse a bit/value range. /// RangePiece ::= INTVAL +/// RangePiece ::= INTVAL '...' INTVAL /// RangePiece ::= INTVAL '-' INTVAL -/// RangePiece ::= INTVAL INTVAL +/// RangePiece ::= INTVAL INTVAL +// The last two forms are deprecated. bool TGParser::ParseRangePiece(SmallVectorImpl<unsigned> &Ranges, TypedInit *FirstItem) { Init *CurVal = FirstItem; @@ -693,6 +699,8 @@ bool TGParser::ParseRangePiece(SmallVectorImpl<unsigned> &Ranges, default: Ranges.push_back(Start); return false; + + case tgtok::dotdotdot: case tgtok::minus: { Lex.Lex(); // eat @@ -795,8 +803,8 @@ bool TGParser::ParseOptionalBitList(SmallVectorImpl<unsigned> &Ranges) { RecTy *TGParser::ParseType() { switch (Lex.getCode()) { default: TokError("Unknown token when expecting a type"); return nullptr; - case tgtok::String: Lex.Lex(); return StringRecTy::get(); - case tgtok::Code: Lex.Lex(); return CodeRecTy::get(); + case tgtok::String: + case tgtok::Code: Lex.Lex(); return StringRecTy::get(); case tgtok::Bit: Lex.Lex(); return BitRecTy::get(); case tgtok::Int: Lex.Lex(); return IntRecTy::get(); case tgtok::Dag: Lex.Lex(); return DagRecTy::get(); @@ -809,12 +817,12 @@ RecTy *TGParser::ParseType() { TokError("expected '<' after bits type"); return nullptr; } - if (Lex.Lex() != tgtok::IntVal) { // Eat '<' + if (Lex.Lex() != tgtok::IntVal) { // Eat '<' TokError("expected integer in bits<n> type"); return nullptr; } uint64_t Val = Lex.getCurIntVal(); - if (Lex.Lex() != tgtok::greater) { // Eat count. + if (Lex.Lex() != tgtok::greater) { // Eat count. TokError("expected '>' at end of bits<n> type"); return nullptr; } @@ -839,8 +847,7 @@ RecTy *TGParser::ParseType() { } } -/// ParseIDValue - This is just like ParseIDValue above, but it assumes the ID -/// has already been read. +/// ParseIDValue Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc, IDParseMode Mode) { if (CurRec) { @@ -902,14 +909,15 @@ Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc, Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { switch (Lex.getCode()) { default: - TokError("unknown operation"); + TokError("unknown bang operator"); return nullptr; + case tgtok::XNOT: case tgtok::XHead: case tgtok::XTail: case tgtok::XSize: case tgtok::XEmpty: case tgtok::XCast: - case tgtok::XGetOp: { // Value ::= !unop '(' Value ')' + case tgtok::XGetDagOp: { // Value ::= !unop '(' Value ')' UnOpInit::UnaryOp Code; RecTy *Type = nullptr; @@ -927,6 +935,11 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { } break; + case tgtok::XNOT: + Lex.Lex(); // eat the operation + Code = UnOpInit::NOT; + Type = IntRecTy::get(); + break; case tgtok::XHead: Lex.Lex(); // eat the operation Code = UnOpInit::HEAD; @@ -945,12 +958,12 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { Code = UnOpInit::EMPTY; Type = IntRecTy::get(); break; - case tgtok::XGetOp: + case tgtok::XGetDagOp: Lex.Lex(); // eat the operation if (Lex.getCode() == tgtok::less) { // Parse an optional type suffix, so that you can say - // !getop<BaseClass>(someDag) as a shorthand for - // !cast<BaseClass>(!getop(someDag)). + // !getdagop<BaseClass>(someDag) as a shorthand for + // !cast<BaseClass>(!getdagop(someDag)). Type = ParseOperatorType(); if (!Type) { @@ -959,13 +972,13 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { } if (!isa<RecordRecTy>(Type)) { - TokError("type for !getop must be a record type"); + TokError("type for !getdagop must be a record type"); // but keep parsing, to consume the operand } } else { Type = RecordRecTy::get({}); } - Code = UnOpInit::GETOP; + Code = UnOpInit::GETDAGOP; break; } if (!consume(tgtok::l_paren)) { @@ -976,56 +989,58 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { Init *LHS = ParseValue(CurRec); if (!LHS) return nullptr; - if (Code == UnOpInit::HEAD || - Code == UnOpInit::TAIL || - Code == UnOpInit::EMPTY) { + if (Code == UnOpInit::EMPTY || Code == UnOpInit::SIZE) { ListInit *LHSl = dyn_cast<ListInit>(LHS); StringInit *LHSs = dyn_cast<StringInit>(LHS); + DagInit *LHSd = dyn_cast<DagInit>(LHS); TypedInit *LHSt = dyn_cast<TypedInit>(LHS); - if (!LHSl && !LHSs && !LHSt) { - TokError("expected list or string type argument in unary operator"); + if (!LHSl && !LHSs && !LHSd && !LHSt) { + TokError("expected string, list, or dag type argument in unary operator"); return nullptr; } if (LHSt) { ListRecTy *LType = dyn_cast<ListRecTy>(LHSt->getType()); StringRecTy *SType = dyn_cast<StringRecTy>(LHSt->getType()); - if (!LType && !SType) { - TokError("expected list or string type argument in unary operator"); + DagRecTy *DType = dyn_cast<DagRecTy>(LHSt->getType()); + if (!LType && !SType && !DType) { + TokError("expected string, list, or dag type argument in unary operator"); return nullptr; } } + } - if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL || - Code == UnOpInit::SIZE) { - if (!LHSl && !LHSt) { + if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL) { + ListInit *LHSl = dyn_cast<ListInit>(LHS); + TypedInit *LHSt = dyn_cast<TypedInit>(LHS); + if (!LHSl && !LHSt) { + TokError("expected list type argument in unary operator"); + return nullptr; + } + if (LHSt) { + ListRecTy *LType = dyn_cast<ListRecTy>(LHSt->getType()); + if (!LType) { TokError("expected list type argument in unary operator"); return nullptr; } } - if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL) { - if (LHSl && LHSl->empty()) { - TokError("empty list argument in unary operator"); + if (LHSl && LHSl->empty()) { + TokError("empty list argument in unary operator"); + return nullptr; + } + if (LHSl) { + Init *Item = LHSl->getElement(0); + TypedInit *Itemt = dyn_cast<TypedInit>(Item); + if (!Itemt) { + TokError("untyped list element in unary operator"); return nullptr; } - if (LHSl) { - Init *Item = LHSl->getElement(0); - TypedInit *Itemt = dyn_cast<TypedInit>(Item); - if (!Itemt) { - TokError("untyped list element in unary operator"); - return nullptr; - } - Type = (Code == UnOpInit::HEAD) ? Itemt->getType() - : ListRecTy::get(Itemt->getType()); - } else { - assert(LHSt && "expected list type argument in unary operator"); - ListRecTy *LType = dyn_cast<ListRecTy>(LHSt->getType()); - if (!LType) { - TokError("expected list type argument in unary operator"); - return nullptr; - } - Type = (Code == UnOpInit::HEAD) ? LType->getElementType() : LType; - } + Type = (Code == UnOpInit::HEAD) ? Itemt->getType() + : ListRecTy::get(Itemt->getType()); + } else { + assert(LHSt && "expected list type argument in unary operator"); + ListRecTy *LType = dyn_cast<ListRecTy>(LHSt->getType()); + Type = (Code == UnOpInit::HEAD) ? LType->getElementType() : LType; } } @@ -1063,9 +1078,11 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { case tgtok::XConcat: case tgtok::XADD: + case tgtok::XSUB: case tgtok::XMUL: case tgtok::XAND: case tgtok::XOR: + case tgtok::XXOR: case tgtok::XSRA: case tgtok::XSRL: case tgtok::XSHL: @@ -1078,7 +1095,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { case tgtok::XListConcat: case tgtok::XListSplat: case tgtok::XStrConcat: - case tgtok::XSetOp: { // Value ::= !binop '(' Value ',' Value ')' + case tgtok::XInterleave: + case tgtok::XSetDagOp: { // Value ::= !binop '(' Value ',' Value ')' tgtok::TokKind OpTok = Lex.getCode(); SMLoc OpLoc = Lex.getLoc(); Lex.Lex(); // eat the operation @@ -1088,9 +1106,11 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { default: llvm_unreachable("Unhandled code!"); case tgtok::XConcat: Code = BinOpInit::CONCAT; break; case tgtok::XADD: Code = BinOpInit::ADD; break; + case tgtok::XSUB: Code = BinOpInit::SUB; break; case tgtok::XMUL: Code = BinOpInit::MUL; break; case tgtok::XAND: Code = BinOpInit::AND; break; case tgtok::XOR: Code = BinOpInit::OR; break; + case tgtok::XXOR: Code = BinOpInit::XOR; break; case tgtok::XSRA: Code = BinOpInit::SRA; break; case tgtok::XSRL: Code = BinOpInit::SRL; break; case tgtok::XSHL: Code = BinOpInit::SHL; break; @@ -1101,9 +1121,10 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { case tgtok::XGe: Code = BinOpInit::GE; break; case tgtok::XGt: Code = BinOpInit::GT; break; case tgtok::XListConcat: Code = BinOpInit::LISTCONCAT; break; - case tgtok::XListSplat: Code = BinOpInit::LISTSPLAT; break; - case tgtok::XStrConcat: Code = BinOpInit::STRCONCAT; break; - case tgtok::XSetOp: Code = BinOpInit::SETOP; break; + case tgtok::XListSplat: Code = BinOpInit::LISTSPLAT; break; + case tgtok::XStrConcat: Code = BinOpInit::STRCONCAT; break; + case tgtok::XInterleave: Code = BinOpInit::INTERLEAVE; break; + case tgtok::XSetDagOp: Code = BinOpInit::SETDAGOP; break; } RecTy *Type = nullptr; @@ -1112,31 +1133,30 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { default: llvm_unreachable("Unhandled code!"); case tgtok::XConcat: - case tgtok::XSetOp: + case tgtok::XSetDagOp: Type = DagRecTy::get(); ArgType = DagRecTy::get(); break; case tgtok::XAND: case tgtok::XOR: + case tgtok::XXOR: case tgtok::XSRA: case tgtok::XSRL: case tgtok::XSHL: case tgtok::XADD: + case tgtok::XSUB: case tgtok::XMUL: Type = IntRecTy::get(); ArgType = IntRecTy::get(); break; case tgtok::XEq: case tgtok::XNe: - Type = BitRecTy::get(); - // ArgType for Eq / Ne is not known at this point - break; case tgtok::XLe: case tgtok::XLt: case tgtok::XGe: case tgtok::XGt: Type = BitRecTy::get(); - ArgType = IntRecTy::get(); + // ArgType for the comparison operators is not yet known. break; case tgtok::XListConcat: // We don't know the list type until we parse the first argument @@ -1149,6 +1169,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { Type = StringRecTy::get(); ArgType = StringRecTy::get(); break; + case tgtok::XInterleave: + Type = StringRecTy::get(); + // The first argument type is not yet known. } if (Type && ItemType && !Type->typeIsConvertibleTo(ItemType)) { @@ -1165,6 +1188,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { SmallVector<Init*, 2> InitList; + // Note that this loop consumes an arbitrary number of arguments. + // The actual count is checked later. for (;;) { SMLoc InitLoc = Lex.getLoc(); InitList.push_back(ParseValue(CurRec, ArgType)); @@ -1177,7 +1202,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { return nullptr; } RecTy *ListType = InitListBack->getType(); + if (!ArgType) { + // Argument type must be determined from the argument itself. ArgType = ListType; switch (Code) { @@ -1218,15 +1245,54 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { case BinOpInit::EQ: case BinOpInit::NE: if (!ArgType->typeIsConvertibleTo(IntRecTy::get()) && + !ArgType->typeIsConvertibleTo(StringRecTy::get()) && + !ArgType->typeIsConvertibleTo(RecordRecTy::get({}))) { + Error(InitLoc, Twine("expected bit, bits, int, string, or record; " + "got value of type '") + ArgType->getAsString() + + "'"); + return nullptr; + } + break; + case BinOpInit::LE: + case BinOpInit::LT: + case BinOpInit::GE: + case BinOpInit::GT: + if (!ArgType->typeIsConvertibleTo(IntRecTy::get()) && !ArgType->typeIsConvertibleTo(StringRecTy::get())) { - Error(InitLoc, Twine("expected int, bits, or string; got value of " - "type '") + ArgType->getAsString() + "'"); + Error(InitLoc, Twine("expected bit, bits, int, or string; " + "got value of type '") + ArgType->getAsString() + + "'"); return nullptr; } break; + case BinOpInit::INTERLEAVE: + switch (InitList.size()) { + case 1: // First argument must be a list of strings or integers. + if (ArgType != StringRecTy::get()->getListTy() && + !ArgType->typeIsConvertibleTo(IntRecTy::get()->getListTy())) { + Error(InitLoc, Twine("expected list of string, int, bits, or bit; " + "got value of type '") + + ArgType->getAsString() + "'"); + return nullptr; + } + break; + case 2: // Second argument must be a string. + if (!isa<StringRecTy>(ArgType)) { + Error(InitLoc, Twine("expected second argument to be a string, " + "got value of type '") + + ArgType->getAsString() + "'"); + return nullptr; + } + break; + default: ; + } + ArgType = nullptr; // Broken invariant: types not identical. + break; default: llvm_unreachable("other ops have fixed argument types"); } + } else { + // Desired argument type is a known and in ArgType. RecTy *Resolved = resolveTypes(ArgType, ListType); if (!Resolved) { Error(InitLoc, Twine("expected value of type '") + @@ -1234,8 +1300,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { ListType->getAsString() + "'"); return nullptr; } - if (Code != BinOpInit::ADD && Code != BinOpInit::AND && - Code != BinOpInit::OR && Code != BinOpInit::SRA && + if (Code != BinOpInit::ADD && Code != BinOpInit::SUB && + Code != BinOpInit::AND && Code != BinOpInit::OR && + Code != BinOpInit::XOR && Code != BinOpInit::SRA && Code != BinOpInit::SRL && Code != BinOpInit::SHL && Code != BinOpInit::MUL) ArgType = Resolved; @@ -1244,7 +1311,7 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { // Deal with BinOps whose arguments have different types, by // rewriting ArgType in between them. switch (Code) { - case BinOpInit::SETOP: + case BinOpInit::SETDAGOP: // After parsing the first dag argument, switch to expecting // a record, with no restriction on its superclasses. ArgType = RecordRecTy::get({}); @@ -1274,7 +1341,7 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { if (Code == BinOpInit::STRCONCAT || Code == BinOpInit::LISTCONCAT || Code == BinOpInit::CONCAT || Code == BinOpInit::ADD || Code == BinOpInit::AND || Code == BinOpInit::OR || - Code == BinOpInit::MUL) { + Code == BinOpInit::XOR || Code == BinOpInit::MUL) { while (InitList.size() > 2) { Init *RHS = InitList.pop_back_val(); RHS = (BinOpInit::get(Code, InitList.back(), RHS, Type))->Fold(CurRec); @@ -1290,118 +1357,14 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { return nullptr; } - case tgtok::XForEach: { // Value ::= !foreach '(' Id ',' Value ',' Value ')' - SMLoc OpLoc = Lex.getLoc(); - Lex.Lex(); // eat the operation - if (Lex.getCode() != tgtok::l_paren) { - TokError("expected '(' after !foreach"); - return nullptr; - } - - if (Lex.Lex() != tgtok::Id) { // eat the '(' - TokError("first argument of !foreach must be an identifier"); - return nullptr; - } - - Init *LHS = StringInit::get(Lex.getCurStrVal()); - Lex.Lex(); - - if (CurRec && CurRec->getValue(LHS)) { - TokError((Twine("iteration variable '") + LHS->getAsString() + - "' already defined") - .str()); - return nullptr; - } - - if (!consume(tgtok::comma)) { // eat the id - TokError("expected ',' in ternary operator"); - return nullptr; - } - - Init *MHS = ParseValue(CurRec); - if (!MHS) - return nullptr; - - if (!consume(tgtok::comma)) { - TokError("expected ',' in ternary operator"); - return nullptr; - } - - TypedInit *MHSt = dyn_cast<TypedInit>(MHS); - if (!MHSt) { - TokError("could not get type of !foreach input"); - return nullptr; - } - - RecTy *InEltType = nullptr; - RecTy *OutEltType = nullptr; - bool IsDAG = false; - - if (ListRecTy *InListTy = dyn_cast<ListRecTy>(MHSt->getType())) { - InEltType = InListTy->getElementType(); - if (ItemType) { - if (ListRecTy *OutListTy = dyn_cast<ListRecTy>(ItemType)) { - OutEltType = OutListTy->getElementType(); - } else { - Error(OpLoc, - "expected value of type '" + Twine(ItemType->getAsString()) + - "', but got !foreach of list type"); - return nullptr; - } - } - } else if (DagRecTy *InDagTy = dyn_cast<DagRecTy>(MHSt->getType())) { - InEltType = InDagTy; - if (ItemType && !isa<DagRecTy>(ItemType)) { - Error(OpLoc, - "expected value of type '" + Twine(ItemType->getAsString()) + - "', but got !foreach of dag type"); - return nullptr; - } - IsDAG = true; - } else { - TokError("!foreach must have list or dag input"); - return nullptr; - } - - // We need to create a temporary record to provide a scope for the iteration - // variable while parsing top-level foreach's. - std::unique_ptr<Record> ParseRecTmp; - Record *ParseRec = CurRec; - if (!ParseRec) { - ParseRecTmp = std::make_unique<Record>(".parse", ArrayRef<SMLoc>{}, Records); - ParseRec = ParseRecTmp.get(); - } - - ParseRec->addValue(RecordVal(LHS, InEltType, false)); - Init *RHS = ParseValue(ParseRec, OutEltType); - ParseRec->removeValue(LHS); - if (!RHS) - return nullptr; - - if (!consume(tgtok::r_paren)) { - TokError("expected ')' in binary operator"); - return nullptr; - } - - RecTy *OutType; - if (IsDAG) { - OutType = InEltType; - } else { - TypedInit *RHSt = dyn_cast<TypedInit>(RHS); - if (!RHSt) { - TokError("could not get type of !foreach result"); - return nullptr; - } - OutType = RHSt->getType()->getListTy(); - } - - return (TernOpInit::get(TernOpInit::FOREACH, LHS, MHS, RHS, OutType)) - ->Fold(CurRec); + case tgtok::XForEach: + case tgtok::XFilter: { + return ParseOperationForEachFilter(CurRec, ItemType); } case tgtok::XDag: case tgtok::XIf: - case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' + case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' TernOpInit::TernaryOp Code; RecTy *Type = nullptr; @@ -1536,11 +1499,14 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec); } + case tgtok::XSubstr: + return ParseOperationSubstr(CurRec, ItemType); + case tgtok::XCond: return ParseOperationCond(CurRec, ItemType); case tgtok::XFoldl: { - // Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')' + // Value ::= !foldl '(' Value ',' Value ',' Id ',' Id ',' Expr ')' Lex.Lex(); // eat the operation if (!consume(tgtok::l_paren)) { TokError("expected '(' after !foldl"); @@ -1623,8 +1589,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { } Lex.Lex(); // eat the ',' - // We need to create a temporary record to provide a scope for the iteration - // variable while parsing top-level foreach's. + // We need to create a temporary record to provide a scope for the + // two variables. std::unique_ptr<Record> ParseRecTmp; Record *ParseRec = CurRec; if (!ParseRec) { @@ -1632,8 +1598,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { ParseRec = ParseRecTmp.get(); } - ParseRec->addValue(RecordVal(A, Start->getType(), false)); - ParseRec->addValue(RecordVal(B, ListType->getElementType(), false)); + ParseRec->addValue(RecordVal(A, Start->getType(), RecordVal::FK_Normal)); + ParseRec->addValue(RecordVal(B, ListType->getElementType(), + RecordVal::FK_Normal)); Init *ExprUntyped = ParseValue(ParseRec); ParseRec->removeValue(A); ParseRec->removeValue(B); @@ -1677,6 +1644,9 @@ RecTy *TGParser::ParseOperatorType() { return nullptr; } + if (Lex.getCode() == tgtok::Code) + TokError("the 'code' type is not allowed in bang operators; use 'string'"); + Type = ParseType(); if (!Type) { @@ -1692,6 +1662,221 @@ RecTy *TGParser::ParseOperatorType() { return Type; } +/// Parse the !substr operation. Return null on error. +/// +/// Substr ::= !substr(string, start-int [, length-int]) => string +Init *TGParser::ParseOperationSubstr(Record *CurRec, RecTy *ItemType) { + TernOpInit::TernaryOp Code = TernOpInit::SUBSTR; + RecTy *Type = StringRecTy::get(); + + Lex.Lex(); // eat the operation + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after !substr operator"); + return nullptr; + } + + Init *LHS = ParseValue(CurRec); + if (!LHS) + return nullptr; + + if (!consume(tgtok::comma)) { + TokError("expected ',' in !substr operator"); + return nullptr; + } + + SMLoc MHSLoc = Lex.getLoc(); + Init *MHS = ParseValue(CurRec); + if (!MHS) + return nullptr; + + SMLoc RHSLoc = Lex.getLoc(); + Init *RHS; + if (consume(tgtok::comma)) { + RHSLoc = Lex.getLoc(); + RHS = ParseValue(CurRec); + if (!RHS) + return nullptr; + } else { + RHS = IntInit::get(std::numeric_limits<int64_t>::max()); + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !substr operator"); + return nullptr; + } + + if (ItemType && !Type->typeIsConvertibleTo(ItemType)) { + Error(RHSLoc, Twine("expected value of type '") + + ItemType->getAsString() + "', got '" + + Type->getAsString() + "'"); + } + + TypedInit *LHSt = dyn_cast<TypedInit>(LHS); + if (!LHSt && !isa<UnsetInit>(LHS)) { + TokError("could not determine type of the string in !substr"); + return nullptr; + } + if (LHSt && !isa<StringRecTy>(LHSt->getType())) { + TokError(Twine("expected string, got type '") + + LHSt->getType()->getAsString() + "'"); + return nullptr; + } + + TypedInit *MHSt = dyn_cast<TypedInit>(MHS); + if (!MHSt && !isa<UnsetInit>(MHS)) { + TokError("could not determine type of the start position in !substr"); + return nullptr; + } + if (MHSt && !isa<IntRecTy>(MHSt->getType())) { + Error(MHSLoc, Twine("expected int, got type '") + + MHSt->getType()->getAsString() + "'"); + return nullptr; + } + + if (RHS) { + TypedInit *RHSt = dyn_cast<TypedInit>(RHS); + if (!RHSt && !isa<UnsetInit>(RHS)) { + TokError("could not determine type of the length in !substr"); + return nullptr; + } + if (RHSt && !isa<IntRecTy>(RHSt->getType())) { + TokError(Twine("expected int, got type '") + + RHSt->getType()->getAsString() + "'"); + return nullptr; + } + } + + return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec); +} + +/// Parse the !foreach and !filter operations. Return null on error. +/// +/// ForEach ::= !foreach(ID, list-or-dag, expr) => list<expr type> +/// Filter ::= !foreach(ID, list, predicate) ==> list<list type> +Init *TGParser::ParseOperationForEachFilter(Record *CurRec, RecTy *ItemType) { + SMLoc OpLoc = Lex.getLoc(); + tgtok::TokKind Operation = Lex.getCode(); + Lex.Lex(); // eat the operation + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after !foreach/!filter"); + return nullptr; + } + + if (Lex.Lex() != tgtok::Id) { // eat the '(' + TokError("first argument of !foreach/!filter must be an identifier"); + return nullptr; + } + + Init *LHS = StringInit::get(Lex.getCurStrVal()); + Lex.Lex(); // eat the ID. + + if (CurRec && CurRec->getValue(LHS)) { + TokError((Twine("iteration variable '") + LHS->getAsString() + + "' is already defined") + .str()); + return nullptr; + } + + if (!consume(tgtok::comma)) { + TokError("expected ',' in !foreach/!filter"); + return nullptr; + } + + Init *MHS = ParseValue(CurRec); + if (!MHS) + return nullptr; + + if (!consume(tgtok::comma)) { + TokError("expected ',' in !foreach/!filter"); + return nullptr; + } + + TypedInit *MHSt = dyn_cast<TypedInit>(MHS); + if (!MHSt) { + TokError("could not get type of !foreach/!filter list or dag"); + return nullptr; + } + + RecTy *InEltType = nullptr; + RecTy *ExprEltType = nullptr; + bool IsDAG = false; + + if (ListRecTy *InListTy = dyn_cast<ListRecTy>(MHSt->getType())) { + InEltType = InListTy->getElementType(); + if (ItemType) { + if (ListRecTy *OutListTy = dyn_cast<ListRecTy>(ItemType)) { + ExprEltType = (Operation == tgtok::XForEach) + ? OutListTy->getElementType() + : IntRecTy::get(); + } else { + Error(OpLoc, + "expected value of type '" + + Twine(ItemType->getAsString()) + + "', but got list type"); + return nullptr; + } + } + } else if (DagRecTy *InDagTy = dyn_cast<DagRecTy>(MHSt->getType())) { + if (Operation == tgtok::XFilter) { + TokError("!filter must have a list argument"); + return nullptr; + } + InEltType = InDagTy; + if (ItemType && !isa<DagRecTy>(ItemType)) { + Error(OpLoc, + "expected value of type '" + Twine(ItemType->getAsString()) + + "', but got dag type"); + return nullptr; + } + IsDAG = true; + } else { + if (Operation == tgtok::XForEach) + TokError("!foreach must have a list or dag argument"); + else + TokError("!filter must have a list argument"); + return nullptr; + } + + // We need to create a temporary record to provide a scope for the + // iteration variable. + std::unique_ptr<Record> ParseRecTmp; + Record *ParseRec = CurRec; + if (!ParseRec) { + ParseRecTmp = + std::make_unique<Record>(".parse", ArrayRef<SMLoc>{}, Records); + ParseRec = ParseRecTmp.get(); + } + + ParseRec->addValue(RecordVal(LHS, InEltType, RecordVal::FK_Normal)); + Init *RHS = ParseValue(ParseRec, ExprEltType); + ParseRec->removeValue(LHS); + if (!RHS) + return nullptr; + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !foreach/!filter"); + return nullptr; + } + + RecTy *OutType = InEltType; + if (Operation == tgtok::XForEach && !IsDAG) { + TypedInit *RHSt = dyn_cast<TypedInit>(RHS); + if (!RHSt) { + TokError("could not get type of !foreach result expression"); + return nullptr; + } + OutType = RHSt->getType()->getListTy(); + } else if (Operation == tgtok::XFilter) { + OutType = InEltType->getListTy(); + } + + return (TernOpInit::get((Operation == tgtok::XForEach) ? TernOpInit::FOREACH + : TernOpInit::FILTER, + LHS, MHS, RHS, OutType)) + ->Fold(CurRec); +} + Init *TGParser::ParseOperationCond(Record *CurRec, RecTy *ItemType) { Lex.Lex(); // eat the operation 'cond' @@ -1783,6 +1968,7 @@ Init *TGParser::ParseOperationCond(Record *CurRec, RecTy *ItemType) { /// SimpleValue ::= '(' IDValue DagArgList ')' /// SimpleValue ::= CONCATTOK '(' Value ',' Value ')' /// SimpleValue ::= ADDTOK '(' Value ',' Value ')' +/// SimpleValue ::= SUBTOK '(' Value ',' Value ')' /// SimpleValue ::= SHLTOK '(' Value ',' Value ')' /// SimpleValue ::= SRATOK '(' Value ',' Value ')' /// SimpleValue ::= SRLTOK '(' Value ',' Value ')' @@ -1795,8 +1981,20 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { Init *R = nullptr; switch (Lex.getCode()) { - default: TokError("Unknown token when parsing a value"); break; - case tgtok::IntVal: R = IntInit::get(Lex.getCurIntVal()); Lex.Lex(); break; + default: TokError("Unknown or reserved token when parsing a value"); break; + + case tgtok::TrueVal: + R = IntInit::get(1); + Lex.Lex(); + break; + case tgtok::FalseVal: + R = IntInit::get(0); + Lex.Lex(); + break; + case tgtok::IntVal: + R = IntInit::get(Lex.getCurIntVal()); + Lex.Lex(); + break; case tgtok::BinaryIntVal: { auto BinaryVal = Lex.getCurBinaryIntVal(); SmallVector<Init*, 16> Bits(BinaryVal.second); @@ -1820,7 +2018,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, break; } case tgtok::CodeFragment: - R = CodeInit::get(Lex.getCurStrVal(), Lex.getLoc()); + R = StringInit::get(Lex.getCurStrVal(), StringInit::SF_Code); Lex.Lex(); break; case tgtok::question: @@ -1951,7 +2149,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, if (ItemType) { ListRecTy *ListType = dyn_cast<ListRecTy>(ItemType); if (!ListType) { - TokError(Twine("Type mismatch for list, expected list type, got ") + + TokError(Twine("Encountered a list when expecting a ") + ItemType->getAsString()); return nullptr; } @@ -2035,7 +2233,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, case tgtok::l_paren: { // Value ::= '(' IDValue DagArgList ')' Lex.Lex(); // eat the '(' if (Lex.getCode() != tgtok::Id && Lex.getCode() != tgtok::XCast && - Lex.getCode() != tgtok::question && Lex.getCode() != tgtok::XGetOp) { + Lex.getCode() != tgtok::question && Lex.getCode() != tgtok::XGetDagOp) { TokError("expected identifier in dag init"); return nullptr; } @@ -2073,14 +2271,17 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, case tgtok::XSize: case tgtok::XEmpty: case tgtok::XCast: - case tgtok::XGetOp: // Value ::= !unop '(' Value ')' + case tgtok::XGetDagOp: // Value ::= !unop '(' Value ')' case tgtok::XIsA: case tgtok::XConcat: case tgtok::XDag: case tgtok::XADD: + case tgtok::XSUB: case tgtok::XMUL: + case tgtok::XNOT: case tgtok::XAND: case tgtok::XOR: + case tgtok::XXOR: case tgtok::XSRA: case tgtok::XSRL: case tgtok::XSHL: @@ -2093,12 +2294,15 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, case tgtok::XListConcat: case tgtok::XListSplat: case tgtok::XStrConcat: - case tgtok::XSetOp: // Value ::= !binop '(' Value ',' Value ')' + case tgtok::XInterleave: + case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')' case tgtok::XIf: case tgtok::XCond: case tgtok::XFoldl: case tgtok::XForEach: - case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' + case tgtok::XFilter: + case tgtok::XSubst: + case tgtok::XSubstr: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' return ParseOperation(CurRec, ItemType); } } @@ -2106,7 +2310,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, return R; } -/// ParseValue - Parse a tblgen value. This returns null on error. +/// ParseValue - Parse a TableGen value. This returns null on error. /// /// Value ::= SimpleValue ValueSuffix* /// ValueSuffix ::= '{' BitList '}' @@ -2167,8 +2371,8 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { } break; } - case tgtok::period: { - if (Lex.Lex() != tgtok::Id) { // eat the . + case tgtok::dot: { + if (Lex.Lex() != tgtok::Id) { // eat the . TokError("expected field identifier after '.'"); return nullptr; } @@ -2195,6 +2399,8 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { if (isa<ListRecTy>(LHS->getType())) { Lex.Lex(); // Eat the '#'. + assert(Mode == ParseValueMode && "encountered paste of lists in name"); + switch (Lex.getCode()) { case tgtok::colon: case tgtok::semi: @@ -2202,8 +2408,11 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { Result = LHS; // trailing paste, ignore. break; default: - Init *RHSResult = ParseValue(CurRec, ItemType, ParseNameMode); + Init *RHSResult = ParseValue(CurRec, ItemType, ParseValueMode); + if (!RHSResult) + return nullptr; Result = BinOpInit::getListConcat(LHS, RHSResult); + break; } break; } @@ -2239,6 +2448,8 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { default: Init *RHSResult = ParseValue(CurRec, nullptr, ParseNameMode); + if (!RHSResult) + return nullptr; RHS = dyn_cast<TypedInit>(RHSResult); if (!RHS) { Error(PasteLoc, "RHS of paste is not typed!"); @@ -2410,8 +2621,10 @@ Init *TGParser::ParseDeclaration(Record *CurRec, "::"); } - // Add the value. - if (AddValue(CurRec, IdLoc, RecordVal(DeclName, Type, HasField))) + // Add the field to the record. + if (AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type, + HasField ? RecordVal::FK_NonconcreteOK + : RecordVal::FK_Normal))) return nullptr; // If a value is present, parse it. @@ -2552,12 +2765,16 @@ bool TGParser::ParseTemplateArgList(Record *CurRec) { return false; } -/// ParseBodyItem - Parse a single item at within the body of a def or class. +/// ParseBodyItem - Parse a single item within the body of a def or class. /// /// BodyItem ::= Declaration ';' /// BodyItem ::= LET ID OptionalBitList '=' Value ';' /// BodyItem ::= Defvar +/// BodyItem ::= Assert bool TGParser::ParseBodyItem(Record *CurRec) { + if (Lex.getCode() == tgtok::Assert) + return ParseAssert(nullptr, CurRec); + if (Lex.getCode() == tgtok::Defvar) return ParseDefvar(); @@ -2619,7 +2836,7 @@ bool TGParser::ParseBody(Record *CurRec) { return false; if (!consume(tgtok::l_brace)) - return TokError("Expected ';' or '{' to start body"); + return TokError("Expected '{' to start body or ';' for declaration only"); // An object body introduces a new scope for local variables. TGLocalVarScope *BodyScope = PushLocalScope(); @@ -2632,6 +2849,14 @@ bool TGParser::ParseBody(Record *CurRec) { // Eat the '}'. Lex.Lex(); + + // If we have a semicolon, print a gentle error. + SMLoc SemiLoc = Lex.getLoc(); + if (consume(tgtok::semi)) { + PrintError(SemiLoc, "A class or def body should not end with a semicolon"); + PrintNote("Semicolon ignored; remove to eliminate this error"); + } + return false; } @@ -2963,6 +3188,41 @@ bool TGParser::ParseIfBody(MultiClass *CurMultiClass, StringRef Kind) { return false; } +/// ParseAssert - Parse an assert statement. +/// +/// Assert ::= ASSERT condition , message ; +bool TGParser::ParseAssert(MultiClass *CurMultiClass, Record *CurRec) { + assert(Lex.getCode() == tgtok::Assert && "Unknown tok"); + Lex.Lex(); // Eat the 'assert' token. + + SMLoc ConditionLoc = Lex.getLoc(); + Init *Condition = ParseValue(CurRec); + if (!Condition) + return true; + + if (!consume(tgtok::comma)) { + TokError("expected ',' in assert statement"); + return true; + } + + Init *Message = ParseValue(CurRec); + if (!Message) + return true; + + if (!consume(tgtok::semi)) + return TokError("expected ';'"); + + if (CurMultiClass) { + assert(false && "assert in multiclass not yet supported"); + } else if (CurRec) { + CurRec->addAssertion(ConditionLoc, Condition, Message); + } else { // at top level + CheckAssert(ConditionLoc, Condition, Message); + } + + return false; +} + /// ParseClass - Parse a tblgen class definition. /// /// ClassInst ::= CLASS ID TemplateArgList? ObjectBody @@ -3162,14 +3422,17 @@ bool TGParser::ParseMultiClass() { while (Lex.getCode() != tgtok::r_brace) { switch (Lex.getCode()) { default: - return TokError("expected 'let', 'def', 'defm', 'defvar', 'foreach' " - "or 'if' in multiclass body"); - case tgtok::Let: + return TokError("expected 'assert', 'def', 'defm', 'defvar', " + "'foreach', 'if', or 'let' in multiclass body"); + case tgtok::Assert: + return TokError("an assert statement in a multiclass is not yet supported"); + case tgtok::Def: case tgtok::Defm: case tgtok::Defvar: case tgtok::Foreach: case tgtok::If: + case tgtok::Let: if (ParseObject(CurMultiClass)) return true; break; @@ -3177,6 +3440,13 @@ bool TGParser::ParseMultiClass() { } Lex.Lex(); // eat the '}'. + // If we have a semicolon, print a gentle error. + SMLoc SemiLoc = Lex.getLoc(); + if (consume(tgtok::semi)) { + PrintError(SemiLoc, "A multiclass body should not end with a semicolon"); + PrintNote("Semicolon ignored; remove to eliminate this error"); + } + PopLocalScope(MulticlassScope); } @@ -3320,22 +3590,23 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) { /// Object ::= LETCommand Object /// Object ::= Defset /// Object ::= Defvar +/// Object ::= Assert bool TGParser::ParseObject(MultiClass *MC) { switch (Lex.getCode()) { default: - return TokError("Expected class, def, defm, defset, multiclass, let, " - "foreach or if"); - case tgtok::Let: return ParseTopLevelLet(MC); - case tgtok::Def: return ParseDef(MC); - case tgtok::Foreach: return ParseForeach(MC); - case tgtok::If: return ParseIf(MC); - case tgtok::Defm: return ParseDefm(MC); + return TokError( + "Expected assert, class, def, defm, defset, foreach, if, or let"); + case tgtok::Assert: return ParseAssert(MC, nullptr); + case tgtok::Def: return ParseDef(MC); + case tgtok::Defm: return ParseDefm(MC); + case tgtok::Defvar: return ParseDefvar(); + case tgtok::Foreach: return ParseForeach(MC); + case tgtok::If: return ParseIf(MC); + case tgtok::Let: return ParseTopLevelLet(MC); case tgtok::Defset: if (MC) return TokError("defset is not allowed inside multiclass"); return ParseDefset(); - case tgtok::Defvar: - return ParseDefvar(); case tgtok::Class: if (MC) return TokError("class is not allowed inside multiclass"); @@ -3367,7 +3638,38 @@ bool TGParser::ParseFile() { if (Lex.getCode() == tgtok::Eof) return false; - return TokError("Unexpected input at top level"); + return TokError("Unexpected token at top level"); +} + +// Check an assertion: Obtain the condition value and be sure it is true. +// If not, print a nonfatal error along with the message. +void TGParser::CheckAssert(SMLoc Loc, Init *Condition, Init *Message) { + auto *CondValue = dyn_cast_or_null<IntInit>( + Condition->convertInitializerTo(IntRecTy::get())); + if (CondValue) { + if (!CondValue->getValue()) { + PrintError(Loc, "assertion failed"); + if (auto *MessageInit = dyn_cast<StringInit>(Message)) + PrintNote(MessageInit->getValue()); + else + PrintNote("(assert message is not a string)"); + } + } else { + PrintError(Loc, "assert condition must of type bit, bits, or int."); + } +} + +// Check all record assertions: For each one, resolve the condition +// and message, then call CheckAssert(). +void TGParser::CheckRecordAsserts(Record &Rec) { + RecordResolver R(Rec); + R.setFinal(true); + + for (auto Assertion : Rec.getAssertions()) { + Init *Condition = std::get<1>(Assertion)->resolveReferences(R); + Init *Message = std::get<2>(Assertion)->resolveReferences(R); + CheckAssert(std::get<0>(Assertion), Condition, Message); + } } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h index 07a4003219f5..578a56c9d01c 100644 --- a/llvm/lib/TableGen/TGParser.h +++ b/llvm/lib/TableGen/TGParser.h @@ -222,6 +222,7 @@ private: // Parser methods. bool ParseForeach(MultiClass *CurMultiClass); bool ParseIf(MultiClass *CurMultiClass); bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind); + bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec); bool ParseTopLevelLet(MultiClass *CurMultiClass); void ParseLetList(SmallVectorImpl<LetRecord> &Result); @@ -254,6 +255,8 @@ private: // Parser methods. TypedInit *FirstItem = nullptr); RecTy *ParseType(); Init *ParseOperation(Record *CurRec, RecTy *ItemType); + Init *ParseOperationSubstr(Record *CurRec, RecTy *ItemType); + Init *ParseOperationForEachFilter(Record *CurRec, RecTy *ItemType); Init *ParseOperationCond(Record *CurRec, RecTy *ItemType); RecTy *ParseOperatorType(); Init *ParseObjectName(MultiClass *CurMultiClass); @@ -261,6 +264,8 @@ private: // Parser methods. MultiClass *ParseMultiClassID(); bool ApplyLetStack(Record *CurRec); bool ApplyLetStack(RecordsEntry &Entry); + void CheckAssert(SMLoc Loc, Init *Condition, Init *Message); + void CheckRecordAsserts(Record &Rec); }; } // end namespace llvm diff --git a/llvm/lib/TableGen/TableGenBackendSkeleton.cpp b/llvm/lib/TableGen/TableGenBackendSkeleton.cpp new file mode 100644 index 000000000000..4ce88e003e65 --- /dev/null +++ b/llvm/lib/TableGen/TableGenBackendSkeleton.cpp @@ -0,0 +1,64 @@ +//===- SkeletonEmitter.cpp - Skeleton TableGen backend -*- 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 emits ... +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.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 <algorithm> +#include <set> +#include <string> +#include <vector> + +#define DEBUG_TYPE "skeleton-emitter" + +using namespace llvm; + +namespace { + +// Any helper data structures can be defined here. Some backends use +// structs to collect information from the records. + +class SkeletonEmitter { +private: + RecordKeeper &Records; + +public: + SkeletonEmitter(RecordKeeper &RK) : Records(RK) {} + + void run(raw_ostream &OS); +}; // emitter class + +} // anonymous namespace + +void SkeletonEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("Skeleton data structures", OS); + + (void)Records; // To suppress unused variable warning; remove on use. +} + +namespace llvm { + +// The only thing that should be in the llvm namespace is the +// emitter entry point function. + +void EmitSkeleton(RecordKeeper &RK, raw_ostream &OS) { + // Instantiate the emitter class and invoke run(). + SkeletonEmitter(RK).run(OS); +} + +} // namespace llvm |