[llvm] r326790 - TableGen: Add !foldl operation

Nicolai Haehnle via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 6 05:49:16 PST 2018


Author: nha
Date: Tue Mar  6 05:49:16 2018
New Revision: 326790

URL: http://llvm.org/viewvc/llvm-project?rev=326790&view=rev
Log:
TableGen: Add !foldl operation

Change-Id: I63d67bf6e0b315e2d3360e47e3b62c9517f38987

Added:
    llvm/trunk/test/TableGen/foldl.td
Modified:
    llvm/trunk/docs/TableGen/LangIntro.rst
    llvm/trunk/docs/TableGen/LangRef.rst
    llvm/trunk/include/llvm/TableGen/Record.h
    llvm/trunk/lib/TableGen/Record.cpp
    llvm/trunk/lib/TableGen/TGLexer.cpp
    llvm/trunk/lib/TableGen/TGLexer.h
    llvm/trunk/lib/TableGen/TGParser.cpp

Modified: llvm/trunk/docs/TableGen/LangIntro.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/TableGen/LangIntro.rst?rev=326790&r1=326789&r2=326790&view=diff
==============================================================================
--- llvm/trunk/docs/TableGen/LangIntro.rst (original)
+++ llvm/trunk/docs/TableGen/LangIntro.rst Tue Mar  6 05:49:16 2018
@@ -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.'
 

Modified: llvm/trunk/docs/TableGen/LangRef.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/TableGen/LangRef.rst?rev=326790&r1=326789&r2=326790&view=diff
==============================================================================
--- llvm/trunk/docs/TableGen/LangRef.rst (original)
+++ llvm/trunk/docs/TableGen/LangRef.rst Tue Mar  6 05:49:16 2018
@@ -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

Modified: llvm/trunk/include/llvm/TableGen/Record.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/TableGen/Record.h?rev=326790&r1=326789&r2=326790&view=diff
==============================================================================
--- llvm/trunk/include/llvm/TableGen/Record.h (original)
+++ llvm/trunk/include/llvm/TableGen/Record.h Tue Mar  6 05:49:16 2018
@@ -316,6 +316,7 @@ protected:
     IK_TernOpInit,
     IK_UnOpInit,
     IK_LastOpInit,
+    IK_FoldOpInit,
     IK_StringInit,
     IK_VarInit,
     IK_VarListElementInit,
@@ -912,6 +913,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 {

Modified: llvm/trunk/lib/TableGen/Record.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/Record.cpp?rev=326790&r1=326789&r2=326790&view=diff
==============================================================================
--- llvm/trunk/lib/TableGen/Record.cpp (original)
+++ llvm/trunk/lib/TableGen/Record.cpp Tue Mar  6 05:49:16 2018
@@ -1160,6 +1160,77 @@ std::string TernOpInit::getAsString() co
          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()) {

Modified: llvm/trunk/lib/TableGen/TGLexer.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/TGLexer.cpp?rev=326790&r1=326789&r2=326790&view=diff
==============================================================================
--- llvm/trunk/lib/TableGen/TGLexer.cpp (original)
+++ llvm/trunk/lib/TableGen/TGLexer.cpp Tue Mar  6 05:49:16 2018
@@ -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)

Modified: llvm/trunk/lib/TableGen/TGLexer.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/TGLexer.h?rev=326790&r1=326789&r2=326790&view=diff
==============================================================================
--- llvm/trunk/lib/TableGen/TGLexer.h (original)
+++ llvm/trunk/lib/TableGen/TGLexer.h Tue Mar  6 05:49:16 2018
@@ -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,

Modified: llvm/trunk/lib/TableGen/TGParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/TableGen/TGParser.cpp?rev=326790&r1=326789&r2=326790&view=diff
==============================================================================
--- llvm/trunk/lib/TableGen/TGParser.cpp (original)
+++ llvm/trunk/lib/TableGen/TGParser.cpp Tue Mar  6 05:49:16 2018
@@ -1232,6 +1232,123 @@ Init *TGParser::ParseOperation(Record *C
     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
   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 *CurRe
       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::InstantiateMulticlassD
     // 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;
     }

Added: llvm/trunk/test/TableGen/foldl.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/TableGen/foldl.td?rev=326790&view=auto
==============================================================================
--- llvm/trunk/test/TableGen/foldl.td (added)
+++ llvm/trunk/test/TableGen/foldl.td Tue Mar  6 05:49:16 2018
@@ -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]>;




More information about the llvm-commits mailing list