| //===- IndependenceTransforms.cpp - Make ops independent of values --------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Dialect/Tensor/Transforms/Transforms.h" | |
| #include "mlir/Dialect/Affine/IR/AffineOps.h" | |
| #include "mlir/Dialect/Affine/Transforms/Transforms.h" | |
| #include "mlir/Dialect/Tensor/IR/Tensor.h" | |
| #include "mlir/Interfaces/ValueBoundsOpInterface.h" | |
| using namespace mlir; | |
| using namespace mlir::tensor; | |
| /// Make the given OpFoldResult independent of all independencies. | |
| static FailureOr<OpFoldResult> makeIndependent(OpBuilder &b, Location loc, | |
| OpFoldResult ofr, | |
| ValueRange independencies) { | |
| if (isa<Attribute>(ofr)) | |
| return ofr; | |
| Value value = cast<Value>(ofr); | |
| AffineMap boundMap; | |
| ValueDimList mapOperands; | |
| if (failed(ValueBoundsConstraintSet::computeIndependentBound( | |
| boundMap, mapOperands, presburger::BoundType::UB, value, | |
| independencies, ValueBoundsOptions{/*closedUB=*/true}))) | |
| return failure(); | |
| return mlir::affine::materializeComputedBound(b, loc, boundMap, mapOperands); | |
| } | |
| FailureOr<Value> tensor::buildIndependentOp(OpBuilder &b, tensor::PadOp padOp, | |
| ValueRange independencies) { | |
| OpBuilder::InsertionGuard g(b); | |
| b.setInsertionPoint(padOp); | |
| Location loc = padOp.getLoc(); | |
| // Non-constant padding not supported. | |
| Value constantPadding = padOp.getConstantPaddingValue(); | |
| if (!constantPadding) | |
| return failure(); | |
| SmallVector<OpFoldResult> newMixedLow, newMixedHigh; | |
| for (OpFoldResult ofr : padOp.getMixedLowPad()) { | |
| auto ub = makeIndependent(b, loc, ofr, independencies); | |
| if (failed(ub)) | |
| return failure(); | |
| newMixedLow.push_back(*ub); | |
| } | |
| for (OpFoldResult ofr : padOp.getMixedHighPad()) { | |
| auto ub = makeIndependent(b, loc, ofr, independencies); | |
| if (failed(ub)) | |
| return failure(); | |
| newMixedHigh.push_back(*ub); | |
| } | |
| // Return existing tensor::PadOp if nothing has changed. | |
| if (llvm::equal(padOp.getMixedLowPad(), newMixedLow) && | |
| llvm::equal(padOp.getMixedHighPad(), newMixedHigh)) | |
| return padOp.getResult(); | |
| // Create a new tensor::PadOp. | |
| auto newPadOp = | |
| PadOp::create(b, loc, padOp.getResultType(), padOp.getSource(), | |
| newMixedLow, newMixedHigh, constantPadding, | |
| padOp.getNofold(), /*attrs=*/ArrayRef<NamedAttribute>{}); | |
| // Create a tensor::ExtractSliceOp. | |
| // Reify the result sizes of the old tensor::PadOp. | |
| ReifiedRankedShapedTypeDims reifiedSizes; | |
| ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface = | |
| dyn_cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation()); | |
| if (failed(reifyShapedTypeInterface.reifyResultShapes(b, reifiedSizes))) | |
| return failure(); | |
| SmallVector<OpFoldResult> offsets, sizes, strides; | |
| for (int64_t i = 0, e = padOp.getResultType().getRank(); i < e; ++i) { | |
| // offset = ub(low_padding) - low_padding | |
| OpFoldResult prevLow = padOp.getMixedLowPad()[i]; | |
| if (isa<Attribute>(prevLow)) { | |
| offsets.push_back(b.getIndexAttr(0)); | |
| } else { | |
| offsets.push_back( | |
| affine::AffineApplyOp::create( | |
| b, loc, b.getAffineDimExpr(0) - b.getAffineDimExpr(1), | |
| std::initializer_list<Value>{cast<Value>(newMixedLow[i]), | |
| cast<Value>(prevLow)}) | |
| .getResult()); | |
| } | |
| // size = reified result size | |
| if (!padOp.getResultType().isDynamicDim(i)) { | |
| sizes.push_back(b.getIndexAttr(padOp.getResultType().getDimSize(i))); | |
| } else { | |
| sizes.push_back(reifiedSizes[0][i]); | |
| } | |
| // stride = 1 | |
| strides.push_back(b.getIndexAttr(1)); | |
| } | |
| return ExtractSliceOp::create(b, loc, newPadOp, offsets, sizes, strides) | |
| .getResult(); | |
| } | |
| FailureOr<Value> tensor::buildIndependentOp(OpBuilder &b, | |
| tensor::EmptyOp emptyOp, | |
| ValueRange independencies) { | |
| OpBuilder::InsertionGuard g(b); | |
| b.setInsertionPoint(emptyOp); | |
| Location loc = emptyOp.getLoc(); | |
| SmallVector<OpFoldResult> newSizes; | |
| for (OpFoldResult ofr : emptyOp.getMixedSizes()) { | |
| auto ub = makeIndependent(b, loc, ofr, independencies); | |
| if (failed(ub)) | |
| return failure(); | |
| newSizes.push_back(*ub); | |
| } | |
| // Return existing tensor::EmptyOp if nothing has changed. | |
| if (llvm::equal(emptyOp.getMixedSizes(), newSizes)) | |
| return emptyOp.getResult(); | |
| // Create a new tensor::EmptyOp. | |
| Value newEmptyOp = | |
| EmptyOp::create(b, loc, newSizes, emptyOp.getType().getElementType()); | |
| // Create a tensor::ExtractSliceOp. | |
| SmallVector<OpFoldResult> offsets(newSizes.size(), b.getIndexAttr(0)); | |
| SmallVector<OpFoldResult> strides(newSizes.size(), b.getIndexAttr(1)); | |
| return ExtractSliceOp::create(b, loc, newEmptyOp, offsets, | |
| emptyOp.getMixedSizes(), strides) | |
| .getResult(); | |
| } |