diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -215,13 +215,13 @@ .. 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 different syntax compared to other bang operators, so it is defined separately: @@ -1617,6 +1617,12 @@ ``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 --- a/llvm/docs/TableGen/index.rst +++ b/llvm/docs/TableGen/index.rst @@ -25,7 +25,7 @@ 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 --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -88,7 +88,7 @@ /// a bit set is not an int, but they are convertible. virtual bool typeIsA(const RecTy *RHS) const; - /// Returns the type representing list. + /// Returns the type representing list. ListRecTy *getListTy(); }; @@ -809,8 +809,8 @@ 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 @@ 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 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -878,6 +878,34 @@ 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(List->getElement(0))->getValue()); + + for (unsigned I = 1, E = List->size(); I < E; ++I) { + Result.append(Delim->getValue()); + Result.append(dyn_cast(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(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(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(I0)) @@ -904,10 +932,6 @@ 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 @@ return ConcatStringInits(LHSs, RHSs); break; } + case INTERLEAVE: { + ListInit *List = dyn_cast(LHS); + StringInit *Delim = dyn_cast(RHS); + if (List && Delim) { + if (isa(List->getElementType())) + return interleaveStringList(List, Delim); + else + return interleaveIntList(List, Delim); + } + break; + } case EQ: case NE: case LE: @@ -1091,6 +1126,7 @@ 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.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -52,9 +52,9 @@ // !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/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -579,6 +579,7 @@ .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/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -906,7 +906,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ SmallVector 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 @@ 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 @@ 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(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 @@ 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 --- a/llvm/lib/Target/AMDGPU/MIMGInstructions.td +++ b/llvm/lib/Target/AMDGPU/MIMGInstructions.td @@ -186,8 +186,7 @@ !foldl([], [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 --- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td +++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td @@ -7367,10 +7367,7 @@ list reg_names = RegSeq.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 --- /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 = []; +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]; + +class Ishify words> { + list 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 Colors = ["red", "green", "yellow"]; + string ColorList = !interleave(Ishify.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 --- a/mlir/include/mlir/IR/OpBase.td +++ b/mlir/include/mlir/IR/OpBase.td @@ -38,14 +38,15 @@ string result = r; } +// TODO: Use !interleave() directly rather than through StrJoin/StrJoinInt. + // Concatenates a list of strings with a separator (default ", ") class StrJoin strings, string sep = ", "> : - StrFunc; + StrFunc; // Concatenates a list of integers into a string with a separator (default ", ") class StrJoinInt integers, string sep = ", "> : - StrJoin(i)), sep>; + StrFunc; //===----------------------------------------------------------------------===// // Predicate definitions @@ -1585,8 +1586,7 @@ class AllAttrConstraintsOf constraints> : AttrConstraint< And, - !foldl(/*init*/!head(constraints).description, /*list*/!tail(constraints), - prev, cur, prev # " and " # cur.description)> { + !interleave(!foreach(con, constraints, con.description), " and ")> { } class IntMinValue : AttrConstraint<