diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -1375,15 +1375,23 @@ In addition to defining global variables, the ``defvar`` statement can be used inside the :token:`Body` of a class or record definition to define -local variables. The scope of the variable extends from the ``defvar`` -statement to the end of the body. It cannot be set to a different value -within its scope. The ``defvar`` statement can also be used in the statement +local variables. Template arguments of ``class`` or ``multiclass`` can be +used in the value expression. The scope of the variable extends from the +``defvar`` statement to the end of the body. It cannot be set to a different +value within its scope. The ``defvar`` statement can also be used in the statement list of a ``foreach``, which establishes a scope. A variable named ``V`` in an inner scope shadows (hides) any variables ``V`` -in outer scopes. In particular, ``V`` in a record body shadows a global -``V``, and ``V`` in a ``foreach`` statement list shadows any ``V`` in -surrounding record or global scopes. +in outer scopes. In particular, there are several cases: + +* ``V`` in a record body shadows a global ``V``. + +* ``V`` in a record body shadows template argument ``V``. + +* ``V`` in template arguments shadows a global ``V``. + +* ``V`` in a ``foreach`` statement list shadows any ``V`` in surrounding record or + global scopes. Variables defined in a ``foreach`` go out of scope at the end of each loop iteration, so their value in one iteration is not available in 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 @@ -77,55 +77,65 @@ SmallVector Elements; }; -class TGLocalVarScope { - // A scope to hold local variable definitions from defvar. - std::map> vars; - std::unique_ptr parent; + struct MultiClass { + Record Rec; // Placeholder for template args and Name. + std::vector Entries; -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); - } + void dump() const; - 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; - } + MultiClass(StringRef Name, SMLoc Loc, RecordKeeper &Records) + : Rec(Name, Loc, Records) {} + }; - 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(); - } + class TGVarScope { + public: + enum ScopeKind { SK_Local, SK_Record, SK_ForeachLoop, SK_MultiClass }; + + private: + ScopeKind Kind; + std::unique_ptr Parent; + // A scope to hold variable definitions from defvar. + std::map> Vars; + Record *CurRec = nullptr; + ForeachLoop *CurLoop = nullptr; + MultiClass *CurMultiClass = nullptr; + + public: + TGVarScope(std::unique_ptr Parent) + : Kind(SK_Local), Parent(std::move(Parent)) {} + TGVarScope(std::unique_ptr Parent, Record *Rec) + : Kind(SK_Record), Parent(std::move(Parent)), CurRec(Rec) {} + TGVarScope(std::unique_ptr Parent, ForeachLoop *Loop) + : Kind(SK_ForeachLoop), Parent(std::move(Parent)), CurLoop(Loop) {} + TGVarScope(std::unique_ptr Parent, MultiClass *Multiclass) + : Kind(SK_MultiClass), Parent(std::move(Parent)), + CurMultiClass(Multiclass) {} + + 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); + } - void addVar(StringRef Name, Init *I) { - bool Ins = vars.insert(std::make_pair(std::string(Name), I)).second; - (void)Ins; - assert(Ins && "Local variable already exists"); - } -}; + Init *getVar(RecordKeeper &Records, StringInit *Name, SMRange NameLoc, + bool TrackReferenceLocs) const; -struct MultiClass { - Record Rec; // Placeholder for template args and Name. - std::vector Entries; + 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 dump() const; + void addVar(StringRef Name, Init *I) { + bool Ins = Vars.insert(std::make_pair(std::string(Name), I)).second; + (void)Ins; + assert(Ins && "Local variable already exists"); + } - MultiClass(StringRef Name, SMLoc Loc, RecordKeeper &Records) : - Rec(Name, Loc, Records) {} -}; + bool isOutermost() const { return Parent == nullptr; } + }; class TGParser { TGLexer Lex; @@ -142,9 +152,8 @@ /// current value. MultiClass *CurMultiClass; - /// CurLocalScope - Innermost of the current nested scopes for 'defvar' local - /// variables. - std::unique_ptr CurLocalScope; + /// CurScope - Innermost of the current nested scopes for 'defvar' variables. + std::unique_ptr CurScope; // Record tracker RecordKeeper &Records; @@ -186,17 +195,29 @@ return Lex.getDependencies(); } - TGLocalVarScope *PushLocalScope() { - CurLocalScope = std::make_unique(std::move(CurLocalScope)); + TGVarScope *PushScope() { + CurScope = std::make_unique(std::move(CurScope)); // 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 + // to PopScope which will check by assertion that the pushes and pops // match up properly. - return CurLocalScope.get(); + return CurScope.get(); + } + TGVarScope *PushScope(Record *Rec) { + CurScope = std::make_unique(std::move(CurScope), Rec); + return CurScope.get(); + } + TGVarScope *PushScope(ForeachLoop *Loop) { + CurScope = std::make_unique(std::move(CurScope), Loop); + return CurScope.get(); + } + TGVarScope *PushScope(MultiClass *Multiclass) { + CurScope = std::make_unique(std::move(CurScope), Multiclass); + return CurScope.get(); } - void PopLocalScope(TGLocalVarScope *ExpectedStackTop) { - assert(ExpectedStackTop == CurLocalScope.get() && + void PopScope(TGVarScope *ExpectedStackTop) { + assert(ExpectedStackTop == CurScope.get() && "Mismatched pushes and pops of local variable scopes"); - CurLocalScope = CurLocalScope->extractParent(); + CurScope = CurScope->extractParent(); } private: // Semantic analysis methods. 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 @@ -139,6 +139,77 @@ return QualifiedNameOfImplicitName(MC->Rec, MC); } +Init *TGVarScope::getVar(RecordKeeper &Records, StringInit *Name, + SMRange NameLoc, bool TrackReferenceLocs) const { + // First, we search in local variables. + auto It = Vars.find(Name->getValue()); + if (It != Vars.end()) + return It->second; + + std::function FindValueInArgs = + [&](Record *Rec, StringInit *Name, StringRef Scoper) -> Init * { + if (!Rec) + return nullptr; + Init *ArgName = QualifyName(*Rec, CurMultiClass, Name, Scoper); + if (Rec->isTemplateArg(ArgName)) { + RecordVal *RV = Rec->getValue(ArgName); + assert(RV && "Template or function arg doesn't exist??"); + RV->setUsed(true); + if (TrackReferenceLocs) + RV->addReferenceLoc(NameLoc); + return VarInit::get(ArgName, RV->getType()); + } + return Name->getValue() == "NAME" + ? VarInit::get(ArgName, StringRecTy::get(Records)) + : nullptr; + }; + + // If not found, we try to find the variable in additional variables like + // arguments, loop iterator, etc. + switch (Kind) { + case SK_Local: + break; /* do nothing. */ + case SK_Record: { + if (CurRec) { + // The variable is a record field? + if (RecordVal *RV = CurRec->getValue(Name)) { + if (TrackReferenceLocs) + RV->addReferenceLoc(NameLoc); + return VarInit::get(Name, RV->getType()); + } + + // The variable is a class template argument? + if (CurRec->isClass()) + if (auto *V = FindValueInArgs(CurRec, Name, ":")) + return V; + } + break; + } + case SK_ForeachLoop: { + // The variable is a loop iterator? + if (CurLoop->IterVar) { + VarInit *IterVar = dyn_cast(CurLoop->IterVar); + if (IterVar && IterVar->getNameInit() == Name) + return IterVar; + } + break; + } + case SK_MultiClass: { + // The variable is a multiclass template argument? + if (CurMultiClass) + if (auto *V = FindValueInArgs(&CurMultiClass->Rec, Name, "::")) + return V; + break; + } + } + + // Then, we try to find the name in parent scope. + if (Parent) + return Parent->getVar(Records, Name, NameLoc, TrackReferenceLocs); + + return nullptr; +} + bool TGParser::AddValue(Record *CurRec, SMLoc Loc, const RecordVal &RV) { if (!CurRec) CurRec = &CurMultiClass->Rec; @@ -1037,47 +1108,8 @@ /// ParseIDValue Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMRange NameLoc, IDParseMode Mode) { - if (CurRec) { - if (RecordVal *RV = CurRec->getValue(Name)) { - if (TrackReferenceLocs) - RV->addReferenceLoc(NameLoc); - return VarInit::get(Name, RV->getType()); - } - } - - if ((CurRec && CurRec->isClass()) || CurMultiClass) { - Init *TemplateArgName; - if (CurMultiClass) { - TemplateArgName = - QualifyName(CurMultiClass->Rec, CurMultiClass, Name, "::"); - } else - TemplateArgName = QualifyName(*CurRec, CurMultiClass, Name, ":"); - - Record *TemplateRec = CurMultiClass ? &CurMultiClass->Rec : CurRec; - if (TemplateRec->isTemplateArg(TemplateArgName)) { - RecordVal *RV = TemplateRec->getValue(TemplateArgName); - assert(RV && "Template arg doesn't exist??"); - RV->setUsed(true); - if (TrackReferenceLocs) - RV->addReferenceLoc(NameLoc); - return VarInit::get(TemplateArgName, RV->getType()); - } else if (Name->getValue() == "NAME") { - return VarInit::get(TemplateArgName, StringRecTy::get(Records)); - } - } - - if (CurLocalScope) - if (Init *I = CurLocalScope->getVar(Name->getValue())) - return I; - - // If this is in a foreach loop, make sure it's not a loop iterator - for (const auto &L : Loops) { - if (L->IterVar) { - VarInit *IterVar = dyn_cast(L->IterVar); - if (IterVar && IterVar->getNameInit() == Name) - return IterVar; - } - } + if (Init *I = CurScope->getVar(Records, Name, NameLoc, TrackReferenceLocs)) + return I; if (Mode == ParseNameMode) return Name; @@ -1942,12 +1974,14 @@ ParseRec = ParseRecTmp.get(); } + TGVarScope *FoldScope = PushScope(ParseRec); ParseRec->addValue(RecordVal(A, Start->getType(), RecordVal::FK_Normal)); - ParseRec->addValue(RecordVal(B, ListType->getElementType(), - RecordVal::FK_Normal)); + ParseRec->addValue( + RecordVal(B, ListType->getElementType(), RecordVal::FK_Normal)); Init *ExprUntyped = ParseValue(ParseRec); ParseRec->removeValue(A); ParseRec->removeValue(B); + PopScope(FoldScope); if (!ExprUntyped) return nullptr; @@ -2279,10 +2313,11 @@ std::make_unique(".parse", ArrayRef{}, Records); ParseRec = ParseRecTmp.get(); } - + TGVarScope *TempScope = PushScope(ParseRec); ParseRec->addValue(RecordVal(LHS, InEltType, RecordVal::FK_Normal)); Init *RHS = ParseValue(ParseRec, ExprEltType); ParseRec->removeValue(LHS); + PopScope(TempScope); if (!RHS) return nullptr; @@ -3070,6 +3105,11 @@ return nullptr; } + if (!ParsingTemplateArgs && CurScope->varAlreadyDefined(Str)) { + TokError("local variable of this name already exists"); + return nullptr; + } + SMLoc IdLoc = Lex.getLoc(); Init *DeclName = StringInit::get(Records, Str); Lex.Lex(); @@ -3306,15 +3346,10 @@ if (!consume(tgtok::l_brace)) return TokError("Expected '{' to start body or ';' for declaration only"); - // An object body introduces a new scope for local variables. - TGLocalVarScope *BodyScope = PushLocalScope(); - while (Lex.getCode() != tgtok::r_brace) if (ParseBodyItem(CurRec)) return true; - PopLocalScope(BodyScope); - // Eat the '}'. Lex.Lex(); @@ -3365,6 +3400,8 @@ /// BaseClassListNE ::= SubClassRef (',' SubClassRef)* /// bool TGParser::ParseObjectBody(Record *CurRec) { + // An object body introduces a new scope for local variables. + TGVarScope *ObjectScope = PushScope(CurRec); // If there is a baseclass list, read it. if (consume(tgtok::colon)) { @@ -3387,7 +3424,9 @@ if (ApplyLetStack(CurRec)) return true; - return ParseBody(CurRec); + bool Result = ParseBody(CurRec); + PopScope(ObjectScope); + return Result; } /// ParseDef - Parse and return a top level or multiclass record definition. @@ -3482,14 +3521,21 @@ if (Lex.getCode() != tgtok::Id) return TokError("expected identifier"); StringInit *DeclName = StringInit::get(Records, Lex.getCurStrVal()); - if (CurLocalScope) { - if (CurLocalScope->varAlreadyDefined(DeclName->getValue())) - return TokError("local variable of this name already exists"); - } else { - if (Records.getGlobal(DeclName->getValue())) - return TokError("def or global variable of this name already exists"); + if (CurScope->varAlreadyDefined(DeclName->getValue())) + return TokError("local variable of this name already exists"); + + // The name should not be conflicted with existed field names. + if (CurRec) { + auto *V = CurRec->getValue(DeclName->getValue()); + if (V && !V->isTemplateArg()) + return TokError("field of this name already exists"); } + // If this defvar is in the top level, the name should not be conflicted + // with existed global names. + if (CurScope->isOutermost() && Records.getGlobal(DeclName->getValue())) + return TokError("def or global variable of this name already exists"); + Lex.Lex(); if (!consume(tgtok::equal)) return TokError("expected '='"); @@ -3501,8 +3547,8 @@ if (!consume(tgtok::semi)) return TokError("expected ';'"); - if (CurLocalScope) - CurLocalScope->addVar(DeclName->getValue(), Value); + if (!CurScope->isOutermost()) + CurScope->addVar(DeclName->getValue(), Value); else Records.addExtraGlobal(DeclName->getValue(), Value); @@ -3531,10 +3577,10 @@ return TokError("Unknown tok"); // Create a loop object and remember it. - Loops.push_back(std::make_unique(Loc, IterName, ListValue)); - + auto TheLoop = std::make_unique(Loc, IterName, ListValue); // A foreach loop introduces a new scope for local variables. - TGLocalVarScope *ForeachScope = PushLocalScope(); + TGVarScope *ForeachScope = PushScope(TheLoop.get()); + Loops.push_back(std::move(TheLoop)); if (Lex.getCode() != tgtok::l_brace) { // FOREACH Declaration IN Object @@ -3555,7 +3601,7 @@ } } - PopLocalScope(ForeachScope); + PopScope(ForeachScope); // Resolve the loop or store it for later resolution. std::unique_ptr Loop = std::move(Loops.back()); @@ -3644,7 +3690,8 @@ /// IfBody ::= '{' ObjectList '}' /// bool TGParser::ParseIfBody(MultiClass *CurMultiClass, StringRef Kind) { - TGLocalVarScope *BodyScope = PushLocalScope(); + // An if-statement introduces a new scope for local variables. + TGVarScope *BodyScope = PushScope(); if (Lex.getCode() != tgtok::l_brace) { // A single object. @@ -3665,7 +3712,7 @@ } } - PopLocalScope(BodyScope); + PopScope(BodyScope); return false; } @@ -3732,6 +3779,8 @@ } Lex.Lex(); // eat the name. + // A class definition introduces a new scope. + TGVarScope *ClassScope = PushScope(CurRec); // If there are template args, parse them. if (Lex.getCode() == tgtok::less) if (ParseTemplateArgList(CurRec)) @@ -3742,6 +3791,8 @@ if (!NoWarnOnUnusedTemplateArgs) CurRec->checkUnusedTemplateArgs(); + + PopScope(ClassScope); return false; } @@ -3807,8 +3858,6 @@ if (!consume(tgtok::In)) return TokError("expected 'in' at end of top-level 'let'"); - TGLocalVarScope *LetScope = PushLocalScope(); - // If this is a scalar let, just handle it now if (Lex.getCode() != tgtok::l_brace) { // LET LetList IN Object @@ -3819,6 +3868,9 @@ // Otherwise, this is a group let. Lex.Lex(); // eat the '{'. + // A group let introduces a new scope for local variables. + TGVarScope *LetScope = PushScope(); + // Parse the object list. if (ParseObjectList(CurMultiClass)) return true; @@ -3827,9 +3879,9 @@ TokError("expected '}' at end of top level let command"); return Error(BraceLoc, "to match this '{'"); } - } - PopLocalScope(LetScope); + PopScope(LetScope); + } // Outside this let scope, this let block is not active. LetStack.pop_back(); @@ -3867,6 +3919,9 @@ CurMultiClass = Result.first->second.get(); Lex.Lex(); // Eat the identifier. + // A multiclass body introduces a new scope for local variables. + TGVarScope *MulticlassScope = PushScope(CurMultiClass); + // If there are template args, parse them. if (Lex.getCode() == tgtok::less) if (ParseTemplateArgList(nullptr)) @@ -3904,9 +3959,6 @@ 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: @@ -3933,13 +3985,12 @@ PrintError(SemiLoc, "A multiclass body should not end with a semicolon"); PrintNote("Semicolon ignored; remove to eliminate this error"); } - - PopLocalScope(MulticlassScope); } if (!NoWarnOnUnusedTemplateArgs) CurMultiClass->Rec.checkUnusedTemplateArgs(); + PopScope(MulticlassScope); CurMultiClass = nullptr; return false; } @@ -4118,7 +4169,10 @@ bool TGParser::ParseFile() { Lex.Lex(); // Prime the lexer. - if (ParseObjectList()) return true; + TGVarScope *GlobalScope = PushScope(); + if (ParseObjectList()) + return true; + PopScope(GlobalScope); // If we have unread input at the end of the file, report it. if (Lex.getCode() == tgtok::Eof) diff --git a/llvm/test/TableGen/defvar.td b/llvm/test/TableGen/defvar.td --- a/llvm/test/TableGen/defvar.td +++ b/llvm/test/TableGen/defvar.td @@ -2,6 +2,8 @@ // 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 +// RUN: not llvm-tblgen -DERROR4 %s 2>&1 | FileCheck --check-prefix=ERROR4 %s +// RUN: not llvm-tblgen -DERROR5 %s 2>&1 | FileCheck --check-prefix=ERROR5 %s #ifdef ERROR1 // Refer to a variable we haven't defined *yet*, expecting an error. @@ -18,18 +20,6 @@ defvar myvar = "another value"; #endif -// These variables should be overrided by template arguments. -defvar a = 2333; -defvar b = 2333; -class VarScopeTest { - defvar c = !add(a, b); - int value = !add(c, c); -} - -// CHECK: def aaa_scope_test { -// CHECK-NEXT: int value = 10; -def aaa_scope_test: VarScopeTest<2, 3>; - multiclass Test { // Refer to a global variable, while inside a local scope like a multiclass. def _with_global_string { string s = myvar; } @@ -146,6 +136,62 @@ def shadowOuterBelowIf # first { int var = shadowedVariable; } } +class RedefinitionTest { + #ifdef ERROR4 + defvar value = !add(a, b); + #endif + // ERROR4: [[@LINE+1]]:7: error: local variable of this name already exists + int value = !add(a, b); + #ifdef ERROR5 + // ERROR5: [[@LINE+1]]:10: error: field of this name already exists + defvar value = !add(a, b); + #endif +} + +// These variables should be shadowed by class/multiclass template arguments. +defvar a = 2333; +defvar b = 2333; +defvar c = 2333; +class ShadowGlobalsTest { + // Template arguments have higher priorities than global variables. + int value = !add(a, b, c); +} + +class ShadowClassArgumentTest { + // Local variable 'c' has higher priority than class template argument 'c'. + defvar c = !add(c, c); + int value = !add(a, b, c); +} + +multiclass ShadowMulticlassArgumentTest { + // Local variable 'c' has higher priority than multiclass template argument 'c'. + defvar c = !add(c, c); + def "" { + int value = !add(a, b, c); + } +} + +// CHECK: def shadowTestOfClassArgument { +// CHECK-NEXT: int value = 11; +// CHECK: def shadowTestOfGlobals { +// CHECK-NEXT: int value = 8; +// CHECK: def shadowTestOfLoopIterator0 { +// CHECK-NEXT: int value = 10; +// CHECK: def shadowTestOfLoopIterator1 { +// CHECK-NEXT: int value = 11; +// CHECK: def shadowTestOfMulticlassArgument { +// CHECK-NEXT: int value = 11; +def shadowTestOfClassArgument: ShadowClassArgumentTest<2, 3, 3>; +def shadowTestOfGlobals: ShadowGlobalsTest<2, 3, 3>; +foreach i = 0...1 in { + // Local variable 'i' has higher priority than loop iterator 'i'. + def shadowTestOfLoopIterator # i { + defvar i = !add(10, i); + int value = i; + } +} +defm shadowTestOfMulticlassArgument: ShadowMulticlassArgumentTest<2, 3, 3>; + // 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).