[Mlir-commits] [mlir] [mlir][ods] Enable basic string interpolation in constraint summary. (PR #153603)
Jacques Pienaar
llvmlistbot at llvm.org
Thu Nov 6 14:48:53 PST 2025
https://github.com/jpienaar updated https://github.com/llvm/llvm-project/pull/153603
>From 904e1fdf437be4dfcd083e7b337012bc03617098 Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jacques+gh at japienaar.info>
Date: Thu, 14 Aug 2025 15:24:02 +0000
Subject: [PATCH 1/7] [mlir][ods] Enable basic string interpolation in
constraint summary.
This enables printing, for example, the attribute value from a
mismatched predicate. Example of resultant output (here made
non-negative report value seen as sign-extended int):
```
PDL/ops.mlir:21:1: error: 'pdl.pattern' op attribute 'benefit' failed to satisfy constraint: 16-bit signless integer attribute whose value is non-negative (got -31 : i16)
pdl.pattern @rewrite_with_args : benefit(-31) {
^
```
This is primarily the mechanism and didn't change any existing
constraints. I also attempted to keep the error format as close to the
original as possible - but did notice 2 errors that were inconsistent
with the rest and updated them to be consistent.
---
mlir/include/mlir/TableGen/CodeGenHelpers.h | 20 +++-
mlir/lib/TableGen/CodeGenHelpers.cpp | 91 ++++++++++++++++---
mlir/test/mlir-tblgen/constraint-unique.td | 6 +-
mlir/test/mlir-tblgen/op-attribute.td | 16 ++--
.../mlir-tblgen/op-properties-predicates.td | 2 +-
mlir/test/mlir-tblgen/predicate.td | 16 ++--
mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp | 47 +++++++---
mlir/unittests/TableGen/CMakeLists.txt | 1 +
.../unittests/TableGen/CodeGenHelpersTest.cpp | 30 ++++++
9 files changed, 184 insertions(+), 45 deletions(-)
create mode 100644 mlir/unittests/TableGen/CodeGenHelpersTest.cpp
diff --git a/mlir/include/mlir/TableGen/CodeGenHelpers.h b/mlir/include/mlir/TableGen/CodeGenHelpers.h
index 997aef26bdc01..ae470a05e8650 100644
--- a/mlir/include/mlir/TableGen/CodeGenHelpers.h
+++ b/mlir/include/mlir/TableGen/CodeGenHelpers.h
@@ -52,6 +52,13 @@ class DialectNamespaceEmitter {
std::optional<llvm::NamespaceEmitter> nsEmitter;
};
+enum class ErrorStreamType {
+ // Inside a string that's streamed into an InflightDiagnostic.
+ InString,
+ // Inside a string inside an OpError.
+ InsideOpError,
+};
+
/// This class deduplicates shared operation verification code by emitting
/// static functions alongside the op definitions. These methods are local to
/// the definition file, and are invoked within the operation verify methods.
@@ -192,7 +199,8 @@ class StaticVerifierFunctionEmitter {
/// A generic function to emit constraints
void emitConstraints(const ConstraintMap &constraints, StringRef selfName,
- const char *codeTemplate);
+ const char *codeTemplate,
+ ErrorStreamType errorStreamType);
/// Assign a unique name to a unique constraint.
std::string getUniqueName(StringRef kind, unsigned index);
@@ -243,6 +251,16 @@ std::string stringify(T &&t) {
apply(std::forward<T>(t));
}
+/// Helper to generate a C++ streaming error messages from a given message.
+/// Message can contain '{{...}}' placeholders that are substituted with
+/// C-expressions via tgfmt. It would effectively convert:
+/// "Failed to verify {{foo}}"
+/// into:
+/// "Failed to verify " << tgfmt(foo, &ctx)
+std::string buildErrorStreamingString(
+ StringRef message, const FmtContext &ctx,
+ ErrorStreamType errorStreamType = ErrorStreamType::InString);
+
} // namespace tblgen
} // namespace mlir
diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp
index d52d5e769ee6d..a36c0722c09e2 100644
--- a/mlir/lib/TableGen/CodeGenHelpers.cpp
+++ b/mlir/lib/TableGen/CodeGenHelpers.cpp
@@ -12,12 +12,26 @@
//===----------------------------------------------------------------------===//
#include "mlir/TableGen/CodeGenHelpers.h"
+#include "mlir/Support/LLVM.h"
+#include "mlir/TableGen/Argument.h"
+#include "mlir/TableGen/Attribute.h"
+#include "mlir/TableGen/Format.h"
#include "mlir/TableGen/Operator.h"
#include "mlir/TableGen/Pattern.h"
+#include "mlir/TableGen/Property.h"
+#include "mlir/TableGen/Region.h"
+#include "mlir/TableGen/Successor.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/CodeGenHelpers.h"
+#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
+#include <cassert>
+#include <optional>
+#include <string>
using namespace llvm;
using namespace mlir;
@@ -112,6 +126,56 @@ StringRef StaticVerifierFunctionEmitter::getRegionConstraintFn(
// Constraint Emission
//===----------------------------------------------------------------------===//
+/// Helper to generate a C++ string expression from a given message.
+/// Message can contain '{{...}}' placeholders that are substituted with
+/// C-expressions via tgfmt.
+std::string mlir::tblgen::buildErrorStreamingString(
+ StringRef message, const FmtContext &ctx, ErrorStreamType errorStreamType) {
+ std::string result;
+ raw_string_ostream os(result);
+
+ std::string msgStr = escapeString(message);
+ StringRef msg = msgStr;
+
+ // Split the message by '{{' and '}}' and build a streaming expression.
+ auto split = msg.split("{{");
+ if (split.second.empty()) {
+ os << split.first;
+ return msgStr;
+ }
+
+ os << split.first;
+ if (errorStreamType == ErrorStreamType::InsideOpError)
+ os << "\")";
+ else
+ os << '"';
+
+ msg = split.second;
+ while (!msg.empty()) {
+ split = msg.split("}}");
+ StringRef var = split.first;
+ StringRef rest = split.second;
+
+ os << " << " << tgfmt(var, &ctx);
+
+ if (rest.empty())
+ break;
+
+ split = rest.split("{{");
+ if (split.second.empty() &&
+ errorStreamType == ErrorStreamType::InsideOpError) {
+ // To enable having part of string post, this adds a parenthesis before
+ // the last string segment to match the exiting one.
+ os << " << (\"" << split.first;
+ } else {
+ os << " << \"" << split.first;
+ }
+ msg = split.second;
+ }
+
+ return os.str();
+}
+
/// Code templates for emitting type, attribute, successor, and region
/// constraints. Each of these templates require the following arguments:
///
@@ -224,22 +288,24 @@ static ::llvm::LogicalResult {0}(
void StaticVerifierFunctionEmitter::emitConstraints(
const ConstraintMap &constraints, StringRef selfName,
- const char *const codeTemplate) {
+ const char *const codeTemplate, ErrorStreamType errorStreamType) {
FmtContext ctx;
ctx.addSubst("_op", "*op").withSelf(selfName);
+
for (auto &it : constraints) {
os << formatv(codeTemplate, it.second,
tgfmt(it.first.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()));
+ buildErrorStreamingString(it.first.getSummary(), ctx));
}
}
-
void StaticVerifierFunctionEmitter::emitTypeConstraints() {
- emitConstraints(typeConstraints, "type", typeConstraintCode);
+ emitConstraints(typeConstraints, "type", typeConstraintCode,
+ ErrorStreamType::InString);
}
void StaticVerifierFunctionEmitter::emitAttrConstraints() {
- emitConstraints(attrConstraints, "attr", attrConstraintCode);
+ emitConstraints(attrConstraints, "attr", attrConstraintCode,
+ ErrorStreamType::InString);
}
/// Unlike with the other helpers, this one has to substitute in the interface
@@ -251,17 +317,19 @@ void StaticVerifierFunctionEmitter::emitPropConstraints() {
auto propConstraint = cast<PropConstraint>(it.first);
os << formatv(propConstraintCode, it.second,
tgfmt(propConstraint.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()),
+ buildErrorStreamingString(it.first.getSummary(), ctx),
propConstraint.getInterfaceType());
}
}
void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
- emitConstraints(successorConstraints, "successor", successorConstraintCode);
+ emitConstraints(successorConstraints, "successor", successorConstraintCode,
+ ErrorStreamType::InString);
}
void StaticVerifierFunctionEmitter::emitRegionConstraints() {
- emitConstraints(regionConstraints, "region", regionConstraintCode);
+ emitConstraints(regionConstraints, "region", regionConstraintCode,
+ ErrorStreamType::InString);
}
void StaticVerifierFunctionEmitter::emitPatternConstraints() {
@@ -270,13 +338,14 @@ void StaticVerifierFunctionEmitter::emitPatternConstraints() {
for (auto &it : typeConstraints) {
os << formatv(patternConstraintCode, it.second,
tgfmt(it.first.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()), "::mlir::Type type");
+ buildErrorStreamingString(it.first.getSummary(), ctx),
+ "::mlir::Type type");
}
ctx.withSelf("attr");
for (auto &it : attrConstraints) {
os << formatv(patternConstraintCode, it.second,
tgfmt(it.first.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()),
+ buildErrorStreamingString(it.first.getSummary(), ctx),
"::mlir::Attribute attr");
}
ctx.withSelf("prop");
@@ -291,7 +360,7 @@ void StaticVerifierFunctionEmitter::emitPatternConstraints() {
}
os << formatv(patternConstraintCode, it.second,
tgfmt(propConstraint.getConditionTemplate(), &ctx),
- escapeString(propConstraint.getSummary()),
+ buildErrorStreamingString(propConstraint.getSummary(), ctx),
Twine(interfaceType) + " prop");
}
}
diff --git a/mlir/test/mlir-tblgen/constraint-unique.td b/mlir/test/mlir-tblgen/constraint-unique.td
index d51e1a5f43ee7..44dc57d524fb6 100644
--- a/mlir/test/mlir-tblgen/constraint-unique.td
+++ b/mlir/test/mlir-tblgen/constraint-unique.td
@@ -16,7 +16,7 @@ def AType : Type<ATypePred, "a type">;
def OtherType : Type<ATypePred, "another type">;
def AnAttrPred : CPred<"attrPred($_self, $_op)">;
-def AnAttr : Attr<AnAttrPred, "an attribute">;
+def AnAttr : Attr<AnAttrPred, "an attribute (got {{reformat($_self)}})">;
def OtherAttr : Attr<AnAttrPred, "another attribute">;
def ASuccessorPred : CPred<"successorPred($_self, $_op)">;
@@ -71,10 +71,10 @@ def OpC : NS_Op<"op_c"> {
// CHECK: static ::llvm::LogicalResult [[$A_ATTR_CONSTRAINT:__mlir_ods_local_attr_constraint.*]](
// CHECK: if (attr && !((attrPred(attr, *op))))
// CHECK-NEXT: return emitError() << "attribute '" << attrName
-// CHECK-NEXT: << "' failed to satisfy constraint: an attribute";
+// CHECK-NEXT: << "' failed to satisfy constraint: an attribute (got " << reformat(attr) << ")";
/// Test that duplicate attribute constraint was not generated.
-// CHECK-NOT: << "' failed to satisfy constraint: an attribute";
+// CHECK-NOT: << "' failed to satisfy constraint: an attribute
/// Test that a attribute constraint with a different description was generated.
// CHECK: static ::llvm::LogicalResult [[$O_ATTR_CONSTRAINT:__mlir_ods_local_attr_constraint.*]](
diff --git a/mlir/test/mlir-tblgen/op-attribute.td b/mlir/test/mlir-tblgen/op-attribute.td
index 549830e06042f..a3cb9a41a5b7f 100644
--- a/mlir/test/mlir-tblgen/op-attribute.td
+++ b/mlir/test/mlir-tblgen/op-attribute.td
@@ -69,19 +69,19 @@ def AOp : NS_Op<"a_op", []> {
// DEF: ::llvm::LogicalResult AOpAdaptor::verify
// DEF-NEXT: auto tblgen_aAttr = getProperties().aAttr; (void)tblgen_aAttr;
-// DEF-NEXT: if (!tblgen_aAttr) return emitError(loc, "'test.a_op' op ""requires attribute 'aAttr'");
+// DEF-NEXT: if (!tblgen_aAttr) return emitError(loc, "'test.a_op' op requires attribute 'aAttr'");
// DEF-NEXT: auto tblgen_bAttr = getProperties().bAttr; (void)tblgen_bAttr;
// DEF-NEXT: auto tblgen_cAttr = getProperties().cAttr; (void)tblgen_cAttr;
// DEF-NEXT: auto tblgen_dAttr = getProperties().dAttr; (void)tblgen_dAttr;
// DEF: if (tblgen_aAttr && !((some-condition)))
-// DEF-NEXT: return emitError(loc, "'test.a_op' op ""attribute 'aAttr' failed to satisfy constraint: some attribute kind");
+// DEF-NEXT: return emitError(loc, "'test.a_op' op attribute 'aAttr' failed to satisfy constraint: some attribute kind");
// DEF: if (tblgen_bAttr && !((some-condition)))
-// DEF-NEXT: return emitError(loc, "'test.a_op' op ""attribute 'bAttr' failed to satisfy constraint: some attribute kind");
+// DEF-NEXT: return emitError(loc, "'test.a_op' op attribute 'bAttr' failed to satisfy constraint: some attribute kind");
// DEF: if (tblgen_cAttr && !((some-condition)))
-// DEF-NEXT: return emitError(loc, "'test.a_op' op ""attribute 'cAttr' failed to satisfy constraint: some attribute kind");
+// DEF-NEXT: return emitError(loc, "'test.a_op' op attribute 'cAttr' failed to satisfy constraint: some attribute kind");
// DEF: if (tblgen_dAttr && !((some-condition)))
-// DEF-NEXT: return emitError(loc, "'test.a_op' op ""attribute 'dAttr' failed to satisfy constraint: some attribute kind");
+// DEF-NEXT: return emitError(loc, "'test.a_op' op attribute 'dAttr' failed to satisfy constraint: some attribute kind");
// Test getter methods
// ---
@@ -219,13 +219,13 @@ def AgetOp : Op<Test2_Dialect, "a_get_op", []> {
// DEF: ::llvm::LogicalResult AgetOpAdaptor::verify
// DEF: auto tblgen_aAttr = getProperties().aAttr; (void)tblgen_aAttr;
-// DEF: if (!tblgen_aAttr) return emitError(loc, "'test2.a_get_op' op ""requires attribute 'aAttr'");
+// DEF: if (!tblgen_aAttr) return emitError(loc, "'test2.a_get_op' op requires attribute 'aAttr'");
// DEF: auto tblgen_bAttr = getProperties().bAttr; (void)tblgen_bAttr;
// DEF: auto tblgen_cAttr = getProperties().cAttr; (void)tblgen_cAttr;
// DEF: if (tblgen_bAttr && !((some-condition)))
-// DEF-NEXT: return emitError(loc, "'test2.a_get_op' op ""attribute 'bAttr' failed to satisfy constraint: some attribute kind");
+// DEF-NEXT: return emitError(loc, "'test2.a_get_op' op attribute 'bAttr' failed to satisfy constraint: some attribute kind");
// DEF: if (tblgen_cAttr && !((some-condition)))
-// DEF-NEXT: return emitError(loc, "'test2.a_get_op' op ""attribute 'cAttr' failed to satisfy constraint: some attribute kind");
+// DEF-NEXT: return emitError(loc, "'test2.a_get_op' op attribute 'cAttr' failed to satisfy constraint: some attribute kind");
// Test getter methods
// ---
diff --git a/mlir/test/mlir-tblgen/op-properties-predicates.td b/mlir/test/mlir-tblgen/op-properties-predicates.td
index af09ee7c12f53..7cc9633850069 100644
--- a/mlir/test/mlir-tblgen/op-properties-predicates.td
+++ b/mlir/test/mlir-tblgen/op-properties-predicates.td
@@ -74,7 +74,7 @@ def OpWithPredicates : NS_Op<"op_with_predicates"> {
// Note: comprehensive emission of verifiers is tested in verifyINvariantsImpl() below
// CHECK: int64_t tblgen_scalar = this->getScalar();
// CHECK: if (!((tblgen_scalar >= 0)))
-// CHECK: return emitError(loc, "'test.op_with_predicates' op ""property 'scalar' failed to satisfy constraint: non-negative int64_t");
+// CHECK: return emitError(loc, "'test.op_with_predicates' op property 'scalar' failed to satisfy constraint: non-negative int64_t");
// CHECK-LABEL: OpWithPredicates::verifyInvariantsImpl()
// Note: for test readability, we capture [[maybe_unused]] into the variable maybe_unused
diff --git a/mlir/test/mlir-tblgen/predicate.td b/mlir/test/mlir-tblgen/predicate.td
index c1fcd3fa76089..41e041f171213 100644
--- a/mlir/test/mlir-tblgen/predicate.td
+++ b/mlir/test/mlir-tblgen/predicate.td
@@ -55,7 +55,7 @@ def OpF : NS_Op<"op_for_int_min_val", []> {
// CHECK-LABEL: OpFAdaptor::verify
// CHECK: (::llvm::cast<::mlir::IntegerAttr>(tblgen_attr).getInt() >= 10)
-// CHECK-NEXT: "attribute 'attr' failed to satisfy constraint: 32-bit signless integer attribute whose minimum value is 10"
+// CHECK-NEXT: attribute 'attr' failed to satisfy constraint: 32-bit signless integer attribute whose minimum value is 10"
def OpFX : NS_Op<"op_for_int_max_val", []> {
let arguments = (ins ConfinedAttr<I32Attr, [IntMaxValue<10>]>:$attr);
@@ -63,7 +63,7 @@ def OpFX : NS_Op<"op_for_int_max_val", []> {
// CHECK-LABEL: OpFXAdaptor::verify
// CHECK: (::llvm::cast<::mlir::IntegerAttr>(tblgen_attr).getInt() <= 10)
-// CHECK-NEXT: "attribute 'attr' failed to satisfy constraint: 32-bit signless integer attribute whose maximum value is 10"
+// CHECK-NEXT: attribute 'attr' failed to satisfy constraint: 32-bit signless integer attribute whose maximum value is 10"
def OpG : NS_Op<"op_for_arr_min_count", []> {
let arguments = (ins ConfinedAttr<ArrayAttr, [ArrayMinCount<8>]>:$attr);
@@ -71,7 +71,7 @@ def OpG : NS_Op<"op_for_arr_min_count", []> {
// CHECK-LABEL: OpGAdaptor::verify
// CHECK: (::llvm::cast<::mlir::ArrayAttr>(tblgen_attr).size() >= 8)
-// CHECK-NEXT: "attribute 'attr' failed to satisfy constraint: array attribute with at least 8 elements"
+// CHECK-NEXT: attribute 'attr' failed to satisfy constraint: array attribute with at least 8 elements"
def OpH : NS_Op<"op_for_arr_value_at_index", []> {
let arguments = (ins ConfinedAttr<ArrayAttr, [IntArrayNthElemEq<0, 8>]>:$attr);
@@ -79,7 +79,7 @@ def OpH : NS_Op<"op_for_arr_value_at_index", []> {
// CHECK-LABEL: OpHAdaptor::verify
// CHECK: (((::llvm::cast<::mlir::ArrayAttr>(tblgen_attr).size() > 0)) && ((::llvm::cast<::mlir::IntegerAttr>(::llvm::cast<::mlir::ArrayAttr>(tblgen_attr)[0]).getInt() == 8)))))
-// CHECK-NEXT: "attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be 8"
+// CHECK-NEXT: attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be 8"
def OpI: NS_Op<"op_for_arr_min_value_at_index", []> {
let arguments = (ins ConfinedAttr<ArrayAttr, [IntArrayNthElemMinValue<0, 8>]>:$attr);
@@ -87,7 +87,7 @@ def OpI: NS_Op<"op_for_arr_min_value_at_index", []> {
// CHECK-LABEL: OpIAdaptor::verify
// CHECK: (((::llvm::cast<::mlir::ArrayAttr>(tblgen_attr).size() > 0)) && ((::llvm::cast<::mlir::IntegerAttr>(::llvm::cast<::mlir::ArrayAttr>(tblgen_attr)[0]).getInt() >= 8)))))
-// CHECK-NEXT: "attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be at least 8"
+// CHECK-NEXT: attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be at least 8"
def OpJ: NS_Op<"op_for_arr_max_value_at_index", []> {
let arguments = (ins ConfinedAttr<ArrayAttr, [IntArrayNthElemMaxValue<0, 8>]>:$attr);
@@ -95,7 +95,7 @@ def OpJ: NS_Op<"op_for_arr_max_value_at_index", []> {
// CHECK-LABEL: OpJAdaptor::verify
// CHECK: (((::llvm::cast<::mlir::ArrayAttr>(tblgen_attr).size() > 0)) && ((::llvm::cast<::mlir::IntegerAttr>(::llvm::cast<::mlir::ArrayAttr>(tblgen_attr)[0]).getInt() <= 8)))))
-// CHECK-NEXT: "attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be at most 8"
+// CHECK-NEXT: attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be at most 8"
def OpK: NS_Op<"op_for_arr_in_range_at_index", []> {
let arguments = (ins ConfinedAttr<ArrayAttr, [IntArrayNthElemInRange<0, 4, 8>]>:$attr);
@@ -103,7 +103,7 @@ def OpK: NS_Op<"op_for_arr_in_range_at_index", []> {
// CHECK-LABEL: OpKAdaptor::verify
// CHECK: (((::llvm::cast<::mlir::ArrayAttr>(tblgen_attr).size() > 0)) && ((::llvm::cast<::mlir::IntegerAttr>(::llvm::cast<::mlir::ArrayAttr>(tblgen_attr)[0]).getInt() >= 4)) && ((::llvm::cast<::mlir::IntegerAttr>(::llvm::cast<::mlir::ArrayAttr>(tblgen_attr)[0]).getInt() <= 8)))))
-// CHECK-NEXT: "attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be at least 4 and at most 8"
+// CHECK-NEXT: attribute 'attr' failed to satisfy constraint: array attribute whose 0-th element must be at least 4 and at most 8"
def OpL: NS_Op<"op_for_TCopVTEtAreSameAt", [
PredOpTrait<"operands indexed at 0, 2, 3 should all have "
@@ -121,7 +121,7 @@ def OpL: NS_Op<"op_for_TCopVTEtAreSameAt", [
// CHECK: ::llvm::all_equal(::llvm::map_range(
// CHECK-SAME: ::mlir::ArrayRef<unsigned>({0, 2, 3}),
// CHECK-SAME: [this](unsigned i) { return getElementTypeOrSelf(this->getOperand(i)); }))
-// CHECK: "failed to verify that operands indexed at 0, 2, 3 should all have the same type"
+// CHECK: failed to verify that operands indexed at 0, 2, 3 should all have the same type"
def OpM : NS_Op<"op_for_AnyTensorOf", []> {
let arguments = (ins TensorOf<[F32, I32]>:$x);
diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
index 4d9b1b2328018..ed948bfe0875a 100644
--- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
@@ -17,6 +17,7 @@
#include "OpGenHelpers.h"
#include "mlir/TableGen/Argument.h"
#include "mlir/TableGen/Attribute.h"
+#include "mlir/TableGen/Builder.h"
#include "mlir/TableGen/Class.h"
#include "mlir/TableGen/CodeGenHelpers.h"
#include "mlir/TableGen/Format.h"
@@ -24,22 +25,39 @@
#include "mlir/TableGen/Interfaces.h"
#include "mlir/TableGen/Operator.h"
#include "mlir/TableGen/Property.h"
+#include "mlir/TableGen/Region.h"
#include "mlir/TableGen/SideEffects.h"
+#include "mlir/TableGen/Successor.h"
#include "mlir/TableGen/Trait.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/CodeGenHelpers.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
#define DEBUG_TYPE "mlir-tblgen-opdefgen"
@@ -380,9 +398,8 @@ class OpOrAdaptorHelper {
Formatter emitErrorPrefix() const {
return [this](raw_ostream &os) -> raw_ostream & {
if (emitForOp)
- return os << "emitOpError(";
- return os << formatv("emitError(loc, \"'{0}' op \"",
- op.getOperationName());
+ return os << "emitOpError(\"";
+ return os << formatv("emitError(loc, \"'{0}' op ", op.getOperationName());
};
}
@@ -940,7 +957,7 @@ genAttributeVerifier(const OpOrAdaptorHelper &emitHelper, FmtContext &ctx,
// {4}: Attribute/constraint description.
const char *const verifyAttrInline = R"(
if ({0} && !({1}))
- return {2}"attribute '{3}' failed to satisfy constraint: {4}");
+ return {2}attribute '{3}' failed to satisfy constraint: {4}");
)";
// Verify the attribute using a uniqued constraint. Can only be used within
// the context of an op.
@@ -993,10 +1010,11 @@ while (true) {{
(constraintFn = staticVerifierEmitter.getAttrConstraintFn(attr))) {
body << formatv(verifyAttrUnique, *constraintFn, varName, attrName);
} else {
- body << formatv(verifyAttrInline, varName,
- tgfmt(condition, &ctx.withSelf(varName)),
- emitHelper.emitErrorPrefix(), attrName,
- escapeString(attr.getSummary()));
+ body << formatv(
+ verifyAttrInline, varName, tgfmt(condition, &ctx.withSelf(varName)),
+ emitHelper.emitErrorPrefix(), attrName,
+ buildErrorStreamingString(attr.getSummary(), ctx.withSelf(varName),
+ ErrorStreamType::InsideOpError));
}
};
@@ -1017,7 +1035,7 @@ while (true) {{
it.first);
if (metadata.isRequired)
body << formatv(
- "if (!tblgen_{0}) return {1}\"requires attribute '{0}'\");\n",
+ "if (!tblgen_{0}) return {1}requires attribute '{0}'\");\n",
it.first, emitHelper.emitErrorPrefix());
}
} else {
@@ -1099,7 +1117,7 @@ static void genPropertyVerifier(
// {3}: Property description.
const char *const verifyPropertyInline = R"(
if (!({0}))
- return {1}"property '{2}' failed to satisfy constraint: {3}");
+ return {1}property '{2}' failed to satisfy constraint: {3}");
)";
// Verify the property using a uniqued constraint. Can only be used
@@ -1143,9 +1161,12 @@ static void genPropertyVerifier(
if (uniquedFn.has_value() && emitHelper.isEmittingForOp())
body << formatv(verifyPropertyUniqued, *uniquedFn, varName, prop.name);
else
- body << formatv(
- verifyPropertyInline, tgfmt(rawCondition, &ctx.withSelf(varName)),
- emitHelper.emitErrorPrefix(), prop.name, prop.prop.getSummary());
+ body << formatv(verifyPropertyInline,
+ tgfmt(rawCondition, &ctx.withSelf(varName)),
+ emitHelper.emitErrorPrefix(), prop.name,
+ buildErrorStreamingString(
+ prop.prop.getSummary(), ctx.withSelf(varName),
+ ErrorStreamType::InsideOpError));
}
}
diff --git a/mlir/unittests/TableGen/CMakeLists.txt b/mlir/unittests/TableGen/CMakeLists.txt
index c51bda6e8d6cc..31bf772ba41b4 100644
--- a/mlir/unittests/TableGen/CMakeLists.txt
+++ b/mlir/unittests/TableGen/CMakeLists.txt
@@ -8,6 +8,7 @@ mlir_tablegen(PassGenTest.h.inc -gen-pass-decls -name TableGenTest)
add_public_tablegen_target(MLIRTableGenTestPassIncGen)
add_mlir_unittest(MLIRTableGenTests
+ CodeGenHelpersTest.cpp
EnumsGenTest.cpp
FormatTest.cpp
OpBuildGen.cpp
diff --git a/mlir/unittests/TableGen/CodeGenHelpersTest.cpp b/mlir/unittests/TableGen/CodeGenHelpersTest.cpp
new file mode 100644
index 0000000000000..52a90738f2c3c
--- /dev/null
+++ b/mlir/unittests/TableGen/CodeGenHelpersTest.cpp
@@ -0,0 +1,30 @@
+//===- CodeGenHelpersTest.cpp - TableGen CodeGenHelpers Utility Tests -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/TableGen/CodeGenHelpers.h"
+#include "mlir/TableGen/Format.h"
+#include "gmock/gmock.h"
+
+using mlir::tblgen::buildErrorStreamingString;
+using mlir::tblgen::ErrorStreamType;
+using mlir::tblgen::FmtContext;
+using ::testing::StrEq;
+
+TEST(CodeGenHelpersTest, BuildErrorStreamingString) {
+ FmtContext ctx;
+ ctx.withSelf("this_thing");
+ std::string result1 =
+ buildErrorStreamingString("here {{reformat($_self)}} is block", ctx,
+ ErrorStreamType::InsideOpError);
+ EXPECT_THAT(result1,
+ StrEq("here \") << reformat(this_thing) << (\" is block"));
+ std::string result2 = buildErrorStreamingString(
+ "here {{reformat($_self)}} is block", ctx, ErrorStreamType::InString);
+ EXPECT_THAT(result2, StrEq("here \" << reformat(this_thing) << \" is block"));
+}
+
>From f51414d98e7f281d941bee2e5e39bf052bf73ceb Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jacques+gh at japienaar.info>
Date: Thu, 14 Aug 2025 15:52:14 +0000
Subject: [PATCH 2/7] Formatting
---
mlir/unittests/TableGen/CodeGenHelpersTest.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/mlir/unittests/TableGen/CodeGenHelpersTest.cpp b/mlir/unittests/TableGen/CodeGenHelpersTest.cpp
index 52a90738f2c3c..a16df639d2f7d 100644
--- a/mlir/unittests/TableGen/CodeGenHelpersTest.cpp
+++ b/mlir/unittests/TableGen/CodeGenHelpersTest.cpp
@@ -27,4 +27,3 @@ TEST(CodeGenHelpersTest, BuildErrorStreamingString) {
"here {{reformat($_self)}} is block", ctx, ErrorStreamType::InString);
EXPECT_THAT(result2, StrEq("here \" << reformat(this_thing) << \" is block"));
}
-
>From 83da7e2f33170e787df1ad1b53449548c18ff0a5 Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jacques+gh at japienaar.info>
Date: Fri, 15 Aug 2025 07:47:53 +0000
Subject: [PATCH 3/7] Update cmake to potentially address windows failure
---
mlir/unittests/TableGen/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/unittests/TableGen/CMakeLists.txt b/mlir/unittests/TableGen/CMakeLists.txt
index 31bf772ba41b4..0c7db774d6a21 100644
--- a/mlir/unittests/TableGen/CMakeLists.txt
+++ b/mlir/unittests/TableGen/CMakeLists.txt
@@ -26,6 +26,6 @@ target_include_directories(MLIRTableGenTests
)
target_link_libraries(MLIRTableGenTests
- PRIVATE MLIRTableGen MLIRIR
+ PRIVATE LLVMTableGen MLIRTableGen MLIRIR
PUBLIC MLIRTestDialect
)
>From a41b032dbe0350bb3bf817a3b5cead2688a66721 Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jacques+gh at japienaar.info>
Date: Thu, 6 Nov 2025 05:46:02 +0000
Subject: [PATCH 4/7] Rebase & address review comments
---
mlir/include/mlir/TableGen/CodeGenHelpers.h | 6 +++---
mlir/lib/TableGen/CodeGenHelpers.cpp | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/mlir/include/mlir/TableGen/CodeGenHelpers.h b/mlir/include/mlir/TableGen/CodeGenHelpers.h
index ae470a05e8650..283a62c274f21 100644
--- a/mlir/include/mlir/TableGen/CodeGenHelpers.h
+++ b/mlir/include/mlir/TableGen/CodeGenHelpers.h
@@ -251,12 +251,12 @@ std::string stringify(T &&t) {
apply(std::forward<T>(t));
}
-/// Helper to generate a C++ streaming error messages from a given message.
+/// Helper to generate a C++ streaming error message from a given message.
/// Message can contain '{{...}}' placeholders that are substituted with
/// C-expressions via tgfmt. It would effectively convert:
-/// "Failed to verify {{foo}}"
+/// "failed to verify {{foo}}"
/// into:
-/// "Failed to verify " << tgfmt(foo, &ctx)
+/// "failed to verify " << tgfmt(foo, &ctx)
std::string buildErrorStreamingString(
StringRef message, const FmtContext &ctx,
ErrorStreamType errorStreamType = ErrorStreamType::InString);
diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp
index a36c0722c09e2..c42807d9ca4da 100644
--- a/mlir/lib/TableGen/CodeGenHelpers.cpp
+++ b/mlir/lib/TableGen/CodeGenHelpers.cpp
@@ -165,7 +165,7 @@ std::string mlir::tblgen::buildErrorStreamingString(
if (split.second.empty() &&
errorStreamType == ErrorStreamType::InsideOpError) {
// To enable having part of string post, this adds a parenthesis before
- // the last string segment to match the exiting one.
+ // the last string segment to match the existing one.
os << " << (\"" << split.first;
} else {
os << " << \"" << split.first;
>From ebb3987526c7e825daf29f45074163bf05c1145d Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jacques+gh at japienaar.info>
Date: Thu, 6 Nov 2025 09:58:50 +0000
Subject: [PATCH 5/7] Address review comments
---
mlir/include/mlir/TableGen/CodeGenHelpers.h | 6 +++++-
mlir/lib/TableGen/CodeGenHelpers.cpp | 3 +--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/mlir/include/mlir/TableGen/CodeGenHelpers.h b/mlir/include/mlir/TableGen/CodeGenHelpers.h
index 283a62c274f21..b56172f55a157 100644
--- a/mlir/include/mlir/TableGen/CodeGenHelpers.h
+++ b/mlir/include/mlir/TableGen/CodeGenHelpers.h
@@ -52,6 +52,8 @@ class DialectNamespaceEmitter {
std::optional<llvm::NamespaceEmitter> nsEmitter;
};
+/// This class represents how an error stream string being constructed will be
+/// consumed.
enum class ErrorStreamType {
// Inside a string that's streamed into an InflightDiagnostic.
InString,
@@ -256,7 +258,9 @@ std::string stringify(T &&t) {
/// C-expressions via tgfmt. It would effectively convert:
/// "failed to verify {{foo}}"
/// into:
-/// "failed to verify " << tgfmt(foo, &ctx)
+/// "failed to verify " << bar
+/// where bar is the result of evaluating 'tgfmt("foo", &ctx)' at compile
+/// time.
std::string buildErrorStreamingString(
StringRef message, const FmtContext &ctx,
ErrorStreamType errorStreamType = ErrorStreamType::InString);
diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp
index c42807d9ca4da..9ad031eb701ad 100644
--- a/mlir/lib/TableGen/CodeGenHelpers.cpp
+++ b/mlir/lib/TableGen/CodeGenHelpers.cpp
@@ -139,12 +139,11 @@ std::string mlir::tblgen::buildErrorStreamingString(
// Split the message by '{{' and '}}' and build a streaming expression.
auto split = msg.split("{{");
+ os << split.first;
if (split.second.empty()) {
- os << split.first;
return msgStr;
}
- os << split.first;
if (errorStreamType == ErrorStreamType::InsideOpError)
os << "\")";
else
>From 8d8d2eb4f006faf95f4219034b3273d36ba13702 Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jacques+gh at japienaar.info>
Date: Thu, 6 Nov 2025 14:23:57 +0000
Subject: [PATCH 6/7] Remove transitive std headers here
---
mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
index ed948bfe0875a..3b10842f2a127 100644
--- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
@@ -49,15 +49,6 @@
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <optional>
-#include <string>
-#include <utility>
-#include <vector>
#define DEBUG_TYPE "mlir-tblgen-opdefgen"
>From 1e881058b839feb68885ad63da777627978300f9 Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jacques+gh at japienaar.info>
Date: Thu, 6 Nov 2025 22:44:53 +0000
Subject: [PATCH 7/7] Add additional formatting test in lit
---
mlir/test/mlir-tblgen/constraint-unique.td | 4 +--
mlir/unittests/TableGen/CMakeLists.txt | 1 -
.../unittests/TableGen/CodeGenHelpersTest.cpp | 29 -------------------
3 files changed, 2 insertions(+), 32 deletions(-)
delete mode 100644 mlir/unittests/TableGen/CodeGenHelpersTest.cpp
diff --git a/mlir/test/mlir-tblgen/constraint-unique.td b/mlir/test/mlir-tblgen/constraint-unique.td
index 44dc57d524fb6..3f2e5cd4bfad4 100644
--- a/mlir/test/mlir-tblgen/constraint-unique.td
+++ b/mlir/test/mlir-tblgen/constraint-unique.td
@@ -24,7 +24,7 @@ def ASuccessor : Successor<ASuccessorPred, "a successor">;
def OtherSuccessor : Successor<ASuccessorPred, "another successor">;
def ARegionPred : CPred<"regionPred($_self, $_op)">;
-def ARegion : Region<ARegionPred, "a region">;
+def ARegion : Region<ARegionPred, "a region ({{find(foo)}})">;
def OtherRegion : Region<ARegionPred, "another region">;
// OpA and OpB have the same type, attribute, successor, and region constraints.
@@ -103,7 +103,7 @@ def OpC : NS_Op<"op_c"> {
// CHECK: if (!((regionPred(region, *op)))) {
// CHECK-NEXT: return op->emitOpError("region #") << regionIndex
// CHECK-NEXT: << (regionName.empty() ? " " : " ('" + regionName + "') ")
-// CHECK-NEXT: << "failed to verify constraint: a region";
+// CHECK-NEXT: << "failed to verify constraint: a region (" << find(foo) << ")";
/// Test that duplicate region constraint was not generated.
// CHECK-NOT: << "failed to verify constraint: a region";
diff --git a/mlir/unittests/TableGen/CMakeLists.txt b/mlir/unittests/TableGen/CMakeLists.txt
index 0c7db774d6a21..4d8e508ecdf5b 100644
--- a/mlir/unittests/TableGen/CMakeLists.txt
+++ b/mlir/unittests/TableGen/CMakeLists.txt
@@ -8,7 +8,6 @@ mlir_tablegen(PassGenTest.h.inc -gen-pass-decls -name TableGenTest)
add_public_tablegen_target(MLIRTableGenTestPassIncGen)
add_mlir_unittest(MLIRTableGenTests
- CodeGenHelpersTest.cpp
EnumsGenTest.cpp
FormatTest.cpp
OpBuildGen.cpp
diff --git a/mlir/unittests/TableGen/CodeGenHelpersTest.cpp b/mlir/unittests/TableGen/CodeGenHelpersTest.cpp
deleted file mode 100644
index a16df639d2f7d..0000000000000
--- a/mlir/unittests/TableGen/CodeGenHelpersTest.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-//===- CodeGenHelpersTest.cpp - TableGen CodeGenHelpers Utility Tests -----===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "mlir/TableGen/CodeGenHelpers.h"
-#include "mlir/TableGen/Format.h"
-#include "gmock/gmock.h"
-
-using mlir::tblgen::buildErrorStreamingString;
-using mlir::tblgen::ErrorStreamType;
-using mlir::tblgen::FmtContext;
-using ::testing::StrEq;
-
-TEST(CodeGenHelpersTest, BuildErrorStreamingString) {
- FmtContext ctx;
- ctx.withSelf("this_thing");
- std::string result1 =
- buildErrorStreamingString("here {{reformat($_self)}} is block", ctx,
- ErrorStreamType::InsideOpError);
- EXPECT_THAT(result1,
- StrEq("here \") << reformat(this_thing) << (\" is block"));
- std::string result2 = buildErrorStreamingString(
- "here {{reformat($_self)}} is block", ctx, ErrorStreamType::InString);
- EXPECT_THAT(result2, StrEq("here \" << reformat(this_thing) << \" is block"));
-}
More information about the Mlir-commits
mailing list