forked from OSchip/llvm-project
[TableGen] Add support for the 'assert' statement in multiclasses
This commit is contained in:
parent
1206313f82
commit
3b9a15d910
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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">;
|
||||
|
|
Loading…
Reference in New Issue