Skip to content

Commit

Permalink
[TableGen] Add true and false literals to represent booleans
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Paul C. Anagnostopoulos committed Nov 5, 2020
1 parent 0e61d02 commit ae2cb4f
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 41 deletions.
32 changes: 20 additions & 12 deletions llvm/docs/TableGen/ProgRef.rst
Expand Up @@ -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.
Expand Down Expand Up @@ -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`)*

Expand All @@ -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
Expand All @@ -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`

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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`

Expand Down
26 changes: 17 additions & 9 deletions llvm/lib/TableGen/TGLexer.cpp
Expand Up @@ -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())
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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;
}

Expand Down
14 changes: 9 additions & 5 deletions llvm/lib/TableGen/TGLexer.h
Expand Up @@ -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,

Expand Down
17 changes: 15 additions & 2 deletions llvm/lib/TableGen/TGParser.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -2267,6 +2279,7 @@ Init *TGParser::ParseValue(Record *CurRec, RecTy *ItemType, IDParseMode Mode) {
if (!RHSResult)
return nullptr;
Result = BinOpInit::getListConcat(LHS, RHSResult);
break;
}
break;
}
Expand Down
24 changes: 15 additions & 9 deletions llvm/test/TableGen/condsbit.td
Expand Up @@ -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;
4 changes: 2 additions & 2 deletions llvm/test/TableGen/foreach-range-parse-errors0.td
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/TableGen/paste-reserved.td
Expand Up @@ -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


Expand All @@ -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
75 changes: 75 additions & 0 deletions llvm/test/TableGen/true-false.td
@@ -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

0 comments on commit ae2cb4f

Please sign in to comment.