blob: 674fd72a23e25c033fbf2d74b8a8d66626f9f8af [file]
// Copyright 2021 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/compiler/Codegen/LLVMCPU/Passes.h"
#include "llvm/Support/CommandLine.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Vector/IR/ScalableValueBoundsConstraintSet.h"
#include "mlir/Interfaces/ValueBoundsOpInterface.h"
#include "mlir/Pass/Pass.h"
namespace mlir::iree_compiler {
#define GEN_PASS_DEF_LLVMCPUCHECKIRBEFORELLVMCONVERSIONPASS
#include "iree/compiler/Codegen/LLVMCPU/Passes.h.inc"
static llvm::cl::opt<int> clMaxAllocationSizeInBytes(
"iree-llvmcpu-stack-allocation-limit",
llvm::cl::desc("maximum allowed stack allocation size in bytes"),
llvm::cl::init(32768));
static llvm::cl::opt<unsigned> clAssumedVscaleValue(
"iree-llvmcpu-stack-allocation-assumed-vscale",
llvm::cl::desc(
"assumed value of vscale when checking (scalable) stack allocations"),
llvm::cl::init(1));
namespace {
struct LLVMCPUCheckIRBeforeLLVMConversionPass
: impl::LLVMCPUCheckIRBeforeLLVMConversionPassBase<
LLVMCPUCheckIRBeforeLLVMConversionPass> {
using impl::LLVMCPUCheckIRBeforeLLVMConversionPassBase<
LLVMCPUCheckIRBeforeLLVMConversionPass>::
LLVMCPUCheckIRBeforeLLVMConversionPassBase;
void runOnOperation() override;
};
} // namespace
/// Returns success if the cummulative stack allocation size is less than the
/// limit set by clMaxAllocationSizeInBytes.
static LogicalResult
checkStackAllocationSize(mlir::FunctionOpInterface funcOp) {
if (funcOp.getFunctionBody().empty())
return success();
SmallVector<memref::AllocaOp> allocaOps;
funcOp.walk(
[&](memref::AllocaOp allocaOp) { allocaOps.push_back(allocaOp); });
if (allocaOps.empty()) {
return success();
}
int cumSize = 0;
const unsigned assumedVscale = clAssumedVscaleValue;
for (auto allocaOp : allocaOps) {
if (allocaOp->getBlock() != &funcOp.getFunctionBody().front()) {
return allocaOp->emitOpError(
"all stack allocations need to be hoisted to the entry block of the "
"function");
}
int allocaSize = 1;
auto allocaType = llvm::cast<ShapedType>(allocaOp.getType());
for (auto dimSize : allocaType.getShape()) {
if (ShapedType::isDynamic(dimSize))
continue;
allocaSize *= dimSize;
}
for (auto operand : allocaOp.getDynamicSizes()) {
// Assume vscale is `clAssumedVscaleValue` for determining if the alloca
// is within the stack limit. This should always resolve to a constant
// bound. Note: This may be an underestimate if the runtime larger than
// `clAssumedVscaleValue`, but should still catch unreasonable allocatons
// (which will have large static factors).
auto ub = vector::ScalableValueBoundsConstraintSet::computeScalableBound(
operand, /*dim=*/std::nullopt,
/*vscaleMin=*/assumedVscale,
/*vscaleMax=*/assumedVscale, presburger::BoundType::UB);
if (succeeded(ub)) {
allocaSize *= ub->getSize()->baseSize;
continue;
}
return allocaOp.emitOpError("expected no unbounded stack allocations");
}
allocaSize *= allocaType.getElementType().getIntOrFloatBitWidth();
if (allocaOp.getAlignment()) {
int64_t alignmentInBits = *allocaOp.getAlignment() * 8;
allocaSize =
(llvm::divideCeil(allocaSize, alignmentInBits) * alignmentInBits);
}
cumSize += allocaSize / 8;
}
if (cumSize > clMaxAllocationSizeInBytes) {
return funcOp.emitOpError("exceeded stack allocation limit of ")
<< clMaxAllocationSizeInBytes.getValue()
<< " bytes for function. Got " << cumSize << " bytes";
}
return success();
}
void LLVMCPUCheckIRBeforeLLVMConversionPass::runOnOperation() {
if (!failOnOutOfBounds) {
return;
}
auto funcOp = getOperation();
if (failed(checkStackAllocationSize(funcOp))) {
return signalPassFailure();
}
}
} // namespace mlir::iree_compiler