[llvm][TableGen] Add a !initialized predicate to allow testing for ? (#117964)

There are cases (like in an upcoming patch to MLIR's `Property` class)
where the ? value is a useful null value. However, existing predicates
make ti difficult to test if the value in a record one is operating is ?
or not.

This commit adds the !initialized predicate, which is 1 on concrete,
non-? values and 0 on ?.

---------

Co-authored-by: Akshat Oke <Akshat.Oke@amd.com>
This commit is contained in:
Krzysztof Drewniak 2024-12-17 18:34:35 -08:00 committed by GitHub
parent fb33268d2f
commit b24caf3d2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 99 additions and 16 deletions

View File

@ -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:
@ -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.

View File

@ -860,6 +860,7 @@ public:
LOG2,
REPR,
LISTFLATTEN,
INITIALIZED,
};
private:

View File

@ -917,6 +917,13 @@ const Init *UnOpInit::Fold(const Record *CurRec, bool IsFinal) const {
return NewInit;
break;
case INITIALIZED:
if (isa<UnsetInit>(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<IntInit>(
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() + ")";
}

View File

@ -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)

View File

@ -135,6 +135,7 @@ enum TokKind {
XTail,
XSize,
XEmpty,
XInitialized,
XIf,
XCond,
XEq,

View File

@ -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;

View File

@ -0,0 +1,59 @@
// RUN: llvm-tblgen %s | FileCheck %s
// CHECK: class F<Y [[ARG:.+]] = ?> {
// 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<Y y> {
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<Y0>;
def FY1 : F<Y1>;
class G<Y y> {
list<string> v = [y.str];
bit isInit = !initialized(v);
}
// CHECK-LABEL: def GY0
// CHECK: isInit = 1
// CHECK-LABEL: def GY1
// CHECK: isInit = 1
def GY0 : G<Y0>;
def GY1 : G<Y1>;
class Thing;
def aThing : Thing;
class Propagate<Thing t> {
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<aThing>;