Supported before: `fir.store`, `fir.box_addr`, and `fir.call` only for PointerAllocate/PointerDeallocate. Added now: fir.call support for PointerAllocateSource, PointerDeallocatePolymorphic, AllocatableAllocate, AllocatableAllocateSource, AllocatableDeallocate, AllocatableDeallocatePolymorphic (found in flang/include/flang/Runtime/allocatable.h).
240 lines
9.4 KiB
C++
240 lines
9.4 KiB
C++
//===- ACCDeclareActionConversion.cpp -------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Implements the allocation and deallocation semantics for allocatables and
|
|
// pointers in declare directives. OpenACC 3.4, Section 2.13.2: in Fortran, if
|
|
// a variable in the declare var-list has the allocatable or pointer attribute,
|
|
// then for a non-shared memory device, an allocate (or intrinsic assignment
|
|
// that allocates) allocates in both local and device memory and sets the
|
|
// dynamic reference counter to one; a deallocate (or assignment that
|
|
// deallocates) deallocates from both and sets the counter to zero.
|
|
//
|
|
// How this pass works:
|
|
// - Lowering generates recipe functions that hold the recipe for creating the
|
|
// device copy (using acc dialect operations, e.g. acc.create).
|
|
// - Lowering also attaches an attribute to the operations that allocate or
|
|
// deallocate the object.
|
|
// - This pass finds operations with that attribute and inserts calls to the
|
|
// corresponding recipe.
|
|
//
|
|
// Example:
|
|
// module mm
|
|
// real, allocatable :: arr(:)
|
|
// !$acc declare create(arr)
|
|
// contains
|
|
// subroutine sub()
|
|
// allocate(arr(100))
|
|
// end subroutine sub
|
|
// end module mm
|
|
//
|
|
// Relevant IR before this pass (recipe function and store with attribute):
|
|
// func.func private @_QMmmEarr_acc_declare_update_desc_post_alloc(...) {
|
|
// ... // acc ops to create/register device copy
|
|
// return
|
|
// }
|
|
// func.func @_QMmmPsub() {
|
|
// ...
|
|
// fir.store %box to %desc {acc.declare_action = #acc.declare_action<
|
|
// postAlloc = @_QMmmEarr_acc_declare_update_desc_post_alloc>} ...
|
|
// }
|
|
//
|
|
// After this pass (call to recipe inserted after the store):
|
|
// func.func @_QMmmPsub() {
|
|
// ...
|
|
// fir.store %box to %desc ...
|
|
// fir.call @_QMmmEarr_acc_declare_update_desc_post_alloc()
|
|
// }
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "flang/Optimizer/Dialect/FIROps.h"
|
|
#include "flang/Optimizer/Dialect/FIRType.h"
|
|
#include "flang/Optimizer/OpenACC/Passes.h"
|
|
#include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
|
|
#include "flang/Optimizer/Support/LazySymbolTable.h"
|
|
#include "flang/Runtime/entry-names.h"
|
|
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
|
#include "mlir/Dialect/OpenACC/OpenACC.h"
|
|
#include "mlir/IR/Builders.h"
|
|
#include "mlir/IR/Operation.h"
|
|
#include "mlir/IR/Value.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include <cassert>
|
|
|
|
#define DEBUG_TYPE "acc-declare-action-conversion"
|
|
|
|
namespace fir {
|
|
namespace acc {
|
|
#define GEN_PASS_DEF_ACCDECLAREACTIONCONVERSION
|
|
#include "flang/Optimizer/OpenACC/Passes.h.inc"
|
|
} // namespace acc
|
|
} // namespace fir
|
|
|
|
using namespace mlir;
|
|
|
|
namespace {
|
|
|
|
// Fortran runtime symbol names for pointer allocate/deallocate.
|
|
static constexpr llvm::StringLiteral pointerAllocateName =
|
|
RTNAME_STRING(PointerAllocate);
|
|
static constexpr llvm::StringLiteral pointerAllocateSourceName =
|
|
RTNAME_STRING(PointerAllocateSource);
|
|
static constexpr llvm::StringLiteral pointerDeallocateName =
|
|
RTNAME_STRING(PointerDeallocate);
|
|
static constexpr llvm::StringLiteral pointerDeallocatePolymorphicName =
|
|
RTNAME_STRING(PointerDeallocatePolymorphic);
|
|
static constexpr llvm::StringLiteral allocatableAllocateName =
|
|
RTNAME_STRING(AllocatableAllocate);
|
|
static constexpr llvm::StringLiteral allocatableAllocateSourceName =
|
|
RTNAME_STRING(AllocatableAllocateSource);
|
|
static constexpr llvm::StringLiteral allocatableDeallocateName =
|
|
RTNAME_STRING(AllocatableDeallocate);
|
|
static constexpr llvm::StringLiteral allocatableDeallocatePolymorphicName =
|
|
RTNAME_STRING(AllocatableDeallocatePolymorphic);
|
|
|
|
static bool isSupportedDeclareActionRuntime(llvm::StringRef funcName) {
|
|
return llvm::StringSwitch<bool>(funcName)
|
|
.Case(pointerAllocateName, true)
|
|
.Case(pointerAllocateSourceName, true)
|
|
.Case(pointerDeallocateName, true)
|
|
.Case(pointerDeallocatePolymorphicName, true)
|
|
.Case(allocatableAllocateName, true)
|
|
.Case(allocatableAllocateSourceName, true)
|
|
.Case(allocatableDeallocateName, true)
|
|
.Case(allocatableDeallocatePolymorphicName, true)
|
|
.Default(false);
|
|
}
|
|
|
|
class ACCDeclareActionConversion
|
|
: public fir::acc::impl::ACCDeclareActionConversionBase<
|
|
ACCDeclareActionConversion> {
|
|
public:
|
|
using fir::acc::impl::ACCDeclareActionConversionBase<
|
|
ACCDeclareActionConversion>::ACCDeclareActionConversionBase;
|
|
|
|
void runOnOperation() override {
|
|
ModuleOp mod = getOperation();
|
|
OpBuilder builder(mod);
|
|
fir::LazySymbolTable symbolTable(mod);
|
|
|
|
mod.walk([&](Operation *op) {
|
|
auto declareAction = op->getAttrOfType<acc::DeclareActionAttr>(
|
|
acc::getDeclareActionAttrName());
|
|
if (!declareAction)
|
|
return;
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Found " << acc::getDeclareActionAttrName()
|
|
<< " on: " << *op << "\n");
|
|
|
|
auto preAlloc = declareAction.getPreAlloc();
|
|
auto postAlloc = declareAction.getPostAlloc();
|
|
auto preDealloc = declareAction.getPreDealloc();
|
|
auto postDealloc = declareAction.getPostDealloc();
|
|
|
|
if (!preAlloc && !postAlloc && !preDealloc && !postDealloc)
|
|
return;
|
|
|
|
for (auto action : {preAlloc, postAlloc, preDealloc, postDealloc}) {
|
|
if (!action)
|
|
continue;
|
|
|
|
if (auto func = dyn_cast<SymbolRefAttr>(action)) {
|
|
Operation *funcDef = symbolTable.lookupSymbol(func);
|
|
if (!funcDef)
|
|
continue;
|
|
|
|
if (auto funcOp = dyn_cast<func::FuncOp>(funcDef))
|
|
if (!funcOp->hasAttr(mlir::acc::getDeclareActionAttrName()))
|
|
funcOp->setAttr(mlir::acc::getDeclareActionAttrName(),
|
|
mlir::UnitAttr::get(funcOp.getContext()));
|
|
|
|
if (action == declareAction.getPreAlloc() ||
|
|
action == declareAction.getPreDealloc())
|
|
builder.setInsertionPoint(op);
|
|
else
|
|
builder.setInsertionPointAfter(op);
|
|
|
|
auto funcOp = dyn_cast<func::FuncOp>(funcDef);
|
|
if (!funcOp) {
|
|
op->emitError("declare action callee is not a func.func operation");
|
|
return;
|
|
}
|
|
SmallVector<Value> argVec;
|
|
if (funcOp.getNumArguments() > 0) {
|
|
Value varRef =
|
|
llvm::TypeSwitch<Operation *, Value>(op)
|
|
.Case<fir::StoreOp>(
|
|
[&](auto store) { return store.getMemref(); })
|
|
.Case<fir::BoxAddrOp>(
|
|
[&](auto boxAddr) { return boxAddr.getVal(); })
|
|
.Case<fir::CallOp>([&](fir::CallOp call) -> Value {
|
|
if (auto callee = call.getCalleeAttr()) {
|
|
StringRef funcName =
|
|
callee.getLeafReference().getValue();
|
|
const bool isSupported =
|
|
isSupportedDeclareActionRuntime(funcName);
|
|
assert(isSupported && "unexpected fir.call callee for "
|
|
"acc.declare_action");
|
|
if (!isSupported)
|
|
return {};
|
|
auto args = call.getArgs();
|
|
if (args.empty())
|
|
return {};
|
|
Value boxRef = args[0];
|
|
if (!fir::isBoxAddress(boxRef.getType()))
|
|
return {};
|
|
return boxRef;
|
|
}
|
|
return {};
|
|
})
|
|
.Default([](Operation *) { return Value(); });
|
|
|
|
if (!varRef) {
|
|
op->emitError(
|
|
"could not find argument for declare action recipe call");
|
|
return;
|
|
}
|
|
if (fir::isa_box_type(varRef.getType())) {
|
|
auto loadOp = varRef.getDefiningOp<fir::LoadOp>();
|
|
if (!loadOp) {
|
|
op->emitError("varRef for declare action is not from fir.load");
|
|
return;
|
|
}
|
|
varRef = loadOp.getMemref();
|
|
}
|
|
varRef = fir::acc::getOriginalDef(varRef, /*stripDeclare=*/false);
|
|
// Runtime calls (e.g. PointerAllocate) use ref<box<none>>; recipe
|
|
// expects typed box ref. Look through one convert to get the typed
|
|
// ref when getOriginalDef stopped at the convert (original LRO
|
|
// semantics: do not look through when result is box none).
|
|
Type recipeArgTy = funcOp.getFunctionType().getInput(0);
|
|
if (varRef.getType() != recipeArgTy) {
|
|
if (auto convertOp = varRef.getDefiningOp<fir::ConvertOp>()) {
|
|
Value converted = convertOp.getValue();
|
|
if (converted.getType() == recipeArgTy)
|
|
varRef = converted;
|
|
}
|
|
}
|
|
if (varRef.getType() != recipeArgTy) {
|
|
op->emitError("declare action recipe expects typed box ref");
|
|
return;
|
|
}
|
|
argVec.push_back(varRef);
|
|
}
|
|
fir::CallOp::create(builder, op->getLoc(), funcOp, argVec);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
} // namespace
|