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
==================
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;
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)
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
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);
NewRec->resolveReferences();
Records.addDef(std::move(NewRecOwner));
+ // Check the assertions.
+ NewRec->checkAssertions();
+
Def = DefInit::get(NewRec);
}
// 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;
}
}
}
+
+ // 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) {
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
") of parent class '" + SC->getNameInitAsString() + "'");
}
+ // Copy the subclass record's assertions to the new record.
+ CurRec->appendAssertions(SC);
+
Init *Name;
if (CurRec->isClass())
Name =
Rec->resolveReferences(NewName);
checkConcrete(*Rec);
- CheckRecordAsserts(*Rec);
-
if (!isa<StringInit>(Rec->getNameInit())) {
PrintError(Rec->getLoc(), Twine("record name '") +
Rec->getNameInit()->getAsString() +
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?");
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)
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
// 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
// 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.