[Mlir-commits] [mlir] d56cd42 - [TableGen] Add !interleave operator to concatenate a list of values with delimiters

Paul C. Anagnostopoulos llvmlistbot at llvm.org
Wed Nov 4 06:25:04 PST 2020


Author: Paul C. Anagnostopoulos
Date: 2020-11-04T09:23:54-05:00
New Revision: d56cd4291e40c91fd04a36681ea053b794dc5e7b

URL: https://github.com/llvm/llvm-project/commit/d56cd4291e40c91fd04a36681ea053b794dc5e7b
DIFF: https://github.com/llvm/llvm-project/commit/d56cd4291e40c91fd04a36681ea053b794dc5e7b.diff

LOG: [TableGen] Add !interleave operator to concatenate a list of values with delimiters

Add a test. Use it in some TableGen files.

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

Added: 
    llvm/test/TableGen/interleave.td

Modified: 
    llvm/docs/TableGen/ProgRef.rst
    llvm/docs/TableGen/index.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/lib/Target/AMDGPU/MIMGInstructions.td
    llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
    mlir/include/mlir/IR/OpBase.td

Removed: 
    


################################################################################
diff  --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index c20ad45b94f8..4d2d44f10d68 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -215,13 +215,13 @@ TableGen provides "bang operators" that have a wide variety of uses:
 
 .. productionlist::
    BangOperator: one of
-               : !add        !and         !cast        !con         !dag
+               : !add        !and         !cast        !con         !dag 
                : !empty      !eq          !foldl       !foreach     !ge
-               : !getdagop   !gt          !head        !if          !isa
-               : !le         !listconcat  !listsplat   !lt          !mul
-               : !ne         !not         !or          !setdagop    !shl
-               : !size       !sra         !srl         !strconcat   !sub
-               : !subst      !tail        !xor
+               : !getdagop   !gt          !head        !if          !interleave
+               : !isa        !le          !listconcat  !listsplat   !lt
+               : !mul        !ne          !not         !or          !setdagop
+               : !shl        !size        !sra         !srl         !strconcat
+               : !sub        !subst       !tail        !xor
 
 The ``!cond`` operator has a slightly 
diff erent
 syntax compared to other bang operators, so it is defined separately:
@@ -1617,6 +1617,12 @@ and non-0 as true.
   ``int``. If the result is not 0, the *then* expression is produced; otherwise
   the *else* expression is produced.
 
+``!interleave(``\ *list*\ ``,`` *delim*\ ``)``
+    This operator concatenates the items in the *list*, interleaving the
+    *delim* string between each pair, and produces the resulting string.
+    The list can be a list of string, int, bits, or bit. An empty list
+    results in an empty string. The delimiter can be the empty string.
+
 ``!isa<``\ *type*\ ``>(``\ *a*\ ``)``
     This operator produces 1 if the type of *a* is a subtype of the given *type*; 0
     otherwise.

diff  --git a/llvm/docs/TableGen/index.rst b/llvm/docs/TableGen/index.rst
index 6d06937f3599..312a0264e6a1 100644
--- a/llvm/docs/TableGen/index.rst
+++ b/llvm/docs/TableGen/index.rst
@@ -25,7 +25,7 @@ it easier to structure domain specific information.
 The TableGen front end parses a file, instantiates the declarations, and
 hands the result off to a domain-specific `backend`_ for processing.  See
 the :doc:`TableGen Programmer's Reference <./ProgRef>` for an in-depth
-description of TableGen. See :doc:`xxx-tblgen: Target Description to C++
+description of TableGen. See :doc:`xxx-tblgen - Target Description to C++
 Code <../CommandGuide/tblgen>` for details on the various
 ``xxx-tblgen`` commands that invoke TableGen.
 

diff  --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index 50f20c7fe814..7dae9e46a9a4 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -88,7 +88,7 @@ class RecTy {
   /// a bit set is not an int, but they are convertible.
   virtual bool typeIsA(const RecTy *RHS) const;
 
-  /// Returns the type representing list<this>.
+  /// Returns the type representing list<thistype>.
   ListRecTy *getListTy();
 };
 
@@ -809,8 +809,8 @@ class UnOpInit : public OpInit, public FoldingSetNode {
 class BinOpInit : public OpInit, public FoldingSetNode {
 public:
   enum BinaryOp : uint8_t { ADD, SUB, MUL, AND, OR, XOR, SHL, SRA, SRL, LISTCONCAT,
-                            LISTSPLAT, STRCONCAT, CONCAT, EQ, NE, LE, LT, GE,
-                            GT, SETDAGOP };
+                            LISTSPLAT, STRCONCAT, INTERLEAVE, CONCAT, EQ,
+                            NE, LE, LT, GE, GT, SETDAGOP };
 
 private:
   Init *LHS, *RHS;
@@ -830,7 +830,6 @@ class BinOpInit : public OpInit, public FoldingSetNode {
                         RecTy *Type);
   static Init *getStrConcat(Init *lhs, Init *rhs);
   static Init *getListConcat(TypedInit *lhs, Init *rhs);
-  static Init *getListSplat(TypedInit *lhs, Init *rhs);
 
   void Profile(FoldingSetNodeID &ID) const;
 

diff  --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index 4d39c02e20a1..b86b5e9bf508 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -878,6 +878,34 @@ static StringInit *ConcatStringInits(const StringInit *I0,
   return StringInit::get(Concat);
 }
 
+static StringInit *interleaveStringList(const ListInit *List,
+                                        const StringInit *Delim) {
+  if (List->size() == 0)
+    return StringInit::get("");
+  SmallString<80> Result(dyn_cast<StringInit>(List->getElement(0))->getValue());
+  
+  for (unsigned I = 1, E = List->size(); I < E; ++I) {
+    Result.append(Delim->getValue());
+    Result.append(dyn_cast<StringInit>(List->getElement(I))->getValue());
+  }
+  return StringInit::get(Result);
+}
+
+static StringInit *interleaveIntList(const ListInit *List,
+                                     const StringInit *Delim) {
+  if (List->size() == 0)
+    return StringInit::get("");
+  SmallString<80> Result(dyn_cast<IntInit>(List->getElement(0)->
+                             getCastTo(IntRecTy::get()))->getAsString());
+  
+  for (unsigned I = 1, E = List->size(); I < E; ++I) {
+    Result.append(Delim->getValue());
+    Result.append(dyn_cast<IntInit>(List->getElement(I)->
+                      getCastTo(IntRecTy::get()))->getAsString());
+  }
+  return StringInit::get(Result);
+}
+
 Init *BinOpInit::getStrConcat(Init *I0, Init *I1) {
   // Shortcut for the common case of concatenating two strings.
   if (const StringInit *I0s = dyn_cast<StringInit>(I0))
@@ -904,10 +932,6 @@ Init *BinOpInit::getListConcat(TypedInit *LHS, Init *RHS) {
    return BinOpInit::get(BinOpInit::LISTCONCAT, LHS, RHS, LHS->getType());
 }
 
-Init *BinOpInit::getListSplat(TypedInit *LHS, Init *RHS) {
-  return BinOpInit::get(BinOpInit::LISTSPLAT, LHS, RHS, LHS->getType());
-}
-
 Init *BinOpInit::Fold(Record *CurRec) const {
   switch (getOpcode()) {
   case CONCAT: {
@@ -969,6 +993,17 @@ Init *BinOpInit::Fold(Record *CurRec) const {
       return ConcatStringInits(LHSs, RHSs);
     break;
   }
+  case INTERLEAVE: {
+    ListInit *List = dyn_cast<ListInit>(LHS);
+    StringInit *Delim = dyn_cast<StringInit>(RHS);
+    if (List && Delim) {
+      if (isa<StringRecTy>(List->getElementType()))
+        return interleaveStringList(List, Delim);
+      else
+        return interleaveIntList(List, Delim);
+    }
+    break;
+  }
   case EQ:
   case NE:
   case LE:
@@ -1091,6 +1126,7 @@ std::string BinOpInit::getAsString() const {
   case LISTCONCAT: Result = "!listconcat"; break;
   case LISTSPLAT: Result = "!listsplat"; break;
   case STRCONCAT: Result = "!strconcat"; break;
+  case INTERLEAVE: Result = "!interleave"; break;
   case SETDAGOP: Result = "!setdagop"; break;
   }
   return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")";

diff  --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp
index 9533624a0ae8..7693f82ba7cc 100644
--- a/llvm/lib/TableGen/TGLexer.cpp
+++ b/llvm/lib/TableGen/TGLexer.cpp
@@ -579,6 +579,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
     .Case("listconcat", tgtok::XListConcat)
     .Case("listsplat", tgtok::XListSplat)
     .Case("strconcat", tgtok::XStrConcat)
+    .Case("interleave", tgtok::XInterleave)
     .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated.
     .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated.
     .Default(tgtok::Error);

diff  --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h
index 405592d6554d..eaba640061fd 100644
--- a/llvm/lib/TableGen/TGLexer.h
+++ b/llvm/lib/TableGen/TGLexer.h
@@ -52,9 +52,9 @@ namespace tgtok {
 
     // !keywords.
     XConcat, XADD, XSUB, XMUL, XNOT, XAND, XOR, XXOR, XSRA, XSRL, XSHL,
-    XListConcat, XListSplat, XStrConcat, XCast, XSubst, XForEach, XFoldl,
-    XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe, XLe,
-    XLt, XGe, XGt, XSetDagOp, XGetDagOp,
+    XListConcat, XListSplat, XStrConcat, XInterleave, XCast, XSubst, XForEach,
+    XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe,
+    XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp,
 
     // Integer value.
     IntVal,

diff  --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index a210351c1996..4f64f4be5401 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -906,7 +906,7 @@ Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc,
 Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
   switch (Lex.getCode()) {
   default:
-    TokError("unknown operation");
+    TokError("unknown bang operator");
     return nullptr;
   case tgtok::XNOT:
   case tgtok::XHead:
@@ -1092,6 +1092,7 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
   case tgtok::XListConcat:
   case tgtok::XListSplat:
   case tgtok::XStrConcat:
+  case tgtok::XInterleave:
   case tgtok::XSetDagOp: { // Value ::= !binop '(' Value ',' Value ')'
     tgtok::TokKind OpTok = Lex.getCode();
     SMLoc OpLoc = Lex.getLoc();
@@ -1119,6 +1120,7 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
     case tgtok::XListConcat: Code = BinOpInit::LISTCONCAT; break;
     case tgtok::XListSplat:  Code = BinOpInit::LISTSPLAT; break;
     case tgtok::XStrConcat:  Code = BinOpInit::STRCONCAT; break;
+    case tgtok::XInterleave: Code = BinOpInit::INTERLEAVE; break;
     case tgtok::XSetDagOp:   Code = BinOpInit::SETDAGOP; break;
     }
 
@@ -1167,6 +1169,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
       Type = StringRecTy::get();
       ArgType = StringRecTy::get();
       break;
+    case tgtok::XInterleave:
+      Type = StringRecTy::get();
+      // The first argument type is not yet known.
     }
 
     if (Type && ItemType && !Type->typeIsConvertibleTo(ItemType)) {
@@ -1183,6 +1188,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
 
     SmallVector<Init*, 2> InitList;
 
+    // Note that this loop consumes an arbitrary number of arguments.
+    // The actual count is checked later.
     for (;;) {
       SMLoc InitLoc = Lex.getLoc();
       InitList.push_back(ParseValue(CurRec, ArgType));
@@ -1195,7 +1202,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
         return nullptr;
       }
       RecTy *ListType = InitListBack->getType();
+
       if (!ArgType) {
+        // Argument type must be determined from the argument itself.
         ArgType = ListType;
 
         switch (Code) {
@@ -1242,9 +1251,34 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
             return nullptr;
           }
           break;
+        case BinOpInit::INTERLEAVE:
+          switch (InitList.size()) {
+          case 1: // First argument must be a list of strings or integers.
+            if (ArgType != StringRecTy::get()->getListTy() &&
+                !ArgType->typeIsConvertibleTo(IntRecTy::get()->getListTy())) {
+              Error(InitLoc, Twine("expected list of string, int, bits, or bit; "
+                                   "got value of type '") +
+                                   ArgType->getAsString() + "'");
+              return nullptr;
+            }
+            break;
+          case 2: // Second argument must be a string.
+            if (!isa<StringRecTy>(ArgType)) {
+              Error(InitLoc, Twine("expected second argument to be a string, "
+                                   "got value of type '") +
+                                 ArgType->getAsString() + "'");
+              return nullptr;
+            }
+            break;
+          default: ;
+          }
+          ArgType = nullptr; // Broken invariant: types not identical.
+          break;
         default: llvm_unreachable("other ops have fixed argument types");
         }
+
       } else {
+        // Desired argument type is a known and in ArgType.
         RecTy *Resolved = resolveTypes(ArgType, ListType);
         if (!Resolved) {
           Error(InitLoc, Twine("expected value of type '") +
@@ -2117,6 +2151,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
   case tgtok::XListConcat:
   case tgtok::XListSplat:
   case tgtok::XStrConcat:
+  case tgtok::XInterleave:
   case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')'
   case tgtok::XIf:
   case tgtok::XCond:

diff  --git a/llvm/lib/Target/AMDGPU/MIMGInstructions.td b/llvm/lib/Target/AMDGPU/MIMGInstructions.td
index e9ee87283553..4d04940232c0 100644
--- a/llvm/lib/Target/AMDGPU/MIMGInstructions.td
+++ b/llvm/lib/Target/AMDGPU/MIMGInstructions.td
@@ -186,8 +186,7 @@ class MIMGNSAHelper<int num_addrs> {
     !foldl([]<string>, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], lhs, i,
            !if(!lt(i, num_addrs), !listconcat(lhs, ["vaddr"#!size(lhs)]), lhs));
   dag AddrIns = !dag(ins, !foreach(arg, AddrAsmNames, VGPR_32), AddrAsmNames);
-  string AddrAsm = "[" # !foldl("$" # !head(AddrAsmNames), !tail(AddrAsmNames), lhs, rhs,
-                                lhs # ", $" # rhs) # "]";
+  string AddrAsm = "[$" # !interleave(AddrAsmNames, ", $") # "]";
 
   int NSA = !if(!le(num_addrs, 1), ?,
             !if(!le(num_addrs, 5), 1,

diff  --git a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
index 76a4a1d4030a..ec2e359358f7 100644
--- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
+++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
@@ -7367,10 +7367,7 @@ class WMMA_REGINFO<WMMA_REGS r>
   list<string> reg_names = RegSeq<!size(ptx_regs), "r"#frag>.ret;
 
   // Generates "{{$r0, $r1,.... $rN-1}}" for use in asm string construction.
-  string regstring = "{{$" # !head(reg_names)
-                           # !foldl("", !tail(reg_names), a, b,
-                                    !strconcat(a, ", $", b))
-                     # "}}";
+  string regstring = "{{$" # !interleave(reg_names, ", $") # "}}";
 
   // Predicates for particular fragment variant. Technically those are
   // per-instruction predicates, but currently all fragments that can be used in

diff  --git a/llvm/test/TableGen/interleave.td b/llvm/test/TableGen/interleave.td
new file mode 100644
index 000000000000..7d6bfee15a56
--- /dev/null
+++ b/llvm/test/TableGen/interleave.td
@@ -0,0 +1,80 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
+
+defvar EmptyList = []<string>;
+defvar OneList = ["hello"];
+defvar StringList = ["foo", "bar", "zoo", "snork", "quux"];
+defvar IntList = [0, 1, 2, 3, 4, 5, 6, 7];
+defvar BitsList = [ {0, 1, 0}, {1, 1, 1}, {0, 0, 1} ];
+defvar BitList = [0, 1, 1, 0, 1]<bit>;
+
+class Ishify<list<string> words> {
+  list<string> ret = !foreach(w, words, w # "ify");
+}
+
+// CHECK: def Rec1
+// CHECK:   Test1 = "";
+// CHECK:   Test2 = "hello";
+// CHECK:   Test3 = "foobarzoosnorkquux";
+// CHECK:   Test4 = "foo, bar, zoo, snork, quux";
+// CHECK:   Test5 = "foo & bar & zoo & snork & quux & grits";
+
+def Rec1 {
+  string Test1 = !interleave(EmptyList, "/");
+  string Test2 = !interleave(OneList, ":");
+  string Test3 = !interleave(StringList, "");
+  string Test4 = !interleave(StringList, ", ");
+  string Test5 = !interleave(!listconcat(StringList, ["grits"]), " & ");
+}
+
+// CHECK: def Rec2
+// CHECK: Test1 = "01234567";
+// CHECK: Test2 = "0, 1, 2, 3, 4, 5, 6, 7";
+// CHECK: Test3 = "0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 42";
+
+def Rec2 {
+  string Test1 = !interleave(IntList, "");
+  string Test2 = !interleave(IntList, ", ");
+  string Test3 = !interleave(!listconcat(IntList, [42]), " & ");
+}
+
+// CHECK: def Rec3
+// CHECK: Test1 = "271";
+// CHECK: Test2 = "2, 7, 1";
+// CHECK: Test3 = "2 & 7 & 1 & 0";
+
+def Rec3 {
+  string Test1 = !interleave(BitsList, "");
+  string Test2 = !interleave(BitsList, ", ");
+  string Test3 = !interleave(!listconcat(BitsList, [ {0, 0, 0} ]), " & ");
+}
+
+// CHECK: def Rec4
+// CHECK: Test1 = "01101";
+// CHECK: Test2 = "0, 1, 1, 0, 1";
+// CHECK: Test3 = "0 and 1 and 1 and 0 and 1 and 1";
+
+def Rec4 {
+  string Test1 = !interleave(BitList, "");
+  string Test2 = !interleave(BitList, ", ");
+  string Test3 = !interleave(!listconcat(BitList, [1]), " and ");
+}
+
+// CHECK: def Rec5
+// CHECK: Colors = ["red", "green", "yellow"];
+// CHECK: ColorList = "redify, greenify, yellowify";
+
+def Rec5 {
+  list<string> Colors = ["red", "green", "yellow"];
+  string ColorList = !interleave(Ishify<Colors>.ret, ", ");
+}
+
+#ifdef ERROR1
+def op;
+
+// ERROR1: expected list of string, int, bits, or bit; got value of type
+
+def Rec6 {
+  string Bad = !interleave([(op), (op "hello")], " = ");
+}
+#endif

diff  --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td
index 11daa8dafdcf..814b8573d8b3 100644
--- a/mlir/include/mlir/IR/OpBase.td
+++ b/mlir/include/mlir/IR/OpBase.td
@@ -38,14 +38,15 @@ class StrFunc<string r> {
   string result = r;
 }
 
+// TODO: Use !interleave() directly rather than through StrJoin/StrJoinInt.
+
 // Concatenates a list of strings with a separator (default ", ")
 class StrJoin<list<string> strings, string sep = ", "> :
-    StrFunc<!if(!empty(strings), "",
-         !foldl(!head(strings), !tail(strings), prev, cur, prev # sep # cur))>;
+    StrFunc<!interleave(strings, sep)>;
 
 // Concatenates a list of integers into a string with a separator (default ", ")
 class StrJoinInt<list<int> integers, string sep = ", "> :
-    StrJoin<!foreach(i, integers, !cast<string>(i)), sep>;
+    StrFunc<!interleave(integers, sep)>;
 
 //===----------------------------------------------------------------------===//
 // Predicate definitions
@@ -1585,8 +1586,7 @@ class Confined<Attr attr, list<AttrConstraint> constraints> : Attr<
 class AllAttrConstraintsOf<list<AttrConstraint> constraints> : AttrConstraint<
     And<!listconcat([!head(constraints).predicate],
                       !foreach(pred, !tail(constraints), pred.predicate))>,
-    !foldl(/*init*/!head(constraints).description, /*list*/!tail(constraints),
-           prev, cur, prev # " and " # cur.description)> {
+    !interleave(!foreach(con, constraints, con.description), " and ")> {
 }
 
 class IntMinValue<int n> : AttrConstraint<


        


More information about the Mlir-commits mailing list