More than two arguments are accepted with the result being the concatenation
of all the lists given.
+``!listsplat(a, size)``
+ A list value that contains the value ``a`` ``size`` times.
+ Example: ``!listsplat(0, 2)`` results in ``[0, 0]``.
+
``!strconcat(a, b, ...)``
A string value that is the result of concatenating the 'a' and 'b' strings.
More than two arguments are accepted with the result being the concatenation
:!or !empty !subst !foreach !strconcat
:!cast !listconcat !size !foldl
:!isa !dag !le !lt !ge
- :!gt !ne !mul
+ :!gt !ne !mul !listsplat
TableGen also has !cond operator that needs a slightly different
syntax compared to other "bang operators":
class BinOpInit : public OpInit, public FoldingSetNode {
public:
enum BinaryOp : uint8_t { ADD, MUL, AND, OR, SHL, SRA, SRL, LISTCONCAT,
- STRCONCAT, CONCAT, EQ, NE, LE, LT, GE, GT };
+ LISTSPLAT, STRCONCAT, CONCAT, EQ, NE, LE, LT, GE,
+ GT };
private:
Init *LHS, *RHS;
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;
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: {
}
break;
}
+ case LISTSPLAT: {
+ TypedInit *Value = dyn_cast<TypedInit>(LHS);
+ IntInit *Size = dyn_cast<IntInit>(RHS);
+ if (Value && Size) {
+ SmallVector<Init *, 8> Args(Size->getValue(), Value);
+ return ListInit::get(Args, Value->getType());
+ }
+ break;
+ }
case STRCONCAT: {
StringInit *LHSs = dyn_cast<StringInit>(LHS);
StringInit *RHSs = dyn_cast<StringInit>(RHS);
case GE: Result = "!ge"; break;
case GT: Result = "!gt"; break;
case LISTCONCAT: Result = "!listconcat"; break;
+ case LISTSPLAT: Result = "!listsplat"; break;
case STRCONCAT: Result = "!strconcat"; break;
}
return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")";
.Case("foldl", tgtok::XFoldl)
.Case("foreach", tgtok::XForEach)
.Case("listconcat", tgtok::XListConcat)
+ .Case("listsplat", tgtok::XListSplat)
.Case("strconcat", tgtok::XStrConcat)
.Default(tgtok::Error);
MultiClass, String, Defset,
// !keywords.
- XConcat, XADD, XMUL, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat,
- XCast, XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond,
- XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt,
+ XConcat, XADD, XMUL, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XListSplat,
+ XStrConcat, XCast, XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty,
+ XIf, XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt,
// Integer value.
IntVal,
case tgtok::XGe:
case tgtok::XGt:
case tgtok::XListConcat:
+ case tgtok::XListSplat:
case tgtok::XStrConcat: { // Value ::= !binop '(' Value ',' Value ')'
tgtok::TokKind OpTok = Lex.getCode();
SMLoc OpLoc = Lex.getLoc();
case tgtok::XGe: Code = BinOpInit::GE; break;
case tgtok::XGt: Code = BinOpInit::GT; break;
case tgtok::XListConcat: Code = BinOpInit::LISTCONCAT; break;
+ case tgtok::XListSplat: Code = BinOpInit::LISTSPLAT; break;
case tgtok::XStrConcat: Code = BinOpInit::STRCONCAT; break;
}
// We don't know the list type until we parse the first argument
ArgType = ItemType;
break;
+ case tgtok::XListSplat:
+ // Can't do any typechecking until we parse the first argument.
+ break;
case tgtok::XStrConcat:
Type = StringRecTy::get();
ArgType = StringRecTy::get();
return nullptr;
}
break;
+ case BinOpInit::LISTSPLAT:
+ if (ItemType && InitList.size() == 1) {
+ if (!isa<ListRecTy>(ItemType)) {
+ Error(OpLoc,
+ Twine("expected output type to be a list, got type '") +
+ ItemType->getAsString() + "'");
+ return nullptr;
+ }
+ if (!ArgType->getListTy()->typeIsConvertibleTo(ItemType)) {
+ Error(OpLoc, Twine("expected first arg type to be '") +
+ ArgType->getAsString() +
+ "', got value of type '" +
+ cast<ListRecTy>(ItemType)
+ ->getElementType()
+ ->getAsString() +
+ "'");
+ return nullptr;
+ }
+ }
+ if (InitList.size() == 2 && !isa<IntRecTy>(ArgType)) {
+ Error(InitLoc, Twine("expected second parameter to be an int, got "
+ "value of type '") +
+ ArgType->getAsString() + "'");
+ return nullptr;
+ }
+ ArgType = nullptr; // Broken invariant: types not identical.
+ break;
case BinOpInit::EQ:
case BinOpInit::NE:
if (!ArgType->typeIsConvertibleTo(IntRecTy::get()) &&
}
Lex.Lex(); // eat the ')'
+ // listconcat returns a list with type of the argument.
if (Code == BinOpInit::LISTCONCAT)
Type = ArgType;
+ // listsplat returns a list of type of the *first* argument.
+ if (Code == BinOpInit::LISTSPLAT)
+ Type = cast<TypedInit>(InitList.front())->getType()->getListTy();
// We allow multiple operands to associative operators like !strconcat as
// shorthand for nesting them.
/// SimpleValue ::= SRATOK '(' Value ',' Value ')'
/// SimpleValue ::= SRLTOK '(' Value ',' Value ')'
/// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')'
+/// SimpleValue ::= LISTSPLATTOK '(' Value ',' Value ')'
/// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')'
/// SimpleValue ::= COND '(' [Value ':' Value,]+ ')'
///
case tgtok::XGe:
case tgtok::XGt:
case tgtok::XListConcat:
+ case tgtok::XListSplat:
case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')'
case tgtok::XIf:
case tgtok::XCond:
--- /dev/null
+// RUN: llvm-tblgen %s | FileCheck %s
+
+// CHECK: ------------- Classes -----------------
+// CHECK-NEXT: class X<int X:a = ?, int X:b = ?> {
+// CHECK-NEXT: list<int> x = !listsplat(X:a, X:b);
+// CHECK-NEXT: }
+// CHECK-NEXT: class Y<string Y:a = ?, int Y:b = ?> {
+// CHECK-NEXT: list<string> x = !listsplat(Y:a, Y:b);
+// CHECK-NEXT: }
+// CHECK-NEXT: ------------- Defs -----------------
+// CHECK-NEXT: def DX00 { // X
+// CHECK-NEXT: list<int> x = [];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DX01 { // X
+// CHECK-NEXT: list<int> x = [0];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DX02 { // X
+// CHECK-NEXT: list<int> x = [0, 0];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DX10 { // X
+// CHECK-NEXT: list<int> x = [];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DX11 { // X
+// CHECK-NEXT: list<int> x = [1];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DX12 { // X
+// CHECK-NEXT: list<int> x = [1, 1];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DYa0 { // Y
+// CHECK-NEXT: list<string> x = [];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DYa1 { // Y
+// CHECK-NEXT: list<string> x = ["a"];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DYa2 { // Y
+// CHECK-NEXT: list<string> x = ["a", "a"];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DYe0 { // Y
+// CHECK-NEXT: list<string> x = [];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DYe1 { // Y
+// CHECK-NEXT: list<string> x = [""];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DYe2 { // Y
+// CHECK-NEXT: list<string> x = ["", ""];
+// CHECK-NEXT: }
+// CHECK-NEXT: def DZ { // X
+// CHECK-NEXT: list<int> x = [42, 42, 42];
+// CHECK-NEXT: }
+
+class X<int a, int b> {
+ list<int> x = !listsplat(a, b);
+}
+
+class Y<string a, int b> {
+ list<string> x = !listsplat(a, b);
+}
+
+def DX00 : X<0, 0>;
+def DX01 : X<0, 1>;
+def DX02 : X<0, 2>;
+
+def DX10 : X<1, 0>;
+def DX11 : X<1, 1>;
+def DX12 : X<1, 2>;
+
+def DYe0 : Y<"", 0>;
+def DYe1 : Y<"", 1>;
+def DYe2 : Y<"", 2>;
+
+def DYa0 : Y<"a", 0>;
+def DYa1 : Y<"a", 1>;
+def DYa2 : Y<"a", 2>;
+
+def DZ : X<42, !size([1, 2, 3])>;
<item> !strconcat </item>
<item> !cast </item>
<item> !listconcat </item>
+ <item> !listsplat </item>
<item> !size </item>
<item> !foldl </item>
<item> !isa </item>