-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[mlir][linalg] Extend FuseElementwiseOps pattern to work with named ops
#144922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 24 commits
c76a8cc
fa60fa5
20b25f3
8e471a7
d723913
5280b87
c2f52bc
8d2e8e0
58582bf
cf67ab6
7d402c1
b1d15b2
459bcb4
f7e164b
9acb5d0
8a375bf
5c3bd5f
a12b417
ce33596
1eb3461
389a9c4
55cca22
b914037
8c906a0
047266b
880d9c0
5588c86
95635df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -77,11 +77,11 @@ static AffineMap getIndexingMapOfProducerOperandsInCoordinatesOfFusedOp( | |||||
| // of the fused producer & consumer after the fusion can still compute the | ||||||
| // bounds of the op. | ||||||
| static bool isOpOperandCanBeDroppedAfterFusedLinalgs( | ||||||
| GenericOp producer, GenericOp consumer, | ||||||
| LinalgOp producer, LinalgOp consumer, | ||||||
| ArrayRef<OpOperand *> opOperandsToIgnore) { | ||||||
| SmallVector<AffineMap> indexingMaps; | ||||||
|
|
||||||
| SmallVector<GenericOp> ops = {producer, consumer}; | ||||||
| SmallVector<LinalgOp> ops = {producer, consumer}; | ||||||
| for (auto &op : ops) { | ||||||
| for (auto &opOperand : op->getOpOperands()) { | ||||||
| if (llvm::is_contained(opOperandsToIgnore, &opOperand)) { | ||||||
|
|
@@ -109,8 +109,9 @@ static bool isOpOperandCanBeDroppedAfterFusedLinalgs( | |||||
| /// * There is a chance that the implementation of the transformation does not | ||||||
| /// agree with the result of this method. This function gives a prediction based | ||||||
| /// on an optimized fusion. | ||||||
| llvm::SmallDenseSet<int> mlir::linalg::getPreservedProducerResults( | ||||||
| GenericOp producer, GenericOp consumer, OpOperand *fusedOperand) { | ||||||
| llvm::SmallDenseSet<int> | ||||||
| mlir::linalg::getPreservedProducerResults(LinalgOp producer, LinalgOp consumer, | ||||||
| OpOperand *fusedOperand) { | ||||||
| llvm::SmallDenseSet<int> preservedProducerResults; | ||||||
| llvm::SmallVector<OpOperand *> opOperandsToIgnore; | ||||||
|
|
||||||
|
|
@@ -140,8 +141,8 @@ bool mlir::linalg::areElementwiseOpsFusable(OpOperand *fusedOperand) { | |||||
| if (!fusedOperand) | ||||||
| return false; | ||||||
|
|
||||||
| auto producer = fusedOperand->get().getDefiningOp<GenericOp>(); | ||||||
| auto consumer = dyn_cast<GenericOp>(fusedOperand->getOwner()); | ||||||
| auto producer = fusedOperand->get().getDefiningOp<LinalgOp>(); | ||||||
| auto consumer = dyn_cast<LinalgOp>(fusedOperand->getOwner()); | ||||||
|
|
||||||
| // Check producer and consumer are generic ops. | ||||||
| if (!producer || !consumer) | ||||||
|
|
@@ -216,16 +217,17 @@ bool mlir::linalg::areElementwiseOpsFusable(OpOperand *fusedOperand) { | |||||
| /// Generate the region of the fused tensor operation. The region of the fused | ||||||
| /// op must be empty. | ||||||
| static void generateFusedElementwiseOpRegion( | ||||||
| RewriterBase &rewriter, GenericOp fusedOp, | ||||||
| RewriterBase &rewriter, LinalgOp fusedOp, | ||||||
| AffineMap consumerToProducerLoopsMap, OpOperand *fusedOperand, | ||||||
| unsigned nloops, llvm::SmallDenseSet<int> &preservedProducerResults) { | ||||||
| auto producer = cast<GenericOp>(fusedOperand->get().getDefiningOp()); | ||||||
| auto consumer = cast<GenericOp>(fusedOperand->getOwner()); | ||||||
| auto producer = cast<LinalgOp>(fusedOperand->get().getDefiningOp()); | ||||||
| auto consumer = cast<LinalgOp>(fusedOperand->getOwner()); | ||||||
| // Build the region of the fused op. | ||||||
|
|
||||||
| Block &producerBlock = producer->getRegion(0).front(); | ||||||
| Block &consumerBlock = consumer->getRegion(0).front(); | ||||||
| OpBuilder::InsertionGuard guard(rewriter); | ||||||
| Block *fusedBlock = rewriter.createBlock(&fusedOp.getRegion()); | ||||||
| Block *fusedBlock = rewriter.createBlock(&fusedOp->getRegion(0)); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong. Please use interface methods to do it correctly:
or
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm actually unsure how to apply this suggestion. the other one above makes sense since i'm just getting the blocks that have already been created. however this one is creating a block in the op's region and I dont see an interface method for getting a region. If region 0 isn't guaranteed, shouldn't the interface have a method for that? Anyway, @rengolin had a discussion back when this PR first went up about generating named ops post fusion when possible. I think it was agreed that it makes sense to leave this as a TODO (see #144922 (comment)). So when we get there we can revisit how to do this, unless there's an obvious solution now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Probably better to declare it as a GenericOp yes, since the transformation always (today) returns a GenericOp anyway.
I think that is a different problem. My main concern is that expecting something from an interface when the interface doesn't gurantee it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is a separate problem. i just meant that when we make the changes to fuse to named ops (when possible) then |
||||||
| IRMapping mapper; | ||||||
|
|
||||||
| // 2. Add an index operation for every fused loop dimension and use the | ||||||
|
|
@@ -331,7 +333,7 @@ static void generateFusedElementwiseOpRegion( | |||||
| YieldOp::create(rewriter, fusedOp.getLoc(), fusedYieldValues); | ||||||
|
|
||||||
| // Sanity checks. | ||||||
| assert(fusedBlock->getNumArguments() == fusedOp.getNumOperands() && | ||||||
| assert(fusedBlock->getNumArguments() == fusedOp->getNumOperands() && | ||||||
| "Ill-formed GenericOp region"); | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -341,8 +343,8 @@ mlir::linalg::fuseElementwiseOps(RewriterBase &rewriter, | |||||
| assert(areElementwiseOpsFusable(fusedOperand) && | ||||||
| "expected elementwise operation pre-conditions to pass"); | ||||||
| auto producerResult = cast<OpResult>(fusedOperand->get()); | ||||||
| auto producer = cast<GenericOp>(producerResult.getOwner()); | ||||||
| auto consumer = cast<GenericOp>(fusedOperand->getOwner()); | ||||||
| auto producer = cast<LinalgOp>(producerResult.getOwner()); | ||||||
| auto consumer = cast<LinalgOp>(fusedOperand->getOwner()); | ||||||
| // TODO: allow fusing the producer of an output operand. | ||||||
| assert(consumer.isDpsInput(fusedOperand) && | ||||||
| "expected producer of input operand"); | ||||||
|
|
@@ -419,10 +421,7 @@ mlir::linalg::fuseElementwiseOps(RewriterBase &rewriter, | |||||
| // Generate the fused op. | ||||||
| auto fusedOp = GenericOp::create( | ||||||
| rewriter, consumer.getLoc(), fusedResultTypes, fusedInputOperands, | ||||||
| fusedOutputOperands, rewriter.getAffineMapArrayAttr(fusedIndexMaps), | ||||||
| consumer.getIteratorTypes(), | ||||||
| /*doc=*/nullptr, | ||||||
| /*library_call=*/nullptr); | ||||||
| fusedOutputOperands, fusedIndexMaps, consumer.getIteratorTypesArray()); | ||||||
| if (!fusedOp.getShapesToLoopsMap()) { | ||||||
| // Fused op has invalid indexing maps. Typically this means something is off | ||||||
| // in the input, but going ahead here would result in verification errors. | ||||||
|
|
@@ -461,14 +460,14 @@ mlir::linalg::fuseElementwiseOps(RewriterBase &rewriter, | |||||
|
|
||||||
| namespace { | ||||||
| /// Patterns to fuse a generic op, with the producer of its operands. | ||||||
| class FuseElementwiseOps : public OpRewritePattern<GenericOp> { | ||||||
| class FuseElementwiseOps : public OpInterfaceRewritePattern<LinalgOp> { | ||||||
| public: | ||||||
| FuseElementwiseOps(MLIRContext *context, ControlFusionFn fun, | ||||||
| PatternBenefit benefit = 1) | ||||||
| : OpRewritePattern<GenericOp>(context, benefit), | ||||||
| : OpInterfaceRewritePattern<LinalgOp>(context, benefit), | ||||||
| controlFn(std::move(fun)) {} | ||||||
|
|
||||||
| LogicalResult matchAndRewrite(GenericOp genericOp, | ||||||
| LogicalResult matchAndRewrite(LinalgOp genericOp, | ||||||
| PatternRewriter &rewriter) const override { | ||||||
| // Find the first operand that is defined by another generic op on tensors. | ||||||
| for (OpOperand &opOperand : genericOp->getOpOperands()) { | ||||||
|
|
@@ -495,7 +494,7 @@ class FuseElementwiseOps : public OpRewritePattern<GenericOp> { | |||||
| rewriter.eraseOp(genericOp); | ||||||
| return success(); | ||||||
| } | ||||||
| return failure(); | ||||||
| return rewriter.notifyMatchFailure(genericOp, "no fusable operands"); | ||||||
| } | ||||||
|
|
||||||
| private: | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,3 +59,154 @@ func.func @handle_unused_operands(%arg0: tensor<8xf32>, %arg1: tensor<8xf32>) -> | |
| // CHECK: %[[FUSED_OP:.+]] = linalg.generic | ||
| // CHECK-SAME: outs(%[[EMPTY]] : | ||
| // CHECK-NOT: linalg.generic | ||
|
|
||
| // ----- | ||
|
|
||
| func.func @map_ops(%in1: tensor<8xf32>, %in2: tensor<8xf32>) -> tensor<8xf32> { | ||
| %fill = tensor.empty() : tensor<8xf32> | ||
| %add = linalg.map {arith.addf} ins(%in1, %in2: tensor<8xf32>, tensor<8xf32>) outs(%fill: tensor<8xf32>) | ||
| %sqrt = linalg.map { math.sqrt } ins(%add : tensor<8xf32>) outs(%fill : tensor<8xf32>) | ||
| return %sqrt : tensor<8xf32> | ||
| } | ||
|
|
||
| // CHECK-LABEL: func @map_ops | ||
| // CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<8xf32> | ||
| // CHECK: %[[FUSED_OP:.+]] = linalg.generic | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe not for this PR, but we have discussed keeping the named ops for as long as possible. Here, since they're both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yah I mentioned in a comment that I wanted to try to do that, but don't know of a clean way to do that yet. I've done something like that before by just using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also that wouldn't help with elementwise+elementwise -> map anyway There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Right, the idea is that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one thing to note. |
||
| // CHECK-SAME: ins(%[[ARG0]], %[[ARG1]] : {{.*}}) outs(%[[EMPTY]] : | ||
| // CHECK-NEXT: ^bb0(%[[IN0:.*]]: f32, %[[IN1:.*]]: f32, %[[OUT:.*]]: f32): | ||
| // CHECK-NEXT: %[[ADD:.*]] = arith.addf %[[IN0]], %[[IN1]] | ||
| // CHECK-NEXT: %[[SQRT:.*]] = math.sqrt %[[ADD]] | ||
| // CHECK-NEXT: linalg.yield %[[SQRT]] | ||
| // CHECK-NOT: linalg.map | ||
|
|
||
| // ----- | ||
|
|
||
| func.func @map_ops_mixed_types(%arg0: tensor<8xf32>, %arg1: tensor<8xf32>) -> tensor<8xf32> { | ||
srcarroll marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| %init = tensor.empty() : tensor<8xi1> | ||
| %initf = tensor.empty() : tensor<8xf32> | ||
| %0 = linalg.map {math.sqrt} ins(%arg0 : tensor<8xf32>) outs(%initf : tensor<8xf32>) | ||
| %1 = linalg.map {math.exp} ins(%arg1 : tensor<8xf32>) outs(%initf : tensor<8xf32>) | ||
| %2 = linalg.map ins(%0, %1 : tensor<8xf32>, tensor<8xf32>) outs (%init : tensor<8xi1>) | ||
| (%in0 : f32, %in1 : f32, %out : i1) { | ||
| %cmp = arith.cmpf olt, %in0, %in1 : f32 | ||
| linalg.yield %cmp : i1 | ||
| } | ||
| %3 = linalg.map { arith.select } ins(%2, %0, %1 : tensor<8xi1>, tensor<8xf32>, tensor<8xf32>) outs(%initf : tensor<8xf32>) | ||
| return %3 : tensor<8xf32> | ||
| } | ||
|
|
||
| // CHECK-LABEL: func @map_ops_mixed_types | ||
| // CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<8xf32> | ||
| // CHECK: %[[FUSED_OP:.+]] = linalg.generic | ||
| // CHECK-SAME: ins(%[[ARG0]], %[[ARG1]] : {{.*}}) outs(%[[EMPTY]] : | ||
| // CHECK-NEXT: ^bb0(%[[IN0:.*]]: f32, %[[IN1:.*]]: f32, %[[OUT:.*]]: f32): | ||
| // CHECK-NEXT: %[[EXP0:.*]] = math.exp %[[IN1]] | ||
| // CHECK-NEXT: %[[SQRT0:.*]] = math.sqrt %[[IN0]] | ||
| // CHECK-NEXT: %[[EXP1:.*]] = math.exp %[[IN1]] | ||
| // CHECK-NEXT: %[[SQRT1:.*]] = math.sqrt %[[IN0]] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think the duplicate computations are an old artifact. these do go away with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is indeed odd. Looks like a bug in the fuser. Could be related to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did make a generic version of this and ran the old version of the pass and got same results to confirm it's a pre-existing thing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here's the generic version |
||
| // CHECK-NEXT: %[[CMP:.*]] = arith.cmpf olt, %[[SQRT1]], %[[EXP1]] | ||
| // CHECK-NEXT: %[[RES:.*]] = arith.select %[[CMP]], %[[SQRT0]], %[[EXP0]] | ||
| // CHECK-NEXT: linalg.yield %[[RES]] | ||
| // CHECK-NOT: linalg.map | ||
|
|
||
| // ----- | ||
|
|
||
| #identity = affine_map<(d0, d1) -> (d0, d1)> | ||
| #bcast = affine_map<(d0, d1) -> (d0)> | ||
| func.func @elementwise_ops(%in1: tensor<8xf32>, %in2: tensor<8x10xf32>) -> tensor<8x10xf32> { | ||
| %fill = tensor.empty() : tensor<8x10xf32> | ||
| %add = linalg.elementwise | ||
| kind=#linalg.elementwise_kind<add> | ||
| indexing_maps = [#bcast, #identity, #identity] | ||
| ins(%in1, %in2: tensor<8xf32>, tensor<8x10xf32>) outs(%fill: tensor<8x10xf32>) -> tensor<8x10xf32> | ||
| %sqrt = linalg.elementwise | ||
| kind=#linalg.elementwise_kind<sqrt> | ||
| indexing_maps = [#identity, #identity] | ||
| ins(%add : tensor<8x10xf32>) outs(%fill : tensor<8x10xf32>) -> tensor<8x10xf32> | ||
| return %sqrt : tensor<8x10xf32> | ||
| } | ||
|
|
||
| // CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1) -> (d0, d1)> | ||
| // CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1) -> (d0)> | ||
| // CHECK-LABEL: func @elementwise_ops | ||
| // CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]: tensor<8x10xf32> | ||
| // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<8x10xf32> | ||
| // CHECK: %[[FUSED_OP:.+]] = linalg.generic | ||
| // CHECK-SAME: indexing_maps = [#[[$MAP1]], #[[$MAP0]], #[[$MAP0]]] | ||
| // CHECK-SAME: ins(%[[ARG0]], %[[ARG1]] : {{.*}}) outs(%[[EMPTY]] : | ||
| // CHECK-NEXT: ^bb0(%[[IN0:.*]]: f32, %[[IN1:.*]]: f32, %[[OUT:.*]]: f32): | ||
| // CHECK-NEXT: %[[ADD:.*]] = arith.addf %[[IN0]], %[[IN1]] | ||
| // CHECK-NEXT: %[[SQRT:.*]] = math.sqrt %[[ADD]] | ||
| // CHECK-NEXT: linalg.yield %[[SQRT]] | ||
| // CHECK-NOT: linalg.map | ||
|
|
||
| // ----- | ||
|
|
||
| func.func @map_multi_ops(%arg0: tensor<8xf32>, %arg1: tensor<8xf32>, %arg2: tensor<8xf32>) -> tensor<8xf32> { | ||
| %fill = tensor.empty() : tensor<8xf32> | ||
| %add_exp = linalg.map ins(%arg0, %arg1: tensor<8xf32>, tensor<8xf32>) outs(%fill: tensor<8xf32>) | ||
| (%in0 : f32, %in1 : f32, %out : f32) { | ||
| %add = arith.addf %in0, %in1 : f32 | ||
| %exp = math.exp %add : f32 | ||
| linalg.yield %exp : f32 | ||
| } | ||
| %sqrt_mul = linalg.map ins(%add_exp, %arg2 : tensor<8xf32>, tensor<8xf32>) outs(%fill : tensor<8xf32>) | ||
| (%in0 : f32, %in1 : f32, %out : f32) { | ||
| %sqrt = math.sqrt %in0 : f32 | ||
| %mul = arith.mulf %sqrt, %in1 : f32 | ||
| linalg.yield %mul : f32 | ||
| } | ||
| return %sqrt_mul : tensor<8xf32> | ||
| } | ||
|
|
||
| // CHECK-LABEL: func @map_multi_ops | ||
| // CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK-SAME: %[[ARG2:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<8xf32> | ||
| // CHECK: %[[FUSED_OP:.+]] = linalg.generic | ||
| // CHECK-SAME: ins(%[[ARG0]], %[[ARG1]], %[[ARG2]] : {{.*}}) outs(%[[EMPTY]] : | ||
| // CHECK-NEXT: ^bb0(%[[IN0:.*]]: f32, %[[IN1:.*]]: f32, %[[IN2:.*]]: f32, %[[OUT:.*]]: f32): | ||
| // CHECK-NEXT: %[[ADD:.*]] = arith.addf %[[IN0]], %[[IN1]] | ||
| // CHECK-NEXT: %[[EXP:.*]] = math.exp %[[ADD]] | ||
| // CHECK-NEXT: %[[SQRT:.*]] = math.sqrt %[[EXP]] | ||
| // CHECK-NEXT: %[[MUL:.*]] = arith.mulf %[[SQRT]], %[[IN2]] | ||
| // CHECK-NEXT: linalg.yield %[[MUL]] | ||
| // CHECK-NOT: linalg.map | ||
|
|
||
| // ----- | ||
|
|
||
| #identity = affine_map<(d0, d1) -> (d0, d1)> | ||
| #bcast = affine_map<(d0, d1) -> (d0)> | ||
| func.func @map_genric_ops(%arg0: tensor<8xf32>, %arg1: tensor<8x10xf32>) -> tensor<8x10xf32> { | ||
| %fill = tensor.empty() : tensor<8x10xf32> | ||
| %add = linalg.generic | ||
| {indexing_maps = [#bcast, #identity, #identity], iterator_types = ["parallel", "parallel"]} | ||
| ins(%arg0, %arg1: tensor<8xf32>, tensor<8x10xf32>) outs(%fill: tensor<8x10xf32>) { | ||
| ^bb0(%in0: f32, %in1: f32, %out: f32): | ||
| %add = arith.addf %in0, %in1 : f32 | ||
| linalg.yield %add : f32 | ||
| } -> tensor<8x10xf32> | ||
| %sqrt = linalg.map { math.sqrt } ins(%add : tensor<8x10xf32>) outs(%fill : tensor<8x10xf32>) | ||
| return %sqrt : tensor<8x10xf32> | ||
| } | ||
|
|
||
| // CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0, d1) -> (d0, d1)> | ||
| // CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0, d1) -> (d0)> | ||
| // CHECK-LABEL: func @map_genric_ops | ||
| // CHECK-SAME: %[[ARG0:[a-zA-Z0-9]+]]: tensor<8xf32> | ||
| // CHECK-SAME: %[[ARG1:[a-zA-Z0-9]+]]: tensor<8x10xf32> | ||
| // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<8x10xf32> | ||
| // CHECK: %[[FUSED_OP:.+]] = linalg.generic | ||
| // CHECK-SAME: indexing_maps = [#[[$MAP1]], #[[$MAP0]], #[[$MAP0]]] | ||
| // CHECK-SAME: ins(%[[ARG0]], %[[ARG1]] : {{.*}}) outs(%[[EMPTY]] : | ||
| // CHECK-NEXT: ^bb0(%[[IN0:.*]]: f32, %[[IN1:.*]]: f32, %[[OUT:.*]]: f32): | ||
| // CHECK-NEXT: %[[ADD:.*]] = arith.addf %[[IN0]], %[[IN1]] | ||
| // CHECK-NEXT: %[[SQRT:.*]] = math.sqrt %[[ADD]] | ||
| // CHECK-NEXT: linalg.yield %[[SQRT]] | ||
| // CHECK-NOT: linalg.map | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You cannot do this on an interface. There is no gurantee that the op implementing this interface has region 0 as the region of the op. You can use the interface methods to get this:
llvm-project/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.td
Line 491 in cdf52a1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes. thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done