summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/TableGen/LangIntro.rst9
-rw-r--r--docs/TableGen/LangRef.rst2
-rw-r--r--include/llvm/TableGen/Record.h38
-rw-r--r--lib/TableGen/Record.cpp71
-rw-r--r--lib/TableGen/TGLexer.cpp1
-rw-r--r--lib/TableGen/TGLexer.h2
-rw-r--r--lib/TableGen/TGParser.cpp122
-rw-r--r--test/TableGen/foldl.td71
8 files changed, 312 insertions, 4 deletions
diff --git a/docs/TableGen/LangIntro.rst b/docs/TableGen/LangIntro.rst
index 7437723bf3b..67000fa03c6 100644
--- a/docs/TableGen/LangIntro.rst
+++ b/docs/TableGen/LangIntro.rst
@@ -217,6 +217,15 @@ supported include:
of a variable that will be substituted by members of 'b' in 'c'.
This operation is analogous to $(foreach) in GNU make.
+``!foldl(start, lst, a, b, expr)``
+ Perform a left-fold over 'lst' with the given starting value. 'a' and 'b'
+ are variable names which will be substituted in 'expr'. If you think of
+ expr as a function f(a,b), the fold will compute
+ 'f(...f(f(start, lst[0]), lst[1]), ...), lst[n-1])' for a list of length n.
+ As usual, 'a' will be of the type of 'start', and 'b' will be of the type
+ of elements of 'lst'. These types need not be the same, but 'expr' must be
+ of the same type as 'start'.
+
``!head(a)``
The first element of list 'a.'
diff --git a/docs/TableGen/LangRef.rst b/docs/TableGen/LangRef.rst
index 5f7917a5b4b..5da852879d2 100644
--- a/docs/TableGen/LangRef.rst
+++ b/docs/TableGen/LangRef.rst
@@ -98,7 +98,7 @@ wide variety of meanings:
:!eq !if !head !tail !con
:!add !shl !sra !srl !and
:!or !empty !subst !foreach !strconcat
- :!cast !listconcat !size
+ :!cast !listconcat !size !foldl
Syntax
diff --git a/include/llvm/TableGen/Record.h b/include/llvm/TableGen/Record.h
index ec8d2fcc92a..e10b0bfb64e 100644
--- a/include/llvm/TableGen/Record.h
+++ b/include/llvm/TableGen/Record.h
@@ -316,6 +316,7 @@ protected:
IK_TernOpInit,
IK_UnOpInit,
IK_LastOpInit,
+ IK_FoldOpInit,
IK_StringInit,
IK_VarInit,
IK_VarListElementInit,
@@ -913,6 +914,43 @@ public:
std::string getAsString() const override;
};
+/// !foldl (a, b, expr, start, lst) - Fold over a list.
+class FoldOpInit : public TypedInit, public FoldingSetNode {
+private:
+ Init *Start;
+ Init *List;
+ Init *A;
+ Init *B;
+ Init *Expr;
+
+ FoldOpInit(Init *Start, Init *List, Init *A, Init *B, Init *Expr, RecTy *Type)
+ : TypedInit(IK_FoldOpInit, Type), Start(Start), List(List), A(A), B(B),
+ Expr(Expr) {}
+
+public:
+ FoldOpInit(const FoldOpInit &) = delete;
+ FoldOpInit &operator=(const FoldOpInit &) = delete;
+
+ static bool classof(const Init *I) { return I->getKind() == IK_FoldOpInit; }
+
+ static FoldOpInit *get(Init *Start, Init *List, Init *A, Init *B, Init *Expr,
+ RecTy *Type);
+
+ void Profile(FoldingSetNodeID &ID) const;
+
+ // Fold - If possible, fold this to a simpler init. Return this if not
+ // possible to fold.
+ Init *Fold(Record *CurRec) const;
+
+ bool isComplete() const override { return false; }
+
+ Init *resolveReferences(Resolver &R) const override;
+
+ Init *getBit(unsigned Bit) const override;
+
+ std::string getAsString() const override;
+};
+
/// 'Opcode' - Represent a reference to an entire variable object.
class VarInit : public TypedInit {
Init *VarName;
diff --git a/lib/TableGen/Record.cpp b/lib/TableGen/Record.cpp
index 2fe58dd61d0..c1344aea4eb 100644
--- a/lib/TableGen/Record.cpp
+++ b/lib/TableGen/Record.cpp
@@ -1160,6 +1160,77 @@ std::string TernOpInit::getAsString() const {
RHS->getAsString() + ")";
}
+static void ProfileFoldOpInit(FoldingSetNodeID &ID, Init *A, Init *B,
+ Init *Start, Init *List, Init *Expr,
+ RecTy *Type) {
+ ID.AddPointer(Start);
+ ID.AddPointer(List);
+ ID.AddPointer(A);
+ ID.AddPointer(B);
+ ID.AddPointer(Expr);
+ ID.AddPointer(Type);
+}
+
+FoldOpInit *FoldOpInit::get(Init *Start, Init *List, Init *A, Init *B,
+ Init *Expr, RecTy *Type) {
+ static FoldingSet<FoldOpInit> ThePool;
+
+ FoldingSetNodeID ID;
+ ProfileFoldOpInit(ID, Start, List, A, B, Expr, Type);
+
+ void *IP = nullptr;
+ if (FoldOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP))
+ return I;
+
+ FoldOpInit *I = new (Allocator) FoldOpInit(Start, List, A, B, Expr, Type);
+ ThePool.InsertNode(I, IP);
+ return I;
+}
+
+void FoldOpInit::Profile(FoldingSetNodeID &ID) const {
+ ProfileFoldOpInit(ID, Start, List, A, B, Expr, getType());
+}
+
+Init *FoldOpInit::Fold(Record *CurRec) const {
+ if (ListInit *LI = dyn_cast<ListInit>(List)) {
+ Init *Accum = Start;
+ for (Init *Elt : *LI) {
+ MapResolver R(CurRec);
+ R.set(A, Accum);
+ R.set(B, Elt);
+ Accum = Expr->resolveReferences(R);
+ }
+ return Accum;
+ }
+ return const_cast<FoldOpInit *>(this);
+}
+
+Init *FoldOpInit::resolveReferences(Resolver &R) const {
+ Init *NewStart = Start->resolveReferences(R);
+ Init *NewList = List->resolveReferences(R);
+ ShadowResolver SR(R);
+ SR.addShadow(A);
+ SR.addShadow(B);
+ Init *NewExpr = Expr->resolveReferences(SR);
+
+ if (Start == NewStart && List == NewList && Expr == NewExpr)
+ return const_cast<FoldOpInit *>(this);
+
+ return get(NewStart, NewList, A, B, NewExpr, getType())
+ ->Fold(R.getCurrentRecord());
+}
+
+Init *FoldOpInit::getBit(unsigned Bit) const {
+ return VarBitInit::get(const_cast<FoldOpInit *>(this), Bit);
+}
+
+std::string FoldOpInit::getAsString() const {
+ return (Twine("!foldl(") + Start->getAsString() + ", " + List->getAsString() +
+ ", " + A->getAsUnquotedString() + ", " + B->getAsUnquotedString() +
+ ", " + Expr->getAsString() + ")")
+ .str();
+}
+
RecTy *TypedInit::getFieldType(StringInit *FieldName) const {
if (RecordRecTy *RecordType = dyn_cast<RecordRecTy>(getType())) {
for (Record *Rec : RecordType->getClasses()) {
diff --git a/lib/TableGen/TGLexer.cpp b/lib/TableGen/TGLexer.cpp
index bc03055eb23..9d8d693f975 100644
--- a/lib/TableGen/TGLexer.cpp
+++ b/lib/TableGen/TGLexer.cpp
@@ -480,6 +480,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
.Case("cast", tgtok::XCast)
.Case("empty", tgtok::XEmpty)
.Case("subst", tgtok::XSubst)
+ .Case("foldl", tgtok::XFoldl)
.Case("foreach", tgtok::XForEach)
.Case("listconcat", tgtok::XListConcat)
.Case("strconcat", tgtok::XStrConcat)
diff --git a/lib/TableGen/TGLexer.h b/lib/TableGen/TGLexer.h
index 342eb76fc0f..cfed08ec4d4 100644
--- a/lib/TableGen/TGLexer.h
+++ b/lib/TableGen/TGLexer.h
@@ -48,7 +48,7 @@ namespace tgtok {
// !keywords.
XConcat, XADD, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast,
- XSubst, XForEach, XHead, XTail, XSize, XEmpty, XIf, XEq,
+ XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XEq,
// Integer value.
IntVal,
diff --git a/lib/TableGen/TGParser.cpp b/lib/TableGen/TGParser.cpp
index 31b2c008ba4..488d1c8d82a 100644
--- a/lib/TableGen/TGParser.cpp
+++ b/lib/TableGen/TGParser.cpp
@@ -1232,6 +1232,123 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec,
CurMultiClass);
}
+
+ case tgtok::XFoldl: {
+ // Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')'
+ Lex.Lex(); // eat the operation
+ if (Lex.getCode() != tgtok::l_paren) {
+ TokError("expected '(' after !foldl");
+ return nullptr;
+ }
+ Lex.Lex(); // eat the '('
+
+ Init *StartUntyped = ParseValue(CurRec);
+ if (!StartUntyped)
+ return nullptr;
+
+ TypedInit *Start = dyn_cast<TypedInit>(StartUntyped);
+ if (!Start) {
+ TokError(Twine("could not get type of !foldl start: '") +
+ StartUntyped->getAsString() + "'");
+ return nullptr;
+ }
+
+ if (Lex.getCode() != tgtok::comma) {
+ TokError("expected ',' in !foldl");
+ return nullptr;
+ }
+ Lex.Lex(); // eat the ','
+
+ Init *ListUntyped = ParseValue(CurRec);
+ if (!ListUntyped)
+ return nullptr;
+
+ TypedInit *List = dyn_cast<TypedInit>(ListUntyped);
+ if (!List) {
+ TokError(Twine("could not get type of !foldl list: '") +
+ ListUntyped->getAsString() + "'");
+ return nullptr;
+ }
+
+ ListRecTy *ListType = dyn_cast<ListRecTy>(List->getType());
+ if (!ListType) {
+ TokError(Twine("!foldl list must be a list, but is of type '") +
+ List->getType()->getAsString());
+ return nullptr;
+ }
+
+ if (Lex.getCode() != tgtok::comma) {
+ TokError("expected ',' in !foldl");
+ return nullptr;
+ }
+
+ if (Lex.Lex() != tgtok::Id) { // eat the ','
+ TokError("third argument of !foldl must be an identifier");
+ return nullptr;
+ }
+
+ Init *A = StringInit::get(Lex.getCurStrVal());
+ if (CurRec->getValue(A)) {
+ TokError((Twine("left !foldl variable '") + A->getAsString() +
+ "' already defined")
+ .str());
+ return nullptr;
+ }
+
+ if (Lex.Lex() != tgtok::comma) { // eat the id
+ TokError("expected ',' in !foldl");
+ return nullptr;
+ }
+
+ if (Lex.Lex() != tgtok::Id) { // eat the ','
+ TokError("fourth argument of !foldl must be an identifier");
+ return nullptr;
+ }
+
+ Init *B = StringInit::get(Lex.getCurStrVal());
+ if (CurRec->getValue(B)) {
+ TokError((Twine("right !foldl variable '") + B->getAsString() +
+ "' already defined")
+ .str());
+ return nullptr;
+ }
+
+ if (Lex.Lex() != tgtok::comma) { // eat the id
+ TokError("expected ',' in !foldl");
+ return nullptr;
+ }
+ Lex.Lex(); // eat the ','
+
+ CurRec->addValue(RecordVal(A, Start->getType(), false));
+ CurRec->addValue(RecordVal(B, ListType->getElementType(), false));
+ Init *ExprUntyped = ParseValue(CurRec);
+ CurRec->removeValue(A);
+ CurRec->removeValue(B);
+ if (!ExprUntyped)
+ return nullptr;
+
+ TypedInit *Expr = dyn_cast<TypedInit>(ExprUntyped);
+ if (!Expr) {
+ TokError("could not get type of !foldl expression");
+ return nullptr;
+ }
+
+ if (Expr->getType() != Start->getType()) {
+ TokError(Twine("!foldl expression must be of same type as start (") +
+ Start->getType()->getAsString() + "), but is of type " +
+ Expr->getType()->getAsString());
+ return nullptr;
+ }
+
+ if (Lex.getCode() != tgtok::r_paren) {
+ TokError("expected ')' in fold operator");
+ return nullptr;
+ }
+ Lex.Lex(); // eat the ')'
+
+ return FoldOpInit::get(Start, List, A, B, Expr, Start->getType())
+ ->Fold(CurRec);
+ }
}
}
@@ -1590,6 +1707,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
case tgtok::XListConcat:
case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')'
case tgtok::XIf:
+ case tgtok::XFoldl:
case tgtok::XForEach:
case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')'
return ParseOperation(CurRec, ItemType);
@@ -1697,7 +1815,7 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
TypedInit *RHS = nullptr;
Lex.Lex(); // Eat the '#'.
- switch (Lex.getCode()) {
+ switch (Lex.getCode()) {
case tgtok::colon:
case tgtok::semi:
case tgtok::l_brace:
@@ -2579,7 +2697,7 @@ Record *TGParser::InstantiateMulticlassDef(MultiClass &MC, Record *DefProto,
// Ensure redefinition doesn't happen.
if (Records.getDef(CurRec->getNameInitAsString())) {
Error(DefmPrefixRange.Start, "def '" + CurRec->getNameInitAsString() +
- "' already defined, instantiating defm with subdef '" +
+ "' already defined, instantiating defm with subdef '" +
DefProto->getNameInitAsString() + "'");
return nullptr;
}
diff --git a/test/TableGen/foldl.td b/test/TableGen/foldl.td
new file mode 100644
index 00000000000..879092470cc
--- /dev/null
+++ b/test/TableGen/foldl.td
@@ -0,0 +1,71 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// XFAIL: vg_leak
+
+// CHECK: --- Defs ---
+
+// CHECK: def A1 {
+// CHECK: int ret = 0;
+// CHECK: }
+
+// CHECK: def A2 {
+// CHECK: int ret = 5;
+// CHECK: }
+
+// CHECK: def A3 {
+// CHECK: int ret = 10;
+// CHECK: }
+
+// CHECK: def B1 {
+// CHECK: list<string> ret = [];
+// CHECK: }
+
+// CHECK: def B2 {
+// CHECK: list<string> ret = [];
+// CHECK: }
+
+// CHECK: def B3 {
+// CHECK: list<string> ret = ["a"];
+// CHECK: }
+
+// CHECK: def B4 {
+// CHECK: list<string> ret = ["a", "b", "c", "d"];
+// CHECK: }
+
+// CHECK: def E0 {
+// CHECK: list<int> ret = [45, 45, 45, 45];
+// CHECK: }
+
+class Sum<list<int> lst> {
+ int ret = !foldl(0, lst, a, b, !add(a, b));
+}
+
+class Flatten<list<list<string>> lst> {
+ list<string> ret = !foldl([]<string>, lst, a, b, !listconcat(a, b));
+}
+
+def A1 : Sum<[]>;
+def A2 : Sum<[5]>;
+def A3 : Sum<[1, 2, 3, 4]>;
+
+def B1 : Flatten<[]>;
+def B2 : Flatten<[[]]>;
+def B3 : Flatten<[["a"]]>;
+def B4 : Flatten<[["a", "b"], [], ["c"], ["d"]]>;
+
+// The variables a and b are declared both in the "inner" foldl and in the
+// other foreach. The test checks that they don't "leak".
+class C<list<int> lst> {
+ int ret = !foldl(0, lst, a, b, !add(a, b));
+}
+
+class D<list<int> lst1, list<int> lst2> {
+ list<int> x = !foreach(a, lst1, C<lst2>.ret);
+ list<int> y = !foreach(b, lst1, C<lst2>.ret);
+ list<int> z = !listconcat(x, y);
+}
+
+class E<list<int> lst2> {
+ list<int> ret = D<[0, 1], lst2>.z;
+}
+
+def E0 : E<[10, 15, 20]>;