15e118f93SMehdi Amini //===- TestOpProperties.cpp - Test all properties-related APIs ------------===// 25e118f93SMehdi Amini // 35e118f93SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45e118f93SMehdi Amini // See https://llvm.org/LICENSE.txt for license information. 55e118f93SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65e118f93SMehdi Amini // 75e118f93SMehdi Amini //===----------------------------------------------------------------------===// 85e118f93SMehdi Amini 9c1eab576SOleksandr "Alex" Zinenko #include "mlir/IR/Attributes.h" 105e118f93SMehdi Amini #include "mlir/IR/OpDefinition.h" 11b336ab42SOleksandr "Alex" Zinenko #include "mlir/IR/OperationSupport.h" 125e118f93SMehdi Amini #include "mlir/Parser/Parser.h" 135e118f93SMehdi Amini #include "gtest/gtest.h" 145e118f93SMehdi Amini #include <optional> 155e118f93SMehdi Amini 165e118f93SMehdi Amini using namespace mlir; 175e118f93SMehdi Amini 185e118f93SMehdi Amini namespace { 195e118f93SMehdi Amini /// Simple structure definining a struct to define "properties" for a given 205e118f93SMehdi Amini /// operation. Default values are honored when creating an operation. 215e118f93SMehdi Amini struct TestProperties { 225e118f93SMehdi Amini int a = -1; 235e118f93SMehdi Amini float b = -1.; 245e118f93SMehdi Amini std::vector<int64_t> array = {-33}; 255e118f93SMehdi Amini /// A shared_ptr to a const object is safe: it is equivalent to a value-based 265e118f93SMehdi Amini /// member. Here the label will be deallocated when the last operation 275e118f93SMehdi Amini /// referring to it is destroyed. However there is no pool-allocation: this is 285e118f93SMehdi Amini /// offloaded to the client. 295e118f93SMehdi Amini std::shared_ptr<const std::string> label; 305e118f93SMehdi Amini MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestProperties) 315e118f93SMehdi Amini }; 325e118f93SMehdi Amini 33ed8bd717SDudeldu bool operator==(const TestProperties &lhs, TestProperties &rhs) { 34ed8bd717SDudeldu return lhs.a == rhs.a && lhs.b == rhs.b && lhs.array == rhs.array && 35ed8bd717SDudeldu lhs.label == rhs.label; 36ed8bd717SDudeldu } 37ed8bd717SDudeldu 385e118f93SMehdi Amini /// Convert a DictionaryAttr to a TestProperties struct, optionally emit errors 395e118f93SMehdi Amini /// through the provided diagnostic if any. This is used for example during 405e118f93SMehdi Amini /// parsing with the generic format. 415e118f93SMehdi Amini static LogicalResult 425e118f93SMehdi Amini setPropertiesFromAttribute(TestProperties &prop, Attribute attr, 43c50617daSMehdi Amini function_ref<InFlightDiagnostic()> emitError) { 445e118f93SMehdi Amini DictionaryAttr dict = dyn_cast<DictionaryAttr>(attr); 455e118f93SMehdi Amini if (!dict) { 46c50617daSMehdi Amini emitError() << "expected DictionaryAttr to set TestProperties"; 475e118f93SMehdi Amini return failure(); 485e118f93SMehdi Amini } 495e118f93SMehdi Amini auto aAttr = dict.getAs<IntegerAttr>("a"); 505e118f93SMehdi Amini if (!aAttr) { 51c50617daSMehdi Amini emitError() << "expected IntegerAttr for key `a`"; 525e118f93SMehdi Amini return failure(); 535e118f93SMehdi Amini } 545e118f93SMehdi Amini auto bAttr = dict.getAs<FloatAttr>("b"); 555e118f93SMehdi Amini if (!bAttr || 565e118f93SMehdi Amini &bAttr.getValue().getSemantics() != &llvm::APFloatBase::IEEEsingle()) { 57c50617daSMehdi Amini emitError() << "expected FloatAttr for key `b`"; 585e118f93SMehdi Amini return failure(); 595e118f93SMehdi Amini } 605e118f93SMehdi Amini 615e118f93SMehdi Amini auto arrayAttr = dict.getAs<DenseI64ArrayAttr>("array"); 625e118f93SMehdi Amini if (!arrayAttr) { 63c50617daSMehdi Amini emitError() << "expected DenseI64ArrayAttr for key `array`"; 645e118f93SMehdi Amini return failure(); 655e118f93SMehdi Amini } 665e118f93SMehdi Amini 675e118f93SMehdi Amini auto label = dict.getAs<mlir::StringAttr>("label"); 685e118f93SMehdi Amini if (!label) { 69c50617daSMehdi Amini emitError() << "expected StringAttr for key `label`"; 705e118f93SMehdi Amini return failure(); 715e118f93SMehdi Amini } 725e118f93SMehdi Amini 735e118f93SMehdi Amini prop.a = aAttr.getValue().getSExtValue(); 745e118f93SMehdi Amini prop.b = bAttr.getValue().convertToFloat(); 755e118f93SMehdi Amini prop.array.assign(arrayAttr.asArrayRef().begin(), 765e118f93SMehdi Amini arrayAttr.asArrayRef().end()); 775e118f93SMehdi Amini prop.label = std::make_shared<std::string>(label.getValue()); 785e118f93SMehdi Amini return success(); 795e118f93SMehdi Amini } 805e118f93SMehdi Amini 815e118f93SMehdi Amini /// Convert a TestProperties struct to a DictionaryAttr, this is used for 825e118f93SMehdi Amini /// example during printing with the generic format. 835e118f93SMehdi Amini static Attribute getPropertiesAsAttribute(MLIRContext *ctx, 845e118f93SMehdi Amini const TestProperties &prop) { 855e118f93SMehdi Amini SmallVector<NamedAttribute> attrs; 865e118f93SMehdi Amini Builder b{ctx}; 875e118f93SMehdi Amini attrs.push_back(b.getNamedAttr("a", b.getI32IntegerAttr(prop.a))); 885e118f93SMehdi Amini attrs.push_back(b.getNamedAttr("b", b.getF32FloatAttr(prop.b))); 895e118f93SMehdi Amini attrs.push_back(b.getNamedAttr("array", b.getDenseI64ArrayAttr(prop.array))); 905e118f93SMehdi Amini attrs.push_back(b.getNamedAttr( 915e118f93SMehdi Amini "label", b.getStringAttr(prop.label ? *prop.label : "<nullptr>"))); 925e118f93SMehdi Amini return b.getDictionaryAttr(attrs); 935e118f93SMehdi Amini } 945e118f93SMehdi Amini 955e118f93SMehdi Amini inline llvm::hash_code computeHash(const TestProperties &prop) { 965e118f93SMehdi Amini // We hash `b` which is a float using its underlying array of char: 975e118f93SMehdi Amini unsigned char const *p = reinterpret_cast<unsigned char const *>(&prop.b); 985e118f93SMehdi Amini ArrayRef<unsigned char> bBytes{p, sizeof(prop.b)}; 995e118f93SMehdi Amini return llvm::hash_combine( 1005e118f93SMehdi Amini prop.a, llvm::hash_combine_range(bBytes.begin(), bBytes.end()), 1015e118f93SMehdi Amini llvm::hash_combine_range(prop.array.begin(), prop.array.end()), 1025e118f93SMehdi Amini StringRef(*prop.label)); 1035e118f93SMehdi Amini } 1045e118f93SMehdi Amini 1055e118f93SMehdi Amini /// A custom operation for the purpose of showcasing how to use "properties". 1065e118f93SMehdi Amini class OpWithProperties : public Op<OpWithProperties> { 1075e118f93SMehdi Amini public: 1085e118f93SMehdi Amini // Begin boilerplate 1095e118f93SMehdi Amini MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OpWithProperties) 1105e118f93SMehdi Amini using Op::Op; 1115e118f93SMehdi Amini static ArrayRef<StringRef> getAttributeNames() { return {}; } 1125e118f93SMehdi Amini static StringRef getOperationName() { 1135e118f93SMehdi Amini return "test_op_properties.op_with_properties"; 1145e118f93SMehdi Amini } 1155e118f93SMehdi Amini // End boilerplate 1165e118f93SMehdi Amini 1175e118f93SMehdi Amini // This alias is the only definition needed for enabling "properties" for this 1185e118f93SMehdi Amini // operation. 1195e118f93SMehdi Amini using Properties = TestProperties; 1209ea6b30aSMehdi Amini static std::optional<mlir::Attribute> getInherentAttr(MLIRContext *context, 1219ea6b30aSMehdi Amini const Properties &prop, 1225e118f93SMehdi Amini StringRef name) { 1235e118f93SMehdi Amini return std::nullopt; 1245e118f93SMehdi Amini } 1255e118f93SMehdi Amini static void setInherentAttr(Properties &prop, StringRef name, 1265e118f93SMehdi Amini mlir::Attribute value) {} 1279ea6b30aSMehdi Amini static void populateInherentAttrs(MLIRContext *context, 1289ea6b30aSMehdi Amini const Properties &prop, 1295e118f93SMehdi Amini NamedAttrList &attrs) {} 1305e118f93SMehdi Amini static LogicalResult 1315e118f93SMehdi Amini verifyInherentAttrs(OperationName opName, NamedAttrList &attrs, 132c50617daSMehdi Amini function_ref<InFlightDiagnostic()> emitError) { 1335e118f93SMehdi Amini return success(); 1345e118f93SMehdi Amini } 1355e118f93SMehdi Amini }; 1365e118f93SMehdi Amini 137c1eab576SOleksandr "Alex" Zinenko /// A custom operation for the purpose of showcasing how discardable attributes 138c1eab576SOleksandr "Alex" Zinenko /// are handled in absence of properties. 139c1eab576SOleksandr "Alex" Zinenko class OpWithoutProperties : public Op<OpWithoutProperties> { 140c1eab576SOleksandr "Alex" Zinenko public: 141c1eab576SOleksandr "Alex" Zinenko // Begin boilerplate. 142c1eab576SOleksandr "Alex" Zinenko MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(OpWithoutProperties) 143c1eab576SOleksandr "Alex" Zinenko using Op::Op; 144c1eab576SOleksandr "Alex" Zinenko static ArrayRef<StringRef> getAttributeNames() { 145c1eab576SOleksandr "Alex" Zinenko static StringRef attributeNames[] = {StringRef("inherent_attr")}; 146c1eab576SOleksandr "Alex" Zinenko return ArrayRef(attributeNames); 147c1eab576SOleksandr "Alex" Zinenko }; 148c1eab576SOleksandr "Alex" Zinenko static StringRef getOperationName() { 149c1eab576SOleksandr "Alex" Zinenko return "test_op_properties.op_without_properties"; 150c1eab576SOleksandr "Alex" Zinenko } 151c1eab576SOleksandr "Alex" Zinenko // End boilerplate. 152c1eab576SOleksandr "Alex" Zinenko }; 153c1eab576SOleksandr "Alex" Zinenko 1545e118f93SMehdi Amini // A trivial supporting dialect to register the above operation. 1555e118f93SMehdi Amini class TestOpPropertiesDialect : public Dialect { 1565e118f93SMehdi Amini public: 1575e118f93SMehdi Amini MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOpPropertiesDialect) 1585e118f93SMehdi Amini static constexpr StringLiteral getDialectNamespace() { 1595e118f93SMehdi Amini return StringLiteral("test_op_properties"); 1605e118f93SMehdi Amini } 1615e118f93SMehdi Amini explicit TestOpPropertiesDialect(MLIRContext *context) 1625e118f93SMehdi Amini : Dialect(getDialectNamespace(), context, 1635e118f93SMehdi Amini TypeID::get<TestOpPropertiesDialect>()) { 164c1eab576SOleksandr "Alex" Zinenko addOperations<OpWithProperties, OpWithoutProperties>(); 1655e118f93SMehdi Amini } 1665e118f93SMehdi Amini }; 1675e118f93SMehdi Amini 1685e118f93SMehdi Amini constexpr StringLiteral mlirSrc = R"mlir( 1695e118f93SMehdi Amini "test_op_properties.op_with_properties"() 1705e118f93SMehdi Amini <{a = -42 : i32, 1715e118f93SMehdi Amini b = -4.200000e+01 : f32, 1725e118f93SMehdi Amini array = array<i64: 40, 41>, 1735e118f93SMehdi Amini label = "bar foo"}> : () -> () 1745e118f93SMehdi Amini )mlir"; 1755e118f93SMehdi Amini 1765e118f93SMehdi Amini TEST(OpPropertiesTest, Properties) { 1775e118f93SMehdi Amini MLIRContext context; 1785e118f93SMehdi Amini context.getOrLoadDialect<TestOpPropertiesDialect>(); 1795e118f93SMehdi Amini ParserConfig config(&context); 1805e118f93SMehdi Amini // Parse the operation with some properties. 1815e118f93SMehdi Amini OwningOpRef<Operation *> op = parseSourceString(mlirSrc, config); 1825e118f93SMehdi Amini ASSERT_TRUE(op.get() != nullptr); 1835e118f93SMehdi Amini auto opWithProp = dyn_cast<OpWithProperties>(op.get()); 1845e118f93SMehdi Amini ASSERT_TRUE(opWithProp); 1855e118f93SMehdi Amini { 1865e118f93SMehdi Amini std::string output; 1875e118f93SMehdi Amini llvm::raw_string_ostream os(output); 1885e118f93SMehdi Amini opWithProp.print(os); 1895e118f93SMehdi Amini ASSERT_STREQ("\"test_op_properties.op_with_properties\"() " 1905e118f93SMehdi Amini "<{a = -42 : i32, " 1915e118f93SMehdi Amini "array = array<i64: 40, 41>, " 1925e118f93SMehdi Amini "b = -4.200000e+01 : f32, " 1935e118f93SMehdi Amini "label = \"bar foo\"}> : () -> ()\n", 194*ffc80de8SJOE1994 output.c_str()); 1955e118f93SMehdi Amini } 1965e118f93SMehdi Amini // Get a mutable reference to the properties for this operation and modify it 1975e118f93SMehdi Amini // in place one member at a time. 1985e118f93SMehdi Amini TestProperties &prop = opWithProp.getProperties(); 1995e118f93SMehdi Amini prop.a = 42; 2005e118f93SMehdi Amini { 2015e118f93SMehdi Amini std::string output; 2025e118f93SMehdi Amini llvm::raw_string_ostream os(output); 2035e118f93SMehdi Amini opWithProp.print(os); 204*ffc80de8SJOE1994 StringRef view(output); 205*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("a = 42")); 206*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("b = -4.200000e+01")); 207*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("array = array<i64: 40, 41>")); 208*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("label = \"bar foo\"")); 2095e118f93SMehdi Amini } 2105e118f93SMehdi Amini prop.b = 42.; 2115e118f93SMehdi Amini { 2125e118f93SMehdi Amini std::string output; 2135e118f93SMehdi Amini llvm::raw_string_ostream os(output); 2145e118f93SMehdi Amini opWithProp.print(os); 215*ffc80de8SJOE1994 StringRef view(output); 216*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("a = 42")); 217*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("b = 4.200000e+01")); 218*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("array = array<i64: 40, 41>")); 219*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("label = \"bar foo\"")); 2205e118f93SMehdi Amini } 2215e118f93SMehdi Amini prop.array.push_back(42); 2225e118f93SMehdi Amini { 2235e118f93SMehdi Amini std::string output; 2245e118f93SMehdi Amini llvm::raw_string_ostream os(output); 2255e118f93SMehdi Amini opWithProp.print(os); 226*ffc80de8SJOE1994 StringRef view(output); 227*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("a = 42")); 228*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("b = 4.200000e+01")); 229*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("array = array<i64: 40, 41, 42>")); 230*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("label = \"bar foo\"")); 2315e118f93SMehdi Amini } 2325e118f93SMehdi Amini prop.label = std::make_shared<std::string>("foo bar"); 2335e118f93SMehdi Amini { 2345e118f93SMehdi Amini std::string output; 2355e118f93SMehdi Amini llvm::raw_string_ostream os(output); 2365e118f93SMehdi Amini opWithProp.print(os); 237*ffc80de8SJOE1994 StringRef view(output); 238*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("a = 42")); 239*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("b = 4.200000e+01")); 240*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("array = array<i64: 40, 41, 42>")); 241*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("label = \"foo bar\"")); 2425e118f93SMehdi Amini } 2435e118f93SMehdi Amini } 2445e118f93SMehdi Amini 2455e118f93SMehdi Amini // Test diagnostic emission when using invalid dictionary. 2465e118f93SMehdi Amini TEST(OpPropertiesTest, FailedProperties) { 2475e118f93SMehdi Amini MLIRContext context; 2485e118f93SMehdi Amini context.getOrLoadDialect<TestOpPropertiesDialect>(); 2495e118f93SMehdi Amini std::string diagnosticStr; 2505e118f93SMehdi Amini context.getDiagEngine().registerHandler([&](Diagnostic &diag) { 2515e118f93SMehdi Amini diagnosticStr += diag.str(); 2525e118f93SMehdi Amini return success(); 2535e118f93SMehdi Amini }); 2545e118f93SMehdi Amini 2555e118f93SMehdi Amini // Parse the operation with some properties. 2565e118f93SMehdi Amini ParserConfig config(&context); 2575e118f93SMehdi Amini 2585e118f93SMehdi Amini // Parse an operation with invalid (incomplete) properties. 2595e118f93SMehdi Amini OwningOpRef<Operation *> owningOp = 2605e118f93SMehdi Amini parseSourceString("\"test_op_properties.op_with_properties\"() " 2615e118f93SMehdi Amini "<{a = -42 : i32}> : () -> ()\n", 2625e118f93SMehdi Amini config); 2635e118f93SMehdi Amini ASSERT_EQ(owningOp.get(), nullptr); 2645e118f93SMehdi Amini EXPECT_STREQ( 2655e118f93SMehdi Amini "invalid properties {a = -42 : i32} for op " 2665e118f93SMehdi Amini "test_op_properties.op_with_properties: expected FloatAttr for key `b`", 2675e118f93SMehdi Amini diagnosticStr.c_str()); 2685e118f93SMehdi Amini diagnosticStr.clear(); 2695e118f93SMehdi Amini 2705e118f93SMehdi Amini owningOp = parseSourceString(mlirSrc, config); 2715e118f93SMehdi Amini Operation *op = owningOp.get(); 2725e118f93SMehdi Amini ASSERT_TRUE(op != nullptr); 2735e118f93SMehdi Amini Location loc = op->getLoc(); 2745e118f93SMehdi Amini auto opWithProp = dyn_cast<OpWithProperties>(op); 2755e118f93SMehdi Amini ASSERT_TRUE(opWithProp); 2765e118f93SMehdi Amini 2775e118f93SMehdi Amini OperationState state(loc, op->getName()); 2785e118f93SMehdi Amini Builder b{&context}; 2795e118f93SMehdi Amini NamedAttrList attrs; 2805e118f93SMehdi Amini attrs.push_back(b.getNamedAttr("a", b.getStringAttr("foo"))); 2815e118f93SMehdi Amini state.propertiesAttr = attrs.getDictionary(&context); 2825e118f93SMehdi Amini { 283c50617daSMehdi Amini auto emitError = [&]() { 284c50617daSMehdi Amini return op->emitError("setting properties failed: "); 2858c2bff1aSMehdi Amini }; 286c50617daSMehdi Amini auto result = state.setProperties(op, emitError); 2875e118f93SMehdi Amini EXPECT_TRUE(result.failed()); 2885e118f93SMehdi Amini } 2895e118f93SMehdi Amini EXPECT_STREQ("setting properties failed: expected IntegerAttr for key `a`", 2905e118f93SMehdi Amini diagnosticStr.c_str()); 2915e118f93SMehdi Amini } 2925e118f93SMehdi Amini 2935e118f93SMehdi Amini TEST(OpPropertiesTest, DefaultValues) { 2945e118f93SMehdi Amini MLIRContext context; 2955e118f93SMehdi Amini context.getOrLoadDialect<TestOpPropertiesDialect>(); 2965e118f93SMehdi Amini OperationState state(UnknownLoc::get(&context), 2975e118f93SMehdi Amini "test_op_properties.op_with_properties"); 2985e118f93SMehdi Amini Operation *op = Operation::create(state); 2995e118f93SMehdi Amini ASSERT_TRUE(op != nullptr); 3005e118f93SMehdi Amini { 3015e118f93SMehdi Amini std::string output; 3025e118f93SMehdi Amini llvm::raw_string_ostream os(output); 3035e118f93SMehdi Amini op->print(os); 304*ffc80de8SJOE1994 StringRef view(output); 305*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("a = -1")); 306*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("b = -1")); 307*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("array = array<i64: -33>")); 3085e118f93SMehdi Amini } 3095e118f93SMehdi Amini op->erase(); 3105e118f93SMehdi Amini } 3115e118f93SMehdi Amini 3125e118f93SMehdi Amini TEST(OpPropertiesTest, Cloning) { 3135e118f93SMehdi Amini MLIRContext context; 3145e118f93SMehdi Amini context.getOrLoadDialect<TestOpPropertiesDialect>(); 3155e118f93SMehdi Amini ParserConfig config(&context); 3165e118f93SMehdi Amini // Parse the operation with some properties. 3175e118f93SMehdi Amini OwningOpRef<Operation *> op = parseSourceString(mlirSrc, config); 3185e118f93SMehdi Amini ASSERT_TRUE(op.get() != nullptr); 3195e118f93SMehdi Amini auto opWithProp = dyn_cast<OpWithProperties>(op.get()); 3205e118f93SMehdi Amini ASSERT_TRUE(opWithProp); 3215e118f93SMehdi Amini Operation *clone = opWithProp->clone(); 3225e118f93SMehdi Amini 3235e118f93SMehdi Amini // Check that op and its clone prints equally 3245e118f93SMehdi Amini std::string opStr; 3255e118f93SMehdi Amini std::string cloneStr; 3265e118f93SMehdi Amini { 3275e118f93SMehdi Amini llvm::raw_string_ostream os(opStr); 3285e118f93SMehdi Amini op.get()->print(os); 3295e118f93SMehdi Amini } 3305e118f93SMehdi Amini { 3315e118f93SMehdi Amini llvm::raw_string_ostream os(cloneStr); 3325e118f93SMehdi Amini clone->print(os); 3335e118f93SMehdi Amini } 3345e118f93SMehdi Amini clone->erase(); 3355e118f93SMehdi Amini EXPECT_STREQ(opStr.c_str(), cloneStr.c_str()); 3365e118f93SMehdi Amini } 3375e118f93SMehdi Amini 3385e118f93SMehdi Amini TEST(OpPropertiesTest, Equivalence) { 3395e118f93SMehdi Amini MLIRContext context; 3405e118f93SMehdi Amini context.getOrLoadDialect<TestOpPropertiesDialect>(); 3415e118f93SMehdi Amini ParserConfig config(&context); 3425e118f93SMehdi Amini // Parse the operation with some properties. 3435e118f93SMehdi Amini OwningOpRef<Operation *> op = parseSourceString(mlirSrc, config); 3445e118f93SMehdi Amini ASSERT_TRUE(op.get() != nullptr); 3455e118f93SMehdi Amini auto opWithProp = dyn_cast<OpWithProperties>(op.get()); 3465e118f93SMehdi Amini ASSERT_TRUE(opWithProp); 3475e118f93SMehdi Amini llvm::hash_code reference = OperationEquivalence::computeHash(opWithProp); 3485e118f93SMehdi Amini TestProperties &prop = opWithProp.getProperties(); 3495e118f93SMehdi Amini prop.a = 42; 3505e118f93SMehdi Amini EXPECT_NE(reference, OperationEquivalence::computeHash(opWithProp)); 3515e118f93SMehdi Amini prop.a = -42; 3525e118f93SMehdi Amini EXPECT_EQ(reference, OperationEquivalence::computeHash(opWithProp)); 3535e118f93SMehdi Amini prop.b = 42.; 3545e118f93SMehdi Amini EXPECT_NE(reference, OperationEquivalence::computeHash(opWithProp)); 3555e118f93SMehdi Amini prop.b = -42.; 3565e118f93SMehdi Amini EXPECT_EQ(reference, OperationEquivalence::computeHash(opWithProp)); 3575e118f93SMehdi Amini prop.array.push_back(42); 3585e118f93SMehdi Amini EXPECT_NE(reference, OperationEquivalence::computeHash(opWithProp)); 3595e118f93SMehdi Amini prop.array.pop_back(); 3605e118f93SMehdi Amini EXPECT_EQ(reference, OperationEquivalence::computeHash(opWithProp)); 3615e118f93SMehdi Amini } 3625e118f93SMehdi Amini 3635e118f93SMehdi Amini TEST(OpPropertiesTest, getOrAddProperties) { 3645e118f93SMehdi Amini MLIRContext context; 3655e118f93SMehdi Amini context.getOrLoadDialect<TestOpPropertiesDialect>(); 3665e118f93SMehdi Amini OperationState state(UnknownLoc::get(&context), 3675e118f93SMehdi Amini "test_op_properties.op_with_properties"); 3685e118f93SMehdi Amini // Test `getOrAddProperties` API on OperationState. 3695e118f93SMehdi Amini TestProperties &prop = state.getOrAddProperties<TestProperties>(); 3705e118f93SMehdi Amini prop.a = 1; 3715e118f93SMehdi Amini prop.b = 2; 3725e118f93SMehdi Amini prop.array = {3, 4, 5}; 3735e118f93SMehdi Amini Operation *op = Operation::create(state); 3745e118f93SMehdi Amini ASSERT_TRUE(op != nullptr); 3755e118f93SMehdi Amini { 3765e118f93SMehdi Amini std::string output; 3775e118f93SMehdi Amini llvm::raw_string_ostream os(output); 3785e118f93SMehdi Amini op->print(os); 379*ffc80de8SJOE1994 StringRef view(output); 380*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("a = 1")); 381*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("b = 2")); 382*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("array = array<i64: 3, 4, 5>")); 3835e118f93SMehdi Amini } 3845e118f93SMehdi Amini op->erase(); 3855e118f93SMehdi Amini } 3865e118f93SMehdi Amini 387c1eab576SOleksandr "Alex" Zinenko constexpr StringLiteral withoutPropertiesAttrsSrc = R"mlir( 388c1eab576SOleksandr "Alex" Zinenko "test_op_properties.op_without_properties"() 389c1eab576SOleksandr "Alex" Zinenko {inherent_attr = 42, other_attr = 56} : () -> () 390c1eab576SOleksandr "Alex" Zinenko )mlir"; 391c1eab576SOleksandr "Alex" Zinenko 392c1eab576SOleksandr "Alex" Zinenko TEST(OpPropertiesTest, withoutPropertiesDiscardableAttrs) { 393c1eab576SOleksandr "Alex" Zinenko MLIRContext context; 394c1eab576SOleksandr "Alex" Zinenko context.getOrLoadDialect<TestOpPropertiesDialect>(); 395c1eab576SOleksandr "Alex" Zinenko ParserConfig config(&context); 396c1eab576SOleksandr "Alex" Zinenko OwningOpRef<Operation *> op = 397c1eab576SOleksandr "Alex" Zinenko parseSourceString(withoutPropertiesAttrsSrc, config); 398c1eab576SOleksandr "Alex" Zinenko ASSERT_EQ(llvm::range_size(op->getDiscardableAttrs()), 1u); 399c1eab576SOleksandr "Alex" Zinenko EXPECT_EQ(op->getDiscardableAttrs().begin()->getName().getValue(), 400c1eab576SOleksandr "Alex" Zinenko "other_attr"); 401c1eab576SOleksandr "Alex" Zinenko 402c1eab576SOleksandr "Alex" Zinenko EXPECT_EQ(op->getAttrs().size(), 2u); 403c1eab576SOleksandr "Alex" Zinenko EXPECT_TRUE(op->getInherentAttr("inherent_attr") != std::nullopt); 404c1eab576SOleksandr "Alex" Zinenko EXPECT_TRUE(op->getDiscardableAttr("other_attr") != Attribute()); 405fc0fdd1aSAlex Zinenko 406fc0fdd1aSAlex Zinenko std::string output; 407fc0fdd1aSAlex Zinenko llvm::raw_string_ostream os(output); 408fc0fdd1aSAlex Zinenko op->print(os); 409*ffc80de8SJOE1994 StringRef view(output); 410*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("inherent_attr = 42")); 411*ffc80de8SJOE1994 EXPECT_TRUE(view.contains("other_attr = 56")); 412b336ab42SOleksandr "Alex" Zinenko 413b336ab42SOleksandr "Alex" Zinenko OwningOpRef<Operation *> reparsed = parseSourceString(os.str(), config); 414b336ab42SOleksandr "Alex" Zinenko auto trivialHash = [](Value v) { return hash_value(v); }; 415b336ab42SOleksandr "Alex" Zinenko auto hash = [&](Operation *operation) { 416b336ab42SOleksandr "Alex" Zinenko return OperationEquivalence::computeHash( 417b336ab42SOleksandr "Alex" Zinenko operation, trivialHash, trivialHash, 418b336ab42SOleksandr "Alex" Zinenko OperationEquivalence::Flags::IgnoreLocations); 419b336ab42SOleksandr "Alex" Zinenko }; 420b336ab42SOleksandr "Alex" Zinenko EXPECT_TRUE(hash(op.get()) == hash(reparsed.get())); 421c1eab576SOleksandr "Alex" Zinenko } 422c1eab576SOleksandr "Alex" Zinenko 4235e118f93SMehdi Amini } // namespace 424