1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00

[TableGen] Add true and false literals to represent booleans

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
This commit is contained in:
Paul C. Anagnostopoulos 2020-11-02 12:37:25 -05:00
parent 499b0ffb24
commit 0830936d52
8 changed files with 155 additions and 41 deletions

View 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`

View 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;
}

View 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,

View 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;
}

View 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;

View 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 {

View 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

View File

@ -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