Skip to content

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

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

Merged
merged 3 commits into from
Dec 18, 2024

Conversation

krzysz00
Copy link
Contributor

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 ?.

@llvmbot
Copy link
Member

llvmbot commented Nov 28, 2024

@llvm/pr-subscribers-tablegen

Author: Krzysztof Drewniak (krzysz00)

Changes

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 ?.


Full diff: https://github.com/llvm/llvm-project/pull/117964.diff

7 Files Affected:

  • (modified) llvm/docs/TableGen/ProgRef.rst (+12-7)
  • (modified) llvm/include/llvm/TableGen/Record.h (+1)
  • (modified) llvm/lib/TableGen/Record.cpp (+10)
  • (modified) llvm/lib/TableGen/TGLexer.cpp (+1)
  • (modified) llvm/lib/TableGen/TGLexer.h (+1)
  • (modified) llvm/lib/TableGen/TGParser.cpp (+15-9)
  • (added) llvm/test/TableGen/initialized.td (+59)
diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index 03fe1157b4042e..5f0d44a3171c7c 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 is produces a 1 if *a* is not the unitialized 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..33607005a787a5 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_and_nonnull<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() + ")";
 }
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 e01342ffcd3c8f..176af09dd29dd4 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<unsigned> &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<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>;

@optimisan optimisan self-requested a review November 29, 2024 11:09
Copy link
Contributor

@optimisan optimisan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think codewise GTM.

@optimisan optimisan requested a review from nhaehnle December 1, 2024 14:33
Copy link
Contributor

@jurahul jurahul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thanks!

krzysz00 and others added 3 commits December 16, 2024 19:15
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 ?.
@krzysz00 krzysz00 force-pushed the users/krzysz00/initialized-predicate-tblgen branch from d972251 to 20deaa1 Compare December 17, 2024 03:16
@krzysz00 krzysz00 merged commit b24caf3 into main Dec 18, 2024
9 checks passed
@krzysz00 krzysz00 deleted the users/krzysz00/initialized-predicate-tblgen branch December 18, 2024 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants