[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