-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[Flang][OpenMP] Make implicitly captured scalars fully firstprivatized #147442
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
#include "flang/Semantics/openmp-modifiers.h" | ||
#include "flang/Semantics/symbol.h" | ||
#include "flang/Semantics/tools.h" | ||
#include "flang/Support/Flags.h" | ||
#include "llvm/Frontend/OpenMP/OMP.h.inc" | ||
#include "llvm/Support/Debug.h" | ||
#include <list> | ||
|
@@ -56,6 +57,10 @@ template <typename T> class DirectiveAttributeVisitor { | |
Scope &scope; | ||
Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC | ||
std::map<const Symbol *, Symbol::Flag> objectWithDSA; | ||
std::map<parser::OmpVariableCategory::Value, | ||
parser::OmpDefaultmapClause::ImplicitBehavior> | ||
defaultMap; | ||
|
||
bool withinConstruct{false}; | ||
std::int64_t associatedLoopLevel{0}; | ||
}; | ||
|
@@ -80,6 +85,10 @@ template <typename T> class DirectiveAttributeVisitor { | |
GetContext().directiveSource = dir; | ||
} | ||
Scope &currScope() { return GetContext().scope; } | ||
void AddContextDefaultmapBehaviour(parser::OmpVariableCategory::Value VarCat, | ||
parser::OmpDefaultmapClause::ImplicitBehavior ImpBehav) { | ||
GetContext().defaultMap[VarCat] = ImpBehav; | ||
} | ||
void SetContextDefaultDSA(Symbol::Flag flag) { | ||
GetContext().defaultDSA = flag; | ||
} | ||
|
@@ -557,6 +566,7 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> { | |
ResolveOmpObjectList(x.v, Symbol::Flag::OmpExclusiveScan); | ||
return false; | ||
} | ||
void Post(const parser::OmpClause::Defaultmap &); | ||
void Post(const parser::OmpDefaultClause &); | ||
bool Pre(const parser::OmpClause::Shared &x) { | ||
ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared); | ||
|
@@ -810,6 +820,11 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> { | |
Symbol::Flag::OmpLastPrivate, Symbol::Flag::OmpReduction, | ||
Symbol::Flag::OmpLinear}; | ||
|
||
Symbol::Flags dataMappingAttributeFlags{Symbol::Flag::OmpMapTo, | ||
Symbol::Flag::OmpMapFrom, Symbol::Flag::OmpMapToFrom, | ||
Symbol::Flag::OmpMapStorage, Symbol::Flag::OmpMapDelete, | ||
Symbol::Flag::OmpIsDevicePtr, Symbol::Flag::OmpHasDeviceAddr}; | ||
|
||
Symbol::Flags privateDataSharingAttributeFlags{Symbol::Flag::OmpPrivate, | ||
Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate}; | ||
|
||
|
@@ -2214,6 +2229,28 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPAllocatorsConstruct &x) { | |
return true; | ||
} | ||
|
||
void OmpAttributeVisitor::Post(const parser::OmpClause::Defaultmap &x) { | ||
using ImplicitBehavior = parser::OmpDefaultmapClause::ImplicitBehavior; | ||
using VariableCategory = parser::OmpVariableCategory; | ||
|
||
VariableCategory::Value varCategory; | ||
ImplicitBehavior impBehavior; | ||
|
||
if (!dirContext_.empty()) { | ||
impBehavior = std::get<ImplicitBehavior>(x.v.t); | ||
|
||
auto &modifiers{OmpGetModifiers(x.v)}; | ||
auto *maybeCategory{ | ||
OmpGetUniqueModifier<parser::OmpVariableCategory>(modifiers)}; | ||
if (maybeCategory) | ||
varCategory = maybeCategory->v; | ||
else | ||
varCategory = VariableCategory::Value::All; | ||
|
||
AddContextDefaultmapBehaviour(varCategory, impBehavior); | ||
} | ||
} | ||
|
||
void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { | ||
// The DEFAULT clause may also be used on METADIRECTIVE. In that case | ||
// there is nothing to do. | ||
|
@@ -2365,6 +2402,70 @@ static bool IsSymbolStaticStorageDuration(const Symbol &symbol) { | |
(ultSym.flags().test(Symbol::Flag::InCommonBlock)); | ||
} | ||
|
||
static bool IsTargetCaptureImplicitlyFirstprivatizeable(const Symbol &symbol, | ||
const Symbol::Flags &dsa, const Symbol::Flags &dataSharingAttributeFlags, | ||
const Symbol::Flags &dataMappingAttributeFlags, | ||
std::map<parser::OmpVariableCategory::Value, | ||
parser::OmpDefaultmapClause::ImplicitBehavior> | ||
defaultMap) { | ||
// If a Defaultmap clause is present for the current target scope, and it has | ||
// specified behaviour other than Firstprivate for scalars then we exit early, | ||
// as it overrides the implicit Firstprivatization of scalars OpenMP rule. | ||
if (!defaultMap.empty()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. < minor >: Could you please add a comment here to suggest that we exit early if we are dealing with a scalar and the defaultmap clause has set the implicit mapping behavior to something other than firstprivate. Helps readability because the compound conditional is quite long. |
||
if (llvm::is_contained( | ||
defaultMap, parser::OmpVariableCategory::Value::All) && | ||
defaultMap[parser::OmpVariableCategory::Value::All] != | ||
parser::OmpDefaultmapClause::ImplicitBehavior::Firstprivate) { | ||
return false; | ||
} | ||
|
||
if (llvm::is_contained( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check seems to subsume the check above. If that's correct, then we should lead with this check rather than the one before. |
||
defaultMap, parser::OmpVariableCategory::Value::Scalar) && | ||
defaultMap[parser::OmpVariableCategory::Value::Scalar] != | ||
parser::OmpDefaultmapClause::ImplicitBehavior::Firstprivate) { | ||
return false; | ||
} | ||
} | ||
|
||
auto checkSymbol = [&](const Symbol &checkSym) { | ||
// if we're associated with any other flags we skip implicit privitization | ||
// for now. If we're an allocatable, pointer or declare target, we're not | ||
// implicitly firstprivitizeable under OpenMP restrictions. | ||
// TODO: Relax restriction as we progress privitization and further | ||
// investigate the flags we can intermix with. | ||
if (!(dsa & (dataSharingAttributeFlags | dataMappingAttributeFlags)) | ||
.none() || | ||
!checkSym.flags().none() || semantics::IsAssumedShape(checkSym) || | ||
semantics::IsAllocatableOrPointer(checkSym)) { | ||
return false; | ||
} | ||
|
||
// It is default firstprivatizeable as far as the OpenMP specification is | ||
// concerned if it is a non-array scalar type that has been implicitly | ||
// captured in a target region | ||
const auto *type{checkSym.GetType()}; | ||
if ((!checkSym.GetShape() || checkSym.GetShape()->empty()) && | ||
(type->category() == | ||
Fortran::semantics::DeclTypeSpec::Category::Numeric || | ||
type->category() == | ||
Fortran::semantics::DeclTypeSpec::Category::Logical || | ||
type->category() == | ||
Fortran::semantics::DeclTypeSpec::Category::Character)) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
|
||
if (checkSymbol(symbol)) { | ||
const auto *hostAssoc{symbol.detailsIf<HostAssocDetails>()}; | ||
if (hostAssoc) { | ||
return checkSymbol(hostAssoc->symbol()); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
void OmpAttributeVisitor::CreateImplicitSymbols(const Symbol *symbol) { | ||
if (!IsPrivatizable(symbol)) { | ||
return; | ||
|
@@ -2456,7 +2557,7 @@ void OmpAttributeVisitor::CreateImplicitSymbols(const Symbol *symbol) { | |
|
||
bool taskGenDir = llvm::omp::taskGeneratingSet.test(dirContext.directive); | ||
bool targetDir = llvm::omp::allTargetSet.test(dirContext.directive); | ||
bool parallelDir = llvm::omp::allParallelSet.test(dirContext.directive); | ||
bool parallelDir = llvm::omp::topParallelSet.test(dirContext.directive); | ||
bool teamsDir = llvm::omp::allTeamsSet.test(dirContext.directive); | ||
bool isStaticStorageDuration = IsSymbolStaticStorageDuration(*symbol); | ||
|
||
|
@@ -2512,8 +2613,19 @@ void OmpAttributeVisitor::CreateImplicitSymbols(const Symbol *symbol) { | |
useLastDeclSymbol(); | ||
PRINT_IMPLICIT_RULE("3) enclosing context"); | ||
} else if (targetDir) { | ||
// TODO 4) not mapped target variable -> firstprivate | ||
// 4) not mapped target variable -> firstprivate | ||
// - i.e. implicit, but meets OpenMP specification rules for | ||
// firstprivate "promotion" | ||
if (enableDelayedPrivatizationStaging && | ||
IsTargetCaptureImplicitlyFirstprivatizeable(*symbol, prevDSA, | ||
dataSharingAttributeFlags, dataMappingAttributeFlags, | ||
dirContext.defaultMap)) { | ||
prevDSA.set(Symbol::Flag::OmpImplicit); | ||
prevDSA.set(Symbol::Flag::OmpFirstPrivate); | ||
makeSymbol(prevDSA); | ||
} | ||
luporl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
dsa = prevDSA; | ||
PRINT_IMPLICIT_RULE("4) not mapped target variable -> firstprivate"); | ||
} else if (taskGenDir) { | ||
// TODO 5) dummy arg in orphaned taskgen construct -> firstprivate | ||
if (prevDSA.test(Symbol::Flag::OmpShared) || | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, a nit really. But, doens't this second check subsume the check above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be equivalent due to the checkSymbol call at L2500, but without it I don't think it is, since we have L2500 though I'll see if removing it works fine! Thank you for spotting that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, that's exactly what I meant. I meant to mark this comment on L2500 instead of L2489.