diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -196,14 +196,14 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ See `Using Classes as Subroutines`_ for more information. .. productionlist:: - SimpleValue8: `BangOperator` ["<" `Type` ">"] "(" `ValueListNE` ")" + SimpleValue9: `BangOperator` ["<" `Type` ">"] "(" `ValueListNE` ")" :| `CondOperator` "(" `CondClause` ("," `CondClause`)* ")" CondClause: `Value` ":" `Value` diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -45,17 +45,21 @@ 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, diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -150,7 +150,7 @@ 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 @@ 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(Str) .Case("int", tgtok::Int) .Case("bit", tgtok::Bit) @@ -356,6 +351,8 @@ .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 @@ .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; } diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -1850,8 +1850,20 @@ 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 Bits(BinaryVal.second); @@ -2267,6 +2279,7 @@ if (!RHSResult) return nullptr; Result = BinOpInit::getListConcat(LHS, RHSResult); + break; } break; } diff --git a/llvm/test/TableGen/condsbit.td b/llvm/test/TableGen/condsbit.td --- a/llvm/test/TableGen/condsbit.td +++ b/llvm/test/TableGen/condsbit.td @@ -3,15 +3,21 @@ // Check that !cond works well with bit conditional values. -// CHECK: a = 6 -// CHECK: a = 5 - -class A { - 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 { + 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; def Y : A; diff --git a/llvm/test/TableGen/foreach-range-parse-errors0.td b/llvm/test/TableGen/foreach-range-parse-errors0.td --- a/llvm/test/TableGen/foreach-range-parse-errors0.td +++ b/llvm/test/TableGen/foreach-range-parse-errors0.td @@ -10,8 +10,8 @@ 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 { diff --git a/llvm/test/TableGen/paste-reserved.td b/llvm/test/TableGen/paste-reserved.td --- a/llvm/test/TableGen/paste-reserved.td +++ b/llvm/test/TableGen/paste-reserved.td @@ -9,7 +9,7 @@ def list_paste { list 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 @@ #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 --- /dev/null +++ b/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 +