- Notifications
You must be signed in to change notification settings - Fork 15.3k
[TableGen] Add !instances operator to get defined records #129680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| @llvm/pr-subscribers-tablegen Author: Pengcheng Wang (wangpc-pp) ChangesThis operator is like Full diff: https://github.com/llvm/llvm-project/pull/129680.diff 7 Files Affected:
diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst index edb97109c9289..563832e324539 100644 --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -226,10 +226,10 @@ TableGen provides "bang operators" that have a wide variety of uses: : !initialized !interleave !isa !le !listconcat : !listflatten !listremove !listsplat !logtwo !lt : !mul !ne !not !or !range - : !repr !setdagarg !setdagname !setdagop !shl - : !size !sra !srl !strconcat !sub - : !subst !substr !tail !tolower !toupper - : !xor + : !repr !select !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: @@ -1920,6 +1920,10 @@ and non-0 as true. Represents *value* as a string. String format for the value is not guaranteed to be stable. Intended for debugging purposes only. +``!select<``\ *type*\ ``>(``\ *regex*\ ``)`` + This operator produces a list of records whose type is *type* and record + names match regular expression *regex*. + ``!setdagarg(``\ *dag*\ ``,``\ *key*\ ``,``\ *arg*\ ``)`` This operator produces a DAG node with the same operator and arguments as *dag*, but replacing the value of the argument specified by the *key* with diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h index 334007524c954..22c5c7032864b 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_SelectOpInit, IK_AnonymousNameInit, IK_StringInit, IK_VarInit, @@ -1191,6 +1192,38 @@ class ExistsOpInit final : public TypedInit, public FoldingSetNode { std::string getAsString() const override; }; +/// !select<type>(regex) - Produces a list of records whose type is `type` and +/// record names match regular expression `regex`. +class SelectOpInit final : public TypedInit, public FoldingSetNode { +private: + const RecTy *Type; + const Init *Regex; + + SelectOpInit(const RecTy *Type, const Init *Regex) + : TypedInit(IK_SelectOpInit, IntRecTy::get(Type->getRecordKeeper())), + Type(Type), Regex(Regex) {} + +public: + SelectOpInit(const SelectOpInit &) = delete; + SelectOpInit &operator=(const SelectOpInit &) = delete; + + static bool classof(const Init *I) { return I->getKind() == IK_SelectOpInit; } + + static const SelectOpInit *get(const RecTy *Type, const Init *Regex); + + void Profile(FoldingSetNodeID &ID) const; + + const Init *Fold() 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; diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index 590656786bc66..68870991e672d 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h" @@ -83,6 +84,7 @@ struct RecordKeeperImpl { FoldingSet<FoldOpInit> TheFoldOpInitPool; FoldingSet<IsAOpInit> TheIsAOpInitPool; FoldingSet<ExistsOpInit> TheExistsOpInitPool; + FoldingSet<SelectOpInit> TheSelectOpInitPool; DenseMap<std::pair<const RecTy *, const Init *>, VarInit *> TheVarInitPool; DenseMap<std::pair<const TypedInit *, unsigned>, VarBitInit *> TheVarBitInitPool; @@ -2199,6 +2201,66 @@ std::string ExistsOpInit::getAsString() const { .str(); } +static void ProfileSelectOpInit(FoldingSetNodeID &ID, const RecTy *Type, + const Init *Regex) { + ID.AddPointer(Type); + ID.AddPointer(Regex); +} + +const SelectOpInit *SelectOpInit::get(const RecTy *Type, const Init *Regex) { + FoldingSetNodeID ID; + ProfileSelectOpInit(ID, Type, Regex); + + detail::RecordKeeperImpl &RK = Regex->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (const SelectOpInit *I = + RK.TheSelectOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + SelectOpInit *I = new (RK.Allocator) SelectOpInit(Type, Regex); + RK.TheSelectOpInitPool.InsertNode(I, IP); + return I; +} + +void SelectOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileSelectOpInit(ID, Type, Regex); +} + +const Init *SelectOpInit::Fold() const { + if (const auto *RegexInit = dyn_cast<StringInit>(Regex)) { + StringRef RegexStr = RegexInit->getValue(); + llvm::Regex Matcher(RegexStr); + if (!Matcher.isValid()) + PrintFatalError(Twine("invalid regex '") + RegexStr + Twine("'")); + + const RecordKeeper &RK = Type->getRecordKeeper(); + SmallVector<Init *, 8> Selected; + for (auto &Def : RK.getAllDerivedDefinitionsIfDefined(Type->getAsString())) + if (Matcher.match(Def->getName())) + Selected.push_back(Def->getDefInit()); + + return ListInit::get(Selected, Type); + } + return this; +} + +const Init *SelectOpInit::resolveReferences(Resolver &R) const { + const Init *NewRegex = Regex->resolveReferences(R); + if (Regex != NewRegex) + return get(Type, NewRegex)->Fold(); + return this; +} + +const Init *SelectOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(this, Bit); +} + +std::string SelectOpInit::getAsString() const { + return (Twine("!select<") + Type->getAsString() + ">(" + + Regex->getAsString() + ")") + .str(); +} + const RecTy *TypedInit::getFieldType(const StringInit *FieldName) const { if (const auto *RecordType = dyn_cast<RecordRecTy>(getType())) { for (const Record *Rec : RecordType->getClasses()) { diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp index 983242ade0fe5..e495aeb9cb7fa 100644 --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -644,6 +644,7 @@ tgtok::TokKind TGLexer::LexExclaim() { .Case("tolower", tgtok::XToLower) .Case("toupper", tgtok::XToUpper) .Case("repr", tgtok::XRepr) + .Case("select", tgtok::XSelect) .Default(tgtok::Error); return Kind != tgtok::Error ? Kind diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h index 6680915211205..e2fe98b483c7a 100644 --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -158,7 +158,8 @@ enum TokKind { XSetDagArg, XSetDagName, XRepr, - BANG_OPERATOR_LAST = XRepr, + XSelect, + BANG_OPERATOR_LAST = XSelect, // String valued tokens. STRING_VALUE_FIRST, diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp index 9a8301cffb930..b8f312be73884 100644 --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -1455,6 +1455,44 @@ const Init *TGParser::ParseOperation(Record *CurRec, const RecTy *ItemType) { return (ExistsOpInit::get(Type, Expr))->Fold(CurRec); } + case tgtok::XSelect: { + // Value ::= !select '<' 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 !select"); + return nullptr; + } + + SMLoc RegexLoc = Lex.getLoc(); + const Init *Regex = ParseValue(CurRec); + if (!Regex) + return nullptr; + + const auto *RegexType = dyn_cast<TypedInit>(Regex); + if (!RegexType) { + Error(RegexLoc, "expected string type argument in !select operator"); + return nullptr; + } + + const auto *SType = dyn_cast<StringRecTy>(RegexType->getType()); + if (!SType) { + Error(RegexLoc, "expected string type argument in !select operator"); + return nullptr; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !select"); + return nullptr; + } + + return (SelectOpInit::get(Type, Regex))->Fold(); + } + case tgtok::XConcat: case tgtok::XADD: case tgtok::XSUB: diff --git a/llvm/test/TableGen/select.td b/llvm/test/TableGen/select.td new file mode 100644 index 0000000000000..22e0c61784023 --- /dev/null +++ b/llvm/test/TableGen/select.td @@ -0,0 +1,60 @@ +// 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; + +def select_A { + list<A> selected = !select<A>(".*"); +} + +def select_A_x0 { + list<A> selected = !select<A>(".*0"); +} + +def select_A_x1 { + list<A> selected = !select<A>(".*1"); +} + +def select_B { + list<B> selected = !select<B>(".*"); +} + +// CHECK-LABEL: def select_A { +// CHECK-NEXT: list<A> selected = [a0, a1, b0, b1]; +// CHECK-NEXT: } + +// CHECK-LABEL: def select_A_x0 { +// CHECK-NEXT: list<A> selected = [a0, b0]; +// CHECK-NEXT: } + +// CHECK-LABEL: def select_A_x1 { +// CHECK-NEXT: list<A> selected = [a1, b1]; +// CHECK-NEXT: } + +// CHECK-LABEL: def select_B { +// CHECK-NEXT: list<B> selected = [b0, b1]; +// CHECK-NEXT: } + +#ifdef ERROR1 +defvar error1 = !select<A>(123) +// ERROR1: error: expected string type argument in !select operator +#endif + +#ifdef ERROR2 +defvar error2 = !select<1>("") +// ERROR2: error: Unknown token when expecting a type +#endif + +#ifdef ERROR3 +defvar error3 = !select<A>("([)]") +// ERROR3: error: invalid regex '([)]' +#endif |
| Add some RISC-V reviewers here because the intention is to simplify the generation of lists of RVV pseudos. For example: list<RISCVVPseudo> VADD = !select<RISCVVPseudo>(".*VADD.*"); list<RISCVVPseudo> M8 = !select<RISCVVPseudo>(".*_M8"); |
| My gut feeling is that this operator does too much, and the name "select" is too generic. Can you split the functionality into smaller pieces:
|
There are two things we can't do with current operators:
What about this solution?
WDYT? |
Sounds reasonable to me.
Sounds reasonable, although there might be arguments about exactly what regex syntax it should support.
This seems like premature optimization. Is there really a significant performance problem? |
These predicates can also be used in macro fusion and scheduling model. This is stacked on llvm#129680.
I just use
I strongly believe it is! The TableGen chained operators will be at least 100x slower than the native filter implemented by C++. |
It's tablegen, so when isn't there one? |
8e9bdeb to 69b0b7b Compare !select operator to select records!defined operator to get defined records | Ping! |
| I don't like the name |
Maybe |
69b0b7b to d1535bf Compare !defined operator to get defined records!records operator to get defined records
As an aside about naming: I know that record are called "records". But the name of this operator should suggest not just "records"; it should suggest "list of all records derived from a specified class". |
Yeah, what about |
| I think I still prefer |
Make sense to me, I renamed it to |
!records operator to get defined records!instances operator to get defined records
Yes, !instances. |
be046e8 to 72eeedc Compare
Artem-B left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
The format is: `!records<T>([regex])`. This operator produces a list of records whose type is `T`. 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).
e5c2d39 to 66f71ba Compare
The format is:
!instances<T>([regex]).This operator produces a list of records whose type is
T. Ifregexis provided, only records whose name matches the regularexpression
regexwill be included. The format ofregexis ERE(Extended POSIX Regular Expressions).