diff options
Diffstat (limited to 'llvm/lib/TableGen')
-rw-r--r-- | llvm/lib/TableGen/Error.cpp | 82 | ||||
-rw-r--r-- | llvm/lib/TableGen/JSONBackend.cpp | 188 | ||||
-rw-r--r-- | llvm/lib/TableGen/Main.cpp | 142 | ||||
-rw-r--r-- | llvm/lib/TableGen/Record.cpp | 2422 | ||||
-rw-r--r-- | llvm/lib/TableGen/SetTheory.cpp | 333 | ||||
-rw-r--r-- | llvm/lib/TableGen/StringMatcher.cpp | 156 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGLexer.cpp | 1021 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGLexer.h | 373 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGParser.cpp | 3243 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGParser.h | 208 | ||||
-rw-r--r-- | llvm/lib/TableGen/TableGenBackend.cpp | 52 |
11 files changed, 8220 insertions, 0 deletions
diff --git a/llvm/lib/TableGen/Error.cpp b/llvm/lib/TableGen/Error.cpp new file mode 100644 index 000000000000..54b063cb4f8d --- /dev/null +++ b/llvm/lib/TableGen/Error.cpp @@ -0,0 +1,82 @@ +//===- 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/TableGen/Error.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.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"); +} + +void PrintNote(const Twine &Msg) { WithColor::note() << Msg << "\n"; } + +void PrintNote(ArrayRef<SMLoc> NoteLoc, const Twine &Msg) { + PrintMessage(NoteLoc, SourceMgr::DK_Note, Msg); +} + +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); +} + +void PrintWarning(const Twine &Msg) { WithColor::warning() << 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); +} + +void PrintError(const Twine &Msg) { WithColor::error() << Msg << "\n"; } + +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); +} + +} // end namespace llvm diff --git a/llvm/lib/TableGen/JSONBackend.cpp b/llvm/lib/TableGen/JSONBackend.cpp new file mode 100644 index 000000000000..196644cda667 --- /dev/null +++ b/llvm/lib/TableGen/JSONBackend.cpp @@ -0,0 +1,188 @@ +//===- 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/BitVector.h" +#include "llvm/Support/Debug.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include "llvm/Support/JSON.h" + +#define DEBUG_TYPE "json-emitter" + +using namespace llvm; + +namespace { + +class JSONEmitter { +private: + RecordKeeper &Records; + + json::Value translateInit(const Init &I); + json::Array listSuperclasses(const Record &R); + +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 *Code = dyn_cast<CodeInit>(&I)) { + return Code->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()) { + auto &Name = C.second->getNameInitAsString(); + (void)instance_lists[Name]; + } + + // Main iteration over the defs. + for (const auto &D : Records.getDefs()) { + 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.getPrefix()) + 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/llvm/lib/TableGen/Main.cpp b/llvm/lib/TableGen/Main.cpp new file mode 100644 index 000000000000..48ded6c45a46 --- /dev/null +++ b/llvm/lib/TableGen/Main.cpp @@ -0,0 +1,142 @@ +//===- 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/ADT/StringExtras.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 <cstdio> +#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 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_None); + if (EC) + return reportError(argv0, "error opening " + DependFilename + ":" + + EC.message() + "\n"); + DepOut.os() << OutputFilename << ":"; + for (const auto &Dep : Parser.getDependencies()) { + DepOut.os() << ' ' << Dep.first; + } + DepOut.os() << "\n"; + DepOut.keep(); + return 0; +} + +int llvm::TableGenMain(char *argv0, TableGenMainFn *MainFn) { + RecordKeeper Records; + + // Parse the input file. + ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = + MemoryBuffer::getFileOrSTDIN(InputFilename); + if (std::error_code EC = FileOrErr.getError()) + return reportError(argv0, "Could not open input file '" + InputFilename + + "': " + EC.message() + "\n"); + + // 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); + + if (Parser.ParseFile()) + return 1; + + // Write output to memory. + std::string OutString; + raw_string_ostream Out(OutString); + if (MainFn(Out, Records)) + 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; + } + + if (WriteIfChanged) { + // Only updates the real output file if there are any differences. + // This prevents recompilation of all the files depending on it if there + // aren't any. + if (auto ExistingOrErr = MemoryBuffer::getFile(OutputFilename)) + if (std::move(ExistingOrErr.get())->getBuffer() == Out.str()) + return 0; + } + + std::error_code EC; + ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None); + if (EC) + return reportError(argv0, "error opening " + OutputFilename + ":" + + EC.message() + "\n"); + OutFile.os() << Out.str(); + + if (ErrorsPrinted > 0) + return reportError(argv0, Twine(ErrorsPrinted) + " errors.\n"); + + // Declare success. + OutFile.keep(); + return 0; +} diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp new file mode 100644 index 000000000000..835ef8c7141b --- /dev/null +++ b/llvm/lib/TableGen/Record.cpp @@ -0,0 +1,2422 @@ +//===- 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/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/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.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/SMLoc.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <cassert> +#include <cstdint> +#include <memory> +#include <map> +#include <string> +#include <utility> +#include <vector> + +using namespace llvm; + +#define DEBUG_TYPE "tblgen-records" + +static BumpPtrAllocator Allocator; + +STATISTIC(CodeInitsConstructed, + "The total number of unique CodeInits constructed"); + +//===----------------------------------------------------------------------===// +// Type implementations +//===----------------------------------------------------------------------===// + +BitRecTy BitRecTy::Shared; +CodeRecTy CodeRecTy::Shared; +IntRecTy IntRecTy::Shared; +StringRecTy StringRecTy::Shared; +DagRecTy DagRecTy::Shared; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void RecTy::dump() const { print(errs()); } +#endif + +ListRecTy *RecTy::getListTy() { + if (!ListTy) + ListTy = new(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; } + +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(unsigned Sz) { + static std::vector<BitsRecTy*> Shared; + if (Sz >= Shared.size()) + Shared.resize(Sz + 1); + BitsRecTy *&Ty = Shared[Sz]; + if (!Ty) + Ty = new(Allocator) BitsRecTy(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; +} + +bool IntRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + RecTyKind kind = RHS->getRecTyKind(); + return kind==BitRecTyKind || kind==BitsRecTyKind || kind==IntRecTyKind; +} + +bool CodeRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + RecTyKind Kind = RHS->getRecTyKind(); + return Kind == CodeRecTyKind || Kind == StringRecTyKind; +} + +std::string StringRecTy::getAsString() const { + return "string"; +} + +bool StringRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + RecTyKind Kind = RHS->getRecTyKind(); + return Kind == StringRecTyKind || Kind == CodeRecTyKind; +} + +std::string ListRecTy::getAsString() const { + return "list<" + Ty->getAsString() + ">"; +} + +bool ListRecTy::typeIsConvertibleTo(const RecTy *RHS) const { + if (const auto *ListTy = dyn_cast<ListRecTy>(RHS)) + return Ty->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; +} + +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(ArrayRef<Record *> UnsortedClasses) { + if (UnsortedClasses.empty()) { + static RecordRecTy AnyRecord(0); + return &AnyRecord; + } + + FoldingSet<RecordRecTy> &ThePool = + UnsortedClasses[0]->getRecords().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 = Allocator.Allocate(totalSizeToAlloc<Record *>(Classes.size()), + alignof(RecordRecTy)); + RecordRecTy *Ty = new(Mem) RecordRecTy(Classes.size()); + std::uninitialized_copy(Classes.begin(), Classes.end(), + Ty->getTrailingObjects<Record *>()); + ThePool.InsertNode(Ty, IP); + return Ty; +} + +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; + + Stack.insert(Stack.end(), T1->classes_begin(), T1->classes_end()); + + while (!Stack.empty()) { + Record *R = Stack.back(); + Stack.pop_back(); + + if (T2->isSubClassOf(R)) { + CommonSuperClasses.push_back(R); + } else { + R->getDirectSuperClasses(Stack); + } + } + + return RecordRecTy::get(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 + +UnsetInit *UnsetInit::get() { + static UnsetInit TheInit; + return &TheInit; +} + +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(bool V) { + static BitInit True(true); + static BitInit False(false); + + return V ? &True : &False; +} + +Init *BitInit::convertInitializerTo(RecTy *Ty) const { + if (isa<BitRecTy>(Ty)) + return const_cast<BitInit *>(this); + + if (isa<IntRecTy>(Ty)) + return IntInit::get(getValue()); + + if (auto *BRT = dyn_cast<BitsRecTy>(Ty)) { + // Can only convert single bit. + if (BRT->getNumBits() == 1) + return BitsInit::get(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(ArrayRef<Init *> Range) { + static FoldingSet<BitsInit> ThePool; + + FoldingSetNodeID ID; + ProfileBitsInit(ID, Range); + + void *IP = nullptr; + if (BitsInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = Allocator.Allocate(totalSizeToAlloc<Init *>(Range.size()), + alignof(BitsInit)); + BitsInit *I = new(Mem) BitsInit(Range.size()); + std::uninitialized_copy(Range.begin(), Range.end(), + I->getTrailingObjects<Init *>()); + ThePool.InsertNode(I, IP); + return I; +} + +void BitsInit::Profile(FoldingSetNodeID &ID) const { + ProfileBitsInit(ID, makeArrayRef(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(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(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(NewBits); + + return const_cast<BitsInit *>(this); +} + +IntInit *IntInit::get(int64_t V) { + static std::map<int64_t, IntInit*> ThePool; + + IntInit *&I = ThePool[V]; + if (!I) I = new(Allocator) IntInit(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(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(Value & ((i < 64) ? (1LL << i) : 0)); + + return BitsInit::get(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(Value & (INT64_C(1) << Bits[i])); + } + return BitsInit::get(NewBits); +} + +CodeInit *CodeInit::get(StringRef V, const SMLoc &Loc) { + static StringSet<BumpPtrAllocator &> ThePool(Allocator); + + CodeInitsConstructed++; + + // Unlike StringMap, StringSet doesn't accept empty keys. + if (V.empty()) + return new (Allocator) CodeInit("", Loc); + + // Location tracking prevents us from de-duping CodeInits as we're never + // called with the same string and same location twice. However, we can at + // least de-dupe the strings for a modest saving. + auto &Entry = *ThePool.insert(V).first; + return new(Allocator) CodeInit(Entry.getKey(), Loc); +} + +StringInit *StringInit::get(StringRef V) { + static StringMap<StringInit*, BumpPtrAllocator &> ThePool(Allocator); + + auto &Entry = *ThePool.insert(std::make_pair(V, nullptr)).first; + if (!Entry.second) + Entry.second = new(Allocator) StringInit(Entry.getKey()); + return Entry.second; +} + +Init *StringInit::convertInitializerTo(RecTy *Ty) const { + if (isa<StringRecTy>(Ty)) + return const_cast<StringInit *>(this); + if (isa<CodeRecTy>(Ty)) + return CodeInit::get(getValue(), SMLoc()); + + return nullptr; +} + +Init *CodeInit::convertInitializerTo(RecTy *Ty) const { + if (isa<CodeRecTy>(Ty)) + return const_cast<CodeInit *>(this); + if (isa<StringRecTy>(Ty)) + return StringInit::get(getValue()); + + return nullptr; +} + +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) { + static FoldingSet<ListInit> ThePool; + + FoldingSetNodeID ID; + ProfileListInit(ID, Range, EltTy); + + void *IP = nullptr; + if (ListInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + assert(Range.empty() || !isa<TypedInit>(Range[0]) || + cast<TypedInit>(Range[0])->getType()->typeIsConvertibleTo(EltTy)); + + void *Mem = 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 *>()); + ThePool.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 { + 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::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()) + 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) { + static FoldingSet<UnOpInit> ThePool; + + FoldingSetNodeID ID; + ProfileUnOpInit(ID, Opc, LHS, Type); + + void *IP = nullptr; + if (UnOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + UnOpInit *I = new(Allocator) UnOpInit(Opc, LHS, Type); + ThePool.InsertNode(I, IP); + return I; +} + +void UnOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileUnOpInit(ID, getOpcode(), getOperand(), getType()); +} + +Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { + 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(LHSd->getAsString()); + + if (IntInit *LHSi = dyn_cast<IntInit>(LHS)) + return StringInit::get(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. + if (Name == CurRec->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 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(LHSl->size()); + break; + + case EMPTY: + if (ListInit *LHSl = dyn_cast<ListInit>(LHS)) + return IntInit::get(LHSl->empty()); + if (StringInit *LHSs = dyn_cast<StringInit>(LHS)) + return IntInit::get(LHSs->getValue().empty()); + 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 HEAD: Result = "!head"; break; + case TAIL: Result = "!tail"; break; + case SIZE: Result = "!size"; break; + case EMPTY: Result = "!empty"; 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) { + static FoldingSet<BinOpInit> ThePool; + + FoldingSetNodeID ID; + ProfileBinOpInit(ID, Opc, LHS, RHS, Type); + + void *IP = nullptr; + if (BinOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + BinOpInit *I = new(Allocator) BinOpInit(Opc, LHS, RHS, Type); + ThePool.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(Concat); +} + +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()); +} + +static ListInit *ConcatListInits(const ListInit *LHS, + const ListInit *RHS) { + SmallVector<Init *, 8> Args; + Args.insert(Args.end(), LHS->begin(), LHS->end()); + Args.insert(Args.end(), RHS->begin(), RHS->end()); + 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()); +} + +Init *BinOpInit::getListSplat(TypedInit *LHS, Init *RHS) { + return BinOpInit::get(BinOpInit::LISTSPLAT, LHS, RHS, LHS->getType()); +} + +Init *BinOpInit::Fold(Record *CurRec) const { + switch (getOpcode()) { + case CONCAT: { + 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 || !ROp) + break; + if (LOp->getDef() != ROp->getDef()) { + PrintFatalError(Twine("Concatenated Dag operators do not match: '") + + LHSs->getAsString() + "' vs. '" + RHSs->getAsString() + + "'"); + } + 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(LHSs->getOperator(), 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; + Args.insert(Args.end(), LHSs->begin(), LHSs->end()); + Args.insert(Args.end(), RHSs->begin(), RHSs->end()); + 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 STRCONCAT: { + StringInit *LHSs = dyn_cast<StringInit>(LHS); + StringInit *RHSs = dyn_cast<StringInit>(RHS); + if (LHSs && RHSs) + return ConcatStringInits(LHSs, RHSs); + break; + } + case EQ: + case NE: + case LE: + case LT: + case GE: + case GT: { + // try to fold eq comparison for 'bit' and 'int', otherwise fallback + // to string objects. + IntInit *L = + dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get())); + IntInit *R = + dyn_cast_or_null<IntInit>(RHS->convertInitializerTo(IntRecTy::get())); + + if (L && R) { + bool Result; + switch (getOpcode()) { + case EQ: Result = L->getValue() == R->getValue(); break; + case NE: Result = L->getValue() != R->getValue(); break; + case LE: Result = L->getValue() <= R->getValue(); break; + case LT: Result = L->getValue() < R->getValue(); break; + case GE: Result = L->getValue() >= R->getValue(); break; + case GT: Result = L->getValue() > R->getValue(); break; + default: llvm_unreachable("unhandled comparison"); + } + return BitInit::get(Result); + } + + if (getOpcode() == EQ || getOpcode() == NE) { + StringInit *LHSs = dyn_cast<StringInit>(LHS); + StringInit *RHSs = dyn_cast<StringInit>(RHS); + + // Make sure we've resolved + if (LHSs && RHSs) { + bool Equal = LHSs->getValue() == RHSs->getValue(); + return BitInit::get(getOpcode() == EQ ? Equal : !Equal); + } + } + + break; + } + case ADD: + case MUL: + case AND: + case OR: + case SHL: + case SRA: + case SRL: { + IntInit *LHSi = + dyn_cast_or_null<IntInit>(LHS->convertInitializerTo(IntRecTy::get())); + IntInit *RHSi = + dyn_cast_or_null<IntInit>(RHS->convertInitializerTo(IntRecTy::get())); + 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 MUL: Result = LHSv * RHSv; break; + case AND: Result = LHSv & RHSv; break; + case OR: Result = LHSv | RHSv; break; + case SHL: Result = LHSv << RHSv; break; + case SRA: Result = LHSv >> RHSv; break; + case SRL: Result = (uint64_t)LHSv >> (uint64_t)RHSv; break; + } + return IntInit::get(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 MUL: Result = "!mul"; break; + case AND: Result = "!and"; break; + case OR: Result = "!or"; 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 STRCONCAT: Result = "!strconcat"; 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) { + static FoldingSet<TernOpInit> ThePool; + + FoldingSetNodeID ID; + ProfileTernOpInit(ID, Opc, LHS, MHS, RHS, Type); + + void *IP = nullptr; + if (TernOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + TernOpInit *I = new(Allocator) TernOpInit(Opc, LHS, MHS, RHS, Type); + ThePool.InsertNode(I, IP); + return I; +} + +void TernOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileTernOpInit(ID, getOpcode(), getLHS(), getMHS(), getRHS(), getType()); +} + +static Init *ForeachApply(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 = ForeachApply(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 = ForeachApply(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 = ForeachApply(LHS, Item, RHS, CurRec); + if (NewItem != Item) + Item = NewItem; + } + return ListInit::get(NewList, cast<ListRecTy>(Type)->getElementType()); + } + + return nullptr; +} + +Init *TernOpInit::Fold(Record *CurRec) const { + 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 = RHSv->getName(); + if (LHSv->getAsString() == RHSv->getAsString()) + Val = MHSv->getName(); + return VarInit::get(Val, getType()); + } + if (LHSs && MHSs && RHSs) { + std::string Val = RHSs->getValue(); + + std::string::size_type found; + std::string::size_type idx = 0; + while (true) { + found = Val.find(LHSs->getValue(), idx); + if (found == std::string::npos) + break; + Val.replace(found, LHSs->getValue().size(), MHSs->getValue()); + idx = found + MHSs->getValue().size(); + } + + return StringInit::get(Val); + } + break; + } + + case FOREACH: { + if (Init *Result = ForeachHelper(LHS, MHS, RHS, getType(), CurRec)) + return Result; + break; + } + + case IF: { + if (IntInit *LHSi = dyn_cast_or_null<IntInit>( + LHS->convertInitializerTo(IntRecTy::get()))) { + 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(); + Init *Name = RHSl ? RHSl->getElement(i) : UnsetInit::get(); + 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; + } + } + + 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()))) { + // Short-circuit + if (Value->getValue()) + return MHS->resolveReferences(R); + return RHS->resolveReferences(R); + } + } + + Init *mhs = MHS->resolveReferences(R); + Init *rhs; + + if (getOpcode() == FOREACH) { + 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 SUBST: Result = "!subst"; break; + case FOREACH: Result = "!foreach"; UnquotedLHS = true; break; + case IF: Result = "!if"; break; + case DAG: Result = "!dag"; break; + } + return (Result + "(" + + (UnquotedLHS ? LHS->getAsUnquotedString() : LHS->getAsString()) + + ", " + MHS->getAsString() + ", " + RHS->getAsString() + ")"); +} + +static void ProfileFoldOpInit(FoldingSetNodeID &ID, Init *A, Init *B, + Init *Start, Init *List, 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) { + static FoldingSet<FoldOpInit> ThePool; + + FoldingSetNodeID ID; + ProfileFoldOpInit(ID, Start, List, A, B, Expr, Type); + + void *IP = nullptr; + if (FoldOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + FoldOpInit *I = new (Allocator) FoldOpInit(Start, List, A, B, Expr, Type); + ThePool.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) { + static FoldingSet<IsAOpInit> ThePool; + + FoldingSetNodeID ID; + ProfileIsAOpInit(ID, CheckType, Expr); + + void *IP = nullptr; + if (IsAOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + IsAOpInit *I = new (Allocator) IsAOpInit(CheckType, Expr); + ThePool.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(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(0); + } else { + // We treat non-record types as not castable. + return IntInit::get(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(); +} + +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({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(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(VN); + return VarInit::get(Value, T); +} + +VarInit *VarInit::get(Init *VN, RecTy *T) { + using Key = std::pair<RecTy *, Init *>; + static DenseMap<Key, VarInit*> ThePool; + + Key TheKey(std::make_pair(T, VN)); + + VarInit *&I = ThePool[TheKey]; + if (!I) + I = new(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()) + 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) { + using Key = std::pair<TypedInit *, unsigned>; + static DenseMap<Key, VarBitInit*> ThePool; + + Key TheKey(std::make_pair(T, B)); + + VarBitInit *&I = ThePool[TheKey]; + if (!I) + I = new(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) { + using Key = std::pair<TypedInit *, unsigned>; + static DenseMap<Key, VarListElementInit*> ThePool; + + Key TheKey(std::make_pair(T, E)); + + VarListElementInit *&I = ThePool[TheKey]; + if (!I) I = new(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()) + 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 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::get(Record *Class, ArrayRef<Init *> Args) { + static FoldingSet<VarDefInit> ThePool; + + FoldingSetNodeID ID; + ProfileVarDefInit(ID, Class, Args); + + void *IP = nullptr; + if (VarDefInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = 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 *>()); + ThePool.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); + + // 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)); + + 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) { + using Key = std::pair<Init *, StringInit *>; + static DenseMap<Key, FieldInit*> ThePool; + + Key TheKey(std::make_pair(R, FN)); + + FieldInit *&I = ThePool[TheKey]; + if (!I) I = new(Allocator) FieldInit(R, FN); + return I; +} + +Init *FieldInit::getBit(unsigned Bit) const { + if (getType() == BitRecTy::get()) + 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->isComplete()) + return FieldVal; + } + return const_cast<FieldInit *>(this); +} + +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, + makeArrayRef(getTrailingObjects<Init *>(), NumConds), + makeArrayRef(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!"); + + static FoldingSet<CondOpInit> ThePool; + FoldingSetNodeID ID; + ProfileCondOpInit(ID, CondRange, ValRange, Ty); + + void *IP = nullptr; + if (CondOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = 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()); + ThePool.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 { + 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()))) { + 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) { + static FoldingSet<DagInit> ThePool; + + FoldingSetNodeID ID; + ProfileDagInit(ID, V, VN, ArgRange, NameRange); + + void *IP = nullptr; + if (DagInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) + return I; + + void *Mem = 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 *>()); + ThePool.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, makeArrayRef(getTrailingObjects<Init *>(), NumArgs), makeArrayRef(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, bool P) + : Name(N), TyAndPrefix(T, P) { + setValue(UnsetInit::get()); + assert(Value && "Cannot create unset value for current type!"); +} + +StringRef RecordVal::getName() const { + return cast<StringInit>(getNameInit())->getValue(); +} + +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(Bits); + } + } + } + return Value == nullptr; + } + Value = nullptr; + return false; +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; } +#endif + +void RecordVal::print(raw_ostream &OS, bool PrintSem) const { + if (getPrefix()) OS << "field "; + OS << *getType() << " " << getNameInitAsString(); + + if (getValue()) + OS << " = " << *getValue(); + + if (PrintSem) OS << ";\n"; +} + +unsigned Record::LastID = 0; + +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(DirectSCs); +} + +DefInit *Record::getDefInit() { + if (!TheInit) + TheInit = new(Allocator) DefInit(this); + return TheInit; +} + +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. +} + +void Record::getDirectSuperClasses(SmallVectorImpl<Record *> &Classes) const { + ArrayRef<std::pair<Record *, SMRange>> SCs = getSuperClasses(); + while (!SCs.empty()) { + // Superclasses are in reverse preorder, so 'back' is a direct superclass, + // and its transitive superclasses are directly preceding it. + Record *SC = SCs.back().first; + SCs = SCs.drop_back(1 + SC->getSuperClasses().size()); + Classes.push_back(SC); + } +} + +void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) { + 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 + + "is found when setting '" + + Value.getNameInitAsString() + + "' of type '" + + Value.getType()->getAsString() + + "' after resolving references: " + + VR->getAsUnquotedString() + "\n"); + } + } + } + Init *OldName = getNameInit(); + Init *NewName = Name->resolveReferences(R); + if (NewName != OldName) { + // Re-register with RecordKeeper. + setName(NewName); + } +} + +void Record::resolveReferences() { + RecordResolver R(*this); + R.setFinal(true); + resolveReferences(R); +} + +void Record::resolveReferencesTo(const RecordVal *RV) { + RecordValResolver R(*this, RV); + resolveReferences(R, RV); +} + +#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.getPrefix() && !R.isTemplateArg(Val.getNameInit())) + OS << Val; + for (const RecordVal &Val : R.getValues()) + if (!Val.getPrefix() && !R.isTemplateArg(Val.getNameInit())) + OS << Val; + + return OS << "}\n"; +} + +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 { + const RecordVal *R = getValue(FieldName); + if (!R || !R->getValue()) + PrintFatalError(getLoc(), "Record `" + getName() + + "' does not have a field named `" + FieldName + "'!\n"); + + if (StringInit *SI = dyn_cast<StringInit>(R->getValue())) + return SI->getValue(); + if (CodeInit *CI = dyn_cast<CodeInit>(R->getValue())) + return CI->getValue(); + + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + + FieldName + "' does not have a string initializer!"); +} + +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 + "' does not have a BitsInit initializer!"); +} + +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 + "' does not have a list initializer!"); +} + +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 + + "' does not have an int initializer: " + + 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 + + "' does not have a list of ints initializer: " + + 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 + + "' does not have a list of strings initializer: " + + 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!"); +} + +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!"); +} + +#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 StringInit::get("anonymous_" + utostr(AnonCounter++)); +} + +std::vector<Record *> +RecordKeeper::getAllDerivedDefinitions(StringRef ClassName) const { + Record *Class = getClass(ClassName); + if (!Class) + PrintFatalError("ERROR: Couldn't find the `" + ClassName + "' class!\n"); + + std::vector<Record*> Defs; + for (const auto &D : getDefs()) + if (D.second->isSubClassOf(Class)) + Defs.push_back(D.second.get()); + + return Defs; +} + +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; + + for (Init *S : Stack) { + if (S == 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(); + } + } + + 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/llvm/lib/TableGen/SetTheory.cpp b/llvm/lib/TableGen/SetTheory.cpp new file mode 100644 index 000000000000..5a30ee98cce9 --- /dev/null +++ b/llvm/lib/TableGen/SetTheory.cpp @@ -0,0 +1,333 @@ +//===- 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/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.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 "llvm/TableGen/SetTheory.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 (RecSet::iterator I = Add.begin(), E = Add.end(); I != E; ++I) + 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 (RecSet::iterator I = S1.begin(), E = S1.end(); I != E; ++I) + 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 = 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/llvm/lib/TableGen/StringMatcher.cpp b/llvm/lib/TableGen/StringMatcher.cpp new file mode 100644 index 000000000000..2fca068893f3 --- /dev/null +++ b/llvm/lib/TableGen/StringMatcher.cpp @@ -0,0 +1,156 @@ +//===- 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/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 (unsigned str = 0, e = Matches.size(); str != e; ++str) + if (Matches[str]->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 (unsigned i = 0, e = Matches.size(); i != e; ++i) + MatchesByLetter[Matches[i]->first[CharNo]].push_back(Matches[i]); + + + // 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 (std::map<char, std::vector<const StringPair*>>::iterator LI = + MatchesByLetter.begin(), E = MatchesByLetter.end(); LI != E; ++LI) { + // 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 (unsigned i = 0, e = Matches.size(); i != e; ++i) + MatchesByLength[Matches[i].first.size()].push_back(&Matches[i]); + + // 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 (std::map<unsigned, std::vector<const StringPair*>>::iterator LI = + MatchesByLength.begin(), E = MatchesByLength.end(); LI != E; ++LI) { + 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/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp new file mode 100644 index 000000000000..da2286e41fe5 --- /dev/null +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -0,0 +1,1021 @@ +//===- 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/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. + std::for_each(Macros.begin(), Macros.end(), + [this](const std::string &MacroName) { + DefinedMacros.insert(MacroName); + }); +} + +SMLoc TGLexer::getLoc() const { + return SMLoc::getFromPointer(TokStart); +} + +/// 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 random nul in the file. Disambiguate that here. + if (CurPtr-1 != CurBuf.end()) + return 0; // Just whitespace. + + // Otherwise, return end of file. + --CurPtr; // Another call to lex will return EOF again. + return EOF; + } + 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 '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::period; + 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; + + case '\r': + PrintFatalError("getNextChar() must never return '\r'"); + return tgtok::Error; + + case 0: + 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(); + LLVM_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"); + LLVM_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 keyword. + StringRef Str(IdentStart, CurPtr-IdentStart); + + if (Str == "include") { + if (LexInclude()) return tgtok::Error; + return Lex(); + } + + tgtok::TokKind Kind = StringSwitch<tgtok::TokKind>(Str) + .Case("int", tgtok::Int) + .Case("bit", tgtok::Bit) + .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("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) + .Default(tgtok::Id); + + if (Kind == tgtok::Id) + CurStrVal.assign(Str.begin(), Str.end()); + 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; + } + + DependenciesMapTy::const_iterator Found = Dependencies.find(IncludedFile); + if (Found != Dependencies.end()) { + PrintError(getLoc(), + "File '" + IncludedFile + "' has already been included."); + SrcMgr.PrintMessage(Found->second, SourceMgr::DK_Note, + "previously included here"); + return true; + } + Dependencies.insert(std::make_pair(IncludedFile, getLoc())); + // 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; +} + +void TGLexer::SkipBCPLComment() { + ++CurPtr; // skip the second slash. + while (true) { + switch (*CurPtr) { + case '\n': + case '\r': + return; // Newline is end of comment. + case 0: + // If this is the end of the buffer, end the comment. + if (CurPtr == CurBuf.end()) + return; + break; + } + // Otherwise, skip the character. + ++CurPtr; + } +} + +/// 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("mul", tgtok::XMUL) + .Case("and", tgtok::XAND) + .Case("or", tgtok::XOR) + .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("listconcat", tgtok::XListConcat) + .Case("listsplat", tgtok::XListSplat) + .Case("strconcat", tgtok::XStrConcat) + .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 (unsigned ID = 0; ID < llvm::array_lengthof(PreprocessorDirs); ++ID) { + int NextChar = *CurPtr; + bool Match = true; + unsigned I = 0; + for (; I < strlen(PreprocessorDirs[ID].Word); ++I) { + if (NextChar != PreprocessorDirs[ID].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 = PreprocessorDirs[ID].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 (unsigned ID = 0; ID < llvm::array_lengthof(PreprocessorDirs); ++ID) + if (PreprocessorDirs[ID].Kind == Kind) { + // Advance CurPtr to the end of the preprocessing word. + CurPtr += strlen(PreprocessorDirs[ID].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 (auto I = PrepIncludeStack.back()->rbegin(), + E = PrepIncludeStack.back()->rend(); + I != E; ++I) { + 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/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h new file mode 100644 index 000000000000..3085ab2c0478 --- /dev/null +++ b/llvm/lib/TableGen/TGLexer.h @@ -0,0 +1,373 @@ +//===- 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/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/SMLoc.h" +#include <cassert> +#include <map> +#include <memory> +#include <string> + +namespace llvm { +class SourceMgr; +class SMLoc; +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, period, // , . + equal, question, // = ? + paste, // # + + // Keywords. + Bit, Bits, Class, Code, Dag, Def, Foreach, Defm, Field, In, Int, Let, List, + MultiClass, String, Defset, + + // !keywords. + XConcat, XADD, XMUL, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XListSplat, + XStrConcat, XCast, XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, + XIf, XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, + + // 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; + StringRef CurBuf; + + // Information about the current token. + const char *TokStart; + tgtok::TokKind CurCode; + std::string CurStrVal; // This is valid for ID, STRVAL, VARNAME, CODEFRAGMENT + int64_t CurIntVal; // This is valid for INTVAL. + + /// CurBuffer - This is the current buffer index we're lexing from as managed + /// by the SourceMgr object. + unsigned CurBuffer; + +public: + typedef std::map<std::string, SMLoc> DependenciesMapTy; +private: + /// Dependencies - This is the list of all included files. + DependenciesMapTy Dependencies; + +public: + TGLexer(SourceMgr &SrcMgr, ArrayRef<std::string> Macros); + + tgtok::TokKind Lex() { + return CurCode = LexToken(CurPtr == CurBuf.begin()); + } + + const DependenciesMapTy &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; + +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 occured 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 occured 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/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp new file mode 100644 index 000000000000..c373e2899a5d --- /dev/null +++ b/llvm/lib/TableGen/TGParser.cpp @@ -0,0 +1,3243 @@ +//===- 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/None.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.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 "llvm/TableGen/Record.h" +#include <algorithm> +#include <cassert> +#include <cstdint> + +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.getPrefix()) + 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) { + Init *NewName = + BinOpInit::getStrConcat(CurRec.getNameInit(), StringInit::get(Scoper)); + NewName = BinOpInit::getStrConcat(NewName, Name); + if (CurMultiClass && Scoper != "::") { + Init *Prefix = BinOpInit::getStrConcat(CurMultiClass->Rec.getNameInit(), + StringInit::get("::")); + 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("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) { + 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(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(NewBits); + } + + if (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, "Value '" + ValName->getAsUnquotedString() + + "' of type '" + RV->getType()->getAsString() + + "' is incompatible with initializer '" + + 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; + // Add all of the values in the subclass into the current class. + for (const RecordVal &Val : SC->getValues()) + if (AddValue(CurRec, SubClass.RefRange.Start, Val)) + return true; + + ArrayRef<Init *> TArgs = SC->getTemplateArgs(); + + // Ensure that an appropriate number of template arguments are specified. + if (TArgs.size() < SubClass.TemplateArgs.size()) + return Error(SubClass.RefRange.Start, + "More template args specified than expected"); + + // Loop over all of the template arguments, setting them to the specified + // value or leaving them as the default if necessary. + MapResolver R(CurRec); + + for (unsigned i = 0, e = TArgs.size(); i != e; ++i) { + if (i < SubClass.TemplateArgs.size()) { + // If a value is specified for this template arg, set it now. + if (SetValue(CurRec, SubClass.RefRange.Start, TArgs[i], + None, SubClass.TemplateArgs[i])) + return true; + } else if (!CurRec->getValue(TArgs[i])->getValue()->isComplete()) { + return Error(SubClass.RefRange.Start, + "Value not specified for template argument #" + + Twine(i) + " (" + TArgs[i]->getAsUnquotedString() + + ") of subclass '" + SC->getNameInitAsString() + "'!"); + } + + R.set(TArgs[i], CurRec->getValue(TArgs[i])->getValue()); + + CurRec->removeValue(TArgs[i]); + } + + Init *Name; + if (CurRec->isClass()) + Name = + VarInit::get(QualifiedNameOfImplicitName(*CurRec), StringRecTy::get()); + 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); + + 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())); + + // Add all of the defs in the subclass into the current multiclass. + return resolve(SMC->Entries, TemplateArgs, false, &CurMC->Entries); +} + +/// Add a record or foreach loop to the current context (global record keeper, +/// current inner-most foreach loop, or multiclass). +bool TGParser::addEntry(RecordsEntry E) { + assert(!E.Rec || !E.Loop); + + if (!Loops.empty()) { + Loops.back()->Entries.push_back(std::move(E)); + return false; + } + + if (E.Loop) { + SubstStack Stack; + return resolve(*E.Loop, Stack, CurMultiClass == nullptr, + CurMultiClass ? &CurMultiClass->Entries : nullptr); + } + + if (CurMultiClass) { + CurMultiClass->Entries.push_back(std::move(E)); + return false; + } + + 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) { + Substs.emplace_back(Loop.IterVar->getNameInit(), Elt); + Error = resolve(Loop.Entries, Substs, Final, Dest); + 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 { + 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) { + 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; + } + Rec->setName(Records.getNewAnonymousName()); + } + + Rec->resolveReferences(); + checkConcrete(*Rec); + + if (!isa<StringInit>(Rec->getNameInit())) { + PrintError(Rec->getLoc(), Twine("record name '") + + Rec->getNameInit()->getAsString() + + "' could not be fully resolved"); + return true; + } + + // 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 an Object. +static bool isObjectStart(tgtok::TokKind K) { + return K == tgtok::Class || K == tgtok::Def || K == tgtok::Defm || + K == tgtok::Let || K == tgtok::MultiClass || K == tgtok::Foreach || + K == tgtok::Defset; +} + +/// 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(); + default: + break; + } + + Record *CurRec = nullptr; + if (CurMultiClass) + CurRec = &CurMultiClass->Rec; + + Init *Name = ParseValue(CurRec, StringRecTy::get(), 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()), + 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); + } + + 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 to a templated +/// subclass. 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 (Lex.getCode() != tgtok::less) { + Result.RefRange.End = Lex.getLoc(); + return Result; + } + Lex.Lex(); // Eat the '<' + + if (Lex.getCode() == tgtok::greater) { + TokError("subclass reference requires a non-empty list of template values"); + Result.Rec = nullptr; + return Result; + } + + ParseValueList(Result.TemplateArgs, CurRec, Result.Rec); + if (Result.TemplateArgs.empty()) { + Result.Rec = nullptr; // Error parsing value list. + return Result; + } + + if (Lex.getCode() != tgtok::greater) { + TokError("expected '>' in template value list"); + Result.Rec = nullptr; + return Result; + } + Lex.Lex(); + 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 (Lex.getCode() != tgtok::less) { + Result.RefRange.End = Lex.getLoc(); + return Result; + } + Lex.Lex(); // Eat the '<' + + if (Lex.getCode() == tgtok::greater) { + TokError("subclass reference requires a non-empty list of template values"); + Result.MC = nullptr; + return Result; + } + + ParseValueList(Result.TemplateArgs, &CurMC->Rec, &Result.MC->Rec); + if (Result.TemplateArgs.empty()) { + Result.MC = nullptr; // Error parsing value list. + return Result; + } + + if (Lex.getCode() != tgtok::greater) { + TokError("expected '>' in template value list"); + Result.MC = nullptr; + return Result; + } + Lex.Lex(); + Result.RefRange.End = Lex.getLoc(); + + return Result; +} + +/// ParseRangePiece - Parse a bit/value range. +/// RangePiece ::= INTVAL +/// RangePiece ::= INTVAL '-' INTVAL +/// RangePiece ::= INTVAL INTVAL +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::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 (Lex.getCode() == tgtok::comma) { + Lex.Lex(); // Eat the 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) { + if (Lex.getCode() != tgtok::less) + return false; + + SMLoc StartLoc = Lex.getLoc(); + Lex.Lex(); // eat the '<' + + // Parse the range list. + ParseRangeList(Ranges); + if (Ranges.empty()) return true; + + if (Lex.getCode() != tgtok::greater) { + TokError("expected '>' at end of range list"); + return Error(StartLoc, "to match this '<'"); + } + Lex.Lex(); // eat the '>'. + return false; +} + +/// ParseOptionalBitList - Parse either a bit list in {}'s or nothing. +/// OptionalBitList ::= '{' RangeList '}' +/// OptionalBitList ::= /*empty*/ +bool TGParser::ParseOptionalBitList(SmallVectorImpl<unsigned> &Ranges) { + if (Lex.getCode() != tgtok::l_brace) + return false; + + SMLoc StartLoc = Lex.getLoc(); + Lex.Lex(); // eat the '{' + + // Parse the range list. + ParseRangeList(Ranges); + if (Ranges.empty()) return true; + + if (Lex.getCode() != tgtok::r_brace) { + TokError("expected '}' at end of bit list"); + return Error(StartLoc, "to match this '{'"); + } + Lex.Lex(); // eat the '}'. + 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: Lex.Lex(); return StringRecTy::get(); + case tgtok::Code: Lex.Lex(); return CodeRecTy::get(); + case tgtok::Bit: Lex.Lex(); return BitRecTy::get(); + case tgtok::Int: Lex.Lex(); return IntRecTy::get(); + case tgtok::Dag: Lex.Lex(); return DagRecTy::get(); + 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(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 (Lex.getCode() != tgtok::greater) { + TokError("expected '>' at end of list<ty> type"); + return nullptr; + } + Lex.Lex(); // Eat '>' + return ListRecTy::get(SubType); + } + } +} + +/// ParseIDValue - This is just like ParseIDValue above, but it assumes the ID +/// has already been read. +Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc, + IDParseMode Mode) { + if (CurRec) { + if (const RecordVal *RV = CurRec->getValue(Name)) + 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)) { + const RecordVal *RV = TemplateRec->getValue(TemplateArgName); + assert(RV && "Template arg doesn't exist??"); + return VarInit::get(TemplateArgName, RV->getType()); + } else if (Name->getValue() == "NAME") { + return VarInit::get(TemplateArgName, StringRecTy::get()); + } + } + + // If this is in a foreach loop, make sure it's not a loop iterator + for (const auto &L : Loops) { + 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())) + 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, "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 operation"); + return nullptr; + case tgtok::XHead: + case tgtok::XTail: + case tgtok::XSize: + case tgtok::XEmpty: + case tgtok::XCast: { // 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::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(); + break; + case tgtok::XEmpty: + Lex.Lex(); // eat the operation + Code = UnOpInit::EMPTY; + Type = IntRecTy::get(); + break; + } + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after unary operator"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + Init *LHS = ParseValue(CurRec); + if (!LHS) return nullptr; + + if (Code == UnOpInit::HEAD || + Code == UnOpInit::TAIL || + Code == UnOpInit::EMPTY) { + ListInit *LHSl = dyn_cast<ListInit>(LHS); + StringInit *LHSs = dyn_cast<StringInit>(LHS); + TypedInit *LHSt = dyn_cast<TypedInit>(LHS); + if (!LHSl && !LHSs && !LHSt) { + TokError("expected list or string type argument in unary operator"); + return nullptr; + } + if (LHSt) { + ListRecTy *LType = dyn_cast<ListRecTy>(LHSt->getType()); + StringRecTy *SType = dyn_cast<StringRecTy>(LHSt->getType()); + if (!LType && !SType) { + TokError("expected list or string type argument in unary operator"); + return nullptr; + } + } + + if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL || + Code == UnOpInit::SIZE) { + if (!LHSl && !LHSt) { + TokError("expected list type argument in unary operator"); + return nullptr; + } + } + + if (Code == UnOpInit::HEAD || Code == UnOpInit::TAIL) { + if (LHSl && LHSl->empty()) { + TokError("empty list argument in unary operator"); + return nullptr; + } + if (LHSl) { + Init *Item = LHSl->getElement(0); + TypedInit *Itemt = dyn_cast<TypedInit>(Item); + if (!Itemt) { + TokError("untyped list element in unary operator"); + return nullptr; + } + Type = (Code == UnOpInit::HEAD) ? Itemt->getType() + : ListRecTy::get(Itemt->getType()); + } else { + assert(LHSt && "expected list type argument in unary operator"); + ListRecTy *LType = dyn_cast<ListRecTy>(LHSt->getType()); + if (!LType) { + TokError("expected list type argument in unary operator"); + return nullptr; + } + Type = (Code == UnOpInit::HEAD) ? LType->getElementType() : LType; + } + } + } + + if (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in unary operator"); + return nullptr; + } + Lex.Lex(); // eat the ')' + 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 (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after type of !isa"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + Init *LHS = ParseValue(CurRec); + if (!LHS) + return nullptr; + + if (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in !isa"); + return nullptr; + } + Lex.Lex(); // eat the ')' + + return (IsAOpInit::get(Type, LHS))->Fold(); + } + + case tgtok::XConcat: + case tgtok::XADD: + case tgtok::XMUL: + case tgtok::XAND: + case tgtok::XOR: + 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::XStrConcat: { // 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::XMUL: Code = BinOpInit::MUL; break; + case tgtok::XAND: Code = BinOpInit::AND; break; + case tgtok::XOR: Code = BinOpInit::OR; 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::XStrConcat: Code = BinOpInit::STRCONCAT; break; + } + + RecTy *Type = nullptr; + RecTy *ArgType = nullptr; + switch (OpTok) { + default: + llvm_unreachable("Unhandled code!"); + case tgtok::XConcat: + Type = DagRecTy::get(); + ArgType = DagRecTy::get(); + break; + case tgtok::XAND: + case tgtok::XOR: + case tgtok::XSRA: + case tgtok::XSRL: + case tgtok::XSHL: + case tgtok::XADD: + case tgtok::XMUL: + Type = IntRecTy::get(); + ArgType = IntRecTy::get(); + break; + case tgtok::XEq: + case tgtok::XNe: + Type = BitRecTy::get(); + // ArgType for Eq / Ne is not known at this point + break; + case tgtok::XLe: + case tgtok::XLt: + case tgtok::XGe: + case tgtok::XGt: + Type = BitRecTy::get(); + ArgType = IntRecTy::get(); + 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::XStrConcat: + Type = StringRecTy::get(); + ArgType = StringRecTy::get(); + break; + } + + if (Type && ItemType && !Type->typeIsConvertibleTo(ItemType)) { + Error(OpLoc, Twine("expected value of type '") + + ItemType->getAsString() + "', got '" + + Type->getAsString() + "'"); + return nullptr; + } + + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after binary operator"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + SmallVector<Init*, 2> InitList; + + for (;;) { + SMLoc InitLoc = Lex.getLoc(); + InitList.push_back(ParseValue(CurRec, ArgType)); + if (!InitList.back()) return nullptr; + + // All BinOps require their arguments to be of compatible types. + RecTy *ListType = cast<TypedInit>(InitList.back())->getType(); + if (!ArgType) { + 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::EQ: + case BinOpInit::NE: + if (!ArgType->typeIsConvertibleTo(IntRecTy::get()) && + !ArgType->typeIsConvertibleTo(StringRecTy::get())) { + Error(InitLoc, Twine("expected int, bits, or string; got value of " + "type '") + ArgType->getAsString() + "'"); + return nullptr; + } + break; + default: llvm_unreachable("other ops have fixed argument types"); + } + } else { + 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::AND && + Code != BinOpInit::OR && Code != BinOpInit::SRA && + Code != BinOpInit::SRL && Code != BinOpInit::SHL && + Code != BinOpInit::MUL) + ArgType = Resolved; + } + + if (Lex.getCode() != tgtok::comma) + break; + Lex.Lex(); // eat the ',' + } + + if (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in operator"); + return nullptr; + } + Lex.Lex(); // eat the ')' + + // 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(); + + // 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::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: { // Value ::= !foreach '(' Id ',' Value ',' Value ')' + SMLoc OpLoc = Lex.getLoc(); + Lex.Lex(); // eat the operation + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after !foreach"); + return nullptr; + } + + if (Lex.Lex() != tgtok::Id) { // eat the '(' + TokError("first argument of !foreach must be an identifier"); + return nullptr; + } + + Init *LHS = StringInit::get(Lex.getCurStrVal()); + + if (CurRec && CurRec->getValue(LHS)) { + TokError((Twine("iteration variable '") + LHS->getAsString() + + "' already defined") + .str()); + return nullptr; + } + + if (Lex.Lex() != tgtok::comma) { // eat the id + TokError("expected ',' in ternary operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + Init *MHS = ParseValue(CurRec); + if (!MHS) + return nullptr; + + if (Lex.getCode() != tgtok::comma) { + TokError("expected ',' in ternary operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + TypedInit *MHSt = dyn_cast<TypedInit>(MHS); + if (!MHSt) { + TokError("could not get type of !foreach input"); + return nullptr; + } + + RecTy *InEltType = nullptr; + RecTy *OutEltType = nullptr; + bool IsDAG = false; + + if (ListRecTy *InListTy = dyn_cast<ListRecTy>(MHSt->getType())) { + InEltType = InListTy->getElementType(); + if (ItemType) { + if (ListRecTy *OutListTy = dyn_cast<ListRecTy>(ItemType)) { + OutEltType = OutListTy->getElementType(); + } else { + Error(OpLoc, + "expected value of type '" + Twine(ItemType->getAsString()) + + "', but got !foreach of list type"); + return nullptr; + } + } + } else if (DagRecTy *InDagTy = dyn_cast<DagRecTy>(MHSt->getType())) { + InEltType = InDagTy; + if (ItemType && !isa<DagRecTy>(ItemType)) { + Error(OpLoc, + "expected value of type '" + Twine(ItemType->getAsString()) + + "', but got !foreach of dag type"); + return nullptr; + } + IsDAG = true; + } else { + TokError("!foreach must have list or dag input"); + return nullptr; + } + + // We need to create a temporary record to provide a scope for the iteration + // variable while parsing top-level foreach's. + std::unique_ptr<Record> ParseRecTmp; + Record *ParseRec = CurRec; + if (!ParseRec) { + ParseRecTmp = std::make_unique<Record>(".parse", ArrayRef<SMLoc>{}, Records); + ParseRec = ParseRecTmp.get(); + } + + ParseRec->addValue(RecordVal(LHS, InEltType, false)); + Init *RHS = ParseValue(ParseRec, OutEltType); + ParseRec->removeValue(LHS); + if (!RHS) + return nullptr; + + if (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in binary operator"); + return nullptr; + } + Lex.Lex(); // eat the ')' + + RecTy *OutType; + if (IsDAG) { + OutType = InEltType; + } else { + TypedInit *RHSt = dyn_cast<TypedInit>(RHS); + if (!RHSt) { + TokError("could not get type of !foreach result"); + return nullptr; + } + OutType = RHSt->getType()->getListTy(); + } + + return (TernOpInit::get(TernOpInit::FOREACH, LHS, MHS, RHS, OutType)) + ->Fold(CurRec); + } + + case tgtok::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(); + ItemType = nullptr; + break; + case tgtok::XIf: + Code = TernOpInit::IF; + break; + case tgtok::XSubst: + Code = TernOpInit::SUBST; + break; + } + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after ternary operator"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + Init *LHS = ParseValue(CurRec); + if (!LHS) return nullptr; + + if (Lex.getCode() != tgtok::comma) { + TokError("expected ',' in ternary operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + SMLoc MHSLoc = Lex.getLoc(); + Init *MHS = ParseValue(CurRec, ItemType); + if (!MHS) + return nullptr; + + if (Lex.getCode() != tgtok::comma) { + TokError("expected ',' in ternary operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + SMLoc RHSLoc = Lex.getLoc(); + Init *RHS = ParseValue(CurRec, ItemType); + if (!RHS) + return nullptr; + + if (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in binary operator"); + return nullptr; + } + Lex.Lex(); // eat the ')' + + 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()->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(MHSbits->getNumBits()); + if (isa<BitInit>(MHS)) + MHSTy = BitRecTy::get(); + + if (TypedInit *RHSt = dyn_cast<TypedInit>(RHS)) + RHSTy = RHSt->getType(); + if (BitsInit *RHSbits = dyn_cast<BitsInit>(RHS)) + RHSTy = BitsRecTy::get(RHSbits->getNumBits()); + if (isa<BitInit>(RHS)) + RHSTy = BitRecTy::get(); + + // 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::XCond: + return ParseOperationCond(CurRec, ItemType); + + case tgtok::XFoldl: { + // Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')' + Lex.Lex(); // eat the operation + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after !foldl"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + 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 (Lex.getCode() != tgtok::comma) { + TokError("expected ',' in !foldl"); + return nullptr; + } + Lex.Lex(); // eat the ',' + + 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(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(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 iteration + // variable while parsing top-level foreach's. + std::unique_ptr<Record> ParseRecTmp; + Record *ParseRec = CurRec; + if (!ParseRec) { + ParseRecTmp = std::make_unique<Record>(".parse", ArrayRef<SMLoc>{}, Records); + ParseRec = ParseRecTmp.get(); + } + + ParseRec->addValue(RecordVal(A, Start->getType(), false)); + ParseRec->addValue(RecordVal(B, ListType->getElementType(), false)); + 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 (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in fold operator"); + return nullptr; + } + Lex.Lex(); // eat the ')' + + 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 (Lex.getCode() != tgtok::less) { + TokError("expected type name for operator"); + return nullptr; + } + Lex.Lex(); // eat the < + + Type = ParseType(); + + if (!Type) { + TokError("expected type name for operator"); + return nullptr; + } + + if (Lex.getCode() != tgtok::greater) { + TokError("expected type name for operator"); + return nullptr; + } + Lex.Lex(); // eat the > + + return Type; +} + +Init *TGParser::ParseOperationCond(Record *CurRec, RecTy *ItemType) { + Lex.Lex(); // eat the operation 'cond' + + if (Lex.getCode() != tgtok::l_paren) { + TokError("expected '(' after !cond operator"); + return nullptr; + } + Lex.Lex(); // eat the '(' + + // Parse through '[Case: Val,]+' + SmallVector<Init *, 4> Case; + SmallVector<Init *, 4> Val; + while (true) { + if (Lex.getCode() == tgtok::r_paren) { + Lex.Lex(); // eat the ')' + break; + } + + Init *V = ParseValue(CurRec); + if (!V) + return nullptr; + Case.push_back(V); + + if (Lex.getCode() != tgtok::colon) { + TokError("expected ':' following a condition in !cond operator"); + return nullptr; + } + Lex.Lex(); // eat the ':' + + V = ParseValue(CurRec, ItemType); + if (!V) + return nullptr; + Val.push_back(V); + + if (Lex.getCode() == tgtok::r_paren) { + Lex.Lex(); // eat the ')' + break; + } + + if (Lex.getCode() != tgtok::comma) { + TokError("expected ',' or ')' following a value in !cond operator"); + return nullptr; + } + Lex.Lex(); // eat the ',' + } + + 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(Vbits->getNumBits()); + if (isa<BitInit>(V)) + VTy = BitRecTy::get(); + + 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 ::= SHLTOK '(' Value ',' Value ')' +/// SimpleValue ::= SRATOK '(' Value ',' Value ')' +/// SimpleValue ::= SRLTOK '(' Value ',' Value ')' +/// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')' +/// SimpleValue ::= LISTSPLATTOK '(' 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 token when parsing a value"); break; + case tgtok::paste: + // This is a leading paste operation. This is deprecated but + // still exists in some .td files. Ignore it. + Lex.Lex(); // Skip '#'. + return ParseSimpleValue(CurRec, ItemType, Mode); + case tgtok::IntVal: R = IntInit::get(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(BinaryVal.first & (1LL << i)); + R = BitsInit::get(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(Val); + break; + } + case tgtok::CodeFragment: + R = CodeInit::get(Lex.getCurStrVal(), Lex.getLoc()); + Lex.Lex(); + break; + case tgtok::question: + R = UnsetInit::get(); + Lex.Lex(); + break; + case tgtok::Id: { + SMLoc NameLoc = Lex.getLoc(); + StringInit *Name = StringInit::get(Lex.getCurStrVal()); + if (Lex.Lex() != tgtok::less) // consume the Id. + return ParseIDValue(CurRec, Name, NameLoc, Mode); // Value ::= IDValue + + // Value ::= ID '<' ValueListNE '>' + if (Lex.Lex() == tgtok::greater) { + TokError("expected non-empty value list"); + return nullptr; + } + + // This is a CLASS<initvalslist> expression. This is supposed to synthesize + // a new anonymous definition, deriving from CLASS<initvalslist> with no + // body. + Record *Class = Records.getClass(Name->getValue()); + if (!Class) { + Error(NameLoc, "Expected a class name, got '" + Name->getValue() + "'"); + return nullptr; + } + + SmallVector<Init *, 8> Args; + ParseValueList(Args, CurRec, Class); + if (Args.empty()) return nullptr; + + if (Lex.getCode() != tgtok::greater) { + TokError("expected '>' at end of value list"); + return nullptr; + } + Lex.Lex(); // eat the '>' + + // Typecheck the template arguments list + ArrayRef<Init *> ExpectedArgs = Class->getTemplateArgs(); + if (ExpectedArgs.size() < Args.size()) { + Error(NameLoc, + "More template args specified than expected"); + return nullptr; + } + + for (unsigned i = 0, e = ExpectedArgs.size(); i != e; ++i) { + RecordVal *ExpectedArg = Class->getValue(ExpectedArgs[i]); + if (i < Args.size()) { + if (TypedInit *TI = dyn_cast<TypedInit>(Args[i])) { + RecTy *ExpectedType = ExpectedArg->getType(); + if (!TI->getType()->typeIsConvertibleTo(ExpectedType)) { + Error(NameLoc, + "Value specified for template argument #" + Twine(i) + " (" + + ExpectedArg->getNameInitAsString() + ") is of type '" + + TI->getType()->getAsString() + "', expected '" + + ExpectedType->getAsString() + "': " + TI->getAsString()); + return nullptr; + } + continue; + } + } else if (ExpectedArg->getValue()->isComplete()) + continue; + + Error(NameLoc, + "Value not specified for template argument #" + Twine(i) + " (" + + ExpectedArgs[i]->getAsUnquotedString() + ")"); + return nullptr; + } + + 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 (Lex.getCode() != tgtok::r_brace) { + TokError("expected '}' at end of bit list value"); + return nullptr; + } + Lex.Lex(); // eat the '}' + + 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()); + 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(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("Type mismatch for list, expected list type, got ") + + ItemType->getAsString()); + return nullptr; + } + GivenListTy = ListType; + } + + if (Lex.getCode() != tgtok::r_square) { + ParseValueList(Vals, CurRec, nullptr, + GivenListTy ? GivenListTy->getElementType() : nullptr); + if (Vals.empty()) return nullptr; + } + if (Lex.getCode() != tgtok::r_square) { + TokError("expected ']' at end of list value"); + return nullptr; + } + Lex.Lex(); // eat the ']' + + RecTy *GivenEltTy = nullptr; + if (Lex.getCode() == tgtok::less) { + // Optional list element type + Lex.Lex(); // eat the '<' + + GivenEltTy = ParseType(); + if (!GivenEltTy) { + // Couldn't parse element type + return nullptr; + } + + if (Lex.getCode() != tgtok::greater) { + TokError("expected '>' at end of list element type"); + return nullptr; + } + Lex.Lex(); // eat the '>' + } + + // 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) { + 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 (Lex.getCode() == tgtok::colon) { + if (Lex.Lex() != tgtok::VarName) { // eat the ':' + TokError("expected variable name in dag operator"); + return nullptr; + } + OperatorName = StringInit::get(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 (Lex.getCode() != tgtok::r_paren) { + TokError("expected ')' in dag init"); + return nullptr; + } + Lex.Lex(); // eat the ')' + + return DagInit::get(Operator, OperatorName, DagArgs); + } + + case tgtok::XHead: + case tgtok::XTail: + case tgtok::XSize: + case tgtok::XEmpty: + case tgtok::XCast: // Value ::= !unop '(' Value ')' + case tgtok::XIsA: + case tgtok::XConcat: + case tgtok::XDag: + case tgtok::XADD: + case tgtok::XMUL: + case tgtok::XAND: + case tgtok::XOR: + 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::XStrConcat: // Value ::= !binop '(' Value ',' Value ')' + case tgtok::XIf: + case tgtok::XCond: + case tgtok::XFoldl: + case tgtok::XForEach: + case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' + return ParseOperation(CurRec, ItemType); + } + } + + return R; +} + +/// ParseValue - Parse a tblgen 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 (Lex.getCode() != tgtok::r_brace) { + TokError("expected '}' at end of bit range list"); + return nullptr; + } + Lex.Lex(); + 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 (Lex.getCode() != tgtok::r_square) { + TokError("expected ']' at end of list slice"); + return nullptr; + } + Lex.Lex(); + break; + } + case tgtok::period: { + if (Lex.Lex() != tgtok::Id) { // eat the . + TokError("expected field identifier after '.'"); + return nullptr; + } + StringInit *FieldName = StringInit::get(Lex.getCurStrVal()); + if (!Result->getFieldType(FieldName)) { + TokError("Cannot access field '" + Lex.getCurStrVal() + "' of value '" + + Result->getAsString() + "'"); + return nullptr; + } + 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 '#'. + + 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, ParseNameMode); + Result = BinOpInit::getListConcat(LHS, RHSResult); + } + break; + } + + // Create a !strconcat() operation, first casting each operand to + // a string if necessary. + if (LHS->getType() != StringRecTy::get()) { + auto CastLHS = dyn_cast<TypedInit>( + UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get()) + ->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(""); + break; + + default: + Init *RHSResult = ParseValue(CurRec, nullptr, ParseNameMode); + RHS = dyn_cast<TypedInit>(RHSResult); + if (!RHS) { + Error(PasteLoc, "RHS of paste is not typed!"); + return nullptr; + } + + if (RHS->getType() != StringRecTy::get()) { + auto CastRHS = dyn_cast<TypedInit>( + UnOpInit::get(UnOpInit::CAST, RHS, StringRecTy::get()) + ->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(Lex.getCurStrVal()); + Result.emplace_back(UnsetInit::get(), 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(Lex.getCurStrVal()); + Lex.Lex(); // eat the VarName. + } + + Result.push_back(std::make_pair(Val, VarName)); + } + if (Lex.getCode() != tgtok::comma) break; + Lex.Lex(); // eat the ',' + } +} + +/// ParseValueList - Parse a comma separated list of values, returning them as 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, + Record *ArgsRec, RecTy *EltTy) { + RecTy *ItemType = EltTy; + unsigned int ArgN = 0; + if (ArgsRec && !EltTy) { + ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs(); + if (TArgs.empty()) { + TokError("template argument provided to non-template class"); + Result.clear(); + return; + } + const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]); + if (!RV) { + errs() << "Cannot find template arg " << ArgN << " (" << TArgs[ArgN] + << ")\n"; + } + assert(RV && "Template argument record not found??"); + ItemType = RV->getType(); + ++ArgN; + } + Result.push_back(ParseValue(CurRec, ItemType)); + if (!Result.back()) { + Result.clear(); + return; + } + + while (Lex.getCode() == tgtok::comma) { + Lex.Lex(); // Eat the comma + + // ignore trailing comma for lists + if (Lex.getCode() == tgtok::r_square) + return; + + if (ArgsRec && !EltTy) { + ArrayRef<Init *> TArgs = ArgsRec->getTemplateArgs(); + if (ArgN >= TArgs.size()) { + TokError("too many template arguments"); + Result.clear(); + return; + } + const RecordVal *RV = ArgsRec->getValue(TArgs[ArgN]); + assert(RV && "Template argument record not found??"); + ItemType = RV->getType(); + ++ArgN; + } + Result.push_back(ParseValue(CurRec, ItemType)); + if (!Result.back()) { + Result.clear(); + return; + } + } +} + +/// ParseDeclaration - Read a declaration, returning the name of field ID, or an +/// empty string on error. This can happen in a number of different context's, +/// including within a def or in the template args for a def (which 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 = Lex.getCode() == tgtok::Field; + if (HasField) Lex.Lex(); + + 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(Str); + Lex.Lex(); + + if (ParsingTemplateArgs) { + if (CurRec) + DeclName = QualifyName(*CurRec, CurMultiClass, DeclName, ":"); + else + assert(CurMultiClass); + if (CurMultiClass) + DeclName = QualifyName(CurMultiClass->Rec, CurMultiClass, DeclName, + "::"); + } + + // Add the value. + if (AddValue(CurRec, IdLoc, RecordVal(DeclName, Type, HasField))) + return nullptr; + + // If a value is present, parse it. + if (Lex.getCode() == tgtok::equal) { + Lex.Lex(); + SMLoc ValLoc = Lex.getLoc(); + Init *Val = ParseValue(CurRec, Type); + if (!Val || + SetValue(CurRec, ValLoc, DeclName, None, Val)) + // 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(Lex.getCurStrVal()); + Lex.Lex(); + + // If a value is present, parse it. + if (Lex.getCode() != tgtok::equal) { + TokError("Expected '=' in foreach declaration"); + return nullptr; + } + Lex.Lex(); // Eat the '=' + + RecTy *IterType = nullptr; + SmallVector<unsigned, 16> Ranges; + + switch (Lex.getCode()) { + case tgtok::l_brace: { // '{' RangeList '}' + Lex.Lex(); // eat the '{' + ParseRangeList(Ranges); + if (Lex.getCode() != tgtok::r_brace) { + TokError("expected '}' at end of bit range list"); + return nullptr; + } + Lex.Lex(); + 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; + } + + std::string Type; + if (TI) + Type = (Twine("' of type '") + TI->getType()->getAsString()).str(); + Error(ValueLoc, "expected a list, got '" + I->getAsString() + Type + "'"); + 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(); + std::vector<Init*> Values; + for (unsigned R : Ranges) + Values.push_back(IntInit::get(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 def, which may or may not be in a multiclass. 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 (Lex.getCode() == tgtok::comma) { + Lex.Lex(); // eat the ',' + + // 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 (Lex.getCode() != tgtok::greater) + return TokError("expected '>' at end of template argument list"); + Lex.Lex(); // eat the '>'. + return false; +} + +/// ParseBodyItem - Parse a single item at within the body of a def or class. +/// +/// BodyItem ::= Declaration ';' +/// BodyItem ::= LET ID OptionalBitList '=' Value ';' +bool TGParser::ParseBodyItem(Record *CurRec) { + if (Lex.getCode() != tgtok::Let) { + if (!ParseDeclaration(CurRec, false)) + return true; + + if (Lex.getCode() != tgtok::semi) + return TokError("expected ';' after declaration"); + Lex.Lex(); + 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(Lex.getCurStrVal()); + Lex.Lex(); // eat the field name. + + SmallVector<unsigned, 16> BitList; + if (ParseOptionalBitList(BitList)) + return true; + std::reverse(BitList.begin(), BitList.end()); + + if (Lex.getCode() != tgtok::equal) + return TokError("expected '=' in let expression"); + Lex.Lex(); // eat the '='. + + RecordVal *Field = CurRec->getValue(FieldName); + if (!Field) + return TokError("Value '" + FieldName->getValue() + "' unknown!"); + + RecTy *Type = Field->getType(); + + Init *Val = ParseValue(CurRec, Type); + if (!Val) return true; + + if (Lex.getCode() != tgtok::semi) + return TokError("expected ';' after let expression"); + Lex.Lex(); + + 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 (Lex.getCode() == tgtok::semi) { + Lex.Lex(); + return false; + } + + if (Lex.getCode() != tgtok::l_brace) + return TokError("Expected ';' or '{' to start body"); + // Eat the '{'. + Lex.Lex(); + + while (Lex.getCode() != tgtok::r_brace) + if (ParseBodyItem(CurRec)) + return true; + + // Eat the '}'. + Lex.Lex(); + 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; +} + +bool TGParser::ApplyLetStack(RecordsEntry &Entry) { + if (Entry.Rec) + return ApplyLetStack(Entry.Rec.get()); + + 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 (Lex.getCode() == tgtok::colon) { + Lex.Lex(); + + // 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 (Lex.getCode() != tgtok::comma) break; + Lex.Lex(); // eat ','. + SubClass = ParseSubClassReference(CurRec, false); + } + } + + if (ApplyLetStack(CurRec)) + return true; + + return ParseBody(CurRec); +} + +/// ParseDef - Parse and return a top level or multiclass def, return the record +/// corresponding to it. This returns null on 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. + + // 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, DefLoc, 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(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 (Lex.getCode() != tgtok::r_brace) { + TokError("expected '}' at end of defset"); + return Error(BraceLoc, "to match this '{'"); + } + Lex.Lex(); // Eat the '}' + + Records.addExtraGlobal(DeclName->getValue(), + ListInit::get(Defset.Elements, Defset.EltTy)); + 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 (Lex.getCode() != tgtok::In) + return TokError("Unknown tok"); + Lex.Lex(); // Eat the in + + // Create a loop object and remember it. + Loops.push_back(std::make_unique<ForeachLoop>(Loc, IterName, ListValue)); + + 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 (Lex.getCode() != tgtok::r_brace) { + TokError("expected '}' at end of foreach command"); + return Error(BraceLoc, "to match this '{'"); + } + Lex.Lex(); // Eat the } + } + + // 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)); +} + +/// 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"); + } 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; + + return ParseObjectBody(CurRec); +} + +/// 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) { + while (true) { + if (Lex.getCode() != tgtok::Id) { + TokError("expected identifier in let definition"); + Result.clear(); + return; + } + + StringInit *Name = StringInit::get(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 (Lex.getCode() != tgtok::equal) { + TokError("expected '=' in let expression"); + Result.clear(); + return; + } + Lex.Lex(); // eat the '='. + + Init *Val = ParseValue(nullptr); + if (!Val) { + Result.clear(); + return; + } + + // Now that we have everything, add the record. + Result.emplace_back(Name, Bits, Val, NameLoc); + + if (Lex.getCode() != tgtok::comma) + return; + Lex.Lex(); // eat the 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 (Lex.getCode() != tgtok::In) + return TokError("expected 'in' at end of top-level 'let'"); + Lex.Lex(); + + // 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 (Lex.getCode() != tgtok::r_brace) { + TokError("expected '}' at end of top level let command"); + return Error(BraceLoc, "to match this '{'"); + } + Lex.Lex(); + } + + // 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 ::= DefInst +/// MultiClassObject ::= MultiClassInst +/// MultiClassObject ::= DefMInst +/// 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 (Lex.getCode() == tgtok::colon) { + inherits = true; + + Lex.Lex(); + + // 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 (Lex.getCode() != tgtok::comma) break; + Lex.Lex(); // eat ','. + SubMultiClass = ParseSubMultiClassReference(CurMultiClass); + } + } + + if (Lex.getCode() != tgtok::l_brace) { + if (!inherits) + return TokError("expected '{' in multiclass definition"); + if (Lex.getCode() != tgtok::semi) + return TokError("expected ';' in multiclass definition"); + Lex.Lex(); // eat the ';'. + } else { + if (Lex.Lex() == tgtok::r_brace) // eat the '{'. + return TokError("multiclass must contain at least one def"); + + while (Lex.getCode() != tgtok::r_brace) { + switch (Lex.getCode()) { + default: + return TokError("expected 'let', 'def', 'defm' or 'foreach' in " + "multiclass body"); + case tgtok::Let: + case tgtok::Def: + case tgtok::Defm: + case tgtok::Foreach: + if (ParseObject(CurMultiClass)) + return true; + break; + } + } + Lex.Lex(); // eat the '}'. + } + + 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()), + 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 need to first get the multiclass, then + // instantiate each def contained in the multiclass with the SubClassRef + // template parameters. + MultiClass *MC = MultiClasses[Ref.Rec->getName()].get(); + assert(MC && "Didn't lookup multiclass correctly?"); + ArrayRef<Init*> TemplateVals = Ref.TemplateArgs; + + // Verify that the correct number of template arguments were specified. + ArrayRef<Init *> TArgs = MC->Rec.getTemplateArgs(); + if (TArgs.size() < TemplateVals.size()) + return Error(SubClassLoc, + "more template args specified than multiclass expects"); + + 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 #" + + Twine(i) + " (" + TArgs[i]->getAsUnquotedString() + + ") of multiclass '" + MC->Rec.getNameInitAsString() + + "'"); + } + Substs.emplace_back(TArgs[i], Default); + } + } + + Substs.emplace_back(QualifiedNameOfImplicitName(MC), DefmName); + + if (resolve(MC->Entries, Substs, CurMultiClass == nullptr, &NewEntries, + &SubClassLoc)) + return true; + + if (Lex.getCode() != tgtok::comma) break; + Lex.Lex(); // eat ','. + + if (Lex.getCode() != tgtok::Id) + return TokError("expected identifier"); + + SubClassLoc = Lex.getLoc(); + + // A defm can inherit from regular classes (non-multiclass) 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 (Lex.getCode() != tgtok::comma) break; + Lex.Lex(); // eat ','. + SubClass = ParseSubClassReference(nullptr, false); + } + } + + for (auto &E : NewEntries) { + if (ApplyLetStack(E)) + return true; + + addEntry(std::move(E)); + } + + if (Lex.getCode() != tgtok::semi) + return TokError("expected ';' at end of defm"); + Lex.Lex(); + + return false; +} + +/// ParseObject +/// Object ::= ClassInst +/// Object ::= DefInst +/// Object ::= MultiClassInst +/// Object ::= DefMInst +/// Object ::= LETCommand '{' ObjectList '}' +/// Object ::= LETCommand Object +bool TGParser::ParseObject(MultiClass *MC) { + switch (Lex.getCode()) { + default: + return TokError("Expected class, def, defm, defset, multiclass, let or " + "foreach"); + case tgtok::Let: return ParseTopLevelLet(MC); + case tgtok::Def: return ParseDef(MC); + case tgtok::Foreach: return ParseForeach(MC); + case tgtok::Defm: return ParseDefm(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 input at top level"); +} + +#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/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h new file mode 100644 index 000000000000..af2b639f8d59 --- /dev/null +++ b/llvm/lib/TableGen/TGParser.h @@ -0,0 +1,208 @@ +//===- 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/ADT/Twine.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include <map> + +namespace llvm { + class Record; + class RecordVal; + class RecordKeeper; + class RecTy; + class Init; + 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 - Can be either a record or a foreach loop. + struct RecordsEntry { + std::unique_ptr<Record> Rec; + std::unique_ptr<ForeachLoop> Loop; + + void dump() const; + + RecordsEntry() {} + RecordsEntry(std::unique_ptr<Record> Rec) : Rec(std::move(Rec)) {} + RecordsEntry(std::unique_ptr<ForeachLoop> Loop) + : Loop(std::move(Loop)) {} + }; + + /// ForeachLoop - Record the iteration state associated with a for loop. + /// This is used to instantiate items in the loop body. + 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; + SmallVector<Init *, 16> Elements; + }; + +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; + + // 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. + }; + +public: + TGParser(SourceMgr &SrcMgr, ArrayRef<std::string> Macros, + RecordKeeper &records) + : Lex(SrcMgr, Macros), CurMultiClass(nullptr), Records(records) {} + + /// 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::DependenciesMapTy &getDependencies() const { + return Lex.getDependencies(); + } + +private: // Semantic analysis methods. + bool AddValue(Record *TheRec, SMLoc Loc, const RecordVal &RV); + bool SetValue(Record *TheRec, SMLoc Loc, Init *ValName, + ArrayRef<unsigned> BitList, Init *V, + bool AllowSelfAssignment = false); + 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 ParseObjectList(MultiClass *MC = nullptr); + bool ParseObject(MultiClass *MC); + bool ParseClass(); + bool ParseMultiClass(); + bool ParseDefm(MultiClass *CurMultiClass); + bool ParseDef(MultiClass *CurMultiClass); + bool ParseDefset(); + bool ParseForeach(MultiClass *CurMultiClass); + 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, SMLoc 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, + Record *ArgsRec = nullptr, RecTy *EltTy = nullptr); + 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 *ParseOperationCond(Record *CurRec, RecTy *ItemType); + RecTy *ParseOperatorType(); + Init *ParseObjectName(MultiClass *CurMultiClass); + Record *ParseClassID(); + MultiClass *ParseMultiClassID(); + bool ApplyLetStack(Record *CurRec); + bool ApplyLetStack(RecordsEntry &Entry); +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/TableGen/TableGenBackend.cpp b/llvm/lib/TableGen/TableGenBackend.cpp new file mode 100644 index 000000000000..e11b28e8cff9 --- /dev/null +++ b/llvm/lib/TableGen/TableGenBackend.cpp @@ -0,0 +1,52 @@ +//===- 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" + +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'; +} |