[llvm] ddbc0b1 - [TableGen] Introduce an if/then/else statement.
Simon Tatham via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 14 02:20:17 PST 2020
Author: Simon Tatham
Date: 2020-01-14T10:19:53Z
New Revision: ddbc0b1e516407a24d986a1998026f1ac5864270
URL: https://github.com/llvm/llvm-project/commit/ddbc0b1e516407a24d986a1998026f1ac5864270
DIFF: https://github.com/llvm/llvm-project/commit/ddbc0b1e516407a24d986a1998026f1ac5864270.diff
LOG: [TableGen] Introduce an if/then/else statement.
Summary:
This allows you to make some of the defs in a multiclass or `foreach`
conditional on an expression computed from the parameters or iteration
variables.
It was already possible to simulate an if statement using a `foreach`
with a dummy iteration variable and a list constructed using `!if` so
that it had length 0 or 1 depending on the condition, e.g.
foreach unusedIterationVar = !if(condition, [1], []<int>) in { ... }
But this syntax is nicer to read, and also more convenient because it
allows an else clause.
To avoid upheaval in the implementation, I've implemented `if` as pure
syntactic sugar on the `foreach` implementation: internally, `ParseIf`
actually does construct exactly the kind of foreach shown above (and
another reversed one for the else clause if present).
Reviewers: nhaehnle, hfinkel
Reviewed By: hfinkel
Subscribers: hiraditya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D71474
Added:
llvm/test/TableGen/ifstmt.td
Modified:
llvm/docs/TableGen/LangRef.rst
llvm/lib/TableGen/TGLexer.cpp
llvm/lib/TableGen/TGLexer.h
llvm/lib/TableGen/TGParser.cpp
llvm/lib/TableGen/TGParser.h
llvm/test/TableGen/defvar.td
Removed:
################################################################################
diff --git a/llvm/docs/TableGen/LangRef.rst b/llvm/docs/TableGen/LangRef.rst
index 7cbe6d996430..74af1ee659f5 100644
--- a/llvm/docs/TableGen/LangRef.rst
+++ b/llvm/docs/TableGen/LangRef.rst
@@ -89,6 +89,7 @@ TableGen also has the following keywords::
bit bits class code dag
def foreach defm field in
int let list multiclass string
+ if then else
TableGen also has "bang operators" which have a
wide variety of meanings:
@@ -125,7 +126,7 @@ TableGen's top-level production consists of "objects".
.. productionlist::
TableGenFile: `Object`*
Object: `Class` | `Def` | `Defm` | `Defset` | `Defvar` | `Let` |
- `MultiClass` | `Foreach`
+ `MultiClass` | `Foreach` | `If`
``class``\es
------------
@@ -466,6 +467,24 @@ iterated value.
Note that the productions involving RangeList and RangePiece have precedence
over the more generic value parsing based on the first token.
+``if``
+------
+
+.. productionlist::
+ If: "if" `Value` "then" `IfBody`
+ :| "if" `Value` "then" `IfBody` "else" `IfBody`
+ IfBody: "{" `Object`* "}" | `Object`
+
+The value expression after the ``if`` keyword is evaluated, and if it evaluates
+to true (in the same sense used by the ``!if`` operator), then the object
+definition(s) after the ``then`` keyword are executed. Otherwise, if there is
+an ``else`` keyword, the definition(s) after the ``else`` are executed instead.
+
+Because the braces around the ``then`` clause are optional, this grammar rule
+has the usual ambiguity about dangling ``else`` clauses, and it is resolved in
+the usual way: in a case like ``if v1 then if v2 then {...} else {...}``, the
+``else`` binds to the inner ``if`` rather than the outer one.
+
Top-Level ``let``
-----------------
diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp
index c160704b6e77..1a3f5a7392d5 100644
--- a/llvm/lib/TableGen/TGLexer.cpp
+++ b/llvm/lib/TableGen/TGLexer.cpp
@@ -351,6 +351,9 @@ tgtok::TokKind TGLexer::LexIdentifier() {
.Case("let", tgtok::Let)
.Case("in", tgtok::In)
.Case("defvar", tgtok::Defvar)
+ .Case("if", tgtok::If)
+ .Case("then", tgtok::Then)
+ .Case("else", tgtok::ElseKW)
.Default(tgtok::Id);
if (Kind == tgtok::Id)
diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h
index 04c72900b0b4..6d10af348674 100644
--- a/llvm/lib/TableGen/TGLexer.h
+++ b/llvm/lib/TableGen/TGLexer.h
@@ -44,9 +44,10 @@ namespace tgtok {
equal, question, // = ?
paste, // #
- // Keywords.
+ // 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,
+ MultiClass, String, Defset, Defvar, If, Then, ElseKW,
// !keywords.
XConcat, XADD, XMUL, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XListSplat,
diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index 34d993c518ac..01cc1af34ab6 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -391,9 +391,11 @@ bool TGParser::resolve(const ForeachLoop &Loop, SubstStack &Substs,
bool Error = false;
for (auto Elt : *LI) {
- Substs.emplace_back(Loop.IterVar->getNameInit(), Elt);
+ if (Loop.IterVar)
+ Substs.emplace_back(Loop.IterVar->getNameInit(), Elt);
Error = resolve(Loop.Entries, Substs, Final, Dest);
- Substs.pop_back();
+ if (Loop.IterVar)
+ Substs.pop_back();
if (Error)
break;
}
@@ -482,7 +484,7 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
static bool isObjectStart(tgtok::TokKind K) {
return K == tgtok::Class || K == tgtok::Def || K == tgtok::Defm ||
K == tgtok::Let || K == tgtok::MultiClass || K == tgtok::Foreach ||
- K == tgtok::Defset || K == tgtok::Defvar;
+ K == tgtok::Defset || K == tgtok::Defvar || K == tgtok::If;
}
/// ParseObjectName - If a valid object name is specified, return it. If no
@@ -875,9 +877,11 @@ Init *TGParser::ParseIDValue(Record *CurRec, StringInit *Name, SMLoc NameLoc,
// If this is in a foreach loop, make sure it's not a loop iterator
for (const auto &L : Loops) {
- VarInit *IterVar = dyn_cast<VarInit>(L->IterVar);
- if (IterVar && IterVar->getNameInit() == Name)
- return IterVar;
+ if (L->IterVar) {
+ VarInit *IterVar = dyn_cast<VarInit>(L->IterVar);
+ if (IterVar && IterVar->getNameInit() == Name)
+ return IterVar;
+ }
}
if (Mode == ParseNameMode)
@@ -2909,6 +2913,115 @@ bool TGParser::ParseForeach(MultiClass *CurMultiClass) {
return addEntry(std::move(Loop));
}
+/// ParseIf - Parse an if statement.
+///
+/// If ::= IF Value THEN IfBody
+/// If ::= IF Value THEN IfBody ELSE IfBody
+///
+bool TGParser::ParseIf(MultiClass *CurMultiClass) {
+ SMLoc Loc = Lex.getLoc();
+ assert(Lex.getCode() == tgtok::If && "Unknown tok");
+ Lex.Lex(); // Eat the 'if' token.
+
+ // Make a temporary object to record items associated with the for
+ // loop.
+ Init *Condition = ParseValue(nullptr);
+ if (!Condition)
+ return true;
+
+ if (Lex.getCode() != tgtok::Then)
+ return TokError("Unknown tok");
+ Lex.Lex(); // Eat the 'then'
+
+ // We have to be able to save if statements to execute later, and they have
+ // to live on the same stack as foreach loops. The simplest implementation
+ // technique is to convert each 'then' or 'else' clause *into* a foreach
+ // loop, over a list of length 0 or 1 depending on the condition, and with no
+ // iteration variable being assigned.
+
+ ListInit *EmptyList = ListInit::get({}, BitRecTy::get());
+ ListInit *SingletonList = ListInit::get({BitInit::get(1)}, BitRecTy::get());
+ RecTy *BitListTy = ListRecTy::get(BitRecTy::get());
+
+ // The foreach containing the then-clause selects SingletonList if
+ // the condition is true.
+ Init *ThenClauseList =
+ TernOpInit::get(TernOpInit::IF, Condition, SingletonList, EmptyList,
+ BitListTy)
+ ->Fold(nullptr);
+ Loops.push_back(std::make_unique<ForeachLoop>(Loc, nullptr, ThenClauseList));
+
+ if (ParseIfBody(CurMultiClass, "then"))
+ return true;
+
+ std::unique_ptr<ForeachLoop> Loop = std::move(Loops.back());
+ Loops.pop_back();
+
+ if (addEntry(std::move(Loop)))
+ return true;
+
+ // Now look for an optional else clause. The if-else syntax has the usual
+ // dangling-else ambiguity, and by greedily matching an else here if we can,
+ // we implement the usual resolution of pairing with the innermost unmatched
+ // if.
+ if (Lex.getCode() == tgtok::ElseKW) {
+ Lex.Lex(); // Eat the 'else'
+
+ // The foreach containing the else-clause uses the same pair of lists as
+ // above, but this time, selects SingletonList if the condition is *false*.
+ Init *ElseClauseList =
+ TernOpInit::get(TernOpInit::IF, Condition, EmptyList, SingletonList,
+ BitListTy)
+ ->Fold(nullptr);
+ Loops.push_back(
+ std::make_unique<ForeachLoop>(Loc, nullptr, ElseClauseList));
+
+ if (ParseIfBody(CurMultiClass, "else"))
+ return true;
+
+ Loop = std::move(Loops.back());
+ Loops.pop_back();
+
+ if (addEntry(std::move(Loop)))
+ return true;
+ }
+
+ return false;
+}
+
+/// ParseIfBody - Parse the then-clause or else-clause of an if statement.
+///
+/// IfBody ::= Object
+/// IfBody ::= '{' ObjectList '}'
+///
+bool TGParser::ParseIfBody(MultiClass *CurMultiClass, StringRef Kind) {
+ TGLocalVarScope *BodyScope = PushLocalScope();
+
+ if (Lex.getCode() != tgtok::l_brace) {
+ // A single object.
+ if (ParseObject(CurMultiClass))
+ return true;
+ } else {
+ SMLoc BraceLoc = Lex.getLoc();
+ // A braced block.
+ Lex.Lex(); // eat the '{'.
+
+ // Parse the object list.
+ if (ParseObjectList(CurMultiClass))
+ return true;
+
+ if (Lex.getCode() != tgtok::r_brace) {
+ TokError("expected '}' at end of '" + Kind + "' clause");
+ return Error(BraceLoc, "to match this '{'");
+ }
+
+ Lex.Lex(); // Eat the }
+ }
+
+ PopLocalScope(BodyScope);
+ return false;
+}
+
/// ParseClass - Parse a tblgen class definition.
///
/// ClassInst ::= CLASS ID TemplateArgList? ObjectBody
@@ -3118,13 +3231,14 @@ bool TGParser::ParseMultiClass() {
while (Lex.getCode() != tgtok::r_brace) {
switch (Lex.getCode()) {
default:
- return TokError("expected 'let', 'def', 'defm', 'defvar' or 'foreach' "
- "in multiclass body");
+ return TokError("expected 'let', 'def', 'defm', 'defvar', 'foreach' "
+ "or 'if' in multiclass body");
case tgtok::Let:
case tgtok::Def:
case tgtok::Defm:
case tgtok::Defvar:
case tgtok::Foreach:
+ case tgtok::If:
if (ParseObject(CurMultiClass))
return true;
break;
@@ -3279,11 +3393,12 @@ bool TGParser::ParseDefm(MultiClass *CurMultiClass) {
bool TGParser::ParseObject(MultiClass *MC) {
switch (Lex.getCode()) {
default:
- return TokError("Expected class, def, defm, defset, multiclass, let or "
- "foreach");
+ return TokError("Expected class, def, defm, defset, multiclass, let, "
+ "foreach or if");
case tgtok::Let: return ParseTopLevelLet(MC);
case tgtok::Def: return ParseDef(MC);
case tgtok::Foreach: return ParseForeach(MC);
+ case tgtok::If: return ParseIf(MC);
case tgtok::Defm: return ParseDefm(MC);
case tgtok::Defset:
if (MC)
diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h
index edcac1e8fa76..c66c79771298 100644
--- a/llvm/lib/TableGen/TGParser.h
+++ b/llvm/lib/TableGen/TGParser.h
@@ -56,6 +56,10 @@ namespace llvm {
/// ForeachLoop - Record the iteration state associated with a for loop.
/// This is used to instantiate items in the loop body.
+ ///
+ /// IterVar is allowed to be null, in which case no iteration variable is
+ /// defined in the loop at all. (This happens when a ForeachLoop is
+ /// constructed by desugaring an if statement.)
struct ForeachLoop {
SMLoc Loc;
VarInit *IterVar;
@@ -220,6 +224,8 @@ class TGParser {
bool ParseDefset();
bool ParseDefvar();
bool ParseForeach(MultiClass *CurMultiClass);
+ bool ParseIf(MultiClass *CurMultiClass);
+ bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind);
bool ParseTopLevelLet(MultiClass *CurMultiClass);
void ParseLetList(SmallVectorImpl<LetRecord> &Result);
diff --git a/llvm/test/TableGen/defvar.td b/llvm/test/TableGen/defvar.td
index 4cb114b1c02c..4c39c37c8a3b 100644
--- a/llvm/test/TableGen/defvar.td
+++ b/llvm/test/TableGen/defvar.td
@@ -95,6 +95,8 @@ defvar firstStr = "now define this at the top level and still expect no error";
// CHECK-NEXT: int var = 2;
// CHECK: def shadowInner22 {
// CHECK-NEXT: int var = 2;
+// CHECK: def shadowInnerIf1 {
+// CHECK-NEXT: int var = 3;
// CHECK: def shadowOuterAbove1 {
// CHECK-NEXT: int var = 1;
// CHECK: def shadowOuterAbove2 {
@@ -103,6 +105,10 @@ defvar firstStr = "now define this at the top level and still expect no error";
// CHECK-NEXT: int var = 1;
// CHECK: def shadowOuterBelowForeach2 {
// CHECK-NEXT: int var = 1;
+// CHECK: def shadowOuterBelowIf1 {
+// CHECK-NEXT: int var = 1;
+// CHECK: def shadowOuterBelowIf2 {
+// CHECK-NEXT: int var = 1;
foreach first = [ 1, 2 ] in {
defvar shadowedVariable = 1;
@@ -117,6 +123,15 @@ foreach first = [ 1, 2 ] in {
// Now the outer variable is back in scope.
def shadowOuterBelowForeach # first { int var = shadowedVariable; }
+
+ // An if statement also opens a new scope.
+ if !eq(first, 1) then {
+ defvar shadowedVariable = 3;
+ def shadowInnerIf # first { int var = shadowedVariable; }
+ }
+
+ // Now the outer variable is back in scope again.
+ def shadowOuterBelowIf # first { int var = shadowedVariable; }
}
// Test that a top-level let statement also makes a variable scope (on the
diff --git a/llvm/test/TableGen/ifstmt.td b/llvm/test/TableGen/ifstmt.td
new file mode 100644
index 000000000000..22354310e7ba
--- /dev/null
+++ b/llvm/test/TableGen/ifstmt.td
@@ -0,0 +1,92 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
+// RUN: not llvm-tblgen -DERROR2 %s 2>&1 | FileCheck --check-prefix=ERROR2 %s
+
+// Test at top level.
+
+// CHECK-NOT: def aNo
+// CHECK: def aYes
+if 0 then def aNo;
+if 1 then def aYes;
+
+// Test inside a foreach, with condition based on the iteration variable.
+
+// CHECK: def bNotThree1
+// CHECK: def bNotThree2
+// CHECK: def bNotThree4
+// CHECK: def bThree3
+foreach i = 1-4 in {
+ if !eq(i, 3) then {
+ def "bThree" # i;
+ } else {
+ def "bNotThree" # i;
+ }
+}
+
+// Test inside a multiclass, with condition based on a multiclass parameter.
+
+multiclass Multi<int i> {
+ def Unconditional;
+
+ if !eq(i, 2) then
+ def Cond;
+
+ if !ge(i, 3) then
+ def ThenRec;
+ else
+ def ElseRec;
+}
+
+// CHECK-NOT: def c1Cond
+// CHECK: def c1ElseRec
+// CHECK-NOT: def c1ThenRec
+// CHECK: def c1Unconditional
+defm c1: Multi<1>;
+
+// CHECK: def c2Cond
+// CHECK: def c2ElseRec
+// CHECK-NOT: def c2ThenRec
+// CHECK: def c2Unconditional
+defm c2: Multi<2>;
+
+// CHECK-NOT: def c3Cond
+// CHECK-NOT: def c3ElseRec
+// CHECK: def c3ThenRec
+// CHECK: def c3Unconditional
+defm c3: Multi<3>;
+
+// Test resolution of the dangling-else ambiguity.
+
+// CHECK: def dThenElse00
+// CHECK-NOT: def dThenElse1
+// CHECK-NOT: def dThenElse11
+// CHECK: def dThenThen01
+foreach i = 0-1 in
+ foreach j = 0-1 in
+ if !eq(i,0) then
+ if !eq(j,1) then
+ def "dThenThen"#i#j;
+ else // binds to the inner if, not the outer one
+ def "dThenElse"#i#j;
+
+// Error tests: ensure you can't put an if inside a def or class.
+
+#ifdef ERROR1
+def baddef {
+ int x = 3;
+ // ERROR1: [[@LINE+1]]:3: error: Unknown token when expecting a type
+ if 1 then {
+ int y = 4;
+ }
+}
+#endif
+
+#ifdef ERROR2
+class badclass<int i> {
+ int x = 3;
+ // ERROR2: [[@LINE+1]]:3: error: Unknown token when expecting a type
+ if !eq(i, 5) then {
+ int y = 4;
+ }
+}
+#endif
More information about the llvm-commits
mailing list