16#include "llvm/ADT/DenseMap.h"
20#define GEN_PASS_DEF_SCALARIZESINGLEELEMENTTENSORRETURNPASS
21#include "mlir/Dialect/Tensor/Transforms/Passes.h.inc"
32enum class ScalarizationState {
50struct ScalarizableFunctionInfo {
51 RankedTensorType tensorType;
52 SmallVector<func::ReturnOp> returnOps;
58static FailureOr<ScalarizableFunctionInfo>
59getScalarizableFunctionInfoIfEligible(func::FuncOp
func) {
60 if (
func.isDeclaration() || !
func.isPrivate())
63 FunctionType functionType =
func.getFunctionType();
64 if (functionType.getNumResults() != 1)
67 auto tensorType = dyn_cast<RankedTensorType>(functionType.getResult(0));
68 if (!tensorType || !tensorType.hasStaticShape() ||
69 tensorType.getNumElements() != 1)
72 ScalarizableFunctionInfo sfi{tensorType, {}};
74 auto returnOp = dyn_cast<func::ReturnOp>(block.getTerminator());
76 sfi.returnOps.push_back(returnOp);
81 if (sfi.returnOps.empty())
86struct ScalarizationAnalysis {
87 explicit ScalarizationAnalysis(SymbolUserMap &userMap) : userMap(userMap) {}
89 SymbolUserMap &userMap;
95 SmallVector<func::FuncOp> rewriteOrder;
100static ScalarizationState
101computeScalarizationState(func::FuncOp
func, ScalarizationAnalysis &analysis) {
103 analysis.states.try_emplace(
func, ScalarizationState::unknown);
106 if (it->second == ScalarizationState::visiting)
107 return ScalarizationState::blocked;
117 auto setBlocked = [&] {
118 analysis.states[
func] = ScalarizationState::blocked;
119 return ScalarizationState::blocked;
121 auto setRewritable = [&] {
122 analysis.states[
func] = ScalarizationState::rewritable;
123 analysis.rewriteOrder.push_back(
func);
124 return ScalarizationState::rewritable;
127 analysis.states[
func] = ScalarizationState::visiting;
130 for (
Operation *user : analysis.userMap.getUsers(
func.getOperation())) {
131 auto directCall = dyn_cast<func::CallOp>(user);
136 directCallUsers.push_back(directCall);
138 func::FuncOp caller = directCall->getParentOfType<func::FuncOp>();
142 if (!caller || !analysis.candidateInfos.contains(caller))
145 if (computeScalarizationState(caller, analysis) !=
146 ScalarizationState::rewritable)
150 analysis.callUsers.try_emplace(
func, std::move(directCallUsers));
151 return setRewritable();
155static void computeScalarizationAnalysis(ModuleOp module,
156 ScalarizationAnalysis &analysis) {
158 for (func::FuncOp
func : module.getOps<func::FuncOp>()) {
159 FailureOr<ScalarizableFunctionInfo> sfi =
160 getScalarizableFunctionInfoIfEligible(
func);
162 analysis.candidateInfos.try_emplace(
func, std::move(*sfi));
165 for (func::FuncOp
func : module.getOps<func::FuncOp>())
166 if (analysis.candidateInfos.contains(
func))
167 (
void)computeScalarizationState(
func, analysis);
172static void rewriteScalarizableFunction(func::FuncOp
func,
173 const ScalarizableFunctionInfo &sfi,
178 RankedTensorType tensorType = sfi.tensorType;
180 if (tensorType.getRank() != 0) {
183 zeroIndices.assign(tensorType.getRank(), zero);
186 Type scalarType = tensorType.getElementType();
187 for (func::ReturnOp funcReturn : sfi.returnOps) {
188 assert(funcReturn.getNumOperands() == 1 &&
189 "func.return must have exactly one operand");
190 assert(funcReturn.getOperand(0).getType() == tensorType &&
191 "func.return operand type must match the function result type");
194 funcReturn.getLoc(), funcReturn.getOperand(0), zeroIndices);
198 FunctionType functionType =
func.getFunctionType();
203 func.setType(FunctionType::get(
func.getContext(), functionType.getInputs(),
206 for (func::CallOp directCall : directCalls) {
208 func::CallOp newDirectCall = func::CallOp::create(
209 rewriter, directCall.getLoc(),
func, directCall.getOperands());
210 newDirectCall->setDiscardableAttrs(
211 directCall->getDiscardableAttrDictionary());
213 if (!directCall.getResult(0).use_empty()) {
214 Value wrappedResult = tensor::FromElementsOp::create(
215 rewriter, directCall.getLoc(), tensorType,
217 rewriter.
replaceOp(directCall, wrappedResult);
263ScalarizeSingleElementTensorReturns(ModuleOp module,
RewriterBase &rewriter) {
273 ScalarizationAnalysis
analysis(userMap);
274 computeScalarizationAnalysis(module, analysis);
276 for (func::FuncOp
func : llvm::reverse(
analysis.rewriteOrder)) {
277 const ScalarizableFunctionInfo &sfi =
280 rewriteScalarizableFunction(
func, sfi, directCalls, rewriter);
286struct ScalarizeSingleElementTensorReturnPass
288 ScalarizeSingleElementTensorReturnPass> {
291 void runOnOperation()
override {
293 if (
failed(ScalarizeSingleElementTensorReturns(getOperation(), rewriter)))
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
Block represents an ordered list of Operations.
RAII guard to reset the insertion point of the builder when destroyed.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Operation is the basic unit of execution within MLIR.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents a collection of SymbolTables.
This class represents a map of symbols to users, and provides efficient implementations of symbol que...
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap