blob: 4b025f71c87d5d05edc3f18089a48aa9f405d97b [file] [edit]
// Copyright 2023 The IREE Authors
//
// Licensed 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 "iree/compiler/GlobalOptimization/Utils.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Linalg/Utils/Utils.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
namespace mlir::iree_compiler::GlobalOptimization {
std::optional<CastOpInterface> getDefiningNonI1ExtendingCastOp(Value input) {
// Returns true if the source of cast op has i1 element type.
auto isI1Src = [](CastOpInterface castOp) -> bool {
auto elemType = getElementTypeOrSelf(castOp->getOperandTypes()[0]);
return elemType.getIntOrFloatBitWidth() == 1;
};
auto isExtending = [](CastOpInterface castOp) -> bool {
auto inElemType = getElementTypeOrSelf(castOp->getOperandTypes()[0]);
auto outElemType = getElementTypeOrSelf(castOp->getResultTypes()[0]);
return inElemType.getIntOrFloatBitWidth() <
outElemType.getIntOrFloatBitWidth();
};
std::optional<CastOpInterface> result = std::nullopt;
auto castOp = input.getDefiningOp<CastOpInterface>();
if (castOp) {
if (!isI1Src(castOp) && isExtending(castOp)) {
result = castOp;
}
return result;
}
auto genericOp = input.getDefiningOp<linalg::GenericOp>();
if (!genericOp || genericOp.getNumDpsInputs() != 1 ||
genericOp.getNumDpsInits() != 1 ||
genericOp.getBody()->getOperations().size() != 2 ||
!isElementwise(genericOp)) {
return std::nullopt;
}
auto yieldOp = cast<linalg::YieldOp>(genericOp.getBody()->getTerminator());
castOp = yieldOp->getOperand(0).getDefiningOp<CastOpInterface>();
if (!castOp) {
return std::nullopt;
}
Value castIn = castOp->getOperand(0);
if (auto blockArg = dyn_cast<BlockArgument>(castIn)) {
if (blockArg.getArgNumber() != 0) {
return std::nullopt;
}
}
if (!isI1Src(castOp) && isExtending(castOp)) {
result = castOp;
}
return result;
}
std::optional<Type> getCastElemType(Value input) {
std::optional<CastOpInterface> castOp =
getDefiningNonI1ExtendingCastOp(input);
if (!castOp) {
return std::nullopt;
}
Type castSrcElemType = getElementTypeOrSelf(castOp.value()->getOperand(0));
if (isa<arith::ExtUIOp>(castOp.value())) {
int64_t bitWidth = castSrcElemType.getIntOrFloatBitWidth();
return IntegerType::get(castOp->getContext(), bitWidth,
IntegerType::SignednessSemantics::Unsigned);
}
return castSrcElemType;
}
Value createGenericElementwiseCastOp(
OpBuilder &builder, Location loc, Value input, CastOpInterface castOp,
ArrayRef<NamedAttribute> attrs,
std::optional<IREE::Encoding::EncodingAttr> encoding) {
auto inputType = cast<RankedTensorType>(input.getType());
SmallVector<AffineMap> maps(
2, AffineMap::getMultiDimIdentityMap(inputType.getRank(),
builder.getContext()));
SmallVector<utils::IteratorType> iteratorTypes(inputType.getRank(),
utils::IteratorType::parallel);
auto elementType = getElementTypeOrSelf(castOp->getResultTypes()[0]);
auto castedType = inputType.clone(elementType);
SmallVector<OpFoldResult> inputMixedSizes =
tensor::getMixedSizes(builder, loc, input);
Value init = encoding ? tensor::EmptyOp::create(builder, loc, inputMixedSizes,
elementType, *encoding)
: tensor::EmptyOp::create(builder, loc, inputMixedSizes,
elementType);
return linalg::GenericOp::create(
builder, loc, castedType, input, init, maps, iteratorTypes,
[&](OpBuilder &b, Location nestedLoc, ValueRange args) {
Value castRes =
b.create(nestedLoc, castOp->getName().getIdentifier(),
args[0], elementType)
->getResult(0);
linalg::YieldOp::create(b, nestedLoc, castRes);
},
attrs)
.getResult(0);
}
Value sumReduceDimensionSubset(ImplicitLocOpBuilder &rewriter, Value val,
Type accETy, ArrayRef<bool> is_reduction) {
auto context = val.getContext();
RankedTensorType ty = cast<RankedTensorType>(val.getType());
llvm::SmallVector<int64_t> staticSizes;
SmallVector<Value> dynSizes;
for (int i = 0, s = is_reduction.size(); i < s; i++) {
if (is_reduction[i]) {
continue;
}
staticSizes.push_back(ty.getDimSize(i));
if (ty.isDynamicDim(i)) {
dynSizes.push_back(tensor::DimOp::create(rewriter, val, i));
}
}
// Create a zero-filled accumulator.
Value initAcc =
tensor::EmptyOp::create(rewriter, staticSizes, accETy, dynSizes);
Value zeroInt = arith::ConstantIntOp::create(rewriter, accETy, 0).getResult();
Value zeroAcc =
linalg::FillOp::create(rewriter, zeroInt, initAcc).getResult(0);
SmallVector<AffineExpr> filterExprs(ty.getRank());
SmallVector<AffineExpr> outputExprs;
SmallVector<utils::IteratorType> iterators;
for (int i = 0, s = ty.getRank(); i < s; i++) {
if (!is_reduction[i]) {
auto expr = rewriter.getAffineDimExpr(iterators.size());
iterators.push_back(utils::IteratorType::parallel);
outputExprs.push_back(expr);
filterExprs[i] = expr;
}
}
for (int i = 0, s = filterExprs.size(); i < s; i++) {
if (!filterExprs[i]) {
auto expr = mlir::getAffineDimExpr(iterators.size(), context);
iterators.push_back(utils::IteratorType::reduction);
filterExprs[i] = expr;
}
}
SmallVector<AffineMap> affineMaps{
AffineMap::get(ty.getRank(), 0, filterExprs, context),
AffineMap::get(ty.getRank(), 0, outputExprs, context)};
return linalg::GenericOp::create(
rewriter, zeroAcc.getType(), ValueRange{val}, ValueRange{zeroAcc},
affineMaps, iterators,
[=](OpBuilder &b, Location loc, ValueRange args) {
Value ext = arith::ExtSIOp::create(b, loc, accETy, args[0]);
Value sum = arith::AddIOp::create(b, loc, ext, args[1]);
linalg::YieldOp::create(b, loc, sum);
})
.getResult(0);
}
} // namespace mlir::iree_compiler::GlobalOptimization