CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
188 lines
8.0 KiB
C++
188 lines
8.0 KiB
C++
//===- InlineHLFIRCopyIn.cpp - Inline hlfir.copy_in ops -------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
// Transform hlfir.copy_in array operations into loop nests performing element
|
|
// per element assignments. For simplicity, the inlining is done for trivial
|
|
// data types when the copy_in does not require a corresponding copy_out and
|
|
// when the input array is not behind a pointer. This may change in the future.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
|
#include "flang/Optimizer/Builder/HLFIRTools.h"
|
|
#include "flang/Optimizer/Dialect/FIRType.h"
|
|
#include "flang/Optimizer/HLFIR/HLFIROps.h"
|
|
#include "flang/Optimizer/OpenMP/Passes.h"
|
|
#include "mlir/IR/PatternMatch.h"
|
|
#include "mlir/Support/LLVM.h"
|
|
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
|
|
|
namespace hlfir {
|
|
#define GEN_PASS_DEF_INLINEHLFIRCOPYIN
|
|
#include "flang/Optimizer/HLFIR/Passes.h.inc"
|
|
} // namespace hlfir
|
|
|
|
#define DEBUG_TYPE "inline-hlfir-copy-in"
|
|
|
|
static llvm::cl::opt<bool> noInlineHLFIRCopyIn(
|
|
"no-inline-hlfir-copy-in",
|
|
llvm::cl::desc("Do not inline hlfir.copy_in operations"),
|
|
llvm::cl::init(false));
|
|
|
|
namespace {
|
|
class InlineCopyInConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
|
|
public:
|
|
using mlir::OpRewritePattern<hlfir::CopyInOp>::OpRewritePattern;
|
|
|
|
llvm::LogicalResult
|
|
matchAndRewrite(hlfir::CopyInOp copyIn,
|
|
mlir::PatternRewriter &rewriter) const override;
|
|
};
|
|
|
|
llvm::LogicalResult
|
|
InlineCopyInConversion::matchAndRewrite(hlfir::CopyInOp copyIn,
|
|
mlir::PatternRewriter &rewriter) const {
|
|
fir::FirOpBuilder builder(rewriter, copyIn.getOperation());
|
|
mlir::Location loc = copyIn.getLoc();
|
|
hlfir::Entity inputVariable{copyIn.getVar()};
|
|
mlir::Type resultAddrType = copyIn.getCopiedIn().getType();
|
|
if (!fir::isa_trivial(inputVariable.getFortranElementType()))
|
|
return rewriter.notifyMatchFailure(copyIn,
|
|
"CopyInOp's data type is not trivial");
|
|
|
|
// There should be exactly one user of WasCopied - the corresponding
|
|
// CopyOutOp.
|
|
if (!copyIn.getWasCopied().hasOneUse())
|
|
return rewriter.notifyMatchFailure(
|
|
copyIn, "CopyInOp's WasCopied has no single user");
|
|
// The copy out should always be present, either to actually copy or just
|
|
// deallocate memory.
|
|
auto copyOut = mlir::dyn_cast<hlfir::CopyOutOp>(
|
|
copyIn.getWasCopied().user_begin().getCurrent().getUser());
|
|
|
|
if (!copyOut)
|
|
return rewriter.notifyMatchFailure(copyIn,
|
|
"CopyInOp has no direct CopyOut");
|
|
|
|
if (mlir::cast<fir::BaseBoxType>(resultAddrType).isAssumedRank())
|
|
return rewriter.notifyMatchFailure(copyIn,
|
|
"The result array is assumed-rank");
|
|
|
|
// Only inline the copy_in when copy_out does not need to be done, i.e. in
|
|
// case of intent(in).
|
|
if (copyOut.getVar())
|
|
return rewriter.notifyMatchFailure(copyIn, "CopyIn needs a copy-out");
|
|
|
|
inputVariable =
|
|
hlfir::derefPointersAndAllocatables(loc, builder, inputVariable);
|
|
mlir::Type sequenceType =
|
|
hlfir::getFortranElementOrSequenceType(inputVariable.getType());
|
|
fir::BoxType resultBoxType = fir::BoxType::get(sequenceType);
|
|
mlir::Value isContiguous =
|
|
fir::IsContiguousBoxOp::create(builder, loc, inputVariable);
|
|
mlir::Operation::result_range results =
|
|
builder
|
|
.genIfOp(loc, {resultBoxType, builder.getI1Type()}, isContiguous,
|
|
/*withElseRegion=*/true)
|
|
.genThen([&]() {
|
|
mlir::Value result = inputVariable;
|
|
if (fir::isPointerType(inputVariable.getType())) {
|
|
result = fir::ReboxOp::create(builder, loc, resultBoxType,
|
|
inputVariable, mlir::Value{},
|
|
mlir::Value{});
|
|
}
|
|
fir::ResultOp::create(
|
|
builder, loc,
|
|
mlir::ValueRange{result, builder.createBool(loc, false)});
|
|
})
|
|
.genElse([&] {
|
|
mlir::Value shape = hlfir::genShape(loc, builder, inputVariable);
|
|
llvm::SmallVector<mlir::Value> extents =
|
|
hlfir::getIndexExtents(loc, builder, shape);
|
|
llvm::StringRef tmpName{".tmp.copy_in"};
|
|
llvm::SmallVector<mlir::Value> lenParams;
|
|
mlir::Value alloc = builder.createHeapTemporary(
|
|
loc, sequenceType, tmpName, extents, lenParams);
|
|
|
|
auto declareOp = hlfir::DeclareOp::create(builder, loc, alloc,
|
|
tmpName, shape, lenParams,
|
|
/*dummy_scope=*/nullptr,
|
|
/*storage=*/nullptr,
|
|
/*storage_offset=*/0);
|
|
hlfir::Entity temp{declareOp.getBase()};
|
|
hlfir::LoopNest loopNest =
|
|
hlfir::genLoopNest(loc, builder, extents, /*isUnordered=*/true,
|
|
flangomp::shouldUseWorkshareLowering(copyIn),
|
|
/*couldVectorize=*/false);
|
|
builder.setInsertionPointToStart(loopNest.body);
|
|
hlfir::Entity elem = hlfir::getElementAt(
|
|
loc, builder, inputVariable, loopNest.oneBasedIndices);
|
|
elem = hlfir::loadTrivialScalar(loc, builder, elem);
|
|
hlfir::Entity tempElem = hlfir::getElementAt(
|
|
loc, builder, temp, loopNest.oneBasedIndices);
|
|
hlfir::AssignOp::create(builder, loc, elem, tempElem);
|
|
builder.setInsertionPointAfter(loopNest.outerOp);
|
|
|
|
mlir::Value result;
|
|
// Make sure the result is always a boxed array by boxing it
|
|
// ourselves if need be.
|
|
if (mlir::isa<fir::BaseBoxType>(temp.getType())) {
|
|
result = temp;
|
|
} else {
|
|
fir::ReferenceType refTy =
|
|
fir::ReferenceType::get(temp.getElementOrSequenceType());
|
|
mlir::Value refVal = builder.createConvert(loc, refTy, temp);
|
|
result = fir::EmboxOp::create(builder, loc, resultBoxType, refVal,
|
|
shape);
|
|
}
|
|
|
|
fir::ResultOp::create(
|
|
builder, loc,
|
|
mlir::ValueRange{result, builder.createBool(loc, true)});
|
|
})
|
|
.getResults();
|
|
|
|
mlir::OpResult resultBox = results[0];
|
|
mlir::OpResult needsCleanup = results[1];
|
|
|
|
// Prepare the corresponding copyOut to free the temporary if it is required
|
|
auto alloca = fir::AllocaOp::create(builder, loc, resultBox.getType());
|
|
auto store = fir::StoreOp::create(builder, loc, resultBox, alloca);
|
|
rewriter.startOpModification(copyOut);
|
|
copyOut->setOperand(0, store.getMemref());
|
|
copyOut->setOperand(1, needsCleanup);
|
|
rewriter.finalizeOpModification(copyOut);
|
|
|
|
rewriter.replaceOp(copyIn, {resultBox, builder.genNot(loc, isContiguous)});
|
|
return mlir::success();
|
|
}
|
|
|
|
class InlineHLFIRCopyInPass
|
|
: public hlfir::impl::InlineHLFIRCopyInBase<InlineHLFIRCopyInPass> {
|
|
public:
|
|
void runOnOperation() override {
|
|
mlir::MLIRContext *context = &getContext();
|
|
|
|
mlir::GreedyRewriteConfig config;
|
|
// Prevent the pattern driver from merging blocks.
|
|
config.setRegionSimplificationLevel(
|
|
mlir::GreedySimplifyRegionLevel::Disabled);
|
|
|
|
mlir::RewritePatternSet patterns(context);
|
|
if (!noInlineHLFIRCopyIn) {
|
|
patterns.insert<InlineCopyInConversion>(context);
|
|
}
|
|
|
|
if (mlir::failed(mlir::applyPatternsGreedily(
|
|
getOperation(), std::move(patterns), config))) {
|
|
mlir::emitError(getOperation()->getLoc(),
|
|
"failure in hlfir.copy_in inlining");
|
|
signalPassFailure();
|
|
}
|
|
}
|
|
};
|
|
} // namespace
|