#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Transforms/MemoryUtils.h"
#include "flang/Optimizer/Transforms/Passes.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/DialectConversion.h"
#include "mlir/Transforms/Passes.h"
#include "llvm/ADT/TypeSwitch.h"
namespace fir {
#define GEN_PASS_DEF_MEMORYALLOCATIONOPT
#include "flang/Optimizer/Transforms/Passes.h.inc"
}
#define DEBUG_TYPE "flang-memory-allocation-opt"
static constexpr std::size_t unlimitedArraySize = ~static_cast<std::size_t>(0);
static inline bool
keepStackAllocation(fir::AllocaOp alloca,
const fir::MemoryAllocationOptOptions &options) {
if (options.dynamicArrayOnHeap && alloca.isDynamic())
return false;
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(alloca.getInType())) {
if (!fir::hasDynamicSize(seqTy)) {
std::int64_t numberOfElements = 1;
for (std::int64_t i : seqTy.getShape()) {
numberOfElements *= i;
if (numberOfElements <= 0)
return true;
}
if (static_cast<std::size_t>(numberOfElements) >
options.maxStackArraySize) {
return false;
}
}
}
return true;
}
static mlir::Value genAllocmem(mlir::OpBuilder &builder, fir::AllocaOp alloca,
bool deallocPointsDominateAlloc) {
mlir::Type varTy = alloca.getInType();
auto unpackName = [](std::optional<llvm::StringRef> opt) -> llvm::StringRef {
if (opt)
return *opt;
return {};
};
llvm::StringRef uniqName = unpackName(alloca.getUniqName());
llvm::StringRef bindcName = unpackName(alloca.getBindcName());
auto heap = fir::AllocMemOp::create(builder, alloca.getLoc(), varTy, uniqName,
bindcName, alloca.getTypeparams(),
alloca.getShape());
LLVM_DEBUG(llvm::dbgs() << "memory allocation opt: replaced " << alloca
<< " with " << heap << '\n');
return heap;
}
static void genFreemem(mlir::Location loc, mlir::OpBuilder &builder,
mlir::Value allocmem) {
[[maybe_unused]] auto free = fir::FreeMemOp::create(builder, loc, allocmem);
LLVM_DEBUG(llvm::dbgs() << "memory allocation opt: add free " << free
<< " for " << allocmem << '\n');
}
namespace {
class MemoryAllocationOpt
: public fir::impl::MemoryAllocationOptBase<MemoryAllocationOpt> {
public:
MemoryAllocationOpt() {
options = {dynamicArrayOnHeap, maxStackArraySize};
}
MemoryAllocationOpt(bool dynOnHeap, std::size_t maxStackSize) {
options = {dynOnHeap, maxStackSize};
}
MemoryAllocationOpt(const fir::MemoryAllocationOptOptions &options)
: options{options} {}
inline void useCommandLineOptions() {
if (dynamicArrayOnHeap)
options.dynamicArrayOnHeap = dynamicArrayOnHeap;
if (maxStackArraySize != unlimitedArraySize)
options.maxStackArraySize = maxStackArraySize;
}
void runOnOperation() override {
auto *context = &getContext();
auto func = getOperation();
mlir::RewritePatternSet patterns(context);
mlir::ConversionTarget target(*context);
useCommandLineOptions();
LLVM_DEBUG(llvm::dbgs()
<< "dynamic arrays on heap: " << options.dynamicArrayOnHeap
<< "\nmaximum number of elements of array on stack: "
<< options.maxStackArraySize << '\n');
if (func.empty())
return;
auto tryReplacing = [&](fir::AllocaOp alloca) {
bool res = !keepStackAllocation(alloca, options);
if (res) {
LLVM_DEBUG(llvm::dbgs()
<< "memory allocation opt: found " << alloca << '\n');
}
return res;
};
mlir::IRRewriter rewriter(context);
fir::replaceAllocas(rewriter, func.getOperation(), tryReplacing,
genAllocmem, genFreemem);
}
private:
fir::MemoryAllocationOptOptions options;
};
}