[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