mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 04:32:44 +01:00
[TableGen] Add support for the 'assert' statement in multiclasses
This is step 3 of adding the 'assert' statement. Differential Revision: https://reviews.llvm.org/D99751
This commit is contained in:
parent
91b80509e7
commit
0d02a45ced
@ -1282,7 +1282,9 @@ placement.
|
|||||||
the subclasses and records that inherit from the class. The assertions are
|
the subclasses and records that inherit from the class. The assertions are
|
||||||
then checked when the records are completely built.
|
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
|
Using assertions in TableGen files can simplify record checking in TableGen
|
||||||
backends. Here is an example of an ``assert`` in two class definitions.
|
backends. Here is an example of an ``assert`` in two class definitions.
|
||||||
|
@ -1470,6 +1470,10 @@ inline raw_ostream &operator<<(raw_ostream &OS, const RecordVal &RV) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Record {
|
class Record {
|
||||||
|
public:
|
||||||
|
using AssertionTuple = std::tuple<SMLoc, Init *, Init *>;
|
||||||
|
|
||||||
|
private:
|
||||||
static unsigned LastID;
|
static unsigned LastID;
|
||||||
|
|
||||||
Init *Name;
|
Init *Name;
|
||||||
@ -1479,7 +1483,7 @@ class Record {
|
|||||||
SmallVector<Init *, 0> TemplateArgs;
|
SmallVector<Init *, 0> TemplateArgs;
|
||||||
SmallVector<RecordVal, 0> Values;
|
SmallVector<RecordVal, 0> Values;
|
||||||
// Vector of [source location, condition Init, message Init].
|
// 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
|
// All superclasses in the inheritance forest in post-order (yes, it
|
||||||
// must be a forest; diamond-shaped inheritance is not allowed).
|
// must be a forest; diamond-shaped inheritance is not allowed).
|
||||||
@ -1554,9 +1558,7 @@ public:
|
|||||||
|
|
||||||
ArrayRef<RecordVal> getValues() const { return Values; }
|
ArrayRef<RecordVal> getValues() const { return Values; }
|
||||||
|
|
||||||
ArrayRef<std::tuple<SMLoc, Init *, Init *>> getAssertions() const {
|
ArrayRef<AssertionTuple> getAssertions() const { return Assertions; }
|
||||||
return Assertions;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayRef<std::pair<Record *, SMRange>> getSuperClasses() const {
|
ArrayRef<std::pair<Record *, SMRange>> getSuperClasses() const {
|
||||||
return SuperClasses;
|
return SuperClasses;
|
||||||
@ -1621,7 +1623,7 @@ public:
|
|||||||
Assertions.append(Rec->Assertions);
|
Assertions.append(Rec->Assertions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkAssertions();
|
void checkRecordAssertions();
|
||||||
|
|
||||||
bool isSubClassOf(const Record *R) const {
|
bool isSubClassOf(const Record *R) const {
|
||||||
for (const auto &SCPair : SuperClasses)
|
for (const auto &SCPair : SuperClasses)
|
||||||
|
@ -1846,7 +1846,7 @@ DefInit *VarDefInit::instantiate() {
|
|||||||
Records.addDef(std::move(NewRecOwner));
|
Records.addDef(std::move(NewRecOwner));
|
||||||
|
|
||||||
// Check the assertions.
|
// Check the assertions.
|
||||||
NewRec->checkAssertions();
|
NewRec->checkRecordAssertions();
|
||||||
|
|
||||||
Def = DefInit::get(NewRec);
|
Def = DefInit::get(NewRec);
|
||||||
}
|
}
|
||||||
@ -2629,7 +2629,7 @@ DagInit *Record::getValueAsDag(StringRef FieldName) const {
|
|||||||
// and message, then call CheckAssert().
|
// and message, then call CheckAssert().
|
||||||
// Note: The condition and message are probably already resolved,
|
// Note: The condition and message are probably already resolved,
|
||||||
// but resolving again allows calls before records are resolved.
|
// but resolving again allows calls before records are resolved.
|
||||||
void Record::checkAssertions() {
|
void Record::checkRecordAssertions() {
|
||||||
RecordResolver R(*this);
|
RecordResolver R(*this);
|
||||||
R.setFinal(true);
|
R.setFinal(true);
|
||||||
|
|
||||||
|
@ -339,27 +339,38 @@ bool TGParser::AddSubMultiClass(MultiClass *CurMC,
|
|||||||
return resolve(SMC->Entries, TemplateArgs, false, &CurMC->Entries);
|
return resolve(SMC->Entries, TemplateArgs, false, &CurMC->Entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a record or foreach loop to the current context (global record keeper,
|
/// Add a record, foreach loop, or assertion to the current context.
|
||||||
/// current inner-most foreach loop, or multiclass).
|
|
||||||
bool TGParser::addEntry(RecordsEntry E) {
|
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()) {
|
if (!Loops.empty()) {
|
||||||
Loops.back()->Entries.push_back(std::move(E));
|
Loops.back()->Entries.push_back(std::move(E));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it is a loop, then resolve and perform the loop.
|
||||||
if (E.Loop) {
|
if (E.Loop) {
|
||||||
SubstStack Stack;
|
SubstStack Stack;
|
||||||
return resolve(*E.Loop, Stack, CurMultiClass == nullptr,
|
return resolve(*E.Loop, Stack, CurMultiClass == nullptr,
|
||||||
CurMultiClass ? &CurMultiClass->Entries : nullptr);
|
CurMultiClass ? &CurMultiClass->Entries : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are parsing a multiclass, add it to the multiclass's entries.
|
||||||
if (CurMultiClass) {
|
if (CurMultiClass) {
|
||||||
CurMultiClass->Entries.push_back(std::move(E));
|
CurMultiClass->Entries.push_back(std::move(E));
|
||||||
return false;
|
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));
|
return addDefOne(std::move(E.Rec));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,6 +425,24 @@ bool TGParser::resolve(const std::vector<RecordsEntry> &Source,
|
|||||||
for (auto &E : Source) {
|
for (auto &E : Source) {
|
||||||
if (E.Loop) {
|
if (E.Loop) {
|
||||||
Error = resolve(*E.Loop, Substs, Final, Dest);
|
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 {
|
} else {
|
||||||
auto Rec = std::make_unique<Record>(*E.Rec);
|
auto Rec = std::make_unique<Record>(*E.Rec);
|
||||||
if (Loc)
|
if (Loc)
|
||||||
@ -459,7 +488,7 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check the assertions.
|
// Check the assertions.
|
||||||
Rec->checkAssertions();
|
Rec->checkRecordAssertions();
|
||||||
|
|
||||||
// If ObjectBody has template arguments, it's an error.
|
// If ObjectBody has template arguments, it's an error.
|
||||||
assert(Rec->getTemplateArgs().empty() && "How'd this get template args?");
|
assert(Rec->getTemplateArgs().empty() && "How'd this get template args?");
|
||||||
@ -2842,10 +2871,15 @@ bool TGParser::ApplyLetStack(Record *CurRec) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply the current let bindings to the RecordsEntry.
|
||||||
bool TGParser::ApplyLetStack(RecordsEntry &Entry) {
|
bool TGParser::ApplyLetStack(RecordsEntry &Entry) {
|
||||||
if (Entry.Rec)
|
if (Entry.Rec)
|
||||||
return ApplyLetStack(Entry.Rec.get());
|
return ApplyLetStack(Entry.Rec.get());
|
||||||
|
|
||||||
|
// Let bindings are not applied to assertions.
|
||||||
|
if (Entry.Assertion)
|
||||||
|
return false;
|
||||||
|
|
||||||
for (auto &E : Entry.Loop->Entries) {
|
for (auto &E : Entry.Loop->Entries) {
|
||||||
if (ApplyLetStack(E))
|
if (ApplyLetStack(E))
|
||||||
return true;
|
return true;
|
||||||
@ -2889,8 +2923,8 @@ bool TGParser::ParseObjectBody(Record *CurRec) {
|
|||||||
return ParseBody(CurRec);
|
return ParseBody(CurRec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ParseDef - Parse and return a top level or multiclass def, return the record
|
/// ParseDef - Parse and return a top level or multiclass record definition.
|
||||||
/// corresponding to it. This returns null on error.
|
/// Return false if okay, true if error.
|
||||||
///
|
///
|
||||||
/// DefInst ::= DEF ObjectName ObjectBody
|
/// DefInst ::= DEF ObjectName ObjectBody
|
||||||
///
|
///
|
||||||
@ -3184,12 +3218,12 @@ bool TGParser::ParseAssert(MultiClass *CurMultiClass, Record *CurRec) {
|
|||||||
if (!consume(tgtok::semi))
|
if (!consume(tgtok::semi))
|
||||||
return TokError("expected ';'");
|
return TokError("expected ';'");
|
||||||
|
|
||||||
if (CurMultiClass) {
|
if (CurRec) {
|
||||||
assert(false && "assert in multiclass not yet supported");
|
|
||||||
} else if (CurRec) {
|
|
||||||
CurRec->addAssertion(ConditionLoc, Condition, Message);
|
CurRec->addAssertion(ConditionLoc, Condition, Message);
|
||||||
} else { // at top level
|
} else {
|
||||||
CheckAssert(ConditionLoc, Condition, Message);
|
std::unique_ptr<Record::AssertionTuple> Tuple =
|
||||||
|
std::make_unique<Record::AssertionTuple>(ConditionLoc, Condition, Message);
|
||||||
|
addEntry(std::move(Tuple));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -3330,8 +3364,12 @@ bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) {
|
|||||||
/// MultiClassObject ::= DefInst
|
/// MultiClassObject ::= DefInst
|
||||||
/// MultiClassObject ::= MultiClassInst
|
/// MultiClassObject ::= MultiClassInst
|
||||||
/// MultiClassObject ::= DefMInst
|
/// MultiClassObject ::= DefMInst
|
||||||
|
/// MultiClassObject ::= Defvar
|
||||||
|
/// MultiClassObject ::= Foreach
|
||||||
|
/// MultiClassObject ::= If
|
||||||
/// MultiClassObject ::= LETCommand '{' ObjectList '}'
|
/// MultiClassObject ::= LETCommand '{' ObjectList '}'
|
||||||
/// MultiClassObject ::= LETCommand Object
|
/// MultiClassObject ::= LETCommand Object
|
||||||
|
/// MultiClassObject ::= Assert
|
||||||
///
|
///
|
||||||
bool TGParser::ParseMultiClass() {
|
bool TGParser::ParseMultiClass() {
|
||||||
assert(Lex.getCode() == tgtok::MultiClass && "Unexpected token");
|
assert(Lex.getCode() == tgtok::MultiClass && "Unexpected token");
|
||||||
@ -3396,9 +3434,8 @@ bool TGParser::ParseMultiClass() {
|
|||||||
default:
|
default:
|
||||||
return TokError("expected 'assert', 'def', 'defm', 'defvar', "
|
return TokError("expected 'assert', 'def', 'defm', 'defvar', "
|
||||||
"'foreach', 'if', or 'let' in multiclass body");
|
"'foreach', 'if', or 'let' in multiclass body");
|
||||||
case tgtok::Assert:
|
|
||||||
return TokError("an assert statement in a multiclass is not yet supported");
|
|
||||||
|
|
||||||
|
case tgtok::Assert:
|
||||||
case tgtok::Def:
|
case tgtok::Def:
|
||||||
case tgtok::Defm:
|
case tgtok::Defm:
|
||||||
case tgtok::Defvar:
|
case tgtok::Defvar:
|
||||||
@ -3564,7 +3601,7 @@ bool TGParser::ParseObject(MultiClass *MC) {
|
|||||||
default:
|
default:
|
||||||
return TokError(
|
return TokError(
|
||||||
"Expected assert, class, def, defm, defset, foreach, if, or let");
|
"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::Def: return ParseDef(MC);
|
||||||
case tgtok::Defm: return ParseDefm(MC);
|
case tgtok::Defm: return ParseDefm(MC);
|
||||||
case tgtok::Defvar: return ParseDefvar();
|
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 {
|
struct RecordsEntry {
|
||||||
std::unique_ptr<Record> Rec;
|
std::unique_ptr<Record> Rec;
|
||||||
std::unique_ptr<ForeachLoop> Loop;
|
std::unique_ptr<ForeachLoop> Loop;
|
||||||
|
std::unique_ptr<Record::AssertionTuple> Assertion;
|
||||||
|
|
||||||
void dump() const;
|
void dump() const;
|
||||||
|
|
||||||
@ -47,6 +49,8 @@ namespace llvm {
|
|||||||
RecordsEntry(std::unique_ptr<Record> Rec) : Rec(std::move(Rec)) {}
|
RecordsEntry(std::unique_ptr<Record> Rec) : Rec(std::move(Rec)) {}
|
||||||
RecordsEntry(std::unique_ptr<ForeachLoop> Loop)
|
RecordsEntry(std::unique_ptr<ForeachLoop> Loop)
|
||||||
: Loop(std::move(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.
|
/// ForeachLoop - Record the iteration state associated with a for loop.
|
||||||
@ -222,7 +226,7 @@ private: // Parser methods.
|
|||||||
bool ParseForeach(MultiClass *CurMultiClass);
|
bool ParseForeach(MultiClass *CurMultiClass);
|
||||||
bool ParseIf(MultiClass *CurMultiClass);
|
bool ParseIf(MultiClass *CurMultiClass);
|
||||||
bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind);
|
bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind);
|
||||||
bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec);
|
bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec = nullptr);
|
||||||
bool ParseTopLevelLet(MultiClass *CurMultiClass);
|
bool ParseTopLevelLet(MultiClass *CurMultiClass);
|
||||||
void ParseLetList(SmallVectorImpl<LetRecord> &Result);
|
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";
|
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.
|
// Test the assert statement in a record definition.
|
||||||
|
|
||||||
// CHECK: assertion failed
|
// CHECK: assertion failed
|
||||||
@ -136,3 +145,47 @@ def Rec32 {
|
|||||||
|
|
||||||
// Test the assert statement in a multiclass.
|
// 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
Block a user