diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/TableGen')
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/DetailedRecordsBackend.cpp | 203 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/Error.cpp | 173 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/JSONBackend.cpp | 185 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/Main.cpp | 164 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/Parser.cpp | 41 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/Record.cpp | 3069 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/SetTheory.cpp | 332 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/StringMatcher.cpp | 155 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/TGLexer.cpp | 1044 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/TGLexer.h | 383 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/TGParser.cpp | 3931 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/TGParser.h | 289 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/TableGenBackend.cpp | 53 | ||||
| -rw-r--r-- | contrib/llvm-project/llvm/lib/TableGen/TableGenBackendSkeleton.cpp | 59 |
14 files changed, 10081 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/TableGen/DetailedRecordsBackend.cpp b/contrib/llvm-project/llvm/lib/TableGen/DetailedRecordsBackend.cpp new file mode 100644 index 000000000000..500aa4c78225 --- /dev/null +++ b/contrib/llvm-project/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/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <map> +#include <memory> +#include <string> +#include <utility> + +#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(StringRef 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(); + std::string Name = Rec->getNameInitAsString(); + OS << formatv("\n{0} |{1}|\n", Name.empty() ? "\"\"" : Name, + 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(StringRef 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/contrib/llvm-project/llvm/lib/TableGen/Error.cpp b/contrib/llvm-project/llvm/lib/TableGen/Error.cpp new file mode 100644 index 000000000000..ebe9129ebaeb --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/Error.cpp @@ -0,0 +1,173 @@ +//===- Error.cpp - tblgen error handling helper routines --------*- 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 file contains error handling helper routines to pretty-print diagnostic +// messages from tblgen. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <cstdlib> + +namespace llvm { + +SourceMgr SrcMgr; +unsigned ErrorsPrinted = 0; + +static void PrintMessage(ArrayRef<SMLoc> Loc, SourceMgr::DiagKind Kind, + const Twine &Msg) { + // Count the total number of errors printed. + // This is used to exit with an error code if there were any errors. + if (Kind == SourceMgr::DK_Error) + ++ErrorsPrinted; + + SMLoc NullLoc; + if (Loc.empty()) + Loc = NullLoc; + SrcMgr.PrintMessage(Loc.front(), Kind, Msg); + for (unsigned i = 1; i < Loc.size(); ++i) + SrcMgr.PrintMessage(Loc[i], SourceMgr::DK_Note, + "instantiated from multiclass"); +} + +// 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); +} + +void PrintWarning(const char *Loc, const Twine &Msg) { + SrcMgr.PrintMessage(SMLoc::getFromPointer(Loc), SourceMgr::DK_Warning, Msg); +} + +// 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); +} + +void PrintError(const char *Loc, const Twine &Msg) { + SrcMgr.PrintMessage(SMLoc::getFromPointer(Loc), SourceMgr::DK_Error, Msg); +} + +// 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); + // The following call runs the file cleanup handlers. + sys::RunInterruptHandlers(); + std::exit(1); +} + +void PrintFatalError(ArrayRef<SMLoc> ErrorLoc, const Twine &Msg) { + PrintError(ErrorLoc, 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 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); +} + +// Check an assertion: Obtain the condition value and be sure it is true. +// If not, print a nonfatal error along with the message. +void CheckAssert(SMLoc Loc, Init *Condition, Init *Message) { + auto *CondValue = dyn_cast_or_null<IntInit>(Condition->convertInitializerTo( + IntRecTy::get(Condition->getRecordKeeper()))); + if (!CondValue) + PrintError(Loc, "assert condition must of type bit, bits, or int."); + else 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)"); + } +} + +} // end namespace llvm diff --git a/contrib/llvm-project/llvm/lib/TableGen/JSONBackend.cpp b/contrib/llvm-project/llvm/lib/TableGen/JSONBackend.cpp new file mode 100644 index 000000000000..a9dc30d7f59b --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/JSONBackend.cpp @@ -0,0 +1,185 @@ +//===- JSONBackend.cpp - Generate a JSON dump of all records. -*- 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 back end generates a machine-readable representation +// of all the classes and records defined by the input, in JSON format. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/JSON.h" +#include "llvm/TableGen/Record.h" + +#define DEBUG_TYPE "json-emitter" + +using namespace llvm; + +namespace { + +class JSONEmitter { +private: + RecordKeeper &Records; + + json::Value translateInit(const Init &I); + +public: + JSONEmitter(RecordKeeper &R); + + void run(raw_ostream &OS); +}; + +} // end anonymous namespace + +JSONEmitter::JSONEmitter(RecordKeeper &R) : Records(R) {} + +json::Value JSONEmitter::translateInit(const Init &I) { + + // Init subclasses that we return as JSON primitive values of one + // kind or another. + + if (isa<UnsetInit>(&I)) { + return nullptr; + } else if (auto *Bit = dyn_cast<BitInit>(&I)) { + return Bit->getValue() ? 1 : 0; + } else if (auto *Bits = dyn_cast<BitsInit>(&I)) { + json::Array array; + for (unsigned i = 0, limit = Bits->getNumBits(); i < limit; i++) + array.push_back(translateInit(*Bits->getBit(i))); + return std::move(array); + } else if (auto *Int = dyn_cast<IntInit>(&I)) { + return Int->getValue(); + } else if (auto *Str = dyn_cast<StringInit>(&I)) { + return Str->getValue(); + } else if (auto *List = dyn_cast<ListInit>(&I)) { + json::Array array; + for (auto *val : *List) + array.push_back(translateInit(*val)); + return std::move(array); + } + + // Init subclasses that we return as JSON objects containing a + // 'kind' discriminator. For these, we also provide the same + // translation back into TableGen input syntax that -print-records + // would give. + + json::Object obj; + obj["printable"] = I.getAsString(); + + if (auto *Def = dyn_cast<DefInit>(&I)) { + obj["kind"] = "def"; + obj["def"] = Def->getDef()->getName(); + return std::move(obj); + } else if (auto *Var = dyn_cast<VarInit>(&I)) { + obj["kind"] = "var"; + obj["var"] = Var->getName(); + return std::move(obj); + } else if (auto *VarBit = dyn_cast<VarBitInit>(&I)) { + if (auto *Var = dyn_cast<VarInit>(VarBit->getBitVar())) { + obj["kind"] = "varbit"; + obj["var"] = Var->getName(); + obj["index"] = VarBit->getBitNum(); + return std::move(obj); + } + } else if (auto *Dag = dyn_cast<DagInit>(&I)) { + obj["kind"] = "dag"; + obj["operator"] = translateInit(*Dag->getOperator()); + if (auto name = Dag->getName()) + obj["name"] = name->getAsUnquotedString(); + json::Array args; + for (unsigned i = 0, limit = Dag->getNumArgs(); i < limit; ++i) { + json::Array arg; + arg.push_back(translateInit(*Dag->getArg(i))); + if (auto argname = Dag->getArgName(i)) + arg.push_back(argname->getAsUnquotedString()); + else + arg.push_back(nullptr); + args.push_back(std::move(arg)); + } + obj["args"] = std::move(args); + return std::move(obj); + } + + // Final fallback: anything that gets past here is simply given a + // kind field of 'complex', and the only other field is the standard + // 'printable' representation. + + assert(!I.isConcrete()); + obj["kind"] = "complex"; + return std::move(obj); +} + +void JSONEmitter::run(raw_ostream &OS) { + json::Object root; + + root["!tablegen_json_version"] = 1; + + // Prepare the arrays that will list the instances of every class. + // We mostly fill those in by iterating over the superclasses of + // each def, but we also want to ensure we store an empty list for a + // class with no instances at all, so we do a preliminary iteration + // over the classes, invoking std::map::operator[] to default- + // construct the array for each one. + std::map<std::string, json::Array> instance_lists; + for (const auto &C : Records.getClasses()) { + const auto Name = C.second->getNameInitAsString(); + (void)instance_lists[Name]; + } + + // Main iteration over the defs. + for (const auto &D : Records.getDefs()) { + const auto Name = D.second->getNameInitAsString(); + auto &Def = *D.second; + + json::Object obj; + json::Array fields; + + for (const RecordVal &RV : Def.getValues()) { + if (!Def.isTemplateArg(RV.getNameInit())) { + auto Name = RV.getNameInitAsString(); + if (RV.isNonconcreteOK()) + fields.push_back(Name); + obj[Name] = translateInit(*RV.getValue()); + } + } + + obj["!fields"] = std::move(fields); + + json::Array superclasses; + for (const auto &SuperPair : Def.getSuperClasses()) + superclasses.push_back(SuperPair.first->getNameInitAsString()); + obj["!superclasses"] = std::move(superclasses); + + obj["!name"] = Name; + obj["!anonymous"] = Def.isAnonymous(); + + root[Name] = std::move(obj); + + // Add this def to the instance list for each of its superclasses. + for (const auto &SuperPair : Def.getSuperClasses()) { + auto SuperName = SuperPair.first->getNameInitAsString(); + instance_lists[SuperName].push_back(Name); + } + } + + // Make a JSON object from the std::map of instance lists. + json::Object instanceof; + for (auto kv: instance_lists) + instanceof[kv.first] = std::move(kv.second); + root["!instanceof"] = std::move(instanceof); + + // Done. Write the output. + OS << json::Value(std::move(root)) << "\n"; +} + +namespace llvm { + +void EmitJSON(RecordKeeper &RK, raw_ostream &OS) { JSONEmitter(RK).run(OS); } +} // end namespace llvm diff --git a/contrib/llvm-project/llvm/lib/TableGen/Main.cpp b/contrib/llvm-project/llvm/lib/TableGen/Main.cpp new file mode 100644 index 000000000000..1d5f130737ee --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/Main.cpp @@ -0,0 +1,164 @@ +//===- Main.cpp - Top-Level TableGen implementation -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// TableGen is a tool which can be used to build up a description of something, +// then invoke one or more "tablegen backends" to emit information about the +// description in some predefined format. In practice, this is used by the LLVM +// code generators to automate generation of a code generator through a +// high-level description of the target. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Main.h" +#include "TGParser.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <algorithm> +#include <system_error> +using namespace llvm; + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), + cl::init("-")); + +static cl::opt<std::string> +DependFilename("d", + cl::desc("Dependency filename"), + cl::value_desc("filename"), + cl::init("")); + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); + +static cl::list<std::string> +IncludeDirs("I", cl::desc("Directory of include files"), + cl::value_desc("directory"), cl::Prefix); + +static cl::list<std::string> +MacroNames("D", cl::desc("Name of the macro to be defined"), + cl::value_desc("macro name"), cl::Prefix); + +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 cl::opt<bool> NoWarnOnUnusedTemplateArgs( + "no-warn-on-unused-template-args", + cl::desc("Disable unused template argument warnings.")); + +static int reportError(const char *ProgName, Twine Msg) { + errs() << ProgName << ": " << Msg; + errs().flush(); + return 1; +} + +/// Create a dependency file for `-d` option. +/// +/// This functionality is really only for the benefit of the build system. +/// It is similar to GCC's `-M*` family of options. +static int createDependencyFile(const TGParser &Parser, const char *argv0) { + if (OutputFilename == "-") + return reportError(argv0, "the option -d must be used together with -o\n"); + + std::error_code EC; + ToolOutputFile DepOut(DependFilename, EC, sys::fs::OF_Text); + if (EC) + return reportError(argv0, "error opening " + DependFilename + ":" + + EC.message() + "\n"); + DepOut.os() << OutputFilename << ":"; + for (const auto &Dep : Parser.getDependencies()) { + DepOut.os() << ' ' << Dep; + } + DepOut.os() << "\n"; + DepOut.keep(); + return 0; +} + +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, /*IsText=*/true); + 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()); + + // Record the location of the include directory so that the lexer can find + // it later. + SrcMgr.setIncludeDirs(IncludeDirs); + + TGParser Parser(SrcMgr, MacroNames, Records, NoWarnOnUnusedTemplateArgs); + + if (Parser.ParseFile()) + return 1; + Records.stopTimer(); + + // Write output to memory. + Records.startBackendTimer("Backend overall"); + std::string OutString; + raw_string_ostream Out(OutString); + unsigned status = MainFn(Out, Records); + Records.stopBackendTimer(); + if (status) + return 1; + + // Always write the depfile, even if the main output hasn't changed. + // If it's missing, Ninja considers the output dirty. If this was below + // the early exit below and someone deleted the .inc.d file but not the .inc + // file, tablegen would never write the depfile. + if (!DependFilename.empty()) { + if (int Ret = createDependencyFile(Parser, argv0)) + 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, /*IsText=*/true)) + if (std::move(ExistingOrErr.get())->getBuffer() == Out.str()) + WriteFile = false; + } + if (WriteFile) { + std::error_code EC; + ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_Text); + 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"); + return 0; +} diff --git a/contrib/llvm-project/llvm/lib/TableGen/Parser.cpp b/contrib/llvm-project/llvm/lib/TableGen/Parser.cpp new file mode 100644 index 000000000000..73a336292412 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/Parser.cpp @@ -0,0 +1,41 @@ +//===- Parser.cpp - Top-Level TableGen Parser implementation --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Parser.h" +#include "TGParser.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TableGen/Record.h" + +using namespace llvm; + +bool llvm::TableGenParseFile(SourceMgr &InputSrcMgr, RecordKeeper &Records) { + // Initialize the global TableGen source manager by temporarily taking control + // of the input buffer in `SrcMgr`. This is kind of a hack, but allows for + // preserving TableGen's current awkward diagnostic behavior. If we can remove + // this reliance, we could drop all of this. + SrcMgr = SourceMgr(); + SrcMgr.takeSourceBuffersFrom(InputSrcMgr); + SrcMgr.setIncludeDirs(InputSrcMgr.getIncludeDirs()); + SrcMgr.setDiagHandler(InputSrcMgr.getDiagHandler(), + InputSrcMgr.getDiagContext()); + + // Setup the record keeper and try to parse the file. + auto *MainFileBuffer = SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID()); + Records.saveInputFilename(MainFileBuffer->getBufferIdentifier().str()); + + TGParser Parser(SrcMgr, /*Macros=*/std::nullopt, Records, + /*NoWarnOnUnusedTemplateArgs=*/false, + /*TrackReferenceLocs=*/true); + bool ParseResult = Parser.ParseFile(); + + // After parsing, reclaim the source manager buffers from TableGen's global + // manager. + InputSrcMgr.takeSourceBuffersFrom(SrcMgr); + SrcMgr = SourceMgr(); + return ParseResult; +} diff --git a/contrib/llvm-project/llvm/lib/TableGen/Record.cpp b/contrib/llvm-project/llvm/lib/TableGen/Record.cpp new file mode 100644 index 000000000000..9ea68e2eca51 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/Record.cpp @@ -0,0 +1,3069 @@ +//===- Record.cpp - Record implementation ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implement the tablegen record classes. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Record.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include <cassert> +#include <cstdint> +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +using namespace llvm; + +#define DEBUG_TYPE "tblgen-records" + +//===----------------------------------------------------------------------===// +// Context +//===----------------------------------------------------------------------===// + +namespace llvm { +namespace detail { +/// This class represents the internal implementation of the RecordKeeper. +/// It contains all of the contextual static state of the Record classes. It is +/// kept out-of-line to simplify dependencies, and also make it easier for +/// internal classes to access the uniquer state of the keeper. +struct RecordKeeperImpl { + RecordKeeperImpl(RecordKeeper &RK) + : SharedBitRecTy(RK), SharedIntRecTy(RK), SharedStringRecTy(RK), + SharedDagRecTy(RK), AnyRecord(RK, 0), TheUnsetInit(RK), + TrueBitInit(true, &SharedBitRecTy), + FalseBitInit(false, &SharedBitRecTy), StringInitStringPool(Allocator), + StringInitCodePool(Allocator), AnonCounter(0), LastRecordID(0) {} + + BumpPtrAllocator Allocator; + std::vector<BitsRecTy *> SharedBitsRecTys; + BitRecTy SharedBitRecTy; + IntRecTy SharedIntRecTy; + StringRecTy SharedStringRecTy; + DagRecTy SharedDagRecTy; + + RecordRecTy AnyRecord; + UnsetInit TheUnsetInit; + BitInit TrueBitInit; + BitInit FalseBitInit; + + FoldingSet<BitsInit> TheBitsInitPool; + std::map<int64_t, IntInit *> TheIntInitPool; + StringMap<StringInit *, BumpPtrAllocator &> StringInitStringPool; + StringMap<StringInit *, BumpPtrAllocator &> StringInitCodePool; + FoldingSet<ListInit> TheListInitPool; + FoldingSet<UnOpInit> TheUnOpInitPool; + FoldingSet<BinOpInit> TheBinOpInitPool; + FoldingSet<TernOpInit> TheTernOpInitPool; + FoldingSet<FoldOpInit> TheFoldOpInitPool; + FoldingSet<IsAOpInit> TheIsAOpInitPool; + FoldingSet<ExistsOpInit> TheExistsOpInitPool; + DenseMap<std::pair<RecTy *, Init *>, VarInit *> TheVarInitPool; + DenseMap<std::pair<TypedInit *, unsigned>, VarBitInit *> TheVarBitInitPool; + DenseMap<std::pair<TypedInit *, unsigned>, VarListElementInit *> + TheVarListElementInitPool; + FoldingSet<VarDefInit> TheVarDefInitPool; + DenseMap<std::pair<Init *, StringInit *>, FieldInit *> TheFieldInitPool; + FoldingSet<CondOpInit> TheCondOpInitPool; + FoldingSet<DagInit> TheDagInitPool; + FoldingSet<RecordRecTy> RecordTypePool; + + unsigned AnonCounter; + unsigned LastRecordID; +}; +} // namespace detail +} // namespace llvm + +//===----------------------------------------------------------------------===// +// Type implementations +//===----------------------------------------------------------------------===// + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void RecTy::dump() const { print(errs()); } +#endif + +ListRecTy *RecTy::getListTy() { + if (!ListTy) + ListTy = new (RK.getImpl().Allocator) ListRecTy(this); + return ListTy; +} + +bool RecTy::typeIsConvertibleTo(const RecTy *RHS) const { + assert(RHS && "NULL pointer"); + return Kind == RHS->getRecTyKind(); +} + +bool RecTy::typeIsA(const RecTy *RHS) const { return this == RHS; } + +BitRecTy *BitRecTy::get(RecordKeeper &RK) { + return &RK.getImpl().SharedBitRecTy; +} + +bool BitRecTy::typeIsConvertibleTo(const RecTy *RHS) const{ + if (RecTy::typeIsConvertibleTo(RHS) || RHS->getRecTyKind() == IntRecTyKind) + return true; + if (const BitsRecTy *BitsTy = dyn_cast<BitsRecTy>(RHS)) + return BitsTy->getNumBits() == 1; + return false; +} + +BitsRecTy *BitsRecTy::get(RecordKeeper &RK, unsigned Sz) { + detail::RecordKeeperImpl &RKImpl = RK.getImpl(); + if (Sz >= RKImpl.SharedBitsRecTys.size()) + RKImpl.SharedBitsRecTys.resize(Sz + 1); + BitsRecTy *&Ty = RKImpl.SharedBitsRecTys[Sz]; + if (!Ty) + Ty = new (RKImpl.Allocator) BitsRecTy(RK, Sz); + return Ty; +} + +std::string BitsRecTy::getAsString() const { + return "bits<" + utostr(Size) + ">"; +} + +bool BitsRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + if (RecTy::typeIsConvertibleTo(RHS)) //argument and the sender are same type + return cast<BitsRecTy>(RHS)->Size == Size; + RecTyKind kind = RHS->getRecTyKind(); + return (kind == BitRecTyKind && Size == 1) || (kind == IntRecTyKind); +} + +bool BitsRecTy::typeIsA(const RecTy *RHS) const { + if (const BitsRecTy *RHSb = dyn_cast<BitsRecTy>(RHS)) + return RHSb->Size == Size; + return false; +} + +IntRecTy *IntRecTy::get(RecordKeeper &RK) { + return &RK.getImpl().SharedIntRecTy; +} + +bool IntRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + RecTyKind kind = RHS->getRecTyKind(); + return kind==BitRecTyKind || kind==BitsRecTyKind || kind==IntRecTyKind; +} + +StringRecTy *StringRecTy::get(RecordKeeper &RK) { + return &RK.getImpl().SharedStringRecTy; +} + +std::string StringRecTy::getAsString() const { + return "string"; +} + +bool StringRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + RecTyKind Kind = RHS->getRecTyKind(); + return Kind == StringRecTyKind; +} + +std::string ListRecTy::getAsString() const { + return "list<" + ElementTy->getAsString() + ">"; +} + +bool ListRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + if (const auto *ListTy = dyn_cast<ListRecTy>(RHS)) + return ElementTy->typeIsConvertibleTo(ListTy->getElementType()); + return false; +} + +bool ListRecTy::typeIsA(const RecTy *RHS) const { + if (const ListRecTy *RHSl = dyn_cast<ListRecTy>(RHS)) + return getElementType()->typeIsA(RHSl->getElementType()); + return false; +} + +DagRecTy *DagRecTy::get(RecordKeeper &RK) { + return &RK.getImpl().SharedDagRecTy; +} + +std::string DagRecTy::getAsString() const { + return "dag"; +} + +static void ProfileRecordRecTy(FoldingSetNodeID &ID, + ArrayRef<Record *> Classes) { + ID.AddInteger(Classes.size()); + for (Record *R : Classes) + ID.AddPointer(R); +} + +RecordRecTy *RecordRecTy::get(RecordKeeper &RK, + ArrayRef<Record *> UnsortedClasses) { + detail::RecordKeeperImpl &RKImpl = RK.getImpl(); + if (UnsortedClasses.empty()) + return &RKImpl.AnyRecord; + + FoldingSet<RecordRecTy> &ThePool = RKImpl.RecordTypePool; + + SmallVector<Record *, 4> Classes(UnsortedClasses.begin(), + UnsortedClasses.end()); + llvm::sort(Classes, [](Record *LHS, Record *RHS) { + return LHS->getNameInitAsString() < RHS->getNameInitAsString(); + }); + + FoldingSetNodeID ID; + ProfileRecordRecTy(ID, Classes); + + void *IP = nullptr; + if (RecordRecTy *Ty = ThePool.FindNodeOrInsertPos(ID, IP)) + return Ty; + +#ifndef NDEBUG + // Check for redundancy. + for (unsigned i = 0; i < Classes.size(); ++i) { + for (unsigned j = 0; j < Classes.size(); ++j) { + assert(i == j || !Classes[i]->isSubClassOf(Classes[j])); + } + assert(&Classes[0]->getRecords() == &Classes[i]->getRecords()); + } +#endif + + void *Mem = RKImpl.Allocator.Allocate( + totalSizeToAlloc<Record *>(Classes.size()), alignof(RecordRecTy)); + RecordRecTy *Ty = new (Mem) RecordRecTy(RK, Classes.size()); + std::uninitialized_copy(Classes.begin(), Classes.end(), + Ty->getTrailingObjects<Record *>()); + ThePool.InsertNode(Ty, IP); + return Ty; +} +RecordRecTy *RecordRecTy::get(Record *Class) { + assert(Class && "unexpected null class"); + return get(Class->getRecords(), Class); +} + +void RecordRecTy::Profile(FoldingSetNodeID &ID) const { + ProfileRecordRecTy(ID, getClasses()); +} + +std::string RecordRecTy::getAsString() const { + if (NumClasses == 1) + return getClasses()[0]->getNameInitAsString(); + + std::string Str = "{"; + bool First = true; + for (Record *R : getClasses()) { + if (!First) + Str += ", "; + First = false; + Str += R->getNameInitAsString(); + } + Str += "}"; + return Str; +} + +bool RecordRecTy::isSubClassOf(Record *Class) const { + return llvm::any_of(getClasses(), [Class](Record *MySuperClass) { + return MySuperClass == Class || + MySuperClass->isSubClassOf(Class); + }); +} + +bool RecordRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + if (this == RHS) + return true; + + const RecordRecTy *RTy = dyn_cast<RecordRecTy>(RHS); + if (!RTy) + return false; + + return llvm::all_of(RTy->getClasses(), [this](Record *TargetClass) { + return isSubClassOf(TargetClass); + }); +} + +bool RecordRecTy::typeIsA(const RecTy *RHS) const { + return typeIsConvertibleTo(RHS); +} + +static RecordRecTy *resolveRecordTypes(RecordRecTy *T1, RecordRecTy *T2) { + SmallVector<Record *, 4> CommonSuperClasses; + SmallVector<Record *, 4> Stack(T1->classes_begin(), T1->classes_end()); + + while (!Stack.empty()) { + Record *R = Stack.pop_back_val(); + + if (T2->isSubClassOf(R)) { + CommonSuperClasses.push_back(R); + } else { + R->getDirectSuperClasses(Stack); + } + } + + return RecordRecTy::get(T1->getRecordKeeper(), CommonSuperClasses); +} + +RecTy *llvm::resolveTypes(RecTy *T1, RecTy *T2) { + if (T1 == T2) + return T1; + + if (RecordRecTy *RecTy1 = dyn_cast<RecordRecTy>(T1)) { + if (RecordRecTy *RecTy2 = dyn_cast<RecordRecTy>(T2)) + return resolveRecordTypes(RecTy1, RecTy2); + } + + if (T1->typeIsConvertibleTo(T2)) + return T2; + if (T2->typeIsConvertibleTo(T1)) + return T1; + + if (ListRecTy *ListTy1 = dyn_cast<ListRecTy>(T1)) { + if (ListRecTy *ListTy2 = dyn_cast<ListRecTy>(T2)) { + RecTy* NewType = resolveTypes(ListTy1->getElementType(), + ListTy2->getElementType()); + if (NewType) + return NewType->getListTy(); + } + } + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Initializer implementations +//===----------------------------------------------------------------------===// + +void Init::anchor() {} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void Init::dump() const { return print(errs()); } +#endif + +RecordKeeper &Init::getRecordKeeper() const { + if (auto *TyInit = dyn_cast<TypedInit>(this)) + return TyInit->getType()->getRecordKeeper(); + return cast<UnsetInit>(this)->getRecordKeeper(); +} + +UnsetInit *UnsetInit::get(RecordKeeper &RK) { + return &RK.getImpl().TheUnsetInit; +} + +Init *UnsetInit::getCastTo(RecTy *Ty) const { + return const_cast<UnsetInit *>(this); +} + +Init *UnsetInit::convertInitializerTo(RecTy *Ty) const { + return const_cast<UnsetInit *>(this); +} + +BitInit *BitInit::get(RecordKeeper &RK, bool V) { + return V ? &RK.getImpl().TrueBitInit : &RK.getImpl().FalseBitInit; +} + +Init *BitInit::convertInitializerTo(RecTy *Ty) const { + if (isa<BitRecTy>(Ty)) + return const_cast<BitInit *>(this); + + if (isa<IntRecTy>(Ty)) + return IntInit::get(getRecordKeeper(), getValue()); + + if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) { + // Can only convert single bit. + if (BRT->getNumBits() == 1) + return BitsInit::get(getRecordKeeper(), const_cast<BitInit *>(this)); + } + + return nullptr; +} + +static void +ProfileBitsInit(FoldingSetNodeID &ID, ArrayRef<Init *> Range) { + ID.AddInteger(Range.size()); + + for (Init *I : Range) + ID.AddPointer(I); +} + +BitsInit *BitsInit::get(RecordKeeper &RK, ArrayRef<Init *> Range) { + FoldingSetNodeID ID; + ProfileBitsInit(ID, Range); + + detail::RecordKeeperImpl &RKImpl = RK.getImpl(); + void *IP = nullptr; + if (BitsInit *I = RKImpl.TheBitsInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = RKImpl.Allocator.Allocate(totalSizeToAlloc<Init *>(Range.size()), + alignof(BitsInit)); + BitsInit *I = new (Mem) BitsInit(RK, Range.size()); + std::uninitialized_copy(Range.begin(), Range.end(), + I->getTrailingObjects<Init *>()); + RKImpl.TheBitsInitPool.InsertNode(I, IP); + return I; +} + +void BitsInit::Profile(FoldingSetNodeID &ID) const { + ProfileBitsInit(ID, ArrayRef(getTrailingObjects<Init *>(), NumBits)); +} + +Init *BitsInit::convertInitializerTo(RecTy *Ty) const { + if (isa<BitRecTy>(Ty)) { + if (getNumBits() != 1) return nullptr; // Only accept if just one bit! + return getBit(0); + } + + if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) { + // If the number of bits is right, return it. Otherwise we need to expand + // or truncate. + if (getNumBits() != BRT->getNumBits()) return nullptr; + return const_cast<BitsInit *>(this); + } + + if (isa<IntRecTy>(Ty)) { + int64_t Result = 0; + for (unsigned i = 0, e = getNumBits(); i != e; ++i) + if (auto *Bit = dyn_cast<BitInit>(getBit(i))) + Result |= static_cast<int64_t>(Bit->getValue()) << i; + else + return nullptr; + return IntInit::get(getRecordKeeper(), Result); + } + + return nullptr; +} + +Init * +BitsInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const { + SmallVector<Init *, 16> NewBits(Bits.size()); + + for (unsigned i = 0, e = Bits.size(); i != e; ++i) { + if (Bits[i] >= getNumBits()) + return nullptr; + NewBits[i] = getBit(Bits[i]); + } + return BitsInit::get(getRecordKeeper(), NewBits); +} + +bool BitsInit::isConcrete() const { + for (unsigned i = 0, e = getNumBits(); i != e; ++i) { + if (!getBit(i)->isConcrete()) + return false; + } + return true; +} + +std::string BitsInit::getAsString() const { + std::string Result = "{ "; + for (unsigned i = 0, e = getNumBits(); i != e; ++i) { + if (i) Result += ", "; + if (Init *Bit = getBit(e-i-1)) + Result += Bit->getAsString(); + else + Result += "*"; + } + return Result + " }"; +} + +// resolveReferences - If there are any field references that refer to fields +// that have been filled in, we can propagate the values now. +Init *BitsInit::resolveReferences(Resolver &R) const { + bool Changed = false; + SmallVector<Init *, 16> NewBits(getNumBits()); + + Init *CachedBitVarRef = nullptr; + Init *CachedBitVarResolved = nullptr; + + for (unsigned i = 0, e = getNumBits(); i != e; ++i) { + Init *CurBit = getBit(i); + Init *NewBit = CurBit; + + if (VarBitInit *CurBitVar = dyn_cast<VarBitInit>(CurBit)) { + if (CurBitVar->getBitVar() != CachedBitVarRef) { + CachedBitVarRef = CurBitVar->getBitVar(); + CachedBitVarResolved = CachedBitVarRef->resolveReferences(R); + } + assert(CachedBitVarResolved && "Unresolved bitvar reference"); + NewBit = CachedBitVarResolved->getBit(CurBitVar->getBitNum()); + } else { + // getBit(0) implicitly converts int and bits<1> values to bit. + NewBit = CurBit->resolveReferences(R)->getBit(0); + } + + if (isa<UnsetInit>(NewBit) && R.keepUnsetBits()) + NewBit = CurBit; + NewBits[i] = NewBit; + Changed |= CurBit != NewBit; + } + + if (Changed) + return BitsInit::get(getRecordKeeper(), NewBits); + + return const_cast<BitsInit *>(this); +} + +IntInit *IntInit::get(RecordKeeper &RK, int64_t V) { + IntInit *&I = RK.getImpl().TheIntInitPool[V]; + if (!I) + I = new (RK.getImpl().Allocator) IntInit(RK, V); + return I; +} + +std::string IntInit::getAsString() const { + return itostr(Value); +} + +static bool canFitInBitfield(int64_t Value, unsigned NumBits) { + // For example, with NumBits == 4, we permit Values from [-7 .. 15]. + return (NumBits >= sizeof(Value) * 8) || + (Value >> NumBits == 0) || (Value >> (NumBits-1) == -1); +} + +Init *IntInit::convertInitializerTo(RecTy *Ty) const { + if (isa<IntRecTy>(Ty)) + return const_cast<IntInit *>(this); + + if (isa<BitRecTy>(Ty)) { + int64_t Val = getValue(); + if (Val != 0 && Val != 1) return nullptr; // Only accept 0 or 1 for a bit! + return BitInit::get(getRecordKeeper(), Val != 0); + } + + if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) { + int64_t Value = getValue(); + // Make sure this bitfield is large enough to hold the integer value. + if (!canFitInBitfield(Value, BRT->getNumBits())) + return nullptr; + + SmallVector<Init *, 16> NewBits(BRT->getNumBits()); + for (unsigned i = 0; i != BRT->getNumBits(); ++i) + NewBits[i] = + BitInit::get(getRecordKeeper(), Value & ((i < 64) ? (1LL << i) : 0)); + + return BitsInit::get(getRecordKeeper(), NewBits); + } + + return nullptr; +} + +Init * +IntInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const { + SmallVector<Init *, 16> NewBits(Bits.size()); + + for (unsigned i = 0, e = Bits.size(); i != e; ++i) { + if (Bits[i] >= 64) + return nullptr; + + NewBits[i] = + BitInit::get(getRecordKeeper(), Value & (INT64_C(1) << Bits[i])); + } + return BitsInit::get(getRecordKeeper(), NewBits); +} + +AnonymousNameInit *AnonymousNameInit::get(RecordKeeper &RK, unsigned V) { + return new (RK.getImpl().Allocator) AnonymousNameInit(RK, V); +} + +StringInit *AnonymousNameInit::getNameInit() const { + return StringInit::get(getRecordKeeper(), getAsString()); +} + +std::string AnonymousNameInit::getAsString() const { + return "anonymous_" + utostr(Value); +} + +Init *AnonymousNameInit::resolveReferences(Resolver &R) const { + auto *Old = const_cast<Init *>(static_cast<const Init *>(this)); + auto *New = R.resolve(Old); + New = New ? New : Old; + if (R.isFinal()) + if (auto *Anonymous = dyn_cast<AnonymousNameInit>(New)) + return Anonymous->getNameInit(); + return New; +} + +StringInit *StringInit::get(RecordKeeper &RK, StringRef V, StringFormat Fmt) { + detail::RecordKeeperImpl &RKImpl = RK.getImpl(); + auto &InitMap = Fmt == SF_String ? RKImpl.StringInitStringPool + : RKImpl.StringInitCodePool; + auto &Entry = *InitMap.insert(std::make_pair(V, nullptr)).first; + if (!Entry.second) + Entry.second = new (RKImpl.Allocator) StringInit(RK, Entry.getKey(), Fmt); + return Entry.second; +} + +Init *StringInit::convertInitializerTo(RecTy *Ty) const { + if (isa<StringRecTy>(Ty)) + return const_cast<StringInit *>(this); + + return nullptr; +} + +static void ProfileListInit(FoldingSetNodeID &ID, + ArrayRef<Init *> Range, + RecTy *EltTy) { + ID.AddInteger(Range.size()); + ID.AddPointer(EltTy); + + for (Init *I : Range) + ID.AddPointer(I); +} + +ListInit *ListInit::get(ArrayRef<Init *> Range, RecTy *EltTy) { + FoldingSetNodeID ID; + ProfileListInit(ID, Range, EltTy); + + detail::RecordKeeperImpl &RK = EltTy->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (ListInit *I = RK.TheListInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + assert(Range.empty() || !isa<TypedInit>(Range[0]) || + cast<TypedInit>(Range[0])->getType()->typeIsConvertibleTo(EltTy)); + + void *Mem = RK.Allocator.Allocate(totalSizeToAlloc<Init *>(Range.size()), + alignof(ListInit)); + ListInit *I = new (Mem) ListInit(Range.size(), EltTy); + std::uninitialized_copy(Range.begin(), Range.end(), + I->getTrailingObjects<Init *>()); + RK.TheListInitPool.InsertNode(I, IP); + return I; +} + +void ListInit::Profile(FoldingSetNodeID &ID) const { + RecTy *EltTy = cast<ListRecTy>(getType())->getElementType(); + + ProfileListInit(ID, getValues(), EltTy); +} + +Init *ListInit::convertInitializerTo(RecTy *Ty) const { + if (getType() == Ty) + return const_cast<ListInit*>(this); + + if (auto *LRT = dyn_cast<ListRecTy>(Ty)) { + SmallVector<Init*, 8> Elements; + Elements.reserve(getValues().size()); + + // Verify that all of the elements of the list are subclasses of the + // appropriate class! + bool Changed = false; + RecTy *ElementType = LRT->getElementType(); + for (Init *I : getValues()) + if (Init *CI = I->convertInitializerTo(ElementType)) { + Elements.push_back(CI); + if (CI != I) + Changed = true; + } else + return nullptr; + + if (!Changed) + return const_cast<ListInit*>(this); + return ListInit::get(Elements, ElementType); + } + + return nullptr; +} + +Init *ListInit::convertInitListSlice(ArrayRef<unsigned> Elements) const { + if (Elements.size() == 1) { + if (Elements[0] >= size()) + return nullptr; + return getElement(Elements[0]); + } + + SmallVector<Init*, 8> Vals; + Vals.reserve(Elements.size()); + for (unsigned Element : Elements) { + if (Element >= size()) + return nullptr; + Vals.push_back(getElement(Element)); + } + return ListInit::get(Vals, getElementType()); +} + +Record *ListInit::getElementAsRecord(unsigned i) const { + assert(i < NumValues && "List element index out of range!"); + DefInit *DI = dyn_cast<DefInit>(getElement(i)); + if (!DI) + PrintFatalError("Expected record in list!"); + return DI->getDef(); +} + +Init *ListInit::resolveReferences(Resolver &R) const { + SmallVector<Init*, 8> Resolved; + Resolved.reserve(size()); + bool Changed = false; + + for (Init *CurElt : getValues()) { + Init *E = CurElt->resolveReferences(R); + Changed |= E != CurElt; + Resolved.push_back(E); + } + + if (Changed) + return ListInit::get(Resolved, getElementType()); + return const_cast<ListInit *>(this); +} + +bool ListInit::isComplete() const { + for (Init *Element : *this) { + if (!Element->isComplete()) + return false; + } + return true; +} + +bool ListInit::isConcrete() const { + for (Init *Element : *this) { + if (!Element->isConcrete()) + return false; + } + return true; +} + +std::string ListInit::getAsString() const { + std::string Result = "["; + const char *sep = ""; + for (Init *Element : *this) { + Result += sep; + sep = ", "; + Result += Element->getAsString(); + } + return Result + "]"; +} + +Init *OpInit::getBit(unsigned Bit) const { + if (getType() == BitRecTy::get(getRecordKeeper())) + return const_cast<OpInit*>(this); + return VarBitInit::get(const_cast<OpInit*>(this), Bit); +} + +static void +ProfileUnOpInit(FoldingSetNodeID &ID, unsigned Opcode, Init *Op, RecTy *Type) { + ID.AddInteger(Opcode); + ID.AddPointer(Op); + ID.AddPointer(Type); +} + +UnOpInit *UnOpInit::get(UnaryOp Opc, Init *LHS, RecTy *Type) { + FoldingSetNodeID ID; + ProfileUnOpInit(ID, Opc, LHS, Type); + + detail::RecordKeeperImpl &RK = Type->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (UnOpInit *I = RK.TheUnOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + UnOpInit *I = new (RK.Allocator) UnOpInit(Opc, LHS, Type); + RK.TheUnOpInitPool.InsertNode(I, IP); + return I; +} + +void UnOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileUnOpInit(ID, getOpcode(), getOperand(), getType()); +} + +Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { + RecordKeeper &RK = getRecordKeeper(); + switch (getOpcode()) { + case CAST: + if (isa<StringRecTy>(getType())) { + if (StringInit *LHSs = dyn_cast<StringInit>(LHS)) + return LHSs; + + if (DefInit *LHSd = dyn_cast<DefInit>(LHS)) + return StringInit::get(RK, LHSd->getAsString()); + + if (IntInit *LHSi = dyn_cast_or_null<IntInit>( + LHS->convertInitializerTo(IntRecTy::get(RK)))) + return StringInit::get(RK, LHSi->getAsString()); + + } else if (isa<RecordRecTy>(getType())) { + if (StringInit *Name = dyn_cast<StringInit>(LHS)) { + if (!CurRec && !IsFinal) + break; + assert(CurRec && "NULL pointer"); + Record *D; + + // Self-references are allowed, but their resolution is delayed until + // the final resolve to ensure that we get the correct type for them. + auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit()); + if (Name == CurRec->getNameInit() || + (Anonymous && Name == Anonymous->getNameInit())) { + if (!IsFinal) + break; + D = CurRec; + } else { + D = CurRec->getRecords().getDef(Name->getValue()); + if (!D) { + if (IsFinal) + PrintFatalError(CurRec->getLoc(), + Twine("Undefined reference to record: '") + + Name->getValue() + "'\n"); + break; + } + } + + DefInit *DI = DefInit::get(D); + if (!DI->getType()->typeIsA(getType())) { + PrintFatalError(CurRec->getLoc(), + Twine("Expected type '") + + getType()->getAsString() + "', got '" + + DI->getType()->getAsString() + "' in: " + + getAsString() + "\n"); + } + return DI; + } + } + + if (Init *NewInit = LHS->convertInitializerTo(getType())) + return NewInit; + break; + + case NOT: + if (IntInit *LHSi = dyn_cast_or_null<IntInit>( + LHS->convertInitializerTo(IntRecTy::get(RK)))) + return IntInit::get(RK, LHSi->getValue() ? 0 : 1); + break; + + case HEAD: + if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) { + assert(!LHSl->empty() && "Empty list in head"); + return LHSl->getElement(0); + } + break; + + case TAIL: + if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) { + assert(!LHSl->empty() && "Empty list in tail"); + // Note the +1. We can't just pass the result of getValues() + // directly. + return ListInit::get(LHSl->getValues().slice(1), LHSl->getElementType()); + } + break; + + case SIZE: + if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) + return IntInit::get(RK, LHSl->size()); + if (DagInit *LHSd = dyn_cast<DagInit>(LHS)) + return IntInit::get(RK, LHSd->arg_size()); + if (StringInit *LHSs = dyn_cast<StringInit>(LHS)) + return IntInit::get(RK, LHSs->getValue().size()); + break; + + case EMPTY: + if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) + return IntInit::get(RK, LHSl->empty()); + if (DagInit *LHSd = dyn_cast<DagInit>(LHS)) + return IntInit::get(RK, LHSd->arg_empty()); + if (StringInit *LHSs = dyn_cast<StringInit>(LHS)) + return IntInit::get(RK, LHSs->getValue().empty()); + break; + + case GETDAGOP: + if (DagInit *Dag = dyn_cast<DagInit>(LHS)) { + DefInit *DI = DefInit::get(Dag->getOperatorAsDef({})); + if (!DI->getType()->typeIsA(getType())) { + PrintFatalError(CurRec->getLoc(), + Twine("Expected type '") + + getType()->getAsString() + "', got '" + + DI->getType()->getAsString() + "' in: " + + getAsString() + "\n"); + } else { + return DI; + } + } + break; + + case LOG2: + if (IntInit *LHSi = dyn_cast_or_null<IntInit>( + LHS->convertInitializerTo(IntRecTy::get(RK)))) { + int64_t LHSv = LHSi->getValue(); + if (LHSv <= 0) { + PrintFatalError(CurRec->getLoc(), + "Illegal operation: logtwo is undefined " + "on arguments less than or equal to 0"); + } else { + uint64_t Log = Log2_64(LHSv); + assert(Log <= INT64_MAX && + "Log of an int64_t must be smaller than INT64_MAX"); + return IntInit::get(RK, static_cast<int64_t>(Log)); + } + } + break; + } + return const_cast<UnOpInit *>(this); +} + +Init *UnOpInit::resolveReferences(Resolver &R) const { + Init *lhs = LHS->resolveReferences(R); + + if (LHS != lhs || (R.isFinal() && getOpcode() == CAST)) + return (UnOpInit::get(getOpcode(), lhs, getType())) + ->Fold(R.getCurrentRecord(), R.isFinal()); + return const_cast<UnOpInit *>(this); +} + +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 GETDAGOP: Result = "!getdagop"; break; + case LOG2 : Result = "!logtwo"; break; + } + return Result + "(" + LHS->getAsString() + ")"; +} + +static void +ProfileBinOpInit(FoldingSetNodeID &ID, unsigned Opcode, Init *LHS, Init *RHS, + RecTy *Type) { + ID.AddInteger(Opcode); + ID.AddPointer(LHS); + ID.AddPointer(RHS); + ID.AddPointer(Type); +} + +BinOpInit *BinOpInit::get(BinaryOp Opc, Init *LHS, Init *RHS, RecTy *Type) { + FoldingSetNodeID ID; + ProfileBinOpInit(ID, Opc, LHS, RHS, Type); + + detail::RecordKeeperImpl &RK = LHS->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (BinOpInit *I = RK.TheBinOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + BinOpInit *I = new (RK.Allocator) BinOpInit(Opc, LHS, RHS, Type); + RK.TheBinOpInitPool.InsertNode(I, IP); + return I; +} + +void BinOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileBinOpInit(ID, getOpcode(), getLHS(), getRHS(), getType()); +} + +static StringInit *ConcatStringInits(const StringInit *I0, + const StringInit *I1) { + SmallString<80> Concat(I0->getValue()); + Concat.append(I1->getValue()); + return StringInit::get( + I0->getRecordKeeper(), Concat, + StringInit::determineFormat(I0->getFormat(), I1->getFormat())); +} + +static StringInit *interleaveStringList(const ListInit *List, + const StringInit *Delim) { + if (List->size() == 0) + return StringInit::get(List->getRecordKeeper(), ""); + 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(List->getRecordKeeper(), Result, Fmt); +} + +static StringInit *interleaveIntList(const ListInit *List, + const StringInit *Delim) { + RecordKeeper &RK = List->getRecordKeeper(); + if (List->size() == 0) + return StringInit::get(RK, ""); + IntInit *Element = dyn_cast_or_null<IntInit>( + List->getElement(0)->convertInitializerTo(IntRecTy::get(RK))); + 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(RK))); + if (!Element) + return nullptr; + Result.append(Element->getAsString()); + } + return StringInit::get(RK, Result); +} + +Init *BinOpInit::getStrConcat(Init *I0, Init *I1) { + // Shortcut for the common case of concatenating two strings. + if (const StringInit *I0s = dyn_cast<StringInit>(I0)) + if (const StringInit *I1s = dyn_cast<StringInit>(I1)) + return ConcatStringInits(I0s, I1s); + return BinOpInit::get(BinOpInit::STRCONCAT, I0, I1, + StringRecTy::get(I0->getRecordKeeper())); +} + +static ListInit *ConcatListInits(const ListInit *LHS, + const ListInit *RHS) { + SmallVector<Init *, 8> Args; + llvm::append_range(Args, *LHS); + llvm::append_range(Args, *RHS); + return ListInit::get(Args, LHS->getElementType()); +} + +Init *BinOpInit::getListConcat(TypedInit *LHS, Init *RHS) { + assert(isa<ListRecTy>(LHS->getType()) && "First arg must be a list"); + + // Shortcut for the common case of concatenating two lists. + if (const ListInit *LHSList = dyn_cast<ListInit>(LHS)) + if (const ListInit *RHSList = dyn_cast<ListInit>(RHS)) + return ConcatListInits(LHSList, RHSList); + return BinOpInit::get(BinOpInit::LISTCONCAT, LHS, RHS, LHS->getType()); +} + +std::optional<bool> BinOpInit::CompareInit(unsigned Opc, Init *LHS, Init *RHS) const { + // First see if we have two bit, bits, or int. + IntInit *LHSi = dyn_cast_or_null<IntInit>( + LHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); + IntInit *RHSi = dyn_cast_or_null<IntInit>( + RHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); + + if (LHSi && RHSi) { + bool Result; + switch (Opc) { + 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 Result; + } + + // Next try strings. + StringInit *LHSs = dyn_cast<StringInit>(LHS); + StringInit *RHSs = dyn_cast<StringInit>(RHS); + + if (LHSs && RHSs) { + bool Result; + switch (Opc) { + 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 Result; + } + + // Finally, !eq and !ne can be used with records. + if (Opc == EQ || Opc == NE) { + DefInit *LHSd = dyn_cast<DefInit>(LHS); + DefInit *RHSd = dyn_cast<DefInit>(RHS); + if (LHSd && RHSd) + return (Opc == EQ) ? LHSd == RHSd : LHSd != RHSd; + } + + return std::nullopt; +} + + Init *BinOpInit::Fold(Record *CurRec) const { + switch (getOpcode()) { + case CONCAT: { + DagInit *LHSs = dyn_cast<DagInit>(LHS); + DagInit *RHSs = dyn_cast<DagInit>(RHS); + if (LHSs && RHSs) { + DefInit *LOp = dyn_cast<DefInit>(LHSs->getOperator()); + DefInit *ROp = dyn_cast<DefInit>(RHSs->getOperator()); + if ((!LOp && !isa<UnsetInit>(LHSs->getOperator())) || + (!ROp && !isa<UnsetInit>(RHSs->getOperator()))) + break; + if (LOp && ROp && LOp->getDef() != ROp->getDef()) { + PrintFatalError(Twine("Concatenated Dag operators do not match: '") + + LHSs->getAsString() + "' vs. '" + RHSs->getAsString() + + "'"); + } + Init *Op = LOp ? LOp : ROp; + if (!Op) + Op = UnsetInit::get(getRecordKeeper()); + + SmallVector<Init*, 8> Args; + SmallVector<StringInit*, 8> ArgNames; + for (unsigned i = 0, e = LHSs->getNumArgs(); i != e; ++i) { + Args.push_back(LHSs->getArg(i)); + ArgNames.push_back(LHSs->getArgName(i)); + } + for (unsigned i = 0, e = RHSs->getNumArgs(); i != e; ++i) { + Args.push_back(RHSs->getArg(i)); + ArgNames.push_back(RHSs->getArgName(i)); + } + return DagInit::get(Op, nullptr, Args, ArgNames); + } + break; + } + case LISTCONCAT: { + ListInit *LHSs = dyn_cast<ListInit>(LHS); + ListInit *RHSs = dyn_cast<ListInit>(RHS); + if (LHSs && RHSs) { + SmallVector<Init *, 8> Args; + llvm::append_range(Args, *LHSs); + llvm::append_range(Args, *RHSs); + return ListInit::get(Args, LHSs->getElementType()); + } + break; + } + case LISTSPLAT: { + TypedInit *Value = dyn_cast<TypedInit>(LHS); + IntInit *Size = dyn_cast<IntInit>(RHS); + if (Value && Size) { + SmallVector<Init *, 8> Args(Size->getValue(), Value); + return ListInit::get(Args, Value->getType()); + } + break; + } + case LISTREMOVE: { + ListInit *LHSs = dyn_cast<ListInit>(LHS); + ListInit *RHSs = dyn_cast<ListInit>(RHS); + if (LHSs && RHSs) { + SmallVector<Init *, 8> Args; + for (Init *EltLHS : *LHSs) { + bool Found = false; + for (Init *EltRHS : *RHSs) { + if (std::optional<bool> Result = CompareInit(EQ, EltLHS, EltRHS)) { + if (*Result) { + Found = true; + break; + } + } + } + if (!Found) + Args.push_back(EltLHS); + } + return ListInit::get(Args, LHSs->getElementType()); + } + break; + } + case STRCONCAT: { + StringInit *LHSs = dyn_cast<StringInit>(LHS); + StringInit *RHSs = dyn_cast<StringInit>(RHS); + if (LHSs && RHSs) + 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: { + if (std::optional<bool> Result = CompareInit(getOpcode(), LHS, RHS)) + return BitInit::get(getRecordKeeper(), *Result); + break; + } + case SETDAGOP: { + DagInit *Dag = dyn_cast<DagInit>(LHS); + DefInit *Op = dyn_cast<DefInit>(RHS); + if (Dag && Op) { + SmallVector<Init*, 8> Args; + SmallVector<StringInit*, 8> ArgNames; + for (unsigned i = 0, e = Dag->getNumArgs(); i != e; ++i) { + Args.push_back(Dag->getArg(i)); + ArgNames.push_back(Dag->getArgName(i)); + } + return DagInit::get(Op, nullptr, Args, ArgNames); + } + break; + } + case ADD: + case SUB: + case MUL: + case DIV: + case AND: + case OR: + case XOR: + case SHL: + case SRA: + case SRL: { + IntInit *LHSi = dyn_cast_or_null<IntInit>( + LHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); + IntInit *RHSi = dyn_cast_or_null<IntInit>( + RHS->convertInitializerTo(IntRecTy::get(getRecordKeeper()))); + if (LHSi && RHSi) { + int64_t LHSv = LHSi->getValue(), RHSv = RHSi->getValue(); + int64_t Result; + switch (getOpcode()) { + default: llvm_unreachable("Bad opcode!"); + case ADD: Result = LHSv + RHSv; break; + case SUB: Result = LHSv - RHSv; break; + case MUL: Result = LHSv * RHSv; break; + case DIV: + if (RHSv == 0) + PrintFatalError(CurRec->getLoc(), + "Illegal operation: division by zero"); + else if (LHSv == INT64_MIN && RHSv == -1) + PrintFatalError(CurRec->getLoc(), + "Illegal operation: INT64_MIN / -1"); + else + 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; + } + return IntInit::get(getRecordKeeper(), Result); + } + break; + } + } + return const_cast<BinOpInit *>(this); +} + +Init *BinOpInit::resolveReferences(Resolver &R) const { + Init *lhs = LHS->resolveReferences(R); + Init *rhs = RHS->resolveReferences(R); + + if (LHS != lhs || RHS != rhs) + return (BinOpInit::get(getOpcode(), lhs, rhs, getType())) + ->Fold(R.getCurrentRecord()); + return const_cast<BinOpInit *>(this); +} + +std::string BinOpInit::getAsString() const { + std::string Result; + switch (getOpcode()) { + case CONCAT: Result = "!con"; break; + case ADD: Result = "!add"; break; + case SUB: Result = "!sub"; break; + case MUL: Result = "!mul"; break; + case DIV: Result = "!div"; 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; + case EQ: Result = "!eq"; break; + case NE: Result = "!ne"; break; + case LE: Result = "!le"; break; + case LT: Result = "!lt"; break; + case GE: Result = "!ge"; break; + case GT: Result = "!gt"; break; + case LISTCONCAT: Result = "!listconcat"; break; + case LISTSPLAT: Result = "!listsplat"; break; + case LISTREMOVE: Result = "!listremove"; break; + case STRCONCAT: Result = "!strconcat"; break; + case INTERLEAVE: Result = "!interleave"; break; + case SETDAGOP: Result = "!setdagop"; break; + } + return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")"; +} + +static void +ProfileTernOpInit(FoldingSetNodeID &ID, unsigned Opcode, Init *LHS, Init *MHS, + Init *RHS, RecTy *Type) { + ID.AddInteger(Opcode); + ID.AddPointer(LHS); + ID.AddPointer(MHS); + ID.AddPointer(RHS); + ID.AddPointer(Type); +} + +TernOpInit *TernOpInit::get(TernaryOp Opc, Init *LHS, Init *MHS, Init *RHS, + RecTy *Type) { + FoldingSetNodeID ID; + ProfileTernOpInit(ID, Opc, LHS, MHS, RHS, Type); + + detail::RecordKeeperImpl &RK = LHS->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (TernOpInit *I = RK.TheTernOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + TernOpInit *I = new (RK.Allocator) TernOpInit(Opc, LHS, MHS, RHS, Type); + RK.TheTernOpInitPool.InsertNode(I, IP); + return I; +} + +void TernOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileTernOpInit(ID, getOpcode(), getLHS(), getMHS(), getRHS(), getType()); +} + +static Init *ItemApply(Init *LHS, Init *MHSe, Init *RHS, Record *CurRec) { + MapResolver R(CurRec); + R.set(LHS, MHSe); + return RHS->resolveReferences(R); +} + +static Init *ForeachDagApply(Init *LHS, DagInit *MHSd, Init *RHS, + Record *CurRec) { + bool Change = false; + Init *Val = ItemApply(LHS, MHSd->getOperator(), RHS, CurRec); + if (Val != MHSd->getOperator()) + Change = true; + + SmallVector<std::pair<Init *, StringInit *>, 8> NewArgs; + for (unsigned int i = 0; i < MHSd->getNumArgs(); ++i) { + Init *Arg = MHSd->getArg(i); + Init *NewArg; + StringInit *ArgName = MHSd->getArgName(i); + + if (DagInit *Argd = dyn_cast<DagInit>(Arg)) + NewArg = ForeachDagApply(LHS, Argd, RHS, CurRec); + else + NewArg = ItemApply(LHS, Arg, RHS, CurRec); + + NewArgs.push_back(std::make_pair(NewArg, ArgName)); + if (Arg != NewArg) + Change = true; + } + + if (Change) + return DagInit::get(Val, nullptr, NewArgs); + return MHSd; +} + +// Applies RHS to all elements of MHS, using LHS as a temp variable. +static Init *ForeachHelper(Init *LHS, Init *MHS, Init *RHS, RecTy *Type, + Record *CurRec) { + if (DagInit *MHSd = dyn_cast<DagInit>(MHS)) + return ForeachDagApply(LHS, MHSd, RHS, CurRec); + + if (ListInit *MHSl = dyn_cast<ListInit>(MHS)) { + SmallVector<Init *, 8> NewList(MHSl->begin(), MHSl->end()); + + for (Init *&Item : NewList) { + Init *NewItem = ItemApply(LHS, Item, RHS, CurRec); + if (NewItem != Item) + Item = NewItem; + } + return ListInit::get(NewList, cast<ListRecTy>(Type)->getElementType()); + } + + 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(LHS->getRecordKeeper())))) { + 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 { + RecordKeeper &RK = getRecordKeeper(); + switch (getOpcode()) { + case SUBST: { + DefInit *LHSd = dyn_cast<DefInit>(LHS); + VarInit *LHSv = dyn_cast<VarInit>(LHS); + StringInit *LHSs = dyn_cast<StringInit>(LHS); + + DefInit *MHSd = dyn_cast<DefInit>(MHS); + VarInit *MHSv = dyn_cast<VarInit>(MHS); + StringInit *MHSs = dyn_cast<StringInit>(MHS); + + DefInit *RHSd = dyn_cast<DefInit>(RHS); + VarInit *RHSv = dyn_cast<VarInit>(RHS); + StringInit *RHSs = dyn_cast<StringInit>(RHS); + + if (LHSd && MHSd && RHSd) { + Record *Val = RHSd->getDef(); + if (LHSd->getAsString() == RHSd->getAsString()) + Val = MHSd->getDef(); + return DefInit::get(Val); + } + if (LHSv && MHSv && RHSv) { + std::string Val = std::string(RHSv->getName()); + if (LHSv->getAsString() == RHSv->getAsString()) + Val = std::string(MHSv->getName()); + return VarInit::get(Val, getType()); + } + if (LHSs && MHSs && RHSs) { + std::string Val = std::string(RHSs->getValue()); + + std::string::size_type found; + std::string::size_type idx = 0; + while (true) { + found = Val.find(std::string(LHSs->getValue()), idx); + if (found == std::string::npos) + break; + Val.replace(found, LHSs->getValue().size(), + std::string(MHSs->getValue())); + idx = found + MHSs->getValue().size(); + } + + return StringInit::get(RK, Val); + } + break; + } + + case FOREACH: { + if (Init *Result = ForeachHelper(LHS, MHS, RHS, getType(), CurRec)) + return Result; + 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(RK)))) { + if (LHSi->getValue()) + return MHS; + return RHS; + } + break; + } + + case DAG: { + ListInit *MHSl = dyn_cast<ListInit>(MHS); + ListInit *RHSl = dyn_cast<ListInit>(RHS); + bool MHSok = MHSl || isa<UnsetInit>(MHS); + bool RHSok = RHSl || isa<UnsetInit>(RHS); + + if (isa<UnsetInit>(MHS) && isa<UnsetInit>(RHS)) + break; // Typically prevented by the parser, but might happen with template args + + if (MHSok && RHSok && (!MHSl || !RHSl || MHSl->size() == RHSl->size())) { + SmallVector<std::pair<Init *, StringInit *>, 8> Children; + unsigned Size = MHSl ? MHSl->size() : RHSl->size(); + for (unsigned i = 0; i != Size; ++i) { + Init *Node = MHSl ? MHSl->getElement(i) : UnsetInit::get(RK); + Init *Name = RHSl ? RHSl->getElement(i) : UnsetInit::get(RK); + if (!isa<StringInit>(Name) && !isa<UnsetInit>(Name)) + return const_cast<TernOpInit *>(this); + Children.emplace_back(Node, dyn_cast<StringInit>(Name)); + } + return DagInit::get(LHS, nullptr, Children); + } + 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(RK, LHSs->getValue().substr(Start, Length), + LHSs->getFormat()); + } + break; + } + + case FIND: { + StringInit *LHSs = dyn_cast<StringInit>(LHS); + StringInit *MHSs = dyn_cast<StringInit>(MHS); + IntInit *RHSi = dyn_cast<IntInit>(RHS); + if (LHSs && MHSs && RHSi) { + int64_t SourceSize = LHSs->getValue().size(); + int64_t Start = RHSi->getValue(); + if (Start < 0 || Start > SourceSize) + PrintError(CurRec->getLoc(), + Twine("!find start position is out of range 0...") + + std::to_string(SourceSize) + ": " + + std::to_string(Start)); + auto I = LHSs->getValue().find(MHSs->getValue(), Start); + if (I == std::string::npos) + return IntInit::get(RK, -1); + return IntInit::get(RK, I); + } + break; + } + } + + return const_cast<TernOpInit *>(this); +} + +Init *TernOpInit::resolveReferences(Resolver &R) const { + Init *lhs = LHS->resolveReferences(R); + + if (getOpcode() == IF && lhs != LHS) { + if (IntInit *Value = dyn_cast_or_null<IntInit>( + lhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) { + // Short-circuit + if (Value->getValue()) + return MHS->resolveReferences(R); + return RHS->resolveReferences(R); + } + } + + Init *mhs = MHS->resolveReferences(R); + Init *rhs; + + if (getOpcode() == FOREACH || getOpcode() == FILTER) { + ShadowResolver SR(R); + SR.addShadow(lhs); + rhs = RHS->resolveReferences(SR); + } else { + rhs = RHS->resolveReferences(R); + } + + if (LHS != lhs || MHS != mhs || RHS != rhs) + return (TernOpInit::get(getOpcode(), lhs, mhs, rhs, getType())) + ->Fold(R.getCurrentRecord()); + return const_cast<TernOpInit *>(this); +} + +std::string TernOpInit::getAsString() const { + std::string Result; + bool UnquotedLHS = false; + switch (getOpcode()) { + 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 SUBST: Result = "!subst"; break; + case SUBSTR: Result = "!substr"; break; + case FIND: Result = "!find"; break; + } + return (Result + "(" + + (UnquotedLHS ? LHS->getAsUnquotedString() : LHS->getAsString()) + + ", " + MHS->getAsString() + ", " + RHS->getAsString() + ")"); +} + +static void ProfileFoldOpInit(FoldingSetNodeID &ID, Init *Start, Init *List, + Init *A, Init *B, Init *Expr, RecTy *Type) { + ID.AddPointer(Start); + ID.AddPointer(List); + ID.AddPointer(A); + ID.AddPointer(B); + ID.AddPointer(Expr); + ID.AddPointer(Type); +} + +FoldOpInit *FoldOpInit::get(Init *Start, Init *List, Init *A, Init *B, + Init *Expr, RecTy *Type) { + FoldingSetNodeID ID; + ProfileFoldOpInit(ID, Start, List, A, B, Expr, Type); + + detail::RecordKeeperImpl &RK = Start->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (FoldOpInit *I = RK.TheFoldOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + FoldOpInit *I = new (RK.Allocator) FoldOpInit(Start, List, A, B, Expr, Type); + RK.TheFoldOpInitPool.InsertNode(I, IP); + return I; +} + +void FoldOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileFoldOpInit(ID, Start, List, A, B, Expr, getType()); +} + +Init *FoldOpInit::Fold(Record *CurRec) const { + if (ListInit *LI = dyn_cast<ListInit>(List)) { + Init *Accum = Start; + for (Init *Elt : *LI) { + MapResolver R(CurRec); + R.set(A, Accum); + R.set(B, Elt); + Accum = Expr->resolveReferences(R); + } + return Accum; + } + return const_cast<FoldOpInit *>(this); +} + +Init *FoldOpInit::resolveReferences(Resolver &R) const { + Init *NewStart = Start->resolveReferences(R); + Init *NewList = List->resolveReferences(R); + ShadowResolver SR(R); + SR.addShadow(A); + SR.addShadow(B); + Init *NewExpr = Expr->resolveReferences(SR); + + if (Start == NewStart && List == NewList && Expr == NewExpr) + return const_cast<FoldOpInit *>(this); + + return get(NewStart, NewList, A, B, NewExpr, getType()) + ->Fold(R.getCurrentRecord()); +} + +Init *FoldOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(const_cast<FoldOpInit *>(this), Bit); +} + +std::string FoldOpInit::getAsString() const { + return (Twine("!foldl(") + Start->getAsString() + ", " + List->getAsString() + + ", " + A->getAsUnquotedString() + ", " + B->getAsUnquotedString() + + ", " + Expr->getAsString() + ")") + .str(); +} + +static void ProfileIsAOpInit(FoldingSetNodeID &ID, RecTy *CheckType, + Init *Expr) { + ID.AddPointer(CheckType); + ID.AddPointer(Expr); +} + +IsAOpInit *IsAOpInit::get(RecTy *CheckType, Init *Expr) { + + FoldingSetNodeID ID; + ProfileIsAOpInit(ID, CheckType, Expr); + + detail::RecordKeeperImpl &RK = Expr->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (IsAOpInit *I = RK.TheIsAOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + IsAOpInit *I = new (RK.Allocator) IsAOpInit(CheckType, Expr); + RK.TheIsAOpInitPool.InsertNode(I, IP); + return I; +} + +void IsAOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileIsAOpInit(ID, CheckType, Expr); +} + +Init *IsAOpInit::Fold() const { + if (TypedInit *TI = dyn_cast<TypedInit>(Expr)) { + // Is the expression type known to be (a subclass of) the desired type? + if (TI->getType()->typeIsConvertibleTo(CheckType)) + return IntInit::get(getRecordKeeper(), 1); + + if (isa<RecordRecTy>(CheckType)) { + // If the target type is not a subclass of the expression type, or if + // the expression has fully resolved to a record, we know that it can't + // be of the required type. + if (!CheckType->typeIsConvertibleTo(TI->getType()) || isa<DefInit>(Expr)) + return IntInit::get(getRecordKeeper(), 0); + } else { + // We treat non-record types as not castable. + return IntInit::get(getRecordKeeper(), 0); + } + } + return const_cast<IsAOpInit *>(this); +} + +Init *IsAOpInit::resolveReferences(Resolver &R) const { + Init *NewExpr = Expr->resolveReferences(R); + if (Expr != NewExpr) + return get(CheckType, NewExpr)->Fold(); + return const_cast<IsAOpInit *>(this); +} + +Init *IsAOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(const_cast<IsAOpInit *>(this), Bit); +} + +std::string IsAOpInit::getAsString() const { + return (Twine("!isa<") + CheckType->getAsString() + ">(" + + Expr->getAsString() + ")") + .str(); +} + +static void ProfileExistsOpInit(FoldingSetNodeID &ID, RecTy *CheckType, + Init *Expr) { + ID.AddPointer(CheckType); + ID.AddPointer(Expr); +} + +ExistsOpInit *ExistsOpInit::get(RecTy *CheckType, Init *Expr) { + FoldingSetNodeID ID; + ProfileExistsOpInit(ID, CheckType, Expr); + + detail::RecordKeeperImpl &RK = Expr->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (ExistsOpInit *I = RK.TheExistsOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + ExistsOpInit *I = new (RK.Allocator) ExistsOpInit(CheckType, Expr); + RK.TheExistsOpInitPool.InsertNode(I, IP); + return I; +} + +void ExistsOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileExistsOpInit(ID, CheckType, Expr); +} + +Init *ExistsOpInit::Fold(Record *CurRec, bool IsFinal) const { + if (StringInit *Name = dyn_cast<StringInit>(Expr)) { + if (!CurRec && !IsFinal) + return const_cast<ExistsOpInit *>(this); + + // Self-references are allowed, but their resolution is delayed until + // the final resolve to ensure that we get the correct type for them. + auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit()); + if (Name == CurRec->getNameInit() || + (Anonymous && Name == Anonymous->getNameInit())) { + if (!IsFinal) + return const_cast<ExistsOpInit *>(this); + + // No doubt that there exists a record, so we should check if types are + // compatiable. + return IntInit::get(getRecordKeeper(), + CurRec->getType()->typeIsA(CheckType)); + } + + // Look up all defined records to see if we can find one. + Record *D = CheckType->getRecordKeeper().getDef(Name->getValue()); + if (!D) { + if (IsFinal) + return IntInit::get(getRecordKeeper(), 0); + return const_cast<ExistsOpInit *>(this); + } + + // Check if types are compatiable. + return IntInit::get(getRecordKeeper(), + DefInit::get(D)->getType()->typeIsA(CheckType)); + } + return const_cast<ExistsOpInit *>(this); +} + +Init *ExistsOpInit::resolveReferences(Resolver &R) const { + Init *NewExpr = Expr->resolveReferences(R); + if (Expr != NewExpr || R.isFinal()) + return get(CheckType, NewExpr)->Fold(R.getCurrentRecord(), R.isFinal()); + return const_cast<ExistsOpInit *>(this); +} + +Init *ExistsOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(const_cast<ExistsOpInit *>(this), Bit); +} + +std::string ExistsOpInit::getAsString() const { + return (Twine("!exists<") + CheckType->getAsString() + ">(" + + Expr->getAsString() + ")") + .str(); +} + +RecTy *TypedInit::getFieldType(StringInit *FieldName) const { + if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) { + for (Record *Rec : RecordType->getClasses()) { + if (RecordVal *Field = Rec->getValue(FieldName)) + return Field->getType(); + } + } + return nullptr; +} + +Init * +TypedInit::convertInitializerTo(RecTy *Ty) const { + if (getType() == Ty || getType()->typeIsA(Ty)) + return const_cast<TypedInit *>(this); + + if (isa<BitRecTy>(getType()) && isa<BitsRecTy>(Ty) && + cast<BitsRecTy>(Ty)->getNumBits() == 1) + return BitsInit::get(getRecordKeeper(), {const_cast<TypedInit *>(this)}); + + return nullptr; +} + +Init *TypedInit::convertInitializerBitRange(ArrayRef<unsigned> Bits) const { + BitsRecTy *T = dyn_cast<BitsRecTy>(getType()); + if (!T) return nullptr; // Cannot subscript a non-bits variable. + unsigned NumBits = T->getNumBits(); + + SmallVector<Init *, 16> NewBits; + NewBits.reserve(Bits.size()); + for (unsigned Bit : Bits) { + if (Bit >= NumBits) + return nullptr; + + NewBits.push_back(VarBitInit::get(const_cast<TypedInit *>(this), Bit)); + } + return BitsInit::get(getRecordKeeper(), NewBits); +} + +Init *TypedInit::getCastTo(RecTy *Ty) const { + // Handle the common case quickly + if (getType() == Ty || getType()->typeIsA(Ty)) + return const_cast<TypedInit *>(this); + + if (Init *Converted = convertInitializerTo(Ty)) { + assert(!isa<TypedInit>(Converted) || + cast<TypedInit>(Converted)->getType()->typeIsA(Ty)); + return Converted; + } + + if (!getType()->typeIsConvertibleTo(Ty)) + return nullptr; + + return UnOpInit::get(UnOpInit::CAST, const_cast<TypedInit *>(this), Ty) + ->Fold(nullptr); +} + +Init *TypedInit::convertInitListSlice(ArrayRef<unsigned> Elements) const { + ListRecTy *T = dyn_cast<ListRecTy>(getType()); + if (!T) return nullptr; // Cannot subscript a non-list variable. + + if (Elements.size() == 1) + return VarListElementInit::get(const_cast<TypedInit *>(this), Elements[0]); + + SmallVector<Init*, 8> ListInits; + ListInits.reserve(Elements.size()); + for (unsigned Element : Elements) + ListInits.push_back(VarListElementInit::get(const_cast<TypedInit *>(this), + Element)); + return ListInit::get(ListInits, T->getElementType()); +} + + +VarInit *VarInit::get(StringRef VN, RecTy *T) { + Init *Value = StringInit::get(T->getRecordKeeper(), VN); + return VarInit::get(Value, T); +} + +VarInit *VarInit::get(Init *VN, RecTy *T) { + detail::RecordKeeperImpl &RK = T->getRecordKeeper().getImpl(); + VarInit *&I = RK.TheVarInitPool[std::make_pair(T, VN)]; + if (!I) + I = new (RK.Allocator) VarInit(VN, T); + return I; +} + +StringRef VarInit::getName() const { + StringInit *NameString = cast<StringInit>(getNameInit()); + return NameString->getValue(); +} + +Init *VarInit::getBit(unsigned Bit) const { + if (getType() == BitRecTy::get(getRecordKeeper())) + return const_cast<VarInit*>(this); + return VarBitInit::get(const_cast<VarInit*>(this), Bit); +} + +Init *VarInit::resolveReferences(Resolver &R) const { + if (Init *Val = R.resolve(VarName)) + return Val; + return const_cast<VarInit *>(this); +} + +VarBitInit *VarBitInit::get(TypedInit *T, unsigned B) { + detail::RecordKeeperImpl &RK = T->getRecordKeeper().getImpl(); + VarBitInit *&I = RK.TheVarBitInitPool[std::make_pair(T, B)]; + if (!I) + I = new (RK.Allocator) VarBitInit(T, B); + return I; +} + +std::string VarBitInit::getAsString() const { + return TI->getAsString() + "{" + utostr(Bit) + "}"; +} + +Init *VarBitInit::resolveReferences(Resolver &R) const { + Init *I = TI->resolveReferences(R); + if (TI != I) + return I->getBit(getBitNum()); + + return const_cast<VarBitInit*>(this); +} + +VarListElementInit *VarListElementInit::get(TypedInit *T, unsigned E) { + detail::RecordKeeperImpl &RK = T->getRecordKeeper().getImpl(); + VarListElementInit *&I = RK.TheVarListElementInitPool[std::make_pair(T, E)]; + if (!I) + I = new (RK.Allocator) VarListElementInit(T, E); + return I; +} + +std::string VarListElementInit::getAsString() const { + return TI->getAsString() + "[" + utostr(Element) + "]"; +} + +Init *VarListElementInit::resolveReferences(Resolver &R) const { + Init *NewTI = TI->resolveReferences(R); + if (ListInit *List = dyn_cast<ListInit>(NewTI)) { + // Leave out-of-bounds array references as-is. This can happen without + // being an error, e.g. in the untaken "branch" of an !if expression. + if (getElementNum() < List->size()) + return List->getElement(getElementNum()); + } + if (NewTI != TI && isa<TypedInit>(NewTI)) + return VarListElementInit::get(cast<TypedInit>(NewTI), getElementNum()); + return const_cast<VarListElementInit *>(this); +} + +Init *VarListElementInit::getBit(unsigned Bit) const { + if (getType() == BitRecTy::get(getRecordKeeper())) + return const_cast<VarListElementInit*>(this); + return VarBitInit::get(const_cast<VarListElementInit*>(this), Bit); +} + +DefInit::DefInit(Record *D) + : TypedInit(IK_DefInit, D->getType()), Def(D) {} + +DefInit *DefInit::get(Record *R) { + return R->getDefInit(); +} + +Init *DefInit::convertInitializerTo(RecTy *Ty) const { + if (auto *RRT = dyn_cast<RecordRecTy>(Ty)) + if (getType()->typeIsConvertibleTo(RRT)) + return const_cast<DefInit *>(this); + return nullptr; +} + +RecTy *DefInit::getFieldType(StringInit *FieldName) const { + if (const RecordVal *RV = Def->getValue(FieldName)) + return RV->getType(); + return nullptr; +} + +std::string DefInit::getAsString() const { return std::string(Def->getName()); } + +static void ProfileVarDefInit(FoldingSetNodeID &ID, + Record *Class, + ArrayRef<Init *> Args) { + ID.AddInteger(Args.size()); + ID.AddPointer(Class); + + for (Init *I : Args) + ID.AddPointer(I); +} + +VarDefInit::VarDefInit(Record *Class, unsigned N) + : TypedInit(IK_VarDefInit, RecordRecTy::get(Class)), Class(Class), + NumArgs(N) {} + +VarDefInit *VarDefInit::get(Record *Class, ArrayRef<Init *> Args) { + FoldingSetNodeID ID; + ProfileVarDefInit(ID, Class, Args); + + detail::RecordKeeperImpl &RK = Class->getRecords().getImpl(); + void *IP = nullptr; + if (VarDefInit *I = RK.TheVarDefInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = RK.Allocator.Allocate(totalSizeToAlloc<Init *>(Args.size()), + alignof(VarDefInit)); + VarDefInit *I = new (Mem) VarDefInit(Class, Args.size()); + std::uninitialized_copy(Args.begin(), Args.end(), + I->getTrailingObjects<Init *>()); + RK.TheVarDefInitPool.InsertNode(I, IP); + return I; +} + +void VarDefInit::Profile(FoldingSetNodeID &ID) const { + ProfileVarDefInit(ID, Class, args()); +} + +DefInit *VarDefInit::instantiate() { + if (!Def) { + RecordKeeper &Records = Class->getRecords(); + auto NewRecOwner = std::make_unique<Record>(Records.getNewAnonymousName(), + Class->getLoc(), Records, + /*IsAnonymous=*/true); + Record *NewRec = NewRecOwner.get(); + + // Copy values from class to instance + for (const RecordVal &Val : Class->getValues()) + NewRec->addValue(Val); + + // Copy assertions from class to instance. + NewRec->appendAssertions(Class); + + // Substitute and resolve template arguments + ArrayRef<Init *> TArgs = Class->getTemplateArgs(); + MapResolver R(NewRec); + + for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { + if (i < args_size()) + R.set(TArgs[i], getArg(i)); + else + R.set(TArgs[i], NewRec->getValue(TArgs[i])->getValue()); + + NewRec->removeValue(TArgs[i]); + } + + NewRec->resolveReferences(R); + + // Add superclasses. + ArrayRef<std::pair<Record *, SMRange>> SCs = Class->getSuperClasses(); + for (const auto &SCPair : SCs) + NewRec->addSuperClass(SCPair.first, SCPair.second); + + NewRec->addSuperClass(Class, + SMRange(Class->getLoc().back(), + Class->getLoc().back())); + + // Resolve internal references and store in record keeper + NewRec->resolveReferences(); + Records.addDef(std::move(NewRecOwner)); + + // Check the assertions. + NewRec->checkRecordAssertions(); + + Def = DefInit::get(NewRec); + } + + return Def; +} + +Init *VarDefInit::resolveReferences(Resolver &R) const { + TrackUnresolvedResolver UR(&R); + bool Changed = false; + SmallVector<Init *, 8> NewArgs; + NewArgs.reserve(args_size()); + + for (Init *Arg : args()) { + Init *NewArg = Arg->resolveReferences(UR); + NewArgs.push_back(NewArg); + Changed |= NewArg != Arg; + } + + if (Changed) { + auto New = VarDefInit::get(Class, NewArgs); + if (!UR.foundUnresolved()) + return New->instantiate(); + return New; + } + return const_cast<VarDefInit *>(this); +} + +Init *VarDefInit::Fold() const { + if (Def) + return Def; + + TrackUnresolvedResolver R; + for (Init *Arg : args()) + Arg->resolveReferences(R); + + if (!R.foundUnresolved()) + return const_cast<VarDefInit *>(this)->instantiate(); + return const_cast<VarDefInit *>(this); +} + +std::string VarDefInit::getAsString() const { + std::string Result = Class->getNameInitAsString() + "<"; + const char *sep = ""; + for (Init *Arg : args()) { + Result += sep; + sep = ", "; + Result += Arg->getAsString(); + } + return Result + ">"; +} + +FieldInit *FieldInit::get(Init *R, StringInit *FN) { + detail::RecordKeeperImpl &RK = R->getRecordKeeper().getImpl(); + FieldInit *&I = RK.TheFieldInitPool[std::make_pair(R, FN)]; + if (!I) + I = new (RK.Allocator) FieldInit(R, FN); + return I; +} + +Init *FieldInit::getBit(unsigned Bit) const { + if (getType() == BitRecTy::get(getRecordKeeper())) + return const_cast<FieldInit*>(this); + return VarBitInit::get(const_cast<FieldInit*>(this), Bit); +} + +Init *FieldInit::resolveReferences(Resolver &R) const { + Init *NewRec = Rec->resolveReferences(R); + if (NewRec != Rec) + return FieldInit::get(NewRec, FieldName)->Fold(R.getCurrentRecord()); + return const_cast<FieldInit *>(this); +} + +Init *FieldInit::Fold(Record *CurRec) const { + if (DefInit *DI = dyn_cast<DefInit>(Rec)) { + Record *Def = DI->getDef(); + if (Def == CurRec) + PrintFatalError(CurRec->getLoc(), + Twine("Attempting to access field '") + + FieldName->getAsUnquotedString() + "' of '" + + Rec->getAsString() + "' is a forbidden self-reference"); + Init *FieldVal = Def->getValue(FieldName)->getValue(); + if (FieldVal->isConcrete()) + return FieldVal; + } + return const_cast<FieldInit *>(this); +} + +bool FieldInit::isConcrete() const { + if (DefInit *DI = dyn_cast<DefInit>(Rec)) { + Init *FieldVal = DI->getDef()->getValue(FieldName)->getValue(); + return FieldVal->isConcrete(); + } + return false; +} + +static void ProfileCondOpInit(FoldingSetNodeID &ID, + ArrayRef<Init *> CondRange, + ArrayRef<Init *> ValRange, + const RecTy *ValType) { + assert(CondRange.size() == ValRange.size() && + "Number of conditions and values must match!"); + ID.AddPointer(ValType); + ArrayRef<Init *>::iterator Case = CondRange.begin(); + ArrayRef<Init *>::iterator Val = ValRange.begin(); + + while (Case != CondRange.end()) { + ID.AddPointer(*Case++); + ID.AddPointer(*Val++); + } +} + +void CondOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileCondOpInit(ID, ArrayRef(getTrailingObjects<Init *>(), NumConds), + ArrayRef(getTrailingObjects<Init *>() + NumConds, NumConds), + ValType); +} + +CondOpInit *CondOpInit::get(ArrayRef<Init *> CondRange, + ArrayRef<Init *> ValRange, RecTy *Ty) { + assert(CondRange.size() == ValRange.size() && + "Number of conditions and values must match!"); + + FoldingSetNodeID ID; + ProfileCondOpInit(ID, CondRange, ValRange, Ty); + + detail::RecordKeeperImpl &RK = Ty->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (CondOpInit *I = RK.TheCondOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = RK.Allocator.Allocate( + totalSizeToAlloc<Init *>(2 * CondRange.size()), alignof(BitsInit)); + CondOpInit *I = new(Mem) CondOpInit(CondRange.size(), Ty); + + std::uninitialized_copy(CondRange.begin(), CondRange.end(), + I->getTrailingObjects<Init *>()); + std::uninitialized_copy(ValRange.begin(), ValRange.end(), + I->getTrailingObjects<Init *>()+CondRange.size()); + RK.TheCondOpInitPool.InsertNode(I, IP); + return I; +} + +Init *CondOpInit::resolveReferences(Resolver &R) const { + SmallVector<Init*, 4> NewConds; + bool Changed = false; + for (const Init *Case : getConds()) { + Init *NewCase = Case->resolveReferences(R); + NewConds.push_back(NewCase); + Changed |= NewCase != Case; + } + + SmallVector<Init*, 4> NewVals; + for (const Init *Val : getVals()) { + Init *NewVal = Val->resolveReferences(R); + NewVals.push_back(NewVal); + Changed |= NewVal != Val; + } + + if (Changed) + return (CondOpInit::get(NewConds, NewVals, + getValType()))->Fold(R.getCurrentRecord()); + + return const_cast<CondOpInit *>(this); +} + +Init *CondOpInit::Fold(Record *CurRec) const { + RecordKeeper &RK = getRecordKeeper(); + for ( unsigned i = 0; i < NumConds; ++i) { + Init *Cond = getCond(i); + Init *Val = getVal(i); + + if (IntInit *CondI = dyn_cast_or_null<IntInit>( + Cond->convertInitializerTo(IntRecTy::get(RK)))) { + if (CondI->getValue()) + return Val->convertInitializerTo(getValType()); + } else { + return const_cast<CondOpInit *>(this); + } + } + + PrintFatalError(CurRec->getLoc(), + CurRec->getName() + + " does not have any true condition in:" + + this->getAsString()); + return nullptr; +} + +bool CondOpInit::isConcrete() const { + for (const Init *Case : getConds()) + if (!Case->isConcrete()) + return false; + + for (const Init *Val : getVals()) + if (!Val->isConcrete()) + return false; + + return true; +} + +bool CondOpInit::isComplete() const { + for (const Init *Case : getConds()) + if (!Case->isComplete()) + return false; + + for (const Init *Val : getVals()) + if (!Val->isConcrete()) + return false; + + return true; +} + +std::string CondOpInit::getAsString() const { + std::string Result = "!cond("; + for (unsigned i = 0; i < getNumConds(); i++) { + Result += getCond(i)->getAsString() + ": "; + Result += getVal(i)->getAsString(); + if (i != getNumConds()-1) + Result += ", "; + } + return Result + ")"; +} + +Init *CondOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(const_cast<CondOpInit *>(this), Bit); +} + +static void ProfileDagInit(FoldingSetNodeID &ID, Init *V, StringInit *VN, + ArrayRef<Init *> ArgRange, + ArrayRef<StringInit *> NameRange) { + ID.AddPointer(V); + ID.AddPointer(VN); + + ArrayRef<Init *>::iterator Arg = ArgRange.begin(); + ArrayRef<StringInit *>::iterator Name = NameRange.begin(); + while (Arg != ArgRange.end()) { + assert(Name != NameRange.end() && "Arg name underflow!"); + ID.AddPointer(*Arg++); + ID.AddPointer(*Name++); + } + assert(Name == NameRange.end() && "Arg name overflow!"); +} + +DagInit *DagInit::get(Init *V, StringInit *VN, ArrayRef<Init *> ArgRange, + ArrayRef<StringInit *> NameRange) { + assert(ArgRange.size() == NameRange.size()); + FoldingSetNodeID ID; + ProfileDagInit(ID, V, VN, ArgRange, NameRange); + + detail::RecordKeeperImpl &RK = V->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (DagInit *I = RK.TheDagInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = RK.Allocator.Allocate( + totalSizeToAlloc<Init *, StringInit *>(ArgRange.size(), NameRange.size()), + alignof(BitsInit)); + DagInit *I = new (Mem) DagInit(V, VN, ArgRange.size(), NameRange.size()); + std::uninitialized_copy(ArgRange.begin(), ArgRange.end(), + I->getTrailingObjects<Init *>()); + std::uninitialized_copy(NameRange.begin(), NameRange.end(), + I->getTrailingObjects<StringInit *>()); + RK.TheDagInitPool.InsertNode(I, IP); + return I; +} + +DagInit * +DagInit::get(Init *V, StringInit *VN, + ArrayRef<std::pair<Init*, StringInit*>> args) { + SmallVector<Init *, 8> Args; + SmallVector<StringInit *, 8> Names; + + for (const auto &Arg : args) { + Args.push_back(Arg.first); + Names.push_back(Arg.second); + } + + return DagInit::get(V, VN, Args, Names); +} + +void DagInit::Profile(FoldingSetNodeID &ID) const { + ProfileDagInit(ID, Val, ValName, + ArrayRef(getTrailingObjects<Init *>(), NumArgs), + ArrayRef(getTrailingObjects<StringInit *>(), NumArgNames)); +} + +Record *DagInit::getOperatorAsDef(ArrayRef<SMLoc> Loc) const { + if (DefInit *DefI = dyn_cast<DefInit>(Val)) + return DefI->getDef(); + PrintFatalError(Loc, "Expected record as operator"); + return nullptr; +} + +Init *DagInit::resolveReferences(Resolver &R) const { + SmallVector<Init*, 8> NewArgs; + NewArgs.reserve(arg_size()); + bool ArgsChanged = false; + for (const Init *Arg : getArgs()) { + Init *NewArg = Arg->resolveReferences(R); + NewArgs.push_back(NewArg); + ArgsChanged |= NewArg != Arg; + } + + Init *Op = Val->resolveReferences(R); + if (Op != Val || ArgsChanged) + return DagInit::get(Op, ValName, NewArgs, getArgNames()); + + return const_cast<DagInit *>(this); +} + +bool DagInit::isConcrete() const { + if (!Val->isConcrete()) + return false; + for (const Init *Elt : getArgs()) { + if (!Elt->isConcrete()) + return false; + } + return true; +} + +std::string DagInit::getAsString() const { + std::string Result = "(" + Val->getAsString(); + if (ValName) + Result += ":" + ValName->getAsUnquotedString(); + if (!arg_empty()) { + Result += " " + getArg(0)->getAsString(); + if (getArgName(0)) Result += ":$" + getArgName(0)->getAsUnquotedString(); + for (unsigned i = 1, e = getNumArgs(); i != e; ++i) { + Result += ", " + getArg(i)->getAsString(); + if (getArgName(i)) Result += ":$" + getArgName(i)->getAsUnquotedString(); + } + } + return Result + ")"; +} + +//===----------------------------------------------------------------------===// +// Other implementations +//===----------------------------------------------------------------------===// + +RecordVal::RecordVal(Init *N, RecTy *T, FieldKind K) + : Name(N), TyAndKind(T, K) { + setValue(UnsetInit::get(N->getRecordKeeper())); + 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(N->getRecordKeeper())); + assert(Value && "Cannot create unset value for current type!"); +} + +StringRef RecordVal::getName() const { + return cast<StringInit>(getNameInit())->getValue(); +} + +std::string RecordVal::getPrintType() const { + if (getType() == StringRecTy::get(getRecordKeeper())) { + 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()); + 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(V->getRecordKeeper(), 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(getRecordKeeper(), Bits); + } + } + } + return Value == nullptr; + } + Value = nullptr; + return false; +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +#include "llvm/TableGen/Record.h" +LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; } +#endif + +void RecordVal::print(raw_ostream &OS, bool PrintSem) const { + if (isNonconcreteOK()) OS << "field "; + OS << getPrintType() << " " << getNameInitAsString(); + + if (getValue()) + OS << " = " << *getValue(); + + if (PrintSem) OS << ";\n"; +} + +void Record::updateClassLoc(SMLoc Loc) { + assert(Locs.size() == 1); + ForwardDeclarationLocs.push_back(Locs.front()); + + Locs.clear(); + Locs.push_back(Loc); +} + +void Record::checkName() { + // Ensure the record name has string type. + const TypedInit *TypedName = cast<const TypedInit>(Name); + if (!isa<StringRecTy>(TypedName->getType())) + PrintFatalError(getLoc(), Twine("Record name '") + Name->getAsString() + + "' is not a string!"); +} + +RecordRecTy *Record::getType() { + SmallVector<Record *, 4> DirectSCs; + getDirectSuperClasses(DirectSCs); + return RecordRecTy::get(TrackedRecords, DirectSCs); +} + +DefInit *Record::getDefInit() { + if (!CorrespondingDefInit) { + CorrespondingDefInit = + new (TrackedRecords.getImpl().Allocator) DefInit(this); + } + return CorrespondingDefInit; +} + +unsigned Record::getNewUID(RecordKeeper &RK) { + return RK.getImpl().LastRecordID++; +} + +void Record::setName(Init *NewName) { + Name = NewName; + checkName(); + // DO NOT resolve record values to the name at this point because + // there might be default values for arguments of this def. Those + // arguments might not have been resolved yet so we don't want to + // prematurely assume values for those arguments were not passed to + // this def. + // + // Nonetheless, it may be that some of this Record's values + // reference the record name. Indeed, the reason for having the + // record name be an Init is to provide this flexibility. The extra + // resolve steps after completely instantiating defs takes care of + // 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()) { + Record *SC = SCs.back().first; + SCs = SCs.drop_back(1 + SC->getSuperClasses().size()); + Classes.push_back(SC); + } +} + +void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) { + Init *OldName = getNameInit(); + Init *NewName = Name->resolveReferences(R); + if (NewName != OldName) { + // Re-register with RecordKeeper. + setName(NewName); + } + + // Resolve the field values. + for (RecordVal &Value : Values) { + if (SkipVal == &Value) // Skip resolve the same field as the given one + continue; + if (Init *V = Value.getValue()) { + Init *VR = V->resolveReferences(R); + if (Value.setValue(VR)) { + std::string Type; + if (TypedInit *VRT = dyn_cast<TypedInit>(VR)) + Type = + (Twine("of type '") + VRT->getType()->getAsString() + "' ").str(); + PrintFatalError( + getLoc(), + Twine("Invalid value ") + Type + "found when setting field '" + + Value.getNameInitAsString() + "' of type '" + + Value.getType()->getAsString() + + "' after resolving references: " + VR->getAsUnquotedString() + + "\n"); + } + } + } + + // Resolve the assertion expressions. + for (auto &Assertion : Assertions) { + Init *Value = Assertion.Condition->resolveReferences(R); + Assertion.Condition = Value; + Value = Assertion.Message->resolveReferences(R); + Assertion.Message = Value; + } +} + +void Record::resolveReferences(Init *NewName) { + RecordResolver R(*this); + R.setName(NewName); + R.setFinal(true); + resolveReferences(R); +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void Record::dump() const { errs() << *this; } +#endif + +raw_ostream &llvm::operator<<(raw_ostream &OS, const Record &R) { + OS << R.getNameInitAsString(); + + ArrayRef<Init *> TArgs = R.getTemplateArgs(); + if (!TArgs.empty()) { + OS << "<"; + bool NeedComma = false; + for (const Init *TA : TArgs) { + if (NeedComma) OS << ", "; + NeedComma = true; + const RecordVal *RV = R.getValue(TA); + assert(RV && "Template argument record not found??"); + RV->print(OS, false); + } + OS << ">"; + } + + OS << " {"; + ArrayRef<std::pair<Record *, SMRange>> SC = R.getSuperClasses(); + if (!SC.empty()) { + OS << "\t//"; + for (const auto &SuperPair : SC) + OS << " " << SuperPair.first->getNameInitAsString(); + } + OS << "\n"; + + for (const RecordVal &Val : R.getValues()) + if (Val.isNonconcreteOK() && !R.isTemplateArg(Val.getNameInit())) + OS << Val; + for (const RecordVal &Val : R.getValues()) + 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()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + return R->getValue(); +} + +StringRef Record::getValueAsString(StringRef FieldName) const { + std::optional<StringRef> S = getValueAsOptionalString(FieldName); + if (!S) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + return *S; +} + +std::optional<StringRef> +Record::getValueAsOptionalString(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + return std::nullopt; + if (isa<UnsetInit>(R->getValue())) + return std::nullopt; + + if (StringInit *SI = dyn_cast<StringInit>(R->getValue())) + return SI->getValue(); + + PrintFatalError(getLoc(), + "Record `" + getName() + "', ` field `" + FieldName + + "' exists but does not have a string initializer!"); +} + +BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (BitsInit *BI = dyn_cast<BitsInit>(R->getValue())) + return BI; + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName + + "' exists but does not have a bits value"); +} + +ListInit *Record::getValueAsListInit(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (ListInit *LI = dyn_cast<ListInit>(R->getValue())) + return LI; + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName + + "' exists but does not have a list value"); +} + +std::vector<Record*> +Record::getValueAsListOfDefs(StringRef FieldName) const { + ListInit *List = getValueAsListInit(FieldName); + std::vector<Record*> Defs; + for (Init *I : List->getValues()) { + if (DefInit *DI = dyn_cast<DefInit>(I)) + Defs.push_back(DI->getDef()); + else + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + + FieldName + "' list is not entirely DefInit!"); + } + return Defs; +} + +int64_t Record::getValueAsInt(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (IntInit *II = dyn_cast<IntInit>(R->getValue())) + return II->getValue(); + PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" + + FieldName + + "' exists but does not have an int value: " + + R->getValue()->getAsString()); +} + +std::vector<int64_t> +Record::getValueAsListOfInts(StringRef FieldName) const { + ListInit *List = getValueAsListInit(FieldName); + std::vector<int64_t> Ints; + for (Init *I : List->getValues()) { + if (IntInit *II = dyn_cast<IntInit>(I)) + Ints.push_back(II->getValue()); + else + PrintFatalError(getLoc(), + Twine("Record `") + getName() + "', field `" + FieldName + + "' exists but does not have a list of ints value: " + + I->getAsString()); + } + return Ints; +} + +std::vector<StringRef> +Record::getValueAsListOfStrings(StringRef FieldName) const { + ListInit *List = getValueAsListInit(FieldName); + std::vector<StringRef> Strings; + for (Init *I : List->getValues()) { + if (StringInit *SI = dyn_cast<StringInit>(I)) + Strings.push_back(SI->getValue()); + else + PrintFatalError(getLoc(), + Twine("Record `") + getName() + "', field `" + FieldName + + "' exists but does not have a list of strings value: " + + I->getAsString()); + } + return Strings; +} + +Record *Record::getValueAsDef(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (DefInit *DI = dyn_cast<DefInit>(R->getValue())) + return DI->getDef(); + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + + FieldName + "' does not have a def initializer!"); +} + +Record *Record::getValueAsOptionalDef(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (DefInit *DI = dyn_cast<DefInit>(R->getValue())) + return DI->getDef(); + if (isa<UnsetInit>(R->getValue())) + return nullptr; + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + + FieldName + "' does not have either a def initializer or '?'!"); +} + + +bool Record::getValueAsBit(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (BitInit *BI = dyn_cast<BitInit>(R->getValue())) + return BI->getValue(); + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + + FieldName + "' does not have a bit initializer!"); +} + +bool Record::getValueAsBitOrUnset(StringRef FieldName, bool &Unset) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName.str() + "'!\n"); + + if (isa<UnsetInit>(R->getValue())) { + Unset = true; + return false; + } + Unset = false; + if (BitInit *BI = dyn_cast<BitInit>(R->getValue())) + return BI->getValue(); + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + + FieldName + "' does not have a bit initializer!"); +} + +DagInit *Record::getValueAsDag(StringRef FieldName) const { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (DagInit *DI = dyn_cast<DagInit>(R->getValue())) + return DI; + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + + FieldName + "' does not have a dag initializer!"); +} + +// Check all record assertions: For each one, resolve the condition +// and message, then call CheckAssert(). +// Note: The condition and message are probably already resolved, +// but resolving again allows calls before records are resolved. +void Record::checkRecordAssertions() { + RecordResolver R(*this); + R.setFinal(true); + + for (const auto &Assertion : getAssertions()) { + Init *Condition = Assertion.Condition->resolveReferences(R); + Init *Message = Assertion.Message->resolveReferences(R); + CheckAssert(Assertion.Loc, Condition, Message); + } +} + +// Report a warning if the record has unused template arguments. +void Record::checkUnusedTemplateArgs() { + for (const Init *TA : getTemplateArgs()) { + const RecordVal *Arg = getValue(TA); + if (!Arg->isUsed()) + PrintWarning(Arg->getLoc(), + "unused template argument: " + Twine(Arg->getName())); + } +} + +RecordKeeper::RecordKeeper() + : Impl(std::make_unique<detail::RecordKeeperImpl>(*this)) {} +RecordKeeper::~RecordKeeper() = default; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void RecordKeeper::dump() const { errs() << *this; } +#endif + +raw_ostream &llvm::operator<<(raw_ostream &OS, const RecordKeeper &RK) { + OS << "------------- Classes -----------------\n"; + for (const auto &C : RK.getClasses()) + OS << "class " << *C.second; + + OS << "------------- Defs -----------------\n"; + for (const auto &D : RK.getDefs()) + OS << "def " << *D.second; + return OS; +} + +/// GetNewAnonymousName - Generate a unique anonymous name that can be used as +/// an identifier. +Init *RecordKeeper::getNewAnonymousName() { + return AnonymousNameInit::get(*this, getImpl().AnonCounter++); +} + +// These functions implement the phase timing facility. Starting a timer +// when one is already running stops the running one. + +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; + } + } +} + +std::vector<Record *> +RecordKeeper::getAllDerivedDefinitions(StringRef ClassName) const { + // We cache the record vectors for single classes. Many backends request + // the same vectors multiple times. + auto Pair = ClassRecordsMap.try_emplace(ClassName); + if (Pair.second) + Pair.first->second = getAllDerivedDefinitions(ArrayRef(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; +} + +std::vector<Record *> +RecordKeeper::getAllDerivedDefinitionsIfDefined(StringRef ClassName) const { + return getClass(ClassName) ? getAllDerivedDefinitions(ClassName) + : std::vector<Record *>(); +} + +Init *MapResolver::resolve(Init *VarName) { + auto It = Map.find(VarName); + if (It == Map.end()) + return nullptr; + + Init *I = It->second.V; + + if (!It->second.Resolved && Map.size() > 1) { + // Resolve mutual references among the mapped variables, but prevent + // infinite recursion. + Map.erase(It); + I = I->resolveReferences(*this); + Map[VarName] = {I, true}; + } + + return I; +} + +Init *RecordResolver::resolve(Init *VarName) { + Init *Val = Cache.lookup(VarName); + if (Val) + return Val; + + if (llvm::is_contained(Stack, VarName)) + return nullptr; // prevent infinite recursion + + if (RecordVal *RV = getCurrentRecord()->getValue(VarName)) { + if (!isa<UnsetInit>(RV->getValue())) { + Val = RV->getValue(); + Stack.push_back(VarName); + Val = Val->resolveReferences(*this); + Stack.pop_back(); + } + } else if (Name && VarName == getCurrentRecord()->getNameInit()) { + Stack.push_back(VarName); + Val = Name->resolveReferences(*this); + Stack.pop_back(); + } + + Cache[VarName] = Val; + return Val; +} + +Init *TrackUnresolvedResolver::resolve(Init *VarName) { + Init *I = nullptr; + + if (R) { + I = R->resolve(VarName); + if (I && !FoundUnresolved) { + // Do not recurse into the resolved initializer, as that would change + // the behavior of the resolver we're delegating, but do check to see + // if there are unresolved variables remaining. + TrackUnresolvedResolver Sub; + I->resolveReferences(Sub); + FoundUnresolved |= Sub.FoundUnresolved; + } + } + + if (!I) + FoundUnresolved = true; + return I; +} + +Init *HasReferenceResolver::resolve(Init *VarName) +{ + if (VarName == VarNameToTrack) + Found = true; + return nullptr; +} diff --git a/contrib/llvm-project/llvm/lib/TableGen/SetTheory.cpp b/contrib/llvm-project/llvm/lib/TableGen/SetTheory.cpp new file mode 100644 index 000000000000..3db46aae6d96 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/SetTheory.cpp @@ -0,0 +1,332 @@ +//===- SetTheory.cpp - Generate ordered sets from DAG expressions ---------===// +// +// 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 file implements the SetTheory class that computes ordered sets of +// Records from DAG expressions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/SetTheory.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <algorithm> +#include <cstdint> +#include <string> +#include <utility> + +using namespace llvm; + +// Define the standard operators. +namespace { + +using RecSet = SetTheory::RecSet; +using RecVec = SetTheory::RecVec; + +// (add a, b, ...) Evaluate and union all arguments. +struct AddOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts, Loc); + } +}; + +// (sub Add, Sub, ...) Set difference. +struct SubOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + if (Expr->arg_size() < 2) + PrintFatalError(Loc, "Set difference needs at least two arguments: " + + Expr->getAsString()); + RecSet Add, Sub; + ST.evaluate(*Expr->arg_begin(), Add, Loc); + ST.evaluate(Expr->arg_begin() + 1, Expr->arg_end(), Sub, Loc); + for (const auto &I : Add) + if (!Sub.count(I)) + Elts.insert(I); + } +}; + +// (and S1, S2) Set intersection. +struct AndOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + if (Expr->arg_size() != 2) + PrintFatalError(Loc, "Set intersection requires two arguments: " + + Expr->getAsString()); + RecSet S1, S2; + ST.evaluate(Expr->arg_begin()[0], S1, Loc); + ST.evaluate(Expr->arg_begin()[1], S2, Loc); + for (const auto &I : S1) + if (S2.count(I)) + Elts.insert(I); + } +}; + +// SetIntBinOp - Abstract base class for (Op S, N) operators. +struct SetIntBinOp : public SetTheory::Operator { + virtual void apply2(SetTheory &ST, DagInit *Expr, RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) = 0; + + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + if (Expr->arg_size() != 2) + PrintFatalError(Loc, "Operator requires (Op Set, Int) arguments: " + + Expr->getAsString()); + RecSet Set; + ST.evaluate(Expr->arg_begin()[0], Set, Loc); + IntInit *II = dyn_cast<IntInit>(Expr->arg_begin()[1]); + if (!II) + PrintFatalError(Loc, "Second argument must be an integer: " + + Expr->getAsString()); + apply2(ST, Expr, Set, II->getValue(), Elts, Loc); + } +}; + +// (shl S, N) Shift left, remove the first N elements. +struct ShlOp : public SetIntBinOp { + void apply2(SetTheory &ST, DagInit *Expr, RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) override { + if (N < 0) + PrintFatalError(Loc, "Positive shift required: " + + Expr->getAsString()); + if (unsigned(N) < Set.size()) + Elts.insert(Set.begin() + N, Set.end()); + } +}; + +// (trunc S, N) Truncate after the first N elements. +struct TruncOp : public SetIntBinOp { + void apply2(SetTheory &ST, DagInit *Expr, RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) override { + if (N < 0) + PrintFatalError(Loc, "Positive length required: " + + Expr->getAsString()); + if (unsigned(N) > Set.size()) + N = Set.size(); + Elts.insert(Set.begin(), Set.begin() + N); + } +}; + +// Left/right rotation. +struct RotOp : public SetIntBinOp { + const bool Reverse; + + RotOp(bool Rev) : Reverse(Rev) {} + + void apply2(SetTheory &ST, DagInit *Expr, RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) override { + if (Reverse) + N = -N; + // N > 0 -> rotate left, N < 0 -> rotate right. + if (Set.empty()) + return; + if (N < 0) + N = Set.size() - (-N % Set.size()); + else + N %= Set.size(); + Elts.insert(Set.begin() + N, Set.end()); + Elts.insert(Set.begin(), Set.begin() + N); + } +}; + +// (decimate S, N) Pick every N'th element of S. +struct DecimateOp : public SetIntBinOp { + void apply2(SetTheory &ST, DagInit *Expr, RecSet &Set, int64_t N, + RecSet &Elts, ArrayRef<SMLoc> Loc) override { + if (N <= 0) + PrintFatalError(Loc, "Positive stride required: " + + Expr->getAsString()); + for (unsigned I = 0; I < Set.size(); I += N) + Elts.insert(Set[I]); + } +}; + +// (interleave S1, S2, ...) Interleave elements of the arguments. +struct InterleaveOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + // Evaluate the arguments individually. + SmallVector<RecSet, 4> Args(Expr->getNumArgs()); + unsigned MaxSize = 0; + for (unsigned i = 0, e = Expr->getNumArgs(); i != e; ++i) { + ST.evaluate(Expr->getArg(i), Args[i], Loc); + MaxSize = std::max(MaxSize, unsigned(Args[i].size())); + } + // Interleave arguments into Elts. + for (unsigned n = 0; n != MaxSize; ++n) + for (unsigned i = 0, e = Expr->getNumArgs(); i != e; ++i) + if (n < Args[i].size()) + Elts.insert(Args[i][n]); + } +}; + +// (sequence "Format", From, To) Generate a sequence of records by name. +struct SequenceOp : public SetTheory::Operator { + void apply(SetTheory &ST, DagInit *Expr, RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + int Step = 1; + if (Expr->arg_size() > 4) + PrintFatalError(Loc, "Bad args to (sequence \"Format\", From, To): " + + Expr->getAsString()); + else if (Expr->arg_size() == 4) { + if (IntInit *II = dyn_cast<IntInit>(Expr->arg_begin()[3])) { + Step = II->getValue(); + } else + PrintFatalError(Loc, "Stride must be an integer: " + + Expr->getAsString()); + } + + std::string Format; + if (StringInit *SI = dyn_cast<StringInit>(Expr->arg_begin()[0])) + Format = std::string(SI->getValue()); + else + PrintFatalError(Loc, "Format must be a string: " + Expr->getAsString()); + + int64_t From, To; + if (IntInit *II = dyn_cast<IntInit>(Expr->arg_begin()[1])) + From = II->getValue(); + else + PrintFatalError(Loc, "From must be an integer: " + Expr->getAsString()); + if (From < 0 || From >= (1 << 30)) + PrintFatalError(Loc, "From out of range"); + + if (IntInit *II = dyn_cast<IntInit>(Expr->arg_begin()[2])) + To = II->getValue(); + else + PrintFatalError(Loc, "To must be an integer: " + Expr->getAsString()); + if (To < 0 || To >= (1 << 30)) + PrintFatalError(Loc, "To out of range"); + + RecordKeeper &Records = + cast<DefInit>(Expr->getOperator())->getDef()->getRecords(); + + Step *= From <= To ? 1 : -1; + while (true) { + if (Step > 0 && From > To) + break; + else if (Step < 0 && From < To) + break; + std::string Name; + raw_string_ostream OS(Name); + OS << format(Format.c_str(), unsigned(From)); + Record *Rec = Records.getDef(OS.str()); + if (!Rec) + PrintFatalError(Loc, "No def named '" + Name + "': " + + Expr->getAsString()); + // Try to reevaluate Rec in case it is a set. + if (const RecVec *Result = ST.expand(Rec)) + Elts.insert(Result->begin(), Result->end()); + else + Elts.insert(Rec); + + From += Step; + } + } +}; + +// Expand a Def into a set by evaluating one of its fields. +struct FieldExpander : public SetTheory::Expander { + StringRef FieldName; + + FieldExpander(StringRef fn) : FieldName(fn) {} + + void expand(SetTheory &ST, Record *Def, RecSet &Elts) override { + ST.evaluate(Def->getValueInit(FieldName), Elts, Def->getLoc()); + } +}; + +} // end anonymous namespace + +// Pin the vtables to this file. +void SetTheory::Operator::anchor() {} +void SetTheory::Expander::anchor() {} + +SetTheory::SetTheory() { + addOperator("add", std::make_unique<AddOp>()); + addOperator("sub", std::make_unique<SubOp>()); + addOperator("and", std::make_unique<AndOp>()); + addOperator("shl", std::make_unique<ShlOp>()); + addOperator("trunc", std::make_unique<TruncOp>()); + addOperator("rotl", std::make_unique<RotOp>(false)); + addOperator("rotr", std::make_unique<RotOp>(true)); + addOperator("decimate", std::make_unique<DecimateOp>()); + addOperator("interleave", std::make_unique<InterleaveOp>()); + addOperator("sequence", std::make_unique<SequenceOp>()); +} + +void SetTheory::addOperator(StringRef Name, std::unique_ptr<Operator> Op) { + Operators[Name] = std::move(Op); +} + +void SetTheory::addExpander(StringRef ClassName, std::unique_ptr<Expander> E) { + Expanders[ClassName] = std::move(E); +} + +void SetTheory::addFieldExpander(StringRef ClassName, StringRef FieldName) { + addExpander(ClassName, std::make_unique<FieldExpander>(FieldName)); +} + +void SetTheory::evaluate(Init *Expr, RecSet &Elts, ArrayRef<SMLoc> Loc) { + // A def in a list can be a just an element, or it may expand. + if (DefInit *Def = dyn_cast<DefInit>(Expr)) { + if (const RecVec *Result = expand(Def->getDef())) + return Elts.insert(Result->begin(), Result->end()); + Elts.insert(Def->getDef()); + return; + } + + // Lists simply expand. + if (ListInit *LI = dyn_cast<ListInit>(Expr)) + return evaluate(LI->begin(), LI->end(), Elts, Loc); + + // Anything else must be a DAG. + DagInit *DagExpr = dyn_cast<DagInit>(Expr); + if (!DagExpr) + PrintFatalError(Loc, "Invalid set element: " + Expr->getAsString()); + DefInit *OpInit = dyn_cast<DefInit>(DagExpr->getOperator()); + if (!OpInit) + PrintFatalError(Loc, "Bad set expression: " + Expr->getAsString()); + auto I = Operators.find(OpInit->getDef()->getName()); + if (I == Operators.end()) + PrintFatalError(Loc, "Unknown set operator: " + Expr->getAsString()); + I->second->apply(*this, DagExpr, Elts, Loc); +} + +const RecVec *SetTheory::expand(Record *Set) { + // Check existing entries for Set and return early. + ExpandMap::iterator I = Expansions.find(Set); + if (I != Expansions.end()) + return &I->second; + + // This is the first time we see Set. Find a suitable expander. + ArrayRef<std::pair<Record *, SMRange>> SC = Set->getSuperClasses(); + for (const auto &SCPair : SC) { + // Skip unnamed superclasses. + if (!isa<StringInit>(SCPair.first->getNameInit())) + continue; + auto I = Expanders.find(SCPair.first->getName()); + if (I != Expanders.end()) { + // This breaks recursive definitions. + RecVec &EltVec = Expansions[Set]; + RecSet Elts; + I->second->expand(*this, Set, Elts); + EltVec.assign(Elts.begin(), Elts.end()); + return &EltVec; + } + } + + // Set is not expandable. + return nullptr; +} diff --git a/contrib/llvm-project/llvm/lib/TableGen/StringMatcher.cpp b/contrib/llvm-project/llvm/lib/TableGen/StringMatcher.cpp new file mode 100644 index 000000000000..c169b4e0a362 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/StringMatcher.cpp @@ -0,0 +1,155 @@ +//===- StringMatcher.cpp - Generate a matcher for input strings -----------===// +// +// 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 file implements the StringMatcher class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <map> +#include <string> +#include <utility> +#include <vector> + +using namespace llvm; + +/// FindFirstNonCommonLetter - Find the first character in the keys of the +/// string pairs that is not shared across the whole set of strings. All +/// strings are assumed to have the same length. +static unsigned +FindFirstNonCommonLetter(const std::vector<const + StringMatcher::StringPair*> &Matches) { + assert(!Matches.empty()); + for (unsigned i = 0, e = Matches[0]->first.size(); i != e; ++i) { + // Check to see if letter i is the same across the set. + char Letter = Matches[0]->first[i]; + + for (const StringMatcher::StringPair *Match : Matches) + if (Match->first[i] != Letter) + return i; + } + + return Matches[0]->first.size(); +} + +/// EmitStringMatcherForChar - Given a set of strings that are known to be the +/// same length and whose characters leading up to CharNo are the same, emit +/// code to verify that CharNo and later are the same. +/// +/// \return - True if control can leave the emitted code fragment. +bool StringMatcher::EmitStringMatcherForChar( + const std::vector<const StringPair *> &Matches, unsigned CharNo, + unsigned IndentCount, bool IgnoreDuplicates) const { + assert(!Matches.empty() && "Must have at least one string to match!"); + std::string Indent(IndentCount * 2 + 4, ' '); + + // If we have verified that the entire string matches, we're done: output the + // matching code. + if (CharNo == Matches[0]->first.size()) { + if (Matches.size() > 1 && !IgnoreDuplicates) + report_fatal_error("Had duplicate keys to match on"); + + // If the to-execute code has \n's in it, indent each subsequent line. + StringRef Code = Matches[0]->second; + + std::pair<StringRef, StringRef> Split = Code.split('\n'); + OS << Indent << Split.first << "\t // \"" << Matches[0]->first << "\"\n"; + + Code = Split.second; + while (!Code.empty()) { + Split = Code.split('\n'); + OS << Indent << Split.first << "\n"; + Code = Split.second; + } + return false; + } + + // Bucket the matches by the character we are comparing. + std::map<char, std::vector<const StringPair*>> MatchesByLetter; + + for (const StringPair *Match : Matches) + MatchesByLetter[Match->first[CharNo]].push_back(Match); + + // If we have exactly one bucket to match, see how many characters are common + // across the whole set and match all of them at once. + if (MatchesByLetter.size() == 1) { + unsigned FirstNonCommonLetter = FindFirstNonCommonLetter(Matches); + unsigned NumChars = FirstNonCommonLetter-CharNo; + + // Emit code to break out if the prefix doesn't match. + if (NumChars == 1) { + // Do the comparison with if (Str[1] != 'f') + // FIXME: Need to escape general characters. + OS << Indent << "if (" << StrVariableName << "[" << CharNo << "] != '" + << Matches[0]->first[CharNo] << "')\n"; + OS << Indent << " break;\n"; + } else { + // Do the comparison with if memcmp(Str.data()+1, "foo", 3). + // FIXME: Need to escape general strings. + OS << Indent << "if (memcmp(" << StrVariableName << ".data()+" << CharNo + << ", \"" << Matches[0]->first.substr(CharNo, NumChars) << "\", " + << NumChars << ") != 0)\n"; + OS << Indent << " break;\n"; + } + + return EmitStringMatcherForChar(Matches, FirstNonCommonLetter, IndentCount, + IgnoreDuplicates); + } + + // Otherwise, we have multiple possible things, emit a switch on the + // character. + OS << Indent << "switch (" << StrVariableName << "[" << CharNo << "]) {\n"; + OS << Indent << "default: break;\n"; + + for (const auto &LI : MatchesByLetter) { + // TODO: escape hard stuff (like \n) if we ever care about it. + OS << Indent << "case '" << LI.first << "':\t // " << LI.second.size() + << " string"; + if (LI.second.size() != 1) + OS << 's'; + OS << " to match.\n"; + if (EmitStringMatcherForChar(LI.second, CharNo + 1, IndentCount + 1, + IgnoreDuplicates)) + OS << Indent << " break;\n"; + } + + OS << Indent << "}\n"; + return true; +} + +/// Emit - Top level entry point. +/// +void StringMatcher::Emit(unsigned Indent, bool IgnoreDuplicates) const { + // If nothing to match, just fall through. + if (Matches.empty()) return; + + // First level categorization: group strings by length. + std::map<unsigned, std::vector<const StringPair*>> MatchesByLength; + + for (const StringPair &Match : Matches) + MatchesByLength[Match.first.size()].push_back(&Match); + + // Output a switch statement on length and categorize the elements within each + // bin. + OS.indent(Indent*2+2) << "switch (" << StrVariableName << ".size()) {\n"; + OS.indent(Indent*2+2) << "default: break;\n"; + + for (const auto &LI : MatchesByLength) { + OS.indent(Indent * 2 + 2) + << "case " << LI.first << ":\t // " << LI.second.size() << " string" + << (LI.second.size() == 1 ? "" : "s") << " to match.\n"; + if (EmitStringMatcherForChar(LI.second, 0, Indent, IgnoreDuplicates)) + OS.indent(Indent*2+4) << "break;\n"; + } + + OS.indent(Indent*2+2) << "}\n"; +} diff --git a/contrib/llvm-project/llvm/lib/TableGen/TGLexer.cpp b/contrib/llvm-project/llvm/lib/TableGen/TGLexer.cpp new file mode 100644 index 000000000000..f2148b40a1b5 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/TGLexer.cpp @@ -0,0 +1,1044 @@ +//===- TGLexer.cpp - Lexer for TableGen -----------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implement the Lexer for TableGen. +// +//===----------------------------------------------------------------------===// + +#include "TGLexer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Config/config.h" // for strtoull()/strtoll() define +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/TableGen/Error.h" +#include <algorithm> +#include <cctype> +#include <cerrno> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +using namespace llvm; + +namespace { +// A list of supported preprocessing directives with their +// internal token kinds and names. +struct { + tgtok::TokKind Kind; + const char *Word; +} PreprocessorDirs[] = { + { tgtok::Ifdef, "ifdef" }, + { tgtok::Ifndef, "ifndef" }, + { tgtok::Else, "else" }, + { tgtok::Endif, "endif" }, + { tgtok::Define, "define" } +}; +} // end anonymous namespace + +TGLexer::TGLexer(SourceMgr &SM, ArrayRef<std::string> Macros) : SrcMgr(SM) { + CurBuffer = SrcMgr.getMainFileID(); + CurBuf = SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(); + CurPtr = CurBuf.begin(); + TokStart = nullptr; + + // Pretend that we enter the "top-level" include file. + PrepIncludeStack.push_back( + std::make_unique<std::vector<PreprocessorControlDesc>>()); + + // Put all macros defined in the command line into the DefinedMacros set. + for (const std::string &MacroName : Macros) + DefinedMacros.insert(MacroName); +} + +SMLoc TGLexer::getLoc() const { + return SMLoc::getFromPointer(TokStart); +} + +SMRange TGLexer::getLocRange() const { + return {getLoc(), SMLoc::getFromPointer(CurPtr)}; +} + +/// ReturnError - Set the error to the specified string at the specified +/// location. This is defined to always return tgtok::Error. +tgtok::TokKind TGLexer::ReturnError(SMLoc Loc, const Twine &Msg) { + PrintError(Loc, Msg); + return tgtok::Error; +} + +tgtok::TokKind TGLexer::ReturnError(const char *Loc, const Twine &Msg) { + return ReturnError(SMLoc::getFromPointer(Loc), Msg); +} + +bool TGLexer::processEOF() { + SMLoc ParentIncludeLoc = SrcMgr.getParentIncludeLoc(CurBuffer); + if (ParentIncludeLoc != SMLoc()) { + // If prepExitInclude() detects a problem with the preprocessing + // control stack, it will return false. Pretend that we reached + // the final EOF and stop lexing more tokens by returning false + // to LexToken(). + if (!prepExitInclude(false)) + return false; + + CurBuffer = SrcMgr.FindBufferContainingLoc(ParentIncludeLoc); + CurBuf = SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(); + CurPtr = ParentIncludeLoc.getPointer(); + // Make sure TokStart points into the parent file's buffer. + // LexToken() assigns to it before calling getNextChar(), + // so it is pointing into the included file now. + TokStart = CurPtr; + return true; + } + + // Pretend that we exit the "top-level" include file. + // Note that in case of an error (e.g. control stack imbalance) + // the routine will issue a fatal error. + prepExitInclude(true); + return false; +} + +int TGLexer::getNextChar() { + char CurChar = *CurPtr++; + switch (CurChar) { + default: + return (unsigned char)CurChar; + + case 0: { + // A NUL character in the stream is either the end of the current buffer or + // a spurious NUL in the file. Disambiguate that here. + if (CurPtr - 1 == CurBuf.end()) { + --CurPtr; // Arrange for another call to return EOF again. + return EOF; + } + PrintError(getLoc(), + "NUL character is invalid in source; treated as space"); + return ' '; + } + + case '\n': + case '\r': + // Handle the newline character by ignoring it and incrementing the line + // count. However, be careful about 'dos style' files with \n\r in them. + // Only treat a \n\r or \r\n as a single line. + if ((*CurPtr == '\n' || (*CurPtr == '\r')) && + *CurPtr != CurChar) + ++CurPtr; // Eat the two char newline sequence. + return '\n'; + } +} + +int TGLexer::peekNextChar(int Index) const { + return *(CurPtr + Index); +} + +tgtok::TokKind TGLexer::LexToken(bool FileOrLineStart) { + TokStart = CurPtr; + // This always consumes at least one character. + int CurChar = getNextChar(); + + switch (CurChar) { + default: + // Handle letters: [a-zA-Z_] + if (isalpha(CurChar) || CurChar == '_') + return LexIdentifier(); + + // Unknown character, emit an error. + return ReturnError(TokStart, "Unexpected character"); + 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 the 'include "..."' + // construct, so LexToken() is called with default + // false parameter. + if (processEOF()) + return LexToken(); + + // Return EOF denoting the end of lexing. + return tgtok::Eof; + + case ':': return tgtok::colon; + case ';': return tgtok::semi; + case ',': return tgtok::comma; + case '<': return tgtok::less; + case '>': return tgtok::greater; + case ']': return tgtok::r_square; + case '{': return tgtok::l_brace; + case '}': return tgtok::r_brace; + case '(': return tgtok::l_paren; + case ')': return tgtok::r_paren; + case '=': return tgtok::equal; + case '?': return tgtok::question; + case '#': + if (FileOrLineStart) { + tgtok::TokKind Kind = prepIsDirective(); + if (Kind != tgtok::Error) + return lexPreprocessor(Kind); + } + + 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; + + case ' ': + case '\t': + // Ignore whitespace. + return LexToken(FileOrLineStart); + case '\n': + // Ignore whitespace, and identify the new line. + return LexToken(true); + case '/': + // If this is the start of a // comment, skip until the end of the line or + // the end of the buffer. + if (*CurPtr == '/') + SkipBCPLComment(); + else if (*CurPtr == '*') { + if (SkipCComment()) + return tgtok::Error; + } else // Otherwise, this is an error. + return ReturnError(TokStart, "Unexpected character"); + return LexToken(FileOrLineStart); + case '-': case '+': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': { + int NextChar = 0; + if (isdigit(CurChar)) { + // Allow identifiers to start with a number if it is followed by + // an identifier. This can happen with paste operations like + // foo#8i. + int i = 0; + do { + NextChar = peekNextChar(i++); + } while (isdigit(NextChar)); + + if (NextChar == 'x' || NextChar == 'b') { + // If this is [0-9]b[01] or [0-9]x[0-9A-fa-f] this is most + // likely a number. + int NextNextChar = peekNextChar(i); + switch (NextNextChar) { + default: + break; + case '0': case '1': + if (NextChar == 'b') + return LexNumber(); + [[fallthrough]]; + case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + if (NextChar == 'x') + return LexNumber(); + break; + } + } + } + + if (isalpha(NextChar) || NextChar == '_') + return LexIdentifier(); + + return LexNumber(); + } + case '"': return LexString(); + case '$': return LexVarName(); + case '[': return LexBracket(); + case '!': return LexExclaim(); + } +} + +/// LexString - Lex "[^"]*" +tgtok::TokKind TGLexer::LexString() { + const char *StrStart = CurPtr; + + CurStrVal = ""; + + while (*CurPtr != '"') { + // If we hit the end of the buffer, report an error. + if (*CurPtr == 0 && CurPtr == CurBuf.end()) + return ReturnError(StrStart, "End of file in string literal"); + + if (*CurPtr == '\n' || *CurPtr == '\r') + return ReturnError(StrStart, "End of line in string literal"); + + if (*CurPtr != '\\') { + CurStrVal += *CurPtr++; + continue; + } + + ++CurPtr; + + switch (*CurPtr) { + case '\\': case '\'': case '"': + // These turn into their literal character. + CurStrVal += *CurPtr++; + break; + case 't': + CurStrVal += '\t'; + ++CurPtr; + break; + case 'n': + CurStrVal += '\n'; + ++CurPtr; + break; + + case '\n': + case '\r': + return ReturnError(CurPtr, "escaped newlines not supported in tblgen"); + + // If we hit the end of the buffer, report an error. + case '\0': + if (CurPtr == CurBuf.end()) + return ReturnError(StrStart, "End of file in string literal"); + [[fallthrough]]; + default: + return ReturnError(CurPtr, "invalid escape in string literal"); + } + } + + ++CurPtr; + return tgtok::StrVal; +} + +tgtok::TokKind TGLexer::LexVarName() { + if (!isalpha(CurPtr[0]) && CurPtr[0] != '_') + return ReturnError(TokStart, "Invalid variable name"); + + // Otherwise, we're ok, consume the rest of the characters. + const char *VarNameStart = CurPtr++; + + while (isalpha(*CurPtr) || isdigit(*CurPtr) || *CurPtr == '_') + ++CurPtr; + + CurStrVal.assign(VarNameStart, CurPtr); + return tgtok::VarName; +} + +tgtok::TokKind TGLexer::LexIdentifier() { + // The first letter is [a-zA-Z_]. + const char *IdentStart = TokStart; + + // Match the rest of the identifier regex: [0-9a-zA-Z_]* + while (isalpha(*CurPtr) || isdigit(*CurPtr) || *CurPtr == '_') + ++CurPtr; + + // Check to see if this identifier is a reserved keyword. + StringRef Str(IdentStart, CurPtr-IdentStart); + + tgtok::TokKind Kind = StringSwitch<tgtok::TokKind>(Str) + .Case("int", tgtok::Int) + .Case("bit", tgtok::Bit) + .Case("bits", tgtok::Bits) + .Case("string", tgtok::String) + .Case("list", tgtok::List) + .Case("code", tgtok::Code) + .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) + .Case("multiclass", tgtok::MultiClass) + .Case("field", tgtok::Field) + .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); + + // 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; +} + +/// LexInclude - We just read the "include" token. Get the string token that +/// comes next and enter the include. +bool TGLexer::LexInclude() { + // The token after the include must be a string. + tgtok::TokKind Tok = LexToken(); + if (Tok == tgtok::Error) return true; + if (Tok != tgtok::StrVal) { + PrintError(getLoc(), "Expected filename after include"); + return true; + } + + // Get the string. + std::string Filename = CurStrVal; + std::string IncludedFile; + + CurBuffer = SrcMgr.AddIncludeFile(Filename, SMLoc::getFromPointer(CurPtr), + IncludedFile); + if (!CurBuffer) { + PrintError(getLoc(), "Could not find include file '" + Filename + "'"); + return true; + } + + Dependencies.insert(IncludedFile); + // Save the line number and lex buffer of the includer. + CurBuf = SrcMgr.getMemoryBuffer(CurBuffer)->getBuffer(); + CurPtr = CurBuf.begin(); + + PrepIncludeStack.push_back( + std::make_unique<std::vector<PreprocessorControlDesc>>()); + return false; +} + +/// SkipBCPLComment - Skip over the comment by finding the next CR or LF. +/// Or we may end up at the end of the buffer. +void TGLexer::SkipBCPLComment() { + ++CurPtr; // skip the second slash. + auto EOLPos = CurBuf.find_first_of("\r\n", CurPtr - CurBuf.data()); + CurPtr = (EOLPos == StringRef::npos) ? CurBuf.end() : CurBuf.data() + EOLPos; +} + +/// SkipCComment - This skips C-style /**/ comments. The only difference from C +/// is that we allow nesting. +bool TGLexer::SkipCComment() { + ++CurPtr; // skip the star. + unsigned CommentDepth = 1; + + while (true) { + int CurChar = getNextChar(); + switch (CurChar) { + case EOF: + PrintError(TokStart, "Unterminated comment!"); + return true; + case '*': + // End of the comment? + if (CurPtr[0] != '/') break; + + ++CurPtr; // End the */. + if (--CommentDepth == 0) + return false; + break; + case '/': + // Start of a nested comment? + if (CurPtr[0] != '*') break; + ++CurPtr; + ++CommentDepth; + break; + } + } +} + +/// LexNumber - Lex: +/// [-+]?[0-9]+ +/// 0x[0-9a-fA-F]+ +/// 0b[01]+ +tgtok::TokKind TGLexer::LexNumber() { + if (CurPtr[-1] == '0') { + if (CurPtr[0] == 'x') { + ++CurPtr; + const char *NumStart = CurPtr; + while (isxdigit(CurPtr[0])) + ++CurPtr; + + // Requires at least one hex digit. + if (CurPtr == NumStart) + return ReturnError(TokStart, "Invalid hexadecimal number"); + + errno = 0; + CurIntVal = strtoll(NumStart, nullptr, 16); + if (errno == EINVAL) + return ReturnError(TokStart, "Invalid hexadecimal number"); + if (errno == ERANGE) { + errno = 0; + CurIntVal = (int64_t)strtoull(NumStart, nullptr, 16); + if (errno == EINVAL) + return ReturnError(TokStart, "Invalid hexadecimal number"); + if (errno == ERANGE) + return ReturnError(TokStart, "Hexadecimal number out of range"); + } + return tgtok::IntVal; + } else if (CurPtr[0] == 'b') { + ++CurPtr; + const char *NumStart = CurPtr; + while (CurPtr[0] == '0' || CurPtr[0] == '1') + ++CurPtr; + + // Requires at least one binary digit. + if (CurPtr == NumStart) + return ReturnError(CurPtr-2, "Invalid binary number"); + CurIntVal = strtoll(NumStart, nullptr, 2); + return tgtok::BinaryIntVal; + } + } + + // Check for a sign without a digit. + if (!isdigit(CurPtr[0])) { + if (CurPtr[-1] == '-') + return tgtok::minus; + else if (CurPtr[-1] == '+') + return tgtok::plus; + } + + while (isdigit(CurPtr[0])) + ++CurPtr; + CurIntVal = strtoll(TokStart, nullptr, 10); + return tgtok::IntVal; +} + +/// LexBracket - We just read '['. If this is a code block, return it, +/// otherwise return the bracket. Match: '[' and '[{ ( [^}]+ | }[^]] )* }]' +tgtok::TokKind TGLexer::LexBracket() { + if (CurPtr[0] != '{') + return tgtok::l_square; + ++CurPtr; + const char *CodeStart = CurPtr; + while (true) { + int Char = getNextChar(); + if (Char == EOF) break; + + if (Char != '}') continue; + + Char = getNextChar(); + if (Char == EOF) break; + if (Char == ']') { + CurStrVal.assign(CodeStart, CurPtr-2); + return tgtok::CodeFragment; + } + } + + return ReturnError(CodeStart - 2, "Unterminated code block"); +} + +/// LexExclaim - Lex '!' and '![a-zA-Z]+'. +tgtok::TokKind TGLexer::LexExclaim() { + if (!isalpha(*CurPtr)) + return ReturnError(CurPtr - 1, "Invalid \"!operator\""); + + const char *Start = CurPtr++; + while (isalpha(*CurPtr)) + ++CurPtr; + + // Check to see which operator this is. + tgtok::TokKind Kind = + StringSwitch<tgtok::TokKind>(StringRef(Start, CurPtr - Start)) + .Case("eq", tgtok::XEq) + .Case("ne", tgtok::XNe) + .Case("le", tgtok::XLe) + .Case("lt", tgtok::XLt) + .Case("ge", tgtok::XGe) + .Case("gt", tgtok::XGt) + .Case("if", tgtok::XIf) + .Case("cond", tgtok::XCond) + .Case("isa", tgtok::XIsA) + .Case("head", tgtok::XHead) + .Case("tail", tgtok::XTail) + .Case("size", tgtok::XSize) + .Case("con", tgtok::XConcat) + .Case("dag", tgtok::XDag) + .Case("add", tgtok::XADD) + .Case("sub", tgtok::XSUB) + .Case("mul", tgtok::XMUL) + .Case("div", tgtok::XDIV) + .Case("not", tgtok::XNOT) + .Case("logtwo", tgtok::XLOG2) + .Case("and", tgtok::XAND) + .Case("or", tgtok::XOR) + .Case("xor", tgtok::XXOR) + .Case("shl", tgtok::XSHL) + .Case("sra", tgtok::XSRA) + .Case("srl", tgtok::XSRL) + .Case("cast", tgtok::XCast) + .Case("empty", tgtok::XEmpty) + .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("listremove", tgtok::XListRemove) + .Case("strconcat", tgtok::XStrConcat) + .Case("interleave", tgtok::XInterleave) + .Case("substr", tgtok::XSubstr) + .Case("find", tgtok::XFind) + .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated. + .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated. + .Case("exists", tgtok::XExists) + .Default(tgtok::Error); + + return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator"); +} + +bool TGLexer::prepExitInclude(bool IncludeStackMustBeEmpty) { + // Report an error, if preprocessor control stack for the current + // file is not empty. + if (!PrepIncludeStack.back()->empty()) { + prepReportPreprocessorStackError(); + + return false; + } + + // Pop the preprocessing controls from the include stack. + if (PrepIncludeStack.empty()) { + PrintFatalError("Preprocessor include stack is empty"); + } + + PrepIncludeStack.pop_back(); + + if (IncludeStackMustBeEmpty) { + if (!PrepIncludeStack.empty()) + PrintFatalError("Preprocessor include stack is not empty"); + } else { + if (PrepIncludeStack.empty()) + PrintFatalError("Preprocessor include stack is empty"); + } + + return true; +} + +tgtok::TokKind TGLexer::prepIsDirective() const { + for (const auto &PD : PreprocessorDirs) { + int NextChar = *CurPtr; + bool Match = true; + unsigned I = 0; + for (; I < strlen(PD.Word); ++I) { + if (NextChar != PD.Word[I]) { + Match = false; + break; + } + + NextChar = peekNextChar(I + 1); + } + + // Check for whitespace after the directive. If there is no whitespace, + // then we do not recognize it as a preprocessing directive. + if (Match) { + tgtok::TokKind Kind = PD.Kind; + + // New line and EOF may follow only #else/#endif. It will be reported + // as an error for #ifdef/#define after the call to prepLexMacroName(). + if (NextChar == ' ' || NextChar == '\t' || NextChar == EOF || + NextChar == '\n' || + // It looks like TableGen does not support '\r' as the actual + // carriage return, e.g. getNextChar() treats a single '\r' + // as '\n'. So we do the same here. + NextChar == '\r') + return Kind; + + // Allow comments after some directives, e.g.: + // #else// OR #else/**/ + // #endif// OR #endif/**/ + // + // Note that we do allow comments after #ifdef/#define here, e.g. + // #ifdef/**/ AND #ifdef// + // #define/**/ AND #define// + // + // These cases will be reported as incorrect after calling + // prepLexMacroName(). We could have supported C-style comments + // after #ifdef/#define, but this would complicate the code + // for little benefit. + if (NextChar == '/') { + NextChar = peekNextChar(I + 1); + + if (NextChar == '*' || NextChar == '/') + return Kind; + + // Pretend that we do not recognize the directive. + } + } + } + + return tgtok::Error; +} + +bool TGLexer::prepEatPreprocessorDirective(tgtok::TokKind Kind) { + TokStart = CurPtr; + + for (const auto &PD : PreprocessorDirs) + if (PD.Kind == Kind) { + // Advance CurPtr to the end of the preprocessing word. + CurPtr += strlen(PD.Word); + return true; + } + + PrintFatalError("Unsupported preprocessing token in " + "prepEatPreprocessorDirective()"); + return false; +} + +tgtok::TokKind TGLexer::lexPreprocessor( + tgtok::TokKind Kind, bool ReturnNextLiveToken) { + + // We must be looking at a preprocessing directive. Eat it! + if (!prepEatPreprocessorDirective(Kind)) + PrintFatalError("lexPreprocessor() called for unknown " + "preprocessor directive"); + + if (Kind == tgtok::Ifdef || Kind == tgtok::Ifndef) { + StringRef MacroName = prepLexMacroName(); + StringRef IfTokName = Kind == tgtok::Ifdef ? "#ifdef" : "#ifndef"; + if (MacroName.empty()) + return ReturnError(TokStart, "Expected macro name after " + IfTokName); + + bool MacroIsDefined = DefinedMacros.count(MacroName) != 0; + + // Canonicalize ifndef to ifdef equivalent + if (Kind == tgtok::Ifndef) { + MacroIsDefined = !MacroIsDefined; + Kind = tgtok::Ifdef; + } + + // Regardless of whether we are processing tokens or not, + // we put the #ifdef control on stack. + PrepIncludeStack.back()->push_back( + {Kind, MacroIsDefined, SMLoc::getFromPointer(TokStart)}); + + if (!prepSkipDirectiveEnd()) + return ReturnError(CurPtr, "Only comments are supported after " + + IfTokName + " NAME"); + + // If we were not processing tokens before this #ifdef, + // then just return back to the lines skipping code. + if (!ReturnNextLiveToken) + return Kind; + + // If we were processing tokens before this #ifdef, + // and the macro is defined, then just return the next token. + if (MacroIsDefined) + return LexToken(); + + // We were processing tokens before this #ifdef, and the macro + // is not defined, so we have to start skipping the lines. + // If the skipping is successful, it will return the token following + // either #else or #endif corresponding to this #ifdef. + if (prepSkipRegion(ReturnNextLiveToken)) + return LexToken(); + + return tgtok::Error; + } else if (Kind == tgtok::Else) { + // Check if this #else is correct before calling prepSkipDirectiveEnd(), + // which will move CurPtr away from the beginning of #else. + if (PrepIncludeStack.back()->empty()) + return ReturnError(TokStart, "#else without #ifdef or #ifndef"); + + PreprocessorControlDesc IfdefEntry = PrepIncludeStack.back()->back(); + + if (IfdefEntry.Kind != tgtok::Ifdef) { + PrintError(TokStart, "double #else"); + return ReturnError(IfdefEntry.SrcPos, "Previous #else is here"); + } + + // Replace the corresponding #ifdef's control with its negation + // on the control stack. + PrepIncludeStack.back()->pop_back(); + PrepIncludeStack.back()->push_back( + {Kind, !IfdefEntry.IsDefined, SMLoc::getFromPointer(TokStart)}); + + if (!prepSkipDirectiveEnd()) + return ReturnError(CurPtr, "Only comments are supported after #else"); + + // If we were processing tokens before this #else, + // we have to start skipping lines until the matching #endif. + if (ReturnNextLiveToken) { + if (prepSkipRegion(ReturnNextLiveToken)) + return LexToken(); + + return tgtok::Error; + } + + // Return to the lines skipping code. + return Kind; + } else if (Kind == tgtok::Endif) { + // Check if this #endif is correct before calling prepSkipDirectiveEnd(), + // which will move CurPtr away from the beginning of #endif. + if (PrepIncludeStack.back()->empty()) + return ReturnError(TokStart, "#endif without #ifdef"); + + auto &IfdefOrElseEntry = PrepIncludeStack.back()->back(); + + if (IfdefOrElseEntry.Kind != tgtok::Ifdef && + IfdefOrElseEntry.Kind != tgtok::Else) { + PrintFatalError("Invalid preprocessor control on the stack"); + return tgtok::Error; + } + + if (!prepSkipDirectiveEnd()) + return ReturnError(CurPtr, "Only comments are supported after #endif"); + + PrepIncludeStack.back()->pop_back(); + + // If we were processing tokens before this #endif, then + // we should continue it. + if (ReturnNextLiveToken) { + return LexToken(); + } + + // Return to the lines skipping code. + return Kind; + } else if (Kind == tgtok::Define) { + StringRef MacroName = prepLexMacroName(); + if (MacroName.empty()) + return ReturnError(TokStart, "Expected macro name after #define"); + + if (!DefinedMacros.insert(MacroName).second) + PrintWarning(getLoc(), + "Duplicate definition of macro: " + Twine(MacroName)); + + if (!prepSkipDirectiveEnd()) + return ReturnError(CurPtr, + "Only comments are supported after #define NAME"); + + if (!ReturnNextLiveToken) { + PrintFatalError("#define must be ignored during the lines skipping"); + return tgtok::Error; + } + + return LexToken(); + } + + PrintFatalError("Preprocessing directive is not supported"); + return tgtok::Error; +} + +bool TGLexer::prepSkipRegion(bool MustNeverBeFalse) { + if (!MustNeverBeFalse) + PrintFatalError("Invalid recursion."); + + do { + // Skip all symbols to the line end. + prepSkipToLineEnd(); + + // Find the first non-whitespace symbol in the next line(s). + if (!prepSkipLineBegin()) + return false; + + // If the first non-blank/comment symbol on the line is '#', + // it may be a start of preprocessing directive. + // + // If it is not '#' just go to the next line. + if (*CurPtr == '#') + ++CurPtr; + else + continue; + + tgtok::TokKind Kind = prepIsDirective(); + + // If we did not find a preprocessing directive or it is #define, + // then just skip to the next line. We do not have to do anything + // for #define in the line-skipping mode. + if (Kind == tgtok::Error || Kind == tgtok::Define) + continue; + + tgtok::TokKind ProcessedKind = lexPreprocessor(Kind, false); + + // If lexPreprocessor() encountered an error during lexing this + // preprocessor idiom, then return false to the calling lexPreprocessor(). + // This will force tgtok::Error to be returned to the tokens processing. + if (ProcessedKind == tgtok::Error) + return false; + + if (Kind != ProcessedKind) + PrintFatalError("prepIsDirective() and lexPreprocessor() " + "returned different token kinds"); + + // If this preprocessing directive enables tokens processing, + // then return to the lexPreprocessor() and get to the next token. + // We can move from line-skipping mode to processing tokens only + // due to #else or #endif. + if (prepIsProcessingEnabled()) { + if (Kind != tgtok::Else && Kind != tgtok::Endif) { + PrintFatalError("Tokens processing was enabled by an unexpected " + "preprocessing directive"); + return false; + } + + return true; + } + } while (CurPtr != CurBuf.end()); + + // We have reached the end of the file, but never left the lines-skipping + // mode. This means there is no matching #endif. + prepReportPreprocessorStackError(); + return false; +} + +StringRef TGLexer::prepLexMacroName() { + // Skip whitespaces between the preprocessing directive and the macro name. + while (*CurPtr == ' ' || *CurPtr == '\t') + ++CurPtr; + + TokStart = CurPtr; + // Macro names start with [a-zA-Z_]. + if (*CurPtr != '_' && !isalpha(*CurPtr)) + return ""; + + // Match the rest of the identifier regex: [0-9a-zA-Z_]* + while (isalpha(*CurPtr) || isdigit(*CurPtr) || *CurPtr == '_') + ++CurPtr; + + return StringRef(TokStart, CurPtr - TokStart); +} + +bool TGLexer::prepSkipLineBegin() { + while (CurPtr != CurBuf.end()) { + switch (*CurPtr) { + case ' ': + case '\t': + case '\n': + case '\r': + break; + + case '/': { + int NextChar = peekNextChar(1); + if (NextChar == '*') { + // Skip C-style comment. + // Note that we do not care about skipping the C++-style comments. + // If the line contains "//", it may not contain any processable + // preprocessing directive. Just return CurPtr pointing to + // the first '/' in this case. We also do not care about + // incorrect symbols after the first '/' - we are in lines-skipping + // mode, so incorrect code is allowed to some extent. + + // Set TokStart to the beginning of the comment to enable proper + // diagnostic printing in case of error in SkipCComment(). + TokStart = CurPtr; + + // CurPtr must point to '*' before call to SkipCComment(). + ++CurPtr; + if (SkipCComment()) + return false; + } else { + // CurPtr points to the non-whitespace '/'. + return true; + } + + // We must not increment CurPtr after the comment was lexed. + continue; + } + + default: + return true; + } + + ++CurPtr; + } + + // We have reached the end of the file. Return to the lines skipping + // code, and allow it to handle the EOF as needed. + return true; +} + +bool TGLexer::prepSkipDirectiveEnd() { + while (CurPtr != CurBuf.end()) { + switch (*CurPtr) { + case ' ': + case '\t': + break; + + case '\n': + case '\r': + return true; + + case '/': { + int NextChar = peekNextChar(1); + if (NextChar == '/') { + // Skip C++-style comment. + // We may just return true now, but let's skip to the line/buffer end + // to simplify the method specification. + ++CurPtr; + SkipBCPLComment(); + } else if (NextChar == '*') { + // When we are skipping C-style comment at the end of a preprocessing + // directive, we can skip several lines. If any meaningful TD token + // follows the end of the C-style comment on the same line, it will + // be considered as an invalid usage of TD token. + // For example, we want to forbid usages like this one: + // #define MACRO class Class {} + // But with C-style comments we also disallow the following: + // #define MACRO /* This macro is used + // to ... */ class Class {} + // One can argue that this should be allowed, but it does not seem + // to be worth of the complication. Moreover, this matches + // the C preprocessor behavior. + + // Set TokStart to the beginning of the comment to enable proper + // diagnostic printer in case of error in SkipCComment(). + TokStart = CurPtr; + ++CurPtr; + if (SkipCComment()) + return false; + } else { + TokStart = CurPtr; + PrintError(CurPtr, "Unexpected character"); + return false; + } + + // We must not increment CurPtr after the comment was lexed. + continue; + } + + default: + // Do not allow any non-whitespaces after the directive. + TokStart = CurPtr; + return false; + } + + ++CurPtr; + } + + return true; +} + +void TGLexer::prepSkipToLineEnd() { + while (*CurPtr != '\n' && *CurPtr != '\r' && CurPtr != CurBuf.end()) + ++CurPtr; +} + +bool TGLexer::prepIsProcessingEnabled() { + for (const PreprocessorControlDesc &I : + llvm::reverse(*PrepIncludeStack.back())) + if (!I.IsDefined) + return false; + + return true; +} + +void TGLexer::prepReportPreprocessorStackError() { + if (PrepIncludeStack.back()->empty()) + PrintFatalError("prepReportPreprocessorStackError() called with " + "empty control stack"); + + auto &PrepControl = PrepIncludeStack.back()->back(); + PrintError(CurBuf.end(), "Reached EOF without matching #endif"); + PrintError(PrepControl.SrcPos, "The latest preprocessor control is here"); + + TokStart = CurPtr; +} diff --git a/contrib/llvm-project/llvm/lib/TableGen/TGLexer.h b/contrib/llvm-project/llvm/lib/TableGen/TGLexer.h new file mode 100644 index 000000000000..284f1bade9de --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/TGLexer.h @@ -0,0 +1,383 @@ +//===- TGLexer.h - Lexer for TableGen Files ---------------------*- 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 class represents the Lexer for tablegen files. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TABLEGEN_TGLEXER_H +#define LLVM_LIB_TABLEGEN_TGLEXER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/SMLoc.h" +#include <cassert> +#include <memory> +#include <set> +#include <string> +#include <vector> + +namespace llvm { +template <typename T> class ArrayRef; +class SourceMgr; +class Twine; + +namespace tgtok { + enum TokKind { + // Markers + Eof, Error, + + // Tokens with no info. + minus, plus, // - + + l_square, r_square, // [ ] + l_brace, r_brace, // { } + l_paren, r_paren, // ( ) + less, greater, // < > + colon, semi, // : ; + comma, dot, // , . + equal, question, // = ? + paste, // # + dotdotdot, // ... + + // 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, + + // Bang operators. + XConcat, XADD, XSUB, XMUL, XDIV, XNOT, XLOG2, XAND, XOR, XXOR, XSRA, XSRL, + XSHL, XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XFind, + XCast, XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf, + XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp, + XExists, XListRemove, + + // Boolean literals. + TrueVal, FalseVal, + + // Integer value. + IntVal, + + // Binary constant. Note that these are sized according to the number of + // bits given. + BinaryIntVal, + + // String valued tokens. + Id, StrVal, VarName, CodeFragment, + + // Preprocessing tokens for internal usage by the lexer. + // They are never returned as a result of Lex(). + Ifdef, Ifndef, Else, Endif, Define + }; +} + +/// TGLexer - TableGen Lexer class. +class TGLexer { + SourceMgr &SrcMgr; + + const char *CurPtr = nullptr; + StringRef CurBuf; + + // 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. + + /// CurBuffer - This is the current buffer index we're lexing from as managed + /// by the SourceMgr object. + unsigned CurBuffer = 0; + +public: + typedef std::set<std::string> DependenciesSetTy; + +private: + /// Dependencies - This is the list of all included files. + DependenciesSetTy Dependencies; + +public: + TGLexer(SourceMgr &SrcMgr, ArrayRef<std::string> Macros); + + tgtok::TokKind Lex() { + return CurCode = LexToken(CurPtr == CurBuf.begin()); + } + + const DependenciesSetTy &getDependencies() const { + return Dependencies; + } + + tgtok::TokKind getCode() const { return CurCode; } + + const std::string &getCurStrVal() const { + assert((CurCode == tgtok::Id || CurCode == tgtok::StrVal || + CurCode == tgtok::VarName || CurCode == tgtok::CodeFragment) && + "This token doesn't have a string value"); + return CurStrVal; + } + int64_t getCurIntVal() const { + assert(CurCode == tgtok::IntVal && "This token isn't an integer"); + return CurIntVal; + } + std::pair<int64_t, unsigned> getCurBinaryIntVal() const { + assert(CurCode == tgtok::BinaryIntVal && + "This token isn't a binary integer"); + return std::make_pair(CurIntVal, (CurPtr - TokStart)-2); + } + + SMLoc getLoc() const; + SMRange getLocRange() const; + +private: + /// LexToken - Read the next token and return its code. + tgtok::TokKind LexToken(bool FileOrLineStart = false); + + tgtok::TokKind ReturnError(SMLoc Loc, const Twine &Msg); + tgtok::TokKind ReturnError(const char *Loc, const Twine &Msg); + + int getNextChar(); + int peekNextChar(int Index) const; + void SkipBCPLComment(); + bool SkipCComment(); + tgtok::TokKind LexIdentifier(); + bool LexInclude(); + tgtok::TokKind LexString(); + tgtok::TokKind LexVarName(); + tgtok::TokKind LexNumber(); + tgtok::TokKind LexBracket(); + tgtok::TokKind LexExclaim(); + + // Process EOF encountered in LexToken(). + // If EOF is met in an include file, then the method will update + // CurPtr, CurBuf and preprocessing include stack, and return true. + // If EOF is met in the top-level file, then the method will + // update and check the preprocessing include stack, and return false. + bool processEOF(); + + // *** Structures and methods for preprocessing support *** + + // A set of macro names that are defined either via command line or + // by using: + // #define NAME + StringSet<> DefinedMacros; + + // Each of #ifdef and #else directives has a descriptor associated + // with it. + // + // An ordered list of preprocessing controls defined by #ifdef/#else + // directives that are in effect currently is called preprocessing + // control stack. It is represented as a vector of PreprocessorControlDesc's. + // + // The control stack is updated according to the following rules: + // + // For each #ifdef we add an element to the control stack. + // For each #else we replace the top element with a descriptor + // with an inverted IsDefined value. + // For each #endif we pop the top element from the control stack. + // + // When CurPtr reaches the current buffer's end, the control stack + // must be empty, i.e. #ifdef and the corresponding #endif + // must be located in the same file. + struct PreprocessorControlDesc { + // Either tgtok::Ifdef or tgtok::Else. + tgtok::TokKind Kind; + + // True, if the condition for this directive is true, false - otherwise. + // Examples: + // #ifdef NAME : true, if NAME is defined, false - otherwise. + // ... + // #else : false, if NAME is defined, true - otherwise. + bool IsDefined; + + // Pointer into CurBuf to the beginning of the preprocessing directive + // word, e.g.: + // #ifdef NAME + // ^ - SrcPos + SMLoc SrcPos; + }; + + // We want to disallow code like this: + // file1.td: + // #define NAME + // #ifdef NAME + // include "file2.td" + // EOF + // file2.td: + // #endif + // EOF + // + // To do this, we clear the preprocessing control stack on entry + // to each of the included file. PrepIncludeStack is used to store + // preprocessing control stacks for the current file and all its + // parent files. The back() element is the preprocessing control + // stack for the current file. + std::vector<std::unique_ptr<std::vector<PreprocessorControlDesc>>> + PrepIncludeStack; + + // Validate that the current preprocessing control stack is empty, + // since we are about to exit a file, and pop the include stack. + // + // If IncludeStackMustBeEmpty is true, the include stack must be empty + // after the popping, otherwise, the include stack must not be empty + // after the popping. Basically, the include stack must be empty + // only if we exit the "top-level" file (i.e. finish lexing). + // + // The method returns false, if the current preprocessing control stack + // is not empty (e.g. there is an unterminated #ifdef/#else), + // true - otherwise. + bool prepExitInclude(bool IncludeStackMustBeEmpty); + + // Look ahead for a preprocessing directive starting from CurPtr. The caller + // must only call this method, if *(CurPtr - 1) is '#'. If the method matches + // a preprocessing directive word followed by a whitespace, then it returns + // one of the internal token kinds, i.e. Ifdef, Else, Endif, Define. + // + // CurPtr is not adjusted by this method. + tgtok::TokKind prepIsDirective() const; + + // Given a preprocessing token kind, adjusts CurPtr to the end + // of the preprocessing directive word. Returns true, unless + // an unsupported token kind is passed in. + // + // We use look-ahead prepIsDirective() and prepEatPreprocessorDirective() + // to avoid adjusting CurPtr before we are sure that '#' is followed + // by a preprocessing directive. If it is not, then we fall back to + // tgtok::paste interpretation of '#'. + bool prepEatPreprocessorDirective(tgtok::TokKind Kind); + + // The main "exit" point from the token parsing to preprocessor. + // + // The method is called for CurPtr, when prepIsDirective() returns + // true. The first parameter matches the result of prepIsDirective(), + // denoting the actual preprocessor directive to be processed. + // + // If the preprocessing directive disables the tokens processing, e.g.: + // #ifdef NAME // NAME is undefined + // then lexPreprocessor() enters the lines-skipping mode. + // In this mode, it does not parse any tokens, because the code under + // the #ifdef may not even be a correct tablegen code. The preprocessor + // looks for lines containing other preprocessing directives, which + // may be prepended with whitespaces and C-style comments. If the line + // does not contain a preprocessing directive, it is skipped completely. + // Otherwise, the preprocessing directive is processed by recursively + // calling lexPreprocessor(). The processing of the encountered + // preprocessing directives includes updating preprocessing control stack + // and adding new macros into DefinedMacros set. + // + // The second parameter controls whether lexPreprocessor() is called from + // LexToken() (true) or recursively from lexPreprocessor() (false). + // + // If ReturnNextLiveToken is true, the method returns the next + // LEX token following the current directive or following the end + // of the disabled preprocessing region corresponding to this directive. + // If ReturnNextLiveToken is false, the method returns the first parameter, + // unless there were errors encountered in the disabled preprocessing + // region - in this case, it returns tgtok::Error. + tgtok::TokKind lexPreprocessor(tgtok::TokKind Kind, + bool ReturnNextLiveToken = true); + + // Worker method for lexPreprocessor() to skip lines after some + // preprocessing directive up to the buffer end or to the directive + // that re-enables token processing. The method returns true + // upon processing the next directive that re-enables tokens + // processing. False is returned if an error was encountered. + // + // Note that prepSkipRegion() calls lexPreprocessor() to process + // encountered preprocessing directives. In this case, the second + // parameter to lexPreprocessor() is set to false. Being passed + // false ReturnNextLiveToken, lexPreprocessor() must never call + // prepSkipRegion(). We assert this by passing ReturnNextLiveToken + // to prepSkipRegion() and checking that it is never set to false. + bool prepSkipRegion(bool MustNeverBeFalse); + + // Lex name of the macro after either #ifdef or #define. We could have used + // LexIdentifier(), but it has special handling of "include" word, which + // could result in awkward diagnostic errors. Consider: + // ---- + // #ifdef include + // class ... + // ---- + // LexIdentifier() will engage LexInclude(), which will complain about + // missing file with name "class". Instead, prepLexMacroName() will treat + // "include" as a normal macro name. + // + // On entry, CurPtr points to the end of a preprocessing directive word. + // The method allows for whitespaces between the preprocessing directive + // and the macro name. The allowed whitespaces are ' ' and '\t'. + // + // If the first non-whitespace symbol after the preprocessing directive + // is a valid start symbol for an identifier (i.e. [a-zA-Z_]), then + // the method updates TokStart to the position of the first non-whitespace + // symbol, sets CurPtr to the position of the macro name's last symbol, + // and returns a string reference to the macro name. Otherwise, + // TokStart is set to the first non-whitespace symbol after the preprocessing + // directive, and the method returns an empty string reference. + // + // In all cases, TokStart may be used to point to the word following + // the preprocessing directive. + StringRef prepLexMacroName(); + + // Skip any whitespaces starting from CurPtr. The method is used + // only in the lines-skipping mode to find the first non-whitespace + // symbol after or at CurPtr. Allowed whitespaces are ' ', '\t', '\n' + // and '\r'. The method skips C-style comments as well, because + // it is used to find the beginning of the preprocessing directive. + // If we do not handle C-style comments the following code would + // result in incorrect detection of a preprocessing directive: + // /* + // #ifdef NAME + // */ + // As long as we skip C-style comments, the following code is correctly + // recognized as a preprocessing directive: + // /* first line comment + // second line comment */ #ifdef NAME + // + // The method returns true upon reaching the first non-whitespace symbol + // or EOF, CurPtr is set to point to this symbol. The method returns false, + // if an error occurred during skipping of a C-style comment. + bool prepSkipLineBegin(); + + // Skip any whitespaces or comments after a preprocessing directive. + // The method returns true upon reaching either end of the line + // or end of the file. If there is a multiline C-style comment + // after the preprocessing directive, the method skips + // the comment, so the final CurPtr may point to one of the next lines. + // The method returns false, if an error occurred during skipping + // C- or C++-style comment, or a non-whitespace symbol appears + // after the preprocessing directive. + // + // The method maybe called both during lines-skipping and tokens + // processing. It actually verifies that only whitespaces or/and + // comments follow a preprocessing directive. + // + // After the execution of this mehod, CurPtr points either to new line + // symbol, buffer end or non-whitespace symbol following the preprocesing + // directive. + bool prepSkipDirectiveEnd(); + + // Skip all symbols to the end of the line/file. + // The method adjusts CurPtr, so that it points to either new line + // symbol in the current line or the buffer end. + void prepSkipToLineEnd(); + + // Return true, if the current preprocessor control stack is such that + // we should allow lexer to process the next token, false - otherwise. + // + // In particular, the method returns true, if all the #ifdef/#else + // controls on the stack have their IsDefined member set to true. + bool prepIsProcessingEnabled(); + + // Report an error, if we reach EOF with non-empty preprocessing control + // stack. This means there is no matching #endif for the previous + // #ifdef/#else. + void prepReportPreprocessorStackError(); +}; + +} // end namespace llvm + +#endif diff --git a/contrib/llvm-project/llvm/lib/TableGen/TGParser.cpp b/contrib/llvm-project/llvm/lib/TableGen/TGParser.cpp new file mode 100644 index 000000000000..7fc46a8b4a87 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/TGParser.cpp @@ -0,0 +1,3931 @@ +//===- TGParser.cpp - Parser for TableGen Files ---------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implement the Parser for TableGen. +// +//===----------------------------------------------------------------------===// + +#include "TGParser.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <limits> + +using namespace llvm; + +//===----------------------------------------------------------------------===// +// Support Code for the Semantic Actions. +//===----------------------------------------------------------------------===// + +namespace llvm { + +struct SubClassReference { + SMRange RefRange; + Record *Rec; + SmallVector<Init*, 4> TemplateArgs; + + SubClassReference() : Rec(nullptr) {} + + bool isInvalid() const { return Rec == nullptr; } +}; + +struct SubMultiClassReference { + SMRange RefRange; + MultiClass *MC; + SmallVector<Init*, 4> TemplateArgs; + + SubMultiClassReference() : MC(nullptr) {} + + bool isInvalid() const { return MC == nullptr; } + void dump() const; +}; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void SubMultiClassReference::dump() const { + errs() << "Multiclass:\n"; + + MC->dump(); + + errs() << "Template args:\n"; + for (Init *TA : TemplateArgs) + TA->dump(); +} +#endif + +} // end namespace llvm + +static bool checkBitsConcrete(Record &R, const RecordVal &RV) { + BitsInit *BV = cast<BitsInit>(RV.getValue()); + for (unsigned i = 0, e = BV->getNumBits(); i != e; ++i) { + Init *Bit = BV->getBit(i); + bool IsReference = false; + if (auto VBI = dyn_cast<VarBitInit>(Bit)) { + if (auto VI = dyn_cast<VarInit>(VBI->getBitVar())) { + if (R.getValue(VI->getName())) + IsReference = true; + } + } else if (isa<VarInit>(Bit)) { + IsReference = true; + } + if (!(IsReference || Bit->isConcrete())) + return false; + } + return true; +} + +static void checkConcrete(Record &R) { + for (const RecordVal &RV : R.getValues()) { + // HACK: Disable this check for variables declared with 'field'. This is + // 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.isNonconcreteOK()) + continue; + + if (Init *V = RV.getValue()) { + bool Ok = isa<BitsInit>(V) ? checkBitsConcrete(R, RV) : V->isConcrete(); + if (!Ok) { + PrintError(R.getLoc(), + Twine("Initializer of '") + RV.getNameInitAsString() + + "' in '" + R.getNameInitAsString() + + "' could not be fully resolved: " + + RV.getValue()->getAsString()); + } + } + } +} + +/// Return an Init with a qualifier prefix referring +/// to CurRec's name. +static Init *QualifyName(Record &CurRec, MultiClass *CurMultiClass, Init *Name, + StringRef Scoper) { + RecordKeeper &RK = CurRec.getRecords(); + Init *NewName = BinOpInit::getStrConcat(CurRec.getNameInit(), + StringInit::get(RK, Scoper)); + NewName = BinOpInit::getStrConcat(NewName, Name); + if (CurMultiClass && Scoper != "::") { + Init *Prefix = BinOpInit::getStrConcat(CurMultiClass->Rec.getNameInit(), + StringInit::get(RK, "::")); + NewName = BinOpInit::getStrConcat(Prefix, NewName); + } + + if (BinOpInit *BinOp = dyn_cast<BinOpInit>(NewName)) + NewName = BinOp->Fold(&CurRec); + return NewName; +} + +/// Return the qualified version of the implicit 'NAME' template argument. +static Init *QualifiedNameOfImplicitName(Record &Rec, + MultiClass *MC = nullptr) { + return QualifyName(Rec, MC, StringInit::get(Rec.getRecords(), "NAME"), + MC ? "::" : ":"); +} + +static Init *QualifiedNameOfImplicitName(MultiClass *MC) { + return QualifiedNameOfImplicitName(MC->Rec, MC); +} + +bool TGParser::AddValue(Record *CurRec, SMLoc Loc, const RecordVal &RV) { + if (!CurRec) + CurRec = &CurMultiClass->Rec; + + if (RecordVal *ERV = CurRec->getValue(RV.getNameInit())) { + // The value already exists in the class, treat this as a set. + if (ERV->setValue(RV.getValue())) + return Error(Loc, "New definition of '" + RV.getName() + "' of type '" + + RV.getType()->getAsString() + "' is incompatible with " + + "previous definition of type '" + + ERV->getType()->getAsString() + "'"); + } else { + CurRec->addValue(RV); + } + return false; +} + +/// SetValue - +/// Return true on error, false on success. +bool TGParser::SetValue(Record *CurRec, SMLoc Loc, Init *ValName, + ArrayRef<unsigned> BitList, Init *V, + bool AllowSelfAssignment, bool OverrideDefLoc) { + if (!V) return false; + + if (!CurRec) CurRec = &CurMultiClass->Rec; + + RecordVal *RV = CurRec->getValue(ValName); + if (!RV) + return Error(Loc, "Value '" + ValName->getAsUnquotedString() + + "' unknown!"); + + // Do not allow assignments like 'X = X'. This will just cause infinite loops + // in the resolution machinery. + if (BitList.empty()) + if (VarInit *VI = dyn_cast<VarInit>(V)) + if (VI->getNameInit() == ValName && !AllowSelfAssignment) + return Error(Loc, "Recursion / self-assignment forbidden"); + + // If we are assigning to a subset of the bits in the value... then we must be + // assigning to a field of BitsRecTy, which must have a BitsInit + // initializer. + // + if (!BitList.empty()) { + BitsInit *CurVal = dyn_cast<BitsInit>(RV->getValue()); + if (!CurVal) + return Error(Loc, "Value '" + ValName->getAsUnquotedString() + + "' is not a bits type"); + + // Convert the incoming value to a bits type of the appropriate size... + Init *BI = V->getCastTo(BitsRecTy::get(Records, BitList.size())); + if (!BI) + return Error(Loc, "Initializer is not compatible with bit range"); + + SmallVector<Init *, 16> NewBits(CurVal->getNumBits()); + + // Loop over bits, assigning values as appropriate. + for (unsigned i = 0, e = BitList.size(); i != e; ++i) { + unsigned Bit = BitList[i]; + if (NewBits[Bit]) + return Error(Loc, "Cannot set bit #" + Twine(Bit) + " of value '" + + ValName->getAsUnquotedString() + "' more than once"); + NewBits[Bit] = BI->getBit(i); + } + + for (unsigned i = 0, e = CurVal->getNumBits(); i != e; ++i) + if (!NewBits[i]) + NewBits[i] = CurVal->getBit(i); + + V = BitsInit::get(Records, NewBits); + } + + if (OverrideDefLoc ? RV->setValue(V, Loc) : RV->setValue(V)) { + 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, "Field '" + ValName->getAsUnquotedString() + + "' of type '" + RV->getType()->getAsString() + + "' is incompatible with value '" + + V->getAsString() + InitType + "'"); + } + return false; +} + +/// AddSubClass - Add SubClass as a subclass to CurRec, resolving its template +/// args as SubClass's template arguments. +bool TGParser::AddSubClass(Record *CurRec, SubClassReference &SubClass) { + Record *SC = SubClass.Rec; + MapResolver R(CurRec); + + // Loop over all the subclass record's fields. Add template arguments + // to the resolver map. Add regular fields to the new record. + for (const RecordVal &Field : SC->getValues()) { + if (Field.isTemplateArg()) { + R.set(Field.getNameInit(), Field.getValue()); + } else { + if (AddValue(CurRec, SubClass.RefRange.Start, Field)) + return true; + } + } + + ArrayRef<Init *> TArgs = SC->getTemplateArgs(); + assert(SubClass.TemplateArgs.size() <= TArgs.size() && + "Too many template arguments allowed"); + + // Loop over the template argument names. If a value was specified, + // reset the map value. If not and there was no default, complain. + for (unsigned I = 0, E = TArgs.size(); I != E; ++I) { + if (I < SubClass.TemplateArgs.size()) + R.set(TArgs[I], SubClass.TemplateArgs[I]); + else if (!R.isComplete(TArgs[I])) + return Error(SubClass.RefRange.Start, + "Value not specified for template argument '" + + TArgs[I]->getAsUnquotedString() + "' (#" + Twine(I) + + ") of parent class '" + SC->getNameInitAsString() + "'"); + } + + // Copy the subclass record's assertions to the new record. + CurRec->appendAssertions(SC); + + Init *Name; + if (CurRec->isClass()) + Name = VarInit::get(QualifiedNameOfImplicitName(*CurRec), + StringRecTy::get(Records)); + else + Name = CurRec->getNameInit(); + R.set(QualifiedNameOfImplicitName(*SC), Name); + + CurRec->resolveReferences(R); + + // Since everything went well, we can now set the "superclass" list for the + // current record. + ArrayRef<std::pair<Record *, SMRange>> SCs = SC->getSuperClasses(); + for (const auto &SCPair : SCs) { + if (CurRec->isSubClassOf(SCPair.first)) + return Error(SubClass.RefRange.Start, + "Already subclass of '" + SCPair.first->getName() + "'!\n"); + CurRec->addSuperClass(SCPair.first, SCPair.second); + } + + if (CurRec->isSubClassOf(SC)) + return Error(SubClass.RefRange.Start, + "Already subclass of '" + SC->getName() + "'!\n"); + CurRec->addSuperClass(SC, SubClass.RefRange); + return false; +} + +bool TGParser::AddSubClass(RecordsEntry &Entry, SubClassReference &SubClass) { + if (Entry.Rec) + return AddSubClass(Entry.Rec.get(), SubClass); + + if (Entry.Assertion) + return false; + + for (auto &E : Entry.Loop->Entries) { + if (AddSubClass(E, SubClass)) + return true; + } + + return false; +} + +/// AddSubMultiClass - Add SubMultiClass as a subclass to +/// CurMC, resolving its template args as SubMultiClass's +/// template arguments. +bool TGParser::AddSubMultiClass(MultiClass *CurMC, + SubMultiClassReference &SubMultiClass) { + MultiClass *SMC = SubMultiClass.MC; + + ArrayRef<Init *> SMCTArgs = SMC->Rec.getTemplateArgs(); + if (SMCTArgs.size() < SubMultiClass.TemplateArgs.size()) + return Error(SubMultiClass.RefRange.Start, + "More template args specified than expected"); + + // Prepare the mapping of template argument name to value, filling in default + // values if necessary. + SubstStack TemplateArgs; + for (unsigned i = 0, e = SMCTArgs.size(); i != e; ++i) { + if (i < SubMultiClass.TemplateArgs.size()) { + TemplateArgs.emplace_back(SMCTArgs[i], SubMultiClass.TemplateArgs[i]); + } else { + Init *Default = SMC->Rec.getValue(SMCTArgs[i])->getValue(); + if (!Default->isComplete()) { + return Error(SubMultiClass.RefRange.Start, + "value not specified for template argument #" + Twine(i) + + " (" + SMCTArgs[i]->getAsUnquotedString() + + ") of multiclass '" + SMC->Rec.getNameInitAsString() + + "'"); + } + TemplateArgs.emplace_back(SMCTArgs[i], Default); + } + } + + TemplateArgs.emplace_back(QualifiedNameOfImplicitName(SMC), + VarInit::get(QualifiedNameOfImplicitName(CurMC), + StringRecTy::get(Records))); + + // Add all of the defs in the subclass into the current multiclass. + return resolve(SMC->Entries, TemplateArgs, false, &CurMC->Entries); +} + +/// Add a record, foreach loop, or assertion to the current context. +bool TGParser::addEntry(RecordsEntry E) { + assert((!!E.Rec + !!E.Loop + !!E.Assertion) == 1 && + "RecordsEntry has invalid number of items"); + + // If we are parsing a loop, add it to the loop's entries. + if (!Loops.empty()) { + Loops.back()->Entries.push_back(std::move(E)); + return false; + } + + // If it is a loop, then resolve and perform the loop. + if (E.Loop) { + SubstStack Stack; + return resolve(*E.Loop, Stack, CurMultiClass == nullptr, + CurMultiClass ? &CurMultiClass->Entries : nullptr); + } + + // If we are parsing a multiclass, add it to the multiclass's entries. + if (CurMultiClass) { + CurMultiClass->Entries.push_back(std::move(E)); + return false; + } + + // If it is an assertion, then it's a top-level one, so check it. + if (E.Assertion) { + CheckAssert(E.Assertion->Loc, E.Assertion->Condition, E.Assertion->Message); + return false; + } + + // It must be a record, so finish it off. + return addDefOne(std::move(E.Rec)); +} + +/// Resolve the entries in \p Loop, going over inner loops recursively +/// and making the given subsitutions of (name, value) pairs. +/// +/// The resulting records are stored in \p Dest if non-null. Otherwise, they +/// are added to the global record keeper. +bool TGParser::resolve(const ForeachLoop &Loop, SubstStack &Substs, + bool Final, std::vector<RecordsEntry> *Dest, + SMLoc *Loc) { + MapResolver R; + for (const auto &S : Substs) + R.set(S.first, S.second); + Init *List = Loop.ListValue->resolveReferences(R); + auto LI = dyn_cast<ListInit>(List); + if (!LI) { + if (!Final) { + Dest->emplace_back(std::make_unique<ForeachLoop>(Loop.Loc, Loop.IterVar, + List)); + return resolve(Loop.Entries, Substs, Final, &Dest->back().Loop->Entries, + Loc); + } + + PrintError(Loop.Loc, Twine("attempting to loop over '") + + List->getAsString() + "', expected a list"); + return true; + } + + bool Error = false; + for (auto *Elt : *LI) { + if (Loop.IterVar) + Substs.emplace_back(Loop.IterVar->getNameInit(), Elt); + Error = resolve(Loop.Entries, Substs, Final, Dest); + if (Loop.IterVar) + Substs.pop_back(); + if (Error) + break; + } + return Error; +} + +/// Resolve the entries in \p Source, going over loops recursively and +/// making the given substitutions of (name, value) pairs. +/// +/// The resulting records are stored in \p Dest if non-null. Otherwise, they +/// are added to the global record keeper. +bool TGParser::resolve(const std::vector<RecordsEntry> &Source, + SubstStack &Substs, bool Final, + std::vector<RecordsEntry> *Dest, SMLoc *Loc) { + bool Error = false; + for (auto &E : Source) { + if (E.Loop) { + Error = resolve(*E.Loop, Substs, Final, Dest); + + } else if (E.Assertion) { + MapResolver R; + for (const auto &S : Substs) + R.set(S.first, S.second); + Init *Condition = E.Assertion->Condition->resolveReferences(R); + Init *Message = E.Assertion->Message->resolveReferences(R); + + if (Dest) + Dest->push_back(std::make_unique<Record::AssertionInfo>( + E.Assertion->Loc, Condition, Message)); + else + CheckAssert(E.Assertion->Loc, Condition, Message); + + } else { + auto Rec = std::make_unique<Record>(*E.Rec); + if (Loc) + Rec->appendLoc(*Loc); + + MapResolver R(Rec.get()); + for (const auto &S : Substs) + R.set(S.first, S.second); + Rec->resolveReferences(R); + + if (Dest) + Dest->push_back(std::move(Rec)); + else + Error = addDefOne(std::move(Rec)); + } + if (Error) + break; + } + return Error; +} + +/// Resolve the record fully and add it to the record keeper. +bool TGParser::addDefOne(std::unique_ptr<Record> Rec) { + Init *NewName = nullptr; + if (Record *Prev = Records.getDef(Rec->getNameInitAsString())) { + if (!Rec->isAnonymous()) { + PrintError(Rec->getLoc(), + "def already exists: " + Rec->getNameInitAsString()); + PrintNote(Prev->getLoc(), "location of previous definition"); + return true; + } + NewName = Records.getNewAnonymousName(); + } + + Rec->resolveReferences(NewName); + checkConcrete(*Rec); + + if (!isa<StringInit>(Rec->getNameInit())) { + PrintError(Rec->getLoc(), Twine("record name '") + + Rec->getNameInit()->getAsString() + + "' could not be fully resolved"); + return true; + } + + // Check the assertions. + Rec->checkRecordAssertions(); + + // If ObjectBody has template arguments, it's an error. + assert(Rec->getTemplateArgs().empty() && "How'd this get template args?"); + + for (DefsetRecord *Defset : Defsets) { + DefInit *I = Rec->getDefInit(); + if (!I->getType()->typeIsA(Defset->EltTy)) { + PrintError(Rec->getLoc(), Twine("adding record of incompatible type '") + + I->getType()->getAsString() + + "' to defset"); + PrintNote(Defset->Loc, "location of defset declaration"); + return true; + } + Defset->Elements.push_back(I); + } + + Records.addDef(std::move(Rec)); + return false; +} + +//===----------------------------------------------------------------------===// +// Parser Code +//===----------------------------------------------------------------------===// + +/// isObjectStart - Return true if this is a valid first token for a statement. +static bool isObjectStart(tgtok::TokKind K) { + 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) { + if (Lex.getCode() == K) { + Lex.Lex(); + return true; + } + return false; +} + +/// ParseObjectName - If a valid object name is specified, return it. If no +/// name is specified, return the unset initializer. Return nullptr on parse +/// error. +/// ObjectName ::= Value [ '#' Value ]* +/// ObjectName ::= /*empty*/ +/// +Init *TGParser::ParseObjectName(MultiClass *CurMultiClass) { + switch (Lex.getCode()) { + case tgtok::colon: + case tgtok::semi: + case tgtok::l_brace: + // These are all of the tokens that can begin an object body. + // Some of these can also begin values but we disallow those cases + // because they are unlikely to be useful. + return UnsetInit::get(Records); + default: + break; + } + + Record *CurRec = nullptr; + if (CurMultiClass) + CurRec = &CurMultiClass->Rec; + + Init *Name = ParseValue(CurRec, StringRecTy::get(Records), ParseNameMode); + if (!Name) + return nullptr; + + if (CurMultiClass) { + Init *NameStr = QualifiedNameOfImplicitName(CurMultiClass); + HasReferenceResolver R(NameStr); + Name->resolveReferences(R); + if (!R.found()) + Name = BinOpInit::getStrConcat( + VarInit::get(NameStr, StringRecTy::get(Records)), Name); + } + + return Name; +} + +/// ParseClassID - Parse and resolve a reference to a class name. This returns +/// null on error. +/// +/// ClassID ::= ID +/// +Record *TGParser::ParseClassID() { + if (Lex.getCode() != tgtok::Id) { + TokError("expected name for ClassID"); + return nullptr; + } + + Record *Result = Records.getClass(Lex.getCurStrVal()); + if (!Result) { + std::string Msg("Couldn't find class '" + Lex.getCurStrVal() + "'"); + if (MultiClasses[Lex.getCurStrVal()].get()) + TokError(Msg + ". Use 'defm' if you meant to use multiclass '" + + Lex.getCurStrVal() + "'"); + else + TokError(Msg); + } else if (TrackReferenceLocs) { + Result->appendReferenceLoc(Lex.getLocRange()); + } + + Lex.Lex(); + return Result; +} + +/// ParseMultiClassID - Parse and resolve a reference to a multiclass name. +/// This returns null on error. +/// +/// MultiClassID ::= ID +/// +MultiClass *TGParser::ParseMultiClassID() { + if (Lex.getCode() != tgtok::Id) { + TokError("expected name for MultiClassID"); + return nullptr; + } + + MultiClass *Result = MultiClasses[Lex.getCurStrVal()].get(); + if (!Result) + TokError("Couldn't find multiclass '" + Lex.getCurStrVal() + "'"); + + Lex.Lex(); + return Result; +} + +/// ParseSubClassReference - Parse a reference to a subclass or a +/// multiclass. This returns a SubClassRefTy with a null Record* on error. +/// +/// SubClassRef ::= ClassID +/// SubClassRef ::= ClassID '<' ValueList '>' +/// +SubClassReference TGParser:: +ParseSubClassReference(Record *CurRec, bool isDefm) { + SubClassReference Result; + Result.RefRange.Start = Lex.getLoc(); + + if (isDefm) { + if (MultiClass *MC = ParseMultiClassID()) + Result.Rec = &MC->Rec; + } else { + Result.Rec = ParseClassID(); + } + if (!Result.Rec) return Result; + + // If there is no template arg list, we're done. + if (!consume(tgtok::less)) { + Result.RefRange.End = Lex.getLoc(); + return Result; + } + + if (ParseTemplateArgValueList(Result.TemplateArgs, CurRec, Result.Rec)) { + Result.Rec = nullptr; // Error parsing value list. + return Result; + } + + if (CheckTemplateArgValues(Result.TemplateArgs, Result.RefRange.Start, + Result.Rec)) { + Result.Rec = nullptr; // Error checking value list. + return Result; + } + + Result.RefRange.End = Lex.getLoc(); + return Result; +} + +/// ParseSubMultiClassReference - Parse a reference to a subclass or to a +/// templated submulticlass. This returns a SubMultiClassRefTy with a null +/// Record* on error. +/// +/// SubMultiClassRef ::= MultiClassID +/// SubMultiClassRef ::= MultiClassID '<' ValueList '>' +/// +SubMultiClassReference TGParser:: +ParseSubMultiClassReference(MultiClass *CurMC) { + SubMultiClassReference Result; + Result.RefRange.Start = Lex.getLoc(); + + Result.MC = ParseMultiClassID(); + if (!Result.MC) return Result; + + // If there is no template arg list, we're done. + if (!consume(tgtok::less)) { + Result.RefRange.End = Lex.getLoc(); + return Result; + } + + if (ParseTemplateArgValueList(Result.TemplateArgs, &CurMC->Rec, + &Result.MC->Rec)) { + Result.MC = nullptr; // Error parsing value list. + return Result; + } + + Result.RefRange.End = Lex.getLoc(); + + return Result; +} + +/// ParseRangePiece - Parse a bit/value range. +/// RangePiece ::= 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; + if (!CurVal) + CurVal = ParseValue(nullptr); + + IntInit *II = dyn_cast_or_null<IntInit>(CurVal); + if (!II) + return TokError("expected integer or bitrange"); + + int64_t Start = II->getValue(); + int64_t End; + + if (Start < 0) + return TokError("invalid range, cannot be negative"); + + switch (Lex.getCode()) { + default: + Ranges.push_back(Start); + return false; + + case tgtok::dotdotdot: + case tgtok::minus: { + Lex.Lex(); // eat + + Init *I_End = ParseValue(nullptr); + IntInit *II_End = dyn_cast_or_null<IntInit>(I_End); + if (!II_End) { + TokError("expected integer value as end of range"); + return true; + } + + End = II_End->getValue(); + break; + } + case tgtok::IntVal: { + End = -Lex.getCurIntVal(); + Lex.Lex(); + break; + } + } + if (End < 0) + return TokError("invalid range, cannot be negative"); + + // Add to the range. + if (Start < End) + for (; Start <= End; ++Start) + Ranges.push_back(Start); + else + for (; Start >= End; --Start) + Ranges.push_back(Start); + return false; +} + +/// ParseRangeList - Parse a list of scalars and ranges into scalar values. +/// +/// RangeList ::= RangePiece (',' RangePiece)* +/// +void TGParser::ParseRangeList(SmallVectorImpl<unsigned> &Result) { + // Parse the first piece. + if (ParseRangePiece(Result)) { + Result.clear(); + return; + } + while (consume(tgtok::comma)) + // Parse the next range piece. + if (ParseRangePiece(Result)) { + Result.clear(); + return; + } +} + +/// ParseOptionalRangeList - Parse either a range list in <>'s or nothing. +/// OptionalRangeList ::= '<' RangeList '>' +/// OptionalRangeList ::= /*empty*/ +bool TGParser::ParseOptionalRangeList(SmallVectorImpl<unsigned> &Ranges) { + SMLoc StartLoc = Lex.getLoc(); + if (!consume(tgtok::less)) + return false; + + // Parse the range list. + ParseRangeList(Ranges); + if (Ranges.empty()) return true; + + if (!consume(tgtok::greater)) { + TokError("expected '>' at end of range list"); + return Error(StartLoc, "to match this '<'"); + } + return false; +} + +/// ParseOptionalBitList - Parse either a bit list in {}'s or nothing. +/// OptionalBitList ::= '{' RangeList '}' +/// OptionalBitList ::= /*empty*/ +bool TGParser::ParseOptionalBitList(SmallVectorImpl<unsigned> &Ranges) { + SMLoc StartLoc = Lex.getLoc(); + if (!consume(tgtok::l_brace)) + return false; + + // Parse the range list. + ParseRangeList(Ranges); + if (Ranges.empty()) return true; + + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of bit list"); + return Error(StartLoc, "to match this '{'"); + } + return false; +} + +/// ParseType - Parse and return a tblgen type. This returns null on error. +/// +/// Type ::= STRING // string type +/// Type ::= CODE // code type +/// Type ::= BIT // bit type +/// Type ::= BITS '<' INTVAL '>' // bits<x> type +/// Type ::= INT // int type +/// Type ::= LIST '<' Type '>' // list<x> type +/// Type ::= DAG // dag type +/// Type ::= ClassID // Record Type +/// +RecTy *TGParser::ParseType() { + switch (Lex.getCode()) { + default: TokError("Unknown token when expecting a type"); return nullptr; + case tgtok::String: + case tgtok::Code: + Lex.Lex(); + return StringRecTy::get(Records); + case tgtok::Bit: + Lex.Lex(); + return BitRecTy::get(Records); + case tgtok::Int: + Lex.Lex(); + return IntRecTy::get(Records); + case tgtok::Dag: + Lex.Lex(); + return DagRecTy::get(Records); + case tgtok::Id: + if (Record *R = ParseClassID()) + return RecordRecTy::get(R); + TokError("unknown class name"); + return nullptr; + case tgtok::Bits: { + if (Lex.Lex() != tgtok::less) { // Eat 'bits' + TokError("expected '<' after bits type"); + return nullptr; + } + 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. + TokError("expected '>' at end of bits<n> type"); + return nullptr; + } + Lex.Lex(); // Eat '>' + return BitsRecTy::get(Records, Val); + } + case tgtok::List: { + if (Lex.Lex() != tgtok::less) { // Eat 'bits' + TokError("expected '<' after list type"); + return nullptr; + } + Lex.Lex(); // Eat '<' + RecTy *SubType = ParseType(); + if (!SubType) return nullptr; + + if (!consume(tgtok::greater)) { + TokError("expected '>' at end of list<ty> type"); + return nullptr; + } + return ListRecTy::get(SubType); + } + } +} + +/// ParseIDValue +Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc, + IDParseMode Mode) { + if (CurRec) { + if (RecordVal *RV = CurRec->getValue(Name)) { + if (TrackReferenceLocs) + RV->addReferenceLoc(NameLoc); + return VarInit::get(Name, RV->getType()); + } + } + + if ((CurRec && CurRec->isClass()) || CurMultiClass) { + Init *TemplateArgName; + if (CurMultiClass) { + TemplateArgName = + QualifyName(CurMultiClass->Rec, CurMultiClass, Name, "::"); + } else + TemplateArgName = QualifyName(*CurRec, CurMultiClass, Name, ":"); + + Record *TemplateRec = CurMultiClass ? &CurMultiClass->Rec : CurRec; + if (TemplateRec->isTemplateArg(TemplateArgName)) { + RecordVal *RV = TemplateRec->getValue(TemplateArgName); + assert(RV && "Template arg doesn't exist??"); + RV->setUsed(true); + if (TrackReferenceLocs) + RV->addReferenceLoc(NameLoc); + return VarInit::get(TemplateArgName, RV->getType()); + } else if (Name->getValue() == "NAME") { + return VarInit::get(TemplateArgName, StringRecTy::get(Records)); + } + } + + if (CurLocalScope) + if (Init *I = CurLocalScope->getVar(Name->getValue())) + return I; + + // If this is in a foreach loop, make sure it's not a loop iterator + for (const auto &L : Loops) { + if (L->IterVar) { + VarInit *IterVar = dyn_cast<VarInit>(L->IterVar); + if (IterVar && IterVar->getNameInit() == Name) + return IterVar; + } + } + + if (Mode == ParseNameMode) + return Name; + + if (Init *I = Records.getGlobal(Name->getValue())) { + // Add a reference to the global if it's a record. + if (TrackReferenceLocs) { + if (auto *Def = dyn_cast<DefInit>(I)) + Def->getDef()->appendReferenceLoc(NameLoc); + } + return I; + } + + // Allow self-references of concrete defs, but delay the lookup so that we + // get the correct type. + if (CurRec && !CurRec->isClass() && !CurMultiClass && + CurRec->getNameInit() == Name) + return UnOpInit::get(UnOpInit::CAST, Name, CurRec->getType()); + + Error(NameLoc.Start, "Variable not defined: '" + Name->getValue() + "'"); + return nullptr; +} + +/// ParseOperation - Parse an operator. This returns null on error. +/// +/// Operation ::= XOperator ['<' Type '>'] '(' Args ')' +/// +Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { + switch (Lex.getCode()) { + default: + TokError("unknown bang operator"); + return nullptr; + case tgtok::XNOT: + case tgtok::XLOG2: + case tgtok::XHead: + case tgtok::XTail: + case tgtok::XSize: + case tgtok::XEmpty: + case tgtok::XCast: + case tgtok::XGetDagOp: { // Value ::= !unop '(' Value ')' + UnOpInit::UnaryOp Code; + RecTy *Type = nullptr; + + switch (Lex.getCode()) { + default: llvm_unreachable("Unhandled code!"); + case tgtok::XCast: + Lex.Lex(); // eat the operation + Code = UnOpInit::CAST; + + Type = ParseOperatorType(); + + if (!Type) { + TokError("did not get type for unary operator"); + return nullptr; + } + + break; + case tgtok::XNOT: + Lex.Lex(); // eat the operation + Code = UnOpInit::NOT; + Type = IntRecTy::get(Records); + break; + case tgtok::XLOG2: + Lex.Lex(); // eat the operation + Code = UnOpInit::LOG2; + Type = IntRecTy::get(Records); + break; + case tgtok::XHead: + Lex.Lex(); // eat the operation + Code = UnOpInit::HEAD; + break; + case tgtok::XTail: + Lex.Lex(); // eat the operation + Code = UnOpInit::TAIL; + break; + case tgtok::XSize: + Lex.Lex(); + Code = UnOpInit::SIZE; + Type = IntRecTy::get(Records); + break; + case tgtok::XEmpty: + Lex.Lex(); // eat the operation + Code = UnOpInit::EMPTY; + Type = IntRecTy::get(Records); + break; + case tgtok::XGetDagOp: + Lex.Lex(); // eat the operation + if (Lex.getCode() == tgtok::less) { + // Parse an optional type suffix, so that you can say + // !getdagop<BaseClass>(someDag) as a shorthand for + // !cast<BaseClass>(!getdagop(someDag)). + Type = ParseOperatorType(); + + if (!Type) { + TokError("did not get type for unary operator"); + return nullptr; + } + + if (!isa<RecordRecTy>(Type)) { + TokError("type for !getdagop must be a record type"); + // but keep parsing, to consume the operand + } + } else { + Type = RecordRecTy::get(Records, {}); + } + Code = UnOpInit::GETDAGOP; + break; + } + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after unary operator"); + return nullptr; + } + + Init *LHS = ParseValue(CurRec); + if (!LHS) return nullptr; + + 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 && !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()); + 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) { + 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 (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; + } + 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; + } + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in unary operator"); + return nullptr; + } + return (UnOpInit::get(Code, LHS, Type))->Fold(CurRec); + } + + case tgtok::XIsA: { + // Value ::= !isa '<' Type '>' '(' Value ')' + Lex.Lex(); // eat the operation + + RecTy *Type = ParseOperatorType(); + if (!Type) + return nullptr; + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after type of !isa"); + return nullptr; + } + + Init *LHS = ParseValue(CurRec); + if (!LHS) + return nullptr; + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !isa"); + return nullptr; + } + + return (IsAOpInit::get(Type, LHS))->Fold(); + } + + case tgtok::XExists: { + // Value ::= !exists '<' Type '>' '(' Value ')' + Lex.Lex(); // eat the operation + + RecTy *Type = ParseOperatorType(); + if (!Type) + return nullptr; + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after type of !exists"); + return nullptr; + } + + SMLoc ExprLoc = Lex.getLoc(); + Init *Expr = ParseValue(CurRec); + if (!Expr) + return nullptr; + + TypedInit *ExprType = dyn_cast<TypedInit>(Expr); + if (!ExprType) { + Error(ExprLoc, "expected string type argument in !exists operator"); + return nullptr; + } + + RecordRecTy *RecType = dyn_cast<RecordRecTy>(ExprType->getType()); + if (RecType) { + Error(ExprLoc, + "expected string type argument in !exists operator, please " + "use !isa instead"); + return nullptr; + } + + StringRecTy *SType = dyn_cast<StringRecTy>(ExprType->getType()); + if (!SType) { + Error(ExprLoc, "expected string type argument in !exists operator"); + return nullptr; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !exists"); + return nullptr; + } + + return (ExistsOpInit::get(Type, Expr))->Fold(CurRec); + } + + case tgtok::XConcat: + case tgtok::XADD: + case tgtok::XSUB: + case tgtok::XMUL: + case tgtok::XDIV: + case tgtok::XAND: + case tgtok::XOR: + case tgtok::XXOR: + case tgtok::XSRA: + case tgtok::XSRL: + case tgtok::XSHL: + case tgtok::XEq: + case tgtok::XNe: + case tgtok::XLe: + case tgtok::XLt: + case tgtok::XGe: + case tgtok::XGt: + case tgtok::XListConcat: + case tgtok::XListSplat: + case tgtok::XListRemove: + case tgtok::XStrConcat: + case tgtok::XInterleave: + case tgtok::XSetDagOp: { // Value ::= !binop '(' Value ',' Value ')' + tgtok::TokKind OpTok = Lex.getCode(); + SMLoc OpLoc = Lex.getLoc(); + Lex.Lex(); // eat the operation + + BinOpInit::BinaryOp Code; + switch (OpTok) { + 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::XDIV: Code = BinOpInit::DIV; 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; + case tgtok::XEq: Code = BinOpInit::EQ; break; + case tgtok::XNe: Code = BinOpInit::NE; break; + case tgtok::XLe: Code = BinOpInit::LE; break; + case tgtok::XLt: Code = BinOpInit::LT; break; + 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::XListRemove: Code = BinOpInit::LISTREMOVE; 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; + RecTy *ArgType = nullptr; + switch (OpTok) { + default: + llvm_unreachable("Unhandled code!"); + case tgtok::XConcat: + case tgtok::XSetDagOp: + Type = DagRecTy::get(Records); + ArgType = DagRecTy::get(Records); + 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: + case tgtok::XDIV: + Type = IntRecTy::get(Records); + ArgType = IntRecTy::get(Records); + break; + case tgtok::XEq: + case tgtok::XNe: + case tgtok::XLe: + case tgtok::XLt: + case tgtok::XGe: + case tgtok::XGt: + Type = BitRecTy::get(Records); + // 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. + ArgType = ItemType; + break; + case tgtok::XListSplat: + // Can't do any typechecking until we parse the first argument. + break; + case tgtok::XListRemove: + // We don't know the list type until we parse the first argument. + ArgType = ItemType; + break; + case tgtok::XStrConcat: + Type = StringRecTy::get(Records); + ArgType = StringRecTy::get(Records); + break; + case tgtok::XInterleave: + Type = StringRecTy::get(Records); + // The first argument type is not yet known. + } + + if (Type && ItemType && !Type->typeIsConvertibleTo(ItemType)) { + Error(OpLoc, Twine("expected value of type '") + + ItemType->getAsString() + "', got '" + + Type->getAsString() + "'"); + return nullptr; + } + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after binary operator"); + return nullptr; + } + + 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)); + if (!InitList.back()) return nullptr; + + TypedInit *InitListBack = dyn_cast<TypedInit>(InitList.back()); + if (!InitListBack) { + Error(OpLoc, Twine("expected value to be a typed value, got '" + + InitList.back()->getAsString() + "'")); + return nullptr; + } + RecTy *ListType = InitListBack->getType(); + + if (!ArgType) { + // Argument type must be determined from the argument itself. + ArgType = ListType; + + switch (Code) { + case BinOpInit::LISTCONCAT: + if (!isa<ListRecTy>(ArgType)) { + Error(InitLoc, Twine("expected a list, got value of type '") + + ArgType->getAsString() + "'"); + return nullptr; + } + break; + case BinOpInit::LISTSPLAT: + if (ItemType && InitList.size() == 1) { + if (!isa<ListRecTy>(ItemType)) { + Error(OpLoc, + Twine("expected output type to be a list, got type '") + + ItemType->getAsString() + "'"); + return nullptr; + } + if (!ArgType->getListTy()->typeIsConvertibleTo(ItemType)) { + Error(OpLoc, Twine("expected first arg type to be '") + + ArgType->getAsString() + + "', got value of type '" + + cast<ListRecTy>(ItemType) + ->getElementType() + ->getAsString() + + "'"); + return nullptr; + } + } + if (InitList.size() == 2 && !isa<IntRecTy>(ArgType)) { + Error(InitLoc, Twine("expected second parameter to be an int, got " + "value of type '") + + ArgType->getAsString() + "'"); + return nullptr; + } + ArgType = nullptr; // Broken invariant: types not identical. + break; + case BinOpInit::LISTREMOVE: + if (!isa<ListRecTy>(ArgType)) { + Error(InitLoc, Twine("expected a list, got value of type '") + + ArgType->getAsString() + "'"); + return nullptr; + } + break; + case BinOpInit::EQ: + case BinOpInit::NE: + if (!ArgType->typeIsConvertibleTo(IntRecTy::get(Records)) && + !ArgType->typeIsConvertibleTo(StringRecTy::get(Records)) && + !ArgType->typeIsConvertibleTo(RecordRecTy::get(Records, {}))) { + 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(Records)) && + !ArgType->typeIsConvertibleTo(StringRecTy::get(Records))) { + 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(Records)->getListTy() && + !ArgType->typeIsConvertibleTo( + IntRecTy::get(Records)->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 '") + + ArgType->getAsString() + "', got '" + + ListType->getAsString() + "'"); + return nullptr; + } + 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 && Code != BinOpInit::DIV) + ArgType = Resolved; + } + + // Deal with BinOps whose arguments have different types, by + // rewriting ArgType in between them. + switch (Code) { + case BinOpInit::SETDAGOP: + // After parsing the first dag argument, switch to expecting + // a record, with no restriction on its superclasses. + ArgType = RecordRecTy::get(Records, {}); + break; + default: + break; + } + + if (!consume(tgtok::comma)) + break; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in operator"); + return nullptr; + } + + // listconcat returns a list with type of the argument. + if (Code == BinOpInit::LISTCONCAT) + Type = ArgType; + // listsplat returns a list of type of the *first* argument. + if (Code == BinOpInit::LISTSPLAT) + Type = cast<TypedInit>(InitList.front())->getType()->getListTy(); + // listremove returns a list with type of the argument. + if (Code == BinOpInit::LISTREMOVE) + Type = ArgType; + + // We allow multiple operands to associative operators like !strconcat as + // shorthand for nesting them. + if (Code == BinOpInit::STRCONCAT || Code == BinOpInit::LISTCONCAT || + Code == BinOpInit::CONCAT || Code == BinOpInit::ADD || + Code == BinOpInit::AND || Code == BinOpInit::OR || + 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); + InitList.back() = RHS; + } + } + + if (InitList.size() == 2) + return (BinOpInit::get(Code, InitList[0], InitList[1], Type)) + ->Fold(CurRec); + + Error(OpLoc, "expected two operands to operator"); + return nullptr; + } + + case tgtok::XForEach: + case tgtok::XFilter: { + return ParseOperationForEachFilter(CurRec, ItemType); + } + + case tgtok::XDag: + case tgtok::XIf: + case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' + TernOpInit::TernaryOp Code; + RecTy *Type = nullptr; + + tgtok::TokKind LexCode = Lex.getCode(); + Lex.Lex(); // eat the operation + switch (LexCode) { + default: llvm_unreachable("Unhandled code!"); + case tgtok::XDag: + Code = TernOpInit::DAG; + Type = DagRecTy::get(Records); + ItemType = nullptr; + break; + case tgtok::XIf: + Code = TernOpInit::IF; + break; + case tgtok::XSubst: + Code = TernOpInit::SUBST; + break; + } + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after ternary operator"); + return nullptr; + } + + Init *LHS = ParseValue(CurRec); + if (!LHS) return nullptr; + + if (!consume(tgtok::comma)) { + TokError("expected ',' in ternary operator"); + return nullptr; + } + + SMLoc MHSLoc = Lex.getLoc(); + Init *MHS = ParseValue(CurRec, ItemType); + if (!MHS) + return nullptr; + + if (!consume(tgtok::comma)) { + TokError("expected ',' in ternary operator"); + return nullptr; + } + + SMLoc RHSLoc = Lex.getLoc(); + Init *RHS = ParseValue(CurRec, ItemType); + if (!RHS) + return nullptr; + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in binary operator"); + return nullptr; + } + + switch (LexCode) { + default: llvm_unreachable("Unhandled code!"); + case tgtok::XDag: { + TypedInit *MHSt = dyn_cast<TypedInit>(MHS); + if (!MHSt && !isa<UnsetInit>(MHS)) { + Error(MHSLoc, "could not determine type of the child list in !dag"); + return nullptr; + } + if (MHSt && !isa<ListRecTy>(MHSt->getType())) { + Error(MHSLoc, Twine("expected list of children, got type '") + + MHSt->getType()->getAsString() + "'"); + return nullptr; + } + + TypedInit *RHSt = dyn_cast<TypedInit>(RHS); + if (!RHSt && !isa<UnsetInit>(RHS)) { + Error(RHSLoc, "could not determine type of the name list in !dag"); + return nullptr; + } + if (RHSt && StringRecTy::get(Records)->getListTy() != RHSt->getType()) { + Error(RHSLoc, Twine("expected list<string>, got type '") + + RHSt->getType()->getAsString() + "'"); + return nullptr; + } + + if (!MHSt && !RHSt) { + Error(MHSLoc, + "cannot have both unset children and unset names in !dag"); + return nullptr; + } + break; + } + case tgtok::XIf: { + RecTy *MHSTy = nullptr; + RecTy *RHSTy = nullptr; + + if (TypedInit *MHSt = dyn_cast<TypedInit>(MHS)) + MHSTy = MHSt->getType(); + if (BitsInit *MHSbits = dyn_cast<BitsInit>(MHS)) + MHSTy = BitsRecTy::get(Records, MHSbits->getNumBits()); + if (isa<BitInit>(MHS)) + MHSTy = BitRecTy::get(Records); + + if (TypedInit *RHSt = dyn_cast<TypedInit>(RHS)) + RHSTy = RHSt->getType(); + if (BitsInit *RHSbits = dyn_cast<BitsInit>(RHS)) + RHSTy = BitsRecTy::get(Records, RHSbits->getNumBits()); + if (isa<BitInit>(RHS)) + RHSTy = BitRecTy::get(Records); + + // For UnsetInit, it's typed from the other hand. + if (isa<UnsetInit>(MHS)) + MHSTy = RHSTy; + if (isa<UnsetInit>(RHS)) + RHSTy = MHSTy; + + if (!MHSTy || !RHSTy) { + TokError("could not get type for !if"); + return nullptr; + } + + Type = resolveTypes(MHSTy, RHSTy); + if (!Type) { + TokError(Twine("inconsistent types '") + MHSTy->getAsString() + + "' and '" + RHSTy->getAsString() + "' for !if"); + return nullptr; + } + break; + } + case tgtok::XSubst: { + TypedInit *RHSt = dyn_cast<TypedInit>(RHS); + if (!RHSt) { + TokError("could not get type for !subst"); + return nullptr; + } + Type = RHSt->getType(); + break; + } + } + return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec); + } + + case tgtok::XSubstr: + return ParseOperationSubstr(CurRec, ItemType); + + case tgtok::XFind: + return ParseOperationFind(CurRec, ItemType); + + case tgtok::XCond: + return ParseOperationCond(CurRec, ItemType); + + case tgtok::XFoldl: { + // Value ::= !foldl '(' Value ',' Value ',' Id ',' Id ',' Expr ')' + Lex.Lex(); // eat the operation + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after !foldl"); + return nullptr; + } + + Init *StartUntyped = ParseValue(CurRec); + if (!StartUntyped) + return nullptr; + + TypedInit *Start = dyn_cast<TypedInit>(StartUntyped); + if (!Start) { + TokError(Twine("could not get type of !foldl start: '") + + StartUntyped->getAsString() + "'"); + return nullptr; + } + + if (!consume(tgtok::comma)) { + TokError("expected ',' in !foldl"); + return nullptr; + } + + Init *ListUntyped = ParseValue(CurRec); + if (!ListUntyped) + return nullptr; + + TypedInit *List = dyn_cast<TypedInit>(ListUntyped); + if (!List) { + TokError(Twine("could not get type of !foldl list: '") + + ListUntyped->getAsString() + "'"); + return nullptr; + } + + ListRecTy *ListType = dyn_cast<ListRecTy>(List->getType()); + if (!ListType) { + TokError(Twine("!foldl list must be a list, but is of type '") + + List->getType()->getAsString()); + return nullptr; + } + + if (Lex.getCode() != tgtok::comma) { + TokError("expected ',' in !foldl"); + return nullptr; + } + + if (Lex.Lex() != tgtok::Id) { // eat the ',' + TokError("third argument of !foldl must be an identifier"); + return nullptr; + } + + Init *A = StringInit::get(Records, Lex.getCurStrVal()); + if (CurRec && CurRec->getValue(A)) { + TokError((Twine("left !foldl variable '") + A->getAsString() + + "' already defined") + .str()); + return nullptr; + } + + if (Lex.Lex() != tgtok::comma) { // eat the id + TokError("expected ',' in !foldl"); + return nullptr; + } + + if (Lex.Lex() != tgtok::Id) { // eat the ',' + TokError("fourth argument of !foldl must be an identifier"); + return nullptr; + } + + Init *B = StringInit::get(Records, Lex.getCurStrVal()); + if (CurRec && CurRec->getValue(B)) { + TokError((Twine("right !foldl variable '") + B->getAsString() + + "' already defined") + .str()); + return nullptr; + } + + if (Lex.Lex() != tgtok::comma) { // eat the id + TokError("expected ',' in !foldl"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + // 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) { + ParseRecTmp = std::make_unique<Record>(".parse", ArrayRef<SMLoc>{}, Records); + ParseRec = ParseRecTmp.get(); + } + + 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); + if (!ExprUntyped) + return nullptr; + + TypedInit *Expr = dyn_cast<TypedInit>(ExprUntyped); + if (!Expr) { + TokError("could not get type of !foldl expression"); + return nullptr; + } + + if (Expr->getType() != Start->getType()) { + TokError(Twine("!foldl expression must be of same type as start (") + + Start->getType()->getAsString() + "), but is of type " + + Expr->getType()->getAsString()); + return nullptr; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in fold operator"); + return nullptr; + } + + return FoldOpInit::get(Start, List, A, B, Expr, Start->getType()) + ->Fold(CurRec); + } + } +} + +/// ParseOperatorType - Parse a type for an operator. This returns +/// null on error. +/// +/// OperatorType ::= '<' Type '>' +/// +RecTy *TGParser::ParseOperatorType() { + RecTy *Type = nullptr; + + if (!consume(tgtok::less)) { + TokError("expected type name for operator"); + return nullptr; + } + + if (Lex.getCode() == tgtok::Code) + TokError("the 'code' type is not allowed in bang operators; use 'string'"); + + Type = ParseType(); + + if (!Type) { + TokError("expected type name for operator"); + return nullptr; + } + + if (!consume(tgtok::greater)) { + TokError("expected type name for operator"); + return nullptr; + } + + 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(Records); + + 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(Records, 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 !find operation. Return null on error. +/// +/// Substr ::= !find(string, string [, start-int]) => int +Init *TGParser::ParseOperationFind(Record *CurRec, RecTy *ItemType) { + TernOpInit::TernaryOp Code = TernOpInit::FIND; + RecTy *Type = IntRecTy::get(Records); + + Lex.Lex(); // eat the operation + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after !find operator"); + return nullptr; + } + + Init *LHS = ParseValue(CurRec); + if (!LHS) + return nullptr; + + if (!consume(tgtok::comma)) { + TokError("expected ',' in !find 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(Records, 0); + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !find 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 source string in !find"); + 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 target string in !find"); + return nullptr; + } + if (MHSt && !isa<StringRecTy>(MHSt->getType())) { + Error(MHSLoc, Twine("expected string, 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 start position in !find"); + 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(Records, 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(Records); + } 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' + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after !cond operator"); + return nullptr; + } + + // Parse through '[Case: Val,]+' + SmallVector<Init *, 4> Case; + SmallVector<Init *, 4> Val; + while (true) { + if (consume(tgtok::r_paren)) + break; + + Init *V = ParseValue(CurRec); + if (!V) + return nullptr; + Case.push_back(V); + + if (!consume(tgtok::colon)) { + TokError("expected ':' following a condition in !cond operator"); + return nullptr; + } + + V = ParseValue(CurRec, ItemType); + if (!V) + return nullptr; + Val.push_back(V); + + if (consume(tgtok::r_paren)) + break; + + if (!consume(tgtok::comma)) { + TokError("expected ',' or ')' following a value in !cond operator"); + return nullptr; + } + } + + if (Case.size() < 1) { + TokError("there should be at least 1 'condition : value' in the !cond operator"); + return nullptr; + } + + // resolve type + RecTy *Type = nullptr; + for (Init *V : Val) { + RecTy *VTy = nullptr; + if (TypedInit *Vt = dyn_cast<TypedInit>(V)) + VTy = Vt->getType(); + if (BitsInit *Vbits = dyn_cast<BitsInit>(V)) + VTy = BitsRecTy::get(Records, Vbits->getNumBits()); + if (isa<BitInit>(V)) + VTy = BitRecTy::get(Records); + + if (Type == nullptr) { + if (!isa<UnsetInit>(V)) + Type = VTy; + } else { + if (!isa<UnsetInit>(V)) { + RecTy *RType = resolveTypes(Type, VTy); + if (!RType) { + TokError(Twine("inconsistent types '") + Type->getAsString() + + "' and '" + VTy->getAsString() + "' for !cond"); + return nullptr; + } + Type = RType; + } + } + } + + if (!Type) { + TokError("could not determine type for !cond from its arguments"); + return nullptr; + } + return CondOpInit::get(Case, Val, Type)->Fold(CurRec); +} + +/// ParseSimpleValue - Parse a tblgen value. This returns null on error. +/// +/// SimpleValue ::= IDValue +/// SimpleValue ::= INTVAL +/// SimpleValue ::= STRVAL+ +/// SimpleValue ::= CODEFRAGMENT +/// SimpleValue ::= '?' +/// SimpleValue ::= '{' ValueList '}' +/// SimpleValue ::= ID '<' ValueListNE '>' +/// SimpleValue ::= '[' ValueList ']' +/// SimpleValue ::= '(' IDValue DagArgList ')' +/// SimpleValue ::= CONCATTOK '(' Value ',' Value ')' +/// SimpleValue ::= ADDTOK '(' Value ',' Value ')' +/// SimpleValue ::= DIVTOK '(' Value ',' Value ')' +/// SimpleValue ::= SUBTOK '(' Value ',' Value ')' +/// SimpleValue ::= SHLTOK '(' Value ',' Value ')' +/// SimpleValue ::= SRATOK '(' Value ',' Value ')' +/// SimpleValue ::= SRLTOK '(' Value ',' Value ')' +/// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')' +/// SimpleValue ::= LISTSPLATTOK '(' Value ',' Value ')' +/// SimpleValue ::= LISTREMOVETOK '(' Value ',' Value ')' +/// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')' +/// SimpleValue ::= COND '(' [Value ':' Value,]+ ')' +/// +Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, + IDParseMode Mode) { + Init *R = nullptr; + switch (Lex.getCode()) { + default: TokError("Unknown or reserved token when parsing a value"); break; + + case tgtok::TrueVal: + R = IntInit::get(Records, 1); + Lex.Lex(); + break; + case tgtok::FalseVal: + R = IntInit::get(Records, 0); + Lex.Lex(); + break; + case tgtok::IntVal: + R = IntInit::get(Records, Lex.getCurIntVal()); + Lex.Lex(); + break; + case tgtok::BinaryIntVal: { + auto BinaryVal = Lex.getCurBinaryIntVal(); + SmallVector<Init*, 16> Bits(BinaryVal.second); + for (unsigned i = 0, e = BinaryVal.second; i != e; ++i) + Bits[i] = BitInit::get(Records, BinaryVal.first & (1LL << i)); + R = BitsInit::get(Records, Bits); + Lex.Lex(); + break; + } + case tgtok::StrVal: { + std::string Val = Lex.getCurStrVal(); + Lex.Lex(); + + // Handle multiple consecutive concatenated strings. + while (Lex.getCode() == tgtok::StrVal) { + Val += Lex.getCurStrVal(); + Lex.Lex(); + } + + R = StringInit::get(Records, Val); + break; + } + case tgtok::CodeFragment: + R = StringInit::get(Records, Lex.getCurStrVal(), StringInit::SF_Code); + Lex.Lex(); + break; + case tgtok::question: + R = UnsetInit::get(Records); + Lex.Lex(); + break; + case tgtok::Id: { + SMRange NameLoc = Lex.getLocRange(); + StringInit *Name = StringInit::get(Records, Lex.getCurStrVal()); + if (Lex.Lex() != tgtok::less) // consume the Id. + return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue + + // Value ::= CLASSID '<' ValueListNE '>' (CLASSID has been consumed) + // This is supposed to synthesize a new anonymous definition, deriving + // from the class with the template arguments, but no body. + Record *Class = Records.getClass(Name->getValue()); + if (!Class) { + Error(NameLoc.Start, + "Expected a class name, got '" + Name->getValue() + "'"); + return nullptr; + } + + SmallVector<Init *, 8> Args; + Lex.Lex(); // consume the < + if (ParseTemplateArgValueList(Args, CurRec, Class)) + return nullptr; // Error parsing value list. + + if (CheckTemplateArgValues(Args, NameLoc.Start, Class)) + return nullptr; // Error checking template argument values. + + // Loop through the arguments that were not specified and make sure + // they have a complete value. + ArrayRef<Init *> TArgs = Class->getTemplateArgs(); + for (unsigned I = Args.size(), E = TArgs.size(); I < E; ++I) { + RecordVal *Arg = Class->getValue(TArgs[I]); + if (!Arg->getValue()->isComplete()) { + Error(NameLoc.Start, "Value not specified for template argument '" + + TArgs[I]->getAsUnquotedString() + "' (#" + + Twine(I) + ") of parent class '" + + Class->getNameInitAsString() + "'"); + } + } + + if (TrackReferenceLocs) + Class->appendReferenceLoc(NameLoc); + return VarDefInit::get(Class, Args)->Fold(); + } + case tgtok::l_brace: { // Value ::= '{' ValueList '}' + SMLoc BraceLoc = Lex.getLoc(); + Lex.Lex(); // eat the '{' + SmallVector<Init*, 16> Vals; + + if (Lex.getCode() != tgtok::r_brace) { + ParseValueList(Vals, CurRec); + if (Vals.empty()) return nullptr; + } + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of bit list value"); + return nullptr; + } + + SmallVector<Init *, 16> NewBits; + + // As we parse { a, b, ... }, 'a' is the highest bit, but we parse it + // first. We'll first read everything in to a vector, then we can reverse + // it to get the bits in the correct order for the BitsInit value. + for (unsigned i = 0, e = Vals.size(); i != e; ++i) { + // FIXME: The following two loops would not be duplicated + // if the API was a little more orthogonal. + + // bits<n> values are allowed to initialize n bits. + if (BitsInit *BI = dyn_cast<BitsInit>(Vals[i])) { + for (unsigned i = 0, e = BI->getNumBits(); i != e; ++i) + NewBits.push_back(BI->getBit((e - i) - 1)); + continue; + } + // bits<n> can also come from variable initializers. + if (VarInit *VI = dyn_cast<VarInit>(Vals[i])) { + if (BitsRecTy *BitsRec = dyn_cast<BitsRecTy>(VI->getType())) { + for (unsigned i = 0, e = BitsRec->getNumBits(); i != e; ++i) + NewBits.push_back(VI->getBit((e - i) - 1)); + continue; + } + // Fallthrough to try convert this to a bit. + } + // All other values must be convertible to just a single bit. + Init *Bit = Vals[i]->getCastTo(BitRecTy::get(Records)); + if (!Bit) { + Error(BraceLoc, "Element #" + Twine(i) + " (" + Vals[i]->getAsString() + + ") is not convertable to a bit"); + return nullptr; + } + NewBits.push_back(Bit); + } + std::reverse(NewBits.begin(), NewBits.end()); + return BitsInit::get(Records, NewBits); + } + case tgtok::l_square: { // Value ::= '[' ValueList ']' + Lex.Lex(); // eat the '[' + SmallVector<Init*, 16> Vals; + + RecTy *DeducedEltTy = nullptr; + ListRecTy *GivenListTy = nullptr; + + if (ItemType) { + ListRecTy *ListType = dyn_cast<ListRecTy>(ItemType); + if (!ListType) { + TokError(Twine("Encountered a list when expecting a ") + + ItemType->getAsString()); + return nullptr; + } + GivenListTy = ListType; + } + + if (Lex.getCode() != tgtok::r_square) { + ParseValueList(Vals, CurRec, + GivenListTy ? GivenListTy->getElementType() : nullptr); + if (Vals.empty()) return nullptr; + } + if (!consume(tgtok::r_square)) { + TokError("expected ']' at end of list value"); + return nullptr; + } + + RecTy *GivenEltTy = nullptr; + if (consume(tgtok::less)) { + // Optional list element type + GivenEltTy = ParseType(); + if (!GivenEltTy) { + // Couldn't parse element type + return nullptr; + } + + if (!consume(tgtok::greater)) { + TokError("expected '>' at end of list element type"); + return nullptr; + } + } + + // Check elements + RecTy *EltTy = nullptr; + for (Init *V : Vals) { + TypedInit *TArg = dyn_cast<TypedInit>(V); + if (TArg) { + if (EltTy) { + EltTy = resolveTypes(EltTy, TArg->getType()); + if (!EltTy) { + TokError("Incompatible types in list elements"); + return nullptr; + } + } else { + EltTy = TArg->getType(); + } + } + } + + if (GivenEltTy) { + if (EltTy) { + // Verify consistency + if (!EltTy->typeIsConvertibleTo(GivenEltTy)) { + TokError("Incompatible types in list elements"); + return nullptr; + } + } + EltTy = GivenEltTy; + } + + if (!EltTy) { + if (!ItemType) { + TokError("No type for list"); + return nullptr; + } + DeducedEltTy = GivenListTy->getElementType(); + } else { + // Make sure the deduced type is compatible with the given type + if (GivenListTy) { + if (!EltTy->typeIsConvertibleTo(GivenListTy->getElementType())) { + TokError(Twine("Element type mismatch for list: element type '") + + EltTy->getAsString() + "' not convertible to '" + + GivenListTy->getElementType()->getAsString()); + return nullptr; + } + } + DeducedEltTy = EltTy; + } + + return ListInit::get(Vals, DeducedEltTy); + } + 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::XGetDagOp) { + TokError("expected identifier in dag init"); + return nullptr; + } + + Init *Operator = ParseValue(CurRec); + if (!Operator) return nullptr; + + // If the operator name is present, parse it. + StringInit *OperatorName = nullptr; + if (consume(tgtok::colon)) { + if (Lex.getCode() != tgtok::VarName) { // eat the ':' + TokError("expected variable name in dag operator"); + return nullptr; + } + OperatorName = StringInit::get(Records, Lex.getCurStrVal()); + Lex.Lex(); // eat the VarName. + } + + SmallVector<std::pair<llvm::Init*, StringInit*>, 8> DagArgs; + if (Lex.getCode() != tgtok::r_paren) { + ParseDagArgList(DagArgs, CurRec); + if (DagArgs.empty()) return nullptr; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in dag init"); + return nullptr; + } + + return DagInit::get(Operator, OperatorName, DagArgs); + } + + case tgtok::XHead: + case tgtok::XTail: + case tgtok::XSize: + case tgtok::XEmpty: + case tgtok::XCast: + case tgtok::XGetDagOp: // Value ::= !unop '(' Value ')' + case tgtok::XExists: + case tgtok::XIsA: + case tgtok::XConcat: + case tgtok::XDag: + case tgtok::XADD: + case tgtok::XSUB: + case tgtok::XMUL: + case tgtok::XDIV: + case tgtok::XNOT: + case tgtok::XLOG2: + case tgtok::XAND: + case tgtok::XOR: + case tgtok::XXOR: + case tgtok::XSRA: + case tgtok::XSRL: + case tgtok::XSHL: + case tgtok::XEq: + case tgtok::XNe: + case tgtok::XLe: + case tgtok::XLt: + case tgtok::XGe: + case tgtok::XGt: + case tgtok::XListConcat: + case tgtok::XListSplat: + case tgtok::XListRemove: + case tgtok::XStrConcat: + case tgtok::XInterleave: + case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')' + case tgtok::XIf: + case tgtok::XCond: + case tgtok::XFoldl: + case tgtok::XForEach: + case tgtok::XFilter: + case tgtok::XSubst: + case tgtok::XSubstr: + case tgtok::XFind: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' + return ParseOperation(CurRec, ItemType); + } + } + + return R; +} + +/// ParseValue - Parse a TableGen value. This returns null on error. +/// +/// Value ::= SimpleValue ValueSuffix* +/// ValueSuffix ::= '{' BitList '}' +/// ValueSuffix ::= '[' BitList ']' +/// ValueSuffix ::= '.' ID +/// +Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) { + Init *Result = ParseSimpleValue(CurRec, ItemType, Mode); + if (!Result) return nullptr; + + // Parse the suffixes now if present. + while (true) { + switch (Lex.getCode()) { + default: return Result; + case tgtok::l_brace: { + if (Mode == ParseNameMode) + // This is the beginning of the object body. + return Result; + + SMLoc CurlyLoc = Lex.getLoc(); + Lex.Lex(); // eat the '{' + SmallVector<unsigned, 16> Ranges; + ParseRangeList(Ranges); + if (Ranges.empty()) return nullptr; + + // Reverse the bitlist. + std::reverse(Ranges.begin(), Ranges.end()); + Result = Result->convertInitializerBitRange(Ranges); + if (!Result) { + Error(CurlyLoc, "Invalid bit range for value"); + return nullptr; + } + + // Eat the '}'. + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of bit range list"); + return nullptr; + } + break; + } + case tgtok::l_square: { + SMLoc SquareLoc = Lex.getLoc(); + Lex.Lex(); // eat the '[' + SmallVector<unsigned, 16> Ranges; + ParseRangeList(Ranges); + if (Ranges.empty()) return nullptr; + + Result = Result->convertInitListSlice(Ranges); + if (!Result) { + Error(SquareLoc, "Invalid range for list slice"); + return nullptr; + } + + // Eat the ']'. + if (!consume(tgtok::r_square)) { + TokError("expected ']' at end of list slice"); + return nullptr; + } + break; + } + case tgtok::dot: { + if (Lex.Lex() != tgtok::Id) { // eat the . + TokError("expected field identifier after '.'"); + return nullptr; + } + SMRange FieldNameLoc = Lex.getLocRange(); + StringInit *FieldName = StringInit::get(Records, Lex.getCurStrVal()); + if (!Result->getFieldType(FieldName)) { + TokError("Cannot access field '" + Lex.getCurStrVal() + "' of value '" + + Result->getAsString() + "'"); + return nullptr; + } + + // Add a reference to this field if we know the record class. + if (TrackReferenceLocs) { + if (auto *DI = dyn_cast<DefInit>(Result)) { + DI->getDef()->getValue(FieldName)->addReferenceLoc(FieldNameLoc); + } else if (auto *TI = dyn_cast<TypedInit>(Result)) { + if (auto *RecTy = dyn_cast<RecordRecTy>(TI->getType())) { + for (Record *R : RecTy->getClasses()) + if (auto *RV = R->getValue(FieldName)) + RV->addReferenceLoc(FieldNameLoc); + } + } + } + + Result = FieldInit::get(Result, FieldName)->Fold(CurRec); + Lex.Lex(); // eat field name + break; + } + + case tgtok::paste: + SMLoc PasteLoc = Lex.getLoc(); + TypedInit *LHS = dyn_cast<TypedInit>(Result); + if (!LHS) { + Error(PasteLoc, "LHS of paste is not typed!"); + return nullptr; + } + + // Check if it's a 'listA # listB' + 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: + case tgtok::l_brace: + Result = LHS; // trailing paste, ignore. + break; + default: + Init *RHSResult = ParseValue(CurRec, ItemType, ParseValueMode); + if (!RHSResult) + return nullptr; + Result = BinOpInit::getListConcat(LHS, RHSResult); + break; + } + break; + } + + // Create a !strconcat() operation, first casting each operand to + // a string if necessary. + if (LHS->getType() != StringRecTy::get(Records)) { + auto CastLHS = dyn_cast<TypedInit>( + UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get(Records)) + ->Fold(CurRec)); + if (!CastLHS) { + Error(PasteLoc, + Twine("can't cast '") + LHS->getAsString() + "' to string"); + return nullptr; + } + LHS = CastLHS; + } + + TypedInit *RHS = nullptr; + + Lex.Lex(); // Eat the '#'. + switch (Lex.getCode()) { + case tgtok::colon: + case tgtok::semi: + case tgtok::l_brace: + // These are all of the tokens that can begin an object body. + // Some of these can also begin values but we disallow those cases + // because they are unlikely to be useful. + + // Trailing paste, concat with an empty string. + RHS = StringInit::get(Records, ""); + break; + + 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!"); + return nullptr; + } + + if (RHS->getType() != StringRecTy::get(Records)) { + auto CastRHS = dyn_cast<TypedInit>( + UnOpInit::get(UnOpInit::CAST, RHS, StringRecTy::get(Records)) + ->Fold(CurRec)); + if (!CastRHS) { + Error(PasteLoc, + Twine("can't cast '") + RHS->getAsString() + "' to string"); + return nullptr; + } + RHS = CastRHS; + } + + break; + } + + Result = BinOpInit::getStrConcat(LHS, RHS); + break; + } + } +} + +/// ParseDagArgList - Parse the argument list for a dag literal expression. +/// +/// DagArg ::= Value (':' VARNAME)? +/// DagArg ::= VARNAME +/// DagArgList ::= DagArg +/// DagArgList ::= DagArgList ',' DagArg +void TGParser::ParseDagArgList( + SmallVectorImpl<std::pair<llvm::Init*, StringInit*>> &Result, + Record *CurRec) { + + while (true) { + // DagArg ::= VARNAME + if (Lex.getCode() == tgtok::VarName) { + // A missing value is treated like '?'. + StringInit *VarName = StringInit::get(Records, Lex.getCurStrVal()); + Result.emplace_back(UnsetInit::get(Records), VarName); + Lex.Lex(); + } else { + // DagArg ::= Value (':' VARNAME)? + Init *Val = ParseValue(CurRec); + if (!Val) { + Result.clear(); + return; + } + + // If the variable name is present, add it. + StringInit *VarName = nullptr; + if (Lex.getCode() == tgtok::colon) { + if (Lex.Lex() != tgtok::VarName) { // eat the ':' + TokError("expected variable name in dag literal"); + Result.clear(); + return; + } + VarName = StringInit::get(Records, Lex.getCurStrVal()); + Lex.Lex(); // eat the VarName. + } + + Result.push_back(std::make_pair(Val, VarName)); + } + if (!consume(tgtok::comma)) + break; + } +} + +/// ParseValueList - Parse a comma separated list of values, returning them +/// in a vector. Note that this always expects to be able to parse at least one +/// value. It returns an empty list if this is not possible. +/// +/// ValueList ::= Value (',' Value) +/// +void TGParser::ParseValueList(SmallVectorImpl<Init *> &Result, Record *CurRec, + RecTy *ItemType) { + + Result.push_back(ParseValue(CurRec, ItemType)); + if (!Result.back()) { + Result.clear(); + return; + } + + while (consume(tgtok::comma)) { + // ignore trailing comma for lists + if (Lex.getCode() == tgtok::r_square) + return; + Result.push_back(ParseValue(CurRec, ItemType)); + if (!Result.back()) { + Result.clear(); + return; + } + } +} + +// ParseTemplateArgValueList - Parse a template argument list with the syntax +// shown, filling in the Result vector. The open angle has been consumed. +// An empty argument list is allowed. Return false if okay, true if an +// error was detected. +// +// TemplateArgList ::= '<' [Value {',' Value}*] '>' +bool TGParser::ParseTemplateArgValueList(SmallVectorImpl<Init *> &Result, + Record *CurRec, Record *ArgsRec) { + + assert(Result.empty() && "Result vector is not empty"); + ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs(); + unsigned ArgIndex = 0; + RecTy *ItemType; + + if (consume(tgtok::greater)) // empty value list + return false; + + while (true) { + if (ArgIndex >= TArgs.size()) { + TokError("Too many template arguments: " + utostr(ArgIndex + 1)); + return true; + } + const RecordVal *Arg = ArgsRec->getValue(TArgs[ArgIndex]); + assert(Arg && "Template argument record not found"); + + ItemType = Arg->getType(); + Init *Value = ParseValue(CurRec, ItemType); + if (!Value) + return true; + Result.push_back(Value); + + if (consume(tgtok::greater)) // end of argument list? + return false; + if (!consume(tgtok::comma)) + return TokError("Expected comma before next argument"); + ++ArgIndex; + } +} + +/// ParseDeclaration - Read a declaration, returning the name of field ID, or an +/// empty string on error. This can happen in a number of different contexts, +/// including within a def or in the template args for a class (in which case +/// CurRec will be non-null) and within the template args for a multiclass (in +/// which case CurRec will be null, but CurMultiClass will be set). This can +/// also happen within a def that is within a multiclass, which will set both +/// CurRec and CurMultiClass. +/// +/// Declaration ::= FIELD? Type ID ('=' Value)? +/// +Init *TGParser::ParseDeclaration(Record *CurRec, + bool ParsingTemplateArgs) { + // Read the field prefix if present. + bool HasField = consume(tgtok::Field); + + RecTy *Type = ParseType(); + if (!Type) return nullptr; + + if (Lex.getCode() != tgtok::Id) { + TokError("Expected identifier in declaration"); + return nullptr; + } + + std::string Str = Lex.getCurStrVal(); + if (Str == "NAME") { + TokError("'" + Str + "' is a reserved variable name"); + return nullptr; + } + + SMLoc IdLoc = Lex.getLoc(); + Init *DeclName = StringInit::get(Records, Str); + Lex.Lex(); + + bool BadField; + if (!ParsingTemplateArgs) { // def, possibly in a multiclass + BadField = AddValue(CurRec, IdLoc, + RecordVal(DeclName, IdLoc, Type, + HasField ? RecordVal::FK_NonconcreteOK + : RecordVal::FK_Normal)); + + } else if (CurRec) { // class template argument + DeclName = QualifyName(*CurRec, CurMultiClass, DeclName, ":"); + BadField = AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type, + RecordVal::FK_TemplateArg)); + + } else { // multiclass template argument + assert(CurMultiClass && "invalid context for template argument"); + DeclName = QualifyName(CurMultiClass->Rec, CurMultiClass, DeclName, "::"); + BadField = AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type, + RecordVal::FK_TemplateArg)); + } + if (BadField) + return nullptr; + + // If a value is present, parse it and set new field's value. + if (consume(tgtok::equal)) { + SMLoc ValLoc = Lex.getLoc(); + Init *Val = ParseValue(CurRec, Type); + if (!Val || + SetValue(CurRec, ValLoc, DeclName, std::nullopt, Val, + /*AllowSelfAssignment=*/false, /*OverrideDefLoc=*/false)) { + // Return the name, even if an error is thrown. This is so that we can + // continue to make some progress, even without the value having been + // initialized. + return DeclName; + } + } + + return DeclName; +} + +/// ParseForeachDeclaration - Read a foreach declaration, returning +/// the name of the declared object or a NULL Init on error. Return +/// the name of the parsed initializer list through ForeachListName. +/// +/// ForeachDeclaration ::= ID '=' '{' RangeList '}' +/// ForeachDeclaration ::= ID '=' RangePiece +/// ForeachDeclaration ::= ID '=' Value +/// +VarInit *TGParser::ParseForeachDeclaration(Init *&ForeachListValue) { + if (Lex.getCode() != tgtok::Id) { + TokError("Expected identifier in foreach declaration"); + return nullptr; + } + + Init *DeclName = StringInit::get(Records, Lex.getCurStrVal()); + Lex.Lex(); + + // If a value is present, parse it. + if (!consume(tgtok::equal)) { + TokError("Expected '=' in foreach declaration"); + return nullptr; + } + + RecTy *IterType = nullptr; + SmallVector<unsigned, 16> Ranges; + + switch (Lex.getCode()) { + case tgtok::l_brace: { // '{' RangeList '}' + Lex.Lex(); // eat the '{' + ParseRangeList(Ranges); + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of bit range list"); + return nullptr; + } + break; + } + + default: { + SMLoc ValueLoc = Lex.getLoc(); + Init *I = ParseValue(nullptr); + if (!I) + return nullptr; + + TypedInit *TI = dyn_cast<TypedInit>(I); + if (TI && isa<ListRecTy>(TI->getType())) { + ForeachListValue = I; + IterType = cast<ListRecTy>(TI->getType())->getElementType(); + break; + } + + if (TI) { + if (ParseRangePiece(Ranges, TI)) + return nullptr; + break; + } + + Error(ValueLoc, "expected a list, got '" + I->getAsString() + "'"); + if (CurMultiClass) { + PrintNote({}, "references to multiclass template arguments cannot be " + "resolved at this time"); + } + return nullptr; + } + } + + + if (!Ranges.empty()) { + assert(!IterType && "Type already initialized?"); + IterType = IntRecTy::get(Records); + std::vector<Init *> Values; + for (unsigned R : Ranges) + Values.push_back(IntInit::get(Records, R)); + ForeachListValue = ListInit::get(Values, IterType); + } + + if (!IterType) + return nullptr; + + return VarInit::get(DeclName, IterType); +} + +/// ParseTemplateArgList - Read a template argument list, which is a non-empty +/// sequence of template-declarations in <>'s. If CurRec is non-null, these are +/// template args for a class. If null, these are the template args for a +/// multiclass. +/// +/// TemplateArgList ::= '<' Declaration (',' Declaration)* '>' +/// +bool TGParser::ParseTemplateArgList(Record *CurRec) { + assert(Lex.getCode() == tgtok::less && "Not a template arg list!"); + Lex.Lex(); // eat the '<' + + Record *TheRecToAddTo = CurRec ? CurRec : &CurMultiClass->Rec; + + // Read the first declaration. + Init *TemplArg = ParseDeclaration(CurRec, true/*templateargs*/); + if (!TemplArg) + return true; + + TheRecToAddTo->addTemplateArg(TemplArg); + + while (consume(tgtok::comma)) { + // Read the following declarations. + SMLoc Loc = Lex.getLoc(); + TemplArg = ParseDeclaration(CurRec, true/*templateargs*/); + if (!TemplArg) + return true; + + if (TheRecToAddTo->isTemplateArg(TemplArg)) + return Error(Loc, "template argument with the same name has already been " + "defined"); + + TheRecToAddTo->addTemplateArg(TemplArg); + } + + if (!consume(tgtok::greater)) + return TokError("expected '>' at end of template argument list"); + return false; +} + +/// 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(); + + if (Lex.getCode() != tgtok::Let) { + if (!ParseDeclaration(CurRec, false)) + return true; + + if (!consume(tgtok::semi)) + return TokError("expected ';' after declaration"); + return false; + } + + // LET ID OptionalRangeList '=' Value ';' + if (Lex.Lex() != tgtok::Id) + return TokError("expected field identifier after let"); + + SMLoc IdLoc = Lex.getLoc(); + StringInit *FieldName = StringInit::get(Records, Lex.getCurStrVal()); + Lex.Lex(); // eat the field name. + + SmallVector<unsigned, 16> BitList; + if (ParseOptionalBitList(BitList)) + return true; + std::reverse(BitList.begin(), BitList.end()); + + if (!consume(tgtok::equal)) + return TokError("expected '=' in let expression"); + + RecordVal *Field = CurRec->getValue(FieldName); + if (!Field) + return TokError("Value '" + FieldName->getValue() + "' unknown!"); + + RecTy *Type = Field->getType(); + if (!BitList.empty() && isa<BitsRecTy>(Type)) { + // When assigning to a subset of a 'bits' object, expect the RHS to have + // the type of that subset instead of the type of the whole object. + Type = BitsRecTy::get(Records, BitList.size()); + } + + Init *Val = ParseValue(CurRec, Type); + if (!Val) return true; + + if (!consume(tgtok::semi)) + return TokError("expected ';' after let expression"); + + return SetValue(CurRec, IdLoc, FieldName, BitList, Val); +} + +/// ParseBody - Read the body of a class or def. Return true on error, false on +/// success. +/// +/// Body ::= ';' +/// Body ::= '{' BodyList '}' +/// BodyList BodyItem* +/// +bool TGParser::ParseBody(Record *CurRec) { + // If this is a null definition, just eat the semi and return. + if (consume(tgtok::semi)) + return false; + + if (!consume(tgtok::l_brace)) + return TokError("Expected '{' to start body or ';' for declaration only"); + + // An object body introduces a new scope for local variables. + TGLocalVarScope *BodyScope = PushLocalScope(); + + while (Lex.getCode() != tgtok::r_brace) + if (ParseBodyItem(CurRec)) + return true; + + PopLocalScope(BodyScope); + + // 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; +} + +/// Apply the current let bindings to \a CurRec. +/// \returns true on error, false otherwise. +bool TGParser::ApplyLetStack(Record *CurRec) { + for (SmallVectorImpl<LetRecord> &LetInfo : LetStack) + for (LetRecord &LR : LetInfo) + if (SetValue(CurRec, LR.Loc, LR.Name, LR.Bits, LR.Value)) + return true; + return false; +} + +/// Apply the current let bindings to the RecordsEntry. +bool TGParser::ApplyLetStack(RecordsEntry &Entry) { + if (Entry.Rec) + return ApplyLetStack(Entry.Rec.get()); + + // Let bindings are not applied to assertions. + if (Entry.Assertion) + return false; + + for (auto &E : Entry.Loop->Entries) { + if (ApplyLetStack(E)) + return true; + } + + return false; +} + +/// ParseObjectBody - Parse the body of a def or class. This consists of an +/// optional ClassList followed by a Body. CurRec is the current def or class +/// that is being parsed. +/// +/// ObjectBody ::= BaseClassList Body +/// BaseClassList ::= /*empty*/ +/// BaseClassList ::= ':' BaseClassListNE +/// BaseClassListNE ::= SubClassRef (',' SubClassRef)* +/// +bool TGParser::ParseObjectBody(Record *CurRec) { + // If there is a baseclass list, read it. + if (consume(tgtok::colon)) { + + // Read all of the subclasses. + SubClassReference SubClass = ParseSubClassReference(CurRec, false); + while (true) { + // Check for error. + if (!SubClass.Rec) return true; + + // Add it. + if (AddSubClass(CurRec, SubClass)) + return true; + + if (!consume(tgtok::comma)) + break; + SubClass = ParseSubClassReference(CurRec, false); + } + } + + if (ApplyLetStack(CurRec)) + return true; + + return ParseBody(CurRec); +} + +/// ParseDef - Parse and return a top level or multiclass record definition. +/// Return false if okay, true if error. +/// +/// DefInst ::= DEF ObjectName ObjectBody +/// +bool TGParser::ParseDef(MultiClass *CurMultiClass) { + SMLoc DefLoc = Lex.getLoc(); + assert(Lex.getCode() == tgtok::Def && "Unknown tok"); + Lex.Lex(); // Eat the 'def' token. + + // If the name of the def is an Id token, use that for the location. + // Otherwise, the name is more complex and we use the location of the 'def' + // token. + SMLoc NameLoc = Lex.getCode() == tgtok::Id ? Lex.getLoc() : DefLoc; + + // Parse ObjectName and make a record for it. + std::unique_ptr<Record> CurRec; + Init *Name = ParseObjectName(CurMultiClass); + if (!Name) + return true; + + if (isa<UnsetInit>(Name)) { + CurRec = + std::make_unique<Record>(Records.getNewAnonymousName(), DefLoc, Records, + /*Anonymous=*/true); + } else { + CurRec = std::make_unique<Record>(Name, NameLoc, Records); + } + + if (ParseObjectBody(CurRec.get())) + return true; + + return addEntry(std::move(CurRec)); +} + +/// ParseDefset - Parse a defset statement. +/// +/// Defset ::= DEFSET Type Id '=' '{' ObjectList '}' +/// +bool TGParser::ParseDefset() { + assert(Lex.getCode() == tgtok::Defset); + Lex.Lex(); // Eat the 'defset' token + + DefsetRecord Defset; + Defset.Loc = Lex.getLoc(); + RecTy *Type = ParseType(); + if (!Type) + return true; + if (!isa<ListRecTy>(Type)) + return Error(Defset.Loc, "expected list type"); + Defset.EltTy = cast<ListRecTy>(Type)->getElementType(); + + if (Lex.getCode() != tgtok::Id) + return TokError("expected identifier"); + StringInit *DeclName = StringInit::get(Records, Lex.getCurStrVal()); + if (Records.getGlobal(DeclName->getValue())) + return TokError("def or global variable of this name already exists"); + + if (Lex.Lex() != tgtok::equal) // Eat the identifier + return TokError("expected '='"); + if (Lex.Lex() != tgtok::l_brace) // Eat the '=' + return TokError("expected '{'"); + SMLoc BraceLoc = Lex.getLoc(); + Lex.Lex(); // Eat the '{' + + Defsets.push_back(&Defset); + bool Err = ParseObjectList(nullptr); + Defsets.pop_back(); + if (Err) + return true; + + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of defset"); + return Error(BraceLoc, "to match this '{'"); + } + + Records.addExtraGlobal(DeclName->getValue(), + ListInit::get(Defset.Elements, Defset.EltTy)); + return false; +} + +/// ParseDefvar - Parse a defvar statement. +/// +/// Defvar ::= DEFVAR Id '=' Value ';' +/// +bool TGParser::ParseDefvar() { + assert(Lex.getCode() == tgtok::Defvar); + Lex.Lex(); // Eat the 'defvar' token + + if (Lex.getCode() != tgtok::Id) + return TokError("expected identifier"); + StringInit *DeclName = StringInit::get(Records, Lex.getCurStrVal()); + if (CurLocalScope) { + if (CurLocalScope->varAlreadyDefined(DeclName->getValue())) + return TokError("local variable of this name already exists"); + } else { + if (Records.getGlobal(DeclName->getValue())) + return TokError("def or global variable of this name already exists"); + } + + Lex.Lex(); + if (!consume(tgtok::equal)) + return TokError("expected '='"); + + Init *Value = ParseValue(nullptr); + if (!Value) + return true; + + if (!consume(tgtok::semi)) + return TokError("expected ';'"); + + if (CurLocalScope) + CurLocalScope->addVar(DeclName->getValue(), Value); + else + Records.addExtraGlobal(DeclName->getValue(), Value); + + return false; +} + +/// ParseForeach - Parse a for statement. Return the record corresponding +/// to it. This returns true on error. +/// +/// Foreach ::= FOREACH Declaration IN '{ ObjectList '}' +/// Foreach ::= FOREACH Declaration IN Object +/// +bool TGParser::ParseForeach(MultiClass *CurMultiClass) { + SMLoc Loc = Lex.getLoc(); + assert(Lex.getCode() == tgtok::Foreach && "Unknown tok"); + Lex.Lex(); // Eat the 'for' token. + + // Make a temporary object to record items associated with the for + // loop. + Init *ListValue = nullptr; + VarInit *IterName = ParseForeachDeclaration(ListValue); + if (!IterName) + return TokError("expected declaration in for"); + + if (!consume(tgtok::In)) + return TokError("Unknown tok"); + + // Create a loop object and remember it. + Loops.push_back(std::make_unique<ForeachLoop>(Loc, IterName, ListValue)); + + // A foreach loop introduces a new scope for local variables. + TGLocalVarScope *ForeachScope = PushLocalScope(); + + if (Lex.getCode() != tgtok::l_brace) { + // FOREACH Declaration IN Object + if (ParseObject(CurMultiClass)) + return true; + } else { + SMLoc BraceLoc = Lex.getLoc(); + // Otherwise, this is a group foreach. + Lex.Lex(); // eat the '{'. + + // Parse the object list. + if (ParseObjectList(CurMultiClass)) + return true; + + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of foreach command"); + return Error(BraceLoc, "to match this '{'"); + } + } + + PopLocalScope(ForeachScope); + + // Resolve the loop or store it for later resolution. + std::unique_ptr<ForeachLoop> Loop = std::move(Loops.back()); + Loops.pop_back(); + + return addEntry(std::move(Loop)); +} + +/// ParseIf - Parse an if statement. +/// +/// If ::= IF Value THEN IfBody +/// If ::= IF Value THEN IfBody ELSE IfBody +/// +bool TGParser::ParseIf(MultiClass *CurMultiClass) { + SMLoc Loc = Lex.getLoc(); + assert(Lex.getCode() == tgtok::If && "Unknown tok"); + Lex.Lex(); // Eat the 'if' token. + + // Make a temporary object to record items associated with the for + // loop. + Init *Condition = ParseValue(nullptr); + if (!Condition) + return true; + + if (!consume(tgtok::Then)) + return TokError("Unknown tok"); + + // We have to be able to save if statements to execute later, and they have + // to live on the same stack as foreach loops. The simplest implementation + // technique is to convert each 'then' or 'else' clause *into* a foreach + // loop, over a list of length 0 or 1 depending on the condition, and with no + // iteration variable being assigned. + + ListInit *EmptyList = ListInit::get({}, BitRecTy::get(Records)); + ListInit *SingletonList = + ListInit::get({BitInit::get(Records, true)}, BitRecTy::get(Records)); + RecTy *BitListTy = ListRecTy::get(BitRecTy::get(Records)); + + // The foreach containing the then-clause selects SingletonList if + // the condition is true. + Init *ThenClauseList = + TernOpInit::get(TernOpInit::IF, Condition, SingletonList, EmptyList, + BitListTy) + ->Fold(nullptr); + Loops.push_back(std::make_unique<ForeachLoop>(Loc, nullptr, ThenClauseList)); + + if (ParseIfBody(CurMultiClass, "then")) + return true; + + std::unique_ptr<ForeachLoop> Loop = std::move(Loops.back()); + Loops.pop_back(); + + if (addEntry(std::move(Loop))) + return true; + + // Now look for an optional else clause. The if-else syntax has the usual + // dangling-else ambiguity, and by greedily matching an else here if we can, + // we implement the usual resolution of pairing with the innermost unmatched + // if. + if (consume(tgtok::ElseKW)) { + // The foreach containing the else-clause uses the same pair of lists as + // above, but this time, selects SingletonList if the condition is *false*. + Init *ElseClauseList = + TernOpInit::get(TernOpInit::IF, Condition, EmptyList, SingletonList, + BitListTy) + ->Fold(nullptr); + Loops.push_back( + std::make_unique<ForeachLoop>(Loc, nullptr, ElseClauseList)); + + if (ParseIfBody(CurMultiClass, "else")) + return true; + + Loop = std::move(Loops.back()); + Loops.pop_back(); + + if (addEntry(std::move(Loop))) + return true; + } + + return false; +} + +/// ParseIfBody - Parse the then-clause or else-clause of an if statement. +/// +/// IfBody ::= Object +/// IfBody ::= '{' ObjectList '}' +/// +bool TGParser::ParseIfBody(MultiClass *CurMultiClass, StringRef Kind) { + TGLocalVarScope *BodyScope = PushLocalScope(); + + if (Lex.getCode() != tgtok::l_brace) { + // A single object. + if (ParseObject(CurMultiClass)) + return true; + } else { + SMLoc BraceLoc = Lex.getLoc(); + // A braced block. + Lex.Lex(); // eat the '{'. + + // Parse the object list. + if (ParseObjectList(CurMultiClass)) + return true; + + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of '" + Kind + "' clause"); + return Error(BraceLoc, "to match this '{'"); + } + } + + PopLocalScope(BodyScope); + 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 (CurRec) + CurRec->addAssertion(ConditionLoc, Condition, Message); + else + addEntry(std::make_unique<Record::AssertionInfo>(ConditionLoc, Condition, + Message)); + return false; +} + +/// ParseClass - Parse a tblgen class definition. +/// +/// ClassInst ::= CLASS ID TemplateArgList? ObjectBody +/// +bool TGParser::ParseClass() { + assert(Lex.getCode() == tgtok::Class && "Unexpected token!"); + Lex.Lex(); + + if (Lex.getCode() != tgtok::Id) + return TokError("expected class name after 'class' keyword"); + + Record *CurRec = Records.getClass(Lex.getCurStrVal()); + if (CurRec) { + // If the body was previously defined, this is an error. + if (!CurRec->getValues().empty() || + !CurRec->getSuperClasses().empty() || + !CurRec->getTemplateArgs().empty()) + return TokError("Class '" + CurRec->getNameInitAsString() + + "' already defined"); + + CurRec->updateClassLoc(Lex.getLoc()); + } else { + // If this is the first reference to this class, create and add it. + auto NewRec = + std::make_unique<Record>(Lex.getCurStrVal(), Lex.getLoc(), Records, + /*Class=*/true); + CurRec = NewRec.get(); + Records.addClass(std::move(NewRec)); + } + Lex.Lex(); // eat the name. + + // If there are template args, parse them. + if (Lex.getCode() == tgtok::less) + if (ParseTemplateArgList(CurRec)) + return true; + + if (ParseObjectBody(CurRec)) + return true; + + if (!NoWarnOnUnusedTemplateArgs) + CurRec->checkUnusedTemplateArgs(); + return false; +} + +/// ParseLetList - Parse a non-empty list of assignment expressions into a list +/// of LetRecords. +/// +/// LetList ::= LetItem (',' LetItem)* +/// LetItem ::= ID OptionalRangeList '=' Value +/// +void TGParser::ParseLetList(SmallVectorImpl<LetRecord> &Result) { + do { + if (Lex.getCode() != tgtok::Id) { + TokError("expected identifier in let definition"); + Result.clear(); + return; + } + + StringInit *Name = StringInit::get(Records, Lex.getCurStrVal()); + SMLoc NameLoc = Lex.getLoc(); + Lex.Lex(); // Eat the identifier. + + // Check for an optional RangeList. + SmallVector<unsigned, 16> Bits; + if (ParseOptionalRangeList(Bits)) { + Result.clear(); + return; + } + std::reverse(Bits.begin(), Bits.end()); + + if (!consume(tgtok::equal)) { + TokError("expected '=' in let expression"); + Result.clear(); + return; + } + + Init *Val = ParseValue(nullptr); + if (!Val) { + Result.clear(); + return; + } + + // Now that we have everything, add the record. + Result.emplace_back(Name, Bits, Val, NameLoc); + } while (consume(tgtok::comma)); +} + +/// ParseTopLevelLet - Parse a 'let' at top level. This can be a couple of +/// different related productions. This works inside multiclasses too. +/// +/// Object ::= LET LetList IN '{' ObjectList '}' +/// Object ::= LET LetList IN Object +/// +bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) { + assert(Lex.getCode() == tgtok::Let && "Unexpected token"); + Lex.Lex(); + + // Add this entry to the let stack. + SmallVector<LetRecord, 8> LetInfo; + ParseLetList(LetInfo); + if (LetInfo.empty()) return true; + LetStack.push_back(std::move(LetInfo)); + + if (!consume(tgtok::In)) + return TokError("expected 'in' at end of top-level 'let'"); + + TGLocalVarScope *LetScope = PushLocalScope(); + + // If this is a scalar let, just handle it now + if (Lex.getCode() != tgtok::l_brace) { + // LET LetList IN Object + if (ParseObject(CurMultiClass)) + return true; + } else { // Object ::= LETCommand '{' ObjectList '}' + SMLoc BraceLoc = Lex.getLoc(); + // Otherwise, this is a group let. + Lex.Lex(); // eat the '{'. + + // Parse the object list. + if (ParseObjectList(CurMultiClass)) + return true; + + if (!consume(tgtok::r_brace)) { + TokError("expected '}' at end of top level let command"); + return Error(BraceLoc, "to match this '{'"); + } + } + + PopLocalScope(LetScope); + + // Outside this let scope, this let block is not active. + LetStack.pop_back(); + return false; +} + +/// ParseMultiClass - Parse a multiclass definition. +/// +/// MultiClassInst ::= MULTICLASS ID TemplateArgList? +/// ':' BaseMultiClassList '{' MultiClassObject+ '}' +/// MultiClassObject ::= Assert +/// MultiClassObject ::= DefInst +/// MultiClassObject ::= DefMInst +/// MultiClassObject ::= Defvar +/// MultiClassObject ::= Foreach +/// MultiClassObject ::= If +/// MultiClassObject ::= LETCommand '{' ObjectList '}' +/// MultiClassObject ::= LETCommand Object +/// +bool TGParser::ParseMultiClass() { + assert(Lex.getCode() == tgtok::MultiClass && "Unexpected token"); + Lex.Lex(); // Eat the multiclass token. + + if (Lex.getCode() != tgtok::Id) + return TokError("expected identifier after multiclass for name"); + std::string Name = Lex.getCurStrVal(); + + auto Result = + MultiClasses.insert(std::make_pair(Name, + std::make_unique<MultiClass>(Name, Lex.getLoc(),Records))); + + if (!Result.second) + return TokError("multiclass '" + Name + "' already defined"); + + CurMultiClass = Result.first->second.get(); + Lex.Lex(); // Eat the identifier. + + // If there are template args, parse them. + if (Lex.getCode() == tgtok::less) + if (ParseTemplateArgList(nullptr)) + return true; + + bool inherits = false; + + // If there are submulticlasses, parse them. + if (consume(tgtok::colon)) { + inherits = true; + + // Read all of the submulticlasses. + SubMultiClassReference SubMultiClass = + ParseSubMultiClassReference(CurMultiClass); + while (true) { + // Check for error. + if (!SubMultiClass.MC) return true; + + // Add it. + if (AddSubMultiClass(CurMultiClass, SubMultiClass)) + return true; + + if (!consume(tgtok::comma)) + break; + SubMultiClass = ParseSubMultiClassReference(CurMultiClass); + } + } + + if (Lex.getCode() != tgtok::l_brace) { + if (!inherits) + return TokError("expected '{' in multiclass definition"); + if (!consume(tgtok::semi)) + return TokError("expected ';' in multiclass definition"); + } else { + if (Lex.Lex() == tgtok::r_brace) // eat the '{'. + return TokError("multiclass must contain at least one def"); + + // A multiclass body introduces a new scope for local variables. + TGLocalVarScope *MulticlassScope = PushLocalScope(); + + while (Lex.getCode() != tgtok::r_brace) { + switch (Lex.getCode()) { + default: + return TokError("expected 'assert', 'def', 'defm', 'defvar', " + "'foreach', 'if', or 'let' in multiclass body"); + + case tgtok::Assert: + case tgtok::Def: + case tgtok::Defm: + case tgtok::Defvar: + case tgtok::Foreach: + case tgtok::If: + case tgtok::Let: + if (ParseObject(CurMultiClass)) + return true; + break; + } + } + 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); + } + + if (!NoWarnOnUnusedTemplateArgs) + CurMultiClass->Rec.checkUnusedTemplateArgs(); + + CurMultiClass = nullptr; + return false; +} + +/// ParseDefm - Parse the instantiation of a multiclass. +/// +/// DefMInst ::= DEFM ID ':' DefmSubClassRef ';' +/// +bool TGParser::ParseDefm(MultiClass *CurMultiClass) { + assert(Lex.getCode() == tgtok::Defm && "Unexpected token!"); + Lex.Lex(); // eat the defm + + Init *DefmName = ParseObjectName(CurMultiClass); + if (!DefmName) + return true; + if (isa<UnsetInit>(DefmName)) { + DefmName = Records.getNewAnonymousName(); + if (CurMultiClass) + DefmName = BinOpInit::getStrConcat( + VarInit::get(QualifiedNameOfImplicitName(CurMultiClass), + StringRecTy::get(Records)), + DefmName); + } + + if (Lex.getCode() != tgtok::colon) + return TokError("expected ':' after defm identifier"); + + // Keep track of the new generated record definitions. + std::vector<RecordsEntry> NewEntries; + + // This record also inherits from a regular class (non-multiclass)? + bool InheritFromClass = false; + + // eat the colon. + Lex.Lex(); + + SMLoc SubClassLoc = Lex.getLoc(); + SubClassReference Ref = ParseSubClassReference(nullptr, true); + + while (true) { + if (!Ref.Rec) return true; + + // To instantiate a multiclass, we get the multiclass and then loop + // through its template argument names. Substs contains a substitution + // value for each argument, either the value specified or the default. + // Then we can resolve the template arguments. + MultiClass *MC = MultiClasses[std::string(Ref.Rec->getName())].get(); + assert(MC && "Didn't lookup multiclass correctly?"); + + ArrayRef<Init *> TemplateVals = Ref.TemplateArgs; + ArrayRef<Init *> TArgs = MC->Rec.getTemplateArgs(); + SubstStack Substs; + + for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { + if (i < TemplateVals.size()) { + Substs.emplace_back(TArgs[i], TemplateVals[i]); + } else { + Init *Default = MC->Rec.getValue(TArgs[i])->getValue(); + if (!Default->isComplete()) + return Error(SubClassLoc, + "value not specified for template argument '" + + TArgs[i]->getAsUnquotedString() + "' (#" + + Twine(i) + ") of multiclass '" + + MC->Rec.getNameInitAsString() + "'"); + Substs.emplace_back(TArgs[i], Default); + } + } + + Substs.emplace_back(QualifiedNameOfImplicitName(MC), DefmName); + + if (resolve(MC->Entries, Substs, !CurMultiClass && Loops.empty(), + &NewEntries, &SubClassLoc)) + return true; + + if (!consume(tgtok::comma)) + break; + + if (Lex.getCode() != tgtok::Id) + return TokError("expected identifier"); + + SubClassLoc = Lex.getLoc(); + + // A defm can inherit from regular classes (non-multiclasses) as + // long as they come in the end of the inheritance list. + InheritFromClass = (Records.getClass(Lex.getCurStrVal()) != nullptr); + + if (InheritFromClass) + break; + + Ref = ParseSubClassReference(nullptr, true); + } + + if (InheritFromClass) { + // Process all the classes to inherit as if they were part of a + // regular 'def' and inherit all record values. + SubClassReference SubClass = ParseSubClassReference(nullptr, false); + while (true) { + // Check for error. + if (!SubClass.Rec) return true; + + // Get the expanded definition prototypes and teach them about + // the record values the current class to inherit has + for (auto &E : NewEntries) { + // Add it. + if (AddSubClass(E, SubClass)) + return true; + } + + if (!consume(tgtok::comma)) + break; + SubClass = ParseSubClassReference(nullptr, false); + } + } + + for (auto &E : NewEntries) { + if (ApplyLetStack(E)) + return true; + + addEntry(std::move(E)); + } + + if (!consume(tgtok::semi)) + return TokError("expected ';' at end of defm"); + + return false; +} + +/// ParseObject +/// Object ::= ClassInst +/// Object ::= DefInst +/// Object ::= MultiClassInst +/// Object ::= DefMInst +/// Object ::= LETCommand '{' ObjectList '}' +/// Object ::= LETCommand Object +/// Object ::= Defset +/// Object ::= Defvar +/// Object ::= Assert +bool TGParser::ParseObject(MultiClass *MC) { + switch (Lex.getCode()) { + default: + return TokError( + "Expected assert, class, def, defm, defset, foreach, if, or let"); + case tgtok::Assert: return ParseAssert(MC); + 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::Class: + if (MC) + return TokError("class is not allowed inside multiclass"); + if (!Loops.empty()) + return TokError("class is not allowed inside foreach loop"); + return ParseClass(); + case tgtok::MultiClass: + if (!Loops.empty()) + return TokError("multiclass is not allowed inside foreach loop"); + return ParseMultiClass(); + } +} + +/// ParseObjectList +/// ObjectList :== Object* +bool TGParser::ParseObjectList(MultiClass *MC) { + while (isObjectStart(Lex.getCode())) { + if (ParseObject(MC)) + return true; + } + return false; +} + +bool TGParser::ParseFile() { + Lex.Lex(); // Prime the lexer. + if (ParseObjectList()) return true; + + // If we have unread input at the end of the file, report it. + if (Lex.getCode() == tgtok::Eof) + return false; + + return TokError("Unexpected token at top level"); +} + +// Check the types of the template argument values for a class +// inheritance, multiclass invocation, or anonymous class invocation. +// If necessary, replace an argument with a cast to the required type. +// The argument count has already been checked. +bool TGParser::CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values, + SMLoc Loc, Record *ArgsRec) { + + ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs(); + + for (unsigned I = 0, E = Values.size(); I < E; ++I) { + RecordVal *Arg = ArgsRec->getValue(TArgs[I]); + RecTy *ArgType = Arg->getType(); + auto *Value = Values[I]; + + if (TypedInit *ArgValue = dyn_cast<TypedInit>(Value)) { + auto *CastValue = ArgValue->getCastTo(ArgType); + if (CastValue) { + assert((!isa<TypedInit>(CastValue) || + cast<TypedInit>(CastValue)->getType()->typeIsA(ArgType)) && + "result of template arg value cast has wrong type"); + Values[I] = CastValue; + } else { + PrintFatalError(Loc, + "Value specified for template argument '" + + Arg->getNameInitAsString() + "' (#" + Twine(I) + + ") is of type " + ArgValue->getType()->getAsString() + + "; expected type " + ArgType->getAsString() + ": " + + ArgValue->getAsString()); + } + } + } + + return false; +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void RecordsEntry::dump() const { + if (Loop) + Loop->dump(); + if (Rec) + Rec->dump(); +} + +LLVM_DUMP_METHOD void ForeachLoop::dump() const { + errs() << "foreach " << IterVar->getAsString() << " = " + << ListValue->getAsString() << " in {\n"; + + for (const auto &E : Entries) + E.dump(); + + errs() << "}\n"; +} + +LLVM_DUMP_METHOD void MultiClass::dump() const { + errs() << "Record:\n"; + Rec.dump(); + + errs() << "Defs:\n"; + for (const auto &E : Entries) + E.dump(); +} +#endif diff --git a/contrib/llvm-project/llvm/lib/TableGen/TGParser.h b/contrib/llvm-project/llvm/lib/TableGen/TGParser.h new file mode 100644 index 000000000000..f7271a5e79c3 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/TGParser.h @@ -0,0 +1,289 @@ +//===- TGParser.h - Parser for TableGen Files -------------------*- 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 class represents the Parser for tablegen files. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TABLEGEN_TGPARSER_H +#define LLVM_LIB_TABLEGEN_TGPARSER_H + +#include "TGLexer.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <map> + +namespace llvm { + class SourceMgr; + class Twine; + struct ForeachLoop; + struct MultiClass; + struct SubClassReference; + struct SubMultiClassReference; + + struct LetRecord { + StringInit *Name; + std::vector<unsigned> Bits; + Init *Value; + SMLoc Loc; + LetRecord(StringInit *N, ArrayRef<unsigned> B, Init *V, SMLoc L) + : Name(N), Bits(B), Value(V), Loc(L) { + } + }; + + /// RecordsEntry - Holds exactly one of a Record, ForeachLoop, or + /// AssertionInfo. + struct RecordsEntry { + std::unique_ptr<Record> Rec; + std::unique_ptr<ForeachLoop> Loop; + std::unique_ptr<Record::AssertionInfo> Assertion; + + void dump() const; + + RecordsEntry() = default; + RecordsEntry(std::unique_ptr<Record> Rec) : Rec(std::move(Rec)) {} + RecordsEntry(std::unique_ptr<ForeachLoop> Loop) + : Loop(std::move(Loop)) {} + RecordsEntry(std::unique_ptr<Record::AssertionInfo> Assertion) + : Assertion(std::move(Assertion)) {} + }; + + /// ForeachLoop - Record the iteration state associated with a for loop. + /// This is used to instantiate items in the loop body. + /// + /// IterVar is allowed to be null, in which case no iteration variable is + /// defined in the loop at all. (This happens when a ForeachLoop is + /// constructed by desugaring an if statement.) + struct ForeachLoop { + SMLoc Loc; + VarInit *IterVar; + Init *ListValue; + std::vector<RecordsEntry> Entries; + + void dump() const; + + ForeachLoop(SMLoc Loc, VarInit *IVar, Init *LValue) + : Loc(Loc), IterVar(IVar), ListValue(LValue) {} + }; + + struct DefsetRecord { + SMLoc Loc; + RecTy *EltTy = nullptr; + SmallVector<Init *, 16> Elements; + }; + +class TGLocalVarScope { + // A scope to hold local variable definitions from defvar. + std::map<std::string, Init *, std::less<>> vars; + std::unique_ptr<TGLocalVarScope> parent; + +public: + TGLocalVarScope() = default; + TGLocalVarScope(std::unique_ptr<TGLocalVarScope> parent) + : parent(std::move(parent)) {} + + std::unique_ptr<TGLocalVarScope> extractParent() { + // This is expected to be called just before we are destructed, so + // it doesn't much matter what state we leave 'parent' in. + return std::move(parent); + } + + Init *getVar(StringRef Name) const { + auto It = vars.find(Name); + if (It != vars.end()) + return It->second; + if (parent) + return parent->getVar(Name); + return nullptr; + } + + bool varAlreadyDefined(StringRef Name) const { + // When we check whether a variable is already defined, for the purpose of + // reporting an error on redefinition, we don't look up to the parent + // scope, because it's all right to shadow an outer definition with an + // inner one. + return vars.find(Name) != vars.end(); + } + + void addVar(StringRef Name, Init *I) { + bool Ins = vars.insert(std::make_pair(std::string(Name), I)).second; + (void)Ins; + assert(Ins && "Local variable already exists"); + } +}; + +struct MultiClass { + Record Rec; // Placeholder for template args and Name. + std::vector<RecordsEntry> Entries; + + void dump() const; + + MultiClass(StringRef Name, SMLoc Loc, RecordKeeper &Records) : + Rec(Name, Loc, Records) {} +}; + +class TGParser { + TGLexer Lex; + std::vector<SmallVector<LetRecord, 4>> LetStack; + std::map<std::string, std::unique_ptr<MultiClass>> MultiClasses; + + /// Loops - Keep track of any foreach loops we are within. + /// + std::vector<std::unique_ptr<ForeachLoop>> Loops; + + SmallVector<DefsetRecord *, 2> Defsets; + + /// CurMultiClass - If we are parsing a 'multiclass' definition, this is the + /// current value. + MultiClass *CurMultiClass; + + /// CurLocalScope - Innermost of the current nested scopes for 'defvar' local + /// variables. + std::unique_ptr<TGLocalVarScope> CurLocalScope; + + // Record tracker + RecordKeeper &Records; + + // A "named boolean" indicating how to parse identifiers. Usually + // identifiers map to some existing object but in special cases + // (e.g. parsing def names) no such object exists yet because we are + // in the middle of creating in. For those situations, allow the + // parser to ignore missing object errors. + enum IDParseMode { + ParseValueMode, // We are parsing a value we expect to look up. + ParseNameMode, // We are parsing a name of an object that does not yet + // exist. + }; + + bool NoWarnOnUnusedTemplateArgs = false; + bool TrackReferenceLocs = false; + +public: + TGParser(SourceMgr &SM, ArrayRef<std::string> Macros, RecordKeeper &records, + const bool NoWarnOnUnusedTemplateArgs = false, + const bool TrackReferenceLocs = false) + : Lex(SM, Macros), CurMultiClass(nullptr), Records(records), + NoWarnOnUnusedTemplateArgs(NoWarnOnUnusedTemplateArgs), + TrackReferenceLocs(TrackReferenceLocs) {} + + /// ParseFile - Main entrypoint for parsing a tblgen file. These parser + /// routines return true on error, or false on success. + bool ParseFile(); + + bool Error(SMLoc L, const Twine &Msg) const { + PrintError(L, Msg); + return true; + } + bool TokError(const Twine &Msg) const { + return Error(Lex.getLoc(), Msg); + } + const TGLexer::DependenciesSetTy &getDependencies() const { + return Lex.getDependencies(); + } + + TGLocalVarScope *PushLocalScope() { + CurLocalScope = std::make_unique<TGLocalVarScope>(std::move(CurLocalScope)); + // Returns a pointer to the new scope, so that the caller can pass it back + // to PopLocalScope which will check by assertion that the pushes and pops + // match up properly. + return CurLocalScope.get(); + } + void PopLocalScope(TGLocalVarScope *ExpectedStackTop) { + assert(ExpectedStackTop == CurLocalScope.get() && + "Mismatched pushes and pops of local variable scopes"); + CurLocalScope = CurLocalScope->extractParent(); + } + +private: // Semantic analysis methods. + bool AddValue(Record *TheRec, SMLoc Loc, const RecordVal &RV); + /// Set the value of a RecordVal within the given record. If `OverrideDefLoc` + /// is set, the provided location overrides any existing location of the + /// RecordVal. + bool SetValue(Record *TheRec, SMLoc Loc, Init *ValName, + ArrayRef<unsigned> BitList, Init *V, + bool AllowSelfAssignment = false, bool OverrideDefLoc = true); + bool AddSubClass(Record *Rec, SubClassReference &SubClass); + bool AddSubClass(RecordsEntry &Entry, SubClassReference &SubClass); + bool AddSubMultiClass(MultiClass *CurMC, + SubMultiClassReference &SubMultiClass); + + using SubstStack = SmallVector<std::pair<Init *, Init *>, 8>; + + bool addEntry(RecordsEntry E); + bool resolve(const ForeachLoop &Loop, SubstStack &Stack, bool Final, + std::vector<RecordsEntry> *Dest, SMLoc *Loc = nullptr); + bool resolve(const std::vector<RecordsEntry> &Source, SubstStack &Substs, + bool Final, std::vector<RecordsEntry> *Dest, + SMLoc *Loc = nullptr); + bool addDefOne(std::unique_ptr<Record> Rec); + +private: // Parser methods. + bool consume(tgtok::TokKind K); + bool ParseObjectList(MultiClass *MC = nullptr); + bool ParseObject(MultiClass *MC); + bool ParseClass(); + bool ParseMultiClass(); + bool ParseDefm(MultiClass *CurMultiClass); + bool ParseDef(MultiClass *CurMultiClass); + bool ParseDefset(); + bool ParseDefvar(); + bool ParseForeach(MultiClass *CurMultiClass); + bool ParseIf(MultiClass *CurMultiClass); + bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind); + bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec = nullptr); + bool ParseTopLevelLet(MultiClass *CurMultiClass); + void ParseLetList(SmallVectorImpl<LetRecord> &Result); + + bool ParseObjectBody(Record *CurRec); + bool ParseBody(Record *CurRec); + bool ParseBodyItem(Record *CurRec); + + bool ParseTemplateArgList(Record *CurRec); + Init *ParseDeclaration(Record *CurRec, bool ParsingTemplateArgs); + VarInit *ParseForeachDeclaration(Init *&ForeachListValue); + + SubClassReference ParseSubClassReference(Record *CurRec, bool isDefm); + SubMultiClassReference ParseSubMultiClassReference(MultiClass *CurMC); + + Init *ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc, + IDParseMode Mode = ParseValueMode); + Init *ParseSimpleValue(Record *CurRec, RecTy *ItemType = nullptr, + IDParseMode Mode = ParseValueMode); + Init *ParseValue(Record *CurRec, RecTy *ItemType = nullptr, + IDParseMode Mode = ParseValueMode); + void ParseValueList(SmallVectorImpl<llvm::Init*> &Result, + Record *CurRec, RecTy *ItemType = nullptr); + bool ParseTemplateArgValueList(SmallVectorImpl<llvm::Init *> &Result, + Record *CurRec, Record *ArgsRec); + void ParseDagArgList( + SmallVectorImpl<std::pair<llvm::Init*, StringInit*>> &Result, + Record *CurRec); + bool ParseOptionalRangeList(SmallVectorImpl<unsigned> &Ranges); + bool ParseOptionalBitList(SmallVectorImpl<unsigned> &Ranges); + void ParseRangeList(SmallVectorImpl<unsigned> &Result); + bool ParseRangePiece(SmallVectorImpl<unsigned> &Ranges, + TypedInit *FirstItem = nullptr); + RecTy *ParseType(); + Init *ParseOperation(Record *CurRec, RecTy *ItemType); + Init *ParseOperationSubstr(Record *CurRec, RecTy *ItemType); + Init *ParseOperationFind(Record *CurRec, RecTy *ItemType); + Init *ParseOperationForEachFilter(Record *CurRec, RecTy *ItemType); + Init *ParseOperationCond(Record *CurRec, RecTy *ItemType); + RecTy *ParseOperatorType(); + Init *ParseObjectName(MultiClass *CurMultiClass); + Record *ParseClassID(); + MultiClass *ParseMultiClassID(); + bool ApplyLetStack(Record *CurRec); + bool ApplyLetStack(RecordsEntry &Entry); + bool CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values, + SMLoc Loc, Record *ArgsRec); +}; + +} // end namespace llvm + +#endif diff --git a/contrib/llvm-project/llvm/lib/TableGen/TableGenBackend.cpp b/contrib/llvm-project/llvm/lib/TableGen/TableGenBackend.cpp new file mode 100644 index 000000000000..252f126d2d00 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/TableGenBackend.cpp @@ -0,0 +1,53 @@ +//===- TableGenBackend.cpp - Utilities for TableGen Backends ----*- 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 file provides useful services for TableGen backends... +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/TableGenBackend.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +using namespace llvm; + +const size_t MAX_LINE_LEN = 80U; + +static void printLine(raw_ostream &OS, const Twine &Prefix, char Fill, + StringRef Suffix) { + size_t Pos = (size_t)OS.tell(); + assert((Prefix.str().size() + Suffix.size() <= MAX_LINE_LEN) && + "header line exceeds max limit"); + OS << Prefix; + for (size_t i = (size_t)OS.tell() - Pos, e = MAX_LINE_LEN - Suffix.size(); + i < e; ++i) + OS << Fill; + OS << Suffix << '\n'; +} + +void llvm::emitSourceFileHeader(StringRef Desc, raw_ostream &OS) { + printLine(OS, "/*===- TableGen'erated file ", '-', "*- C++ -*-===*\\"); + StringRef Prefix("|* "); + StringRef Suffix(" *|"); + printLine(OS, Prefix, ' ', Suffix); + size_t PSLen = Prefix.size() + Suffix.size(); + assert(PSLen < MAX_LINE_LEN); + size_t Pos = 0U; + do { + size_t Length = std::min(Desc.size() - Pos, MAX_LINE_LEN - PSLen); + printLine(OS, Prefix + Desc.substr(Pos, Length), ' ', Suffix); + Pos += Length; + } while (Pos < Desc.size()); + printLine(OS, Prefix, ' ', Suffix); + printLine(OS, Prefix + "Automatically generated file, do not edit!", ' ', + Suffix); + printLine(OS, Prefix, ' ', Suffix); + printLine(OS, "\\*===", '-', "===*/"); + OS << '\n'; +} diff --git a/contrib/llvm-project/llvm/lib/TableGen/TableGenBackendSkeleton.cpp b/contrib/llvm-project/llvm/lib/TableGen/TableGenBackendSkeleton.cpp new file mode 100644 index 000000000000..0ba00c8d8ab1 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TableGen/TableGenBackendSkeleton.cpp @@ -0,0 +1,59 @@ +//===- 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/DenseMapInfo.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/TableGen/TableGenBackend.h" + +#define DEBUG_TYPE "skeleton-emitter" + +namespace llvm { +class RecordKeeper; +class raw_ostream; +} // namespace llvm + +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 |
