Skip to content

Commit 04ed64e

Browse files
committed
[RISCV][llvm-tblgen] Support conditional definitions using !exists clauses
The core part of this change is an extension to the tablegen language to allow conditional definition of records using if-statements based on !exists conditions. The RISCV td file change is mostly to illustrate the potential use of conditional definitions. I am deliberately not maximally simplifying in this change to make merging with downstream code (or simply rebasing while this on review) easier. Some background to make the change understandable. TableGen does not have an if statement internally. It has if expressions - in the form of TernInitOp with IF opcode - and foreach statements. It implements an if-statement as a foreach which iterates either 0 or 1 times. Foreach nodes are then evaluated via unrolling inside the parser. Specifically, they are evaluated, at latest, when the outermost multiclass or loop containing them reaches end of scope. The unrolled statements remain (potentially) unresolved after unrolling, but the number of iterations must be known at this point. An !exists clause can only evaluate at final evaluation. (Specifically, forward references to definitions are allowed - up to the end of the containing scope at least.) The existing code did not set the final flag on the resolver, and thus would leave the !exists clause in an unresolved state. This would then cause an error since we don't know how many iterations on which to unroll the (synthetic) foreach loop. I chose to only finally-evaluate the condition of the if-expression. This allows us to pick an arm at scope exit without inhibiting definitions in the arm from having self references. Differential Revision: https://reviews.llvm.org/D145108
1 parent bd80dbf commit 04ed64e

File tree

4 files changed

+97
-37
lines changed

4 files changed

+97
-37
lines changed

llvm/lib/TableGen/Record.cpp

+23-23
Original file line numberDiff line numberDiff line change
@@ -1772,34 +1772,34 @@ void ExistsOpInit::Profile(FoldingSetNodeID &ID) const {
17721772

17731773
Init *ExistsOpInit::Fold(Record *CurRec, bool IsFinal) const {
17741774
if (StringInit *Name = dyn_cast<StringInit>(Expr)) {
1775-
if (!CurRec && !IsFinal)
1776-
return const_cast<ExistsOpInit *>(this);
1777-
1778-
// Self-references are allowed, but their resolution is delayed until
1779-
// the final resolve to ensure that we get the correct type for them.
1780-
auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit());
1781-
if (Name == CurRec->getNameInit() ||
1782-
(Anonymous && Name == Anonymous->getNameInit())) {
1783-
if (!IsFinal)
1784-
return const_cast<ExistsOpInit *>(this);
1785-
1786-
// No doubt that there exists a record, so we should check if types are
1787-
// compatiable.
1788-
return IntInit::get(getRecordKeeper(),
1789-
CurRec->getType()->typeIsA(CheckType));
1790-
}
17911775

17921776
// Look up all defined records to see if we can find one.
17931777
Record *D = CheckType->getRecordKeeper().getDef(Name->getValue());
1794-
if (!D) {
1795-
if (IsFinal)
1796-
return IntInit::get(getRecordKeeper(), 0);
1797-
return const_cast<ExistsOpInit *>(this);
1778+
if (D) {
1779+
// Check if types are compatiable.
1780+
return IntInit::get(getRecordKeeper(),
1781+
DefInit::get(D)->getType()->typeIsA(CheckType));
17981782
}
17991783

1800-
// Check if types are compatiable.
1801-
return IntInit::get(getRecordKeeper(),
1802-
DefInit::get(D)->getType()->typeIsA(CheckType));
1784+
if (CurRec) {
1785+
// Self-references are allowed, but their resolution is delayed until
1786+
// the final resolve to ensure that we get the correct type for them.
1787+
auto *Anonymous = dyn_cast<AnonymousNameInit>(CurRec->getNameInit());
1788+
if (Name == CurRec->getNameInit() ||
1789+
(Anonymous && Name == Anonymous->getNameInit())) {
1790+
if (!IsFinal)
1791+
return const_cast<ExistsOpInit *>(this);
1792+
1793+
// No doubt that there exists a record, so we should check if types are
1794+
// compatiable.
1795+
return IntInit::get(getRecordKeeper(),
1796+
CurRec->getType()->typeIsA(CheckType));
1797+
}
1798+
}
1799+
1800+
if (IsFinal)
1801+
return IntInit::get(getRecordKeeper(), 0);
1802+
return const_cast<ExistsOpInit *>(this);
18031803
}
18041804
return const_cast<ExistsOpInit *>(this);
18051805
}

llvm/lib/TableGen/TGParser.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -384,10 +384,35 @@ bool TGParser::addEntry(RecordsEntry E) {
384384
bool TGParser::resolve(const ForeachLoop &Loop, SubstStack &Substs,
385385
bool Final, std::vector<RecordsEntry> *Dest,
386386
SMLoc *Loc) {
387+
387388
MapResolver R;
388389
for (const auto &S : Substs)
389390
R.set(S.first, S.second);
390391
Init *List = Loop.ListValue->resolveReferences(R);
392+
393+
// For if-then-else blocks, we lower to a foreach loop whose list is a
394+
// ternary selection between lists of different length. Since we don't
395+
// have a means to track variable length record lists, we *must* resolve
396+
// the condition here. We want to defer final resolution of the arms
397+
// until the resulting records are finalized.
398+
// e.g. !if(!exists<SchedWrite>("__does_not_exist__"), [1], [])
399+
if (auto *TI = dyn_cast<TernOpInit>(List);
400+
TI && TI->getOpcode() == TernOpInit::IF && Final) {
401+
Init *OldLHS = TI->getLHS();
402+
R.setFinal(true);
403+
Init *LHS = OldLHS->resolveReferences(R);
404+
if (LHS == OldLHS) {
405+
PrintError(Loop.Loc,
406+
Twine("unable to resolve if condition '") +
407+
LHS->getAsString() + "' at end of containing scope");
408+
return true;
409+
}
410+
Init *MHS = TI->getMHS();
411+
Init *RHS = TI->getRHS();
412+
List = TernOpInit::get(TernOpInit::IF, LHS, MHS, RHS, TI->getType())
413+
->Fold(nullptr);
414+
}
415+
391416
auto LI = dyn_cast<ListInit>(List);
392417
if (!LI) {
393418
if (!Final) {

llvm/lib/Target/RISCV/RISCVScheduleV.td

+15-14
Original file line numberDiff line numberDiff line change
@@ -31,43 +31,44 @@ multiclass LMULSchedReadsImpl<string name, list<string> MxList> {
3131
def name # "_" # mx : SchedRead;
3232
}
3333
}
34-
multiclass LMULWriteResImpl<string name, list<string> MxList,
35-
list<ProcResourceKind> resources> {
36-
foreach mx = MxList in {
37-
def : WriteRes<!cast<SchedWrite>(name # "_" # mx), resources>;
34+
multiclass LMULWriteResImpl<string name, list<ProcResourceKind> resources> {
35+
foreach mx = SchedMxList in {
36+
if !exists<SchedWrite>(name # "_" # mx) then
37+
def : WriteRes<!cast<SchedWrite>(name # "_" # mx), resources>;
3838
}
3939
}
40-
multiclass LMULReadAdvanceImpl<string name, list<string> MxList, int val,
40+
multiclass LMULReadAdvanceImpl<string name, int val,
4141
list<SchedWrite> writes = []> {
42-
foreach mx = MxList in {
43-
def : ReadAdvance<!cast<SchedRead>(name # "_" # mx), val, writes>;
42+
foreach mx = SchedMxList in {
43+
if !exists<SchedRead>(name # "_" # mx) then
44+
def : ReadAdvance<!cast<SchedRead>(name # "_" # mx), val, writes>;
4445
}
4546
}
4647

4748
multiclass LMULSchedWrites<string name> : LMULSchedWritesImpl<name, SchedMxList>;
4849
multiclass LMULSchedReads<string name> : LMULSchedReadsImpl<name, SchedMxList>;
4950
multiclass LMULWriteRes<string name, list<ProcResourceKind> resources>
50-
: LMULWriteResImpl<name, SchedMxList, resources>;
51+
: LMULWriteResImpl<name, resources>;
5152
multiclass LMULReadAdvance<string name, int val, list<SchedWrite> writes = []>
52-
: LMULReadAdvanceImpl<name, SchedMxList, val, writes>;
53+
: LMULReadAdvanceImpl<name, val, writes>;
5354

5455
multiclass LMULSchedWritesW<string name> : LMULSchedWritesImpl<name, SchedMxListW>;
5556
multiclass LMULSchedReadsW<string name> : LMULSchedReadsImpl<name, SchedMxListW>;
5657
multiclass LMULWriteResW<string name, list<ProcResourceKind> resources>
57-
: LMULWriteResImpl<name, SchedMxListW, resources>;
58+
: LMULWriteResImpl<name, resources>;
5859
multiclass LMULReadAdvanceW<string name, int val, list<SchedWrite> writes = []>
59-
: LMULReadAdvanceImpl<name, SchedMxListW, val, writes>;
60+
: LMULReadAdvanceImpl<name, val, writes>;
6061

6162
multiclass LMULSchedWritesFW<string name> : LMULSchedWritesImpl<name, SchedMxListFW>;
6263
multiclass LMULSchedReadsFW<string name> : LMULSchedReadsImpl<name, SchedMxListFW>;
6364
multiclass LMULWriteResFW<string name, list<ProcResourceKind> resources>
64-
: LMULWriteResImpl<name, SchedMxListFW, resources>;
65+
: LMULWriteResImpl<name, resources>;
6566
multiclass LMULReadAdvanceFW<string name, int val, list<SchedWrite> writes = []>
66-
: LMULReadAdvanceImpl<name, SchedMxListFW, val, writes>;
67+
: LMULReadAdvanceImpl<name, val, writes>;
6768

6869
multiclass LMULSchedWritesFWRed<string name> : LMULSchedWritesImpl<name, SchedMxListFWRed>;
6970
multiclass LMULWriteResFWRed<string name, list<ProcResourceKind> resources>
70-
: LMULWriteResImpl<name, SchedMxListFWRed, resources>;
71+
: LMULWriteResImpl<name, resources>;
7172

7273

7374
// 3.6 Vector Byte Length vlenb

llvm/test/TableGen/exists.td

+34
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,30 @@ def self_reference : Self_check<"self_reference">; // Self-reference
3737
def current_missing : Self_check<"current">;
3838
def current : Self_check<"current">;
3939

40+
41+
// Check that conditional definitions dependent on the resolution of an
42+
// exists clause work as expected.
43+
// Reminder: a0 exists, a1 does not.
44+
class C {
45+
int exists = 1;
46+
}
47+
if !exists<A>("a0") then
48+
def if_exists : C;
49+
if !exists<A>("a1") then
50+
def if_no_exists: C;
51+
foreach e = ["a0", "a1"] in {
52+
if !exists<A>(e) then
53+
def for_exists_ # e: C;
54+
}
55+
multiclass mc {
56+
foreach e = ["a0", "a1"] in {
57+
if !exists<A>(e) then
58+
def _ # e: C;
59+
}
60+
}
61+
defm multiclass_exists : mc<>;
62+
63+
4064
// CHECK: def a0_exists {
4165
// CHECK: int exists = 1;
4266
// CHECK: }
@@ -58,6 +82,16 @@ def current : Self_check<"current">;
5882
// CHECK: int exists = 0;
5983
// CHECK: }
6084

85+
// CHECK: def for_exists_a0 {
86+
// CHECK: int exists = 1;
87+
// CHECK: }
88+
// CHECK: def if_exists {
89+
// CHECK: int exists = 1;
90+
// CHECK: }
91+
// CHECK: def multiclass_exists_a0 {
92+
// CHECK: int exists = 1;
93+
// CHECK: }
94+
6195
// CHECK: def self_reference {
6296
// CHECK: int exists = 1;
6397
// CHECK: }

0 commit comments

Comments
 (0)