diff --git a/llvm/docs/TableGen/LangRef.rst b/llvm/docs/TableGen/LangRef.rst --- a/llvm/docs/TableGen/LangRef.rst +++ b/llvm/docs/TableGen/LangRef.rst @@ -89,6 +89,7 @@ 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 @@ .. productionlist:: TableGenFile: `Object`* Object: `Class` | `Def` | `Defm` | `Defset` | `Defvar` | `Let` | - `MultiClass` | `Foreach` + `MultiClass` | `Foreach` | `If` ``class``\es ------------ @@ -466,6 +467,24 @@ 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.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -44,9 +44,10 @@ 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/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -351,6 +351,9 @@ .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/TGParser.h b/llvm/lib/TableGen/TGParser.h --- a/llvm/lib/TableGen/TGParser.h +++ b/llvm/lib/TableGen/TGParser.h @@ -56,6 +56,10 @@ /// 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 @@ 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 &Result); 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 @@ -391,9 +391,11 @@ 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 @@ 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 @@ // If this is in a foreach loop, make sure it's not a loop iterator for (const auto &L : Loops) { - VarInit *IterVar = dyn_cast(L->IterVar); - if (IterVar && IterVar->getNameInit() == Name) - return IterVar; + if (L->IterVar) { + VarInit *IterVar = dyn_cast(L->IterVar); + if (IterVar && IterVar->getNameInit() == Name) + return IterVar; + } } if (Mode == ParseNameMode) @@ -2909,6 +2913,115 @@ 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(Loc, nullptr, ThenClauseList)); + + if (ParseIfBody(CurMultiClass, "then")) + return true; + + std::unique_ptr 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(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 @@ 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::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/test/TableGen/defvar.td b/llvm/test/TableGen/defvar.td --- a/llvm/test/TableGen/defvar.td +++ b/llvm/test/TableGen/defvar.td @@ -95,6 +95,8 @@ // 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 @@ // 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 @@ // 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 --- /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 { + 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 x = 3; + // ERROR2: [[@LINE+1]]:3: error: Unknown token when expecting a type + if !eq(i, 5) then { + int y = 4; + } +} +#endif