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 @@ -1282,7 +1282,9 @@ the subclasses and records that inherit from the class. The assertions are then checked when the records are completely built. -* In a multiclass definition, ... [this placement is not yet available] +* In a multiclass definition, the assertions are saved with the other + components of the multiclass and then checked each time the multiclass + is instantiated with ``defm``. Using assertions in TableGen files can simplify record checking in TableGen backends. Here is an example of an ``assert`` in two class definitions. diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -1470,6 +1470,10 @@ } class Record { +public: + using AssertionTuple = std::tuple; + +private: static unsigned LastID; Init *Name; @@ -1479,7 +1483,7 @@ SmallVector TemplateArgs; SmallVector Values; // Vector of [source location, condition Init, message Init]. - SmallVector, 0> Assertions; + SmallVector Assertions; // All superclasses in the inheritance forest in post-order (yes, it // must be a forest; diamond-shaped inheritance is not allowed). @@ -1554,9 +1558,7 @@ ArrayRef getValues() const { return Values; } - ArrayRef> getAssertions() const { - return Assertions; - } + ArrayRef getAssertions() const { return Assertions; } ArrayRef> getSuperClasses() const { return SuperClasses; @@ -1621,7 +1623,7 @@ Assertions.append(Rec->Assertions); } - void checkAssertions(); + void checkRecordAssertions(); bool isSubClassOf(const Record *R) const { for (const auto &SCPair : SuperClasses) diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -1846,7 +1846,7 @@ Records.addDef(std::move(NewRecOwner)); // Check the assertions. - NewRec->checkAssertions(); + NewRec->checkRecordAssertions(); Def = DefInit::get(NewRec); } @@ -2629,7 +2629,7 @@ // and message, then call CheckAssert(). // Note: The condition and message are probably already resolved, // but resolving again allows calls before records are resolved. -void Record::checkAssertions() { +void Record::checkRecordAssertions() { RecordResolver R(*this); R.setFinal(true); 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 @@ -36,10 +36,12 @@ } }; - /// RecordsEntry - Can be either a record or a foreach loop. + /// RecordsEntry - Holds exactly one of a Record, ForeachLoop, or + /// assertion tuple. struct RecordsEntry { std::unique_ptr Rec; std::unique_ptr Loop; + std::unique_ptr Assertion; void dump() const; @@ -47,6 +49,8 @@ RecordsEntry(std::unique_ptr Rec) : Rec(std::move(Rec)) {} RecordsEntry(std::unique_ptr Loop) : Loop(std::move(Loop)) {} + RecordsEntry(std::unique_ptr Assertion) + : Assertion(std::move(Assertion)) {} }; /// ForeachLoop - Record the iteration state associated with a for loop. @@ -222,7 +226,7 @@ bool ParseForeach(MultiClass *CurMultiClass); bool ParseIf(MultiClass *CurMultiClass); bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind); - bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec); + bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec = nullptr); 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 @@ -339,27 +339,38 @@ return resolve(SMC->Entries, TemplateArgs, false, &CurMC->Entries); } -/// Add a record or foreach loop to the current context (global record keeper, -/// current inner-most foreach loop, or multiclass). +/// Add a record, foreach loop, or assertion to the current context. bool TGParser::addEntry(RecordsEntry E) { - assert(!E.Rec || !E.Loop); + assert((!!E.Rec + !!E.Loop + !!E.Assertion) == 1 && + "RecordsEntry has invalid number of items"); + // If we are parsing a loop, add it to the loop's entries. if (!Loops.empty()) { Loops.back()->Entries.push_back(std::move(E)); return false; } + // If it is a loop, then resolve and perform the loop. if (E.Loop) { SubstStack Stack; return resolve(*E.Loop, Stack, CurMultiClass == nullptr, CurMultiClass ? &CurMultiClass->Entries : nullptr); } + // If we are parsing a multiclass, add it to the multiclass's entries. if (CurMultiClass) { CurMultiClass->Entries.push_back(std::move(E)); return false; } + // If it is an assertion, then it's a top-level one, so check it. + if (E.Assertion) { + CheckAssert(std::get<0>(*E.Assertion), std::get<1>(*E.Assertion), + std::get<2>(*E.Assertion)); + return false; + } + + // It must be a record, so finish it off. return addDefOne(std::move(E.Rec)); } @@ -414,6 +425,24 @@ for (auto &E : Source) { if (E.Loop) { Error = resolve(*E.Loop, Substs, Final, Dest); + + } else if (E.Assertion) { + MapResolver R; + for (const auto &S : Substs) + R.set(S.first, S.second); + Init *Condition = std::get<1>(*E.Assertion)->resolveReferences(R); + Init *Message = std::get<2>(*E.Assertion)->resolveReferences(R); + + if (Dest) { + std::unique_ptr Tuple = + std::make_unique(std::get<0>(*E.Assertion), + std::move(Condition), + std::move(Message)); + Dest->push_back(std::move(Tuple)); + } else { + CheckAssert(std::get<0>(*E.Assertion), Condition, Message); + } + } else { auto Rec = std::make_unique(*E.Rec); if (Loc) @@ -459,7 +488,7 @@ } // Check the assertions. - Rec->checkAssertions(); + Rec->checkRecordAssertions(); // If ObjectBody has template arguments, it's an error. assert(Rec->getTemplateArgs().empty() && "How'd this get template args?"); @@ -2842,10 +2871,15 @@ return false; } +/// Apply the current let bindings to the RecordsEntry. bool TGParser::ApplyLetStack(RecordsEntry &Entry) { if (Entry.Rec) return ApplyLetStack(Entry.Rec.get()); + // Let bindings are not applied to assertions. + if (Entry.Assertion) + return false; + for (auto &E : Entry.Loop->Entries) { if (ApplyLetStack(E)) return true; @@ -2889,8 +2923,8 @@ return ParseBody(CurRec); } -/// ParseDef - Parse and return a top level or multiclass def, return the record -/// corresponding to it. This returns null on error. +/// ParseDef - Parse and return a top level or multiclass record definition. +/// Return false if okay, true if error. /// /// DefInst ::= DEF ObjectName ObjectBody /// @@ -3184,12 +3218,12 @@ if (!consume(tgtok::semi)) return TokError("expected ';'"); - if (CurMultiClass) { - assert(false && "assert in multiclass not yet supported"); - } else if (CurRec) { + if (CurRec) { CurRec->addAssertion(ConditionLoc, Condition, Message); - } else { // at top level - CheckAssert(ConditionLoc, Condition, Message); + } else { + std::unique_ptr Tuple = + std::make_unique(ConditionLoc, Condition, Message); + addEntry(std::move(Tuple)); } return false; @@ -3330,8 +3364,12 @@ /// MultiClassObject ::= DefInst /// MultiClassObject ::= MultiClassInst /// MultiClassObject ::= DefMInst +/// MultiClassObject ::= Defvar +/// MultiClassObject ::= Foreach +/// MultiClassObject ::= If /// MultiClassObject ::= LETCommand '{' ObjectList '}' /// MultiClassObject ::= LETCommand Object +/// MultiClassObject ::= Assert /// bool TGParser::ParseMultiClass() { assert(Lex.getCode() == tgtok::MultiClass && "Unexpected token"); @@ -3396,9 +3434,8 @@ default: return TokError("expected 'assert', 'def', 'defm', 'defvar', " "'foreach', 'if', or 'let' in multiclass body"); - case tgtok::Assert: - return TokError("an assert statement in a multiclass is not yet supported"); + case tgtok::Assert: case tgtok::Def: case tgtok::Defm: case tgtok::Defvar: @@ -3564,7 +3601,7 @@ default: return TokError( "Expected assert, class, def, defm, defset, foreach, if, or let"); - case tgtok::Assert: return ParseAssert(MC, nullptr); + case tgtok::Assert: return ParseAssert(MC); case tgtok::Def: return ParseDef(MC); case tgtok::Defm: return ParseDefm(MC); case tgtok::Defvar: return ParseDefvar(); diff --git a/llvm/test/TableGen/assert.td b/llvm/test/TableGen/assert.td --- a/llvm/test/TableGen/assert.td +++ b/llvm/test/TableGen/assert.td @@ -39,6 +39,15 @@ assert !eq(Cube<9>.result, 81), "cube of 9 should be 729"; +// CHECK: assertion failed +// CHECK: note: foreach i cannot be 2 +// CHECK-NOT: note: foreach i cannot be 2 + +foreach i = 1...3 in { + assert !ne(i, 2), "foreach i cannot be 2"; + def bar_ # i; +} + // Test the assert statement in a record definition. // CHECK: assertion failed @@ -136,3 +145,47 @@ // Test the assert statement in a multiclass. +// CHECK: assertion failed +// CHECK: note: MC1 id string is too long +// CHECK: assertion failed +// CHECK: note: MC1 seq is too high + +multiclass MC1 { + assert !le(!size(id), 5), "MC1 id string is too long"; + assert !le(seq, 999999), "MC1 seq is too high"; + + def _mc1 { + string ID = id; + int Seq = seq; + } +} + +defm Rec40 : MC1<"ILISP", 999>; +defm Rec41 : MC1<"ILISPX", 999>; +defm Rec42 : MC1<"ILISP", 999999999>; + +// CHECK: assertion failed +// CHECK: note: MC2 phrase must be secret: secrex code + +multiclass MC2 { + assert !eq(!substr(phr, 0, 6), "secret"), "MC2 phrase must be secret: " # phr; + + def _mc2 { + string phrase = phr; + } +} + +multiclass MC3 { + defm _mc3 : MC2; +} + +defm Rec43 : MC3<"secrex code">; + +// CHECK: assertion failed +// CHECK: note: MC2 phrase must be secret: xecret code + +multiclass MC4 : MC2 { + def _def; +} + +defm Rec44 : MC4<"xecret code">;