From dd612d6bf70e80eba41afc7e9f655d0d0280221f Mon Sep 17 00:00:00 2001 From: Fabian Mora Date: Tue, 22 Jul 2025 15:21:46 +0000 Subject: [PATCH 1/2] [mlir][IR] Add `getPropertyFromAttr` and `setPropertyFromAttr` methods. --- mlir/include/mlir/IR/ExtensibleDialect.h | 11 ++ mlir/include/mlir/IR/OpDefinition.h | 21 +++ mlir/include/mlir/IR/Operation.h | 17 +++ mlir/include/mlir/IR/OperationSupport.h | 45 ++++++ mlir/lib/IR/MLIRContext.cpp | 14 ++ mlir/lib/IR/Operation.cpp | 15 ++ mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp | 121 ++++++++++++---- mlir/unittests/IR/OpPropertiesTest.cpp | 144 ++++++++++++++++++++ 8 files changed, 364 insertions(+), 24 deletions(-) diff --git a/mlir/include/mlir/IR/ExtensibleDialect.h b/mlir/include/mlir/IR/ExtensibleDialect.h index 955faaad9408b..fd8ea760eb9fe 100644 --- a/mlir/include/mlir/IR/ExtensibleDialect.h +++ b/mlir/include/mlir/IR/ExtensibleDialect.h @@ -493,6 +493,17 @@ class DynamicOpDefinition : public OperationName::Impl { return failure(); } Attribute getPropertiesAsAttr(Operation *op) final { return {}; } + + LogicalResult + setPropertyFromAttr(OperationName opName, OpaqueProperties properties, + StringRef name, Attribute attr, + function_ref emitError) final { + emitError() << "extensible Dialects don't support properties"; + return failure(); + } + FailureOr getPropertyAsAttr(Operation *op, StringRef name) final { + return failure(); + } void copyProperties(OpaqueProperties lhs, OpaqueProperties rhs) final {} bool compareProperties(OpaqueProperties, OpaqueProperties) final { return false; } llvm::hash_code hashProperties(OpaqueProperties prop) final { return {}; } diff --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h index 883ece32967e4..2e610ec21000c 100644 --- a/mlir/include/mlir/IR/OpDefinition.h +++ b/mlir/include/mlir/IR/OpDefinition.h @@ -1758,6 +1758,17 @@ class Op : public OpState, public Traits... { function_ref emitError) { return setPropertiesFromAttribute(prop, attr, emitError); } + /// Convert the provided attribute to a property and assigned it to the + /// corresponding property. This default implementation forwards to a free + /// function `setPropertiesFromAttribute` that can be looked up with ADL in + /// the namespace where the properties are defined. It can also be overridden + /// in the derived ConcreteOp. + template + static LogicalResult + setPropertyFromAttr(PropertiesTy &prop, StringRef name, Attribute attr, + function_ref emitError) { + return setPropertyFromAttribute(prop, name, attr, emitError); + } /// Convert the provided properties to an attribute. This default /// implementation forwards to a free function `getPropertiesAsAttribute` that /// can be looked up with ADL in the namespace where the properties are @@ -1767,6 +1778,16 @@ class Op : public OpState, public Traits... { const PropertiesTy &prop) { return getPropertiesAsAttribute(ctx, prop); } + /// Convert the provided named property to an attribute. This default + /// implementation forwards to a free function `getPropertiesAsAttribute` that + /// can be looked up with ADL in the namespace where the properties are + /// defined. It can also be overridden in the derived ConcreteOp. + template + static FailureOr getPropertyAsAttr(MLIRContext *ctx, + const PropertiesTy &prop, + StringRef name) { + return getPropertyAsAttribute(ctx, prop, name); + } /// Hash the provided properties. This default implementation forwards to a /// free function `computeHash` that can be looked up with ADL in the /// namespace where the properties are defined. It can also be overridden in diff --git a/mlir/include/mlir/IR/Operation.h b/mlir/include/mlir/IR/Operation.h index fa8a4873572ce..42ab61c055bc3 100644 --- a/mlir/include/mlir/IR/Operation.h +++ b/mlir/include/mlir/IR/Operation.h @@ -920,6 +920,12 @@ class alignas(8) Operation final /// operation. Returns an empty attribute if no properties are present. Attribute getPropertiesAsAttribute(); + /// Return a named property converted to an attribute. + /// This is expensive, and mostly useful when dealing with unregistered + /// operations or in language bindings. Returns failure if there's no property + /// under such name. + FailureOr getPropertyAsAttribute(StringRef name); + /// Set the properties from the provided attribute. /// This is an expensive operation that can fail if the attribute is not /// matching the expectations of the properties for this operation. This is @@ -930,6 +936,17 @@ class alignas(8) Operation final setPropertiesFromAttribute(Attribute attr, function_ref emitError); + /// Set a named property from the provided attribute. + /// This is an expensive operation that can fail if the attribute is not + /// matching the expectations of the properties for this operation. This is + /// mostly useful for unregistered operations, used when parsing the + /// generic format, or in language bindings. An optional diagnostic emitter + /// can be passed in for richer errors, if none is passed then behavior is + /// undefined in error case. + LogicalResult + setPropertyFromAttribute(StringRef name, Attribute attr, + function_ref emitError); + /// Copy properties from an existing other properties object. The two objects /// must be the same type. void copyProperties(OpaqueProperties rhs); diff --git a/mlir/include/mlir/IR/OperationSupport.h b/mlir/include/mlir/IR/OperationSupport.h index 1ff7c56ddca38..7aadcca8f1232 100644 --- a/mlir/include/mlir/IR/OperationSupport.h +++ b/mlir/include/mlir/IR/OperationSupport.h @@ -139,6 +139,12 @@ class OperationName { setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute, function_ref emitError) = 0; virtual Attribute getPropertiesAsAttr(Operation *) = 0; + virtual LogicalResult + setPropertyFromAttr(OperationName, OpaqueProperties, StringRef name, + Attribute, + function_ref emitError) = 0; + virtual FailureOr getPropertyAsAttr(Operation *, + StringRef name) = 0; virtual void copyProperties(OpaqueProperties, OpaqueProperties) = 0; virtual bool compareProperties(OpaqueProperties, OpaqueProperties) = 0; virtual llvm::hash_code hashProperties(OpaqueProperties) = 0; @@ -220,6 +226,11 @@ class OperationName { setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute, function_ref emitError) final; Attribute getPropertiesAsAttr(Operation *) final; + LogicalResult + setPropertyFromAttr(OperationName, OpaqueProperties, StringRef name, + Attribute, + function_ref emitError) final; + FailureOr getPropertyAsAttr(Operation *, StringRef name) final; void copyProperties(OpaqueProperties, OpaqueProperties) final; bool compareProperties(OpaqueProperties, OpaqueProperties) final; llvm::hash_code hashProperties(OpaqueProperties) final; @@ -441,6 +452,20 @@ class OperationName { emitError); } + /// Return an op property converted to an Attribute. + FailureOr getOpPropertyAsAttribute(Operation *op, + StringRef name) const { + return getImpl()->getPropertyAsAttr(op, name); + } + + /// Define an op property from the provided Attribute. + LogicalResult setOpPropertyFromAttribute( + OperationName opName, OpaqueProperties properties, StringRef name, + Attribute attr, function_ref emitError) const { + return getImpl()->setPropertyFromAttr(opName, properties, name, attr, + emitError); + } + void copyOpProperties(OpaqueProperties lhs, OpaqueProperties rhs) const { return getImpl()->copyProperties(lhs, rhs); } @@ -650,6 +675,26 @@ class RegisteredOperationName : public OperationName { } return {}; } + LogicalResult + setPropertyFromAttr(OperationName opName, OpaqueProperties properties, + StringRef name, Attribute attr, + function_ref emitError) final { + if constexpr (hasProperties) { + auto p = properties.as(); + return ConcreteOp::setPropertyFromAttr(*p, name, attr, emitError); + } + emitError() << "this operation does not support properties"; + return failure(); + } + FailureOr getPropertyAsAttr(Operation *op, + StringRef name) final { + if constexpr (hasProperties) { + auto concreteOp = cast(op); + return ConcreteOp::getPropertyAsAttr(concreteOp->getContext(), + concreteOp.getProperties(), name); + } + return failure(); + } bool compareProperties(OpaqueProperties lhs, OpaqueProperties rhs) final { if constexpr (hasProperties) { return *lhs.as() == *rhs.as(); diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp index 06ec1c85fb4d5..7b49a945c549b 100644 --- a/mlir/lib/IR/MLIRContext.cpp +++ b/mlir/lib/IR/MLIRContext.cpp @@ -901,6 +901,20 @@ Attribute OperationName::UnregisteredOpModel::getPropertiesAsAttr(Operation *op) { return *op->getPropertiesStorage().as(); } +LogicalResult OperationName::UnregisteredOpModel::setPropertyFromAttr( + OperationName opName, OpaqueProperties properties, StringRef name, + Attribute attr, function_ref emitError) { + assert(false && + "`setPropertyFromAttr` doesn't work with unregistered operations."); + return failure(); +} +FailureOr +OperationName::UnregisteredOpModel::getPropertyAsAttr(Operation *op, + StringRef name) { + assert(false && + "`getPropertyAsAttr` doesn't work with unregistered operations."); + return failure(); +} void OperationName::UnregisteredOpModel::copyProperties(OpaqueProperties lhs, OpaqueProperties rhs) { *lhs.as() = *rhs.as(); diff --git a/mlir/lib/IR/Operation.cpp b/mlir/lib/IR/Operation.cpp index 8bcfa465e4a22..edd2efedf5a45 100644 --- a/mlir/lib/IR/Operation.cpp +++ b/mlir/lib/IR/Operation.cpp @@ -351,6 +351,12 @@ Attribute Operation::getPropertiesAsAttribute() { return *getPropertiesStorage().as(); return info->getOpPropertiesAsAttribute(this); } +FailureOr Operation::getPropertyAsAttribute(StringRef name) { + std::optional info = getRegisteredInfo(); + assert(info && + "`getPropertyAsAttribute` only works for registered operations."); + return info->getOpPropertyAsAttribute(this, name); +} LogicalResult Operation::setPropertiesFromAttribute( Attribute attr, function_ref emitError) { std::optional info = getRegisteredInfo(); @@ -361,6 +367,15 @@ LogicalResult Operation::setPropertiesFromAttribute( return info->setOpPropertiesFromAttribute( this->getName(), this->getPropertiesStorage(), attr, emitError); } +LogicalResult Operation::setPropertyFromAttribute( + StringRef name, Attribute attr, + function_ref emitError) { + std::optional info = getRegisteredInfo(); + assert(info && + "`setPropertyFromAttribute` only works for registered operations."); + return info->setOpPropertyFromAttribute( + this->getName(), this->getPropertiesStorage(), name, attr, emitError); +} void Operation::copyProperties(OpaqueProperties rhs) { name.copyOpProperties(getPropertiesStorage(), rhs); diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp index f35cfa6826388..d370f975ff2d7 100644 --- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp +++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp @@ -1411,7 +1411,7 @@ void OpEmitter::genPropertiesSupport() { attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value()); if (emitHelper.getResultSegmentsSize()) attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value()); - auto &setPropMethod = + auto &setPropsMethod = opClass .addStaticMethod( "::llvm::LogicalResult", "setPropertiesFromAttr", @@ -1421,12 +1421,31 @@ void OpEmitter::genPropertiesSupport() { "::llvm::function_ref<::mlir::InFlightDiagnostic()>", "emitError")) ->body(); - auto &getPropMethod = + auto &getPropsMethod = opClass .addStaticMethod("::mlir::Attribute", "getPropertiesAsAttr", MethodParameter("::mlir::MLIRContext *", "ctx"), MethodParameter("const Properties &", "prop")) ->body(); + auto &setPropMethod = + opClass + .addStaticMethod( + "::llvm::LogicalResult", "setPropertyFromAttr", + MethodParameter("Properties &", "prop"), + MethodParameter("llvm::StringRef", "name"), + MethodParameter("::mlir::Attribute", "attr"), + MethodParameter( + "::llvm::function_ref<::mlir::InFlightDiagnostic()>", + "emitError")) + ->body(); + auto &getPropMethod = + opClass + .addStaticMethod("llvm::FailureOr<::mlir::Attribute>", + "getPropertyAsAttr", + MethodParameter("::mlir::MLIRContext *", "ctx"), + MethodParameter("const Properties &", "prop"), + MethodParameter("llvm::StringRef", "name")) + ->body(); auto &hashMethod = opClass .addStaticMethod("llvm::hash_code", "computePropertiesHash", @@ -1468,7 +1487,7 @@ void OpEmitter::genPropertiesSupport() { // Convert the property to the attribute form. - setPropMethod << R"decl( + setPropsMethod << R"decl( ::mlir::DictionaryAttr dict = ::llvm::dyn_cast<::mlir::DictionaryAttr>(attr); if (!dict) { emitError() << "expected DictionaryAttr to set properties"; @@ -1480,9 +1499,9 @@ void OpEmitter::genPropertiesSupport() { ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) -> ::mlir::LogicalResult {{ {0} }; - {1}; + {1} )decl"; - const char *attrGetNoDefaultFmt = R"decl(; + const char *attrGetNoDefaultFmt = R"decl( if (attr && ::mlir::failed(setFromAttr(prop.{0}, attr, emitError))) return ::mlir::failure(); )decl"; @@ -1515,22 +1534,33 @@ void OpEmitter::genPropertiesSupport() { } fctx.withBuilder(odsBuilder); - setPropMethod << "{\n" + setPropsMethod << "{\n" + << formatv( + propFromAttrFmt, + tgfmt(prop.getConvertFromAttributeCall(), + &fctx.addSubst("_attr", propertyAttr) + .addSubst("_storage", propertyStorage) + .addSubst("_diag", propertyDiag)), + getAttr); + if (prop.hasStorageTypeValueOverride()) { + setPropsMethod << formatv(attrGetDefaultFmt, name, + prop.getStorageTypeValueOverride()); + } else if (prop.hasDefaultValue()) { + setPropsMethod << formatv(attrGetDefaultFmt, name, + tgfmt(prop.getDefaultValue(), &fctx)); + } else { + setPropsMethod << formatv(attrGetNoDefaultFmt, name); + } + setPropsMethod << " }\n"; + setPropMethod << formatv(" if (name == \"{0}\") {{", name) << formatv(propFromAttrFmt, tgfmt(prop.getConvertFromAttributeCall(), &fctx.addSubst("_attr", propertyAttr) .addSubst("_storage", propertyStorage) .addSubst("_diag", propertyDiag)), - getAttr); - if (prop.hasStorageTypeValueOverride()) { - setPropMethod << formatv(attrGetDefaultFmt, name, - prop.getStorageTypeValueOverride()); - } else if (prop.hasDefaultValue()) { - setPropMethod << formatv(attrGetDefaultFmt, name, - tgfmt(prop.getDefaultValue(), &fctx)); - } else { - setPropMethod << formatv(attrGetNoDefaultFmt, name); - } + ""); + setPropMethod << formatv(attrGetNoDefaultFmt, name); + setPropMethod << " return ::mlir::success();\n"; setPropMethod << " }\n"; } else { const auto *namedAttr = @@ -1548,7 +1578,7 @@ void OpEmitter::genPropertiesSupport() { os << " if (!attr) attr = dict.get(\"result_segment_sizes\");"; } - setPropMethod << formatv(R"decl( + setPropsMethod << formatv(R"decl( {{ auto &propStorage = prop.{0}; {1} @@ -1563,16 +1593,38 @@ void OpEmitter::genPropertiesSupport() { } } )decl", - name, getAttr); + name, getAttr); + setPropMethod << formatv(R"decl( + if (name == "{0}") {{ + auto &propStorage = prop.{0}; + if (attr == nullptr) {{ + propStorage = nullptr; + return ::mlir::success(); + } + auto convertedAttr = ::llvm::dyn_cast>(attr); + if (convertedAttr) {{ + propStorage = convertedAttr; + return ::mlir::success(); + } else {{ + emitError() << "Invalid attribute `{0}` in property conversion: " << attr; + return ::mlir::failure(); } } - setPropMethod << " return ::mlir::success();\n"; +)decl", + name); + } + } + setPropsMethod << " return ::mlir::success();\n"; + setPropMethod << " return emitError() << \"`\" << name << \"` is not an op " + "property\";\n"; // Convert the attribute form to the property. - getPropMethod << " ::mlir::SmallVector<::mlir::NamedAttribute> attrs;\n" - << " ::mlir::Builder odsBuilder{ctx};\n"; - const char *propToAttrFmt = R"decl( + getPropsMethod << " ::mlir::SmallVector<::mlir::NamedAttribute> attrs;\n" + << " ::mlir::Builder odsBuilder{ctx};\n"; + getPropMethod << " ::mlir::Builder odsBuilder{ctx};\n" + << " (void)odsBuilder;\n"; + const char *propsToAttrFmt = R"decl( { const auto &propStorage = prop.{0}; auto attr = [&]() -> ::mlir::Attribute {{ @@ -1580,6 +1632,15 @@ void OpEmitter::genPropertiesSupport() { }(); attrs.push_back(odsBuilder.getNamedAttr("{0}", attr)); } +)decl"; + const char *propToAttrFmt = R"decl( + if (name == "{0}") { + const auto &propStorage = prop.{0}; + auto attr = [&]() -> ::mlir::Attribute {{ + {1} + }(); + return attr; + } )decl"; for (const auto &attrOrProp : attrOrProperties) { if (const auto *namedProperty = @@ -1587,6 +1648,11 @@ void OpEmitter::genPropertiesSupport() { StringRef name = namedProperty->name; auto &prop = namedProperty->prop; FmtContext fctx; + getPropsMethod << formatv( + propsToAttrFmt, name, + tgfmt(prop.getConvertToAttributeCall(), + &fctx.addSubst("_ctxt", "ctx") + .addSubst("_storage", propertyStorage))); getPropMethod << formatv( propToAttrFmt, name, tgfmt(prop.getConvertToAttributeCall(), @@ -1597,21 +1663,28 @@ void OpEmitter::genPropertiesSupport() { const auto *namedAttr = llvm::dyn_cast_if_present(attrOrProp); StringRef name = namedAttr->attrName; - getPropMethod << formatv(R"decl( + getPropsMethod << formatv(R"decl( {{ const auto &propStorage = prop.{0}; if (propStorage) attrs.push_back(odsBuilder.getNamedAttr("{0}", propStorage)); } +)decl", + name); + getPropMethod << formatv(R"decl( + if (name == "{0}") {{ + return prop.{0}; + } )decl", name); } - getPropMethod << R"decl( + getPropsMethod << R"decl( if (!attrs.empty()) return odsBuilder.getDictionaryAttr(attrs); return {}; )decl"; + getPropMethod << " return ::mlir::failure();"; // Hashing for the property diff --git a/mlir/unittests/IR/OpPropertiesTest.cpp b/mlir/unittests/IR/OpPropertiesTest.cpp index 4759735d99605..b735210ed8ee1 100644 --- a/mlir/unittests/IR/OpPropertiesTest.cpp +++ b/mlir/unittests/IR/OpPropertiesTest.cpp @@ -13,6 +13,9 @@ #include "gtest/gtest.h" #include +#include "../../test/lib/Dialect/Test/TestDialect.h" +#include "../../test/lib/Dialect/Test/TestOps.h" + using namespace mlir; namespace { @@ -78,6 +81,43 @@ setPropertiesFromAttribute(TestProperties &prop, Attribute attr, return success(); } +/// Convert an attribute to a TestProperties struct, optionally emit errors +/// through the provided diagnostic if any. This is used for example during +/// parsing with the generic format. +static LogicalResult +setPropertyFromAttribute(TestProperties &prop, StringRef name, Attribute attr, + function_ref emitError) { + if (name == "a") { + auto v = dyn_cast(attr); + if (!v) + return failure(); + prop.a = v.getValue().getSExtValue(); + return success(); + } + if (name == "b") { + auto v = dyn_cast(attr); + if (!v) + return failure(); + prop.a = v.getValue().convertToFloat(); + return success(); + } + if (name == "array") { + auto v = dyn_cast(attr); + if (!v) + return failure(); + prop.array.assign(v.asArrayRef().begin(), v.asArrayRef().end()); + return success(); + } + if (name == "label") { + auto v = dyn_cast(attr); + if (!v) + return failure(); + prop.label = std::make_shared(v.getValue()); + return success(); + } + return failure(); +} + /// Convert a TestProperties struct to a DictionaryAttr, this is used for /// example during printing with the generic format. static Attribute getPropertiesAsAttribute(MLIRContext *ctx, @@ -92,6 +132,20 @@ static Attribute getPropertiesAsAttribute(MLIRContext *ctx, return b.getDictionaryAttr(attrs); } +/// Convert a named property in TestProperties struct to an attribute, this is +/// used for example during printing with the generic format. +static FailureOr getPropertyAsAttribute(MLIRContext *ctx, + const TestProperties &prop, + StringRef name) { + Builder b{ctx}; + return llvm::StringSwitch>(name) + .Case("a", b.getI32IntegerAttr(prop.a)) + .Case("b", b.getF32FloatAttr(prop.b)) + .Case("array", b.getDenseI64ArrayAttr(prop.array)) + .Case("label", b.getStringAttr(prop.label ? *prop.label : "")) + .Default(failure()); +} + inline llvm::hash_code computeHash(const TestProperties &prop) { // We hash `b` which is a float using its underlying array of char: unsigned char const *p = reinterpret_cast(&prop.b); @@ -419,4 +473,94 @@ TEST(OpPropertiesTest, withoutPropertiesDiscardableAttrs) { EXPECT_TRUE(hash(op.get()) == hash(reparsed.get())); } +TEST(OpPropertiesTest, PropertiesAsAttrs) { + MLIRContext context; + context.getOrLoadDialect(); + ParserConfig config(&context); + // Parse the operation with some properties. + OwningOpRef op = parseSourceString(mlirSrc, config); + ASSERT_TRUE(op.get() != nullptr); + auto opWithProp = dyn_cast(op.get()); + ASSERT_TRUE(opWithProp); + { + // Check the attr dict. + std::string output; + llvm::raw_string_ostream os(output); + opWithProp->getPropertiesAsAttribute().print(os); + ASSERT_STREQ("{a = -42 : i32, " + "array = array, " + "b = -4.200000e+01 : f32, " + "label = \"bar foo\"}", + output.c_str()); + } + auto getAttr = [&](FailureOr attr) { + EXPECT_TRUE(succeeded(attr)); + return *attr; + }; + // Get and mutate single properties. + auto a = cast(getAttr(op->getPropertyAsAttribute("a"))); + auto label = cast(getAttr(op->getPropertyAsAttribute("label"))); + std::string newLabel = label.getValue().str() + " " + + std::to_string(a.getValue().getSExtValue()); + { + EXPECT_TRUE(succeeded(op->setPropertyFromAttribute( + "label", StringAttr::get(&context, newLabel), + [&]() { return op->emitError(); }))); + std::string output; + llvm::raw_string_ostream os(output); + opWithProp.print(os); + StringRef view(output); + EXPECT_TRUE(view.contains("label = \"bar foo -42\"")); + } + // Expect error because the named prop doesn't exist. + EXPECT_TRUE(failed( + op->setPropertyFromAttribute("s", a, [&]() { return op->emitError(); }))); +} + +TEST(OpPropertiesTest, TblgenOpProperties) { + MLIRContext context; + context.getOrLoadDialect(); + ParserConfig config(&context); + // Parse the operation with some properties. + OwningOpRef op = parseSourceString(R"mlir( + test.with_properties a = 32, b = "foo", c = "bar", + flag = true, array = [1, 2, 3, 4], array32 = [5, 6] + )mlir", + config); + ASSERT_TRUE(op.get() != nullptr); + auto opWithProp = dyn_cast(op.get()); + ASSERT_TRUE(opWithProp); + { + // Check the attr dict. + std::string output; + llvm::raw_string_ostream os(output); + opWithProp->getPropertiesAsAttribute().print(os); + StringRef view(output); + EXPECT_TRUE(view.contains("a = 32")); + EXPECT_TRUE(view.contains("b = \"foo\"")); + } + { + // Modify a prop. + std::string output; + llvm::raw_string_ostream os(output); + ASSERT_TRUE(succeeded(opWithProp->setPropertyFromAttribute( + "a", IntegerAttr::get(IntegerType::get(&context, 32), 42), + [&]() { return opWithProp->emitError(); }))); + opWithProp->print(os); + StringRef view(output); + EXPECT_FALSE(view.contains("a = 32")); + EXPECT_TRUE(view.contains("a = 42")); + EXPECT_TRUE(view.contains("b = \"foo\"")); + } + { + // Get a prop. + std::string output; + llvm::raw_string_ostream os(output); + FailureOr prop = opWithProp->getPropertyAsAttribute("flag"); + ASSERT_TRUE(succeeded(prop)); + auto flagAttr = dyn_cast_or_null(*prop); + ASSERT_TRUE(flagAttr); + EXPECT_TRUE(flagAttr.getValue()); + } +} } // namespace From afee7ccc7647fde829d5dcf08237f2af40e56742 Mon Sep 17 00:00:00 2001 From: Fabian Mora Date: Tue, 22 Jul 2025 17:35:01 +0000 Subject: [PATCH 2/2] fix typo --- mlir/unittests/IR/OpPropertiesTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/unittests/IR/OpPropertiesTest.cpp b/mlir/unittests/IR/OpPropertiesTest.cpp index b735210ed8ee1..5241717a564af 100644 --- a/mlir/unittests/IR/OpPropertiesTest.cpp +++ b/mlir/unittests/IR/OpPropertiesTest.cpp @@ -98,7 +98,7 @@ setPropertyFromAttribute(TestProperties &prop, StringRef name, Attribute attr, auto v = dyn_cast(attr); if (!v) return failure(); - prop.a = v.getValue().convertToFloat(); + prop.b = v.getValue().convertToFloat(); return success(); } if (name == "array") {