Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flang/include/flang/Optimizer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ add_subdirectory(CodeGen)
add_subdirectory(Dialect)
add_subdirectory(HLFIR)
add_subdirectory(Transforms)
add_subdirectory(OpenACC)
add_subdirectory(OpenMP)
4 changes: 4 additions & 0 deletions flang/include/flang/Optimizer/OpenACC/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
set(LLVM_TARGET_DEFINITIONS Passes.td)
mlir_tablegen(Passes.h.inc -gen-pass-decls -name FIROpenACC)

add_public_tablegen_target(FIROpenACCPassesIncGen)
33 changes: 33 additions & 0 deletions flang/include/flang/Optimizer/OpenACC/Passes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===- Passes.h - OpenACC pass entry points -------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This header declares the OpenACC passes specific to Fortran and FIR.
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_OPTIMIZER_OPENACC_PASSES_H
#define FORTRAN_OPTIMIZER_OPENACC_PASSES_H

#include "mlir/IR/BuiltinOps.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassRegistry.h"

#include <memory>

namespace fir {
namespace acc {
#define GEN_PASS_DECL
#define GEN_PASS_REGISTRATION
#include "flang/Optimizer/OpenACC/Passes.h.inc"

std::unique_ptr<mlir::Pass> createACCRecipeBufferizationPass();

} // namespace acc
} // namespace fir

#endif // FORTRAN_OPTIMIZER_OPENACC_PASSES_H
36 changes: 36 additions & 0 deletions flang/include/flang/Optimizer/OpenACC/Passes.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===-- Passes.td - flang OpenACC pass definitions -----------*- tablegen -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_OPTIMIZER_OPENACC_PASSES
#define FORTRAN_OPTIMIZER_OPENACC_PASSES

include "mlir/Pass/PassBase.td"

def ACCRecipeBufferization
: Pass<"fir-acc-recipe-bufferization", "mlir::ModuleOp"> {
let summary = "Rewrite acc.*.recipe box values to ref<box> and update uses";
let description = [{
Bufferizes OpenACC recipes that operate on fir.box<T> so their type and
region block arguments become fir.ref<fir.box<T>> instead. This applies to
acc.private.recipe, acc.firstprivate.recipe (including copy region), and
acc.reduction.recipe (including combiner region).

For affected regions, the pass inserts required loads at the beginning of
the region to preserve original uses after argument type changes. For yields
of box values, the pass allocates a local fir.ref<fir.box<T>> and stores the
yielded fir.box<T> into it so the region yields a reference to a box.

For acc.private, acc.firstprivate, and acc.reduction operations that use a
bufferized recipe, the pass allocates a host-side fir.ref<fir.box<T>> before
the data op and rewires the data op to use the new memory. Other users of
the original data operation result (outside the paired compute op) are
updated to load through the reference.
}];
}

#endif // FORTRAN_OPTIMIZER_OPENACC_PASSES
1 change: 1 addition & 0 deletions flang/lib/Optimizer/OpenACC/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(Support)
add_subdirectory(Transforms)
191 changes: 191 additions & 0 deletions flang/lib/Optimizer/OpenACC/Transforms/ACCRecipeBufferization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//===- ACCRecipeBufferization.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
//
//===----------------------------------------------------------------------===//
//
// Bufferize OpenACC recipes that yield fir.box<T> to operate on
// fir.ref<fir.box<T>> and update uses accordingly.
//
//===----------------------------------------------------------------------===//

#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/OpenACC/Passes.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/SymbolTable.h"
#include "mlir/IR/Value.h"
#include "mlir/IR/Visitors.h"
#include "llvm/ADT/TypeSwitch.h"

namespace fir::acc {
#define GEN_PASS_DEF_ACCRECIPEBUFFERIZATION
#include "flang/Optimizer/OpenACC/Passes.h.inc"
} // namespace fir::acc

namespace {

class BufferizeInterface {
public:
static std::optional<mlir::Type> mustBufferize(mlir::Type recipeType) {
if (auto boxTy = llvm::dyn_cast<fir::BaseBoxType>(recipeType))
return fir::ReferenceType::get(boxTy);
return std::nullopt;
}

static mlir::Operation *load(mlir::OpBuilder &builder, mlir::Location loc,
mlir::Value value) {
return builder.create<fir::LoadOp>(loc, value);
}

static mlir::Value placeInMemory(mlir::OpBuilder &builder, mlir::Location loc,
mlir::Value value) {
auto alloca = builder.create<fir::AllocaOp>(loc, value.getType());
builder.create<fir::StoreOp>(loc, value, alloca);
return alloca;
}
};

static void bufferizeRegionArgsAndYields(mlir::Region &region,
mlir::Location loc, mlir::Type oldType,
mlir::Type newType) {
if (region.empty())
return;

mlir::OpBuilder builder(&region);
for (mlir::BlockArgument arg : region.getArguments()) {
if (arg.getType() == oldType) {
arg.setType(newType);
if (!arg.use_empty()) {
mlir::Operation *loadOp = BufferizeInterface::load(builder, loc, arg);
arg.replaceAllUsesExcept(loadOp->getResult(0), loadOp);
}
}
}
if (auto yield =
llvm::dyn_cast<mlir::acc::YieldOp>(region.back().getTerminator())) {
llvm::SmallVector<mlir::Value> newOperands;
newOperands.reserve(yield.getNumOperands());
bool changed = false;
for (mlir::Value oldYieldArg : yield.getOperands()) {
if (oldYieldArg.getType() == oldType) {
builder.setInsertionPoint(yield);
mlir::Value alloca =
BufferizeInterface::placeInMemory(builder, loc, oldYieldArg);
newOperands.push_back(alloca);
changed = true;
} else {
newOperands.push_back(oldYieldArg);
}
}
if (changed)
yield->setOperands(newOperands);
}
}

static void updateRecipeUse(mlir::ArrayAttr recipes, mlir::ValueRange operands,
llvm::StringRef recipeSymName,
mlir::Operation *computeOp) {
if (!recipes)
return;
for (auto [recipeSym, oldRes] : llvm::zip(recipes, operands)) {
if (llvm::cast<mlir::SymbolRefAttr>(recipeSym).getLeafReference() !=
recipeSymName)
continue;

mlir::Operation *dataOp = oldRes.getDefiningOp();
assert(dataOp && "dataOp must be paired with computeOp");
mlir::Location loc = dataOp->getLoc();
mlir::OpBuilder builder(dataOp);
llvm::TypeSwitch<mlir::Operation *, void>(dataOp)
.Case<mlir::acc::PrivateOp, mlir::acc::FirstprivateOp,
mlir::acc::ReductionOp>([&](auto privateOp) {
builder.setInsertionPointAfterValue(privateOp.getVar());
mlir::Value alloca = BufferizeInterface::placeInMemory(
builder, loc, privateOp.getVar());
privateOp.getVarMutable().assign(alloca);
privateOp.getAccVar().setType(alloca.getType());
});

llvm::SmallVector<mlir::Operation *> users(oldRes.getUsers().begin(),
oldRes.getUsers().end());
for (mlir::Operation *useOp : users) {
if (useOp == computeOp)
continue;
builder.setInsertionPoint(useOp);
mlir::Operation *load = BufferizeInterface::load(builder, loc, oldRes);
useOp->replaceUsesOfWith(oldRes, load->getResult(0));
}
}
}

class ACCRecipeBufferization
: public fir::acc::impl::ACCRecipeBufferizationBase<
ACCRecipeBufferization> {
public:
void runOnOperation() override {
mlir::ModuleOp module = getOperation();

llvm::SmallVector<llvm::StringRef> recipeNames;
module.walk([&](mlir::Operation *recipe) {
llvm::TypeSwitch<mlir::Operation *, void>(recipe)
.Case<mlir::acc::PrivateRecipeOp, mlir::acc::FirstprivateRecipeOp,
mlir::acc::ReductionRecipeOp>([&](auto recipe) {
mlir::Type oldType = recipe.getType();
auto bufferizedType =
BufferizeInterface::mustBufferize(recipe.getType());
if (!bufferizedType)
return;
recipe.setTypeAttr(mlir::TypeAttr::get(*bufferizedType));
mlir::Location loc = recipe.getLoc();
using RecipeOp = decltype(recipe);
bufferizeRegionArgsAndYields(recipe.getInitRegion(), loc, oldType,
*bufferizedType);
if constexpr (std::is_same_v<RecipeOp,
mlir::acc::FirstprivateRecipeOp>)
bufferizeRegionArgsAndYields(recipe.getCopyRegion(), loc, oldType,
*bufferizedType);
if constexpr (std::is_same_v<RecipeOp,
mlir::acc::ReductionRecipeOp>)
bufferizeRegionArgsAndYields(recipe.getCombinerRegion(), loc,
oldType, *bufferizedType);
bufferizeRegionArgsAndYields(recipe.getDestroyRegion(), loc,
oldType, *bufferizedType);
recipeNames.push_back(recipe.getSymName());
});
});
if (recipeNames.empty())
return;

module.walk([&](mlir::Operation *op) {
llvm::TypeSwitch<mlir::Operation *, void>(op)
.Case<mlir::acc::LoopOp, mlir::acc::ParallelOp, mlir::acc::SerialOp>(
[&](auto computeOp) {
for (llvm::StringRef recipeName : recipeNames) {
if (computeOp.getPrivatizationRecipes())
updateRecipeUse(computeOp.getPrivatizationRecipesAttr(),
computeOp.getPrivateOperands(), recipeName,
op);
if (computeOp.getFirstprivatizationRecipes())
updateRecipeUse(
computeOp.getFirstprivatizationRecipesAttr(),
computeOp.getFirstprivateOperands(), recipeName, op);
if (computeOp.getReductionRecipes())
updateRecipeUse(computeOp.getReductionRecipesAttr(),
computeOp.getReductionOperands(),
recipeName, op);
}
});
});
}
};

} // namespace

std::unique_ptr<mlir::Pass> fir::acc::createACCRecipeBufferizationPass() {
return std::make_unique<ACCRecipeBufferization>();
}
12 changes: 12 additions & 0 deletions flang/lib/Optimizer/OpenACC/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_flang_library(FIROpenACCTransforms
ACCRecipeBufferization.cpp

DEPENDS
FIROpenACCPassesIncGen

LINK_LIBS
MLIRIR
MLIRPass
FIRDialect
MLIROpenACCDialect
)
Loading
Loading