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 @@ -1566,7 +1566,7 @@ result. A logical AND can be performed if all the arguments are either 0 or 1. -``!cast<``\ *type*\ ``>(``\ *a*\ ``)`` +``!cast<``\ *type*\ ``>(``\ *a*\ [``,`` *default_value*] ``)`` This operator performs a cast on *a* and produces the result. If *a* is not a string, then a straightforward cast is performed, say between an ``int`` and a ``bit``, or between record types. This allows @@ -1575,7 +1575,9 @@ If *a* is a string, then it is treated as a record name and looked up in the list of all defined records. The resulting record is expected to be of - the specified *type*. + the specified *type*. Optionally, we can specify a *default_value* of the + specified *type* and get the *default_value* if we can't find the record + with the name *a*; Or TableGen will raise an error. For example, if ``!cast<``\ *type*\ ``>(``\ *name*\ ``)`` appears in a multiclass definition, or in a 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 @@ -310,6 +310,7 @@ IK_LastOpInit, IK_CondOpInit, IK_FoldOpInit, + IK_CastOpInit, IK_IsAOpInit, IK_AnonymousNameInit, IK_StringInit, @@ -783,7 +784,7 @@ /// class UnOpInit : public OpInit, public FoldingSetNode { public: - enum UnaryOp : uint8_t { CAST, NOT, HEAD, TAIL, SIZE, EMPTY, GETDAGOP }; + enum UnaryOp : uint8_t { NOT, HEAD, TAIL, SIZE, EMPTY, GETDAGOP }; private: Init *LHS; @@ -822,7 +823,7 @@ // Fold - If possible, fold this to a simpler init. Return this if not // possible to fold. - Init *Fold(Record *CurRec, bool IsFinal = false) const; + Init *Fold(Record *CurRec) const; Init *resolveReferences(Resolver &R) const override; @@ -1062,6 +1063,38 @@ std::string getAsString() const override; }; +/// !cast(expr[, default_value]) - Cast input to specified type. +class CastOpInit : public TypedInit, public FoldingSetNode { +private: + RecTy *CastType; + Init *Expr, *DefaultValue; + + CastOpInit(RecTy *CastType, Init *Expr, Init *DefaultValue) + : TypedInit(IK_CastOpInit, CastType), CastType(CastType), Expr(Expr), + DefaultValue(DefaultValue) {} + +public: + CastOpInit(const CastOpInit &) = delete; + CastOpInit &operator=(const CastOpInit &) = delete; + + static bool classof(const Init *I) { return I->getKind() == IK_CastOpInit; } + + static CastOpInit *get(RecTy *CastType, Init *Expr, + Init *DefaultValue = nullptr); + + 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; + + Init *resolveReferences(Resolver &R) const override; + + Init *getBit(unsigned Bit) const override; + + std::string getAsString() const override; +}; + /// !isa(expr) - Dynamically determine the type of an expression. class IsAOpInit : public TypedInit, public FoldingSetNode { private: 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 @@ -78,6 +78,7 @@ FoldingSet TheBinOpInitPool; FoldingSet TheTernOpInitPool; FoldingSet TheFoldOpInitPool; + FoldingSet TheCastOpInitPool; FoldingSet TheIsAOpInitPool; DenseMap, VarInit *> TheVarInitPool; DenseMap, VarBitInit *> TheVarBitInitPool; @@ -773,63 +774,9 @@ ProfileUnOpInit(ID, getOpcode(), getOperand(), getType()); } -Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { +Init *UnOpInit::Fold(Record *CurRec) const { RecordKeeper &RK = getRecordKeeper(); switch (getOpcode()) { - case CAST: - if (isa(getType())) { - if (StringInit *LHSs = dyn_cast(LHS)) - return LHSs; - - if (DefInit *LHSd = dyn_cast(LHS)) - return StringInit::get(RK, LHSd->getAsString()); - - if (IntInit *LHSi = dyn_cast_or_null( - LHS->convertInitializerTo(IntRecTy::get(RK)))) - return StringInit::get(RK, LHSi->getAsString()); - - } else if (isa(getType())) { - if (StringInit *Name = dyn_cast(LHS)) { - if (!CurRec && !IsFinal) - break; - assert(CurRec && "NULL pointer"); - Record *D; - - // 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) - break; - D = CurRec; - } else { - D = CurRec->getRecords().getDef(Name->getValue()); - if (!D) { - if (IsFinal) - PrintFatalError(CurRec->getLoc(), - Twine("Undefined reference to record: '") + - Name->getValue() + "'\n"); - break; - } - } - - DefInit *DI = DefInit::get(D); - if (!DI->getType()->typeIsA(getType())) { - PrintFatalError(CurRec->getLoc(), - Twine("Expected type '") + - getType()->getAsString() + "', got '" + - DI->getType()->getAsString() + "' in: " + - getAsString() + "\n"); - } - return DI; - } - } - - if (Init *NewInit = LHS->convertInitializerTo(getType())) - return NewInit; - break; - case NOT: if (IntInit *LHSi = dyn_cast_or_null( LHS->convertInitializerTo(IntRecTy::get(RK)))) @@ -891,16 +838,15 @@ Init *UnOpInit::resolveReferences(Resolver &R) const { Init *lhs = LHS->resolveReferences(R); - if (LHS != lhs || (R.isFinal() && getOpcode() == CAST)) + if (LHS != lhs) return (UnOpInit::get(getOpcode(), lhs, getType())) - ->Fold(R.getCurrentRecord(), R.isFinal()); + ->Fold(R.getCurrentRecord()); return const_cast(this); } std::string UnOpInit::getAsString() const { std::string Result; switch (getOpcode()) { - case CAST: Result = "!cast<" + getType()->getAsString() + ">"; break; case NOT: Result = "!not"; break; case HEAD: Result = "!head"; break; case TAIL: Result = "!tail"; break; @@ -1598,6 +1544,124 @@ .str(); } +static void ProfileCastOpInit(FoldingSetNodeID &ID, RecTy *CastType, Init *Expr, + Init *DefaultValue) { + ID.AddPointer(CastType); + ID.AddPointer(Expr); + if (DefaultValue) + ID.AddPointer(DefaultValue); +} + +CastOpInit *CastOpInit::get(RecTy *CastType, Init *Expr, Init *DefaultValue) { + FoldingSetNodeID ID; + ProfileCastOpInit(ID, CastType, Expr, DefaultValue); + + detail::RecordKeeperImpl &RK = CastType->getRecordKeeper().getImpl(); + void *IP = nullptr; + if (CastOpInit *I = RK.TheCastOpInitPool.FindNodeOrInsertPos(ID, IP)) + return I; + + CastOpInit *I = new (RK.Allocator) CastOpInit(CastType, Expr, DefaultValue); + RK.TheCastOpInitPool.InsertNode(I, IP); + return I; +} + +void CastOpInit::Profile(FoldingSetNodeID &ID) const { + ProfileCastOpInit(ID, CastType, Expr, DefaultValue); +} + +Init *CastOpInit::Fold(Record *CurRec, bool IsFinal) const { + RecordKeeper &RK = getRecordKeeper(); + if (isa(getType())) { + if (StringInit *LHSs = dyn_cast(Expr)) + return LHSs; + + if (DefInit *LHSd = dyn_cast(Expr)) + return StringInit::get(RK, LHSd->getAsString()); + + if (IntInit *LHSi = dyn_cast_or_null( + Expr->convertInitializerTo(IntRecTy::get(RK)))) + return StringInit::get(RK, LHSi->getAsString()); + + } else if (isa(getType())) { + if (StringInit *Name = dyn_cast(Expr)) { + if (!CurRec && !IsFinal) + return const_cast(this); + assert(CurRec && "NULL pointer"); + Record *D; + + // 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); + D = CurRec; + } else { + D = CurRec->getRecords().getDef(Name->getValue()); + if (!D) { + if (IsFinal) { + if (!DefaultValue) { + PrintFatalError(CurRec->getLoc(), + Twine("Undefined reference to record: '") + + Name->getValue() + "'\n"); + } + if (auto *TI = dyn_cast(DefaultValue)) { + if (!TI->getType()->typeIsConvertibleTo(CastType)) + PrintFatalError( + CurRec->getLoc(), + Twine("Default value of !cast should be of type '") + + CastType->getAsString() + "'\n"); + } + return DefaultValue; + } + return const_cast(this); + } + } + + DefInit *DI = DefInit::get(D); + if (!DI->getType()->typeIsA(getType())) { + PrintFatalError(CurRec->getLoc(), + Twine("Expected type '") + getType()->getAsString() + + "', got '" + DI->getType()->getAsString() + + "' in: " + getAsString() + "\n"); + } + return DI; + } + } + + if (Init *NewInit = Expr->convertInitializerTo(getType())) + return NewInit; + + return const_cast(this); +} + +Init *CastOpInit::resolveReferences(Resolver &R) const { + Init *NewExpr = Expr->resolveReferences(R); + Init *NewDefaultValue = nullptr; + if (DefaultValue) + NewDefaultValue = DefaultValue->resolveReferences(R); + if (Expr != NewExpr || DefaultValue != NewDefaultValue || R.isFinal()) + return get(CastType, NewExpr, NewDefaultValue) + ->Fold(R.getCurrentRecord(), R.isFinal()); + return const_cast(this); +} + +Init *CastOpInit::getBit(unsigned Bit) const { + if (getType() == BitRecTy::get(getRecordKeeper())) + return const_cast(this); + return VarBitInit::get(const_cast(this), Bit); +} + +std::string CastOpInit::getAsString() const { + std::string Result = + "!cast<" + getType()->getAsString() + ">(" + Expr->getAsString(); + if (DefaultValue) + Result += "," + DefaultValue->getAsString(); + return Result + ")"; +} + static void ProfileIsAOpInit(FoldingSetNodeID &ID, RecTy *CheckType, Init *Expr) { ID.AddPointer(CheckType); @@ -1712,8 +1776,7 @@ if (!getType()->typeIsConvertibleTo(Ty)) return nullptr; - return UnOpInit::get(UnOpInit::CAST, const_cast(this), Ty) - ->Fold(nullptr); + return CastOpInit::get(Ty, const_cast(this))->Fold(nullptr); } Init *TypedInit::convertInitListSlice(ArrayRef Elements) const { 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 @@ -916,7 +916,7 @@ // get the correct type. if (CurRec && !CurRec->isClass() && !CurMultiClass && CurRec->getNameInit() == Name) - return UnOpInit::get(UnOpInit::CAST, Name, CurRec->getType()); + return CastOpInit::get(CurRec->getType(), Name); Error(NameLoc, "Variable not defined: '" + Name->getValue() + "'"); return nullptr; @@ -936,25 +936,13 @@ case tgtok::XTail: case tgtok::XSize: case tgtok::XEmpty: - case tgtok::XCast: case tgtok::XGetDagOp: { // Value ::= !unop '(' Value ')' UnOpInit::UnaryOp Code; RecTy *Type = nullptr; switch (Lex.getCode()) { - default: llvm_unreachable("Unhandled code!"); - case tgtok::XCast: - Lex.Lex(); // eat the operation - Code = UnOpInit::CAST; - - Type = ParseOperatorType(); - - if (!Type) { - TokError("did not get type for unary operator"); - return nullptr; - } - - break; + default: + llvm_unreachable("Unhandled code!"); case tgtok::XNOT: Lex.Lex(); // eat the operation Code = UnOpInit::NOT; @@ -1070,7 +1058,27 @@ } return (UnOpInit::get(Code, LHS, Type))->Fold(CurRec); } + case tgtok::XCast: { + // Value ::= !cast '<' Type '>' '(' Value [',' Value] ')' + Lex.Lex(); // eat the operation + RecTy *Type = ParseOperatorType(); + if (!Type) { + TokError("did not get type for !cast"); + return nullptr; + } + if (!consume(tgtok::l_paren)) { + TokError("expected '(' after type of !cast"); + return nullptr; + } + Init *Expr = ParseValue(CurRec); + Init *DefaultValue = consume(tgtok::comma) ? ParseValue(CurRec) : nullptr; + if (!consume(tgtok::r_paren)) { + TokError("expected ')' in !cast"); + return nullptr; + } + return (CastOpInit::get(Type, Expr, DefaultValue))->Fold(CurRec); + } case tgtok::XIsA: { // Value ::= !isa '<' Type '>' '(' Value ')' Lex.Lex(); // eat the operation @@ -2508,8 +2516,7 @@ // a string if necessary. if (LHS->getType() != StringRecTy::get(Records)) { auto CastLHS = dyn_cast( - UnOpInit::get(UnOpInit::CAST, LHS, StringRecTy::get(Records)) - ->Fold(CurRec)); + CastOpInit::get(StringRecTy::get(Records), LHS)->Fold(CurRec)); if (!CastLHS) { Error(PasteLoc, Twine("can't cast '") + LHS->getAsString() + "' to string"); @@ -2545,8 +2552,7 @@ if (RHS->getType() != StringRecTy::get(Records)) { auto CastRHS = dyn_cast( - UnOpInit::get(UnOpInit::CAST, RHS, StringRecTy::get(Records)) - ->Fold(CurRec)); + CastOpInit::get(StringRecTy::get(Records), RHS)->Fold(CurRec)); if (!CastRHS) { Error(PasteLoc, Twine("can't cast '") + RHS->getAsString() + "' to string"); diff --git a/llvm/test/TableGen/cast-with-default-value-of-wrong-type.td b/llvm/test/TableGen/cast-with-default-value-of-wrong-type.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/cast-with-default-value-of-wrong-type.td @@ -0,0 +1,27 @@ +// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +// CHECK: Default value of !cast should be of type 'X' + +class X { + int value = v; +} + +class A { + int value = v; +} + +def NonSub : A<0>; + +def NULL : X<-1>; + +class Y { + int value = !cast("X" # id, NonSub).value; +} + +def X0 : X<0>; +def X1 : X<1>; + +def Y0: Y<0>; +def Y1: Y<1>; +def Y2: Y<2>; diff --git a/llvm/test/TableGen/cast-with-default-value.td b/llvm/test/TableGen/cast-with-default-value.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/cast-with-default-value.td @@ -0,0 +1,59 @@ +// RUN: llvm-tblgen --no-warn-on-unused-template-args %s | FileCheck %s +// XFAIL: vg_leak + +// CHECK: def NULL { +// CHECK: int value = -1; +// CHECK: } + +// CHECK: def Sub { +// CHECK: int value = -1; +// CHECK: } + +// CHECK: def X0 { +// CHECK: int value = 0; +// CHECK: } + +// CHECK: def X1 { +// CHECK: int value = 1; +// CHECK: } + +// CHECK: def Y0 { +// CHECK: int value1 = 0; +// CHECK: int value2 = 0; +// CHECK: X x = X0; +// CHECK: } + +// CHECK: def Y1 { +// CHECK: int value1 = 1; +// CHECK: int value2 = 1; +// CHECK: X x = X1; +// CHECK: } + +// CHECK: def Y2 { +// CHECK: int value1 = -1; +// CHECK: int value2 = -1; +// CHECK: X x = ?; +// CHECK: } + +class X { + int value = v; +} + +class A: X; + +def Sub : A<-1>; + +def NULL : X<-1>; + +class Y { + int value1 = !cast("X" # id, NULL).value; + int value2 = !cast("X" # id, Sub).value; + X x = !cast("X" # id, ?); +} + +def X0 : X<0>; +def X1 : X<1>; + +def Y0: Y<0>; +def Y1: Y<1>; +def Y2: Y<2>; diff --git a/llvm/test/TableGen/cast-without-default-value.td b/llvm/test/TableGen/cast-without-default-value.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/cast-without-default-value.td @@ -0,0 +1,19 @@ +// RUN: not llvm-tblgen --no-warn-on-unused-template-args %s 2>&1 | FileCheck %s +// XFAIL: vg_leak + +// CHECK: Undefined reference to record: 'X2' + +class X { + int value = v; +} + +class Y { + int value = !cast("X" # id).value; +} + +def X0 : X<0>; +def X1 : X<1>; + +def Y0: Y<0>; +def Y1: Y<1>; +def Y2: Y<2>;