Files
TheRock/compiler/amd-llvm/flang/lib/Optimizer/HLFIR/Transforms/SeparateAllocatableAssign.cpp
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

176 lines
7.1 KiB
C++

//===- SeparateAllocatableAssign.cpp - Split realloc from assign ----------===//
//
// 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.assign with realloc semantics into a conditional
// reallocation of the LHS followed by a plain hlfir.assign (without realloc).
//
// Before:
// hlfir.assign %rhs to %lhs realloc
//
// After:
// %shape = shape_of(%rhs)
// %new_lhs = genReallocIfNeeded(%lhs, %shape) // host-side alloc
// hlfir.assign %rhs to %new_lhs // element copy
//
// This is useful for OpenACC/OpenMP offloading where the allocation must
// happen on the host before entering a device compute region.
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Analysis/AliasAnalysis.h"
#include "flang/Optimizer/Builder/BoxValue.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/MutableBox.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "flang/Optimizer/HLFIR/Passes.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "llvm/Support/Debug.h"
namespace hlfir {
#define GEN_PASS_DEF_SEPARATEALLOCATABLEASSIGN
#include "flang/Optimizer/HLFIR/Passes.h.inc"
} // namespace hlfir
#define DEBUG_TYPE "separate-allocatable-assign"
namespace {
class SeparateAllocatableAssignConversion
: public mlir::OpRewritePattern<hlfir::AssignOp> {
public:
using mlir::OpRewritePattern<hlfir::AssignOp>::OpRewritePattern;
llvm::LogicalResult
matchAndRewrite(hlfir::AssignOp assign,
mlir::PatternRewriter &rewriter) const override {
if (!assign.isAllocatableAssignment())
return rewriter.notifyMatchFailure(assign, "not an allocatable assign");
hlfir::Entity rhs{assign.getRhs()};
hlfir::Entity lhs{assign.getLhs()};
if (!rhs.isArray())
return rewriter.notifyMatchFailure(assign, "RHS is not an array");
if (!lhs.isArray())
return rewriter.notifyMatchFailure(assign, "LHS is not an array");
mlir::Type rhsEleTy = rhs.getFortranElementType();
if (!fir::isa_trivial(rhsEleTy))
return rewriter.notifyMatchFailure(assign, "RHS type is not trivial");
mlir::Type lhsEleTy = lhs.getFortranElementType();
if (!fir::isa_trivial(lhsEleTy))
return rewriter.notifyMatchFailure(assign, "LHS type is not trivial");
if (lhsEleTy != rhsEleTy)
return rewriter.notifyMatchFailure(assign, "element type mismatch");
if (!fir::isBoxAddress(lhs.getType()))
return rewriter.notifyMatchFailure(assign, "LHS is not a box address");
mlir::Location loc = assign->getLoc();
fir::FirOpBuilder builder(rewriter, assign.getOperation());
builder.setInsertionPoint(assign);
// Reallocation frees the old LHS storage. If the RHS reads that same
// storage, the freed data would be read while producing the value to
// assign, causing use-after-free.
//
// For a variable RHS, query fir::AliasAnalysis to decide whether the RHS
// may access the LHS data and bail out if so. The aliasing question is
// about the *data* the allocatable points to, not the descriptor address:
// the RHS may reach the same storage through a different descriptor (e.g.
// a pointer or a function result whose local descriptor does not alias the
// LHS descriptor). To make the analysis reason about the data, materialize
// a temporary load of the LHS descriptor (a loaded fir.box is a data view)
// and use it as the LHS value in the query, then erase it.
//
// For an hlfir.expr RHS, the realloc is split out and the (lazy)
// expression evaluation is left in place before it. Keeping the expression
// evaluation from being moved across the deallocation is the
// responsibility of the hlfir.assign lowering / expression bufferization,
// so no aliasing analysis is performed here.
if (!mlir::isa<hlfir::ExprType>(rhs.getType())) {
fir::AliasAnalysis aliasAnalysis;
auto lhsDataView = fir::LoadOp::create(builder, loc, lhs.getFirBase());
mlir::AliasResult aliasRes =
aliasAnalysis.alias(lhsDataView.getResult(), assign.getRhs());
rewriter.eraseOp(lhsDataView);
if (!aliasRes.isNo())
return rewriter.notifyMatchFailure(assign, "LHS and RHS may alias");
}
LLVM_DEBUG(llvm::dbgs() << "SeparateAllocatableAssign: splitting realloc "
"from assign\n");
mlir::Value rhsShape = hlfir::genShape(loc, builder, rhs);
llvm::SmallVector<mlir::Value> rhsExtents =
hlfir::getIndexExtents(loc, builder, rhsShape);
// F2018 10.2.1.3: when the LHS is (re-)allocated, its lower bounds
// come from LBOUND(rhs). For variable RHS, extract the actual lower
// bounds from the entity; for hlfir.expr RHS, LBOUND is always 1.
llvm::SmallVector<mlir::Value> rhsLbounds;
if (!mlir::isa<hlfir::ExprType>(rhs.getType())) {
auto bounds = hlfir::genBounds(loc, builder, rhs);
for (auto &[lb, ub] : bounds)
rhsLbounds.push_back(lb);
}
fir::MutableBoxValue mutableBox(lhs.getFirBase(), /*lenParameters=*/{},
/*mutableProperties=*/{});
auto noopHandler = [](fir::ExtendedValue) {};
llvm::SmallVector<mlir::Value> lenParams;
fir::factory::MutableBoxReallocation realloc =
fir::factory::genReallocIfNeeded(builder, loc, mutableBox, rhsExtents,
lenParams, noopHandler);
fir::factory::finalizeRealloc(builder, loc, mutableBox, rhsLbounds,
/*takeLboundsIfRealloc=*/true, realloc);
mlir::Value lhsBox = fir::LoadOp::create(builder, loc, lhs.getFirBase());
hlfir::AssignOp::create(builder, loc, rhs, lhsBox,
/*realloc=*/false,
/*keep_lhs_length_if_realloc=*/false,
assign.isTemporaryLHS());
rewriter.eraseOp(assign);
return mlir::success();
}
};
class SeparateAllocatableAssignPass
: public hlfir::impl::SeparateAllocatableAssignBase<
SeparateAllocatableAssignPass> {
public:
using SeparateAllocatableAssignBase<
SeparateAllocatableAssignPass>::SeparateAllocatableAssignBase;
void runOnOperation() override {
mlir::MLIRContext *context = &getContext();
mlir::GreedyRewriteConfig config;
config.setRegionSimplificationLevel(
mlir::GreedySimplifyRegionLevel::Disabled);
mlir::RewritePatternSet patterns(context);
patterns.insert<SeparateAllocatableAssignConversion>(context);
if (mlir::failed(mlir::applyPatternsGreedily(
getOperation(), std::move(patterns), config))) {
mlir::emitError(getOperation()->getLoc(),
"failure in separate-allocatable-assign");
signalPassFailure();
}
}
};
} // namespace