1
0
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:
Paul C. Anagnostopoulos 2020-10-30 11:40:42 -04:00
parent 115a197e56
commit 9295b21984
10 changed files with 178 additions and 25 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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;

View File

@ -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() + ")";

View File

@ -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);

View File

@ -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,

View File

@ -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:

View File

@ -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,

View File

@ -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

View 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