diff options
author | Simon Tatham <simon.tatham@arm.com> | 2020-01-14 09:10:18 +0000 |
---|---|---|
committer | Simon Tatham <simon.tatham@arm.com> | 2020-01-14 10:19:53 +0000 |
commit | 3388b0f59dcc7813278c753f96b66229f290cc59 (patch) | |
tree | d64f8dcbead51b2deb74e75afe7f86b4516bb5e5 /llvm/lib | |
parent | 45924eb4671692b3fa9fd52fe39c81ec0647a848 (diff) | |
download | bcm5719-llvm-3388b0f59dcc7813278c753f96b66229f290cc59.tar.gz bcm5719-llvm-3388b0f59dcc7813278c753f96b66229f290cc59.zip |
[TableGen] Introduce a `defvar` statement.
Summary:
This allows you to define a global or local variable to an arbitrary
value, and refer to it in subsequent definitions.
The main use I anticipate for this is if you have to compute some
difficult function of the parameters of a multiclass, and then use it
many times. For example:
multiclass Foo<int i, string s> {
defvar op = !cast<BaseClass>("whatnot_" # s # "_" # i);
def myRecord {
dag a = (op this, (op that, the other), (op x, y, z));
int b = op.subfield;
}
def myOtherRecord<"template params including", op>;
}
There are a couple of ways to do this already, but they're not really
satisfactory. You can replace `defvar x = y` with a loop over a
singleton list, `foreach x = [y] in { ... }` - but that's unintuitive
to someone who hasn't seen that workaround idiom before, and requires
an extra pair of braces that you often didn't really want. Or you can
define a nested pair of multiclasses, with the inner one taking `x` as
a template parameter, and the outer one instantiating it just once
with the desired value of `x` computed from its other parameters - but
that makes it awkward to sequentially compute each value based on the
previous ones. I think `defvar` makes things considerably easier.
You can also use `defvar` at the top level, where it inserts globals
into the same map used by `defset`. That allows you to define global
constants without having to make a dummy record for them to live in:
defvar MAX_BUFSIZE = 512;
// previously:
// def Dummy { int MAX_BUFSIZE = 512; }
// and then refer to Dummy.MAX_BUFSIZE everywhere
Reviewers: nhaehnle, hfinkel
Reviewed By: hfinkel
Subscribers: hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D71407
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/TableGen/TGLexer.cpp | 1 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGLexer.h | 2 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGParser.cpp | 77 | ||||
-rw-r--r-- | llvm/lib/TableGen/TGParser.h | 60 |
4 files changed, 135 insertions, 5 deletions
diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp index d22c96e8129..c160704b6e7 100644 --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -350,6 +350,7 @@ tgtok::TokKind TGLexer::LexIdentifier() { .Case("field", tgtok::Field) .Case("let", tgtok::Let) .Case("in", tgtok::In) + .Case("defvar", tgtok::Defvar) .Default(tgtok::Id); if (Kind == tgtok::Id) diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h index b5cf4bd7374..04c72900b0b 100644 --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -46,7 +46,7 @@ namespace tgtok { // Keywords. Bit, Bits, Class, Code, Dag, Def, Foreach, Defm, Field, In, Int, Let, List, - MultiClass, String, Defset, + MultiClass, String, Defset, Defvar, // !keywords. XConcat, XADD, XMUL, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XListSplat, diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp index 71786328c46..34d993c518a 100644 --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -482,7 +482,7 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) { static bool isObjectStart(tgtok::TokKind K) { return K == tgtok::Class || K == tgtok::Def || K == tgtok::Defm || K == tgtok::Let || K == tgtok::MultiClass || K == tgtok::Foreach || - K == tgtok::Defset; + K == tgtok::Defset || K == tgtok::Defvar; } /// ParseObjectName - If a valid object name is specified, return it. If no @@ -869,6 +869,10 @@ Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc, } } + if (CurLocalScope) + if (Init *I = CurLocalScope->getVar(Name->getValue())) + return I; + // If this is in a foreach loop, make sure it's not a loop iterator for (const auto &L : Loops) { VarInit *IterVar = dyn_cast<VarInit>(L->IterVar); @@ -2595,7 +2599,11 @@ bool TGParser::ParseTemplateArgList(Record *CurRec) { /// /// BodyItem ::= Declaration ';' /// BodyItem ::= LET ID OptionalBitList '=' Value ';' +/// BodyItem ::= Defvar bool TGParser::ParseBodyItem(Record *CurRec) { + if (Lex.getCode() == tgtok::Defvar) + return ParseDefvar(); + if (Lex.getCode() != tgtok::Let) { if (!ParseDeclaration(CurRec, false)) return true; @@ -2658,10 +2666,15 @@ bool TGParser::ParseBody(Record *CurRec) { // Eat the '{'. Lex.Lex(); + // An object body introduces a new scope for local variables. + TGLocalVarScope *BodyScope = PushLocalScope(); + while (Lex.getCode() != tgtok::r_brace) if (ParseBodyItem(CurRec)) return true; + PopLocalScope(BodyScope); + // Eat the '}'. Lex.Lex(); return false; @@ -2800,6 +2813,45 @@ bool TGParser::ParseDefset() { return false; } +/// ParseDefvar - Parse a defvar statement. +/// +/// Defvar ::= DEFVAR Id '=' Value ';' +/// +bool TGParser::ParseDefvar() { + assert(Lex.getCode() == tgtok::Defvar); + Lex.Lex(); // Eat the 'defvar' token + + if (Lex.getCode() != tgtok::Id) + return TokError("expected identifier"); + StringInit *DeclName = StringInit::get(Lex.getCurStrVal()); + if (CurLocalScope) { + if (CurLocalScope->varAlreadyDefined(DeclName->getValue())) + return TokError("local variable of this name already exists"); + } else { + if (Records.getGlobal(DeclName->getValue())) + return TokError("def or global variable of this name already exists"); + } + + if (Lex.Lex() != tgtok::equal) // Eat the identifier + return TokError("expected '='"); + Lex.Lex(); // Eat the '=' + + Init *Value = ParseValue(nullptr); + if (!Value) + return true; + + if (Lex.getCode() != tgtok::semi) + return TokError("expected ';'"); + Lex.Lex(); // Eat the ';' + + if (CurLocalScope) + CurLocalScope->addVar(DeclName->getValue(), Value); + else + Records.addExtraGlobal(DeclName->getValue(), Value); + + return false; +} + /// ParseForeach - Parse a for statement. Return the record corresponding /// to it. This returns true on error. /// @@ -2825,6 +2877,9 @@ bool TGParser::ParseForeach(MultiClass *CurMultiClass) { // Create a loop object and remember it. Loops.push_back(std::make_unique<ForeachLoop>(Loc, IterName, ListValue)); + // A foreach loop introduces a new scope for local variables. + TGLocalVarScope *ForeachScope = PushLocalScope(); + if (Lex.getCode() != tgtok::l_brace) { // FOREACH Declaration IN Object if (ParseObject(CurMultiClass)) @@ -2845,6 +2900,8 @@ bool TGParser::ParseForeach(MultiClass *CurMultiClass) { Lex.Lex(); // Eat the } } + PopLocalScope(ForeachScope); + // Resolve the loop or store it for later resolution. std::unique_ptr<ForeachLoop> Loop = std::move(Loops.back()); Loops.pop_back(); @@ -2957,6 +3014,8 @@ bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) { return TokError("expected 'in' at end of top-level 'let'"); Lex.Lex(); + TGLocalVarScope *LetScope = PushLocalScope(); + // If this is a scalar let, just handle it now if (Lex.getCode() != tgtok::l_brace) { // LET LetList IN Object @@ -2978,6 +3037,8 @@ bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) { Lex.Lex(); } + PopLocalScope(LetScope); + // Outside this let scope, this let block is not active. LetStack.pop_back(); return false; @@ -3051,14 +3112,18 @@ bool TGParser::ParseMultiClass() { if (Lex.Lex() == tgtok::r_brace) // eat the '{'. return TokError("multiclass must contain at least one def"); + // A multiclass body introduces a new scope for local variables. + TGLocalVarScope *MulticlassScope = PushLocalScope(); + while (Lex.getCode() != tgtok::r_brace) { switch (Lex.getCode()) { default: - return TokError("expected 'let', 'def', 'defm' or 'foreach' in " - "multiclass body"); + return TokError("expected 'let', 'def', 'defm', 'defvar' or 'foreach' " + "in multiclass body"); case tgtok::Let: case tgtok::Def: case tgtok::Defm: + case tgtok::Defvar: case tgtok::Foreach: if (ParseObject(CurMultiClass)) return true; @@ -3066,6 +3131,8 @@ bool TGParser::ParseMultiClass() { } } Lex.Lex(); // eat the '}'. + + PopLocalScope(MulticlassScope); } CurMultiClass = nullptr; @@ -3207,6 +3274,8 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) { /// Object ::= DefMInst /// Object ::= LETCommand '{' ObjectList '}' /// Object ::= LETCommand Object +/// Object ::= Defset +/// Object ::= Defvar bool TGParser::ParseObject(MultiClass *MC) { switch (Lex.getCode()) { default: @@ -3220,6 +3289,8 @@ bool TGParser::ParseObject(MultiClass *MC) { if (MC) return TokError("defset is not allowed inside multiclass"); return ParseDefset(); + case tgtok::Defvar: + return ParseDefvar(); case tgtok::Class: if (MC) return TokError("class is not allowed inside multiclass"); diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h index cbe0b545d0f..edcac1e8fa7 100644 --- a/llvm/lib/TableGen/TGParser.h +++ b/llvm/lib/TableGen/TGParser.h @@ -74,6 +74,46 @@ namespace llvm { SmallVector<Init *, 16> Elements; }; +class TGLocalVarScope { + // A scope to hold local variable definitions from defvar. + std::map<std::string, Init *, std::less<>> vars; + std::unique_ptr<TGLocalVarScope> parent; + +public: + TGLocalVarScope() = default; + TGLocalVarScope(std::unique_ptr<TGLocalVarScope> parent) + : parent(std::move(parent)) {} + + std::unique_ptr<TGLocalVarScope> extractParent() { + // This is expected to be called just before we are destructed, so + // it doesn't much matter what state we leave 'parent' in. + return std::move(parent); + } + + Init *getVar(StringRef Name) const { + auto It = vars.find(Name); + if (It != vars.end()) + return It->second; + if (parent) + return parent->getVar(Name); + return nullptr; + } + + bool varAlreadyDefined(StringRef Name) const { + // When we check whether a variable is already defined, for the purpose of + // reporting an error on redefinition, we don't look up to the parent + // scope, because it's all right to shadow an outer definition with an + // inner one. + return vars.find(Name) != vars.end(); + } + + void addVar(StringRef Name, Init *I) { + bool Ins = vars.insert(std::make_pair(Name, I)).second; + (void)Ins; + assert(Ins && "Local variable already exists"); + } +}; + struct MultiClass { Record Rec; // Placeholder for template args and Name. std::vector<RecordsEntry> Entries; @@ -99,6 +139,10 @@ class TGParser { /// current value. MultiClass *CurMultiClass; + /// CurLocalScope - Innermost of the current nested scopes for 'defvar' local + /// variables. + std::unique_ptr<TGLocalVarScope> CurLocalScope; + // Record tracker RecordKeeper &Records; @@ -133,7 +177,20 @@ public: return Lex.getDependencies(); } -private: // Semantic analysis methods. + TGLocalVarScope *PushLocalScope() { + CurLocalScope = std::make_unique<TGLocalVarScope>(std::move(CurLocalScope)); + // Returns a pointer to the new scope, so that the caller can pass it back + // to PopLocalScope which will check by assertion that the pushes and pops + // match up properly. + return CurLocalScope.get(); + } + void PopLocalScope(TGLocalVarScope *ExpectedStackTop) { + assert(ExpectedStackTop == CurLocalScope.get() && + "Mismatched pushes and pops of local variable scopes"); + CurLocalScope = CurLocalScope->extractParent(); + } + +private: // Semantic analysis methods. bool AddValue(Record *TheRec, SMLoc Loc, const RecordVal &RV); bool SetValue(Record *TheRec, SMLoc Loc, Init *ValName, ArrayRef<unsigned> BitList, Init *V, @@ -161,6 +218,7 @@ private: // Parser methods. bool ParseDefm(MultiClass *CurMultiClass); bool ParseDef(MultiClass *CurMultiClass); bool ParseDefset(); + bool ParseDefvar(); bool ParseForeach(MultiClass *CurMultiClass); bool ParseTopLevelLet(MultiClass *CurMultiClass); void ParseLetList(SmallVectorImpl<LetRecord> &Result); |