xref: /llvm-project/mlir/unittests/IR/OpPropertiesTest.cpp (revision ffc80de8643969ffa0dbbd377c5b33e3a7488f5e)
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