[llvm] a5aaec8 - [TableGen] Add support for the 'assert' statement in multiclasses
Paul C. Anagnostopoulos via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 19 06:02:18 PDT 2021
Author: Paul C. Anagnostopoulos
Date: 2021-04-19T09:01:42-04:00
New Revision: a5aaec8f4e893baf31f2fe2f1a83b4c095738d66
URL: https://github.com/llvm/llvm-project/commit/a5aaec8f4e893baf31f2fe2f1a83b4c095738d66
DIFF: https://github.com/llvm/llvm-project/commit/a5aaec8f4e893baf31f2fe2f1a83b4c095738d66.diff
LOG: [TableGen] Add support for the 'assert' statement in multiclasses
This is step 3 of adding the 'assert' statement.
Differential Revision: https://reviews.llvm.org/D99751
Added:
Modified:
llvm/docs/TableGen/ProgRef.rst
llvm/include/llvm/TableGen/Record.h
llvm/lib/TableGen/Record.cpp
llvm/lib/TableGen/TGParser.cpp
llvm/lib/TableGen/TGParser.h
llvm/test/TableGen/assert.td
Removed:
################################################################################
diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index c316c33a37f46..c9bee3fa31bb0 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -1282,7 +1282,9 @@ placement.
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
index 177679e317220..3c8ad127b78ad 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -1470,6 +1470,10 @@ inline raw_ostream &operator<<(raw_ostream &OS, const RecordVal &RV) {
}
class Record {
+public:
+ using AssertionTuple = std::tuple<SMLoc, Init *, Init *>;
+
+private:
static unsigned LastID;
Init *Name;
@@ -1479,7 +1483,7 @@ class Record {
SmallVector<Init *, 0> TemplateArgs;
SmallVector<RecordVal, 0> Values;
// Vector of [source location, condition Init, message Init].
- SmallVector<std::tuple<SMLoc, Init *, Init *>, 0> Assertions;
+ SmallVector<AssertionTuple, 0> 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 @@ class Record {
ArrayRef<RecordVal> getValues() const { return Values; }
- ArrayRef<std::tuple<SMLoc, Init *, Init *>> getAssertions() const {
- return Assertions;
- }
+ ArrayRef<AssertionTuple> getAssertions() const { return Assertions; }
ArrayRef<std::pair<Record *, SMRange>> getSuperClasses() const {
return SuperClasses;
@@ -1621,7 +1623,7 @@ class Record {
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
index 1c36b64aee1ed..501654e52b6b0 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -1846,7 +1846,7 @@ DefInit *VarDefInit::instantiate() {
Records.addDef(std::move(NewRecOwner));
// Check the assertions.
- NewRec->checkAssertions();
+ NewRec->checkRecordAssertions();
Def = DefInit::get(NewRec);
}
@@ -2629,7 +2629,7 @@ DagInit *Record::getValueAsDag(StringRef FieldName) const {
// 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.cpp b/llvm/lib/TableGen/TGParser.cpp
index fa255ac27344f..c0bf4b705cbd3 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -339,27 +339,38 @@ bool TGParser::AddSubMultiClass(MultiClass *CurMC,
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 @@ bool TGParser::resolve(const std::vector<RecordsEntry> &Source,
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<Record::AssertionTuple> Tuple =
+ std::make_unique<Record::AssertionTuple>(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<Record>(*E.Rec);
if (Loc)
@@ -459,7 +488,7 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
}
// 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 @@ bool TGParser::ApplyLetStack(Record *CurRec) {
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 @@ bool TGParser::ParseObjectBody(Record *CurRec) {
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 @@ bool TGParser::ParseAssert(MultiClass *CurMultiClass, Record *CurRec) {
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<Record::AssertionTuple> Tuple =
+ std::make_unique<Record::AssertionTuple>(ConditionLoc, Condition, Message);
+ addEntry(std::move(Tuple));
}
return false;
@@ -3330,8 +3364,12 @@ bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) {
/// 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 @@ bool TGParser::ParseMultiClass() {
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 @@ bool TGParser::ParseObject(MultiClass *MC) {
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/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h
index 6514d4276a7f6..254ce3c90980e 100644
--- a/llvm/lib/TableGen/TGParser.h
+++ b/llvm/lib/TableGen/TGParser.h
@@ -36,10 +36,12 @@ namespace llvm {
}
};
- /// 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<Record> Rec;
std::unique_ptr<ForeachLoop> Loop;
+ std::unique_ptr<Record::AssertionTuple> Assertion;
void dump() const;
@@ -47,6 +49,8 @@ namespace llvm {
RecordsEntry(std::unique_ptr<Record> Rec) : Rec(std::move(Rec)) {}
RecordsEntry(std::unique_ptr<ForeachLoop> Loop)
: Loop(std::move(Loop)) {}
+ RecordsEntry(std::unique_ptr<Record::AssertionTuple> Assertion)
+ : Assertion(std::move(Assertion)) {}
};
/// ForeachLoop - Record the iteration state associated with a for loop.
@@ -222,7 +226,7 @@ class TGParser {
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<LetRecord> &Result);
diff --git a/llvm/test/TableGen/assert.td b/llvm/test/TableGen/assert.td
index 1c436301d85d0..fb8f7beccc89c 100644
--- a/llvm/test/TableGen/assert.td
+++ b/llvm/test/TableGen/assert.td
@@ -39,6 +39,15 @@ class Cube<int n> {
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 @@ def Rec32 {
// 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<string id, int seq> {
+ 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<string phr> {
+ assert !eq(!substr(phr, 0, 6), "secret"), "MC2 phrase must be secret: " # phr;
+
+ def _mc2 {
+ string phrase = phr;
+ }
+}
+
+multiclass MC3<string phr> {
+ defm _mc3 : MC2<phr>;
+}
+
+defm Rec43 : MC3<"secrex code">;
+
+// CHECK: assertion failed
+// CHECK: note: MC2 phrase must be secret: xecret code
+
+multiclass MC4<string phr> : MC2<phr> {
+ def _def;
+}
+
+defm Rec44 : MC4<"xecret code">;
More information about the llvm-commits
mailing list