diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst index 0983c6283f7e2..fe0564d2990df 100644 --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -223,13 +223,13 @@ TableGen provides "bang operators" that have a wide variety of uses: : !div !empty !eq !exists !filter : !find !foldl !foreach !ge !getdagarg : !getdagname !getdagop !gt !head !if - : !initialized !interleave !isa !le !listconcat - : !listflatten !listremove !listsplat !logtwo !lt - : !match !mul !ne !not !or - : !range !repr !setdagarg !setdagname !setdagop - : !shl !size !sra !srl !strconcat - : !sub !subst !substr !tail !tolower - : !toupper !xor + : !initialized !instances !interleave !isa !le + : !listconcat !listflatten !listremove !listsplat !logtwo + : !lt !match !mul !ne !not + : !or !range !repr !setdagarg !setdagname + : !setdagop !shl !size !sra !srl + : !strconcat !sub !subst !substr !tail + : !tolower !toupper !xor The ``!cond`` operator has a slightly different syntax compared to other bang operators, so it is defined separately: @@ -1836,6 +1836,15 @@ and non-0 as true. This operator produces 1 if *a* is not the uninitialized value (``?``) and 0 otherwise. +``!instances<``\ *type*\ ``>([``\ *regex*\ ``])`` + This operator produces a list of records whose type is *type*. If *regex* + is provided, only records whose name matches the regular expression *regex* + will be included. The format of *regex* is ERE (Extended POSIX Regular + Expressions). + + If ``!instances`` is in a class/multiclass/foreach, only these records of + *type* that have been instantiated will be considered. + ``!interleave(``\ *list*\ ``,`` *delim*\ ``)`` This operator concatenates the items in the *list*, interleaving the *delim* string between each pair, and produces the resulting string. diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h index ae505631b5433..d3771a3731673 100644 --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -316,6 +316,7 @@ class Init { IK_FoldOpInit, IK_IsAOpInit, IK_ExistsOpInit, + IK_InstancesOpInit, IK_AnonymousNameInit, IK_StringInit, IK_VarInit, @@ -1192,6 +1193,41 @@ class ExistsOpInit final : public TypedInit, public FoldingSetNode { std::string getAsString() const override; }; +/// !instances([regex]) - Produces a list of records whose type is `type`. +/// If `regex` is provided, only records whose name matches the regular +/// expression `regex` will be included. +class InstancesOpInit final : public TypedInit, public FoldingSetNode { +private: + const RecTy *Type; + const Init *Regex; + + InstancesOpInit(const RecTy *Type, const Init *Regex) + : TypedInit(IK_InstancesOpInit, ListRecTy::get(Type)), Type(Type), + Regex(Regex) {} + +public: + InstancesOpInit(const InstancesOpInit &) = delete; + InstancesOpInit &operator=(const InstancesOpInit &) = delete; + + static bool classof(const Init *I) { + return I->getKind() == IK_InstancesOpInit; + } + + static const InstancesOpInit *get(const RecTy *Type, const Init *Regex); + + void Profile(FoldingSetNodeID &ID) const; + + const Init *Fold(const Record *CurRec, bool IsFinal = false) const; + + bool isComplete() const override { return false; } + + const Init *resolveReferences(Resolver &R) const override; + + const Init *getBit(unsigned Bit) const override; + + std::string getAsString() const override; +}; + /// 'Opcode' - Represent a reference to an entire variable object. class VarInit final : public TypedInit { const Init *VarName; @@ -1982,6 +2018,9 @@ class RecordKeeper { bool Ins = Defs.try_emplace(std::string(R->getName()), std::move(R)).second; (void)Ins; assert(Ins && "Record already exists"); + // Clear cache + if (!Cache.empty()) + Cache.clear(); } void addExtraGlobal(StringRef Name, const Init *I) { diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index c5b9b670b6f42..d182b647aa931 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -84,6 +84,7 @@ struct RecordKeeperImpl { FoldingSet TheFoldOpInitPool; FoldingSet TheIsAOpInitPool; FoldingSet TheExistsOpInitPool; + FoldingSet TheInstancesOpInitPool; DenseMap, VarInit *> TheVarInitPool; DenseMap, VarBitInit *> TheVarBitInitPool; @@ -2222,6 +2223,70 @@ std::string ExistsOpInit::getAsString() const { .str(); } +static void ProfileInstancesOpInit(FoldingSetNodeID &ID, const RecTy *Type, + const Init *Regex) { + ID.AddPointer(Type); + ID.AddPointer(Regex); +} + +const InstancesOpInit *InstancesOpInit::get(const RecTy *Type, + const Init *Regex) { + FoldingSetNodeID ID; + ProfileInstancesOpInit(ID, Type, Regex); + + detail::RecordKeeperImpl &RK = Regex->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (const InstancesOpInit *I = + RK.TheInstancesOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + InstancesOpInit *I = new (RK.Allocator) InstancesOpInit(Type, Regex); + RK.TheInstancesOpInitPool.InsertNode(I, IP); + return I; +} + +void InstancesOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileInstancesOpInit(ID, Type, Regex); +} + +const Init *InstancesOpInit::Fold(const Record *CurRec, bool IsFinal) const { + if (CurRec && !IsFinal) + return this; + + const auto *RegexInit = dyn_cast(Regex); + if (!RegexInit) + return this; + + StringRef RegexStr = RegexInit->getValue(); + llvm::Regex Matcher(RegexStr); + if (!Matcher.isValid()) + PrintFatalError(Twine("invalid regex '") + RegexStr + Twine("'")); + + const RecordKeeper &RK = Type->getRecordKeeper(); + SmallVector Selected; + for (auto &Def : RK.getAllDerivedDefinitionsIfDefined(Type->getAsString())) + if (Matcher.match(Def->getName())) + Selected.push_back(Def->getDefInit()); + + return ListInit::get(Selected, Type); +} + +const Init *InstancesOpInit::resolveReferences(Resolver &R) const { + const Init *NewRegex = Regex->resolveReferences(R); + if (Regex != NewRegex || R.isFinal()) + return get(Type, NewRegex)->Fold(R.getCurrentRecord(), R.isFinal()); + return this; +} + +const Init *InstancesOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(this, Bit); +} + +std::string InstancesOpInit::getAsString() const { + return "!instances<" + Type->getAsString() + ">(" + Regex->getAsString() + + ")"; +} + const RecTy *TypedInit::getFieldType(const StringInit *FieldName) const { if (const auto *RecordType = dyn_cast(getType())) { for (const Record *Rec : RecordType->getClasses()) { diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp index 0b2f927446b1e..4da2505cc065a 100644 --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -632,6 +632,7 @@ tgtok::TokKind TGLexer::LexExclaim() { .Case("strconcat", tgtok::XStrConcat) .Case("initialized", tgtok::XInitialized) .Case("interleave", tgtok::XInterleave) + .Case("instances", tgtok::XInstances) .Case("substr", tgtok::XSubstr) .Case("find", tgtok::XFind) .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated. diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h index ef9205197decf..a5b10b37e9886 100644 --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -137,6 +137,7 @@ enum TokKind { XSize, XEmpty, XInitialized, + XInstances, XIf, XCond, XEq, diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp index 787c3e64beac2..3d914b94ff62f 100644 --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -1455,6 +1455,49 @@ const Init *TGParser::ParseOperation(Record *CurRec, const RecTy *ItemType) { return (ExistsOpInit::get(Type, Expr))->Fold(CurRec); } + case tgtok::XInstances: { + // Value ::= !instances '<' Type '>' '(' Regex? ')' + Lex.Lex(); // eat the operation. + + const RecTy *Type = ParseOperatorType(); + if (!Type) + return nullptr; + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after type of !instances"); + return nullptr; + } + + // The Regex can be optional. + const Init *Regex; + if (Lex.getCode() != tgtok::r_paren) { + SMLoc RegexLoc = Lex.getLoc(); + Regex = ParseValue(CurRec); + + const auto *RegexType = dyn_cast(Regex); + if (!RegexType) { + Error(RegexLoc, "expected string type argument in !instances operator"); + return nullptr; + } + + const auto *SType = dyn_cast(RegexType->getType()); + if (!SType) { + Error(RegexLoc, "expected string type argument in !instances operator"); + return nullptr; + } + } else { + // Use wildcard when Regex is not specified. + Regex = StringInit::get(Records, ".*"); + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !instances"); + return nullptr; + } + + return InstancesOpInit::get(Type, Regex)->Fold(CurRec); + } + case tgtok::XConcat: case tgtok::XMatch: case tgtok::XADD: diff --git a/llvm/test/TableGen/instances.td b/llvm/test/TableGen/instances.td new file mode 100644 index 0000000000000..a49b2de5f7520 --- /dev/null +++ b/llvm/test/TableGen/instances.td @@ -0,0 +1,133 @@ +// 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 +// XFAIL: vg_leak + +class A; +def a0 : A; +def a1 : A; + +class B : A; +def b0 : B; +def b1 : B; + +// CHECK-LABEL: def test0_instances_A { +// CHECK-NEXT: list instances = [a0, a1, b0, b1]; +// CHECK-NEXT: } +def test0_instances_A { + list instances = !instances(); +} + +// CHECK-LABEL: def test1_instances_A_x0 { +// CHECK-NEXT: list instances = [a0, b0]; +// CHECK-NEXT: } +def test1_instances_A_x0 { + list instances = !instances(".*0"); +} + +// CHECK-LABEL: def test2_instances_A_x1 { +// CHECK-NEXT: list instances = [a1, b1]; +// CHECK-NEXT: } +def test2_instances_A_x1 { + list instances = !instances(".*1"); +} + +// CHECK-LABEL: def test3_instances_B { +// CHECK-NEXT: list instances = [b0, b1]; +// CHECK-NEXT: } +def test3_instances_B { + list instances = !instances(); +} + +//-----------------------------------------------------------------------------// + +def a2 : A; +def b2 : B; + +class ClassTest { + list instances_A = !instances(); + list instances_B = !instances(); +} + +def a3 : A; +def b3 : B; + +def test4_in_class_def : ClassTest; +// CHECK-LABEL: def test4_in_class_def { +// CHECK-NEXT: list instances_A = [a0, a1, a2, a3, b0, b1, b2, b3]; +// CHECK-NEXT: list instances_B = [b0, b1, b2, b3]; +// CHECK-NEXT: } + +//-----------------------------------------------------------------------------// +// Self-recurrence is not supported, so it won't be count in. + +// CHECK-LABEL: def test5_self_recurrence { +// CHECK-NEXT: list instances_A = [a0, a1, a2, a3, b0, b1, b2, b3]; +// CHECK-NEXT: } +def test5_self_recurrence : A { + list instances_A = !instances(); +} + +//-----------------------------------------------------------------------------// +// Test these in multiclasses/loops. + +class C { + list instances_C = !instances(); +} + +multiclass MultiClassTest { + foreach i = 0-2 in { + def "c"#i : C; + } +} + +// CHECK-LABEL: def test6_in_multiclass_def_c0 { +// CHECK-NEXT: list instances_C = []; +// CHECK-NEXT: } +// CHECK-LABEL: def test6_in_multiclass_def_c1 { +// CHECK-NEXT: list instances_C = [test6_in_multiclass_def_c0]; +// CHECK-NEXT: } +// CHECK-LABEL: def test6_in_multiclass_def_c2 { +// CHECK-NEXT: list instances_C = [test6_in_multiclass_def_c0, test6_in_multiclass_def_c1]; +// CHECK-NEXT: } +defm test6_in_multiclass_def_ : MultiClassTest; + +//-----------------------------------------------------------------------------// +// Default argument/temporary actual parameter will be considered as well. +class D; + +class TestArgument> { + list instances_D = !instances(); +} + +// CHECK-LABEL: def test7_default_arg { +// CHECK-NEXT: list instances_D = [anonymous_0]; +// CHECK-NEXT: } +def test7_default_arg : TestArgument; + +// CHECK-LABEL: def test8_anonymous0_arg { +// CHECK-NEXT: list instances_D = [anonymous_0, anonymous_1]; +// CHECK-NEXT: } +// CHECK-LABEL: def test8_anonymous1_arg { +// CHECK-NEXT: list instances_D = [anonymous_0, anonymous_1, anonymous_2]; +// CHECK-NEXT: } +def test8_anonymous0_arg : TestArgument>; +def test8_anonymous1_arg : TestArgument>; + +//-----------------------------------------------------------------------------// + +#ifdef ERROR1 +defvar error1 = !instances(123); +// ERROR1: error: expected string type argument in !instances operator +#endif + +#ifdef ERROR2 +defvar error2 = !instances<1>(""); +// ERROR2: error: Unknown token when expecting a type +#endif + +#ifdef ERROR3 +defvar error3 = !instances("([)]"); +// ERROR3: error: invalid regex '([)]' +#endif