Skip to content

Commit 56dfb79

Browse files
committed
tablegen: add CppConstant and fix some constraint language bugs
As seen in the test, the verifier errors can become a bit verbose, and could benefit from better indentation; but I leave tweaking that to subsequent changes.
1 parent 30ce354 commit 56dfb79

File tree

9 files changed

+144
-16
lines changed

9 files changed

+144
-16
lines changed

example/ExampleDialect.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,28 @@
2626
#include "ExampleDialect.h"
2727

2828
#define GET_INCLUDES
29+
#include "ExampleDialect.cpp.inc"
30+
31+
const char *xd::toString(VectorKind kind) {
32+
switch (kind) {
33+
case VectorKind::LittleEndian:
34+
return "LittleEndian";
35+
case VectorKind::BigEndian:
36+
return "BigEndian";
37+
case VectorKind::MiddleEndian:
38+
return "MiddleEndian";
39+
default:
40+
return "(bad)";
41+
}
42+
}
43+
44+
template <> struct llvm_dialects::Printable<xd::VectorKind> {
45+
xd::VectorKind x;
46+
47+
explicit Printable(xd::VectorKind x) : x(x) {}
48+
49+
void print(llvm::raw_ostream &out) const { out << toString(x); }
50+
};
51+
2952
#define GET_DIALECT_DEFS
3053
#include "ExampleDialect.cpp.inc"

example/ExampleDialect.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ enum class VectorKind {
3636
MiddleEndian = 2,
3737
};
3838

39+
const char *toString(VectorKind kind);
40+
3941
} // namespace xd
4042

4143
#define GET_DIALECT_DECLS

example/ExampleDialect.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ def ExampleDialect : Dialect {
3232

3333
defm AttrVectorKind : AttrEnum<"VectorKind">;
3434

35+
def VectorKindLittleEndian : CppConstant<"xd::VectorKind::LittleEndian">;
36+
def VectorKindBigEndian : CppConstant<"xd::VectorKind::BigEndian">;
37+
def VectorKindMiddleEndian : CppConstant<"xd::VectorKind::MiddleEndian">;
38+
39+
def isReasonableVectorKind : TgPredicate<
40+
(args AttrVectorKind:$kind),
41+
(eq $kind, (or VectorKindLittleEndian, VectorKindBigEndian))>;
42+
3543
def XdVectorType : DialectType<ExampleDialect, "vector"> {
3644
let typeArguments = (args AttrVectorKind:$kind, type:$element_type,
3745
AttrI32:$num_elements);
@@ -149,6 +157,10 @@ def FromFixedVectorOp : ExampleOp<"fromfixedvector", [Memory<[]>, NoUnwind,
149157

150158
let defaultBuilderHasExplicitResultType = true;
151159

160+
let verifier = [
161+
(isReasonableVectorKind $kind),
162+
];
163+
152164
let summary = "convert <n x T> to our custom vector type";
153165
let description = [{
154166
Demonstrate a more complex unification case.

include/llvm-dialects/Dialect/Dialect.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ def or;
205205
/// Misc. predicates
206206
// ============================================================================
207207

208+
class CppConstant<string cppExpr_> : Predicate, CppPredicateMixin {
209+
let arguments = (args any:$self);
210+
let evaluate = cppExpr_;
211+
let check = "$self == " # cppExpr_;
212+
let capture = [];
213+
}
214+
208215
def ScalarOrFixedVector : TgPredicate<
209216
(args type:$self, type:$scalar_type),
210217
(or (eq $self, $scalar_type),
@@ -247,6 +254,9 @@ def : AttrLlvmType<AttrI64, I64>;
247254

248255
// Define a custom enum attribute that can be used as in operations and dialect
249256
// types
257+
//
258+
// The llvm_dialects::Printable struct template must be specialized for the enum
259+
// type, or automatically generated verifier code may fail to compile.
250260
multiclass AttrEnum<string cppType_> {
251261
def NAME : Attr<cppType_> {
252262
let toLlvmValue = "::llvm::ConstantInt::get($1, static_cast<unsigned>($0))";

include/llvm-dialects/Dialect/Utils.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,27 @@ bool runInstructionVerifier(
4242
bool runTypeVerifier(llvm::function_ref<bool(llvm::raw_ostream &)> verifier,
4343
llvm::Type *type, llvm::raw_ostream *errs = nullptr);
4444

45+
/// Wrap a value to be printed to an llvm::raw_ostream.
46+
///
47+
/// Specialize this struct if you use AttrEnum arguments in operations or
48+
/// types.
4549
template <typename T> struct Printable {
4650
T x;
4751

4852
explicit Printable(const T &x) : x(x) {}
49-
};
5053

51-
template <typename T>
52-
llvm::raw_ostream &operator<<(llvm::raw_ostream &out,
53-
const Printable<T> &p) {
54-
out << p.x;
55-
return out;
56-
}
54+
void print(llvm::raw_ostream &out) const {
55+
if constexpr (std::is_pointer_v<T>) {
56+
out << *x;
57+
} else {
58+
out << x;
59+
}
60+
}
61+
};
5762

5863
template <typename T>
59-
llvm::raw_ostream &operator<<(llvm::raw_ostream &out,
60-
const Printable<T *> &p) {
61-
out << *p.x;
64+
llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Printable<T> &p) {
65+
p.print(out);
6266
return out;
6367
}
6468

lib/TableGen/Constraints.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ bool ConstraintSystem::addConstraintImpl(raw_ostream &errs, Init *init,
109109
result->m_init = init;
110110
result->m_self = self;
111111

112+
if (self)
113+
result->addVariable(self);
114+
112115
for (size_t i = 0; i < dag->getNumArgs(); ++i) {
113116
if (!dag->getArgNameStr(i).empty()) {
114117
errs << "'or' operands cannot be captured\n";
@@ -197,8 +200,10 @@ bool ConstraintSystem::addConstraintImpl(raw_ostream &errs, Init *init,
197200
return false;
198201
}
199202

200-
if (variable)
203+
if (variable) {
201204
addVariable(variable);
205+
result->addVariable(variable);
206+
}
202207
result->m_arguments.push_back(variable);
203208
}
204209
} else {

lib/TableGen/Evaluator.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ bool Evaluator::checkApplyCapture(const Apply *apply) {
507507
m_fmt.addSubst("with", with);
508508

509509
m_out << tgfmt(R"(
510+
return true;
510511
}()) {
511512
$_errs << " while checking $constraint\n";
512513
$with
@@ -571,7 +572,7 @@ bool Evaluator::checkApplyCapture(const Apply *apply) {
571572

572573
bool Evaluator::checkLogicOr(const LogicOr *logicOr) {
573574
for (Variable *variable : logicOr->variables()) {
574-
if (variable->isNamed() && m_assignment.lookup(variable).empty()) {
575+
if (m_assignment.lookup(variable).empty()) {
575576
m_errs << "cannot check " << logicOr->toString() << '\n';
576577
m_errs << "because " << variable->toString() << " is not known\n";
577578
return false;
@@ -584,7 +585,7 @@ bool Evaluator::checkLogicOr(const LogicOr *logicOr) {
584585
{
585586
m_out << tgfmt(R"(
586587
{
587-
std::array<std::string, $0> $orErrors;
588+
std::array<std::string, $0> $_orErrors;
588589
if (true
589590
)",
590591
&m_fmt, logicOr->branches().size());
@@ -596,7 +597,7 @@ bool Evaluator::checkLogicOr(const LogicOr *logicOr) {
596597

597598
m_out << tgfmt(R"(
598599
&& !([&]() {
599-
::llvm::raw_string_ostream $_errs($orErrors[$0]);
600+
::llvm::raw_string_ostream $_errs($_orErrors[$0]);
600601
)",
601602
&m_fmt, index);
602603

@@ -621,7 +622,7 @@ bool Evaluator::checkLogicOr(const LogicOr *logicOr) {
621622
m_fmt.addSubst("value", m_assignment.lookup(self));
622623

623624
m_out << tgfmt(R"(
624-
$_errs << " $name (" << printable($value))
625+
$_errs << " $name (" << printable($value)
625626
<< ") does not match any available option\n";
626627
)",
627628
&m_fmt);
@@ -635,7 +636,7 @@ bool Evaluator::checkLogicOr(const LogicOr *logicOr) {
635636
&m_fmt, i, logicOr->branchInits()[i]->getAsString());
636637
}
637638

638-
m_out << "}\n\n";
639+
m_out << "return false;\n}\n}\n";
639640
return true;
640641
}
641642

test/example/generated/ExampleDialect.cpp.inc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,59 @@ source,
640640
errs << " actual: " << printable(::llvm::cast<::llvm::FixedVectorType>(sourceType)->getNumElements()) << '\n';
641641
return false;
642642
}
643+
if (![&]() {
644+
645+
{
646+
std::array<std::string, 2> orErrors;
647+
if (true
648+
649+
&& !([&]() {
650+
::llvm::raw_string_ostream errs(orErrors[0]);
651+
652+
if (xd::VectorKind::LittleEndian != ::llvm::cast<XdVectorType>(resultType)->getKind()) {
653+
errs << " inconsistent value of eq:$rhs found\n";
654+
errs << " while checking VectorKindLittleEndian:\n";
655+
errs << " here: " << printable(xd::VectorKind::LittleEndian) << '\n';
656+
errs << " previously: " << printable(::llvm::cast<XdVectorType>(resultType)->getKind()) << '\n';
657+
return false;
658+
}
659+
return true;
660+
}())
661+
662+
&& !([&]() {
663+
::llvm::raw_string_ostream errs(orErrors[1]);
664+
665+
if (xd::VectorKind::BigEndian != ::llvm::cast<XdVectorType>(resultType)->getKind()) {
666+
errs << " inconsistent value of eq:$rhs found\n";
667+
errs << " while checking VectorKindBigEndian:\n";
668+
errs << " here: " << printable(xd::VectorKind::BigEndian) << '\n';
669+
errs << " previously: " << printable(::llvm::cast<XdVectorType>(resultType)->getKind()) << '\n';
670+
return false;
671+
}
672+
return true;
673+
}())
674+
) {
675+
676+
errs << " eq:$rhs (" << printable(::llvm::cast<XdVectorType>(resultType)->getKind())
677+
<< ") does not match any available option\n";
678+
679+
errs << " failed option 0 (VectorKindLittleEndian):\n"
680+
<< orErrors[0];
681+
682+
errs << " failed option 1 (VectorKindBigEndian):\n"
683+
<< orErrors[1];
684+
return false;
685+
}
686+
}
687+
688+
return true;
689+
}()) {
690+
errs << " while checking (isReasonableVectorKind ?:$kind)\n";
691+
692+
errs << " with $kind = " << printable(::llvm::cast<XdVectorType>(resultType)->getKind()) << '\n';
693+
694+
return false;
695+
}
643696
return true;
644697
}
645698

test/example/verifier-basic.ll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,29 @@ entry:
3535
; CHECK-LABEL: Verifier error in: %stream.min1 =
3636
; CHECK: wrong number of arguments: 2, expected 3
3737
%stream.min1 = call i8 (...) @xd.stream.min.i8(ptr %p, i64 14)
38+
39+
; CHECK-LABEL: Verifier error in: %fromfixedvector3 =
40+
; CHECK: eq:$rhs (MiddleEndian) does not match any available option
41+
; CHECK: failed option 0 (VectorKindLittleEndian):
42+
; CHECK: inconsistent value of eq:$rhs found
43+
; CHECK: while checking VectorKindLittleEndian:
44+
; CHECK: here: LittleEndian
45+
; CHECK: previously: MiddleEndian
46+
; CHECK: failed option 1 (VectorKindBigEndian):
47+
; CHECK: inconsistent value of eq:$rhs found
48+
; CHECK: while checking VectorKindBigEndian:
49+
; CHECK: here: BigEndian
50+
; CHECK: previously: MiddleEndian
51+
; CHECK: while checking (isReasonableVectorKind ?:$kind)
52+
; CHECK: with $kind = MiddleEndian
53+
%fromfixedvector3 = call target("xd.vector", i32, 2, 2) (...) @xd.fromfixedvector.txd.vector_i32_2_2t(<2 x i32> poison)
54+
3855
ret void
3956
}
4057

4158
declare i32 @xd.sizeof(...)
4259
declare i64 @xd.itrunc.i64(...)
4360
declare target("xd.vector", i32, 0, 2) @xd.fromfixedvector.txd.vector_i32_0_2t(...)
61+
declare target("xd.vector", i32, 2, 2) @xd.fromfixedvector.txd.vector_i32_2_2t(...)
4462
declare <2 x i16> @xd.stream.max.v2i16(...)
4563
declare i8 @xd.stream.min.i8(...)

0 commit comments

Comments
 (0)