14cb2ef4fSOleksandr "Alex" Zinenko# Chapter 4: Matching Payload with Transform Operations 24cb2ef4fSOleksandr "Alex" Zinenko 34cb2ef4fSOleksandr "Alex" Zinenko**Check the continuously-tested version of MLIR files under 44cb2ef4fSOleksandr "Alex" Zinenko[mlir/test/Examples/transform/Ch4](https://github.com/llvm/llvm-project/tree/main/mlir/test/Examples/transform/Ch4).** 54cb2ef4fSOleksandr "Alex" Zinenko 64cb2ef4fSOleksandr "Alex" ZinenkoUp until now, we were applying transform dialect scripts under the assumption 74cb2ef4fSOleksandr "Alex" Zinenkothat specific payload operations are identified by the caller when the transform 84cb2ef4fSOleksandr "Alex" Zinenkodialect interpreter is invoked. This may be seen as contrary to the idea of 94cb2ef4fSOleksandr "Alex" Zinenkodriving transformations from a dialect since the transformation targets must be 104cb2ef4fSOleksandr "Alex" Zinenkoidentified through mechanisms external to the transform dialect interpreter, for 114cb2ef4fSOleksandr "Alex" Zinenkoexample, when invoking the interpreter programmatically in C++ or through pass 124cb2ef4fSOleksandr "Alex" Zinenkoarguments as seen in previous chapters. It also adds practical overhead due to 134cb2ef4fSOleksandr "Alex" Zinenkoincreased interaction with the interpreter in C++, and cognitive overhead of 144cb2ef4fSOleksandr "Alex" Zinenkomanipulating two interfaces at once. To remedy this, Transform dialect proposes 154cb2ef4fSOleksandr "Alex" Zinenkoa subset of operations for _matching_ payload operations that need to be 164cb2ef4fSOleksandr "Alex" Zinenkotransformed. 174cb2ef4fSOleksandr "Alex" Zinenko 184cb2ef4fSOleksandr "Alex" Zinenko_Match_ operations are simply transform operations with some additional 194cb2ef4fSOleksandr "Alex" Zinenkoguarantees. In particular, they are not expected to modify the payload IR and 204cb2ef4fSOleksandr "Alex" Zinenkoare expected to fail if their operands (typically payload operation handles) are 214cb2ef4fSOleksandr "Alex" Zinenkonot associated with payload IR objects having desired properties, such as 224cb2ef4fSOleksandr "Alex" Zinenkooperation names or kinds of arguments. Using simple combinator operations, it 234cb2ef4fSOleksandr "Alex" Zinenkobecomes possible to set up a higher-level match and rewrite infrastructure 244cb2ef4fSOleksandr "Alex" Zinenkodirectly within the transform dialect. 254cb2ef4fSOleksandr "Alex" Zinenko 264cb2ef4fSOleksandr "Alex" Zinenko 274cb2ef4fSOleksandr "Alex" Zinenko## Simple match 284cb2ef4fSOleksandr "Alex" Zinenko 294cb2ef4fSOleksandr "Alex" ZinenkoLet us reconsider the “fully connected layer” example from [Chapter 30*73fa6685Smlevesquedion1](Ch1.md/#chaining-transformations-with-handles), reproduced below for 314cb2ef4fSOleksandr "Alex" Zinenkoconvenience. 324cb2ef4fSOleksandr "Alex" Zinenko 334cb2ef4fSOleksandr "Alex" Zinenko 344cb2ef4fSOleksandr "Alex" Zinenko```mlir 354cb2ef4fSOleksandr "Alex" Zinenko// Original function to optimize. 364cb2ef4fSOleksandr "Alex" Zinenkofunc.func @fc_relu(%lhs: tensor<512x512xf32>, %rhs: tensor<512x512xf32>, 374cb2ef4fSOleksandr "Alex" Zinenko %bias: tensor<512x512xf32>, %output: tensor<512x512xf32>) 384cb2ef4fSOleksandr "Alex" Zinenko -> tensor<512x512xf32> { 394cb2ef4fSOleksandr "Alex" Zinenko // Matrix-matrix multiplication. 404cb2ef4fSOleksandr "Alex" Zinenko %matmul = linalg.matmul 414cb2ef4fSOleksandr "Alex" Zinenko ins(%lhs, %rhs: tensor<512x512xf32>, tensor<512x512xf32>) 424cb2ef4fSOleksandr "Alex" Zinenko outs(%output: tensor<512x512xf32>) -> tensor<512x512xf32> 434cb2ef4fSOleksandr "Alex" Zinenko 444cb2ef4fSOleksandr "Alex" Zinenko // Elementwise addition. 454cb2ef4fSOleksandr "Alex" Zinenko %biased = linalg.elemwise_binary { fun = #linalg.binary_fn<add> } 464cb2ef4fSOleksandr "Alex" Zinenko ins(%matmul, %bias : tensor<512x512xf32>, tensor<512x512xf32>) 474cb2ef4fSOleksandr "Alex" Zinenko outs(%output : tensor<512x512xf32>) -> tensor<512x512xf32> 484cb2ef4fSOleksandr "Alex" Zinenko 494cb2ef4fSOleksandr "Alex" Zinenko // Elementwise max with 0 (ReLU). 504cb2ef4fSOleksandr "Alex" Zinenko %c0f = arith.constant 0.0 : f32 514cb2ef4fSOleksandr "Alex" Zinenko %relued = linalg.elemwise_binary { fun = #linalg.binary_fn<max_signed> } 524cb2ef4fSOleksandr "Alex" Zinenko ins(%biased, %c0f : tensor<512x512xf32>, f32) 534cb2ef4fSOleksandr "Alex" Zinenko outs(%output : tensor<512x512xf32>) -> tensor<512x512xf32> 544cb2ef4fSOleksandr "Alex" Zinenko func.return %relued : tensor<512x512xf32> 554cb2ef4fSOleksandr "Alex" Zinenko} 564cb2ef4fSOleksandr "Alex" Zinenko 574cb2ef4fSOleksandr "Alex" Zinenko``` 584cb2ef4fSOleksandr "Alex" Zinenko 594cb2ef4fSOleksandr "Alex" Zinenko 604cb2ef4fSOleksandr "Alex" ZinenkoIn Chapter 1, we were calling the test transform interpreter pass with 614cb2ef4fSOleksandr "Alex" Zinenkoadditional arguments, `bind-first-extra-to-ops=linalg.matmul 624cb2ef4fSOleksandr "Alex" Zinenkobind-second-extra-to-ops=linalg.elemwise_binary`, to provide initial 634cb2ef4fSOleksandr "Alex" Zinenkoassociations for operation handles. Instead, we can use match operations to 644cb2ef4fSOleksandr "Alex" Zinenkodiscover relevant operations in the payload IR. Match operations can be combined 654cb2ef4fSOleksandr "Alex" Zinenkowith “regular” transform operations using, e.g., the 664cb2ef4fSOleksandr "Alex" Zinenko`transform.collect_matching` combinator operation that leverages the concept of 674cb2ef4fSOleksandr "Alex" Zinenkonamed sequences to organize matchers. 684cb2ef4fSOleksandr "Alex" Zinenko 694cb2ef4fSOleksandr "Alex" Zinenko 704cb2ef4fSOleksandr "Alex" Zinenko```mlir 714cb2ef4fSOleksandr "Alex" Zinenko// The module containing named sequences must have an attribute allowing them 724cb2ef4fSOleksandr "Alex" Zinenko// to enable verification. 734cb2ef4fSOleksandr "Alex" Zinenkomodule @transforms attributes { transform.with_named_sequence } { 744cb2ef4fSOleksandr "Alex" Zinenko // Entry point. This takes as the only argument the root operation (typically 754cb2ef4fSOleksandr "Alex" Zinenko // pass root) given to the transform interpreter. 764cb2ef4fSOleksandr "Alex" Zinenko transform.named_sequence @__transform_main( 774cb2ef4fSOleksandr "Alex" Zinenko %root: !transform.any_op {transform.readonly}) { 784cb2ef4fSOleksandr "Alex" Zinenko // Collect operations that match the criteria specified in named sequence. 794cb2ef4fSOleksandr "Alex" Zinenko // If the named sequence fails with a silenceable failure, silences it (the 804cb2ef4fSOleksandr "Alex" Zinenko // message is forwarded to the debug stream). If the named sequence 814cb2ef4fSOleksandr "Alex" Zinenko // succeeds, appends its results to the results of this operation. 824cb2ef4fSOleksandr "Alex" Zinenko %elemwise = transform.collect_matching @match_elemwise in %root 834cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.any_op 844cb2ef4fSOleksandr "Alex" Zinenko %matmul = transform.collect_matching @match_matmul in %root 854cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.any_op 864cb2ef4fSOleksandr "Alex" Zinenko transform.include @print_elemwise failures(propagate) (%elemwise) 874cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> () 884cb2ef4fSOleksandr "Alex" Zinenko transform.include @print_matmul failures(propagate) (%matmul) 894cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> () 904cb2ef4fSOleksandr "Alex" Zinenko 914cb2ef4fSOleksandr "Alex" Zinenko transform.yield 924cb2ef4fSOleksandr "Alex" Zinenko } 934cb2ef4fSOleksandr "Alex" Zinenko 944cb2ef4fSOleksandr "Alex" Zinenko // This is a matcher sequence. It is given an operation to match and the 954cb2ef4fSOleksandr "Alex" Zinenko // match is considered successful unless any nested operation produces a 964cb2ef4fSOleksandr "Alex" Zinenko // failure. The values yielded by this operation will be forwarded to the 974cb2ef4fSOleksandr "Alex" Zinenko // rewriter sequence on success. 984cb2ef4fSOleksandr "Alex" Zinenko transform.named_sequence @match_elemwise( 994cb2ef4fSOleksandr "Alex" Zinenko %entry: !transform.any_op {transform.readonly}) -> !transform.any_op { 1004cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %entry ["linalg.elemwise_binary"] 1014cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op 1024cb2ef4fSOleksandr "Alex" Zinenko transform.yield %entry : !transform.any_op 1034cb2ef4fSOleksandr "Alex" Zinenko } 1044cb2ef4fSOleksandr "Alex" Zinenko transform.named_sequence @match_matmul( 1054cb2ef4fSOleksandr "Alex" Zinenko %entry: !transform.any_op {transform.readonly}) -> !transform.any_op { 1064cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %entry ["linalg.matmul"] : !transform.any_op 1074cb2ef4fSOleksandr "Alex" Zinenko transform.yield %entry : !transform.any_op 1084cb2ef4fSOleksandr "Alex" Zinenko } 1094cb2ef4fSOleksandr "Alex" Zinenko 1104cb2ef4fSOleksandr "Alex" Zinenko // This is a rewriter sequence. 1114cb2ef4fSOleksandr "Alex" Zinenko transform.named_sequence @print_elemwise( 1124cb2ef4fSOleksandr "Alex" Zinenko %elemwise_binary: !transform.any_op {transform.readonly}) { 1132798b72aSOleksandr "Alex" Zinenko transform.debug.emit_remark_at 1144cb2ef4fSOleksandr "Alex" Zinenko %elemwise_binary, "elementwise binary" : !transform.any_op 1154cb2ef4fSOleksandr "Alex" Zinenko transform.yield 1164cb2ef4fSOleksandr "Alex" Zinenko } 1174cb2ef4fSOleksandr "Alex" Zinenko transform.named_sequence @print_matmul( 1184cb2ef4fSOleksandr "Alex" Zinenko %matmul: !transform.any_op {transform.readonly}) { 1192798b72aSOleksandr "Alex" Zinenko transform.debug.emit_remark_at %matmul, "matmul" : !transform.any_op 1204cb2ef4fSOleksandr "Alex" Zinenko transform.yield 1214cb2ef4fSOleksandr "Alex" Zinenko } 1224cb2ef4fSOleksandr "Alex" Zinenko} 1234cb2ef4fSOleksandr "Alex" Zinenko 1244cb2ef4fSOleksandr "Alex" Zinenko``` 1254cb2ef4fSOleksandr "Alex" Zinenko 1264cb2ef4fSOleksandr "Alex" Zinenko 1274cb2ef4fSOleksandr "Alex" ZinenkoThis script can be executed using the non-test interpreter pass running on the 1284cb2ef4fSOleksandr "Alex" Zinenkoroot operation of the translation unit without additional flags: `mlir-opt 1294cb2ef4fSOleksandr "Alex" Zinenko--transform-interpreter`. It will emit corresponding remarks at 1304cb2ef4fSOleksandr "Alex" Zinenko`linalg.elemwise_binary` and `linalg.matmul` operations. In debug builds, the 1314cb2ef4fSOleksandr "Alex" Zinenkoinfrastructure provides a convenient method to understand the matching process 1324cb2ef4fSOleksandr "Alex" Zinenkoby passing `-debug-only=transform-matcher` to `mlir-opt` or a derived tool. It 1334cb2ef4fSOleksandr "Alex" Zinenkowill print the silenceable failure messages produced by the match operations 1344cb2ef4fSOleksandr "Alex" Zinenkointo the debug stream, for example: 1354cb2ef4fSOleksandr "Alex" Zinenko 1364cb2ef4fSOleksandr "Alex" Zinenko 1374cb2ef4fSOleksandr "Alex" Zinenko``` 1384cb2ef4fSOleksandr "Alex" Zinenko<...> 1394cb2ef4fSOleksandr "Alex" Zinenko[transform-matcher] matching %0 = linalg.matmul ins(%arg0, %arg1 : tensor<512x512xf32>, tensor<512x512xf32>) outs(%arg3 : tensor<512x512xf32>) -> tensor<512x512xf32> @0x5622eee08410 1404cb2ef4fSOleksandr "Alex" Zinenko[transform-matcher] matcher match_elemwise failed: wrong operation name 1414cb2ef4fSOleksandr "Alex" Zinenko<...> 1424cb2ef4fSOleksandr "Alex" Zinenko``` 1434cb2ef4fSOleksandr "Alex" Zinenko 1444cb2ef4fSOleksandr "Alex" Zinenko 1454cb2ef4fSOleksandr "Alex" ZinenkoThis is now sufficient to run the rest of the transform script from Chapter 1, 1464cb2ef4fSOleksandr "Alex" Zinenkosubstituting `%arg1` with `%matmul` and `%arg2` with `%elemwise`. 1474cb2ef4fSOleksandr "Alex" Zinenko 1484cb2ef4fSOleksandr "Alex" Zinenko 1494cb2ef4fSOleksandr "Alex" Zinenko## Matching Chains of Operations 1504cb2ef4fSOleksandr "Alex" Zinenko 1514cb2ef4fSOleksandr "Alex" ZinenkoThe matcher above remains naive as it matches _all_ operations of the certain 1524cb2ef4fSOleksandr "Alex" Zinenkokind under the payload root. These operations may or may not be related, and 1534cb2ef4fSOleksandr "Alex" Zinenkomay, for example, belong to different functions. Even if they are in a single 1544cb2ef4fSOleksandr "Alex" Zinenkofunction, if there are multiple groups of such operations, we wouldn’t be able 1554cb2ef4fSOleksandr "Alex" Zinenkoto differentiate them with this approach. In reality, we want to match a 1564cb2ef4fSOleksandr "Alex" Zinenkospecific group of operations where a `matmul` operation produces a result that 1574cb2ef4fSOleksandr "Alex" Zinenkois used by an elementwise operation, which in turn feeds another elementwise 1584cb2ef4fSOleksandr "Alex" Zinenkooperation in a similar way. 1594cb2ef4fSOleksandr "Alex" Zinenko 1604cb2ef4fSOleksandr "Alex" ZinenkoThis can be achieved using the following matcher sequence. 1614cb2ef4fSOleksandr "Alex" Zinenko 1624cb2ef4fSOleksandr "Alex" Zinenko 1634cb2ef4fSOleksandr "Alex" Zinenko```mlir 1644cb2ef4fSOleksandr "Alex" Zinenko// This is also a matcher sequence. It is similarly given an operation to 1654cb2ef4fSOleksandr "Alex" Zinenko// match and nested operations must succeed in order for a match to be deemed 1664cb2ef4fSOleksandr "Alex" Zinenko// successful. It starts matching from the last operation in the use-def chain 1674cb2ef4fSOleksandr "Alex" Zinenko// and goes back because each operand (use) has exactly one definition. 1684cb2ef4fSOleksandr "Alex" Zinenkotransform.named_sequence @match_matmul_elemwise( 1694cb2ef4fSOleksandr "Alex" Zinenko %last: !transform.any_op {transform.readonly}) 1704cb2ef4fSOleksandr "Alex" Zinenko -> (!transform.any_op, !transform.any_op, !transform.any_op) { 1714cb2ef4fSOleksandr "Alex" Zinenko // The last operation must be an elementwise binary. 1724cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %last ["linalg.elemwise_binary"] 1734cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op 1744cb2ef4fSOleksandr "Alex" Zinenko // Its first operand must be defined by another operation, to which we 1754cb2ef4fSOleksandr "Alex" Zinenko // will get a handle here. We are guaranteed that the first operand exists 1764cb2ef4fSOleksandr "Alex" Zinenko // because we know the operation is binary, but even in absence of such a 1774cb2ef4fSOleksandr "Alex" Zinenko // guarantee, this operation would have produced a silenceable failure when 1784cb2ef4fSOleksandr "Alex" Zinenko // `%last` does not have enough operands. 1794cb2ef4fSOleksandr "Alex" Zinenko %middle = transform.get_producer_of_operand %last[0] 1804cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.any_op 1814cb2ef4fSOleksandr "Alex" Zinenko // The defining operation must itself be an elementwise binary. 1824cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %middle ["linalg.elemwise_binary"] 1834cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op 1844cb2ef4fSOleksandr "Alex" Zinenko // And the first operand of that operation must be defined by yet another 1854cb2ef4fSOleksandr "Alex" Zinenko // operation. 1864cb2ef4fSOleksandr "Alex" Zinenko %matmul = transform.get_producer_of_operand %middle[0] 1874cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.any_op 1884cb2ef4fSOleksandr "Alex" Zinenko // And that operation is a matmul. 1894cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %matmul ["linalg.matmul"] : !transform.any_op 1904cb2ef4fSOleksandr "Alex" Zinenko // We will yield the handles to the matmul and the two elementwise 1914cb2ef4fSOleksandr "Alex" Zinenko // operations separately. 1924cb2ef4fSOleksandr "Alex" Zinenko transform.yield %matmul, %middle, %last 1934cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op, !transform.any_op, !transform.any_op 1944cb2ef4fSOleksandr "Alex" Zinenko} 1954cb2ef4fSOleksandr "Alex" Zinenko``` 1964cb2ef4fSOleksandr "Alex" Zinenko 1974cb2ef4fSOleksandr "Alex" ZinenkoThis matcher is applicable in presence of other `elemwise` and `matmul` 1984cb2ef4fSOleksandr "Alex" Zinenkooperations and will return the triple of _related_ operations rather than 1994cb2ef4fSOleksandr "Alex" Zinenkooperations in the order in which they are found. It can be exercised similarly 2004cb2ef4fSOleksandr "Alex" Zinenkoto the previous incarnation, as follows. 2014cb2ef4fSOleksandr "Alex" Zinenko 2024cb2ef4fSOleksandr "Alex" Zinenko```mlir 2034cb2ef4fSOleksandr "Alex" Zinenko// Alternative entry point. 2044cb2ef4fSOleksandr "Alex" Zinenkotransform.named_sequence @__transform_main( 2054cb2ef4fSOleksandr "Alex" Zinenko %root: !transform.any_op {transform.readonly}) { 2064cb2ef4fSOleksandr "Alex" Zinenko // Collect groups of operations that match the criteria specified in the 2074cb2ef4fSOleksandr "Alex" Zinenko // named sequence. 2084cb2ef4fSOleksandr "Alex" Zinenko %matmul, %el1, %el2 = transform.collect_matching @match_matmul_elemwise in %root 2094cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op) 2104cb2ef4fSOleksandr "Alex" Zinenko %elemwise = transform.merge_handles %el1, %el2 : !transform.any_op 2114cb2ef4fSOleksandr "Alex" Zinenko 2124cb2ef4fSOleksandr "Alex" Zinenko transform.include @print_elemwise failures(propagate) (%elemwise) 2134cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> () 2144cb2ef4fSOleksandr "Alex" Zinenko transform.include @print_matmul failures(propagate) (%matmul) 2154cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> () 2164cb2ef4fSOleksandr "Alex" Zinenko 2174cb2ef4fSOleksandr "Alex" Zinenko transform.yield 2184cb2ef4fSOleksandr "Alex" Zinenko} 2194cb2ef4fSOleksandr "Alex" Zinenko``` 2204cb2ef4fSOleksandr "Alex" Zinenko 2214cb2ef4fSOleksandr "Alex" Zinenko 2224cb2ef4fSOleksandr "Alex" Zinenko## Defining Match Operations 2234cb2ef4fSOleksandr "Alex" Zinenko 2244cb2ef4fSOleksandr "Alex" ZinenkoThe matcher of a chain of operations is correct in presence of other operations, 2254cb2ef4fSOleksandr "Alex" Zinenkobut is still insufficiently robust for many cases of interest. In particular, 2264cb2ef4fSOleksandr "Alex" Zinenkousing `transform.get_producer_of_operand %last[0]` requires that the _first_ 2274cb2ef4fSOleksandr "Alex" Zinenkooperand of elementwise operations is produced by another operation. The same 2284cb2ef4fSOleksandr "Alex" Zinenkotransformation strategy may however apply regardless of the operand position: 2294cb2ef4fSOleksandr "Alex" Zinenkomany binary operations are associative. Let us use this opportunity to introduce 2304cb2ef4fSOleksandr "Alex" Zinenkoa new match operation. Specifically, we would like this operation to succeed if 2314cb2ef4fSOleksandr "Alex" Zinenko_any_ of the operands satisfies certain conditions that can be expressed as 2324cb2ef4fSOleksandr "Alex" Zinenkoother match operations. We also want it to return some of the state and the 2334cb2ef4fSOleksandr "Alex" Zinenkoposition of the matched operand in the operand list. 2344cb2ef4fSOleksandr "Alex" Zinenko 2354cb2ef4fSOleksandr "Alex" ZinenkoMatch operations are defined similarly to other transform operations, with the 2364cb2ef4fSOleksandr "Alex" Zinenkoonly difference of additionally implementing the `MatchOpInterface`. Note that 2374cb2ef4fSOleksandr "Alex" Zinenkothis interface has _no additional methods_ (though it may add some eventually) 2384cb2ef4fSOleksandr "Alex" Zinenkoand is only used as a verification contract that the operation is intended for 2394cb2ef4fSOleksandr "Alex" Zinenkomatching and will not attempt to transform the payload. The minimal definition 2404cb2ef4fSOleksandr "Alex" Zinenkoof our operation is as follows. 2414cb2ef4fSOleksandr "Alex" Zinenko 2424cb2ef4fSOleksandr "Alex" Zinenko 2434cb2ef4fSOleksandr "Alex" Zinenko```tablegen 2444cb2ef4fSOleksandr "Alex" Zinenko// Define the new operation. By convention, prefix its name with `match` 2454cb2ef4fSOleksandr "Alex" Zinenko// followed by the name of the dialect extension. 2464cb2ef4fSOleksandr "Alex" Zinenkodef HasOperandSatisfyingOp : TransformDialectOp<"match.my.has_operand_satisfying", 2474cb2ef4fSOleksandr "Alex" Zinenko [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, 2484cb2ef4fSOleksandr "Alex" Zinenko DeclareOpInterfaceMethods<TransformOpInterface>, 2494cb2ef4fSOleksandr "Alex" Zinenko // Indicate that the operation implements MatchOpInterface in addition to 2504cb2ef4fSOleksandr "Alex" Zinenko // the TransformOpInterface. This interface is only used as a tag at this 2514cb2ef4fSOleksandr "Alex" Zinenko // point and has no methods that are mandatory to implement. 2524cb2ef4fSOleksandr "Alex" Zinenko MatchOpInterface, 2534cb2ef4fSOleksandr "Alex" Zinenko SingleBlockImplicitTerminator<"::mlir::transform::YieldOp">]> { 2544cb2ef4fSOleksandr "Alex" Zinenko let summary = "Succeed if any of the operands matches all nested criteria"; 2554cb2ef4fSOleksandr "Alex" Zinenko let arguments = (ins TransformHandleTypeInterface:$op); 2564cb2ef4fSOleksandr "Alex" Zinenko let results = (outs TransformParamTypeInterface:$position, 2574cb2ef4fSOleksandr "Alex" Zinenko Variadic<Transform_AnyHandleOrParamType>:$results); 2584cb2ef4fSOleksandr "Alex" Zinenko 2594cb2ef4fSOleksandr "Alex" Zinenko // Match operations can be arbitrarily complex, e.g., containing regions. 2604cb2ef4fSOleksandr "Alex" Zinenko let regions = (region SizedRegion<1>:$body); 2614cb2ef4fSOleksandr "Alex" Zinenko let hasVerifier = 1; 2624cb2ef4fSOleksandr "Alex" Zinenko let assemblyFormat = [{ 2634cb2ef4fSOleksandr "Alex" Zinenko $op `:` functional-type($op, results) attr-dict-with-keyword $body 2644cb2ef4fSOleksandr "Alex" Zinenko }]; 2654cb2ef4fSOleksandr "Alex" Zinenko} 2664cb2ef4fSOleksandr "Alex" Zinenko``` 2674cb2ef4fSOleksandr "Alex" Zinenko 2684cb2ef4fSOleksandr "Alex" Zinenko 2694cb2ef4fSOleksandr "Alex" ZinenkoIt takes as argument the handle associated with the payload operations whose 2704cb2ef4fSOleksandr "Alex" Zinenkooperands it will match, has an associated single-block region containing the 2714cb2ef4fSOleksandr "Alex" Zinenkomatch criteria, and returns the position of the matched operand as well as any 2724cb2ef4fSOleksandr "Alex" Zinenkoother transform value yielded from the body on the successful match. 2734cb2ef4fSOleksandr "Alex" Zinenko 2744cb2ef4fSOleksandr "Alex" ZinenkoThe matching logic is implemented in the `apply` method of the 2754cb2ef4fSOleksandr "Alex" Zinenko`TransformOpInterface` and is easily composable with other transform operations. 2764cb2ef4fSOleksandr "Alex" ZinenkoAll facilities for managing the interpreter state and recursively entering the 2774cb2ef4fSOleksandr "Alex" Zinenkoblocks are available in the same way as they are for “regular” transform 2784cb2ef4fSOleksandr "Alex" Zinenkooperations. Match operations are expected to return a silenceable failure to 2794cb2ef4fSOleksandr "Alex" Zinenkoindicate failure to match, and to immediately propagate definite failures. If 2804cb2ef4fSOleksandr "Alex" Zinenkothey have nested operations, they are expected to handle and, in most cases, 2814cb2ef4fSOleksandr "Alex" Zinenkosilence the silenceable failures produced when applying those operations. For 2824cb2ef4fSOleksandr "Alex" Zinenkoour operation, the matching is essentially a loop iterating over all operands of 2834cb2ef4fSOleksandr "Alex" Zinenkothe (single) payload operation and applying nested transform ops until they all 2844cb2ef4fSOleksandr "Alex" Zinenkosucceed for one of the operands. 2854cb2ef4fSOleksandr "Alex" Zinenko 2864cb2ef4fSOleksandr "Alex" Zinenko 2874cb2ef4fSOleksandr "Alex" Zinenko```cpp 2884cb2ef4fSOleksandr "Alex" Zinenko// Matcher ops implement `apply` similarly to other transform ops. They are not 2894cb2ef4fSOleksandr "Alex" Zinenko// expected to modify payload, but use the tri-state result to signal failure or 2904cb2ef4fSOleksandr "Alex" Zinenko// success to match, as well as potential irrecoverable errors. 2914cb2ef4fSOleksandr "Alex" Zinenkomlir::DiagnosedSilenceableFailure 2924cb2ef4fSOleksandr "Alex" Zinenkomlir::transform::HasOperandSatisfyingOp::apply( 2934cb2ef4fSOleksandr "Alex" Zinenko mlir::transform::TransformRewriter &rewriter, 2944cb2ef4fSOleksandr "Alex" Zinenko mlir::transform::TransformResults &results, 2954cb2ef4fSOleksandr "Alex" Zinenko mlir::transform::TransformState &state) { 2964cb2ef4fSOleksandr "Alex" Zinenko // For simplicity, only handle a single payload op. Actual implementations 2974cb2ef4fSOleksandr "Alex" Zinenko // can use `SingleOpMatcher` trait to simplify implementation and document 2984cb2ef4fSOleksandr "Alex" Zinenko // this expectation. 2994cb2ef4fSOleksandr "Alex" Zinenko auto payloadOps = state.getPayloadOps(getOp()); 3004cb2ef4fSOleksandr "Alex" Zinenko if (!llvm::hasSingleElement(payloadOps)) 3014cb2ef4fSOleksandr "Alex" Zinenko return emitSilenceableError() << "expected single payload"; 3024cb2ef4fSOleksandr "Alex" Zinenko 3034cb2ef4fSOleksandr "Alex" Zinenko // Iterate over all operands of the payload op to see if they can be matched 3044cb2ef4fSOleksandr "Alex" Zinenko // using the body of this op. 3054cb2ef4fSOleksandr "Alex" Zinenko Operation *payload = *payloadOps.begin(); 3064cb2ef4fSOleksandr "Alex" Zinenko for (OpOperand &operand : payload->getOpOperands()) { 3074cb2ef4fSOleksandr "Alex" Zinenko // Create a scope for transform values defined in the body. This corresponds 3084cb2ef4fSOleksandr "Alex" Zinenko // to the syntactic scope of the region attached to this op. Any values 3094cb2ef4fSOleksandr "Alex" Zinenko // associated with payloads from now on will be automatically dissociated 3104cb2ef4fSOleksandr "Alex" Zinenko // when this object is destroyed, i.e. at the end of the iteration. 3114cb2ef4fSOleksandr "Alex" Zinenko // Associate the block argument handle with the operand. 3124cb2ef4fSOleksandr "Alex" Zinenko auto matchScope = state.make_region_scope(getBody()); 3134cb2ef4fSOleksandr "Alex" Zinenko if (failed(state.mapBlockArgument(getBody().getArgument(0), 3144cb2ef4fSOleksandr "Alex" Zinenko {operand.get()}))) { 3154cb2ef4fSOleksandr "Alex" Zinenko return DiagnosedSilenceableFailure::definiteFailure(); 3164cb2ef4fSOleksandr "Alex" Zinenko } 3174cb2ef4fSOleksandr "Alex" Zinenko 3184cb2ef4fSOleksandr "Alex" Zinenko // Iterate over all nested matchers with the current mapping and see if they 3194cb2ef4fSOleksandr "Alex" Zinenko // succeed. 3204cb2ef4fSOleksandr "Alex" Zinenko bool matchSucceeded = true; 3214cb2ef4fSOleksandr "Alex" Zinenko for (Operation &matcher : getBody().front().without_terminator()) { 3224cb2ef4fSOleksandr "Alex" Zinenko // Matcher ops are applied similarly to any other transform op. 3234cb2ef4fSOleksandr "Alex" Zinenko DiagnosedSilenceableFailure diag = 3244cb2ef4fSOleksandr "Alex" Zinenko state.applyTransform(cast<TransformOpInterface>(matcher)); 3254cb2ef4fSOleksandr "Alex" Zinenko 3264cb2ef4fSOleksandr "Alex" Zinenko // Definite failures are immediately propagated as they are irrecoverable. 3274cb2ef4fSOleksandr "Alex" Zinenko if (diag.isDefiniteFailure()) 3284cb2ef4fSOleksandr "Alex" Zinenko return diag; 3294cb2ef4fSOleksandr "Alex" Zinenko 3304cb2ef4fSOleksandr "Alex" Zinenko // On success, keep checking the remaining conditions. 3314cb2ef4fSOleksandr "Alex" Zinenko if (diag.succeeded()) 3324cb2ef4fSOleksandr "Alex" Zinenko continue; 3334cb2ef4fSOleksandr "Alex" Zinenko 3344cb2ef4fSOleksandr "Alex" Zinenko // Report failure-to-match for debugging purposes and stop matching this 3354cb2ef4fSOleksandr "Alex" Zinenko // operand. 3364cb2ef4fSOleksandr "Alex" Zinenko assert(diag.isSilenceableFailure()); 3374cb2ef4fSOleksandr "Alex" Zinenko DEBUG_MATCHER(DBGS_MATCHER() 3384cb2ef4fSOleksandr "Alex" Zinenko << "failed to match operand #" << operand.getOperandNumber() 3394cb2ef4fSOleksandr "Alex" Zinenko << ": " << diag.getMessage()); 3404cb2ef4fSOleksandr "Alex" Zinenko (void)diag.silence(); 3414cb2ef4fSOleksandr "Alex" Zinenko matchSucceeded = false; 3424cb2ef4fSOleksandr "Alex" Zinenko break; 3434cb2ef4fSOleksandr "Alex" Zinenko } 3444cb2ef4fSOleksandr "Alex" Zinenko // If failed to match this operand, try other operands. 3454cb2ef4fSOleksandr "Alex" Zinenko if (!matchSucceeded) 3464cb2ef4fSOleksandr "Alex" Zinenko continue; 3474cb2ef4fSOleksandr "Alex" Zinenko 3484cb2ef4fSOleksandr "Alex" Zinenko // If we reached this point, the matching succeeded for the current operand. 3494cb2ef4fSOleksandr "Alex" Zinenko // Remap the values associated with terminator operands to be associated 3504cb2ef4fSOleksandr "Alex" Zinenko // with op results, and also map the parameter result to the operand's 3514cb2ef4fSOleksandr "Alex" Zinenko // position. Note that it is safe to do here despite the end of the scope 3524cb2ef4fSOleksandr "Alex" Zinenko // as `results` are integrated into `state` by the interpreter after `apply` 3534cb2ef4fSOleksandr "Alex" Zinenko // returns rather than immediately. 3544cb2ef4fSOleksandr "Alex" Zinenko SmallVector<SmallVector<MappedValue>> yieldedMappings; 3554cb2ef4fSOleksandr "Alex" Zinenko transform::detail::prepareValueMappings( 3564cb2ef4fSOleksandr "Alex" Zinenko yieldedMappings, getBody().front().getTerminator()->getOperands(), 3574cb2ef4fSOleksandr "Alex" Zinenko state); 3584cb2ef4fSOleksandr "Alex" Zinenko results.setParams(getPosition().cast<OpResult>(), 3594cb2ef4fSOleksandr "Alex" Zinenko {rewriter.getI32IntegerAttr(operand.getOperandNumber())}); 3604cb2ef4fSOleksandr "Alex" Zinenko for (auto &&[result, mapping] : llvm::zip(getResults(), yieldedMappings)) 3614cb2ef4fSOleksandr "Alex" Zinenko results.setMappedValues(result, mapping); 3624cb2ef4fSOleksandr "Alex" Zinenko return DiagnosedSilenceableFailure::success(); 3634cb2ef4fSOleksandr "Alex" Zinenko } 3644cb2ef4fSOleksandr "Alex" Zinenko 3654cb2ef4fSOleksandr "Alex" Zinenko // If we reached this point, none of the operands succeeded the match. 3664cb2ef4fSOleksandr "Alex" Zinenko return emitSilenceableError() 3674cb2ef4fSOleksandr "Alex" Zinenko << "none of the operands satisfied the conditions"; 3684cb2ef4fSOleksandr "Alex" Zinenko} 3694cb2ef4fSOleksandr "Alex" Zinenko 3704cb2ef4fSOleksandr "Alex" Zinenko``` 3714cb2ef4fSOleksandr "Alex" Zinenko 3724cb2ef4fSOleksandr "Alex" Zinenko 3734cb2ef4fSOleksandr "Alex" ZinenkoBy convention, operations implementing `MatchOpInterface` must not modify 3744cb2ef4fSOleksandr "Alex" Zinenkopayload IR and must therefore specify that they only read operand handles and 3754cb2ef4fSOleksandr "Alex" Zinenkopayload as their effects. 3764cb2ef4fSOleksandr "Alex" Zinenko 3774cb2ef4fSOleksandr "Alex" Zinenko 3784cb2ef4fSOleksandr "Alex" Zinenko```cpp 3794cb2ef4fSOleksandr "Alex" Zinenkovoid transform::CollectMatchingOp::getEffects( 3804cb2ef4fSOleksandr "Alex" Zinenko SmallVectorImpl<MemoryEffects::EffectInstance> &effects) { 3814cb2ef4fSOleksandr "Alex" Zinenko onlyReadsHandle(getRoot(), effects); 3824cb2ef4fSOleksandr "Alex" Zinenko producesHandle(getResults(), effects); 3834cb2ef4fSOleksandr "Alex" Zinenko onlyReadsPayload(effects); 3844cb2ef4fSOleksandr "Alex" Zinenko} 3854cb2ef4fSOleksandr "Alex" Zinenko``` 3864cb2ef4fSOleksandr "Alex" Zinenko 3874cb2ef4fSOleksandr "Alex" Zinenko 3884cb2ef4fSOleksandr "Alex" ZinenkoThis operation can now be included in a transform dialect extension, loaded and 3894cb2ef4fSOleksandr "Alex" Zinenkoused in our matcher. Specifically, we will use it to indicate that either of the 3904cb2ef4fSOleksandr "Alex" Zinenkooperands of the “max” elementwise operation in our example can be produced by 3914cb2ef4fSOleksandr "Alex" Zinenkothe previous elementwise operation. The previous operation will still require 3924cb2ef4fSOleksandr "Alex" Zinenkothe matmul to produce the first operand for simplicity. The updated matcher 3934cb2ef4fSOleksandr "Alex" Zinenkosequence looks as follows. 3944cb2ef4fSOleksandr "Alex" Zinenko 3954cb2ef4fSOleksandr "Alex" Zinenko 3964cb2ef4fSOleksandr "Alex" Zinenko```mlir 3974cb2ef4fSOleksandr "Alex" Zinenkotransform.named_sequence @match_matmul_elemwise( 3984cb2ef4fSOleksandr "Alex" Zinenko %last: !transform.any_op {transform.readonly}) 3994cb2ef4fSOleksandr "Alex" Zinenko -> (!transform.any_op, !transform.any_op, !transform.any_op, 4004cb2ef4fSOleksandr "Alex" Zinenko !transform.param<i32>) { 4014cb2ef4fSOleksandr "Alex" Zinenko // The last operation must be an elementwise binary. 4024cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %last ["linalg.elemwise_binary"] 4034cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op 4044cb2ef4fSOleksandr "Alex" Zinenko 4054cb2ef4fSOleksandr "Alex" Zinenko // One of its operands must be defined by another operation, to which we 4064cb2ef4fSOleksandr "Alex" Zinenko // will get a handle here. This is achieved thanks to a newly defined 4074cb2ef4fSOleksandr "Alex" Zinenko // operation that tries to match operands one by one using the match 4084cb2ef4fSOleksandr "Alex" Zinenko // operations nested in its region. 4094cb2ef4fSOleksandr "Alex" Zinenko %pos, %middle = transform.match.my.has_operand_satisfying %last 4104cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> (!transform.param<i32>, !transform.any_op) { 4114cb2ef4fSOleksandr "Alex" Zinenko ^bb0(%operand: !transform.any_value): 4124cb2ef4fSOleksandr "Alex" Zinenko // The operand must be defined by an operation. 4134cb2ef4fSOleksandr "Alex" Zinenko %def = transform.get_defining_op %operand 4144cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_value) -> !transform.any_op 4154cb2ef4fSOleksandr "Alex" Zinenko // The defining operation must itself be an elementwise binary. 4164cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %def ["linalg.elemwise_binary"] 4174cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op 4184cb2ef4fSOleksandr "Alex" Zinenko transform.yield %def : !transform.any_op 4194cb2ef4fSOleksandr "Alex" Zinenko } 4204cb2ef4fSOleksandr "Alex" Zinenko 4214cb2ef4fSOleksandr "Alex" Zinenko // And the first operand of that operation must be defined by yet another 4224cb2ef4fSOleksandr "Alex" Zinenko // operation. 4234cb2ef4fSOleksandr "Alex" Zinenko %matmul = transform.get_producer_of_operand %middle[0] 4244cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.any_op 4254cb2ef4fSOleksandr "Alex" Zinenko // And that operation is a matmul. 4264cb2ef4fSOleksandr "Alex" Zinenko transform.match.operation_name %matmul ["linalg.matmul"] : !transform.any_op 4274cb2ef4fSOleksandr "Alex" Zinenko // We will yield the handles to the matmul and the two elementwise 4284cb2ef4fSOleksandr "Alex" Zinenko // operations separately. 4294cb2ef4fSOleksandr "Alex" Zinenko transform.yield %matmul, %middle, %last, %pos 4304cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op, !transform.any_op, !transform.any_op, 4314cb2ef4fSOleksandr "Alex" Zinenko !transform.param<i32> 4324cb2ef4fSOleksandr "Alex" Zinenko} 4334cb2ef4fSOleksandr "Alex" Zinenko``` 4344cb2ef4fSOleksandr "Alex" Zinenko 4354cb2ef4fSOleksandr "Alex" Zinenko 4364cb2ef4fSOleksandr "Alex" ZinenkoThis achieves the desired effect and matches both `max(add(matmul(...), bias), 4374cb2ef4fSOleksandr "Alex" Zinenko0)` and `max(0, add(matmul(...), bias))` in the same values. The `%pos` value is 4384cb2ef4fSOleksandr "Alex" Zinenkoa transform dialect _parameter_, which is used to store lists of entities known 4394cb2ef4fSOleksandr "Alex" Zinenkoto be constant throughout the transform application. Most often, parameters are 4404cb2ef4fSOleksandr "Alex" Zinenkonumeric values, but they can generally be any MLIR attributes. 4414cb2ef4fSOleksandr "Alex" Zinenko 4424cb2ef4fSOleksandr "Alex" ZinenkoIn order to demonstrate that groups of operations are matched independently of 4434cb2ef4fSOleksandr "Alex" Zinenkoeach other, let us use the `transform.foreach_match` operation that allows one 4444cb2ef4fSOleksandr "Alex" Zinenkoto implement a simple high-level pattern rewriting approach within the transform 4454cb2ef4fSOleksandr "Alex" Zinenkodialect (for advanced or lower-level pattern rewriting, consider PDL(L) or C++ 4464cb2ef4fSOleksandr "Alex" Zinenkorewriting APIs). It maps a matcher named sequence to an action named sequence, 4474cb2ef4fSOleksandr "Alex" Zinenkoand the latter gets invoked whenever the former succeeds. 4484cb2ef4fSOleksandr "Alex" Zinenko 4494cb2ef4fSOleksandr "Alex" Zinenko 4504cb2ef4fSOleksandr "Alex" Zinenko```mlir 4514cb2ef4fSOleksandr "Alex" Zinenko// Traverses the payload IR associated with the operand handle, invoking 4524cb2ef4fSOleksandr "Alex" Zinenko// @match_matmul_elemwise on each of the operations. If the named sequence 4534cb2ef4fSOleksandr "Alex" Zinenko// succeeds, i.e., if none of the nested match (transform) operations 4544cb2ef4fSOleksandr "Alex" Zinenko// produced a silenceable failure, invokes @print_matmul_elemwise and 4554cb2ef4fSOleksandr "Alex" Zinenko// forwards the values yielded as arguments of the new invocation. If the 4564cb2ef4fSOleksandr "Alex" Zinenko// named sequence fails with a silenceable failure, silences it (the message 4574cb2ef4fSOleksandr "Alex" Zinenko// is forwarded to the debug stream). Definite failures are propagated 4584cb2ef4fSOleksandr "Alex" Zinenko// immediately and unconditionally, as usual. 4594cb2ef4fSOleksandr "Alex" Zinenkotransform.foreach_match in %root 4604cb2ef4fSOleksandr "Alex" Zinenko @match_matmul_elemwise -> @print_matmul_elemwise 4614cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.any_op 4624cb2ef4fSOleksandr "Alex" Zinenko``` 4634cb2ef4fSOleksandr "Alex" Zinenko 4644cb2ef4fSOleksandr "Alex" Zinenko 4654cb2ef4fSOleksandr "Alex" ZinenkoThe `@print_matmul_elemwise` named sequence, available in `multiple.mlir`, will 4664cb2ef4fSOleksandr "Alex" Zinenkouse the parameter with the position of the operand to differentiate the two 4674cb2ef4fSOleksandr "Alex" Zinenkogroups. 4684cb2ef4fSOleksandr "Alex" Zinenko 4694cb2ef4fSOleksandr "Alex" Zinenko 4704cb2ef4fSOleksandr "Alex" Zinenko## Matchers for Inferred Features 4714cb2ef4fSOleksandr "Alex" Zinenko 4724cb2ef4fSOleksandr "Alex" ZinenkoThe matcher sequences described above, although useful to drive transformations 4734cb2ef4fSOleksandr "Alex" Zinenkofrom within the transform dialect interpreter, are rather basic since they 4744cb2ef4fSOleksandr "Alex" Zinenkomostly rely on operation names and use-def chains. Alternative implementations 4754cb2ef4fSOleksandr "Alex" Zinenkousing APIs or various declarative rewrite rules are barely less expressive and 4764cb2ef4fSOleksandr "Alex" Zinenkosometimes more concise. The real power of transform dialect matcher ops lies in 4774cb2ef4fSOleksandr "Alex" Zinenkothe possibility to define matchers of _inferred properties_ of payloads, i.e., 4784cb2ef4fSOleksandr "Alex" Zinenkoproperties that are not directly accessible as an attribute of an operation or 4794cb2ef4fSOleksandr "Alex" Zinenkoany straightforward relation between IR components. 4804cb2ef4fSOleksandr "Alex" Zinenko 4814cb2ef4fSOleksandr "Alex" ZinenkoThe utility of such matchers can be easily demonstrated by slightly modifying 4824cb2ef4fSOleksandr "Alex" Zinenkoour original example. If matrix multiplication is expressed as a special case of 4834cb2ef4fSOleksandr "Alex" Zinenkotensor contraction using `linalg.generic` instead of `linalg.matmul`, the 4844cb2ef4fSOleksandr "Alex" Zinenkooperation name-based matcher no longer applies. Yet such a representation is 4854cb2ef4fSOleksandr "Alex" Zinenkovery common and can appear both in the original input and during the course of 4864cb2ef4fSOleksandr "Alex" Zinenkotransformation, e.g., where a higher-dimensional contraction is decomposed into 4874cb2ef4fSOleksandr "Alex" Zinenkoloops around a matrix multiplication. 4884cb2ef4fSOleksandr "Alex" Zinenko 4894cb2ef4fSOleksandr "Alex" ZinenkoIn order to be a (potentially transposed) matrix multiplication, the 4904cb2ef4fSOleksandr "Alex" Zinenko`linalg.generic` operation must have the following features: 4914cb2ef4fSOleksandr "Alex" Zinenko 4924cb2ef4fSOleksandr "Alex" Zinenko 4934cb2ef4fSOleksandr "Alex" Zinenko 4944cb2ef4fSOleksandr "Alex" Zinenko* Total rank of 3. 4954cb2ef4fSOleksandr "Alex" Zinenko* Two inputs accessed as projected permutation of iteration dimensions. 4964cb2ef4fSOleksandr "Alex" Zinenko* One output accessed as projected permutation of iteration dimensions. 4974cb2ef4fSOleksandr "Alex" Zinenko* Iteration dimensions can be subdivided into LHS parallel, RHS parallel and reduction dimensions. 4984cb2ef4fSOleksandr "Alex" Zinenko* The body block consists of a multiplication and an addition. 4994cb2ef4fSOleksandr "Alex" Zinenko 5004cb2ef4fSOleksandr "Alex" ZinenkoMost of these features can be derived from the properties of the operation, 5014cb2ef4fSOleksandr "Alex" Zinenkoe.g., the total rank corresponds to the number of entries in the `iterators` 5024cb2ef4fSOleksandr "Alex" Zinenkoattribute, but almost none of them are immediately accessible in the IR or in 5034cb2ef4fSOleksandr "Alex" Zinenkoany declarative form, which is usually limited to checking the presence or the 5044cb2ef4fSOleksandr "Alex" Zinenkoexact match of an attribute or a type. The transform dialect allows these 5054cb2ef4fSOleksandr "Alex" Zinenkofeatures to be implemented in the `apply` method of a matcher op and reused 5064cb2ef4fSOleksandr "Alex" Zinenkoacross multiple matching cases. For structured linear algebra payload 5074cb2ef4fSOleksandr "Alex" Zinenkooperations, many such match operations are readily available in the `structured` 5084cb2ef4fSOleksandr "Alex" Zinenkoextension. They are sufficient to implement a matrix multiplication matcher 5094cb2ef4fSOleksandr "Alex" Zinenkousing the features listed above almost verbatim. 5104cb2ef4fSOleksandr "Alex" Zinenko 5114cb2ef4fSOleksandr "Alex" Zinenko 5124cb2ef4fSOleksandr "Alex" Zinenko```mlir 5134cb2ef4fSOleksandr "Alex" Zinenkotransform.named_sequence @match_generic_matmul( 5144cb2ef4fSOleksandr "Alex" Zinenko %candidate: !transform.any_op {transform.readonly}) -> !transform.any_op { 5154cb2ef4fSOleksandr "Alex" Zinenko // Match a structured linear algebra operation. 5164cb2ef4fSOleksandr "Alex" Zinenko transform.match.structured %candidate : !transform.any_op { 5174cb2ef4fSOleksandr "Alex" Zinenko ^bb0(%c: !transform.any_op): 5184cb2ef4fSOleksandr "Alex" Zinenko // With a rank equal to 3. 5194cb2ef4fSOleksandr "Alex" Zinenko %rank = transform.match.structured.rank %c 5204cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.param<i64> 5214cb2ef4fSOleksandr "Alex" Zinenko %c3 = transform.param.constant 3 : i64 -> !transform.param<i64> 5224cb2ef4fSOleksandr "Alex" Zinenko transform.match.param.cmpi eq %rank, %c3 : !transform.param<i64> 5234cb2ef4fSOleksandr "Alex" Zinenko 5244cb2ef4fSOleksandr "Alex" Zinenko // With 2 inputs. 5254cb2ef4fSOleksandr "Alex" Zinenko %n_ins = transform.match.structured.num_inputs %c 5264cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.param<i64> 5274cb2ef4fSOleksandr "Alex" Zinenko %c2 = transform.param.constant 2 : i64 -> !transform.param<i64> 5284cb2ef4fSOleksandr "Alex" Zinenko transform.match.param.cmpi eq %n_ins, %c2 : !transform.param<i64> 5294cb2ef4fSOleksandr "Alex" Zinenko 5304cb2ef4fSOleksandr "Alex" Zinenko // With 1 output (note that structured ops in destination passing style 5314cb2ef4fSOleksandr "Alex" Zinenko // has as many inits as outputs). 5324cb2ef4fSOleksandr "Alex" Zinenko %n_inits = transform.match.structured.num_inits %c 5334cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) -> !transform.param<i64> 5344cb2ef4fSOleksandr "Alex" Zinenko %c1 = transform.param.constant 1 : i64 -> !transform.param<i64> 5354cb2ef4fSOleksandr "Alex" Zinenko transform.match.param.cmpi eq %n_inits, %c1 : !transform.param<i64> 5364cb2ef4fSOleksandr "Alex" Zinenko 5374cb2ef4fSOleksandr "Alex" Zinenko // All inputs and inits are accessed with a projected permutation. 5384cb2ef4fSOleksandr "Alex" Zinenko transform.match.structured.input %c[all] {projected_permutation} 5394cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op 5404cb2ef4fSOleksandr "Alex" Zinenko transform.match.structured.init %c[0] {projected_permutation} 5414cb2ef4fSOleksandr "Alex" Zinenko : !transform.any_op 5424cb2ef4fSOleksandr "Alex" Zinenko 5434cb2ef4fSOleksandr "Alex" Zinenko // The body is a mulf/addf contraction with appropriate dimensions. 5444cb2ef4fSOleksandr "Alex" Zinenko transform.match.structured.body %c 5454cb2ef4fSOleksandr "Alex" Zinenko { contraction = ["arith.mulf", "arith.addf"] } : !transform.any_op 5464cb2ef4fSOleksandr "Alex" Zinenko %batch, %lhs, %rhs, %reduction = 5474cb2ef4fSOleksandr "Alex" Zinenko transform.match.structured.classify_contraction_dims %c 5484cb2ef4fSOleksandr "Alex" Zinenko : (!transform.any_op) 5494cb2ef4fSOleksandr "Alex" Zinenko -> (!transform.param<i64>, !transform.param<i64>, !transform.param<i64>, 5504cb2ef4fSOleksandr "Alex" Zinenko !transform.param<i64>) 5514cb2ef4fSOleksandr "Alex" Zinenko 5524cb2ef4fSOleksandr "Alex" Zinenko 5534cb2ef4fSOleksandr "Alex" Zinenko // There is one of lhs, rhs and reduction dimensions and zero batch 5544cb2ef4fSOleksandr "Alex" Zinenko // dimensions. 5554cb2ef4fSOleksandr "Alex" Zinenko %n_batch = transform.num_associations %batch 5564cb2ef4fSOleksandr "Alex" Zinenko : (!transform.param<i64>) -> !transform.param<i64> 5574cb2ef4fSOleksandr "Alex" Zinenko %n_lhs = transform.num_associations %lhs 5584cb2ef4fSOleksandr "Alex" Zinenko : (!transform.param<i64>) -> !transform.param<i64> 5594cb2ef4fSOleksandr "Alex" Zinenko %n_rhs = transform.num_associations %rhs 5604cb2ef4fSOleksandr "Alex" Zinenko : (!transform.param<i64>) -> !transform.param<i64> 5614cb2ef4fSOleksandr "Alex" Zinenko %n_reduction = transform.num_associations %reduction 5624cb2ef4fSOleksandr "Alex" Zinenko : (!transform.param<i64>) -> !transform.param<i64> 5634cb2ef4fSOleksandr "Alex" Zinenko %c0 = transform.param.constant 0 : i64 -> !transform.param<i64> 5644cb2ef4fSOleksandr "Alex" Zinenko transform.match.param.cmpi eq %n_batch, %c0 : !transform.param<i64> 5654cb2ef4fSOleksandr "Alex" Zinenko transform.match.param.cmpi eq %n_lhs, %c1 : !transform.param<i64> 5664cb2ef4fSOleksandr "Alex" Zinenko transform.match.param.cmpi eq %n_rhs, %c1 : !transform.param<i64> 5674cb2ef4fSOleksandr "Alex" Zinenko transform.match.param.cmpi eq %n_reduction, %c1 : !transform.param<i64> 5684cb2ef4fSOleksandr "Alex" Zinenko } 5694cb2ef4fSOleksandr "Alex" Zinenko transform.yield %candidate : !transform.any_op 5704cb2ef4fSOleksandr "Alex" Zinenko} 5714cb2ef4fSOleksandr "Alex" Zinenko``` 5724cb2ef4fSOleksandr "Alex" Zinenko 5734cb2ef4fSOleksandr "Alex" Zinenko 5744cb2ef4fSOleksandr "Alex" ZinenkoWhile this example leverages the contraction-specific matchers that have a 5754cb2ef4fSOleksandr "Alex" Zinenkorather non-trivial C++ implementation, the transform dialect is sufficiently 5764cb2ef4fSOleksandr "Alex" Zinenkoflexible to implement this reasoning directly if desired. One could, for 5774cb2ef4fSOleksandr "Alex" Zinenkoexample, obtain the access map of each input as a parameter and extract the 5784cb2ef4fSOleksandr "Alex" Zinenkoaccessed dimensions as other parameters that can be compared with each other to 5794cb2ef4fSOleksandr "Alex" Zinenkoensure the subscripts are `m,k` for LHS, `k,n` for RHS and `m,n` for the 5804cb2ef4fSOleksandr "Alex" Zinenkoinit/result given the `m,n,k` notation for loops. 5814cb2ef4fSOleksandr "Alex" Zinenko 5820c13a896SKohei Yamaguchi## Appendix: Autogenerated Documentation 5830c13a896SKohei Yamaguchi 5840c13a896SKohei Yamaguchi[include "Tutorials/transform/MyExtensionCh4.md"] 5850c13a896SKohei Yamaguchi 586