[TableGen] Add true and false literals to represent booleans
authorPaul C. Anagnostopoulos <paul@windfall.com>
Mon, 2 Nov 2020 17:37:25 +0000 (12:37 -0500)
committerPaul C. Anagnostopoulos <paul@windfall.com>
Thu, 5 Nov 2020 14:07:21 +0000 (09:07 -0500)
Update the Programmer's Reference document.

Add a test. Update a couple of tests with an improved error message.

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

llvm/docs/TableGen/ProgRef.rst
llvm/lib/TableGen/TGLexer.cpp
llvm/lib/TableGen/TGLexer.h
llvm/lib/TableGen/TGParser.cpp
llvm/test/TableGen/condsbit.td
llvm/test/TableGen/foreach-range-parse-errors0.td
llvm/test/TableGen/paste-reserved.td
llvm/test/TableGen/true-false.td [new file with mode: 0644]

index 4d2d44f10d68155b5910e97015ccbd0a47d228c2..4b83651397155358cc655e9600e9b8a4b9912179 100644 (file)
@@ -196,14 +196,14 @@ Note that, unlike most languages, TableGen allows :token:`TokIdentifier` to
 begin with an integer. In case of ambiguity, a token is interpreted as a
 numeric literal rather than an identifier.
 
-TableGen has the following reserved words, which cannot be used as
+TableGen has the following reserved keywords, which cannot be used as
 identifiers::
 
    bit        bits          class         code          dag
-   def        else          foreach       defm          defset
-   defvar     field         if            in            include
-   int        let           list          multiclass    string
-   then
+   def        else          false         foreach       defm
+   defset     defvar        field         if            in
+   include    int           let           list          multiclass
+   string     then          true
 
 .. warning::
   The ``field`` reserved word is deprecated.
@@ -362,12 +362,20 @@ simple value is the concatenation of the strings. Code fragments become
 strings and then are indistinguishable from them.
 
 .. productionlist::
-   SimpleValue2: "?"
+   SimpleValue2: "true" | "false"
+
+The ``true`` and ``false`` literals are essentially syntactic sugar for the
+integer values 1 and 0. They improve the readability of TableGen files when
+boolean values are used in field values, bit sequences, ``if`` statements.
+etc. When parsed, these literals are converted to integers.
+
+.. productionlist::
+   SimpleValue3: "?"
 
 A question mark represents an uninitialized value.
 
 .. productionlist::
-   SimpleValue3: "{" [`ValueList`] "}"
+   SimpleValue4: "{" [`ValueList`] "}"
    ValueList: `ValueListNE`
    ValueListNE: `Value` ("," `Value`)*
 
@@ -376,7 +384,7 @@ This value represents a sequence of bits, which can be used to initialize a
 must represent a total of *n* bits.
 
 .. productionlist::
-   SimpleValue4: "[" `ValueList` "]" ["<" `Type` ">"]
+   SimpleValue5: "[" `ValueList` "]" ["<" `Type` ">"]
 
 This value is a list initializer (note the brackets). The values in brackets
 are the elements of the list. The optional :token:`Type` can be used to
@@ -385,7 +393,7 @@ from the given values. TableGen can usually infer the type, although
 sometimes not when the value is the empty list (``[]``).
 
 .. productionlist::
-   SimpleValue5: "(" `DagArg` [`DagArgList`] ")"
+   SimpleValue6: "(" `DagArg` [`DagArgList`] ")"
    DagArgList: `DagArg` ("," `DagArg`)*
    DagArg: `Value` [":" `TokVarName`] | `TokVarName`
 
@@ -394,7 +402,7 @@ This represents a DAG initializer (note the parentheses).  The first
 See `Directed acyclic graphs (DAGs)`_ for more details.
 
 .. productionlist::
-   SimpleValue6: `TokIdentifier`
+   SimpleValue7: `TokIdentifier`
 
 The resulting value is the value of the entity named by the identifier. The
 possible identifiers are described here, but the descriptions will make more
@@ -453,7 +461,7 @@ sense after reading the remainder of this guide.
        def Foo#i;
 
 .. productionlist::
-   SimpleValue7: `ClassID` "<" `ValueListNE` ">"
+   SimpleValue8: `ClassID` "<" `ValueListNE` ">"
 
 This form creates a new anonymous record definition (as would be created by an
 unnamed ``def`` inheriting from the given class with the given template
@@ -464,7 +472,7 @@ Invoking a class in this manner can provide a simple subroutine facility.
 See `Using Classes as Subroutines`_ for more information.
 
 .. productionlist::
-   SimpleValue8: `BangOperator` ["<" `Type` ">"] "(" `ValueListNE` ")"
+   SimpleValue9: `BangOperator` ["<" `Type` ">"] "(" `ValueListNE` ")"
               :| `CondOperator` "(" `CondClause` ("," `CondClause`)* ")"
    CondClause: `Value` ":" `Value`
 
index 7693f82ba7cc1bc12d890226044dff8319142929..7cbf9de24b838dfbbab03b667aae0a5a3e9549cb 100644 (file)
@@ -150,7 +150,7 @@ tgtok::TokKind TGLexer::LexToken(bool FileOrLineStart) {
   case EOF:
     // Lex next token, if we just left an include file.
     // Note that leaving an include file means that the next
-    // symbol is located at the end of 'include "..."'
+    // symbol is located at the end of the 'include "..."'
     // construct, so LexToken() is called with default
     // false parameter.
     if (processEOF())
@@ -338,14 +338,9 @@ tgtok::TokKind TGLexer::LexIdentifier() {
   while (isalpha(*CurPtr) || isdigit(*CurPtr) || *CurPtr == '_')
     ++CurPtr;
 
-  // Check to see if this identifier is a keyword.
+  // Check to see if this identifier is a reserved keyword.
   StringRef Str(IdentStart, CurPtr-IdentStart);
 
-  if (Str == "include") {
-    if (LexInclude()) return tgtok::Error;
-    return Lex();
-  }
-
   tgtok::TokKind Kind = StringSwitch<tgtok::TokKind>(Str)
     .Case("int", tgtok::Int)
     .Case("bit", tgtok::Bit)
@@ -356,6 +351,8 @@ tgtok::TokKind TGLexer::LexIdentifier() {
     .Case("dag", tgtok::Dag)
     .Case("class", tgtok::Class)
     .Case("def", tgtok::Def)
+    .Case("true", tgtok::TrueVal)
+    .Case("false", tgtok::FalseVal)
     .Case("foreach", tgtok::Foreach)
     .Case("defm", tgtok::Defm)
     .Case("defset", tgtok::Defset)
@@ -364,13 +361,24 @@ tgtok::TokKind TGLexer::LexIdentifier() {
     .Case("let", tgtok::Let)
     .Case("in", tgtok::In)
     .Case("defvar", tgtok::Defvar)
+    .Case("include", tgtok::Include)
     .Case("if", tgtok::If)
     .Case("then", tgtok::Then)
     .Case("else", tgtok::ElseKW)
     .Default(tgtok::Id);
 
-  if (Kind == tgtok::Id)
-    CurStrVal.assign(Str.begin(), Str.end());
+  // A couple of tokens require special processing.
+  switch (Kind) {
+    case tgtok::Include:
+      if (LexInclude()) return tgtok::Error;
+      return Lex();
+    case tgtok::Id:
+      CurStrVal.assign(Str.begin(), Str.end());
+      break;
+    default:
+      break;
+  }
+
   return Kind;
 }
 
index eaba640061fd6d85487d8c8c6438ce33cc7274c1..cf336aad6f210bbf691250f2c84ee75a2c8ca7ad 100644 (file)
@@ -45,17 +45,21 @@ namespace tgtok {
     paste,              // #
     dotdotdot,          // ...
 
-    // 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, If, Then, ElseKW,
+    // Reserved keywords. ('ElseKW' is named to distinguish it from the
+    // existing 'Else' that means the preprocessor #else.)
+    Bit, Bits, Class, Code, Dag, Def, Defm, Defset, Defvar, ElseKW, FalseKW,
+    Field, Foreach, If, In, Include, Int, Let, List, MultiClass,
+    String, Then, TrueKW,
 
-    // !keywords.
+    // Bang operators.
     XConcat, XADD, XSUB, XMUL, XNOT, XAND, XOR, XXOR, XSRA, XSRL, XSHL,
     XListConcat, XListSplat, XStrConcat, XInterleave, XCast, XSubst, XForEach,
     XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, XNe,
     XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp,
 
+    // Boolean literals.
+    TrueVal, FalseVal,
+
     // Integer value.
     IntVal,
 
index 4f64f4be5401941d36c8db1a5f9edd4d8f58c634..cab1921fdd2acc553d1ece5aaf2a2e5dccf25d5e 100644 (file)
@@ -1850,8 +1850,20 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
                                  IDParseMode Mode) {
   Init *R = nullptr;
   switch (Lex.getCode()) {
-  default: TokError("Unknown token when parsing a value"); break;
-  case tgtok::IntVal: R = IntInit::get(Lex.getCurIntVal()); Lex.Lex(); break;
+  default: TokError("Unknown or reserved token when parsing a value"); break;
+
+  case tgtok::TrueVal:
+    R = IntInit::get(1);
+    Lex.Lex();
+    break;
+  case tgtok::FalseVal:
+    R = IntInit::get(0);
+    Lex.Lex();
+    break;
+  case tgtok::IntVal:
+    R = IntInit::get(Lex.getCurIntVal());
+    Lex.Lex();
+    break;
   case tgtok::BinaryIntVal: {
     auto BinaryVal = Lex.getCurBinaryIntVal();
     SmallVector<Init*, 16> Bits(BinaryVal.second);
@@ -2267,6 +2279,7 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
           if (!RHSResult)
             return nullptr;
           Result = BinOpInit::getListConcat(LHS, RHSResult);
+          break;
         }
         break;
       }
index 1409c0a6b6c69e3f49cd7599d7175e1338441575..d0dda7cbfe6b19638992b7a38c7111c5e734174c 100644 (file)
@@ -3,15 +3,21 @@
 
 // Check that !cond works well with bit conditional values.
 
-// CHECK: a = 6
-// CHECK: a = 5
-
-class A<bit b = 1> {
-  bit True = 1;
-  int a = !cond(b: 5, True : 6);
-  bit c = !cond(b: 0, True : 1);
-  bits<1> d = !cond(b: 0, True : 1);
+class A<bit b = true> {
+  int a = !cond(b: 5, true : 6);
+  bit c = !cond(b: false, true : true);
+  bits<1> d = !cond(b: 0, true : 1);
 }
 
-def X : A<0>;
+// CHECK: def X
+// CHECK:   a = 6
+// CHECK:   c = 1
+// CHECK:   d = { 1 }
+
+// CHECK: def Y
+// CHECK:   a = 5
+// CHECK:   c = 0
+// CHECK:   d = { 0 }
+
+def X : A<false>;
 def Y : A;
index a3e6a9eb975638e422b300dddcc1eae4e6a69dd1..2da5ce05634624249881347eadf981e4656c8b25 100644 (file)
@@ -10,8 +10,8 @@ class ConstantsImpl {
 
 def Constants : ConstantsImpl;
 
-// CHECK-NOT:  error: Unknown token when parsing a value
-// CHECK: [[FILE]]:[[@LINE+3]]:22: error: Unknown token when parsing a value
+// CHECK-NOT:  error: Unknown or reserved token when parsing a value
+// CHECK: [[FILE]]:[[@LINE+3]]:22: error: Unknown or reserved token when parsing a value
 // CHECK: [[FILE]]:[[@LINE+2]]:22: error: expected integer value as end of range
 // CHECK: [[FILE]]:[[@LINE+1]]:22: error: expected declaration in for
 foreach Index = 0 -  in {
index 8e61e5526d20a632f3fb4408976d06263193c1f0..222a8c202a60ffe7e060220939e5ec5e4922c041 100644 (file)
@@ -9,7 +9,7 @@ defvar list1 = ["foo", "bar", "snork"];
 def list_paste {
   list<string> the_list = list1 # in;
 }
-// ERROR1: error: Unknown token when parsing a value
+// ERROR1: error: Unknown or reserved token when parsing a value
 #endif
 
 
@@ -18,5 +18,5 @@ def list_paste {
 #ifdef ERROR2
 def name_paste#in {
 }
-// ERROR2: error: Unknown token when parsing a value
+// ERROR2: error: Unknown or reserved token when parsing a value
 #endif
diff --git a/llvm/test/TableGen/true-false.td b/llvm/test/TableGen/true-false.td
new file mode 100644 (file)
index 0000000..597ad9f
--- /dev/null
@@ -0,0 +1,75 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
+
+// Tests for the true and false literals.
+
+defvar otherwise = true;
+defvar do_it = true;
+
+// CHECK: def rec1
+// CHECK:   bit flag1 = 1;
+// CHECK:   bit flag2 = 0;
+// CHECK:   int true_int = 1;
+
+def rec1 {
+  bit flag1 = true;
+  bit flag2 = false;
+  int true_int = true;
+}
+
+// CHECK: def rec2_true
+
+if true then
+  def rec2_true {}
+else
+  def rec2_bad {}
+
+// CHECK: def rec3_false
+
+if false then
+  def rec3_bad {}
+else
+  def rec3_false {}
+
+// CHECK: def rec4
+// CHECK:   int value = 52;
+
+def rec4 {
+  int value = !add(10, !if(!and(do_it, true), 42, 0));
+}
+
+// CHECK: def rec5
+// CHECK:    string name = "snork";
+
+def rec5 {
+  string name = !cond(false:       "foo",
+                      !not(do_it): "bar",
+                      otherwise:   "snork");
+}
+
+// CHECK: def rec6
+// CHECK:   bit xorFF = 0;
+// CHECK:   bit xorFT = 1;
+// CHECK:   bit xorTF = 1;
+// CHECK:   bit xorTT = 0;
+
+def rec6 {
+  bit xorFF = !xor(false, false);
+  bit xorFT = !xor(false, true);
+  bit xorTF = !xor(true, false);
+  bit xorTT = !xor(true, true);
+}
+
+// CHECK: def rec7
+// CHECK:   bits<3> flags = { 1, 0, 1 };
+
+def rec7 {
+  bits<3> flags = { true, false, true };
+}
+
+#ifdef ERROR1
+// ERROR1: Record name '1' is not a string
+
+def true {}
+#endif
+