[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 4d2d44f..4b83651 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 7693f82..7cbf9de 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 eaba640..cf336aa 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 4f64f4b..cab1921 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 1409c0a..d0dda7c 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 a3e6a9e..2da5ce0 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 8e61e55..222a8c2 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
+