Skip to content

Commit e50a231

Browse files
authored
[flang][OpenMP] Add support for copyprivate (#80485)
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 2c30180 commit e50a231

File tree

7 files changed

+418
-76
lines changed

7 files changed

+418
-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/ClauseProcessor.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,162 @@ bool ClauseProcessor::processCopyin() const {
609609
return hasCopyin;
610610
}
611611

612+
/// Class that extracts information from the specified type.
613+
class TypeInfo {
614+
public:
615+
TypeInfo(mlir::Type ty) { typeScan(ty); }
616+
617+
// Returns the length of character types.
618+
std::optional<fir::CharacterType::LenType> getCharLength() const {
619+
return charLen;
620+
}
621+
622+
// Returns the shape of array types.
623+
const llvm::SmallVector<int64_t> &getShape() const { return shape; }
624+
625+
// Is the type inside a box?
626+
bool isBox() const { return inBox; }
627+
628+
private:
629+
void typeScan(mlir::Type type);
630+
631+
std::optional<fir::CharacterType::LenType> charLen;
632+
llvm::SmallVector<int64_t> shape;
633+
bool inBox = false;
634+
};
635+
636+
void TypeInfo::typeScan(mlir::Type ty) {
637+
if (auto sty = mlir::dyn_cast<fir::SequenceType>(ty)) {
638+
assert(shape.empty() && !sty.getShape().empty());
639+
shape = llvm::SmallVector<int64_t>(sty.getShape());
640+
typeScan(sty.getEleTy());
641+
} else if (auto bty = mlir::dyn_cast<fir::BoxType>(ty)) {
642+
inBox = true;
643+
typeScan(bty.getEleTy());
644+
} else if (auto cty = mlir::dyn_cast<fir::CharacterType>(ty)) {
645+
charLen = cty.getLen();
646+
} else if (auto hty = mlir::dyn_cast<fir::HeapType>(ty)) {
647+
typeScan(hty.getEleTy());
648+
} else if (auto pty = mlir::dyn_cast<fir::PointerType>(ty)) {
649+
typeScan(pty.getEleTy());
650+
} else {
651+
// The scan ends when reaching any built-in or record type.
652+
assert(ty.isIntOrIndexOrFloat() || mlir::isa<fir::ComplexType>(ty) ||
653+
mlir::isa<fir::LogicalType>(ty) || mlir::isa<fir::RecordType>(ty));
654+
}
655+
}
656+
657+
// Create a function that performs a copy between two variables, compatible
658+
// with their types and attributes.
659+
static mlir::func::FuncOp
660+
createCopyFunc(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
661+
mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) {
662+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
663+
mlir::ModuleOp module = builder.getModule();
664+
mlir::Type eleTy = mlir::cast<fir::ReferenceType>(varType).getEleTy();
665+
TypeInfo typeInfo(eleTy);
666+
std::string copyFuncName =
667+
fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy");
668+
669+
if (auto decl = module.lookupSymbol<mlir::func::FuncOp>(copyFuncName))
670+
return decl;
671+
672+
// create function
673+
mlir::OpBuilder::InsertionGuard guard(builder);
674+
mlir::OpBuilder modBuilder(module.getBodyRegion());
675+
llvm::SmallVector<mlir::Type> argsTy = {varType, varType};
676+
auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {});
677+
mlir::func::FuncOp funcOp =
678+
modBuilder.create<mlir::func::FuncOp>(loc, copyFuncName, funcType);
679+
funcOp.setVisibility(mlir::SymbolTable::Visibility::Private);
680+
builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy,
681+
{loc, loc});
682+
builder.setInsertionPointToStart(&funcOp.getRegion().back());
683+
// generate body
684+
fir::FortranVariableFlagsAttr attrs;
685+
if (varAttrs != fir::FortranVariableFlagsEnum::None)
686+
attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs);
687+
llvm::SmallVector<mlir::Value> typeparams;
688+
if (typeInfo.getCharLength().has_value()) {
689+
mlir::Value charLen = builder.createIntegerConstant(
690+
loc, builder.getCharacterLengthType(), *typeInfo.getCharLength());
691+
typeparams.push_back(charLen);
692+
}
693+
mlir::Value shape;
694+
if (!typeInfo.isBox() && !typeInfo.getShape().empty()) {
695+
llvm::SmallVector<mlir::Value> extents;
696+
for (auto extent : typeInfo.getShape())
697+
extents.push_back(
698+
builder.createIntegerConstant(loc, builder.getIndexType(), extent));
699+
shape = builder.create<fir::ShapeOp>(loc, extents);
700+
}
701+
auto declDst = builder.create<hlfir::DeclareOp>(loc, funcOp.getArgument(0),
702+
copyFuncName + "_dst", shape,
703+
typeparams, attrs);
704+
auto declSrc = builder.create<hlfir::DeclareOp>(loc, funcOp.getArgument(1),
705+
copyFuncName + "_src", shape,
706+
typeparams, attrs);
707+
converter.copyVar(loc, declDst.getBase(), declSrc.getBase());
708+
builder.create<mlir::func::ReturnOp>(loc);
709+
return funcOp;
710+
}
711+
712+
bool ClauseProcessor::processCopyPrivate(
713+
mlir::Location currentLocation,
714+
llvm::SmallVectorImpl<mlir::Value> &copyPrivateVars,
715+
llvm::SmallVectorImpl<mlir::Attribute> &copyPrivateFuncs) const {
716+
auto addCopyPrivateVar = [&](Fortran::semantics::Symbol *sym) {
717+
mlir::Value symVal = converter.getSymbolAddress(*sym);
718+
auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>();
719+
if (!declOp)
720+
fir::emitFatalError(currentLocation,
721+
"COPYPRIVATE is supported only in HLFIR mode");
722+
symVal = declOp.getBase();
723+
mlir::Type symType = symVal.getType();
724+
fir::FortranVariableFlagsEnum attrs =
725+
declOp.getFortranAttrs().has_value()
726+
? *declOp.getFortranAttrs()
727+
: fir::FortranVariableFlagsEnum::None;
728+
mlir::Value cpVar = symVal;
729+
730+
// CopyPrivate variables must be passed by reference. However, in the case
731+
// of assumed shapes/vla the type is not a !fir.ref, but a !fir.box.
732+
// In these cases to retrieve the appropriate !fir.ref<!fir.box<...>> to
733+
// access the data we need we must perform an alloca and then store to it
734+
// and retrieve the data from the new alloca.
735+
if (mlir::isa<fir::BaseBoxType>(symType)) {
736+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
737+
auto alloca = builder.create<fir::AllocaOp>(currentLocation, symType);
738+
builder.create<fir::StoreOp>(currentLocation, symVal, alloca);
739+
cpVar = alloca;
740+
}
741+
742+
copyPrivateVars.push_back(cpVar);
743+
mlir::func::FuncOp funcOp =
744+
createCopyFunc(currentLocation, converter, cpVar.getType(), attrs);
745+
copyPrivateFuncs.push_back(mlir::SymbolRefAttr::get(funcOp));
746+
};
747+
748+
bool hasCopyPrivate = findRepeatableClause<ClauseTy::Copyprivate>(
749+
[&](const ClauseTy::Copyprivate *copyPrivateClause,
750+
const Fortran::parser::CharBlock &) {
751+
const Fortran::parser::OmpObjectList &ompObjectList =
752+
copyPrivateClause->v;
753+
for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
754+
Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
755+
if (const auto *commonDetails =
756+
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
757+
for (const auto &mem : commonDetails->objects())
758+
addCopyPrivateVar(&*mem);
759+
break;
760+
}
761+
addCopyPrivateVar(sym);
762+
}
763+
});
764+
765+
return hasCopyPrivate;
766+
}
767+
612768
bool ClauseProcessor::processDepend(
613769
llvm::SmallVectorImpl<mlir::Attribute> &dependTypeOperands,
614770
llvm::SmallVectorImpl<mlir::Value> &dependOperands) const {

flang/lib/Lower/OpenMP/ClauseProcessor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ class ClauseProcessor {
9595
processAllocate(llvm::SmallVectorImpl<mlir::Value> &allocatorOperands,
9696
llvm::SmallVectorImpl<mlir::Value> &allocateOperands) const;
9797
bool processCopyin() const;
98+
bool processCopyPrivate(
99+
mlir::Location currentLocation,
100+
llvm::SmallVectorImpl<mlir::Value> &copyPrivateVars,
101+
llvm::SmallVectorImpl<mlir::Attribute> &copyPrivateFuncs) const;
98102
bool processDepend(llvm::SmallVectorImpl<mlir::Attribute> &dependTypeOperands,
99103
llvm::SmallVectorImpl<mlir::Value> &dependOperands) const;
100104
bool

flang/lib/Lower/OpenMP/OpenMP.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "flang/Optimizer/Builder/BoxValue.h"
2626
#include "flang/Optimizer/Builder/FIRBuilder.h"
2727
#include "flang/Optimizer/Builder/Todo.h"
28+
#include "flang/Optimizer/Dialect/FIRType.h"
2829
#include "flang/Optimizer/HLFIR/HLFIROps.h"
2930
#include "flang/Parser/parse-tree.h"
3031
#include "flang/Semantics/openmp-directive-sets.h"
@@ -639,21 +640,26 @@ genSingleOp(Fortran::lower::AbstractConverter &converter,
639640
const Fortran::parser::OmpClauseList &endClauseList) {
640641
llvm::SmallVector<mlir::Value> allocateOperands, allocatorOperands;
641642
llvm::SmallVector<mlir::Value> copyPrivateVars;
643+
llvm::SmallVector<mlir::Attribute> copyPrivateFuncs;
642644
mlir::UnitAttr nowaitAttr;
643645

644646
ClauseProcessor cp(converter, semaCtx, beginClauseList);
645647
cp.processAllocate(allocatorOperands, allocateOperands);
646-
cp.processTODO<Fortran::parser::OmpClause::Copyprivate>(
647-
currentLocation, llvm::omp::Directive::OMPD_single);
648648

649-
ClauseProcessor(converter, semaCtx, endClauseList).processNowait(nowaitAttr);
649+
ClauseProcessor ecp(converter, semaCtx, endClauseList);
650+
ecp.processNowait(nowaitAttr);
651+
ecp.processCopyPrivate(currentLocation, copyPrivateVars, copyPrivateFuncs);
650652

651653
return genOpWithBody<mlir::omp::SingleOp>(
652654
OpWithBodyGenInfo(converter, semaCtx, currentLocation, eval)
653655
.setGenNested(genNested)
654656
.setClauses(&beginClauseList),
655657
allocateOperands, allocatorOperands, copyPrivateVars,
656-
/*copyPrivateFuncs=*/nullptr, nowaitAttr);
658+
copyPrivateFuncs.empty()
659+
? nullptr
660+
: mlir::ArrayAttr::get(converter.getFirOpBuilder().getContext(),
661+
copyPrivateFuncs),
662+
nowaitAttr);
657663
}
658664

659665
static mlir::omp::TaskOp
@@ -1689,7 +1695,8 @@ genOMP(Fortran::lower::AbstractConverter &converter,
16891695

16901696
for (const auto &clause : endClauseList.v) {
16911697
mlir::Location clauseLocation = converter.genLocation(clause.source);
1692-
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
1698+
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u) &&
1699+
!std::get_if<Fortran::parser::OmpClause::Copyprivate>(&clause.u))
16931700
TODO(clauseLocation, "OpenMP Block construct clause");
16941701
}
16951702

0 commit comments

Comments
 (0)