mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
[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
This commit is contained in:
parent
115a197e56
commit
9295b21984
@ -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 different
|
||||
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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -88,7 +88,7 @@ public:
|
||||
/// 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 @@ public:
|
||||
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 @@ public:
|
||||
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;
|
||||
|
||||
|
@ -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() + ")";
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
80
test/TableGen/interleave.td
Normal file
80
test/TableGen/interleave.td
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user