Skip to content

Commit 4fb1267

Browse files
committed
[flang][OpenMP] Add support for copyprivate
Add initial handling of OpenMP copyprivate clause in Flang. When lowering copyprivate, Flang generates the copy function needed by each variable and builds the appropriate omp.single's CopyPrivateVarList. This is patch 3 of 4, to add support for COPYPRIVATE in Flang. Original PR: #73128
1 parent 594d57e commit 4fb1267

File tree

5 files changed

+414
-76
lines changed

5 files changed

+414
-76
lines changed

flang/include/flang/Lower/AbstractConverter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ class AbstractConverter {
121121
const Fortran::semantics::Symbol &sym,
122122
mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) = 0;
123123

124+
virtual void copyVar(mlir::Location loc, mlir::Value dst,
125+
mlir::Value src) = 0;
126+
124127
/// For a given symbol, check if it is present in the inner-most
125128
/// level of the symbol map.
126129
virtual bool isPresentShallowLookup(Fortran::semantics::Symbol &sym) = 0;

flang/lib/Lower/Bridge.cpp

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
744744
});
745745
}
746746

747+
void copyVar(mlir::Location loc, mlir::Value dst,
748+
mlir::Value src) override final {
749+
copyVarHLFIR(loc, dst, src);
750+
}
751+
747752
void copyHostAssociateVar(
748753
const Fortran::semantics::Symbol &sym,
749754
mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) override final {
@@ -778,64 +783,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
778783
rhs_sb = &hsb;
779784
}
780785

781-
mlir::Location loc = genLocation(sym.name());
782-
783-
if (lowerToHighLevelFIR()) {
784-
hlfir::Entity lhs{lhs_sb->getAddr()};
785-
hlfir::Entity rhs{rhs_sb->getAddr()};
786-
// Temporary_lhs is set to true in hlfir.assign below to avoid user
787-
// assignment to be used and finalization to be called on the LHS.
788-
// This may or may not be correct but mimics the current behaviour
789-
// without HLFIR.
790-
auto copyData = [&](hlfir::Entity l, hlfir::Entity r) {
791-
// Dereference RHS and load it if trivial scalar.
792-
r = hlfir::loadTrivialScalar(loc, *builder, r);
793-
builder->create<hlfir::AssignOp>(
794-
loc, r, l,
795-
/*isWholeAllocatableAssignment=*/false,
796-
/*keepLhsLengthInAllocatableAssignment=*/false,
797-
/*temporary_lhs=*/true);
798-
};
799-
if (lhs.isAllocatable()) {
800-
// Deep copy allocatable if it is allocated.
801-
// Note that when allocated, the RHS is already allocated with the LHS
802-
// shape for copy on entry in createHostAssociateVarClone.
803-
// For lastprivate, this assumes that the RHS was not reallocated in
804-
// the OpenMP region.
805-
lhs = hlfir::derefPointersAndAllocatables(loc, *builder, lhs);
806-
mlir::Value addr = hlfir::genVariableRawAddress(loc, *builder, lhs);
807-
mlir::Value isAllocated = builder->genIsNotNullAddr(loc, addr);
808-
builder->genIfThen(loc, isAllocated)
809-
.genThen([&]() {
810-
// Copy the DATA, not the descriptors.
811-
copyData(lhs, rhs);
812-
})
813-
.end();
814-
} else if (lhs.isPointer()) {
815-
// Set LHS target to the target of RHS (do not copy the RHS
816-
// target data into the LHS target storage).
817-
auto loadVal = builder->create<fir::LoadOp>(loc, rhs);
818-
builder->create<fir::StoreOp>(loc, loadVal, lhs);
819-
} else {
820-
// Non ALLOCATABLE/POINTER variable. Simple DATA copy.
821-
copyData(lhs, rhs);
822-
}
823-
} else {
824-
fir::ExtendedValue lhs = symBoxToExtendedValue(*lhs_sb);
825-
fir::ExtendedValue rhs = symBoxToExtendedValue(*rhs_sb);
826-
mlir::Type symType = genType(sym);
827-
if (auto seqTy = symType.dyn_cast<fir::SequenceType>()) {
828-
Fortran::lower::StatementContext stmtCtx;
829-
Fortran::lower::createSomeArrayAssignment(*this, lhs, rhs, localSymbols,
830-
stmtCtx);
831-
stmtCtx.finalizeAndReset();
832-
} else if (lhs.getBoxOf<fir::CharBoxValue>()) {
833-
fir::factory::CharacterExprHelper{*builder, loc}.createAssign(lhs, rhs);
834-
} else {
835-
auto loadVal = builder->create<fir::LoadOp>(loc, fir::getBase(rhs));
836-
builder->create<fir::StoreOp>(loc, loadVal, fir::getBase(lhs));
837-
}
838-
}
786+
copyVar(sym, *lhs_sb, *rhs_sb);
839787

840788
if (copyAssignIP && copyAssignIP->isSet() &&
841789
sym.test(Fortran::semantics::Symbol::Flag::OmpLastPrivate)) {
@@ -1093,6 +1041,79 @@ class FirConverter : public Fortran::lower::AbstractConverter {
10931041
return true;
10941042
}
10951043

1044+
void copyVar(const Fortran::semantics::Symbol &sym,
1045+
const Fortran::lower::SymbolBox &lhs_sb,
1046+
const Fortran::lower::SymbolBox &rhs_sb) {
1047+
mlir::Location loc = genLocation(sym.name());
1048+
if (lowerToHighLevelFIR())
1049+
copyVarHLFIR(loc, lhs_sb.getAddr(), rhs_sb.getAddr());
1050+
else
1051+
copyVarFIR(loc, sym, lhs_sb, rhs_sb);
1052+
}
1053+
1054+
void copyVarHLFIR(mlir::Location loc, mlir::Value dst, mlir::Value src) {
1055+
assert(lowerToHighLevelFIR());
1056+
hlfir::Entity lhs{dst};
1057+
hlfir::Entity rhs{src};
1058+
// Temporary_lhs is set to true in hlfir.assign below to avoid user
1059+
// assignment to be used and finalization to be called on the LHS.
1060+
// This may or may not be correct but mimics the current behaviour
1061+
// without HLFIR.
1062+
auto copyData = [&](hlfir::Entity l, hlfir::Entity r) {
1063+
// Dereference RHS and load it if trivial scalar.
1064+
r = hlfir::loadTrivialScalar(loc, *builder, r);
1065+
builder->create<hlfir::AssignOp>(
1066+
loc, r, l,
1067+
/*isWholeAllocatableAssignment=*/false,
1068+
/*keepLhsLengthInAllocatableAssignment=*/false,
1069+
/*temporary_lhs=*/true);
1070+
};
1071+
if (lhs.isAllocatable()) {
1072+
// Deep copy allocatable if it is allocated.
1073+
// Note that when allocated, the RHS is already allocated with the LHS
1074+
// shape for copy on entry in createHostAssociateVarClone.
1075+
// For lastprivate, this assumes that the RHS was not reallocated in
1076+
// the OpenMP region.
1077+
lhs = hlfir::derefPointersAndAllocatables(loc, *builder, lhs);
1078+
mlir::Value addr = hlfir::genVariableRawAddress(loc, *builder, lhs);
1079+
mlir::Value isAllocated = builder->genIsNotNullAddr(loc, addr);
1080+
builder->genIfThen(loc, isAllocated)
1081+
.genThen([&]() {
1082+
// Copy the DATA, not the descriptors.
1083+
copyData(lhs, rhs);
1084+
})
1085+
.end();
1086+
} else if (lhs.isPointer()) {
1087+
// Set LHS target to the target of RHS (do not copy the RHS
1088+
// target data into the LHS target storage).
1089+
auto loadVal = builder->create<fir::LoadOp>(loc, rhs);
1090+
builder->create<fir::StoreOp>(loc, loadVal, lhs);
1091+
} else {
1092+
// Non ALLOCATABLE/POINTER variable. Simple DATA copy.
1093+
copyData(lhs, rhs);
1094+
}
1095+
}
1096+
1097+
void copyVarFIR(mlir::Location loc, const Fortran::semantics::Symbol &sym,
1098+
const Fortran::lower::SymbolBox &lhs_sb,
1099+
const Fortran::lower::SymbolBox &rhs_sb) {
1100+
assert(!lowerToHighLevelFIR());
1101+
fir::ExtendedValue lhs = symBoxToExtendedValue(lhs_sb);
1102+
fir::ExtendedValue rhs = symBoxToExtendedValue(rhs_sb);
1103+
mlir::Type symType = genType(sym);
1104+
if (auto seqTy = symType.dyn_cast<fir::SequenceType>()) {
1105+
Fortran::lower::StatementContext stmtCtx;
1106+
Fortran::lower::createSomeArrayAssignment(*this, lhs, rhs, localSymbols,
1107+
stmtCtx);
1108+
stmtCtx.finalizeAndReset();
1109+
} else if (lhs.getBoxOf<fir::CharBoxValue>()) {
1110+
fir::factory::CharacterExprHelper{*builder, loc}.createAssign(lhs, rhs);
1111+
} else {
1112+
auto loadVal = builder->create<fir::LoadOp>(loc, fir::getBase(rhs));
1113+
builder->create<fir::StoreOp>(loc, loadVal, fir::getBase(lhs));
1114+
}
1115+
}
1116+
10961117
/// Map a block argument to a result or dummy symbol. This is not the
10971118
/// definitive mapping. The specification expression have not been lowered
10981119
/// yet. The final mapping will be done using this pre-mapping in

flang/lib/Lower/OpenMP.cpp

Lines changed: 168 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "flang/Optimizer/Builder/BoxValue.h"
2323
#include "flang/Optimizer/Builder/FIRBuilder.h"
2424
#include "flang/Optimizer/Builder/Todo.h"
25+
#include "flang/Optimizer/Dialect/FIRType.h"
2526
#include "flang/Optimizer/HLFIR/HLFIROps.h"
2627
#include "flang/Parser/dump-parse-tree.h"
2728
#include "flang/Parser/parse-tree.h"
@@ -598,6 +599,10 @@ class ClauseProcessor {
598599
processAllocate(llvm::SmallVectorImpl<mlir::Value> &allocatorOperands,
599600
llvm::SmallVectorImpl<mlir::Value> &allocateOperands) const;
600601
bool processCopyin() const;
602+
bool processCopyPrivate(
603+
mlir::Location currentLocation,
604+
llvm::SmallVectorImpl<mlir::Value> &copyPrivateVars,
605+
llvm::SmallVectorImpl<mlir::Attribute> &copyPrivateFuncs) const;
601606
bool processDepend(llvm::SmallVectorImpl<mlir::Attribute> &dependTypeOperands,
602607
llvm::SmallVectorImpl<mlir::Value> &dependOperands) const;
603608
bool
@@ -1178,6 +1183,102 @@ class ReductionProcessor {
11781183
}
11791184
};
11801185

1186+
/// Class that extracts information from the specified type.
1187+
class TypeInfo {
1188+
public:
1189+
TypeInfo(mlir::Type ty) { typeScan(ty); }
1190+
1191+
// Returns the length of character types.
1192+
std::optional<fir::CharacterType::LenType> getCharLength() const {
1193+
return charLen;
1194+
}
1195+
1196+
// Returns the shape of array types.
1197+
const llvm::SmallVector<int64_t> &getShape() const { return shape; }
1198+
1199+
// Is the type inside a box?
1200+
bool isBox() const { return inBox; }
1201+
1202+
private:
1203+
void typeScan(mlir::Type type);
1204+
1205+
std::optional<fir::CharacterType::LenType> charLen;
1206+
llvm::SmallVector<int64_t> shape;
1207+
bool inBox = false;
1208+
};
1209+
1210+
void TypeInfo::typeScan(mlir::Type ty) {
1211+
if (auto sty = mlir::dyn_cast<fir::SequenceType>(ty)) {
1212+
assert(shape.empty() && !sty.getShape().empty());
1213+
shape = llvm::SmallVector<int64_t>(sty.getShape());
1214+
typeScan(sty.getEleTy());
1215+
} else if (auto bty = mlir::dyn_cast<fir::BoxType>(ty)) {
1216+
inBox = true;
1217+
typeScan(bty.getEleTy());
1218+
} else if (auto cty = mlir::dyn_cast<fir::CharacterType>(ty)) {
1219+
charLen = cty.getLen();
1220+
} else if (auto hty = mlir::dyn_cast<fir::HeapType>(ty)) {
1221+
typeScan(hty.getEleTy());
1222+
} else if (auto pty = mlir::dyn_cast<fir::PointerType>(ty)) {
1223+
typeScan(pty.getEleTy());
1224+
}
1225+
}
1226+
1227+
// Create a function that performs a copy between two variables, compatible
1228+
// with their types and attributes.
1229+
static mlir::func::FuncOp
1230+
createCopyFunc(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
1231+
mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) {
1232+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1233+
mlir::ModuleOp module = builder.getModule();
1234+
mlir::Type eleTy = mlir::cast<fir::ReferenceType>(varType).getEleTy();
1235+
TypeInfo typeInfo(eleTy);
1236+
std::string copyFuncName =
1237+
fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy");
1238+
1239+
if (auto decl = module.lookupSymbol<mlir::func::FuncOp>(copyFuncName))
1240+
return decl;
1241+
1242+
// create function
1243+
mlir::OpBuilder::InsertionGuard guard(builder);
1244+
mlir::OpBuilder modBuilder(module.getBodyRegion());
1245+
llvm::SmallVector<mlir::Type> argsTy = {varType, varType};
1246+
auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {});
1247+
mlir::func::FuncOp funcOp =
1248+
modBuilder.create<mlir::func::FuncOp>(loc, copyFuncName, funcType);
1249+
funcOp.setVisibility(mlir::SymbolTable::Visibility::Private);
1250+
builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy,
1251+
{loc, loc});
1252+
builder.setInsertionPointToStart(&funcOp.getRegion().back());
1253+
// generate body
1254+
fir::FortranVariableFlagsAttr attrs;
1255+
if (varAttrs != fir::FortranVariableFlagsEnum::None)
1256+
attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs);
1257+
llvm::SmallVector<mlir::Value> typeparams;
1258+
if (typeInfo.getCharLength().has_value()) {
1259+
mlir::Value charLen = builder.createIntegerConstant(
1260+
loc, builder.getCharacterLengthType(), *typeInfo.getCharLength());
1261+
typeparams.push_back(charLen);
1262+
}
1263+
mlir::Value shape;
1264+
if (!typeInfo.isBox() && !typeInfo.getShape().empty()) {
1265+
llvm::SmallVector<mlir::Value> extents;
1266+
for (auto extent : typeInfo.getShape())
1267+
extents.push_back(
1268+
builder.createIntegerConstant(loc, builder.getIndexType(), extent));
1269+
shape = builder.create<fir::ShapeOp>(loc, extents);
1270+
}
1271+
auto declDst = builder.create<hlfir::DeclareOp>(loc, funcOp.getArgument(0),
1272+
copyFuncName + "_dst", shape,
1273+
typeparams, attrs);
1274+
auto declSrc = builder.create<hlfir::DeclareOp>(loc, funcOp.getArgument(1),
1275+
copyFuncName + "_src", shape,
1276+
typeparams, attrs);
1277+
converter.copyVar(loc, declDst.getBase(), declSrc.getBase());
1278+
builder.create<mlir::func::ReturnOp>(loc);
1279+
return funcOp;
1280+
}
1281+
11811282
static mlir::omp::ScheduleModifier
11821283
translateScheduleModifier(const Fortran::parser::OmpScheduleModifierType &m) {
11831284
switch (m.v) {
@@ -1758,6 +1859,62 @@ bool ClauseProcessor::processCopyin() const {
17581859
return hasCopyin;
17591860
}
17601861

1862+
bool ClauseProcessor::processCopyPrivate(
1863+
mlir::Location currentLocation,
1864+
llvm::SmallVectorImpl<mlir::Value> &copyPrivateVars,
1865+
llvm::SmallVectorImpl<mlir::Attribute> &copyPrivateFuncs) const {
1866+
auto addCopyPrivateVar = [&](Fortran::semantics::Symbol *sym) {
1867+
mlir::Value symVal = converter.getSymbolAddress(*sym);
1868+
auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>();
1869+
if (!declOp)
1870+
fir::emitFatalError(currentLocation,
1871+
"COPYPRIVATE is supported only in HLFIR mode");
1872+
symVal = declOp.getBase();
1873+
mlir::Type symType = symVal.getType();
1874+
fir::FortranVariableFlagsEnum attrs =
1875+
declOp.getFortranAttrs().has_value()
1876+
? *declOp.getFortranAttrs()
1877+
: fir::FortranVariableFlagsEnum::None;
1878+
mlir::Value cpVar = symVal;
1879+
1880+
// CopyPrivate variables must be passed by reference. However, in the case
1881+
// of assumed shapes/vla the type is not a !fir.ref, but a !fir.box.
1882+
// In these cases to retrieve the appropriate !fir.ref<!fir.box<...>> to
1883+
// access the data we need we must perform an alloca and then store to it
1884+
// and retrieve the data from the new alloca.
1885+
if (mlir::isa<fir::BaseBoxType>(symType)) {
1886+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1887+
auto alloca = builder.create<fir::AllocaOp>(currentLocation, symType);
1888+
builder.create<fir::StoreOp>(currentLocation, symVal, alloca);
1889+
cpVar = alloca;
1890+
}
1891+
1892+
copyPrivateVars.push_back(cpVar);
1893+
mlir::func::FuncOp funcOp =
1894+
createCopyFunc(currentLocation, converter, cpVar.getType(), attrs);
1895+
copyPrivateFuncs.push_back(mlir::SymbolRefAttr::get(funcOp));
1896+
};
1897+
1898+
bool hasCopyPrivate = findRepeatableClause<ClauseTy::Copyprivate>(
1899+
[&](const ClauseTy::Copyprivate *copyPrivateClause,
1900+
const Fortran::parser::CharBlock &) {
1901+
const Fortran::parser::OmpObjectList &ompObjectList =
1902+
copyPrivateClause->v;
1903+
for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
1904+
Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
1905+
if (const auto *commonDetails =
1906+
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
1907+
for (const auto &mem : commonDetails->objects())
1908+
addCopyPrivateVar(&*mem);
1909+
break;
1910+
}
1911+
addCopyPrivateVar(sym);
1912+
}
1913+
});
1914+
1915+
return hasCopyPrivate;
1916+
}
1917+
17611918
bool ClauseProcessor::processDepend(
17621919
llvm::SmallVectorImpl<mlir::Attribute> &dependTypeOperands,
17631920
llvm::SmallVectorImpl<mlir::Value> &dependOperands) const {
@@ -2666,21 +2823,26 @@ genSingleOp(Fortran::lower::AbstractConverter &converter,
26662823
const Fortran::parser::OmpClauseList &endClauseList) {
26672824
llvm::SmallVector<mlir::Value> allocateOperands, allocatorOperands;
26682825
llvm::SmallVector<mlir::Value> copyPrivateVars;
2826+
llvm::SmallVector<mlir::Attribute> copyPrivateFuncs;
26692827
mlir::UnitAttr nowaitAttr;
26702828

26712829
ClauseProcessor cp(converter, semaCtx, beginClauseList);
26722830
cp.processAllocate(allocatorOperands, allocateOperands);
2673-
cp.processTODO<Fortran::parser::OmpClause::Copyprivate>(
2674-
currentLocation, llvm::omp::Directive::OMPD_single);
26752831

2676-
ClauseProcessor(converter, semaCtx, endClauseList).processNowait(nowaitAttr);
2832+
ClauseProcessor ecp(converter, semaCtx, endClauseList);
2833+
ecp.processNowait(nowaitAttr);
2834+
ecp.processCopyPrivate(currentLocation, copyPrivateVars, copyPrivateFuncs);
26772835

26782836
return genOpWithBody<mlir::omp::SingleOp>(
26792837
OpWithBodyGenInfo(converter, semaCtx, currentLocation, eval)
26802838
.setGenNested(genNested)
26812839
.setClauses(&beginClauseList),
26822840
allocateOperands, allocatorOperands, copyPrivateVars,
2683-
/*copyPrivateFuncs=*/nullptr, nowaitAttr);
2841+
copyPrivateFuncs.empty()
2842+
? nullptr
2843+
: mlir::ArrayAttr::get(converter.getFirOpBuilder().getContext(),
2844+
copyPrivateFuncs),
2845+
nowaitAttr);
26842846
}
26852847

26862848
static mlir::omp::TaskOp
@@ -3708,7 +3870,8 @@ genOMP(Fortran::lower::AbstractConverter &converter,
37083870

37093871
for (const auto &clause : endClauseList.v) {
37103872
mlir::Location clauseLocation = converter.genLocation(clause.source);
3711-
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
3873+
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u) &&
3874+
!std::get_if<Fortran::parser::OmpClause::Copyprivate>(&clause.u))
37123875
TODO(clauseLocation, "OpenMP Block construct clause");
37133876
}
37143877

0 commit comments

Comments
 (0)