/// Alloc a new buffer of `size`. If `dynamicBuffers` is true allocate exactly
/// the size needed, otherwise try to allocate a static bounding box.
-static Value allocBuffer(Type elementType, Value size, bool dynamicBuffers,
+static Value allocBuffer(const LinalgPromotionOptions &options,
+ Type elementType, Value size, bool dynamicBuffers,
OperationFolder *folder,
Optional<unsigned> alignment = None) {
auto *ctx = size.getContext();
IntegerAttr::get(IntegerType::get(64, ctx), alignment.getValue());
if (!dynamicBuffers)
if (auto cst = size.getDefiningOp<ConstantIndexOp>())
- return std_alloc(
- MemRefType::get(width * cst.getValue(), IntegerType::get(8, ctx)),
- ValueRange{}, alignment_attr);
+ return options.useAlloca
+ ? std_alloca(MemRefType::get(width * cst.getValue(),
+ IntegerType::get(8, ctx)),
+ ValueRange{}, alignment_attr)
+ .value
+ : std_alloc(MemRefType::get(width * cst.getValue(),
+ IntegerType::get(8, ctx)),
+ ValueRange{}, alignment_attr)
+ .value;
Value mul =
folded_std_muli(folder, folded_std_constant_index(folder, width), size);
- return std_alloc(MemRefType::get(-1, IntegerType::get(8, ctx)), mul,
- alignment_attr);
+ return options.useAlloca
+ ? std_alloca(MemRefType::get(-1, IntegerType::get(8, ctx)), mul,
+ alignment_attr)
+ .value
+ : std_alloc(MemRefType::get(-1, IntegerType::get(8, ctx)), mul,
+ alignment_attr)
+ .value;
}
/// Default allocation callback function. This allocates a promoted buffer when
/// no call back to do so is provided. The default is to allocate a
/// memref<..xi8> and return a view to get a memref type of shape
/// boundingSubViewSize.
-static Optional<Value>
-allocBufferCallBack(OpBuilder &builder, SubViewOp subView,
- ArrayRef<Value> boundingSubViewSize, bool dynamicBuffers,
- Optional<unsigned> alignment, OperationFolder *folder) {
+static Optional<Value> defaultAllocBufferCallBack(
+ const LinalgPromotionOptions &options, OpBuilder &builder,
+ SubViewOp subView, ArrayRef<Value> boundingSubViewSize, bool dynamicBuffers,
+ Optional<unsigned> alignment, OperationFolder *folder) {
ShapedType viewType = subView.getType();
int64_t rank = viewType.getRank();
(void)rank;
Value allocSize = one;
for (auto size : llvm::enumerate(boundingSubViewSize))
allocSize = folded_std_muli(folder, allocSize, size.value());
- Value buffer = allocBuffer(viewType.getElementType(), allocSize,
+ Value buffer = allocBuffer(options, viewType.getElementType(), allocSize,
dynamicBuffers, folder, alignment);
SmallVector<int64_t, 4> dynSizes(boundingSubViewSize.size(),
ShapedType::kDynamicSize);
/// Default implementation of deallocation of the buffer use for promotion. It
/// expects to get the same value that the default allocation method returned,
/// i.e. result of a ViewOp.
-static LogicalResult deallocCallBack(OpBuilder &b, Value fullLocalView) {
+static LogicalResult
+defaultDeallocBufferCallBack(const LinalgPromotionOptions &options,
+ OpBuilder &b, Value fullLocalView) {
auto viewOp = fullLocalView.getDefiningOp<ViewOp>();
assert(viewOp && "expected full local view to be a ViewOp");
- std_dealloc(viewOp.source());
+ if (!options.useAlloca)
+ std_dealloc(viewOp.source());
return success();
}
: [&](OpBuilder &builder, SubViewOp subViewOp,
ArrayRef<Value> boundingSubViewSize,
OperationFolder *folder) -> Optional<Value> {
- return allocBufferCallBack(builder, subViewOp, boundingSubViewSize,
- dynamicBuffers, alignment, folder);
+ return defaultAllocBufferCallBack(options, builder, subViewOp,
+ boundingSubViewSize, dynamicBuffers,
+ alignment, folder);
});
deallocationFn =
- (options.deallocationFn ? *(options.deallocationFn) : deallocCallBack);
+ (options.deallocationFn
+ ? *(options.deallocationFn)
+ : [&](OpBuilder &b, Value buffer) {
+ return defaultDeallocBufferCallBack(options, b, buffer);
+ });
auto defaultCopyCallBack = [&](OpBuilder &builder, Value src,
Value dst) -> LogicalResult {
linalg_copy(src, dst);
}
// 4. Dealloc all local buffers.
- for (const auto &pi : *promotedBuffersAndViews) {
+ for (const auto &pi : *promotedBuffersAndViews)
options.deallocationFn(b, pi.second.fullLocalView);
- }
return op;
}
namespace {
struct LinalgPromotionPass : public LinalgPromotionBase<LinalgPromotionPass> {
LinalgPromotionPass() = default;
- LinalgPromotionPass(bool dynamicBuffers) {
+ LinalgPromotionPass(bool dynamicBuffers, bool useAlloca) {
this->dynamicBuffers = dynamicBuffers;
+ this->useAlloca = useAlloca;
}
void runOnFunction() override {
OperationFolder folder(&getContext());
getFunction().walk([this, &folder](LinalgOp op) {
- auto options = LinalgPromotionOptions().setDynamicBuffers(dynamicBuffers);
+ auto options = LinalgPromotionOptions()
+ .setDynamicBuffers(dynamicBuffers)
+ .setUseAlloca(useAlloca);
if (failed(promoteSubviewsPrecondition(op, options)))
return;
LLVM_DEBUG(llvm::dbgs() << "Promote: " << *(op.getOperation()) << "\n");
// TODO: support more transformation options in the pass.
std::unique_ptr<OperationPass<FuncOp>>
-mlir::createLinalgPromotionPass(bool dynamicBuffers) {
- return std::make_unique<LinalgPromotionPass>(dynamicBuffers);
+mlir::createLinalgPromotionPass(bool dynamicBuffers, bool useAlloca) {
+ return std::make_unique<LinalgPromotionPass>(dynamicBuffers, useAlloca);
}
std::unique_ptr<OperationPass<FuncOp>> mlir::createLinalgPromotionPass() {
return std::make_unique<LinalgPromotionPass>();
// RUN: mlir-opt %s -linalg-promote-subviews | FileCheck %s
// RUN: mlir-opt %s -linalg-promote-subviews="test-promote-dynamic" | FileCheck %s --check-prefix=DYNAMIC
+// RUN: mlir-opt %s -linalg-promote-subviews="test-use-alloca" | FileCheck %s --check-prefix=ALLOCA
#map1 = affine_map<(d0) -> (d0 + 2)>
#map2 = affine_map<(d0) -> (d0 + 4)>
// CHECK: %[[vC:.*]] = subview {{.*}} : memref<?x?xf32>
///
// CHECK: %[[tmpA:.*]] = alloc() : memref<32xi8>
+// ALLOCA: %[[tmpA:.*]] = alloca() : memref<32xi8>
// CHECK: %[[fullA:.*]] = std.view %[[tmpA]][{{.*}}][{{.*}}] : memref<32xi8> to memref<?x?xf32>
// DYNAMIC: std.view %{{.*}}[{{.*}}][{{.*}}] : memref<?xi8> to memref<?x?xf32>
// CHECK: %[[partialA:.*]] = subview %[[fullA]]{{.*}} : memref<?x?xf32> to memref<?x?xf32, #[[$strided2D_dynamic]]>
///
// CHECK: %[[tmpB:.*]] = alloc() : memref<48xi8>
+// ALLOCA: %[[tmpB:.*]] = alloca() : memref<48xi8>
// CHECK: %[[fullB:.*]] = std.view %[[tmpB]][{{.*}}][{{.*}}] : memref<48xi8> to memref<?x?xf32>
// DYNAMIC: std.view %{{.*}}[{{.*}}][{{.*}}] : memref<?xi8> to memref<?x?xf32>
// CHECK: %[[partialB:.*]] = subview %[[fullB]]{{.*}} : memref<?x?xf32> to memref<?x?xf32, #[[$strided2D_dynamic]]>
///
// CHECK: %[[tmpC:.*]] = alloc() : memref<24xi8>
+// ALLOCA: %[[tmpC:.*]] = alloca() : memref<24xi8>
// CHECK: %[[fullC:.*]] = std.view %[[tmpC]][{{.*}}][{{.*}}] : memref<24xi8> to memref<?x?xf32>
// DYNAMIC: std.view %{{.*}}[{{.*}}][{{.*}}] : memref<?xi8> to memref<?x?xf32>
// CHECK: %[[partialC:.*]] = subview %[[fullC]]{{.*}} : memref<?x?xf32> to memref<?x?xf32, #[[$strided2D_dynamic]]>
// CHECK: dealloc %[[tmpA]] : memref<32xi8>
// CHECK: dealloc %[[tmpB]] : memref<48xi8>
// CHECK: dealloc %[[tmpC]] : memref<24xi8>
+// ALLOCA-NOT: dealloc %[[tmpA]] : memref<32xi8>
+// ALLOCA-NOT: dealloc %[[tmpB]] : memref<48xi8>
+// ALLOCA-NOT: dealloc %[[tmpC]] : memref<24xi8>
// -----