| // 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 |