diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst index 03fe1157b4042e..cfe61382658ec4 100644 --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -223,12 +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 - : !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 + : !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 The ``!cond`` operator has a slightly different syntax compared to other bang operators, so it is defined separately: @@ -555,7 +556,7 @@ previous case, if the *right-hand-side* operand is an undefined name or a global name, it is treated as a verbatim string of characters. The left-hand-side operand is treated normally. -Values can have a trailing paste operator, in which case the left-hand-side +Values can have a trailing paste operator, in which case the left-hand-side operand is concatenated to an empty string. `Appendix B: Paste Operator Examples`_ presents examples of the behavior of @@ -1815,6 +1816,10 @@ and non-0 as true. ``int``. If the result is not 0, the *then* expression is produced; otherwise the *else* expression is produced. +``!initialized(``\ *a*\ ``)`` + This operator produces 1 if *a* is not the uninitialized value (``?``) and 0 + otherwise. + ``!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 e64b78c3c1e3b9..81a9257425783f 100644 --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -860,6 +860,7 @@ class UnOpInit : public OpInit, public FoldingSetNode { LOG2, REPR, LISTFLATTEN, + INITIALIZED, }; private: diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index feef51f3d203cd..597ccb7ca144bb 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -917,6 +917,13 @@ const Init *UnOpInit::Fold(const Record *CurRec, bool IsFinal) const { return NewInit; break; + case INITIALIZED: + if (isa(LHS)) + return IntInit::get(RK, 0); + if (LHS->isConcrete()) + return IntInit::get(RK, 1); + break; + case NOT: if (const auto *LHSi = dyn_cast_or_null( LHS->convertInitializerTo(IntRecTy::get(RK)))) @@ -1052,6 +1059,9 @@ std::string UnOpInit::getAsString() const { case TOUPPER: Result = "!toupper"; break; + case INITIALIZED: + Result = "!initialized"; + break; } return Result + "(" + LHS->getAsString() + ")"; } diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp index 1e93b2c160ba58..eee42511804f5e 100644 --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -633,6 +633,7 @@ tgtok::TokKind TGLexer::LexExclaim() { .Case("listremove", tgtok::XListRemove) .Case("range", tgtok::XRange) .Case("strconcat", tgtok::XStrConcat) + .Case("initialized", tgtok::XInitialized) .Case("interleave", tgtok::XInterleave) .Case("substr", tgtok::XSubstr) .Case("find", tgtok::XFind) diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h index 9a6874c8975730..963d75e52cc8fd 100644 --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -135,6 +135,7 @@ enum TokKind { XTail, XSize, XEmpty, + XInitialized, XIf, XCond, XEq, diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp index 8a8cd2b7356cd3..e8679439c81de3 100644 --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -969,7 +969,7 @@ const TypedInit *TGParser::ParseSliceElements(Record *CurRec, bool Single) { /// RangePiece ::= INTVAL /// RangePiece ::= INTVAL '...' INTVAL /// RangePiece ::= INTVAL '-' INTVAL -/// RangePiece ::= INTVAL INTVAL +/// RangePiece ::= INTVAL INTVAL // The last two forms are deprecated. bool TGParser::ParseRangePiece(SmallVectorImpl &Ranges, const TypedInit *FirstItem) { @@ -1203,7 +1203,8 @@ const Init *TGParser::ParseOperation(Record *CurRec, const RecTy *ItemType) { case tgtok::XEmpty: case tgtok::XCast: case tgtok::XRepr: - case tgtok::XGetDagOp: { // Value ::= !unop '(' Value ')' + case tgtok::XGetDagOp: + case tgtok::XInitialized: { // Value ::= !unop '(' Value ')' UnOpInit::UnaryOp Code; const RecTy *Type = nullptr; @@ -1291,6 +1292,11 @@ const Init *TGParser::ParseOperation(Record *CurRec, const RecTy *ItemType) { } Code = UnOpInit::GETDAGOP; break; + case tgtok::XInitialized: + Lex.Lex(); // eat the operation + Code = UnOpInit::INITIALIZED; + Type = IntRecTy::get(Records); + break; } if (!consume(tgtok::l_paren)) { TokError("expected '(' after unary operator"); @@ -1655,8 +1661,8 @@ const Init *TGParser::ParseOperation(Record *CurRec, const RecTy *ItemType) { !ArgType->typeIsConvertibleTo(StringRecTy::get(Records)) && !ArgType->typeIsConvertibleTo(RecordRecTy::get(Records, {}))) { Error(InitLoc, Twine("expected bit, bits, int, string, or record; " - "got value of type '") + ArgType->getAsString() + - "'"); + "got value of type '") + + ArgType->getAsString() + "'"); return nullptr; } break; @@ -1669,8 +1675,8 @@ const Init *TGParser::ParseOperation(Record *CurRec, const RecTy *ItemType) { if (!ArgType->typeIsConvertibleTo(IntRecTy::get(Records)) && !ArgType->typeIsConvertibleTo(StringRecTy::get(Records))) { Error(InitLoc, Twine("expected bit, bits, int, or string; " - "got value of type '") + ArgType->getAsString() + - "'"); + "got value of type '") + + ArgType->getAsString() + "'"); return nullptr; } break; @@ -2528,7 +2534,7 @@ const Init *TGParser::ParseOperationForEachFilter(Record *CurRec, OutType = RHSt->getType()->getListTy(); } else if (Operation == tgtok::XFilter) { OutType = InEltType->getListTy(); - } + } return (TernOpInit::get((Operation == tgtok::XForEach) ? TernOpInit::FOREACH : TernOpInit::FILTER, @@ -3548,7 +3554,7 @@ bool TGParser::ParseBody(Record *CurRec) { SMLoc SemiLoc = Lex.getLoc(); if (consume(tgtok::semi)) { PrintError(SemiLoc, "A class or def body should not end with a semicolon"); - PrintNote("Semicolon ignored; remove to eliminate this error"); + PrintNote("Semicolon ignored; remove to eliminate this error"); } return false; @@ -4218,7 +4224,7 @@ bool TGParser::ParseMultiClass() { SMLoc SemiLoc = Lex.getLoc(); if (consume(tgtok::semi)) { PrintError(SemiLoc, "A multiclass body should not end with a semicolon"); - PrintNote("Semicolon ignored; remove to eliminate this error"); + PrintNote("Semicolon ignored; remove to eliminate this error"); } } diff --git a/llvm/test/TableGen/initialized.td b/llvm/test/TableGen/initialized.td new file mode 100644 index 00000000000000..e1743909feb64f --- /dev/null +++ b/llvm/test/TableGen/initialized.td @@ -0,0 +1,59 @@ +// RUN: llvm-tblgen %s | FileCheck %s + +// CHECK: class F { +// CHECK: string ret = !if(!initialized([[ARG]].str), [[ARG]].str, "N/A"); +// CHECK: } + +// CHECK-LABEL: def C +// CHECK: bit c0 = 0 +// CHECK: bit c1 = 1 +// CHECK: bit c2 = 1 +def C { + bit c0 = !initialized(?); + bit c1 = !initialized(0); + bit c2 = !initialized(1); +} + +class Y { + string str = ?; +} + +class F { + string ret = !if(!initialized(y.str), y.str, "N/A"); +} + +def Y0 : Y; +def Y1 : Y { + let str = "foo"; +} + +// CHECK-LABEL: def FY0 +// CHECK: string ret = "N/A"; +// CHECK-LABEL: def FY1 +// CHECK: string ret = "foo"; +def FY0 : F; +def FY1 : F; + +class G { + list v = [y.str]; + bit isInit = !initialized(v); +} + +// CHECK-LABEL: def GY0 +// CHECK: isInit = 1 +// CHECK-LABEL: def GY1 +// CHECK: isInit = 1 +def GY0 : G; +def GY1 : G; + +class Thing; +def aThing : Thing; +class Propagate { + Thing ret = !if(!initialized(t), t, ?); +} +// CHECK-LABEL: def PropagateNothing +// CHECK: Thing ret = ? +// CHECK-LABEL: def PropagateThing +// CHECK: Thing ret = aThing +def PropagateNothing : Propagate; +def PropagateThing : Propagate;