[TableGen] Add !getdagarg and !getdagname
authorMichael Liao <michael.hliao@gmail.com>
Fri, 26 May 2023 16:58:12 +0000 (12:58 -0400)
committerMichael Liao <michael.hliao@gmail.com>
Wed, 31 May 2023 14:54:43 +0000 (10:54 -0400)
- This patch proposes to add `!getdagarg` and `!getdagname` bang
  operators as the inverse operation of `!dag`. They allow us to examine
  arguments of a given dag.

Reviewed By: simon_tatham

Differential Revision: https://reviews.llvm.org/D151602

llvm/docs/TableGen/ProgRef.rst
llvm/include/llvm/TableGen/Record.h
llvm/lib/TableGen/Record.cpp
llvm/lib/TableGen/TGLexer.cpp
llvm/lib/TableGen/TGLexer.h
llvm/lib/TableGen/TGParser.cpp
llvm/test/TableGen/getsetop.td

index d49eddf..2e378ab 100644 (file)
@@ -221,13 +221,14 @@ TableGen provides "bang operators" that have a wide variety of uses:
    BangOperator: one of
                : !add         !and         !cast        !con         !dag
                : !div         !empty       !eq          !exists      !filter
-               : !find        !foldl       !foreach     !ge          !getdagop
-               : !gt          !head        !if          !interleave  !isa
-               : !le          !listconcat  !listremove  !listsplat   !logtwo
-               : !lt          !mul         !ne          !not         !or
-               : !range       !setdagop    !shl         !size        !sra
-               : !srl         !strconcat   !sub         !subst       !substr
-               : !tail        !tolower     !toupper     !xor
+               : !find        !foldl       !foreach     !ge          !getdagarg
+               : !getdagname  !getdagop    !gt          !head        !if
+               : !interleave  !isa         !le          !listconcat  !listremove
+               : !listsplat   !logtwo      !lt          !mul         !ne
+               : !not         !or          !range       !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:
@@ -1368,7 +1369,8 @@ or to associate an argument in one DAG with a like-named argument in another
 DAG.
 
 The following bang operators are useful for working with DAGs:
-``!con``, ``!dag``, ``!empty``, ``!foreach``, ``!getdagop``, ``!setdagop``, ``!size``.
+``!con``, ``!dag``, ``!empty``, ``!foreach``, ``!getdagarg``, ``!getdagname``,
+``!getdagop``, ``!setdagop``, ``!size``.
 
 Defvar in a record body
 -----------------------
@@ -1711,6 +1713,16 @@ and non-0 as true.
     This operator produces 1 if *a* is greater than or equal to *b*; 0 otherwise.
     The arguments must be ``bit``, ``bits``, ``int``, or ``string`` values.
 
+``!getdagarg<``\ *type*\ ``>(``\ *dag*\ ``,``\ *key*\ ``)``
+    This operator retrieves the argument from the given *dag* node by the
+    specified *key*, which is either an integer index or a string name. If that
+    argument is not convertible to the specified *type*, ``?`` is returned.
+
+``!getdagname(``\ *dag*\ ``,``\ *index*\ ``)``
+    This operator retrieves the argument name from the given *dag* node by the
+    specified *index*. If that argument has no name associated, ``?`` is
+    returned.
+
 ``!getdagop(``\ *dag*\ ``)`` --or-- ``!getdagop<``\ *type*\ ``>(``\ *dag*\ ``)``
     This operator produces the operator of the given *dag* node.
     Example: ``!getdagop((foo 1, 2))`` results in ``foo``. Recall that
index adb8e8c..2b2d411 100644 (file)
@@ -858,7 +858,9 @@ public:
     LT,
     GE,
     GT,
-    SETDAGOP
+    GETDAGARG,
+    GETDAGNAME,
+    SETDAGOP,
   };
 
 private:
index 110fd6e..eb7a213 100644 (file)
@@ -1276,6 +1276,75 @@ std::optional<bool> BinOpInit::CompareInit(unsigned Opc, Init *LHS, Init *RHS) c
       return BitInit::get(getRecordKeeper(), *Result);
     break;
   }
+  case GETDAGARG: {
+    DagInit *Dag = dyn_cast<DagInit>(LHS);
+    if (!Dag)
+      break;
+
+    // Helper returning the specified argument.
+    auto getDagArgAsType = [](DagInit *Dag, unsigned Pos,
+                              RecTy *Type) -> Init * {
+      assert(Pos < Dag->getNumArgs());
+      Init *Arg = Dag->getArg(Pos);
+      if (auto *TI = dyn_cast<TypedInit>(Arg))
+        if (!TI->getType()->typeIsConvertibleTo(Type))
+          return UnsetInit::get(Dag->getRecordKeeper());
+      return Arg;
+    };
+
+    // Accessor by index
+    if (IntInit *Idx = dyn_cast<IntInit>(RHS)) {
+      int64_t Pos = Idx->getValue();
+      if (Pos < 0) {
+        // The index is negative.
+        PrintFatalError(CurRec->getLoc(), Twine("!getdagarg index ") +
+                                              std::to_string(Pos) +
+                                              Twine(" is negative"));
+      }
+      if (Pos >= Dag->getNumArgs()) {
+        // The index is out-of-range.
+        PrintFatalError(CurRec->getLoc(),
+                        Twine("!getdagarg index ") + std::to_string(Pos) +
+                            " is out of range (dag has " +
+                            std::to_string(Dag->getNumArgs()) + " arguments)");
+      }
+      return getDagArgAsType(Dag, Pos, getType());
+    }
+    // Accessor by name
+    if (StringInit *Key = dyn_cast<StringInit>(RHS)) {
+      for (unsigned i = 0, e = Dag->getNumArgs(); i < e; ++i) {
+        StringInit *ArgName = Dag->getArgName(i);
+        if (!ArgName || ArgName->getValue() != Key->getValue())
+          continue;
+        // Found
+        return getDagArgAsType(Dag, i, getType());
+      }
+      // The key is not found.
+      PrintFatalError(CurRec->getLoc(), Twine("!getdagarg key '") +
+                                            Key->getValue() +
+                                            Twine("' is not found"));
+    }
+    break;
+  }
+  case GETDAGNAME: {
+    DagInit *Dag = dyn_cast<DagInit>(LHS);
+    IntInit *Idx = dyn_cast<IntInit>(RHS);
+    if (Dag && Idx) {
+      int64_t Pos = Idx->getValue();
+      if (Pos < 0 || Pos >= Dag->getNumArgs()) {
+        // The index is out-of-range.
+        PrintError(CurRec->getLoc(),
+                   Twine("!getdagname index is out of range 0...") +
+                       std::to_string(Dag->getNumArgs() - 1) + ": " +
+                       std::to_string(Pos));
+      }
+      Init *ArgName = Dag->getArgName(Pos);
+      if (!ArgName)
+        return UnsetInit::get(getRecordKeeper());
+      return ArgName;
+    }
+    break;
+  }
   case SETDAGOP: {
     DagInit *Dag = dyn_cast<DagInit>(LHS);
     DefInit *Op = dyn_cast<DefInit>(RHS);
@@ -1380,6 +1449,12 @@ std::string BinOpInit::getAsString() const {
   case STRCONCAT: Result = "!strconcat"; break;
   case INTERLEAVE: Result = "!interleave"; break;
   case SETDAGOP: Result = "!setdagop"; break;
+  case GETDAGARG:
+    Result = "!getdagarg<" + getType()->getAsString() + ">";
+    break;
+  case GETDAGNAME:
+    Result = "!getdagname";
+    break;
   }
   return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")";
 }
index 24ec403..84464dc 100644 (file)
@@ -592,6 +592,8 @@ tgtok::TokKind TGLexer::LexExclaim() {
           .Case("find", tgtok::XFind)
           .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated.
           .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated.
+          .Case("getdagarg", tgtok::XGetDagArg)
+          .Case("getdagname", tgtok::XGetDagName)
           .Case("exists", tgtok::XExists)
           .Case("tolower", tgtok::XToLower)
           .Case("toupper", tgtok::XToUpper)
index 35976c6..dbd3853 100644 (file)
@@ -31,35 +31,108 @@ class Twine;
 namespace tgtok {
 enum TokKind {
   // Markers
-    Eof, Error,
+  Eof,
+  Error,
 
   // Tokens with no info.
-    minus, plus,        // - +
-    l_square, r_square, // [ ]
-    l_brace, r_brace,   // { }
-    l_paren, r_paren,   // ( )
-    less, greater,      // < >
-    colon, semi,        // : ;
-    comma, dot,         // , .
-    equal, question,    // = ?
-    paste,              // #
-    dotdotdot,          // ...
+  minus,     // -
+  plus,      // +
+  l_square,  // [
+  r_square,  // ]
+  l_brace,   // {
+  r_brace,   // }
+  l_paren,   // (
+  r_paren,   // )
+  less,      // <
+  greater,   // >
+  colon,     // :
+  semi,      // ;
+  comma,     // ,
+  dot,       // .
+  equal,     // =
+  question,  // ?
+  paste,     // #
+  dotdotdot, // ...
 
   // Reserved keywords. ('ElseKW' is named to distinguish it from the
   // existing 'Else' that means the preprocessor #else.)
-    Assert, Bit, Bits, Class, Code, Dag, Def, Defm, Defset, Defvar, ElseKW,
-    FalseKW, Field, Foreach, If, In, Include, Int, Let, List, MultiClass,
-    String, Then, TrueKW,
+  Assert,
+  Bit,
+  Bits,
+  Class,
+  Code,
+  Dag,
+  Def,
+  Defm,
+  Defset,
+  Defvar,
+  ElseKW,
+  FalseKW,
+  Field,
+  Foreach,
+  If,
+  In,
+  Include,
+  Int,
+  Let,
+  List,
+  MultiClass,
+  String,
+  Then,
+  TrueKW,
 
   // Bang operators.
-    XConcat, XADD, XSUB, XMUL, XDIV, XNOT, XLOG2, XAND, XOR, XXOR, XSRA, XSRL,
-    XSHL, 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,
-    XExists, XListRemove, XToLower, XToUpper, XRange,
+  XConcat,
+  XADD,
+  XSUB,
+  XMUL,
+  XDIV,
+  XNOT,
+  XLOG2,
+  XAND,
+  XOR,
+  XXOR,
+  XSRA,
+  XSRL,
+  XSHL,
+  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,
+  XExists,
+  XListRemove,
+  XToLower,
+  XToUpper,
+  XRange,
+  XGetDagArg,
+  XGetDagName,
 
   // Boolean literals.
-    TrueVal, FalseVal,
+  TrueVal,
+  FalseVal,
 
   // Integer value.
   IntVal,
@@ -69,11 +142,18 @@ enum TokKind {
   BinaryIntVal,
 
   // String valued tokens.
-    Id, StrVal, VarName, CodeFragment,
+  Id,
+  StrVal,
+  VarName,
+  CodeFragment,
 
   // Preprocessing tokens for internal usage by the lexer.
   // They are never returned as a result of Lex().
-    Ifdef, Ifndef, Else, Endif, Define
+  Ifdef,
+  Ifndef,
+  Else,
+  Endif,
+  Define
 };
 }
 
index 1580c4d..e089222 100644 (file)
@@ -1397,6 +1397,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
   case tgtok::XRange:
   case tgtok::XStrConcat:
   case tgtok::XInterleave:
+  case tgtok::XGetDagArg:
+  case tgtok::XGetDagName:
   case tgtok::XSetDagOp: { // Value ::= !binop '(' Value ',' Value ')'
     tgtok::TokKind OpTok = Lex.getCode();
     SMLoc OpLoc = Lex.getLoc();
@@ -1429,6 +1431,12 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
     case tgtok::XStrConcat:  Code = BinOpInit::STRCONCAT; break;
     case tgtok::XInterleave: Code = BinOpInit::INTERLEAVE; break;
     case tgtok::XSetDagOp:   Code = BinOpInit::SETDAGOP; break;
+    case tgtok::XGetDagArg:
+      Code = BinOpInit::GETDAGARG;
+      break;
+    case tgtok::XGetDagName:
+      Code = BinOpInit::GETDAGNAME;
+      break;
     }
 
     RecTy *Type = nullptr;
@@ -1441,6 +1449,18 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
       Type = DagRecTy::get(Records);
       ArgType = DagRecTy::get(Records);
       break;
+    case tgtok::XGetDagArg:
+      Type = ParseOperatorType();
+      if (!Type) {
+        TokError("did not get type for !getdagarg operator");
+        return nullptr;
+      }
+      ArgType = DagRecTy::get(Records);
+      break;
+    case tgtok::XGetDagName:
+      Type = StringRecTy::get(Records);
+      ArgType = DagRecTy::get(Records);
+      break;
     case tgtok::XAND:
     case tgtok::XOR:
     case tgtok::XXOR:
@@ -1594,6 +1614,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
             return nullptr;
           }
           break;
+        case BinOpInit::GETDAGARG: // The 2nd argument of !getdagarg could be
+                                   // index or name.
         case BinOpInit::LE:
         case BinOpInit::LT:
         case BinOpInit::GE:
@@ -1658,6 +1680,15 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
           // a record, with no restriction on its superclasses.
           ArgType = RecordRecTy::get(Records, {});
           break;
+        case BinOpInit::GETDAGARG:
+          // After parsing the first dag argument, expect an index integer or a
+          // name string.
+          ArgType = nullptr;
+          break;
+        case BinOpInit::GETDAGNAME:
+          // After parsing the first dag argument, expect an index integer.
+          ArgType = IntRecTy::get(Records);
+          break;
         default:
           break;
       }
@@ -2753,6 +2784,8 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
   case tgtok::XRange:
   case tgtok::XStrConcat:
   case tgtok::XInterleave:
+  case tgtok::XGetDagArg:
+  case tgtok::XGetDagName:
   case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')'
   case tgtok::XIf:
   case tgtok::XCond:
index ff8230f..e5a8d8a 100644 (file)
@@ -2,6 +2,9 @@
 // RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
 // RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s
 // RUN: not llvm-tblgen -DERROR3 %s 2>&1 | FileCheck --check-prefix=ERROR3 %s
+// RUN: not llvm-tblgen -DERROR4 %s 2>&1 | FileCheck --check-prefix=ERROR4 %s
+// RUN: not llvm-tblgen -DERROR5 %s 2>&1 | FileCheck --check-prefix=ERROR5 %s
+// RUN: not llvm-tblgen -DERROR6 %s 2>&1 | FileCheck --check-prefix=ERROR6 %s
 
 // !setop and !getop are deprecated in favor of !setdagop and !getdagop.
 // Two tests retain the old names just to be sure they are still supported.
 class Base;
 class OtherBase;
 
+class Super : Base;
+
 def foo: Base;
 def bar: Base;
 def qux: OtherBase;
 
+def alice : Super;
+def bob : Super;
+
 def test {
   dag orig = (foo 1, 2:$a, $b);
   dag another = (qux "hello", $world);
@@ -61,4 +69,49 @@ def test {
   // ERROR3: error: type for !getdagop must be a record type
   int ridiculousCast = !getdagop<int>(orig);
 #endif
+
+  dag in1 = (foo 1:$a, 2:$b, 3:$c);
+  // CHECK: list<string> in1Names = ["a", "b", "c"];
+  list<string> in1Names = !foreach(i, !range(!size(in1)), !getdagname(in1, i));
+  // CHECK: list<int> in1Args = [1, 2, 3];
+  list<int> in1Args = !foreach(i, !range(!size(in1)), !getdagarg<int>(in1, i));
+
+  dag in2 = (foo 1:$a, (bar "x":$x, (qux foo:$s1, bar:$s2):$y, 7:$z):$b, 3:$c);
+  // CHECK: dag in2NestedDag = (qux foo:$s1, bar:$s2);
+  dag in2NestedDag = !getdagarg<dag>(!getdagarg<dag>(in2, 1), "y");
+  // CHECK: Base in2NestedArg = foo;
+  Base in2NestedArg = !getdagarg<Base>(!getdagarg<dag>(!getdagarg<dag>(in2, 1), "y"), "s1");
+
+  dag in3 = (foo 1:$a, ?:$b, 3);
+  // CHECK: list<string> in3Names = ["a", "b", ?];
+  list<string> in3Names = !foreach(i, !range(!size(in3)), !getdagname(in3, i));
+  // CHECK: list<int> in3Args = [1, ?, 3];
+  list<int> in3Args = !foreach(i, !range(!size(in3)), !getdagarg<int>(in3, i));
+
+#ifdef ERROR4
+  // ERROR4: error: !getdagarg index -1 is negative
+  int outOfRange = !getdagarg<int>(in1, -1);
+#endif
+
+#ifdef ERROR5
+  // ERROR5: error: !getdagarg index 3 is out of range (dag has 3 arguments)
+  int outOfRange = !getdagarg<int>(in1, 3);
+#endif
+
+#ifdef ERROR6
+  // ERROR6: error: !getdagarg key 'x' is not found
+  int notFound = !getdagarg<int>(in1, "x");
+#endif
+
+  dag in4 = (foo "arg1":$a, "arg2":$b, "arg3":$c);
+  // CHECK: int misMatchType1 = ?;
+  int misMatchType1 = !getdagarg<int>(in4, 0);
+
+  dag in5 = (foo foo:$a, bar:$b, foo:$c);
+  // CHECK: OtherBase misMatchType2 = ?;
+  OtherBase misMatchType2 = !getdagarg<OtherBase>(in5, 1);
+
+  dag in6 = (foo alice:$a, bob:$b);
+  // CHECK: Base base = bob;
+  Base base = !getdagarg<Base>(in6, 1);
 }