Skip to content

Commit 06f0aa9

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 35da536 commit 06f0aa9

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
@@ -120,6 +120,9 @@ class AbstractConverter {
120120
const Fortran::semantics::Symbol &sym,
121121
mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) = 0;
122122

123+
virtual void copyVar(mlir::Location loc, mlir::Value dst,
124+
mlir::Value src) = 0;
125+
123126
/// For a given symbol, check if it is present in the inner-most
124127
/// level of the symbol map.
125128
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
@@ -743,6 +743,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
743743
});
744744
}
745745

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

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

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

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

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

1844+
bool ClauseProcessor::processCopyPrivate(
1845+
mlir::Location currentLocation,
1846+
llvm::SmallVectorImpl<mlir::Value> &copyPrivateVars,
1847+
llvm::SmallVectorImpl<mlir::Attribute> &copyPrivateFuncs) const {
1848+
auto addCopyPrivateVar = [&](Fortran::semantics::Symbol *sym) {
1849+
mlir::Value symVal = converter.getSymbolAddress(*sym);
1850+
auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>();
1851+
if (!declOp)
1852+
fir::emitFatalError(currentLocation,
1853+
"COPYPRIVATE is supported only in HLFIR mode");
1854+
symVal = declOp.getBase();
1855+
mlir::Type symType = symVal.getType();
1856+
fir::FortranVariableFlagsEnum attrs =
1857+
declOp.getFortranAttrs().has_value()
1858+
? *declOp.getFortranAttrs()
1859+
: fir::FortranVariableFlagsEnum::None;
1860+
mlir::Value cpVar = symVal;
1861+
1862+
// CopyPrivate variables must be passed by reference. However, in the case
1863+
// of assumed shapes/vla the type is not a !fir.ref, but a !fir.box.
1864+
// In these cases to retrieve the appropriate !fir.ref<!fir.box<...>> to
1865+
// access the data we need we must perform an alloca and then store to it
1866+
// and retrieve the data from the new alloca.
1867+
if (mlir::isa<fir::BaseBoxType>(symType)) {
1868+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1869+
auto alloca = builder.create<fir::AllocaOp>(currentLocation, symType);
1870+
builder.create<fir::StoreOp>(currentLocation, symVal, alloca);
1871+
cpVar = alloca;
1872+
}
1873+
1874+
copyPrivateVars.push_back(cpVar);
1875+
mlir::func::FuncOp funcOp =
1876+
createCopyFunc(currentLocation, converter, cpVar.getType(), attrs);
1877+
copyPrivateFuncs.push_back(mlir::SymbolRefAttr::get(funcOp));
1878+
};
1879+
1880+
bool hasCopyPrivate = findRepeatableClause<ClauseTy::Copyprivate>(
1881+
[&](const ClauseTy::Copyprivate *copyPrivateClause,
1882+
const Fortran::parser::CharBlock &) {
1883+
const Fortran::parser::OmpObjectList &ompObjectList =
1884+
copyPrivateClause->v;
1885+
for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
1886+
Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
1887+
if (const auto *commonDetails =
1888+
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
1889+
for (const auto &mem : commonDetails->objects())
1890+
addCopyPrivateVar(&*mem);
1891+
break;
1892+
}
1893+
addCopyPrivateVar(sym);
1894+
}
1895+
});
1896+
1897+
return hasCopyPrivate;
1898+
}
1899+
17431900
bool ClauseProcessor::processDepend(
17441901
llvm::SmallVectorImpl<mlir::Attribute> &dependTypeOperands,
17451902
llvm::SmallVectorImpl<mlir::Value> &dependOperands) const {
@@ -2482,19 +2639,24 @@ genSingleOp(Fortran::lower::AbstractConverter &converter,
24822639
const Fortran::parser::OmpClauseList &endClauseList) {
24832640
llvm::SmallVector<mlir::Value> allocateOperands, allocatorOperands;
24842641
llvm::SmallVector<mlir::Value> copyPrivateVars;
2642+
llvm::SmallVector<mlir::Attribute> copyPrivateFuncs;
24852643
mlir::UnitAttr nowaitAttr;
24862644

24872645
ClauseProcessor cp(converter, beginClauseList);
24882646
cp.processAllocate(allocatorOperands, allocateOperands);
2489-
cp.processTODO<Fortran::parser::OmpClause::Copyprivate>(
2490-
currentLocation, llvm::omp::Directive::OMPD_single);
24912647

2492-
ClauseProcessor(converter, endClauseList).processNowait(nowaitAttr);
2648+
ClauseProcessor ecp(converter, endClauseList);
2649+
ecp.processNowait(nowaitAttr);
2650+
ecp.processCopyPrivate(currentLocation, copyPrivateVars, copyPrivateFuncs);
24932651

24942652
return genOpWithBody<mlir::omp::SingleOp>(
24952653
converter, eval, genNested, currentLocation,
24962654
/*outerCombined=*/false, &beginClauseList, allocateOperands,
2497-
allocatorOperands, copyPrivateVars, /*copyPrivateFuncs=*/nullptr,
2655+
allocatorOperands, copyPrivateVars,
2656+
copyPrivateFuncs.empty()
2657+
? nullptr
2658+
: mlir::ArrayAttr::get(converter.getFirOpBuilder().getContext(),
2659+
copyPrivateFuncs),
24982660
nowaitAttr);
24992661
}
25002662

@@ -3369,7 +3531,8 @@ genOMP(Fortran::lower::AbstractConverter &converter,
33693531

33703532
for (const auto &clause : endClauseList.v) {
33713533
mlir::Location clauseLocation = converter.genLocation(clause.source);
3372-
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
3534+
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u) &&
3535+
!std::get_if<Fortran::parser::OmpClause::Copyprivate>(&clause.u))
33733536
TODO(clauseLocation, "OpenMP Block construct clause");
33743537
}
33753538

0 commit comments

Comments
 (0)