aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/TableGen
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
commitb60736ec1405bb0a8dd40989f67ef4c93da068ab (patch)
tree5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /llvm/lib/TableGen
parentcfca06d7963fa0909f90483b42a6d7d194d01e08 (diff)
downloadsrc-b60736ec1405bb0a8dd40989f67ef4c93da068ab.tar.gz
src-b60736ec1405bb0a8dd40989f67ef4c93da068ab.zip
Diffstat (limited to 'llvm/lib/TableGen')
-rw-r--r--llvm/lib/TableGen/DetailedRecordsBackend.cpp203
-rw-r--r--llvm/lib/TableGen/Error.cpp85
-rw-r--r--llvm/lib/TableGen/JSONBackend.cpp5
-rw-r--r--llvm/lib/TableGen/Main.cpp43
-rw-r--r--llvm/lib/TableGen/Record.cpp519
-rw-r--r--llvm/lib/TableGen/TGLexer.cpp53
-rw-r--r--llvm/lib/TableGen/TGLexer.h28
-rw-r--r--llvm/lib/TableGen/TGParser.cpp722
-rw-r--r--llvm/lib/TableGen/TGParser.h5
-rw-r--r--llvm/lib/TableGen/TableGenBackendSkeleton.cpp64
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