[TableGen] Add support for the 'assert' statement in multiclasses

This commit is contained in:
Paul C. Anagnostopoulos 2021-04-01 13:01:43 -04:00
parent 1206313f82
commit 3b9a15d910
6 changed files with 122 additions and 23 deletions

View File

@ -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.

View File

@ -1469,6 +1469,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;
@ -1478,7 +1482,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).
@ -1553,7 +1557,7 @@ public:
ArrayRef<RecordVal> getValues() const { return Values; }
ArrayRef<std::tuple<SMLoc, Init *, Init *>> getAssertions() const {
ArrayRef<AssertionTuple> getAssertions() const {
return Assertions;
}
@ -1620,7 +1624,7 @@ public:
Assertions.append(Rec->Assertions);
}
void checkAssertions();
void checkRecordAssertions();
bool isSubClassOf(const Record *R) const {
for (const auto &SCPair : SuperClasses)

View File

@ -1832,7 +1832,7 @@ DefInit *VarDefInit::instantiate() {
Records.addDef(std::move(NewRecOwner));
// Check the assertions.
NewRec->checkAssertions();
NewRec->checkRecordAssertions();
Def = DefInit::get(NewRec);
}
@ -2615,7 +2615,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);

View File

@ -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;
@ -3328,10 +3362,13 @@ bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) {
/// MultiClassInst ::= MULTICLASS ID TemplateArgList?
/// ':' BaseMultiClassList '{' MultiClassObject+ '}'
/// 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,8 +3433,6 @@ 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::Def:
case tgtok::Defm:
@ -3405,6 +3440,7 @@ bool TGParser::ParseMultiClass() {
case tgtok::Foreach:
case tgtok::If:
case tgtok::Let:
case tgtok::Assert:
if (ParseObject(CurMultiClass))
return true;
break;
@ -3564,7 +3600,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();

View File

@ -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 @@ private: // Parser methods.
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);

View File

@ -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">;