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`` @@ -359,8 +359,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 +420,23 @@ 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 +multiclass-local variable, whose value is given by the expression on the right +of the ``=``. The type of the variable is automatically inferred. + +If this appears at the top level of the file, then the identifier is defined as +a global variable, in the same scope used by ``defset``. + +``defvar`` may also appear inside a multiclass, in which case the identifier +can be defined in terms of the multiclass parameters, and is local to each +particular instance of the multiclass. Record definitions and further +``defvar`` statements inside the multiclass can refer to it. + ``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 @@ -77,11 +77,22 @@ struct MultiClass { Record Rec; // Placeholder for template args and Name. std::vector Entries; + std::map> Locals; void dump() const; MultiClass(StringRef Name, SMLoc Loc, RecordKeeper &Records) : Rec(Name, Loc, Records) {} + + Init *getLocal(StringRef Name) const { + auto It = Locals.find(Name); + return It == Locals.end() ? nullptr : It->second; + } + void addLocal(StringRef Name, Init *I) { + bool Ins = Locals.insert(std::make_pair(Name, I)).second; + (void)Ins; + assert(Ins && "Local already exists"); + } }; class TGParser { @@ -160,7 +171,8 @@ bool ParseMultiClass(); bool ParseDefm(MultiClass *CurMultiClass); bool ParseDef(MultiClass *CurMultiClass); - bool ParseDefset(); + 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 (CurMultiClass) + if (Init *I = CurMultiClass->getLocal(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); @@ -2800,6 +2804,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 (CurMultiClass) { + if (CurMultiClass->getLocal(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 (CurMultiClass) + CurMultiClass->addLocal(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. /// @@ -3054,11 +3097,12 @@ 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; @@ -3207,6 +3251,8 @@ /// Object ::= DefMInst /// Object ::= LETCommand '{' ObjectList '}' /// Object ::= LETCommand Object +/// Object ::= Defset +/// Object ::= Defvar bool TGParser::ParseObject(MultiClass *MC) { switch (Lex.getCode()) { default: @@ -3220,6 +3266,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,43 @@ +// 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 +// ERROR1: [[@LINE+1]]:22: error: Variable not defined: 'myvar' +def bad { dag x = (? myvar); } +#endif + +defvar myvar = "foo"; + +multiclass Test { + def _with_global_string { string s = myvar; } + defvar myvar = !add(x, 100); + defvar myvar2 = "string of " # myvar; + def _with_local_int { int i = myvar; string s = myvar2; } + +#ifdef ERROR2 + // ERROR2: [[@LINE+1]]:10: error: local variable of this name already exists + defvar myvar = "another value"; +#endif +} + +// 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>; + +#ifdef ERROR3 +// ERROR3: [[@LINE+1]]:8: error: def or global variable of this name already exists +defvar myvar = "another value"; +#endif +