Index: llvm/docs/CommandGuide/tblgen.rst =================================================================== --- llvm/docs/CommandGuide/tblgen.rst +++ llvm/docs/CommandGuide/tblgen.rst @@ -260,7 +260,8 @@ .. option:: -gen-searchable-tables - Generate generic searchable tables. See :doc:`TableGen BackEnds <./BackEnds>` + Generate generic searchable tables. See + :doc:`TableGen BackEnds <../TableGen/BackEnds>` for a detailed description. .. option:: -gen-subtarget Index: llvm/docs/TableGen/ProgRef.rst =================================================================== --- llvm/docs/TableGen/ProgRef.rst +++ llvm/docs/TableGen/ProgRef.rst @@ -209,13 +209,13 @@ .. productionlist:: BangOperator: one of - : !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 + : !add !adjoin !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 The ``!cond`` operator has a slightly different syntax compared to other bang operators, so it is defined separately: @@ -1455,6 +1455,12 @@ ``!add(``\ *a*\ ``,`` *b*\ ``, ...)`` This operator adds *a*, *b*, etc., and produces the sum. +``!adjoin(``\ *list*\ ``,`` *delim*\ ``)`` + This operator concatenates the items in the *list* with 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. + ``!and(``\ *a*\ ``,`` *b*\ ``, ...)`` This operator does a bitwise AND on *a*, *b*, etc., and produces the result. A logical AND can be performed if all the arguments are either Index: llvm/docs/TableGen/index.rst =================================================================== --- llvm/docs/TableGen/index.rst +++ llvm/docs/TableGen/index.rst @@ -11,7 +11,6 @@ BackEnds BackGuide ProgRef - Deficiencies Introduction ============ @@ -26,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. Index: llvm/include/llvm/TableGen/Record.h =================================================================== --- llvm/include/llvm/TableGen/Record.h +++ 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,7 +809,7 @@ 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, + LISTSPLAT, STRCONCAT, ADJOIN, CONCAT, EQ, NE, LE, LT, GE, GT, SETDAGOP }; private: @@ -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; Index: llvm/lib/TableGen/Record.cpp =================================================================== --- llvm/lib/TableGen/Record.cpp +++ llvm/lib/TableGen/Record.cpp @@ -878,6 +878,34 @@ return StringInit::get(Concat); } +static StringInit *adjoinStringList(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 *adjoinIntList(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 ADJOIN: { + ListInit *List = dyn_cast(LHS); + StringInit *Delim = dyn_cast(RHS); + if (List && Delim) { + if (isa(List->getElementType())) + return adjoinStringList(List, Delim); + else + return adjoinIntList(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 ADJOIN: Result = "!adjoin"; break; case SETDAGOP: Result = "!setdagop"; break; } return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")"; Index: llvm/lib/TableGen/TGLexer.h =================================================================== --- llvm/lib/TableGen/TGLexer.h +++ 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, XAdjoin, XCast, XSubst, XForEach, + XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe, + XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp, // Integer value. IntVal, Index: llvm/lib/TableGen/TGLexer.cpp =================================================================== --- llvm/lib/TableGen/TGLexer.cpp +++ llvm/lib/TableGen/TGLexer.cpp @@ -579,6 +579,7 @@ .Case("listconcat", tgtok::XListConcat) .Case("listsplat", tgtok::XListSplat) .Case("strconcat", tgtok::XStrConcat) + .Case("adjoin", tgtok::XAdjoin) .Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated. .Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated. .Default(tgtok::Error); Index: llvm/lib/TableGen/TGParser.cpp =================================================================== --- llvm/lib/TableGen/TGParser.cpp +++ 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::XAdjoin: 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::XAdjoin: Code = BinOpInit::ADJOIN; break; case tgtok::XSetDagOp: Code = BinOpInit::SETDAGOP; break; } @@ -1167,6 +1169,9 @@ Type = StringRecTy::get(); ArgType = StringRecTy::get(); break; + case tgtok::XAdjoin: + 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::ADJOIN: + 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::XAdjoin: case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')' case tgtok::XIf: case tgtok::XCond: Index: llvm/lib/Target/AMDGPU/MIMGInstructions.td =================================================================== --- llvm/lib/Target/AMDGPU/MIMGInstructions.td +++ 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 = "[$" # !adjoin(AddrAsmNames, ", $") # "]"; int NSA = !if(!le(num_addrs, 1), ?, !if(!le(num_addrs, 5), 1, Index: llvm/lib/Target/NVPTX/NVPTXIntrinsics.td =================================================================== --- llvm/lib/Target/NVPTX/NVPTXIntrinsics.td +++ 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 = "{{$" # !adjoin(reg_names, ", $") # "}}"; // Predicates for particular fragment variant. Technically those are // per-instruction predicates, but currently all fragments that can be used in Index: llvm/test/TableGen/adjoin.td =================================================================== --- /dev/null +++ llvm/test/TableGen/adjoin.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 = !adjoin(EmptyList, "/"); + string Test2 = !adjoin(OneList, ":"); + string Test3 = !adjoin(StringList, ""); + string Test4 = !adjoin(StringList, ", "); + string Test5 = !adjoin(!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 = !adjoin(IntList, ""); + string Test2 = !adjoin(IntList, ", "); + string Test3 = !adjoin(!listconcat(IntList, [42]), " & "); +} + +// CHECK: def Rec3 +// CHECK: Test1 = "271"; +// CHECK: Test2 = "2, 7, 1"; +// CHECK: Test3 = "2 & 7 & 1 & 0"; + +def Rec3 { + string Test1 = !adjoin(BitsList, ""); + string Test2 = !adjoin(BitsList, ", "); + string Test3 = !adjoin(!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 = !adjoin(BitList, ""); + string Test2 = !adjoin(BitList, ", "); + string Test3 = !adjoin(!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 = !adjoin(Ishify.ret, ", "); +} + +#ifdef ERROR1 +def op; + +// ERROR1: expected list of string, int, bits, or bit; got value of type + +def Rec6 { + string Bad = !adjoin([(op), (op "hello")], " = "); +} +#endif Index: mlir/include/mlir/IR/OpBase.td =================================================================== --- mlir/include/mlir/IR/OpBase.td +++ mlir/include/mlir/IR/OpBase.td @@ -38,14 +38,15 @@ string result = r; } +// TODO: Use !adjoin() 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)> { + !adjoin(!foreach(con, constraints, con.description), " and ")> { } class IntMinValue : AttrConstraint<