Skip to content

Commit 3b9a15d

Browse files
author
Paul C. Anagnostopoulos
committed
[TableGen] Add support for the 'assert' statement in multiclasses
1 parent 1206313 commit 3b9a15d

File tree

6 files changed

+122
-23
lines changed

6 files changed

+122
-23
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,9 @@ placement.
12821282
the subclasses and records that inherit from the class. The assertions are
12831283
then checked when the records are completely built.
12841284

1285-
* In a multiclass definition, ... [this placement is not yet available]
1285+
* In a multiclass definition, the assertions are saved with the other
1286+
components of the multiclass and then checked each time the multiclass
1287+
is instantiated with ``defm``.
12861288

12871289
Using assertions in TableGen files can simplify record checking in TableGen
12881290
backends. Here is an example of an ``assert`` in two class definitions.

llvm/include/llvm/TableGen/Record.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,10 @@ inline raw_ostream &operator<<(raw_ostream &OS, const RecordVal &RV) {
14691469
}
14701470

14711471
class Record {
1472+
public:
1473+
using AssertionTuple = std::tuple<SMLoc, Init *, Init *>;
1474+
1475+
private:
14721476
static unsigned LastID;
14731477

14741478
Init *Name;
@@ -1478,7 +1482,7 @@ class Record {
14781482
SmallVector<Init *, 0> TemplateArgs;
14791483
SmallVector<RecordVal, 0> Values;
14801484
// Vector of [source location, condition Init, message Init].
1481-
SmallVector<std::tuple<SMLoc, Init *, Init *>, 0> Assertions;
1485+
SmallVector<AssertionTuple, 0> Assertions;
14821486

14831487
// All superclasses in the inheritance forest in post-order (yes, it
14841488
// must be a forest; diamond-shaped inheritance is not allowed).
@@ -1553,7 +1557,7 @@ class Record {
15531557

15541558
ArrayRef<RecordVal> getValues() const { return Values; }
15551559

1556-
ArrayRef<std::tuple<SMLoc, Init *, Init *>> getAssertions() const {
1560+
ArrayRef<AssertionTuple> getAssertions() const {
15571561
return Assertions;
15581562
}
15591563

@@ -1620,7 +1624,7 @@ class Record {
16201624
Assertions.append(Rec->Assertions);
16211625
}
16221626

1623-
void checkAssertions();
1627+
void checkRecordAssertions();
16241628

16251629
bool isSubClassOf(const Record *R) const {
16261630
for (const auto &SCPair : SuperClasses)

llvm/lib/TableGen/Record.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,7 +1832,7 @@ DefInit *VarDefInit::instantiate() {
18321832
Records.addDef(std::move(NewRecOwner));
18331833

18341834
// Check the assertions.
1835-
NewRec->checkAssertions();
1835+
NewRec->checkRecordAssertions();
18361836

18371837
Def = DefInit::get(NewRec);
18381838
}
@@ -2615,7 +2615,7 @@ DagInit *Record::getValueAsDag(StringRef FieldName) const {
26152615
// and message, then call CheckAssert().
26162616
// Note: The condition and message are probably already resolved,
26172617
// but resolving again allows calls before records are resolved.
2618-
void Record::checkAssertions() {
2618+
void Record::checkRecordAssertions() {
26192619
RecordResolver R(*this);
26202620
R.setFinal(true);
26212621

llvm/lib/TableGen/TGParser.cpp

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -339,27 +339,38 @@ bool TGParser::AddSubMultiClass(MultiClass *CurMC,
339339
return resolve(SMC->Entries, TemplateArgs, false, &CurMC->Entries);
340340
}
341341

342-
/// Add a record or foreach loop to the current context (global record keeper,
343-
/// current inner-most foreach loop, or multiclass).
342+
/// Add a record, foreach loop, or assertion to the current context.
344343
bool TGParser::addEntry(RecordsEntry E) {
345-
assert(!E.Rec || !E.Loop);
344+
assert((!!E.Rec + !!E.Loop + !!E.Assertion) == 1 &&
345+
"RecordsEntry has invalid number of items");
346346

347+
// If we are parsing a loop, add it to the loop's entries.
347348
if (!Loops.empty()) {
348349
Loops.back()->Entries.push_back(std::move(E));
349350
return false;
350351
}
351352

353+
// If it is a loop, then resolve and perform the loop.
352354
if (E.Loop) {
353355
SubstStack Stack;
354356
return resolve(*E.Loop, Stack, CurMultiClass == nullptr,
355357
CurMultiClass ? &CurMultiClass->Entries : nullptr);
356358
}
357359

360+
// If we are parsing a multiclass, add it to the multiclass's entries.
358361
if (CurMultiClass) {
359362
CurMultiClass->Entries.push_back(std::move(E));
360363
return false;
361364
}
362365

366+
// If it is an assertion, then it's a top-level one, so check it.
367+
if (E.Assertion) {
368+
CheckAssert(std::get<0>(*E.Assertion), std::get<1>(*E.Assertion),
369+
std::get<2>(*E.Assertion));
370+
return false;
371+
}
372+
373+
// It must be a record, so finish it off.
363374
return addDefOne(std::move(E.Rec));
364375
}
365376

@@ -414,6 +425,24 @@ bool TGParser::resolve(const std::vector<RecordsEntry> &Source,
414425
for (auto &E : Source) {
415426
if (E.Loop) {
416427
Error = resolve(*E.Loop, Substs, Final, Dest);
428+
429+
} else if (E.Assertion) {
430+
MapResolver R;
431+
for (const auto &S : Substs)
432+
R.set(S.first, S.second);
433+
Init *Condition = std::get<1>(*E.Assertion)->resolveReferences(R);
434+
Init *Message = std::get<2>(*E.Assertion)->resolveReferences(R);
435+
436+
if (Dest) {
437+
std::unique_ptr<Record::AssertionTuple> tuple =
438+
std::make_unique<Record::AssertionTuple>(std::get<0>(*E.Assertion),
439+
std::move(Condition),
440+
std::move(Message));
441+
Dest->push_back(std::move(tuple));
442+
} else {
443+
CheckAssert(std::get<0>(*E.Assertion), Condition, Message);
444+
}
445+
417446
} else {
418447
auto Rec = std::make_unique<Record>(*E.Rec);
419448
if (Loc)
@@ -459,7 +488,7 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
459488
}
460489

461490
// Check the assertions.
462-
Rec->checkAssertions();
491+
Rec->checkRecordAssertions();
463492

464493
// If ObjectBody has template arguments, it's an error.
465494
assert(Rec->getTemplateArgs().empty() && "How'd this get template args?");
@@ -2842,10 +2871,15 @@ bool TGParser::ApplyLetStack(Record *CurRec) {
28422871
return false;
28432872
}
28442873

2874+
/// Apply the current let bindings to the RecordsEntry.
28452875
bool TGParser::ApplyLetStack(RecordsEntry &Entry) {
28462876
if (Entry.Rec)
28472877
return ApplyLetStack(Entry.Rec.get());
28482878

2879+
// Let bindings are not applied to assertions.
2880+
if (Entry.Assertion)
2881+
return false;
2882+
28492883
for (auto &E : Entry.Loop->Entries) {
28502884
if (ApplyLetStack(E))
28512885
return true;
@@ -2889,8 +2923,8 @@ bool TGParser::ParseObjectBody(Record *CurRec) {
28892923
return ParseBody(CurRec);
28902924
}
28912925

2892-
/// ParseDef - Parse and return a top level or multiclass def, return the record
2893-
/// corresponding to it. This returns null on error.
2926+
/// ParseDef - Parse and return a top level or multiclass record definition.
2927+
/// Return false if okay, true if error.
28942928
///
28952929
/// DefInst ::= DEF ObjectName ObjectBody
28962930
///
@@ -3184,12 +3218,12 @@ bool TGParser::ParseAssert(MultiClass *CurMultiClass, Record *CurRec) {
31843218
if (!consume(tgtok::semi))
31853219
return TokError("expected ';'");
31863220

3187-
if (CurMultiClass) {
3188-
assert(false && "assert in multiclass not yet supported");
3189-
} else if (CurRec) {
3221+
if (CurRec) {
31903222
CurRec->addAssertion(ConditionLoc, Condition, Message);
3191-
} else { // at top level
3192-
CheckAssert(ConditionLoc, Condition, Message);
3223+
} else {
3224+
std::unique_ptr<Record::AssertionTuple> tuple =
3225+
std::make_unique<Record::AssertionTuple>(ConditionLoc, Condition, Message);
3226+
addEntry(std::move(tuple));
31933227
}
31943228

31953229
return false;
@@ -3328,10 +3362,13 @@ bool TGParser::ParseTopLevelLet(MultiClass *CurMultiClass) {
33283362
/// MultiClassInst ::= MULTICLASS ID TemplateArgList?
33293363
/// ':' BaseMultiClassList '{' MultiClassObject+ '}'
33303364
/// MultiClassObject ::= DefInst
3331-
/// MultiClassObject ::= MultiClassInst
33323365
/// MultiClassObject ::= DefMInst
3366+
/// MultiClassObject ::= Defvar
3367+
/// MultiClassObject ::= Foreach
3368+
/// MultiClassObject ::= If
33333369
/// MultiClassObject ::= LETCommand '{' ObjectList '}'
33343370
/// MultiClassObject ::= LETCommand Object
3371+
/// MultiClassObject ::= Assert
33353372
///
33363373
bool TGParser::ParseMultiClass() {
33373374
assert(Lex.getCode() == tgtok::MultiClass && "Unexpected token");
@@ -3396,15 +3433,14 @@ bool TGParser::ParseMultiClass() {
33963433
default:
33973434
return TokError("expected 'assert', 'def', 'defm', 'defvar', "
33983435
"'foreach', 'if', or 'let' in multiclass body");
3399-
case tgtok::Assert:
3400-
return TokError("an assert statement in a multiclass is not yet supported");
34013436

34023437
case tgtok::Def:
34033438
case tgtok::Defm:
34043439
case tgtok::Defvar:
34053440
case tgtok::Foreach:
34063441
case tgtok::If:
34073442
case tgtok::Let:
3443+
case tgtok::Assert:
34083444
if (ParseObject(CurMultiClass))
34093445
return true;
34103446
break;
@@ -3564,7 +3600,7 @@ bool TGParser::ParseObject(MultiClass *MC) {
35643600
default:
35653601
return TokError(
35663602
"Expected assert, class, def, defm, defset, foreach, if, or let");
3567-
case tgtok::Assert: return ParseAssert(MC, nullptr);
3603+
case tgtok::Assert: return ParseAssert(MC);
35683604
case tgtok::Def: return ParseDef(MC);
35693605
case tgtok::Defm: return ParseDefm(MC);
35703606
case tgtok::Defvar: return ParseDefvar();

llvm/lib/TableGen/TGParser.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,21 @@ namespace llvm {
3636
}
3737
};
3838

39-
/// RecordsEntry - Can be either a record or a foreach loop.
39+
/// RecordsEntry - Holds exactly one of a Record, ForeachLoop, or
40+
/// assertion tuple.
4041
struct RecordsEntry {
4142
std::unique_ptr<Record> Rec;
4243
std::unique_ptr<ForeachLoop> Loop;
44+
std::unique_ptr<Record::AssertionTuple> Assertion;
4345

4446
void dump() const;
4547

4648
RecordsEntry() {}
4749
RecordsEntry(std::unique_ptr<Record> Rec) : Rec(std::move(Rec)) {}
4850
RecordsEntry(std::unique_ptr<ForeachLoop> Loop)
4951
: Loop(std::move(Loop)) {}
52+
RecordsEntry(std::unique_ptr<Record::AssertionTuple> Assertion)
53+
: Assertion(std::move(Assertion)) {}
5054
};
5155

5256
/// ForeachLoop - Record the iteration state associated with a for loop.
@@ -222,7 +226,7 @@ class TGParser {
222226
bool ParseForeach(MultiClass *CurMultiClass);
223227
bool ParseIf(MultiClass *CurMultiClass);
224228
bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind);
225-
bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec);
229+
bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec = nullptr);
226230
bool ParseTopLevelLet(MultiClass *CurMultiClass);
227231
void ParseLetList(SmallVectorImpl<LetRecord> &Result);
228232

llvm/test/TableGen/assert.td

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ class Cube<int n> {
3939

4040
assert !eq(Cube<9>.result, 81), "cube of 9 should be 729";
4141

42+
// CHECK: assertion failed
43+
// CHECK: note: foreach i cannot be 2
44+
// CHECK-NOT: note: foreach i cannot be 2
45+
46+
foreach i = 1...3 in {
47+
assert !ne(i, 2), "foreach i cannot be 2";
48+
def bar_ # i;
49+
}
50+
4251
// Test the assert statement in a record definition.
4352

4453
// CHECK: assertion failed
@@ -136,3 +145,47 @@ def Rec32 {
136145

137146
// Test the assert statement in a multiclass.
138147

148+
// CHECK: assertion failed
149+
// CHECK: note: MC1 id string is too long
150+
// CHECK: assertion failed
151+
// CHECK: note: MC1 seq is too high
152+
153+
multiclass MC1<string id, int seq> {
154+
assert !le(!size(id), 5), "MC1 id string is too long";
155+
assert !le(seq, 999999), "MC1 seq is too high";
156+
157+
def _mc1 {
158+
string ID = id;
159+
int Seq = seq;
160+
}
161+
}
162+
163+
defm Rec40 : MC1<"ILISP", 999>;
164+
defm Rec41 : MC1<"ILISPX", 999>;
165+
defm Rec42 : MC1<"ILISP", 999999999>;
166+
167+
// CHECK: assertion failed
168+
// CHECK: note: MC2 phrase must be secret: secrex code
169+
170+
multiclass MC2<string phr> {
171+
assert !eq(!substr(phr, 0, 6), "secret"), "MC2 phrase must be secret: " # phr;
172+
173+
def _mc2 {
174+
string phrase = phr;
175+
}
176+
}
177+
178+
multiclass MC3<string phr> {
179+
defm _mc3 : MC2<phr>;
180+
}
181+
182+
defm Rec43 : MC3<"secrex code">;
183+
184+
// CHECK: assertion failed
185+
// CHECK: note: MC2 phrase must be secret: xecret code
186+
187+
multiclass MC4<string phr> : MC2<phr> {
188+
def _def;
189+
}
190+
191+
defm Rec44 : MC4<"xecret code">;

0 commit comments

Comments
 (0)