Skip to content

Commit 95131ec

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: llvm#73128
1 parent 9978f6a commit 95131ec

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
@@ -1681,7 +1687,8 @@ genOMP(Fortran::lower::AbstractConverter &converter,
16811687

16821688
for (const auto &clause : endClauseList.v) {
16831689
mlir::Location clauseLocation = converter.genLocation(clause.source);
1684-
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
1690+
if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u) &&
1691+
!std::get_if<Fortran::parser::OmpClause::Copyprivate>(&clause.u))
16851692
TODO(clauseLocation, "OpenMP Block construct clause");
16861693
}
16871694

0 commit comments

Comments
 (0)