diff --git a/llvm/docs/TableGen/LangRef.rst b/llvm/docs/TableGen/LangRef.rst --- a/llvm/docs/TableGen/LangRef.rst +++ b/llvm/docs/TableGen/LangRef.rst @@ -124,8 +124,8 @@ .. productionlist:: TableGenFile: `Object`* - Object: `Class` | `Def` | `Defm` | `Defset` | `Let` | `MultiClass` | - `Foreach` + Object: `Class` | `Def` | `Defm` | `Defset` | `Defvar` | `Let` | + `MultiClass` | `Foreach` ``class``\es ------------ @@ -262,7 +262,7 @@ foreach i = 0-5 in def Foo#i; -* a variable defined by ``defset`` +* a variable defined by ``defset`` or ``defvar`` * the implicit template argument ``NAME`` in a ``class`` or ``multiclass`` @@ -348,6 +348,7 @@ BodyList: BodyItem* BodyItem: `Declaration` ";" :| "let" `TokIdentifier` [ "{" `RangeList` "}" ] "=" `Value` ";" + :| `Defvar` The ``let`` form allows overriding the value of an inherited field. @@ -359,8 +360,8 @@ Defines a record whose name is given by the optional :token:`Value`. The value is parsed in a special mode where global identifiers (records and variables -defined by ``defset``) are not recognized, and all unrecognized identifiers -are interpreted as strings. +defined by ``defset``, and variables defined at global scope by ``defvar``) are +not recognized, and all unrecognized identifiers are interpreted as strings. If no name is given, the record is anonymous. The final name of anonymous records is undefined, but globally unique. @@ -420,6 +421,36 @@ to define a record (via ``def`` or ``defm``) inside the braces which doesn't derive from ``A``. +``defvar`` +---------- +.. productionlist:: + Defvar: "defvar" `TokIdentifier` "=" `Value` ";" + +The identifier on the left of the ``=`` is defined to be a global or local +variable, whose value is given by the expression on the right of the ``=``. The +type of the variable is automatically inferred. + +A ``defvar`` statement at the top level of the file defines a global variable, +in the same scope used by ``defset``. If a ``defvar`` statement appears inside +any other construction, including classes, multiclasses and ``foreach`` +statements, then the variable is scoped to the inside of that construction +only. + +In contexts where the ``defvar`` statement will be encountered multiple times, +the definition is re-evaluated for each instance. For example, a ``defvar`` +inside a ``foreach`` can construct a value based on the iteration variable, +which will be different every time round the loop; a ``defvar`` inside a +templated class or multiclass can have a definition depending on the template +parameters. + +Variables local to a ``foreach`` go out of scope at the end of each loop +iteration, so their previous value is not accessible in the next iteration. (It +won't work to ``defvar i=!add(i,1)`` each time you go round the loop.) + +In general, ``defvar`` variables are immutable once they are defined. It is an +error to define the same variable name twice in the same scope (but legal to +shadow the first definition temporarily in an inner scope). + ``foreach`` ----------- diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -46,7 +46,7 @@ // 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/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -350,6 +350,7 @@ .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/TGParser.h b/llvm/lib/TableGen/TGParser.h --- a/llvm/lib/TableGen/TGParser.h +++ b/llvm/lib/TableGen/TGParser.h @@ -74,6 +74,46 @@ SmallVector Elements; }; +class TGLocalVarScope { + // A scope to hold local variable definitions from defvar. + std::map> vars; + std::unique_ptr parent; + +public: + TGLocalVarScope() = default; + TGLocalVarScope(std::unique_ptr parent) + : parent(std::move(parent)) {} + + std::unique_ptr 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 Entries; @@ -99,6 +139,10 @@ /// current value. MultiClass *CurMultiClass; + /// CurLocalScope - Innermost of the current nested scopes for 'defvar' local + /// variables. + std::unique_ptr CurLocalScope; + // Record tracker RecordKeeper &Records; @@ -133,7 +177,20 @@ return Lex.getDependencies(); } -private: // Semantic analysis methods. + TGLocalVarScope *PushLocalScope() { + CurLocalScope = std::make_unique(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 BitList, Init *V, @@ -161,6 +218,7 @@ bool ParseDefm(MultiClass *CurMultiClass); bool ParseDef(MultiClass *CurMultiClass); bool ParseDefset(); + bool ParseDefvar(); bool ParseForeach(MultiClass *CurMultiClass); bool ParseTopLevelLet(MultiClass *CurMultiClass); void ParseLetList(SmallVectorImpl &Result); diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -482,7 +482,7 @@ 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 @@ } } + 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(L->IterVar); @@ -2595,7 +2599,11 @@ /// /// 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 @@ // 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 @@ 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 @@ // Create a loop object and remember it. Loops.push_back(std::make_unique(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 @@ Lex.Lex(); // Eat the } } + PopLocalScope(ForeachScope); + // Resolve the loop or store it for later resolution. std::unique_ptr Loop = std::move(Loops.back()); Loops.pop_back(); @@ -2957,6 +3014,8 @@ 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 @@ Lex.Lex(); } + PopLocalScope(LetScope); + // Outside this let scope, this let block is not active. LetStack.pop_back(); return false; @@ -3051,14 +3112,18 @@ 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 @@ } } Lex.Lex(); // eat the '}'. + + PopLocalScope(MulticlassScope); } CurMultiClass = nullptr; @@ -3207,6 +3274,8 @@ /// 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 @@ 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/test/TableGen/defvar.td b/llvm/test/TableGen/defvar.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/defvar.td @@ -0,0 +1,130 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s +// RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s +// RUN: not llvm-tblgen -DERROR3 %s 2>&1 | FileCheck --check-prefix=ERROR3 %s + +#ifdef ERROR1 +// Refer to a variable we haven't defined *yet*, expecting an error. +// ERROR1: [[@LINE+1]]:22: error: Variable not defined: 'myvar' +def bad { dag x = (? myvar); } +#endif + +// Define a global variable. +defvar myvar = "foo"; + +#ifdef ERROR2 +// Demonstrate an error when a global variable is redefined. +// ERROR2: [[@LINE+1]]:8: error: def or global variable of this name already exists +defvar myvar = "another value"; +#endif + +multiclass Test { + // Refer to a global variable, while inside a local scope like a multiclass. + def _with_global_string { string s = myvar; } + + // Define some variables local to this multiclass, and prove we can refer to + // those too. + defvar myvar = !add(x, 100); + defvar myvar2 = "string of " # myvar; + def _with_local_int { int i = myvar; string s = myvar2; } + +#ifdef ERROR3 + // Demonstrate an error when a local variable is redefined. + // ERROR3: [[@LINE+1]]:10: error: local variable of this name already exists + defvar myvar = "another value"; +#endif +} + +// Instantiate the above multiclass, and expect all the right outputs. + +// CHECK: def aaa_with_global_string { +// CHECK-NEXT: string s = "foo"; +// CHECK: def aaa_with_local_int { +// CHECK-NEXT: int i = 101; +// CHECK-NEXT: string s = "string of 101"; +// CHECK: def bbb_with_global_string { +// CHECK-NEXT: string s = "foo"; +// CHECK: def bbb_with_local_int { +// CHECK-NEXT: int i = 102; +// CHECK-NEXT: string s = "string of 102"; +defm aaa: Test<1>; +defm bbb: Test<2>; + +// Test that local variables can be defined inside a foreach block, and inside +// an object body. +// +// The scopes nest (you can refer to variables in an outer block from an inner +// one), and the variables go out of scope again at the end of the block (in +// particular, you don't get a redefinition error the next time round the +// loop). + +// CHECK: def nest_f1_s3 { +// CHECK-NEXT: int member = 113; +// CHECK-NEXT: } +// CHECK: def nest_f1_s4 { +// CHECK-NEXT: int member = 114; +// CHECK-NEXT: } +// CHECK: def nest_f2_s3 { +// CHECK-NEXT: int member = 123; +// CHECK-NEXT: } +// CHECK: def nest_f2_s4 { +// CHECK-NEXT: int member = 124; +// CHECK-NEXT: } +foreach first = [ 1, 2 ] in { + defvar firstStr = "f" # first; + foreach second = [ 3, 4 ] in { + defvar secondStr = "s" # second; + def "nest_" # firstStr # "_" # secondStr { + defvar defLocalVariable = !add(!mul(first, 10), second); + int member = !add(100, defLocalVariable); + } + } +} +defvar firstStr = "now define this at the top level and still expect no error"; + +// Test that you can shadow an outer declaration with an inner one. Here, we +// expect all the shadowOuter records (both above and below the inner foreach) +// to get the value 1 from the outer definition of shadowedVariable, and the +// shadowInner ones to get 2 from the inner definition. + +// CHECK: def shadowInner11 { +// CHECK-NEXT: int var = 2; +// CHECK: def shadowInner12 { +// CHECK-NEXT: int var = 2; +// CHECK: def shadowInner21 { +// CHECK-NEXT: int var = 2; +// CHECK: def shadowInner22 { +// CHECK-NEXT: int var = 2; +// CHECK: def shadowOuterAbove1 { +// CHECK-NEXT: int var = 1; +// CHECK: def shadowOuterAbove2 { +// CHECK-NEXT: int var = 1; +// CHECK: def shadowOuterBelowForeach1 { +// CHECK-NEXT: int var = 1; +// CHECK: def shadowOuterBelowForeach2 { +// CHECK-NEXT: int var = 1; + +foreach first = [ 1, 2 ] in { + defvar shadowedVariable = 1; + def shadowOuterAbove # first { int var = shadowedVariable; } + + // The foreach statement opens a new scope, in which a new variable of the + // same name can be defined without clashing with the outer one. + foreach second = [ 1, 2 ] in { + defvar shadowedVariable = 2; + def shadowInner # first # second { int var = shadowedVariable; } + } + + // Now the outer variable is back in scope. + def shadowOuterBelowForeach # first { int var = shadowedVariable; } +} + +// Test that a top-level let statement also makes a variable scope (on the +// general principle of consistency, because it defines a braced sub-block). + +let someVariable = "some value" in { + defvar myvar = "override the definition from above and expect no error"; +} +// CHECK: def topLevelLetTest { +// CHECK-NEXT: string val = "foo"; +def topLevelLetTest { string val = myvar; }