diff --git a/docs/TableGen/LangIntro.rst b/docs/TableGen/LangIntro.rst index 5b6094fa91f..3e74dffb00e 100644 --- a/docs/TableGen/LangIntro.rst +++ b/docs/TableGen/LangIntro.rst @@ -160,6 +160,12 @@ supported include: remaining elements in the list may be arbitrary other values, including nested ```dag``' values. +``!listconcat(a, b, ...)`` + A list value that is the result of concatenating the 'a' and 'b' lists. + The lists must have the same element type. + More than two arguments are accepted with the result being the concatenation + of all the lists given. + ``!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 diff --git a/docs/TableGen/LangRef.rst b/docs/TableGen/LangRef.rst index 781f789faaf..9b074be38dc 100644 --- a/docs/TableGen/LangRef.rst +++ b/docs/TableGen/LangRef.rst @@ -93,7 +93,7 @@ wide variety of meanings: BangOperator: one of :!eq !if !head !tail !con :!add !shl !sra !srl - :!cast !empty !subst !foreach !strconcat + :!cast !empty !subst !foreach !listconcat !strconcat Syntax ====== diff --git a/include/llvm/TableGen/Record.h b/include/llvm/TableGen/Record.h index 5b2a706ae17..e74a8f2d8ac 100644 --- a/include/llvm/TableGen/Record.h +++ b/include/llvm/TableGen/Record.h @@ -929,7 +929,8 @@ public: /// class BinOpInit : public OpInit { public: - enum BinaryOp { ADD, SHL, SRA, SRL, STRCONCAT, CONCAT, EQ }; + enum BinaryOp { ADD, SHL, SRA, SRL, LISTCONCAT, STRCONCAT, CONCAT, EQ }; + private: BinaryOp Opc; Init *LHS, *RHS; diff --git a/lib/TableGen/Record.cpp b/lib/TableGen/Record.cpp index 57d2703b82b..c17aea99f8e 100644 --- a/lib/TableGen/Record.cpp +++ b/lib/TableGen/Record.cpp @@ -918,6 +918,18 @@ Init *BinOpInit::Fold(Record *CurRec, MultiClass *CurMultiClass) const { } break; } + case LISTCONCAT: { + ListInit *LHSs = dyn_cast(LHS); + ListInit *RHSs = dyn_cast(RHS); + if (LHSs && RHSs) { + std::vector Args; + Args.insert(Args.end(), LHSs->begin(), LHSs->end()); + Args.insert(Args.end(), RHSs->begin(), RHSs->end()); + return ListInit::get( + Args, static_cast(LHSs->getType())->getElementType()); + } + break; + } case STRCONCAT: { StringInit *LHSs = dyn_cast(LHS); StringInit *RHSs = dyn_cast(RHS); @@ -987,6 +999,7 @@ std::string BinOpInit::getAsString() const { case SRA: Result = "!sra"; break; case SRL: Result = "!srl"; break; case EQ: Result = "!eq"; break; + case LISTCONCAT: Result = "!listconcat"; break; case STRCONCAT: Result = "!strconcat"; break; } return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")"; diff --git a/lib/TableGen/TGLexer.cpp b/lib/TableGen/TGLexer.cpp index 57f603183ba..1ec2eea67d9 100644 --- a/lib/TableGen/TGLexer.cpp +++ b/lib/TableGen/TGLexer.cpp @@ -478,6 +478,7 @@ tgtok::TokKind TGLexer::LexExclaim() { .Case("empty", tgtok::XEmpty) .Case("subst", tgtok::XSubst) .Case("foreach", tgtok::XForEach) + .Case("listconcat", tgtok::XListConcat) .Case("strconcat", tgtok::XStrConcat) .Default(tgtok::Error); diff --git a/lib/TableGen/TGLexer.h b/lib/TableGen/TGLexer.h index d1bd70d2eca..1e599f84b06 100644 --- a/lib/TableGen/TGLexer.h +++ b/lib/TableGen/TGLexer.h @@ -47,7 +47,7 @@ namespace tgtok { MultiClass, String, // !keywords. - XConcat, XADD, XSRA, XSRL, XSHL, XStrConcat, XCast, XSubst, + XConcat, XADD, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast, XSubst, XForEach, XHead, XTail, XEmpty, XIf, XEq, // Integer value. diff --git a/lib/TableGen/TGParser.cpp b/lib/TableGen/TGParser.cpp index dd3a0967d14..038e0180fbb 100644 --- a/lib/TableGen/TGParser.cpp +++ b/lib/TableGen/TGParser.cpp @@ -903,6 +903,7 @@ Init *TGParser::ParseOperation(Record *CurRec) { case tgtok::XSRL: case tgtok::XSHL: case tgtok::XEq: + case tgtok::XListConcat: case tgtok::XStrConcat: { // Value ::= !binop '(' Value ',' Value ')' tgtok::TokKind OpTok = Lex.getCode(); SMLoc OpLoc = Lex.getLoc(); @@ -919,6 +920,10 @@ Init *TGParser::ParseOperation(Record *CurRec) { case tgtok::XSRL: Code = BinOpInit::SRL; Type = IntRecTy::get(); break; case tgtok::XSHL: Code = BinOpInit::SHL; Type = IntRecTy::get(); break; case tgtok::XEq: Code = BinOpInit::EQ; Type = BitRecTy::get(); break; + case tgtok::XListConcat: + Code = BinOpInit::LISTCONCAT; + // We don't know the list type until we parse the first argument + break; case tgtok::XStrConcat: Code = BinOpInit::STRCONCAT; Type = StringRecTy::get(); @@ -949,9 +954,22 @@ Init *TGParser::ParseOperation(Record *CurRec) { } Lex.Lex(); // eat the ')' + // If we are doing !listconcat, we should know the type by now + if (OpTok == tgtok::XListConcat) { + if (VarInit *Arg0 = dyn_cast(InitList[0])) + Type = Arg0->getType(); + else if (ListInit *Arg0 = dyn_cast(InitList[0])) + Type = Arg0->getType(); + else { + InitList[0]->dump(); + Error(OpLoc, "expected a list"); + return nullptr; + } + } + // We allow multiple operands to associative operators like !strconcat as // shorthand for nesting them. - if (Code == BinOpInit::STRCONCAT) { + if (Code == BinOpInit::STRCONCAT || Code == BinOpInit::LISTCONCAT) { while (InitList.size() > 2) { Init *RHS = InitList.pop_back_val(); RHS = (BinOpInit::get(Code, InitList.back(), RHS, Type)) @@ -1134,6 +1152,7 @@ RecTy *TGParser::ParseOperatorType() { /// SimpleValue ::= SHLTOK '(' Value ',' Value ')' /// SimpleValue ::= SRATOK '(' Value ',' Value ')' /// SimpleValue ::= SRLTOK '(' Value ',' Value ')' +/// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')' /// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')' /// Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, @@ -1417,6 +1436,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, case tgtok::XSRL: case tgtok::XSHL: case tgtok::XEq: + case tgtok::XListConcat: case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')' case tgtok::XIf: case tgtok::XForEach: diff --git a/test/TableGen/listconcat.td b/test/TableGen/listconcat.td new file mode 100644 index 00000000000..870e649d41d --- /dev/null +++ b/test/TableGen/listconcat.td @@ -0,0 +1,18 @@ +// RUN: llvm-tblgen %s | FileCheck %s + +// CHECK: class Y Y:S = ?> { +// CHECK: list T1 = !listconcat(Y:S, ["foo"]); +// CHECK: list T2 = !listconcat(Y:S, !listconcat(["foo"], !listconcat(Y:S, ["bar", "baz"]))); +// CHECK: } + +// CHECK: def Z { +// CHECK: list T1 = ["fu", "foo"]; +// CHECK: list T2 = ["fu", "foo", "fu", "bar", "baz"]; +// CHECK: } + +class Y S> { + list T1 = !listconcat(S, ["foo"]); + list T2 = !listconcat(S, ["foo"], S, ["bar", "baz"]); +} + +def Z : Y<["fu"]>;