24#include "llvm/ADT/APInt.h"
25#include "llvm/ADT/STLExtras.h"
33#define GEN_PASS_DEF_TOSAGATHERSCATTERHARDENINGPASS
34#include "mlir/Dialect/Tosa/Transforms/Passes.h.inc"
44static FailureOr<int64_t> getIndexUpperBound(
Operation *op) {
46 auto valuesType = dyn_cast<RankedTensorType>(values.
getType());
47 if (!valuesType || valuesType.isDynamicDim(1)) {
48 op->
emitOpError(
"requires a statically known indexed dimension for "
49 "gather/scatter hardening");
54 auto elementType = cast<IntegerType>(indicesType.getElementType());
58 llvm::APInt::getSignedMaxValue(elementType.getWidth()).getSExtValue();
59 return std::min(valuesType.getDimSize(1) - 1, maxRepresentable);
63template <
typename OuterOp,
typename InnerOp>
66 (std::is_same_v<OuterOp, tosa::MinimumOp> &&
67 std::is_same_v<InnerOp, tosa::MaximumOp>) ||
68 (std::is_same_v<OuterOp, tosa::MaximumOp> &&
69 std::is_same_v<InnerOp, tosa::MinimumOp>),
70 "expected a tosa::MinimumOp/tosa::MaximumOp pair in either order");
72 auto outerOp =
indices.getDefiningOp<OuterOp>();
78 for (
unsigned boundOperand = 0; boundOperand < 2; ++boundOperand) {
79 llvm::APInt outerBound;
84 llvm::APInt requiredUpper(outerBound.getBitWidth(),
85 static_cast<uint64_t
>(requiredUpperBound));
91 if (outerBound.isNegative() || outerBound.sgt(requiredUpper))
94 auto matchesInnerBound = [&](
Value value) {
95 llvm::APInt innerBound;
98 return isa<tosa::MinimumOp>(outerOp) ? !innerBound.isNegative()
99 : innerBound.sle(requiredUpper);
105 Value innerResult = outerOp->getOperand(1 - boundOperand);
106 if (matchesInnerBound(innerResult))
111 if (llvm::any_of(innerOp->getOperands(), matchesInnerBound))
119 IntegerType elementType,
int64_t value) {
120 auto type = RankedTensorType::get({1, 1}, elementType);
122 IntegerAttr::get(elementType, llvm::APInt(elementType.getWidth(),
123 static_cast<uint64_t
>(value)));
125 return tosa::ConstOp::create(builder, loc, type, values).getResult();
129template <
typename OpTy>
131 HardenIndexUsePattern(MLIRContext *context,
bool &hardeningFailed)
132 : OpRewritePattern<OpTy>(context), hardeningFailed(hardeningFailed) {}
134 LogicalResult matchAndRewrite(OpTy op,
135 PatternRewriter &rewriter)
const override {
136 FailureOr<int64_t> upperBound = getIndexUpperBound(op.getOperation());
138 hardeningFailed =
true;
140 op,
"indexed dimension does not have a static upper bound");
143 Value
indices = op->getOperand(1);
144 if (isAlreadyHardened<tosa::MinimumOp, tosa::MaximumOp>(
indices,
146 isAlreadyHardened<tosa::MaximumOp, tosa::MinimumOp>(
indices,
150 auto indicesType = cast<ShapedType>(
indices.getType());
151 auto elementType = cast<IntegerType>(indicesType.getElementType());
153 createIndexBoundConstant(rewriter, op.getLoc(), elementType, 0);
154 Value upperBoundValue = createIndexBoundConstant(rewriter, op.getLoc(),
155 elementType, *upperBound);
156 Value nonNegativeIndices =
157 tosa::MaximumOp::create(rewriter, op.getLoc(),
indices.getType(),
160 Value clampedIndices =
161 tosa::MinimumOp::create(rewriter, op.getLoc(),
indices.getType(),
162 nonNegativeIndices, upperBoundValue)
166 op, [&] { op->setOperand(1, clampedIndices); });
171 bool &hardeningFailed;
174struct TosaGatherScatterHardeningPass
176 TosaGatherScatterHardeningPass> {
179 void runOnOperation()
override {
180 bool hardeningFailed =
false;
182 patterns.add<HardenIndexUsePattern<tosa::GatherOp>,
183 HardenIndexUsePattern<tosa::ScatterOp>>(&
getContext(),
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...