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 @@ -1724,6 +1724,10 @@ This operator produces 1 if the type of *a* is a subtype of the given *type*; 0 otherwise. +``!instanceof<``\ *type*\ ``>(``\ *name*\ ``)`` + This operator produces 1 if an instance of the given *type* whose name is *name* + exists; 0 otherwise. *name* should be of type *string*. + ``!le(``\ *a*\ ``,`` *b*\ ``)`` This operator produces 1 if *a* is less than or equal to *b*; 0 otherwise. The arguments must be ``bit``, ``bits``, ``int``, or ``string`` values. diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -311,6 +311,7 @@ IK_CondOpInit, IK_FoldOpInit, IK_IsAOpInit, + IK_InstanceOfOpInit, IK_AnonymousNameInit, IK_StringInit, IK_VarInit, @@ -1095,6 +1096,42 @@ std::string getAsString() const override; }; +/// !instanceof(expr) - Dynamically determine if an instance of `type` +/// named `expr` exists. +class InstanceOfOpInit : public TypedInit, public FoldingSetNode { +private: + RecTy *CheckType; + Init *Expr; + + InstanceOfOpInit(RecTy *CheckType, Init *Expr) + : TypedInit(IK_InstanceOfOpInit, IntRecTy::get(CheckType->getRecordKeeper())), + CheckType(CheckType), Expr(Expr) {} + +public: + InstanceOfOpInit(const InstanceOfOpInit &) = delete; + InstanceOfOpInit &operator=(const InstanceOfOpInit &) = delete; + + static bool classof(const Init *I) { + return I->getKind() == IK_InstanceOfOpInit; + } + + static InstanceOfOpInit *get(RecTy *CheckType, Init *Expr); + + void Profile(FoldingSetNodeID &ID) const; + + // Fold - If possible, fold this to a simpler init. Return this if not + // possible to fold. + Init *Fold(Record *CurRec, bool IsFinal = false) const; + + bool isComplete() const override { return false; } + + Init *resolveReferences(Resolver &R) const override; + + Init *getBit(unsigned Bit) const override; + + std::string getAsString() const override; +}; + /// 'Opcode' - Represent a reference to an entire variable object. class VarInit : public TypedInit { Init *VarName; diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -79,6 +79,7 @@ FoldingSet TheTernOpInitPool; FoldingSet TheFoldOpInitPool; FoldingSet TheIsAOpInitPool; + FoldingSet TheInstanceOfOpInitPool; DenseMap, VarInit *> TheVarInitPool; DenseMap, VarBitInit *> TheVarBitInitPool; DenseMap, VarListElementInit *> @@ -1660,6 +1661,82 @@ .str(); } +static void ProfileInstanceOfOpInit(FoldingSetNodeID &ID, RecTy *CheckType, + Init *Expr) { + ID.AddPointer(CheckType); + ID.AddPointer(Expr); +} + +InstanceOfOpInit *InstanceOfOpInit::get(RecTy *CheckType, Init *Expr) { + FoldingSetNodeID ID; + ProfileInstanceOfOpInit(ID, CheckType, Expr); + + detail::RecordKeeperImpl &RK = Expr->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (InstanceOfOpInit *I = + RK.TheInstanceOfOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + InstanceOfOpInit *I = new (RK.Allocator) InstanceOfOpInit(CheckType, Expr); + RK.TheInstanceOfOpInitPool.InsertNode(I, IP); + return I; +} + +void InstanceOfOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileInstanceOfOpInit(ID, CheckType, Expr); +} + +Init *InstanceOfOpInit::Fold(Record *CurRec, bool IsFinal) const { + if (StringInit *Name = dyn_cast(Expr)) { + if (!CurRec && !IsFinal) + return const_cast(this); + + // Self-references are allowed, but their resolution is delayed until + // the final resolve to ensure that we get the correct type for them. + auto *Anonymous = dyn_cast(CurRec->getNameInit()); + if (Name == CurRec->getNameInit() || + (Anonymous && Name == Anonymous->getNameInit())) { + if (!IsFinal) + return const_cast(this); + + // No doubt that there exists an instance, so we should check if types are + // compatiable. + return IntInit::get(getRecordKeeper(), + CurRec->getType()->typeIsA(CheckType)); + } + + // Look up all defined records to see if we can find one. + Record *D = CheckType->getRecordKeeper().getDef(Name->getValue()); + if (!D) { + if (IsFinal) + return IntInit::get(getRecordKeeper(), 0); + return const_cast(this); + } + + // Check if types are compatiable. + return IntInit::get(getRecordKeeper(), + DefInit::get(D)->getType()->typeIsA(CheckType)); + } + return const_cast(this); +} + +Init *InstanceOfOpInit::resolveReferences(Resolver &R) const { + Init *NewExpr = Expr->resolveReferences(R); + if (Expr != NewExpr || R.isFinal()) + return get(CheckType, NewExpr)->Fold(R.getCurrentRecord(), R.isFinal()); + return const_cast(this); +} + +Init *InstanceOfOpInit::getBit(unsigned Bit) const { + return VarBitInit::get(const_cast(this), Bit); +} + +std::string InstanceOfOpInit::getAsString() const { + return (Twine("!instanceof<") + CheckType->getAsString() + ">(" + + Expr->getAsString() + ")") + .str(); +} + RecTy *TypedInit::getFieldType(StringInit *FieldName) const { if (RecordRecTy *RecordType = dyn_cast(getType())) { for (Record *Rec : RecordType->getClasses()) { 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 @@ -56,6 +56,7 @@ XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XFind, XCast, XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp, + XInstanceOf, // Boolean literals. TrueVal, FalseVal, 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 @@ -584,6 +584,7 @@ .Case("find", tgtok::XFind) .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated. .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated. + .Case("instanceof", tgtok::XInstanceOf) .Default(tgtok::Error); return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator"); 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 @@ -1096,6 +1096,43 @@ return (IsAOpInit::get(Type, LHS))->Fold(); } + case tgtok::XInstanceOf: { + // Value ::= !instanceof '<' Type '>' '(' Value ')' + Lex.Lex(); // eat the operation + + RecTy *Type = ParseOperatorType(); + if (!Type) + return nullptr; + + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after type of !instanceof"); + return nullptr; + } + + Init *Expr = ParseValue(CurRec); + if (!Expr) + return nullptr; + + TypedInit *ExprType = dyn_cast(Expr); + if (!ExprType) { + TokError("expected string type argument in !instanceof operator"); + return nullptr; + } + + StringRecTy *SType = dyn_cast(ExprType->getType()); + if (!SType) { + TokError("expected string type argument in !instanceof operator"); + return nullptr; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !instanceof"); + return nullptr; + } + + return (InstanceOfOpInit::get(Type, Expr))->Fold(CurRec); + } + case tgtok::XConcat: case tgtok::XADD: case tgtok::XSUB: @@ -2357,7 +2394,8 @@ case tgtok::XSize: case tgtok::XEmpty: case tgtok::XCast: - case tgtok::XGetDagOp: // Value ::= !unop '(' Value ')' + case tgtok::XGetDagOp: + case tgtok::XInstanceOf: // Value ::= !unop '(' Value ')' case tgtok::XIsA: case tgtok::XConcat: case tgtok::XDag: diff --git a/llvm/test/TableGen/instanceof-error.td b/llvm/test/TableGen/instanceof-error.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/instanceof-error.td @@ -0,0 +1,8 @@ +// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +//CHECK: expected string type argument in !instanceof operator + +class A; + +defvar value = !instanceof(123); diff --git a/llvm/test/TableGen/instanceof.td b/llvm/test/TableGen/instanceof.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/instanceof.td @@ -0,0 +1,59 @@ +// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s +// XFAIL: vg_leak + +//CHECK: def B0 { +//CHECK: int value = 1; +//CHECK: } + +//CHECK: def B1 { +//CHECK: int value = 0; +//CHECK: } + +class A; +def A0 : A; + +class B{ + int value = !instanceof(name); +} + +def B0 : B<"A0">; +def B1 : B<"A1">; + +// Self-references are allowed. + +//CHECK: def C0 { +//CHECK: int value = 1; +//CHECK: } + +//CHECK: def C1 { +//CHECK: int value = 0; +//CHECK: } + +class C { + int value = !instanceof(name); +} + +def C0 : C<"C0">; +// `C2` isn't an instance of `C` in current context though we will define it below. +def C1 : C<"C2">; + +// Subclasses. + +//CHECK: def D0 { +//CHECK: int value = 1; +//CHECK: } + +//CHECK: def C2 { +//CHECK: int value = 1; +//CHECK: } + +//CHECK: def C3 { +//CHECK: int value = 0; +//CHECK: } + +class D : C; + +def D0 : D<"D0">; + +def C2 : C<"D0">; +def C3 : C<"A0">;