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:
parent
499b0ffb24
commit
0830936d52
@ -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`
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
75
test/TableGen/true-false.td
Normal file
75
test/TableGen/true-false.td
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user