[flang-commits] [flang] [flang][OpenMP] Lower cross-module operator declare reduction (PR #207493)
Sairudra More via flang-commits
flang-commits at lists.llvm.org
Wed Jul 8 01:06:54 PDT 2026
================
@@ -2419,4 +2422,140 @@ std::optional<DynamicUserCondition> MakeVariantMatchInfo(
}
return dynamicCond;
}
+
+// User-defined reduction resolution, shared between the OpenMP semantic checks
+// and lowering. The two public entry points (FindUserReductionSymbol and
+// FindOperatorUserReductionSymbol) return the resolved (non-ultimate) reduction
+// symbol; the caller reads its UserReductionDetails.
+
+// Compute the mangled reduction name to look up in a reduction's source module.
+// If the operator was renamed on import (e.g. USE m, ONLY: operator(.local.) =>
+// operator(.remote.)), the local mangled name will not match in the source
+// module; re-derive the lookup name from the source operator's ultimate name.
+// Only defined operators can be renamed (intrinsic operators and named
+// reductions cannot), so a detected rename always has a ".op." source name.
+// For non-renamed lookups the original mangled name is returned unchanged.
+static std::string SourceReductionName(const parser::CharBlock &mangledName,
+ const parser::CharBlock &localName, const parser::CharBlock &sourceName) {
+ if (sourceName != localName && sourceName.size() >= 3 &&
+ sourceName.front() == '.' && sourceName.back() == '.') {
+ return MangleDefinedOperator(sourceName);
+ }
+ return mangledName.ToString();
+}
+
+// Return the reduction details of `symbol` if it is a user reduction that
+// supports `type` (any type when `type` is null).
+static const UserReductionDetails *AcceptReduction(
+ const Symbol &symbol, const DeclTypeSpec *type) {
+ const auto *details{symbol.GetUltimate().detailsIf<UserReductionDetails>()};
+ if (details && (!type || details->SupportsType(*type))) {
+ return details;
+ }
+ return nullptr;
+}
+
+// A reduction symbol is locally declared (authoritative) when it is not reached
+// through any USE association, even via host association. Such a reduction
+// shadows reductions imported or reachable through its operator.
+static bool IsLocalReduction(const Symbol &symbol) {
+ const Symbol *s{&symbol};
+ while (const auto *host{s->detailsIf<HostAssocDetails>()}) {
+ s = &host->symbol();
+ }
+ return !s->detailsIf<UseDetails>();
+}
+
+// Search for a user reduction supporting `type` by following the operator/
+// procedure symbol `opSym` through its USE associations and merged generic
+// sources. Each module the operator passes through is checked for a (possibly
+// renamed) reduction; `localName` is the operator name written at the use site,
+// used to detect renames. A locally declared reduction in a module is
+// authoritative: it is returned if it supports the type, otherwise it shadows
+// reductions reachable further along that branch.
+static const Symbol *SearchOperatorReduction(const Symbol &opSym,
+ const parser::CharBlock &mangledName, const parser::CharBlock &localName,
+ const DeclTypeSpec *type, llvm::SmallPtrSetImpl<const Symbol *> &visited) {
+ if (!visited.insert(&opSym).second) {
+ return nullptr;
+ }
+ const Scope &scope{opSym.owner()};
+ if (scope.kind() == Scope::Kind::Module) {
+ std::string lookupName{
+ SourceReductionName(mangledName, localName, opSym.name())};
+ auto it{scope.find(parser::CharBlock{lookupName})};
+ if (it != scope.end()) {
+ const Symbol &reductionSym{*it->second};
+ const Symbol &reductionUltimate{reductionSym.GetUltimate()};
+ if (!reductionUltimate.attrs().test(Attr::PRIVATE)) {
+ if (AcceptReduction(reductionUltimate, type)) {
+ return &reductionSym;
+ }
+ // A locally declared reduction here shadows reductions reachable
+ // further along this branch.
+ if (reductionUltimate.detailsIf<UserReductionDetails>() &&
+ IsLocalReduction(reductionSym)) {
+ return nullptr;
+ }
+ }
+ }
+ }
+ // Follow a USE-associated operator to the module it was imported from.
+ if (const auto *use{opSym.detailsIf<UseDetails>()}) {
+ return SearchOperatorReduction(
+ use->symbol(), mangledName, localName, type, visited);
+ }
+ // Search each module merged into a generic operator (recursing through
+ // re-exporting facade modules).
+ if (const auto *generic{opSym.detailsIf<GenericDetails>()}) {
+ for (const Symbol &useSym : generic->uses()) {
+ if (const Symbol *result{SearchOperatorReduction(
----------------
Saieiei wrote:
Thanks, this addresses my concern. Collecting all matches and diagnosing the ambiguous case before lowering looks right to me.
https://github.com/llvm/llvm-project/pull/207493
More information about the flang-commits
mailing list