[Mlir-commits] [llvm] [mlir] [mlir][spirv] Implement vector type legalization for function signatures (PR #98337)
Jakub Kuderski
llvmlistbot at llvm.org
Tue Jul 16 21:56:34 PDT 2024
================
@@ -813,6 +860,248 @@ void mlir::populateBuiltinFuncToSPIRVPatterns(SPIRVTypeConverter &typeConverter,
patterns.add<FuncOpConversion>(typeConverter, patterns.getContext());
}
+//===----------------------------------------------------------------------===//
+// func::FuncOp Conversion Patterns
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// A pattern for rewriting function signature to convert vector arguments of
+/// functions to be of valid types
+struct FuncOpVectorUnroll final : OpRewritePattern<func::FuncOp> {
+ using OpRewritePattern::OpRewritePattern;
+
+ LogicalResult matchAndRewrite(func::FuncOp funcOp,
+ PatternRewriter &rewriter) const override {
+ FunctionType fnType = funcOp.getFunctionType();
+
+ // TODO: Handle declarations.
+ if (funcOp.isDeclaration()) {
+ LLVM_DEBUG(llvm::dbgs()
+ << fnType << " illegal: declarations are unsupported\n");
+ return failure();
+ }
+
+ // Create a new func op with the original type and copy the function body.
+ auto newFuncOp = rewriter.create<func::FuncOp>(funcOp.getLoc(),
+ funcOp.getName(), fnType);
+ rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
+ newFuncOp.end());
+
+ Location loc = newFuncOp.getBody().getLoc();
+
+ Block &entryBlock = newFuncOp.getBlocks().front();
+ OpBuilder::InsertionGuard guard(rewriter);
+ rewriter.setInsertionPointToStart(&entryBlock);
+
+ OneToNTypeMapping oneToNTypeMapping(fnType.getInputs());
+
+ // For arguments that are of illegal types and require unrolling.
+ // `unrolledInputNums` stores the indices of arguments that result from
+ // unrolling in the new function signature. `newInputNo` is a counter.
+ SmallVector<size_t> unrolledInputNums;
+ size_t newInputNo = 0;
+
+ // For arguments that are of legal types and do not require unrolling.
+ // `tmpOps` stores a mapping from temporary operations that serve as
+ // placeholders for new arguments that will be added later. These operations
+ // will be erased once the entry block's argument list is updated.
+ llvm::SmallDenseMap<Operation *, size_t> tmpOps;
+
+ // This counts the number of new operations created.
+ size_t newOpCount = 0;
+
+ // Enumerate through the arguments.
+ for (auto [origInputNo, origType] : enumerate(fnType.getInputs())) {
+ // Check whether the argument is of vector type.
+ auto origVecType = dyn_cast<VectorType>(origType);
+ if (!origVecType) {
+ // We need a placeholder for the old argument that will be erased later.
+ Value result = rewriter.create<arith::ConstantOp>(
+ loc, origType, rewriter.getZeroAttr(origType));
+ rewriter.replaceAllUsesWith(newFuncOp.getArgument(origInputNo), result);
+ tmpOps.insert({result.getDefiningOp(), newInputNo});
+ oneToNTypeMapping.addInputs(origInputNo, origType);
+ ++newInputNo;
+ ++newOpCount;
+ continue;
+ }
+ // Check whether the vector needs unrolling.
+ auto targetShape = getTargetShape(origVecType);
+ if (!targetShape) {
+ // We need a placeholder for the old argument that will be erased later.
+ Value result = rewriter.create<arith::ConstantOp>(
+ loc, origType, rewriter.getZeroAttr(origType));
+ rewriter.replaceAllUsesWith(newFuncOp.getArgument(origInputNo), result);
+ tmpOps.insert({result.getDefiningOp(), newInputNo});
+ oneToNTypeMapping.addInputs(origInputNo, origType);
+ ++newInputNo;
+ ++newOpCount;
+ continue;
+ }
+ VectorType unrolledType =
+ VectorType::get(*targetShape, origVecType.getElementType());
+ SmallVector<int64_t> originalShape =
+ llvm::to_vector(origVecType.getShape());
+
+ // Prepare the result vector.
+ Value result = rewriter.create<arith::ConstantOp>(
+ loc, origVecType, rewriter.getZeroAttr(origVecType));
+ ++newOpCount;
+ // Prepare the placeholder for the new arguments that will be added later.
+ Value dummy = rewriter.create<arith::ConstantOp>(
+ loc, unrolledType, rewriter.getZeroAttr(unrolledType));
+ ++newOpCount;
+
+ // Create the `vector.insert_strided_slice` ops.
+ SmallVector<int64_t> strides(targetShape->size(), 1);
+ SmallVector<Type> newTypes;
+ for (SmallVector<int64_t> offsets :
+ StaticTileOffsetRange(originalShape, *targetShape)) {
+ result = rewriter.create<vector::InsertStridedSliceOp>(
+ loc, dummy, result, offsets, strides);
+ newTypes.push_back(unrolledType);
+ unrolledInputNums.push_back(newInputNo);
+ ++newInputNo;
+ ++newOpCount;
+ }
+ rewriter.replaceAllUsesWith(newFuncOp.getArgument(origInputNo), result);
+ oneToNTypeMapping.addInputs(origInputNo, newTypes);
+ }
+
+ // Change the function signature.
+ auto convertedTypes = oneToNTypeMapping.getConvertedTypes();
+ auto newFnType = fnType.clone(convertedTypes, fnType.getResults());
+ rewriter.modifyOpInPlace(newFuncOp,
+ [&] { newFuncOp.setFunctionType(newFnType); });
+
+ // Update the arguments in the entry block.
+ entryBlock.eraseArguments(0, fnType.getNumInputs());
+ SmallVector<Location> locs(convertedTypes.size(), newFuncOp.getLoc());
+ entryBlock.addArguments(convertedTypes, locs);
+
+ // Replace the placeholder values with the new arguments. We assume there is
+ // only one block for now.
+ size_t idx = 0;
+ for (auto [count, op] : enumerate(entryBlock.getOperations())) {
+ // We first look for operands that are placeholders for initially legal
+ // arguments.
+ for (auto [operandIdx, operandVal] : llvm::enumerate(op.getOperands())) {
+ Operation *operandOp = operandVal.getDefiningOp();
+ auto it = tmpOps.find(operandOp);
+ if (it != tmpOps.end())
+ rewriter.modifyOpInPlace(&op, [&] {
+ op.setOperand(operandIdx, newFuncOp.getArgument(it->second));
----------------
kuhar wrote:
This emits warnings:
```
[3505/4252] Building CXX object tools/mlir/lib/Dialect/SPIRV/Transforms/CMakeFiles/obj.MLIRSPIRVConversion.dir/SPIRVConversion.cpp.o
/home/jakub/llvm/llvm-project/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp:993:13: warning: captured structured bindings are a C++20 extension [-Wc++20-extensions]
993 | op.setOperand(operandIdx, newFuncOp.getArgument(it->second));
| ^
/home/jakub/llvm/llvm-project/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp:985:23: note: 'op' declared here
985 | for (auto [count, op] : enumerate(entryBlock.getOperations())) {
| ^
/home/jakub/llvm/llvm-project/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp:993:27: warning: captured structured bindings are a C++20 extension [-Wc++20-extensions]
993 | op.setOperand(operandIdx, newFuncOp.getArgument(it->second));
| ^
/home/jakub/llvm/llvm-project/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp:988:18: note: 'operandIdx' declared here
988 | for (auto [operandIdx, operandVal] : llvm::enumerate(op.getOperands())) {
| ^
/home/jakub/llvm/llvm-project/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp:1004:11: warning: captured structured bindings are a C++20 extension [-Wc++20-extensions]
1004 | op.setOperand(0, newFuncOp.getArgument(unrolledInputNo));
| ^
/home/jakub/llvm/llvm-project/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp:985:23: note: 'op' declared here
985 | for (auto [count, op] : enumerate(entryBlock.getOperations())) {
|
```
https://github.com/llvm/llvm-project/pull/98337
More information about the Mlir-commits
mailing list