25#include "llvm/ADT/MapVector.h"
26#include "llvm/ADT/SmallBitVector.h"
27#include "llvm/ADT/SmallSet.h"
28#include "llvm/ADT/TypeSwitch.h"
29#include "llvm/Support/Debug.h"
35#define DEBUG_TYPE "linalg-promotion"
44 std::optional<unsigned> alignment = std::nullopt) {
45 llvm::TypeSize width = layout.
getTypeSize(elementType);
46 assert(!width.isScalable() &&
"cannot allocate buffer for a scalable vector");
48 IntegerAttr alignmentAttr;
49 if (alignment.has_value())
50 alignmentAttr =
b.getI64IntegerAttr(alignment.value());
53 if (
options.memorySpace.has_value())
54 memorySpaceAttr = *
options.memorySpace;
58 auto staticBufferType = MemRefType::get(width.getFixedValue() * cst.value(),
63 return memref::AllocaOp::create(
b, staticBufferType,
ValueRange{},
66 return memref::AllocOp::create(
b, staticBufferType,
ValueRange{},
71 auto dynamicBufferType =
72 MemRefType::get(ShapedType::kDynamic,
b.getIntegerType(8));
75 Value mul =
b.createOrFold<arith::MulIOp>(
78 return memref::AllocaOp::create(
b, dynamicBufferType,
mul, alignmentAttr);
79 return memref::AllocOp::create(
b, dynamicBufferType,
mul, alignmentAttr);
89 std::optional<unsigned> alignment,
DataLayout &layout) {
90 ShapedType viewType = subView.getType();
96 if (
options.memorySpace.has_value())
97 memorySpaceAttr = *
options.memorySpace;
99 Value allocSize = one;
100 for (
const auto &size : llvm::enumerate(boundingSubViewSize))
101 allocSize =
b.createOrFold<arith::MulIOp>(allocSize, size.value());
105 ShapedType::kDynamic);
107 auto viewMemRefType = MemRefType::get(dynSizes, viewType.getElementType());
110 Value view =
b.createOrFold<memref::ViewOp>(viewMemRefType, buffer, zero,
111 boundingSubViewSize);
122 auto viewOp = cast<memref::ViewOp>(fullLocalView.
getDefiningOp());
123 memref::DeallocOp::create(
b, viewOp.getSource().getLoc(),
135struct LinalgOpInstancePromotionOptions {
136 LinalgOpInstancePromotionOptions(LinalgOp op,
137 const LinalgPromotionOptions &
options);
141 llvm::SmallSet<int64_t, 4> operandsNumbersToCopyIn;
146 bool useOriginalSubviewSize;
156 std::optional<unsigned> alignment;
160LinalgOpInstancePromotionOptions::LinalgOpInstancePromotionOptions(
162 : subViews(), alignment(
options.alignment) {
163 assert(linalgOp.hasPureBufferSemantics() &&
164 "revisit usage of shaped operand");
165 auto vUseFullTileBuffers =
166 options.useFullTileBuffers.value_or(llvm::SmallBitVector());
167 vUseFullTileBuffers.resize(linalgOp->getNumOperands(),
168 options.useFullTileBuffersDefault);
169 useOriginalSubviewSize =
options.useOriginalSubviewSize;
171 for (
OpOperand &opOperand : linalgOp->getOpOperands()) {
172 int64_t operandNumber = opOperand.getOperandNumber();
173 if (
options.operandsToPromote &&
174 !
options.operandsToPromote->count(operandNumber))
176 Operation *op = opOperand.get().getDefiningOp();
177 if (
auto sv = dyn_cast_or_null<memref::SubViewOp>(op)) {
178 subViews[operandNumber] = sv;
181 if (!isa<linalg::GenericOp>(linalgOp) ||
182 linalgOp.payloadUsesValueFromOperand(&opOperand))
183 operandsNumbersToCopyIn.insert(operandNumber);
184 useFullTileBuffers[sv] = vUseFullTileBuffers[operandNumber];
189 allocationFn = *
options.allocationFn;
191 allocationFn = [&](
OpBuilder &
b, memref::SubViewOp subViewOp,
195 boundingSubViewSize, alignment, layout);
200 deallocationFn = *
options.deallocationFn;
210 Value dst) -> LogicalResult {
211 linalg::CopyOp::create(
b, loc, src, dst);
214 copyInFn = (
options.copyInFn ? *(
options.copyInFn) : defaultCopyCallBack);
215 copyOutFn = (
options.copyOutFn ? *(
options.copyOutFn) : defaultCopyCallBack);
239 auto viewType = subView.getType();
240 auto rank = viewType.getRank();
243 fullSizes.reserve(rank);
244 partialSizes.reserve(rank);
245 llvm::SmallBitVector droppedDims = subView.getDroppedDims();
247 for (
const auto &en : llvm::enumerate(subView.getOrCreateRanges(
b, loc))) {
248 if (droppedDims[en.index()])
250 auto rangeValue = en.value();
253 LLVM_DEBUG(llvm::dbgs() <<
"Extract tightest: " << rangeValue.size <<
"\n");
255 if (llvm::isa_and_present<Attribute>(rangeValue.size) ||
256 useOriginalSubviewSize) {
259 FailureOr<int64_t> upperBound =
263 size = failed(upperBound)
267 LLVM_DEBUG(llvm::dbgs() <<
"Extracted tightest: " << size <<
"\n");
268 fullSizes.push_back(size);
269 partialSizes.push_back(
270 b.createOrFold<memref::DimOp>(loc, subView, resultDimIdx++));
274 std::optional<Value> fullLocalView =
275 allocationFn(
b, subView, fullSizes, layout);
280 auto partialLocalView =
b.createOrFold<memref::SubViewOp>(
281 loc, *fullLocalView, zeros, partialSizes, ones);
285static FailureOr<MapVector<int64_t, PromotionInfo>>
293 for (
auto v :
options.subViews) {
294 memref::SubViewOp subView =
295 cast<memref::SubViewOp>(v.second.getDefiningOp());
297 b,
b.getLoc(), subView,
options.useOriginalSubviewSize,
299 if (failed(promotionInfo))
301 promotionInfoMap[v.first] = *promotionInfo;
304 if (!
options.useFullTileBuffers[v.second])
306 Type subviewEltType = subView.getType().getElementType();
309 .Case([&](FloatType t) {
310 return arith::ConstantOp::create(
b, FloatAttr::get(t, 0.0));
312 .Case([&](IntegerType t) {
313 return arith::ConstantOp::create(
b, IntegerAttr::get(t, 0));
315 .Case([&](ComplexType t) {
317 if (
auto et = dyn_cast<FloatType>(t.getElementType()))
318 tmp = arith::ConstantOp::create(
b, FloatAttr::get(et, 0.0));
319 else if (
auto et = cast<IntegerType>(t.getElementType()))
320 tmp = arith::ConstantOp::create(
b, IntegerAttr::get(et, 0));
321 return complex::CreateOp::create(
b, t, tmp, tmp);
326 linalg::FillOp::create(
b, fillVal, promotionInfo->fullLocalView);
330 for (
auto v :
options.subViews) {
331 auto *info = promotionInfoMap.find(v.first);
332 if (info == promotionInfoMap.end())
334 if (
options.operandsNumbersToCopyIn.count(v.first) == 0)
337 b, cast<memref::SubViewOp>(v.second.getDefiningOp()),
338 info->second.partialLocalView)))
341 return promotionInfoMap;
344static FailureOr<LinalgOp>
347 assert(op.hasPureBufferSemantics() &&
348 "expected linalg op with buffer semantics");
352 if (failed(promotedBuffersAndViews) ||
353 promotedBuffersAndViews->size() !=
options.subViews.size())
360 opViews.reserve(op->getNumOperands());
362 writebackViews.reserve(promotedBuffersAndViews->size());
363 for (
OpOperand &opOperand : op->getOpOperands()) {
364 int64_t operandNumber = opOperand.getOperandNumber();
365 if (
options.subViews.count(operandNumber) != 0) {
366 if (
options.useFullTileBuffers[opOperand.get()])
368 (*promotedBuffersAndViews)[operandNumber].fullLocalView);
371 (*promotedBuffersAndViews)[operandNumber].partialLocalView);
372 if (operandNumber >= op.getNumDpsInputs())
373 writebackViews.emplace_back(std::make_pair(
375 (*promotedBuffersAndViews)[operandNumber].partialLocalView));
377 opViews.push_back(opOperand.get());
380 op->setOperands(0, opViews.size(), opViews);
383 b.setInsertionPointAfter(op);
385 for (
auto viewAndPartialLocalView : writebackViews) {
386 if (failed(
options.copyOutFn(
b, viewAndPartialLocalView.second,
387 viewAndPartialLocalView.first)))
392 for (
const auto &pi : *promotedBuffersAndViews)
393 (
void)
options.deallocationFn(
b, pi.second.fullLocalView);
400 LinalgOp linalgOp = dyn_cast<LinalgOp>(op);
402 if (!linalgOp || !linalgOp.hasPureBufferSemantics())
405 for (
OpOperand &opOperand : linalgOp->getOpOperands()) {
407 isa_and_nonnull<memref::SubViewOp>(opOperand.get().getDefiningOp());
409 if (!
options.operandsToPromote ||
410 options.operandsToPromote->count(opOperand.getOperandNumber()))
422 LinalgOpInstancePromotionOptions linalgOptions(linalgOp,
options);
436 gpu::AddressSpace addressSpace) {
439 func::FuncOp funcOp = subview->getParentOfType<func::FuncOp>();
446 for (
Value bound : sizeBounds) {
450 shape.push_back(value.getSExtValue());
454 auto type = MemRefType::get(
455 shape, subview.getType().getElementType(), MemRefLayoutAttrInterface{},
456 gpu::AddressSpaceAttr::get(builder.
getContext(), addressSpace));
458 if (addressSpace == gpu::GPUDialect::getWorkgroupAddressSpace()) {
459 buffer = memref::AllocOp::create(builder, funcOp.getLoc(), type);
460 }
else if (addressSpace == gpu::GPUDialect::getPrivateAddressSpace()) {
461 buffer = memref::AllocaOp::create(builder, funcOp.getLoc(), type);
473 builder, subview, sizeBounds,
474 gpu::GPUDialect::getWorkgroupAddressSpace());
487 gpu::BarrierOp::create(
b, src.
getLoc());
489 gpu::BarrierOp::create(
b, copyOp->
getLoc());
498 builder, subview, sizeBounds, gpu::GPUDialect::getPrivateAddressSpace());
504 memref::CopyOp::create(
b, src.
getLoc(), src, dst);
static llvm::ManagedStatic< PassManagerOptions > options
Attributes are known-constant values of operations.
MLIRContext * getContext() const
The main mechanism for performing data layout queries.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
llvm::TypeSize getTypeSize(Type t) const
Returns the size of the given type in the current scope.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This is a builder type that keeps local references to arguments.
Builder & setMemorySpace(Attribute newMemorySpace)
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
This class represents an operand of an operation.
Operation is the basic unit of execution within MLIR.
Location getLoc()
The source location the operation was defined or derived from.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
static FailureOr< int64_t > computeConstantBound(presburger::BoundType type, const Variable &var, const StopConditionFn &stopCondition=nullptr, ValueBoundsOptions options={})
Compute a constant bound for the given variable.
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...
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
std::optional< Value > allocateWorkgroupMemory(OpBuilder &builder, memref::SubViewOp subview, ArrayRef< Value > sizeBounds, DataLayout &)
Allocate the subview in the GPU workgroup memory.
LogicalResult deallocateGPUPrivateMemory(OpBuilder &, Value)
In case of GPU private memory there is no need to deallocate since the memory is freed when going out...
FailureOr< PromotionInfo > promoteSubviewAsNewBuffer(OpBuilder &b, Location loc, memref::SubViewOp subView, bool useOriginalSubviewSize, const AllocBufferCallbackFn &allocationFn, DataLayout &layout)
std::optional< Value > allocateGPUPrivateMemory(OpBuilder &builder, memref::SubViewOp subview, ArrayRef< Value > sizeBounds, DataLayout &)
Allocate the subview in the GPU private memory.
LogicalResult copyToWorkgroupMemory(OpBuilder &b, Value src, Value dst)
Create Memref copy operations and add gpu barrier guards before and after the copy operation to ensur...
LogicalResult promoteSubviewsPrecondition(Operation *op, LinalgPromotionOptions options)
Promote memref.subviews feeding linalg-on-buffers operations.
LogicalResult copyToGPUPrivateMemory(OpBuilder &b, Value src, Value dst)
Normal copy to between src and dst.
std::function< std::optional< Value >( OpBuilder &b, memref::SubViewOp subView, ArrayRef< Value > boundingSubViewSize, DataLayout &layout)> AllocBufferCallbackFn
Callback function type used to perform the allocation for the promoted subView.
std::function< LogicalResult(OpBuilder &b, Value buffer)> DeallocBufferCallbackFn
Callback function type used to deallocate the buffers used to hold the promoted subview.
FailureOr< LinalgOp > promoteSubViews(OpBuilder &b, LinalgOp op, const LinalgPromotionOptions &options)
Promote the subViews into a new buffer allocated at the insertion point b.
LogicalResult deallocateWorkgroupMemory(OpBuilder &, Value)
In case of GPU group memory there is no need to deallocate.
std::function< LogicalResult(OpBuilder &b, Value src, Value dst)> CopyCallbackFn
Callback function type used to insert copy from original subview to subview of the promoted region fo...
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...
llvm::MapVector< KeyT, ValueT, MapType, VectorType, N > MapVector
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Options that control value bound computation.