[llvm] ddbc0b1 - [TableGen] Introduce an if/then/else statement.

Simon Tatham via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 14 02:20:17 PST 2020


Author: Simon Tatham
Date: 2020-01-14T10:19:53Z
New Revision: ddbc0b1e516407a24d986a1998026f1ac5864270

URL: https://github.com/llvm/llvm-project/commit/ddbc0b1e516407a24d986a1998026f1ac5864270
DIFF: https://github.com/llvm/llvm-project/commit/ddbc0b1e516407a24d986a1998026f1ac5864270.diff

LOG: [TableGen] Introduce an if/then/else statement.

Summary:
This allows you to make some of the defs in a multiclass or `foreach`
conditional on an expression computed from the parameters or iteration
variables.

It was already possible to simulate an if statement using a `foreach`
with a dummy iteration variable and a list constructed using `!if` so
that it had length 0 or 1 depending on the condition, e.g.

  foreach unusedIterationVar = !if(condition, [1], []<int>) in { ... }

But this syntax is nicer to read, and also more convenient because it
allows an else clause.

To avoid upheaval in the implementation, I've implemented `if` as pure
syntactic sugar on the `foreach` implementation: internally, `ParseIf`
actually does construct exactly the kind of foreach shown above (and
another reversed one for the else clause if present).

Reviewers: nhaehnle, hfinkel

Reviewed By: hfinkel

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D71474

Added: 
    llvm/test/TableGen/ifstmt.td

Modified: 
    llvm/docs/TableGen/LangRef.rst
    llvm/lib/TableGen/TGLexer.cpp
    llvm/lib/TableGen/TGLexer.h
    llvm/lib/TableGen/TGParser.cpp
    llvm/lib/TableGen/TGParser.h
    llvm/test/TableGen/defvar.td

Removed: 
    


################################################################################
diff  --git a/llvm/docs/TableGen/LangRef.rst b/llvm/docs/TableGen/LangRef.rst
index 7cbe6d996430..74af1ee659f5 100644
--- a/llvm/docs/TableGen/LangRef.rst
+++ b/llvm/docs/TableGen/LangRef.rst
@@ -89,6 +89,7 @@ TableGen also has the following keywords::
    bit   bits      class   code         dag
    def   foreach   defm    field        in
    int   let       list    multiclass   string
+   if    then      else
 
 TableGen also has "bang operators" which have a
 wide variety of meanings:
@@ -125,7 +126,7 @@ TableGen's top-level production consists of "objects".
 .. productionlist::
    TableGenFile: `Object`*
    Object: `Class` | `Def` | `Defm` | `Defset` | `Defvar` | `Let` |
-           `MultiClass` | `Foreach`
+           `MultiClass` | `Foreach` | `If`
 
 ``class``\es
 ------------
@@ -466,6 +467,24 @@ iterated value.
 Note that the productions involving RangeList and RangePiece have precedence
 over the more generic value parsing based on the first token.
 
+``if``
+------
+
+.. productionlist::
+   If: "if" `Value` "then" `IfBody`
+     :| "if" `Value` "then" `IfBody` "else" `IfBody`
+   IfBody: "{" `Object`* "}" | `Object`
+
+The value expression after the ``if`` keyword is evaluated, and if it evaluates
+to true (in the same sense used by the ``!if`` operator), then the object
+definition(s) after the ``then`` keyword are executed. Otherwise, if there is
+an ``else`` keyword, the definition(s) after the ``else`` are executed instead.
+
+Because the braces around the ``then`` clause are optional, this grammar rule
+has the usual ambiguity about dangling ``else`` clauses, and it is resolved in
+the usual way: in a case like ``if v1 then if v2 then {...} else {...}``, the
+``else`` binds to the inner ``if`` rather than the outer one.
+
 Top-Level ``let``
 -----------------
 

diff  --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp
index c160704b6e77..1a3f5a7392d5 100644
--- a/llvm/lib/TableGen/TGLexer.cpp
+++ b/llvm/lib/TableGen/TGLexer.cpp
@@ -351,6 +351,9 @@ tgtok::TokKind TGLexer::LexIdentifier() {
     .Case("let", tgtok::Let)
     .Case("in", tgtok::In)
     .Case("defvar", tgtok::Defvar)
+    .Case("if", tgtok::If)
+    .Case("then", tgtok::Then)
+    .Case("else", tgtok::ElseKW)
     .Default(tgtok::Id);
 
   if (Kind == tgtok::Id)

diff  --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h
index 04c72900b0b4..6d10af348674 100644
--- a/llvm/lib/TableGen/TGLexer.h
+++ b/llvm/lib/TableGen/TGLexer.h
@@ -44,9 +44,10 @@ namespace tgtok {
     equal, question,    // = ?
     paste,              // #
 
-    // Keywords.
+    // Keywords. ('ElseKW' is named to distinguish it from the existing 'Else'
+    // that means the preprocessor #else.)
     Bit, Bits, Class, Code, Dag, Def, Foreach, Defm, Field, In, Int, Let, List,
-    MultiClass, String, Defset, Defvar,
+    MultiClass, String, Defset, Defvar, If, Then, ElseKW,
 
     // !keywords.
     XConcat, XADD, XMUL, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XListSplat,

diff  --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index 34d993c518ac..01cc1af34ab6 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -391,9 +391,11 @@ bool TGParser::resolve(const ForeachLoop &Loop, SubstStack &Substs,
 
   bool Error = false;
   for (auto Elt : *LI) {
-    Substs.emplace_back(Loop.IterVar->getNameInit(), Elt);
+    if (Loop.IterVar)
+      Substs.emplace_back(Loop.IterVar->getNameInit(), Elt);
     Error = resolve(Loop.Entries, Substs, Final, Dest);
-    Substs.pop_back();
+    if (Loop.IterVar)
+      Substs.pop_back();
     if (Error)
       break;
   }
@@ -482,7 +484,7 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
 static bool isObjectStart(tgtok::TokKind K) {
   return K == tgtok::Class || K == tgtok::Def || K == tgtok::Defm ||
          K == tgtok::Let || K == tgtok::MultiClass || K == tgtok::Foreach ||
-         K == tgtok::Defset || K == tgtok::Defvar;
+         K == tgtok::Defset || K == tgtok::Defvar || K == tgtok::If;
 }
 
 /// ParseObjectName - If a valid object name is specified, return it. If no
@@ -875,9 +877,11 @@ Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc,
 
   // If this is in a foreach loop, make sure it's not a loop iterator
   for (const auto &L : Loops) {
-    VarInit *IterVar = dyn_cast<VarInit>(L->IterVar);
-    if (IterVar && IterVar->getNameInit() == Name)
-      return IterVar;
+    if (L->IterVar) {
+      VarInit *IterVar = dyn_cast<VarInit>(L->IterVar);
+      if (IterVar && IterVar->getNameInit() == Name)
+        return IterVar;
+    }
   }
 
   if (Mode == ParseNameMode)
@@ -2909,6 +2913,115 @@ bool TGParser::ParseForeach(MultiClass *CurMultiClass) {
   return addEntry(std::move(Loop));
 }
 
+/// ParseIf - Parse an if statement.
+///
+///   If ::= IF Value THEN IfBody
+///   If ::= IF Value THEN IfBody ELSE IfBody
+///
+bool TGParser::ParseIf(MultiClass *CurMultiClass) {
+  SMLoc Loc = Lex.getLoc();
+  assert(Lex.getCode() == tgtok::If && "Unknown tok");
+  Lex.Lex(); // Eat the 'if' token.
+
+  // Make a temporary object to record items associated with the for
+  // loop.
+  Init *Condition = ParseValue(nullptr);
+  if (!Condition)
+    return true;
+
+  if (Lex.getCode() != tgtok::Then)
+    return TokError("Unknown tok");
+  Lex.Lex(); // Eat the 'then'
+
+  // We have to be able to save if statements to execute later, and they have
+  // to live on the same stack as foreach loops. The simplest implementation
+  // technique is to convert each 'then' or 'else' clause *into* a foreach
+  // loop, over a list of length 0 or 1 depending on the condition, and with no
+  // iteration variable being assigned.
+
+  ListInit *EmptyList = ListInit::get({}, BitRecTy::get());
+  ListInit *SingletonList = ListInit::get({BitInit::get(1)}, BitRecTy::get());
+  RecTy *BitListTy = ListRecTy::get(BitRecTy::get());
+
+  // The foreach containing the then-clause selects SingletonList if
+  // the condition is true.
+  Init *ThenClauseList =
+      TernOpInit::get(TernOpInit::IF, Condition, SingletonList, EmptyList,
+                      BitListTy)
+          ->Fold(nullptr);
+  Loops.push_back(std::make_unique<ForeachLoop>(Loc, nullptr, ThenClauseList));
+
+  if (ParseIfBody(CurMultiClass, "then"))
+    return true;
+
+  std::unique_ptr<ForeachLoop> Loop = std::move(Loops.back());
+  Loops.pop_back();
+
+  if (addEntry(std::move(Loop)))
+    return true;
+
+  // Now look for an optional else clause. The if-else syntax has the usual
+  // dangling-else ambiguity, and by greedily matching an else here if we can,
+  // we implement the usual resolution of pairing with the innermost unmatched
+  // if.
+  if (Lex.getCode() == tgtok::ElseKW) {
+    Lex.Lex(); // Eat the 'else'
+
+    // The foreach containing the else-clause uses the same pair of lists as
+    // above, but this time, selects SingletonList if the condition is *false*.
+    Init *ElseClauseList =
+        TernOpInit::get(TernOpInit::IF, Condition, EmptyList, SingletonList,
+                        BitListTy)
+            ->Fold(nullptr);
+    Loops.push_back(
+        std::make_unique<ForeachLoop>(Loc, nullptr, ElseClauseList));
+
+    if (ParseIfBody(CurMultiClass, "else"))
+      return true;
+
+    Loop = std::move(Loops.back());
+    Loops.pop_back();
+
+    if (addEntry(std::move(Loop)))
+      return true;
+  }
+
+  return false;
+}
+
+/// ParseIfBody - Parse the then-clause or else-clause of an if statement.
+///
+///   IfBody ::= Object
+///   IfBody ::= '{' ObjectList '}'
+///
+bool TGParser::ParseIfBody(MultiClass *CurMultiClass, StringRef Kind) {
+  TGLocalVarScope *BodyScope = PushLocalScope();
+
+  if (Lex.getCode() != tgtok::l_brace) {
+    // A single object.
+    if (ParseObject(CurMultiClass))
+      return true;
+  } else {
+    SMLoc BraceLoc = Lex.getLoc();
+    // A braced block.
+    Lex.Lex(); // eat the '{'.
+
+    // Parse the object list.
+    if (ParseObjectList(CurMultiClass))
+      return true;
+
+    if (Lex.getCode() != tgtok::r_brace) {
+      TokError("expected '}' at end of '" + Kind + "' clause");
+      return Error(BraceLoc, "to match this '{'");
+    }
+
+    Lex.Lex(); // Eat the }
+  }
+
+  PopLocalScope(BodyScope);
+  return false;
+}
+
 /// ParseClass - Parse a tblgen class definition.
 ///
 ///   ClassInst ::= CLASS ID TemplateArgList? ObjectBody
@@ -3118,13 +3231,14 @@ bool TGParser::ParseMultiClass() {
     while (Lex.getCode() != tgtok::r_brace) {
       switch (Lex.getCode()) {
       default:
-        return TokError("expected 'let', 'def', 'defm', 'defvar' or 'foreach' "
-                        "in multiclass body");
+        return TokError("expected 'let', 'def', 'defm', 'defvar', 'foreach' "
+                        "or 'if' in multiclass body");
       case tgtok::Let:
       case tgtok::Def:
       case tgtok::Defm:
       case tgtok::Defvar:
       case tgtok::Foreach:
+      case tgtok::If:
         if (ParseObject(CurMultiClass))
           return true;
         break;
@@ -3279,11 +3393,12 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) {
 bool TGParser::ParseObject(MultiClass *MC) {
   switch (Lex.getCode()) {
   default:
-    return TokError("Expected class, def, defm, defset, multiclass, let or "
-                    "foreach");
+    return TokError("Expected class, def, defm, defset, multiclass, let, "
+                    "foreach or if");
   case tgtok::Let:   return ParseTopLevelLet(MC);
   case tgtok::Def:   return ParseDef(MC);
   case tgtok::Foreach:   return ParseForeach(MC);
+  case tgtok::If:    return ParseIf(MC);
   case tgtok::Defm:  return ParseDefm(MC);
   case tgtok::Defset:
     if (MC)

diff  --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h
index edcac1e8fa76..c66c79771298 100644
--- a/llvm/lib/TableGen/TGParser.h
+++ b/llvm/lib/TableGen/TGParser.h
@@ -56,6 +56,10 @@ namespace llvm {
 
   /// ForeachLoop - Record the iteration state associated with a for loop.
   /// This is used to instantiate items in the loop body.
+  ///
+  /// IterVar is allowed to be null, in which case no iteration variable is
+  /// defined in the loop at all. (This happens when a ForeachLoop is
+  /// constructed by desugaring an if statement.)
   struct ForeachLoop {
     SMLoc Loc;
     VarInit *IterVar;
@@ -220,6 +224,8 @@ class TGParser {
   bool ParseDefset();
   bool ParseDefvar();
   bool ParseForeach(MultiClass *CurMultiClass);
+  bool ParseIf(MultiClass *CurMultiClass);
+  bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind);
   bool ParseTopLevelLet(MultiClass *CurMultiClass);
   void ParseLetList(SmallVectorImpl<LetRecord> &Result);
 

diff  --git a/llvm/test/TableGen/defvar.td b/llvm/test/TableGen/defvar.td
index 4cb114b1c02c..4c39c37c8a3b 100644
--- a/llvm/test/TableGen/defvar.td
+++ b/llvm/test/TableGen/defvar.td
@@ -95,6 +95,8 @@ defvar firstStr = "now define this at the top level and still expect no error";
 // CHECK-NEXT: int var = 2;
 // CHECK: def shadowInner22 {
 // CHECK-NEXT: int var = 2;
+// CHECK: def shadowInnerIf1 {
+// CHECK-NEXT: int var = 3;
 // CHECK: def shadowOuterAbove1 {
 // CHECK-NEXT: int var = 1;
 // CHECK: def shadowOuterAbove2 {
@@ -103,6 +105,10 @@ defvar firstStr = "now define this at the top level and still expect no error";
 // CHECK-NEXT: int var = 1;
 // CHECK: def shadowOuterBelowForeach2 {
 // CHECK-NEXT: int var = 1;
+// CHECK: def shadowOuterBelowIf1 {
+// CHECK-NEXT: int var = 1;
+// CHECK: def shadowOuterBelowIf2 {
+// CHECK-NEXT: int var = 1;
 
 foreach first = [ 1, 2 ] in {
   defvar shadowedVariable = 1;
@@ -117,6 +123,15 @@ foreach first = [ 1, 2 ] in {
 
   // Now the outer variable is back in scope.
   def shadowOuterBelowForeach # first { int var = shadowedVariable; }
+
+  // An if statement also opens a new scope.
+  if !eq(first, 1) then {
+    defvar shadowedVariable = 3;
+    def shadowInnerIf # first { int var = shadowedVariable; }
+  }
+
+  // Now the outer variable is back in scope again.
+  def shadowOuterBelowIf # first { int var = shadowedVariable; }
 }
 
 // Test that a top-level let statement also makes a variable scope (on the

diff  --git a/llvm/test/TableGen/ifstmt.td b/llvm/test/TableGen/ifstmt.td
new file mode 100644
index 000000000000..22354310e7ba
--- /dev/null
+++ b/llvm/test/TableGen/ifstmt.td
@@ -0,0 +1,92 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
+// RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s
+
+// Test at top level.
+
+// CHECK-NOT: def aNo
+// CHECK: def aYes
+if 0 then def aNo;
+if 1 then def aYes;
+
+// Test inside a foreach, with condition based on the iteration variable.
+
+// CHECK: def bNotThree1
+// CHECK: def bNotThree2
+// CHECK: def bNotThree4
+// CHECK: def bThree3
+foreach i = 1-4 in {
+  if !eq(i, 3) then {
+    def "bThree" # i;
+  } else {
+    def "bNotThree" # i;
+  }
+}
+
+// Test inside a multiclass, with condition based on a multiclass parameter.
+
+multiclass Multi<int i> {
+  def Unconditional;
+
+  if !eq(i, 2) then
+    def Cond;
+
+  if !ge(i, 3) then
+    def ThenRec;
+  else
+    def ElseRec;
+}
+
+// CHECK-NOT: def c1Cond
+// CHECK: def c1ElseRec
+// CHECK-NOT: def c1ThenRec
+// CHECK: def c1Unconditional
+defm c1: Multi<1>;
+
+// CHECK: def c2Cond
+// CHECK: def c2ElseRec
+// CHECK-NOT: def c2ThenRec
+// CHECK: def c2Unconditional
+defm c2: Multi<2>;
+
+// CHECK-NOT: def c3Cond
+// CHECK-NOT: def c3ElseRec
+// CHECK: def c3ThenRec
+// CHECK: def c3Unconditional
+defm c3: Multi<3>;
+
+// Test resolution of the dangling-else ambiguity.
+
+// CHECK: def dThenElse00
+// CHECK-NOT: def dThenElse1
+// CHECK-NOT: def dThenElse11
+// CHECK: def dThenThen01
+foreach i = 0-1 in
+  foreach j = 0-1 in
+    if !eq(i,0) then
+      if !eq(j,1) then
+        def "dThenThen"#i#j;
+      else // binds to the inner if, not the outer one
+        def "dThenElse"#i#j;
+
+// Error tests: ensure you can't put an if inside a def or class.
+
+#ifdef ERROR1
+def baddef {
+  int x = 3;
+  // ERROR1: [[@LINE+1]]:3: error: Unknown token when expecting a type
+  if 1 then {
+    int y = 4;
+  }
+}
+#endif
+
+#ifdef ERROR2
+class badclass<int i> {
+  int x = 3;
+  // ERROR2: [[@LINE+1]]:3: error: Unknown token when expecting a type
+  if !eq(i, 5) then {
+    int y = 4;
+  }
+}
+#endif


        


More information about the llvm-commits mailing list