Skip to content

[flang][OpenMP][MLIR] Basic support for delayed privatization code-gen #81833

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 28, 2024
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
6 changes: 6 additions & 0 deletions flang/include/flang/Lower/AbstractConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "flang/Common/Fortran.h"
#include "flang/Lower/LoweringOptions.h"
#include "flang/Lower/PFTDefs.h"
#include "flang/Lower/SymbolMap.h"
#include "flang/Optimizer/Builder/BoxValue.h"
#include "flang/Semantics/symbol.h"
#include "mlir/IR/Builders.h"
Expand Down Expand Up @@ -299,6 +300,11 @@ class AbstractConverter {
return loweringOptions;
}

/// Find the symbol in one level up of symbol map such as for host-association
/// in OpenMP code or return null.
virtual Fortran::lower::SymbolBox
lookupOneLevelUpSymbol(const Fortran::semantics::Symbol &sym) = 0;

private:
/// Options controlling lowering behavior.
const Fortran::lower::LoweringOptions &loweringOptions;
Expand Down
13 changes: 12 additions & 1 deletion flang/lib/Lower/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,17 @@ class FirConverter : public Fortran::lower::AbstractConverter {
if (sym.detailsIf<Fortran::semantics::CommonBlockDetails>())
return symMap->lookupSymbol(sym);

// For symbols to be privatized in OMP, the symbol is mapped to an
// instance of `SymbolBox::Intrinsic` (i.e. a direct mapping to an MLIR
// SSA value). This MLIR SSA value is the block argument to the
// `omp.private`'s `alloc` block. If this is the case, we return this
// `SymbolBox::Intrinsic` value.
Comment on lines +1003 to +1007
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the point that Extended values are not allowed here now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. I am working on adding more support for other types of value at the moment.

if (Fortran::lower::SymbolBox v = symMap->lookupSymbol(sym))
return v.match(
[&](const Fortran::lower::SymbolBox::Intrinsic &)
-> Fortran::lower::SymbolBox { return v; },
[](const auto &) -> Fortran::lower::SymbolBox { return {}; });

return {};
}
if (Fortran::lower::SymbolBox v = symMap->lookupSymbol(sym))
Expand All @@ -1018,7 +1029,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
/// Find the symbol in one level up of symbol map such as for host-association
/// in OpenMP code or return null.
Fortran::lower::SymbolBox
lookupOneLevelUpSymbol(const Fortran::semantics::Symbol &sym) {
lookupOneLevelUpSymbol(const Fortran::semantics::Symbol &sym) override {
if (Fortran::lower::SymbolBox v = localSymbols.lookupOneLevelUpSymbol(sym))
return v;
return {};
Expand Down
107 changes: 94 additions & 13 deletions flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ void DataSharingProcessor::cloneSymbol(const Fortran::semantics::Symbol *sym) {
}

void DataSharingProcessor::copyFirstPrivateSymbol(
const Fortran::semantics::Symbol *sym) {
const Fortran::semantics::Symbol *sym,
mlir::OpBuilder::InsertPoint *copyAssignIP) {
if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate))
converter.copyHostAssociateVar(*sym);
converter.copyHostAssociateVar(*sym, copyAssignIP);
}

void DataSharingProcessor::copyLastPrivateSymbol(
Expand Down Expand Up @@ -307,14 +308,10 @@ void DataSharingProcessor::privatize() {
for (const Fortran::semantics::Symbol *sym : privatizedSymbols) {
if (const auto *commonDet =
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
for (const auto &mem : commonDet->objects()) {
cloneSymbol(&*mem);
copyFirstPrivateSymbol(&*mem);
}
} else {
cloneSymbol(sym);
copyFirstPrivateSymbol(sym);
}
for (const auto &mem : commonDet->objects())
doPrivatize(&*mem);
} else
doPrivatize(sym);
}
}

Expand All @@ -338,11 +335,95 @@ void DataSharingProcessor::defaultPrivatize() {
!sym->GetUltimate().has<Fortran::semantics::NamelistDetails>() &&
!symbolsInNestedRegions.contains(sym) &&
!symbolsInParentRegions.contains(sym) &&
!privatizedSymbols.contains(sym)) {
!privatizedSymbols.contains(sym))
doPrivatize(sym);
}
}

void DataSharingProcessor::doPrivatize(const Fortran::semantics::Symbol *sym) {
if (!useDelayedPrivatization) {
cloneSymbol(sym);
copyFirstPrivateSymbol(sym);
return;
}

Fortran::lower::SymbolBox hsb = converter.lookupOneLevelUpSymbol(*sym);
assert(hsb && "Host symbol box not found");

mlir::Type symType = hsb.getAddr().getType();
mlir::Location symLoc = hsb.getAddr().getLoc();
std::string privatizerName = sym->name().ToString() + ".privatizer";
bool isFirstPrivate =
sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate);

mlir::omp::PrivateClauseOp privatizerOp = [&]() {
auto moduleOp = firOpBuilder.getModule();
auto uniquePrivatizerName = fir::getTypeAsString(
symType, converter.getKindMap(),
converter.mangleName(*sym) +
(isFirstPrivate ? "_firstprivate" : "_private"));

if (auto existingPrivatizer =
moduleOp.lookupSymbol<mlir::omp::PrivateClauseOp>(
uniquePrivatizerName))
return existingPrivatizer;

auto ip = firOpBuilder.saveInsertionPoint();
firOpBuilder.setInsertionPoint(&moduleOp.getBodyRegion().front(),
moduleOp.getBodyRegion().front().begin());
auto result = firOpBuilder.create<mlir::omp::PrivateClauseOp>(
symLoc, uniquePrivatizerName, symType,
isFirstPrivate ? mlir::omp::DataSharingClauseType::FirstPrivate
: mlir::omp::DataSharingClauseType::Private);

symTable->pushScope();

// Populate the `alloc` region.
{
mlir::Region &allocRegion = result.getAllocRegion();
mlir::Block *allocEntryBlock = firOpBuilder.createBlock(
&allocRegion, /*insertPt=*/{}, symType, symLoc);

firOpBuilder.setInsertionPointToEnd(allocEntryBlock);
symTable->addSymbol(*sym, allocRegion.getArgument(0));
symTable->pushScope();
cloneSymbol(sym);
copyFirstPrivateSymbol(sym);
firOpBuilder.create<mlir::omp::YieldOp>(
hsb.getAddr().getLoc(),
symTable->shallowLookupSymbol(*sym).getAddr());
symTable->popScope();
}
}

// Populate the `copy` region if this is a `firstprivate`.
if (isFirstPrivate) {
mlir::Region &copyRegion = result.getCopyRegion();
// First block argument corresponding to the original/host value while
// second block argument corresponding to the privatized value.
mlir::Block *copyEntryBlock = firOpBuilder.createBlock(
&copyRegion, /*insertPt=*/{}, {symType, symType}, {symLoc, symLoc});
firOpBuilder.setInsertionPointToEnd(copyEntryBlock);
symTable->addSymbol(*sym, copyRegion.getArgument(0),
/*force=*/true);
symTable->pushScope();
symTable->addSymbol(*sym, copyRegion.getArgument(1));
auto ip = firOpBuilder.saveInsertionPoint();
copyFirstPrivateSymbol(sym, &ip);

firOpBuilder.create<mlir::omp::YieldOp>(
hsb.getAddr().getLoc(),
symTable->shallowLookupSymbol(*sym).getAddr());
symTable->popScope();
}

symTable->popScope();
firOpBuilder.restoreInsertionPoint(ip);
return result;
}();

delayedPrivatizationInfo.privatizers.push_back(
mlir::SymbolRefAttr::get(privatizerOp));
delayedPrivatizationInfo.originalAddresses.push_back(hsb.getAddr());
delayedPrivatizationInfo.symbols.push_back(sym);
}

} // namespace omp
Expand Down
38 changes: 35 additions & 3 deletions flang/lib/Lower/OpenMP/DataSharingProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ namespace lower {
namespace omp {

class DataSharingProcessor {
public:
/// Collects all the information needed for delayed privatization. This can be
/// used by ops with data-sharing clauses to properly generate their regions
/// (e.g. add region arguments) and map the original SSA values to their
/// corresponding OMP region operands.
struct DelayedPrivatizationInfo {
// The list of symbols referring to delayed privatizer ops (i.e.
// `omp.private` ops).
llvm::SmallVector<mlir::SymbolRefAttr> privatizers;
// SSA values that correspond to "original" values being privatized.
// "Original" here means the SSA value outside the OpenMP region from which
// a clone is created inside the region.
llvm::SmallVector<mlir::Value> originalAddresses;
// Fortran symbols corresponding to the above SSA values.
llvm::SmallVector<const Fortran::semantics::Symbol *> symbols;
};

private:
bool hasLastPrivateOp;
mlir::OpBuilder::InsertPoint lastPrivIP;
mlir::OpBuilder::InsertPoint insPt;
Expand All @@ -36,6 +54,9 @@ class DataSharingProcessor {
fir::FirOpBuilder &firOpBuilder;
const Fortran::parser::OmpClauseList &opClauseList;
Fortran::lower::pft::Evaluation &eval;
bool useDelayedPrivatization;
Fortran::lower::SymMap *symTable;
DelayedPrivatizationInfo delayedPrivatizationInfo;

bool needBarrier();
void collectSymbols(Fortran::semantics::Symbol::Flag flag);
Expand All @@ -47,21 +68,28 @@ class DataSharingProcessor {
void collectDefaultSymbols();
void privatize();
void defaultPrivatize();
void doPrivatize(const Fortran::semantics::Symbol *sym);
void copyLastPrivatize(mlir::Operation *op);
void insertLastPrivateCompare(mlir::Operation *op);
void cloneSymbol(const Fortran::semantics::Symbol *sym);
void copyFirstPrivateSymbol(const Fortran::semantics::Symbol *sym);
void
copyFirstPrivateSymbol(const Fortran::semantics::Symbol *sym,
mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr);
void copyLastPrivateSymbol(const Fortran::semantics::Symbol *sym,
mlir::OpBuilder::InsertPoint *lastPrivIP);
void insertDeallocs();

public:
DataSharingProcessor(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::OmpClauseList &opClauseList,
Fortran::lower::pft::Evaluation &eval)
Fortran::lower::pft::Evaluation &eval,
bool useDelayedPrivatization = false,
Fortran::lower::SymMap *symTable = nullptr)
: hasLastPrivateOp(false), converter(converter),
firOpBuilder(converter.getFirOpBuilder()), opClauseList(opClauseList),
eval(eval) {}
eval(eval), useDelayedPrivatization(useDelayedPrivatization),
symTable(symTable) {}

// Privatisation is split into two steps.
// Step1 performs cloning of all privatisation clauses and copying for
// firstprivates. Step1 is performed at the place where process/processStep1
Expand All @@ -80,6 +108,10 @@ class DataSharingProcessor {
assert(!loopIV && "Loop iteration variable already set");
loopIV = iv;
}

const DelayedPrivatizationInfo &getDelayedPrivatizationInfo() const {
return delayedPrivatizationInfo;
}
};

} // namespace omp
Expand Down
Loading