[llvm] 5f473a0 - [TableGen] Add support for the 'assert' statement in class definitions.
Paul C. Anagnostopoulos via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 29 06:20:41 PDT 2021
Author: Paul C. Anagnostopoulos
Date: 2021-03-29T09:20:29-04:00
New Revision: 5f473a04af91d713deb71aeab1c783d9420d6fcf
URL: https://github.com/llvm/llvm-project/commit/5f473a04af91d713deb71aeab1c783d9420d6fcf
DIFF: https://github.com/llvm/llvm-project/commit/5f473a04af91d713deb71aeab1c783d9420d6fcf.diff
LOG: [TableGen] Add support for the 'assert' statement in class definitions.
Differential Revision: https://reviews.llvm.org/D99275
Added:
Modified:
llvm/docs/TableGen/ProgRef.rst
llvm/include/llvm/TableGen/Error.h
llvm/include/llvm/TableGen/Record.h
llvm/lib/TableGen/Error.cpp
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 9799e29a63e67..3da07814851e7 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -1272,12 +1272,30 @@ placement.
checked after the record is completely built.
* In a class definition, the assertions are saved and inherited by all
- the record definitions that inherit from the class. The assertions are
- then checked when the records are completely built. [this placement is not
- yet available]
+ 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]
+Using assertions in TableGen files can simplify record checking in TableGen
+backends. Here is an example of an ``assert`` in two class definitions.
+
+.. code-block:: text
+
+ class PersonName<string name> {
+ assert !le(!size(name), 32), "person name is too long: " # name;
+ string Name = name;
+ }
+
+ class Person<string name, int age> : PersonName<name> {
+ assert !and(!ge(age, 1), !le(age, 120)), "person age is invalid: " # age;
+ int Age = age;
+ }
+
+ def Rec20 : Person<"Donald Knuth", 60> {
+ ...
+ }
+
Additional Details
==================
diff --git a/llvm/include/llvm/TableGen/Error.h b/llvm/include/llvm/TableGen/Error.h
index f63b50ad786c2..a0e23aca211e8 100644
--- a/llvm/include/llvm/TableGen/Error.h
+++ b/llvm/include/llvm/TableGen/Error.h
@@ -48,6 +48,8 @@ LLVM_ATTRIBUTE_NORETURN void PrintFatalError(const Record *Rec,
LLVM_ATTRIBUTE_NORETURN void PrintFatalError(const RecordVal *RecVal,
const Twine &Msg);
+void CheckAssert(SMLoc Loc, Init *Condition, Init *Message);
+
extern SourceMgr SrcMgr;
extern unsigned ErrorsPrinted;
diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index ea47d6713026b..9432de6738613 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -1616,6 +1616,12 @@ class Record {
Assertions.push_back(std::make_tuple(Loc, Condition, Message));
}
+ void appendAssertions(const Record *Rec) {
+ Assertions.append(Rec->Assertions);
+ }
+
+ void checkAssertions();
+
bool isSubClassOf(const Record *R) const {
for (const auto &SCPair : SuperClasses)
if (SCPair.first == R)
diff --git a/llvm/lib/TableGen/Error.cpp b/llvm/lib/TableGen/Error.cpp
index eed4de67942ad..6104573b4b25b 100644
--- a/llvm/lib/TableGen/Error.cpp
+++ b/llvm/lib/TableGen/Error.cpp
@@ -154,4 +154,20 @@ void PrintFatalError(const RecordVal *RecVal, const Twine &Msg) {
std::exit(1);
}
+// Check an assertion: Obtain the condition value and be sure it is true.
+// If not, print a nonfatal error along with the message.
+void CheckAssert(SMLoc Loc, Init *Condition, Init *Message) {
+ auto *CondValue = dyn_cast_or_null<IntInit>(
+ Condition->convertInitializerTo(IntRecTy::get()));
+ if (!CondValue)
+ PrintError(Loc, "assert condition must of type bit, bits, or int.");
+ else if (!CondValue->getValue()) {
+ PrintError(Loc, "assertion failed");
+ if (auto *MessageInit = dyn_cast<StringInit>(Message))
+ PrintNote(MessageInit->getValue());
+ else
+ PrintNote("(assert message is not a string)");
+ }
+}
+
} // end namespace llvm
diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index 3172d711e7f63..e5b7065dd2d7f 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -1800,6 +1800,9 @@ DefInit *VarDefInit::instantiate() {
for (const RecordVal &Val : Class->getValues())
NewRec->addValue(Val);
+ // Copy assertions from class to instance.
+ NewRec->appendAssertions(Class);
+
// Substitute and resolve template arguments
ArrayRef<Init *> TArgs = Class->getTemplateArgs();
MapResolver R(NewRec);
@@ -1828,6 +1831,9 @@ DefInit *VarDefInit::instantiate() {
NewRec->resolveReferences();
Records.addDef(std::move(NewRecOwner));
+ // Check the assertions.
+ NewRec->checkAssertions();
+
Def = DefInit::get(NewRec);
}
@@ -2334,6 +2340,8 @@ void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) {
// Re-register with RecordKeeper.
setName(NewName);
}
+
+ // Resolve the field values.
for (RecordVal &Value : Values) {
if (SkipVal == &Value) // Skip resolve the same field as the given one
continue;
@@ -2354,6 +2362,14 @@ void Record::resolveReferences(Resolver &R, const RecordVal *SkipVal) {
}
}
}
+
+ // Resolve the assertion expressions.
+ for (auto &Assertion : Assertions) {
+ Init *Value = std::get<1>(Assertion)->resolveReferences(R);
+ std::get<1>(Assertion) = Value;
+ Value = std::get<2>(Assertion)->resolveReferences(R);
+ std::get<2>(Assertion) = Value;
+ }
}
void Record::resolveReferences(Init *NewName) {
@@ -2595,6 +2611,21 @@ DagInit *Record::getValueAsDag(StringRef FieldName) const {
FieldName + "' does not have a dag initializer!");
}
+// Check all record assertions: For each one, resolve the condition
+// 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() {
+ RecordResolver R(*this);
+ R.setFinal(true);
+
+ for (auto Assertion : getAssertions()) {
+ Init *Condition = std::get<1>(Assertion)->resolveReferences(R);
+ Init *Message = std::get<2>(Assertion)->resolveReferences(R);
+ CheckAssert(std::get<0>(Assertion), Condition, Message);
+ }
+}
+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void RecordKeeper::dump() const { errs() << *this; }
#endif
diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index 974df42de4c1c..3d95ac30ebd0b 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -258,6 +258,9 @@ bool TGParser::AddSubClass(Record *CurRec, SubClassReference &SubClass) {
") of parent class '" + SC->getNameInitAsString() + "'");
}
+ // Copy the subclass record's assertions to the new record.
+ CurRec->appendAssertions(SC);
+
Init *Name;
if (CurRec->isClass())
Name =
@@ -448,8 +451,6 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
Rec->resolveReferences(NewName);
checkConcrete(*Rec);
- CheckRecordAsserts(*Rec);
-
if (!isa<StringInit>(Rec->getNameInit())) {
PrintError(Rec->getLoc(), Twine("record name '") +
Rec->getNameInit()->getAsString() +
@@ -457,6 +458,9 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
return true;
}
+ // Check the assertions.
+ Rec->checkAssertions();
+
// If ObjectBody has template arguments, it's an error.
assert(Rec->getTemplateArgs().empty() && "How'd this get template args?");
@@ -3640,37 +3644,6 @@ bool TGParser::CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values,
return false;
}
-// Check an assertion: Obtain the condition value and be sure it is true.
-// If not, print a nonfatal error along with the message.
-void TGParser::CheckAssert(SMLoc Loc, Init *Condition, Init *Message) {
- auto *CondValue = dyn_cast_or_null<IntInit>(
- Condition->convertInitializerTo(IntRecTy::get()));
- if (CondValue) {
- if (!CondValue->getValue()) {
- PrintError(Loc, "assertion failed");
- if (auto *MessageInit = dyn_cast<StringInit>(Message))
- PrintNote(MessageInit->getValue());
- else
- PrintNote("(assert message is not a string)");
- }
- } else {
- PrintError(Loc, "assert condition must of type bit, bits, or int.");
- }
-}
-
-// Check all record assertions: For each one, resolve the condition
-// and message, then call CheckAssert().
-void TGParser::CheckRecordAsserts(Record &Rec) {
- RecordResolver R(Rec);
- R.setFinal(true);
-
- for (auto Assertion : Rec.getAssertions()) {
- Init *Condition = std::get<1>(Assertion)->resolveReferences(R);
- Init *Message = std::get<2>(Assertion)->resolveReferences(R);
- CheckAssert(std::get<0>(Assertion), Condition, Message);
- }
-}
-
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void RecordsEntry::dump() const {
if (Loop)
diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h
index 5b847ab7344f1..6514d4276a7f6 100644
--- a/llvm/lib/TableGen/TGParser.h
+++ b/llvm/lib/TableGen/TGParser.h
@@ -268,8 +268,6 @@ class TGParser {
bool ApplyLetStack(RecordsEntry &Entry);
bool CheckTemplateArgValues(SmallVectorImpl<llvm::Init *> &Values,
SMLoc Loc, Record *ArgsRec);
- void CheckAssert(SMLoc Loc, Init *Condition, Init *Message);
- void CheckRecordAsserts(Record &Rec);
};
} // end namespace llvm
diff --git a/llvm/test/TableGen/assert.td b/llvm/test/TableGen/assert.td
index 858ed9a9e92e3..1c436301d85d0 100644
--- a/llvm/test/TableGen/assert.td
+++ b/llvm/test/TableGen/assert.td
@@ -14,21 +14,21 @@ assert !le(!size(Name), 20), "primary name is too long: " # Name;
// CHECK: assertion failed
// CHECK: note: first name is incorrect
-def Rec1 {
+def Rec01 {
string name = "Fred Smith";
}
-assert !eq(!substr(Rec1.name, 0, 3), "Jane"),
- !strconcat("first name is incorrect: ", Rec1.name);
+assert !eq(!substr(Rec01.name, 0, 3), "Jane"),
+ !strconcat("first name is incorrect: ", Rec01.name);
// CHECK: assertion failed
-// CHECK: note: record Rec2 is broken
+// CHECK: note: record Rec02 is broken
-def Rec2 {
+def Rec02 {
bit broken = true;
}
-assert !not(Rec2.broken), "record Rec2 is broken";
+assert !not(Rec02.broken), "record Rec02 is broken";
// CHECK: assertion failed
// CHECK: note: cube of 9
@@ -94,5 +94,45 @@ def Rec14 : Cube<3> {
// Test the assert statement in a class definition.
+class PersonName<string name> {
+ assert !le(!size(name), 32), "person name is too long: " # name;
+ string Name = name;
+}
+
+class Person<string name, int age> : PersonName<name> {
+ assert !and(!ge(age, 1), !le(age, 120)),
+ "person age is invalid: " # age;
+ int Age = age;
+}
+
+def Rec20 : Person<"Donald Knuth", 60>;
+
+// CHECK: assertion failed
+// CHECK: note: person name is too long
+
+def Rec21 : Person<"Donald Uh Oh This Name Is Too Long Knuth", 50>;
+
+// CHECK: assertion failed
+// CHECK: note: person age is invalid
+
+def Rec22 : Person<"Donald Knuth", 150>;
+
+// Test the assert statement in an anonymous class invocation.
+
+def Rec30 {
+ string Name = Person<"Margaret Heafield Hamilton", 25>.Name;
+ int Age = Person<"Margaret Heafield Hamilton", 25>.Age;
+}
+
+def Rec31 {
+ string Name = Person<"Margaret Heafield And More Middle Names Hamilton", 25>.Name;
+ int Age = Person<"Margaret Heafield Hamilton", 25>.Age;
+}
+
+def Rec32 {
+ string Name = Person<"Margaret Heafield Hamilton", 25>.Name;
+ int Age = Person<"Margaret Heafield Hamilton", 0>.Age;
+}
+
// Test the assert statement in a multiclass.
More information about the llvm-commits
mailing list