diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10725,6 +10725,13 @@ /// constructs. VarDecl *isOpenMPCapturedDecl(ValueDecl *D, bool CheckScopeInfo = false, unsigned StopAt = 0); + + /// The member expression(this->fd) needs to be rebuilt in the template + /// instantiation to generate private copy for OpenMP when default + /// clause is used. The function will return true if default + /// cluse is used. + bool isOpenMPRebuildMemberExpr(ValueDecl *D); + ExprResult getOpenMPCapturedExpr(VarDecl *Capture, ExprValueKind VK, ExprObjectKind OK, SourceLocation Loc); diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -196,6 +196,22 @@ llvm::DenseSet> UsedInScanDirective; llvm::DenseMap, UsesAllocatorsDeclKind> UsesAllocatorsDecls; + /// Data is required on creating capture fields for implicit + /// default first|private clause. + struct ImplicitDefaultFDInfoTy { + /// Field decl. + const FieldDecl *FD = nullptr; + /// Nesting stack level + size_t StackLevel = 0; + /// Capture variable decl. + VarDecl *VD = nullptr; + ImplicitDefaultFDInfoTy(const FieldDecl *FD, size_t StackLevel, + VarDecl *VD) + : FD(FD), StackLevel(StackLevel), VD(VD) {} + }; + /// List of captured fields + llvm::SmallVector + ImplicitDefaultFirstprivateFDs; Expr *DeclareMapperVar = nullptr; SharingMapTy(OpenMPDirectiveKind DKind, DeclarationNameInfo Name, Scope *CurScope, SourceLocation Loc) @@ -577,7 +593,9 @@ /// predicate. const DSAVarData hasDSA(ValueDecl *D, - const llvm::function_ref CPred, + const llvm::function_ref + CPred, const llvm::function_ref DPred, bool FromParent) const; /// Checks if the specified variables has data-sharing attributes which @@ -1120,6 +1138,52 @@ const SharingMapTy *Top = getTopOfStackOrNull(); return Top ? Top->DeclareMapperVar : nullptr; } + /// get captured field from ImplicitDefaultFirstprivateFDs + VarDecl *getImplicitFDCapExprDecl(const FieldDecl *FD) const { + const_iterator I = begin(); + const_iterator EndI = end(); + size_t StackLevel = getStackSize(); + for (; I != EndI; ++I) { + if (I->DefaultAttr == DSA_firstprivate || I->DefaultAttr == DSA_private) + break; + StackLevel--; + } + assert((StackLevel > 0 && I != EndI) || (StackLevel == 0 && I == EndI)); + if (I == EndI) + return nullptr; + for (const auto &IFD : I->ImplicitDefaultFirstprivateFDs) + if (IFD.FD == FD && IFD.StackLevel == StackLevel) + return IFD.VD; + return nullptr; + } + /// Check if capture decl is field captured in ImplicitDefaultFirstprivateFDs + bool isImplicitDefaultFirstprivateFD(VarDecl *VD) const { + const_iterator I = begin(); + const_iterator EndI = end(); + for (; I != EndI; ++I) + if (I->DefaultAttr == DSA_firstprivate || I->DefaultAttr == DSA_private) + break; + if (I == EndI) + return false; + for (const auto &IFD : I->ImplicitDefaultFirstprivateFDs) + if (IFD.VD == VD) + return true; + return false; + } + /// Store capture FD info in ImplicitDefaultFirstprivateFDs + void addImplicitDefaultFirstprivateFD(const FieldDecl *FD, VarDecl *VD) { + iterator I = begin(); + const_iterator EndI = end(); + size_t StackLevel = getStackSize(); + for (; I != EndI; ++I) { + if (I->DefaultAttr == DSA_private || I->DefaultAttr == DSA_firstprivate) { + I->ImplicitDefaultFirstprivateFDs.emplace_back(FD, StackLevel, VD); + break; + } + StackLevel--; + } + assert((StackLevel > 0 && I != EndI) || (StackLevel == 0 && I == EndI)); + } }; bool isImplicitTaskingRegion(OpenMPDirectiveKind DKind) { @@ -1815,7 +1879,9 @@ const DSAStackTy::DSAVarData DSAStackTy::hasDSA(ValueDecl *D, - const llvm::function_ref CPred, + const llvm::function_ref + CPred, const llvm::function_ref DPred, bool FromParent) const { if (isStackEmpty()) @@ -1831,7 +1897,7 @@ continue; const_iterator NewI = I; DSAVarData DVar = getDSA(NewI, D); - if (I == NewI && CPred(DVar.CKind, DVar.AppliedToPointee)) + if (I == NewI && CPred(DVar.CKind, DVar.AppliedToPointee, I->DefaultAttr)) return DVar; } return {}; @@ -2203,6 +2269,26 @@ false); } +bool Sema::isOpenMPRebuildMemberExpr(ValueDecl *D) { + DSAStackTy::DSAVarData DVarPrivate = DSAStack->hasDSA( + D, + [](OpenMPClauseKind C, bool AppliedToPointee, + DefaultDataSharingAttributes DefaultAttr) { + return isOpenMPPrivate(C) && !AppliedToPointee && + (DefaultAttr == DSA_firstprivate || DefaultAttr == DSA_private); + }, + [](OpenMPDirectiveKind) { return true; }, + DSAStack->isClauseParsingMode()); + if (DVarPrivate.CKind != OMPC_unknown) + return true; + return false; +} + +static OMPCapturedExprDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id, + Expr *CaptureExpr, bool WithInit, + DeclContext *CurContext, + bool AsExpression); + VarDecl *Sema::isOpenMPCapturedDecl(ValueDecl *D, bool CheckScopeInfo, unsigned StopAt) { assert(LangOpts.OpenMP && "OpenMP is not allowed"); @@ -2301,7 +2387,7 @@ // default(none) clause and not used in any clause. DSAStackTy::DSAVarData DVarPrivate = DSAStack->hasDSA( D, - [](OpenMPClauseKind C, bool AppliedToPointee) { + [](OpenMPClauseKind C, bool AppliedToPointee, bool) { return isOpenMPPrivate(C) && !AppliedToPointee; }, [](OpenMPDirectiveKind) { return true; }, @@ -2313,6 +2399,45 @@ DSAStack->getDefaultDSA() != DSA_firstprivate) || DVarTop.CKind == OMPC_shared)) return nullptr; + auto *FD = dyn_cast(D); + if (DVarPrivate.CKind != OMPC_unknown && !VD && FD && + !DVarPrivate.PrivateCopy) { + DSAStackTy::DSAVarData DVarPrivate = DSAStack->hasDSA( + D, + [](OpenMPClauseKind C, bool AppliedToPointee, + DefaultDataSharingAttributes DefaultAttr) { + return isOpenMPPrivate(C) && !AppliedToPointee && + (DefaultAttr == DSA_firstprivate || + DefaultAttr == DSA_private); + }, + [](OpenMPDirectiveKind) { return true; }, + DSAStack->isClauseParsingMode()); + if (DVarPrivate.CKind == OMPC_unknown) + return nullptr; + + VarDecl *VD = DSAStack->getImplicitFDCapExprDecl(FD); + if (VD) + return VD; + if (getCurrentThisType().isNull()) + return nullptr; + Expr *ThisExpr = BuildCXXThisExpr(SourceLocation(), getCurrentThisType(), + /*IsImplicit=*/true); + const CXXScopeSpec CS = CXXScopeSpec(); + Expr *ME = BuildMemberExpr(ThisExpr, /*IsArrow=*/true, SourceLocation(), + NestedNameSpecifierLoc(), SourceLocation(), FD, + DeclAccessPair::make(FD, FD->getAccess()), + /*HadMultipleCandidates=*/false, + DeclarationNameInfo(), FD->getType(), + VK_LValue, OK_Ordinary); + OMPCapturedExprDecl *CD = buildCaptureDecl( + *this, FD->getIdentifier(), ME, DVarPrivate.CKind != OMPC_private, + CurContext->getParent(), /*AsExpression=*/false); + DeclRefExpr *VDPrivateRefExpr = buildDeclRefExpr( + *this, CD, CD->getType().getNonReferenceType(), SourceLocation()); + VD = cast(VDPrivateRefExpr->getDecl()); + DSAStack->addImplicitDefaultFirstprivateFD(FD, VD); + return VD; + } if (DVarPrivate.CKind != OMPC_unknown || (VD && (DSAStack->getDefaultDSA() == DSA_none || DSAStack->getDefaultDSA() == DSA_private || @@ -2344,6 +2469,23 @@ OpenMPClauseKind Sema::isOpenMPPrivateDecl(ValueDecl *D, unsigned Level, unsigned CapLevel) const { assert(LangOpts.OpenMP && "OpenMP is not allowed"); + if (DSAStack->getCurrentDirective() != OMPD_unknown && + (!DSAStack->isClauseParsingMode() || + DSAStack->getParentDirective() != OMPD_unknown)) { + DSAStackTy::DSAVarData DVarPrivate = DSAStack->hasDSA( + D, + [](OpenMPClauseKind C, bool AppliedToPointee, + DefaultDataSharingAttributes DefaultAttr) { + return isOpenMPPrivate(C) && !AppliedToPointee && + DefaultAttr == DSA_private; + }, + [](OpenMPDirectiveKind) { return true; }, + DSAStack->isClauseParsingMode()); + if (DVarPrivate.CKind == OMPC_private && isa(D) && + DSAStack->isImplicitDefaultFirstprivateFD(cast(D)) && + !DSAStack->isLoopControlVariable(D).first) + return OMPC_private; + } if (DSAStack->hasExplicitDirective(isOpenMPTaskingDirective, Level)) { bool IsTriviallyCopyable = D->getType().getNonReferenceType().isTriviallyCopyableType(Context) && @@ -3524,7 +3666,8 @@ if (auto *VD = dyn_cast(E->getDecl())) { // Check the datasharing rules for the expressions in the clauses. if (!CS || (isa(VD) && !CS->capturesVariable(VD) && - !Stack->getTopDSA(VD, /*FromParent=*/false).RefExpr)) { + !Stack->getTopDSA(VD, /*FromParent=*/false).RefExpr && + !Stack->isImplicitDefaultFirstprivateFD(VD))) { if (auto *CED = dyn_cast(VD)) if (!CED->hasAttr()) { Visit(CED->getInit()); @@ -3533,10 +3676,12 @@ } else if (VD->isImplicit() || isa(VD)) // Do not analyze internal variables and do not enclose them into // implicit clauses. - return; + if (!Stack->isImplicitDefaultFirstprivateFD(VD)) + return; VD = VD->getCanonicalDecl(); // Skip internally declared variables. if (VD->hasLocalStorage() && CS && !CS->capturesVariable(VD) && + !Stack->isImplicitDefaultFirstprivateFD(VD) && !Stack->isImplicitTaskFirstprivate(VD)) return; // Skip allocators in uses_allocators clauses. @@ -3554,6 +3699,7 @@ if (VD->hasGlobalStorage() && CS && !CS->capturesVariable(VD) && (Stack->hasRequiresDeclWithClause() || !Res || *Res != OMPDeclareTargetDeclAttr::MT_Link) && + !Stack->isImplicitDefaultFirstprivateFD(VD) && !Stack->isImplicitTaskFirstprivate(VD)) return; @@ -4421,6 +4567,7 @@ static OMPCapturedExprDecl *buildCaptureDecl(Sema &S, IdentifierInfo *Id, Expr *CaptureExpr, bool WithInit, + DeclContext *CurContext, bool AsExpression) { assert(CaptureExpr); ASTContext &C = S.getASTContext(); @@ -4439,11 +4586,11 @@ } WithInit = true; } - auto *CED = OMPCapturedExprDecl::Create(C, S.CurContext, Id, Ty, + auto *CED = OMPCapturedExprDecl::Create(C, CurContext, Id, Ty, CaptureExpr->getBeginLoc()); if (!WithInit) CED->addAttr(OMPCaptureNoInitAttr::CreateImplicit(C)); - S.CurContext->addHiddenDecl(CED); + CurContext->addHiddenDecl(CED); Sema::TentativeAnalysisScope Trap(S); S.AddInitializerToDecl(CED, Init, /*DirectInit=*/false); return CED; @@ -4456,6 +4603,7 @@ CD = cast(VD); else CD = buildCaptureDecl(S, D->getIdentifier(), CaptureExpr, WithInit, + S.CurContext, /*AsExpression=*/false); return buildDeclRefExpr(S, CD, CD->getType().getNonReferenceType(), CaptureExpr->getExprLoc()); @@ -4466,7 +4614,7 @@ if (!Ref) { OMPCapturedExprDecl *CD = buildCaptureDecl( S, &S.getASTContext().Idents.get(".capture_expr."), CaptureExpr, - /*WithInit=*/true, /*AsExpression=*/true); + /*WithInit=*/true, S.CurContext, /*AsExpression=*/true); Ref = buildDeclRefExpr(S, CD, CD->getType().getNonReferenceType(), CaptureExpr->getExprLoc()); } @@ -17536,6 +17684,8 @@ SourceLocation EndLoc) { SmallVector Vars; SmallVector PrivateCopies; + bool IsImplicitClause = + StartLoc.isInvalid() && LParenLoc.isInvalid() && EndLoc.isInvalid(); for (Expr *RefExpr : VarList) { assert(RefExpr && "NULL expr in OpenMP private clause."); SourceLocation ELoc; @@ -17650,9 +17800,17 @@ *this, VDPrivate, RefExpr->getType().getUnqualifiedType(), ELoc); DeclRefExpr *Ref = nullptr; - if (!VD && !CurContext->isDependentContext()) - Ref = buildCapture(*this, D, SimpleRefExpr, /*WithInit=*/false); - DSAStack->addDSA(D, RefExpr->IgnoreParens(), OMPC_private, Ref); + if (!VD && !CurContext->isDependentContext()) { + auto *FD = dyn_cast(D); + VarDecl *VD = FD ? DSAStack->getImplicitFDCapExprDecl(FD) : nullptr; + if (VD) + Ref = buildDeclRefExpr(*this, VD, VD->getType().getNonReferenceType(), + RefExpr->getExprLoc()); + else + Ref = buildCapture(*this, D, SimpleRefExpr, /*WithInit=*/false); + } + if (!IsImplicitClause) + DSAStack->addDSA(D, RefExpr->IgnoreParens(), OMPC_private, Ref); Vars.push_back((VD || CurContext->isDependentContext()) ? RefExpr->IgnoreParens() : Ref); @@ -17925,8 +18083,14 @@ if (TopDVar.CKind == OMPC_lastprivate) { Ref = TopDVar.PrivateCopy; } else { - Ref = buildCapture(*this, D, SimpleRefExpr, /*WithInit=*/true); - if (!isOpenMPCapturedDecl(D)) + auto *FD = dyn_cast(D); + VarDecl *VD = FD ? DSAStack->getImplicitFDCapExprDecl(FD) : nullptr; + if (VD) + Ref = buildDeclRefExpr(*this, VD, VD->getType().getNonReferenceType(), + RefExpr->getExprLoc()); + else + Ref = buildCapture(*this, D, SimpleRefExpr, /*WithInit=*/true); + if (VD || !isOpenMPCapturedDecl(D)) ExprCaptures.push_back(Ref->getDecl()); } } @@ -18196,7 +18360,7 @@ return true; DSAStackTy::DSAVarData DVarPrivate = Stack->hasDSA( VD, - [](OpenMPClauseKind C, bool AppliedToPointee) { + [](OpenMPClauseKind C, bool AppliedToPointee, bool) { return isOpenMPPrivate(C) && !AppliedToPointee; }, [](OpenMPDirectiveKind) { return true; }, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -11120,11 +11120,15 @@ FoundDecl == E->getFoundDecl() && !E->hasExplicitTemplateArgs()) { - // Mark it referenced in the new context regardless. - // FIXME: this is a bit instantiation-specific. - SemaRef.MarkMemberReferenced(E); - - return E; + // Skip for member expression of (this->f), rebuilt thisi->f is needed + // for Openmp where the field need to be privatizized in the case. + if (!(isa(E->getBase()) && + getSema().isOpenMPRebuildMemberExpr(cast(Member)))) { + // Mark it referenced in the new context regardless. + // FIXME: this is a bit instantiation-specific. + SemaRef.MarkMemberReferenced(E); + return E; + } } TemplateArgumentListInfo TransArgs; diff --git a/clang/test/OpenMP/default_firstprivate_ast_print.cpp b/clang/test/OpenMP/default_firstprivate_ast_print.cpp --- a/clang/test/OpenMP/default_firstprivate_ast_print.cpp +++ b/clang/test/OpenMP/default_firstprivate_ast_print.cpp @@ -38,22 +38,31 @@ void apply() { #pragma omp parallel default(firstprivate) { - targetDev++; + [=]() -> int { + return targetDev++; + }(); } // PRINT: #pragma omp parallel default(firstprivate) // PRINT-NEXT: { - // PRINT-NEXT: this->targetDev++; - // CHECK-NEXT: } + // PRINT-NEXT: [=]() -> int { + // PRINT-NEXT: return this->targetDev++; + // PRINT-NEXT: }(); + // PRINT-NEXT: } // DUMP: -OMPParallelDirective - // DUMP->NEXT: -OMPDefaultClause + // DUMP-NEXT: -OMPDefaultClause + // DUMP-NOT: -OMPFirstprivateClause } // PRINT: template<> void apply<32U>() // PRINT: #pragma omp parallel default(firstprivate) // PRINT-NEXT: { - // PRINT-NEXT: this->targetDev++; + // PRINT-NEXT: [=]() -> int { + // PRINT-NEXT: return this->targetDev++; + // PRINT-NEXT: }(); // CHECK-NEXT: } // DUMP: -OMPParallelDirective // DUMP-NEXT: -OMPDefaultClause + // DUMP-NEXT: -OMPFirstprivateClause + // DUMP-NEXT: -DeclRefExpr {{.*}} 'targetDev' }; void use_template() { @@ -99,4 +108,60 @@ // DUMP-NEXT: -DeclRefExpr {{.*}} 'yy' // DUMP-NEXT: -DeclRefExpr {{.*}} 'y' } +void zoo(int); +struct A { + int z; + int f; + A(); + ~A(); + void foo() { +#pragma omp parallel firstprivate(z) default(firstprivate) + { + z++; + f++; + zoo(z + f); + f++; + } + } + // PRINT: #pragma omp parallel firstprivate(this->z) default(firstprivate) + // DUMP: -OMPParallelDirective + // DUMP-NEXT: -OMPFirstprivateClause + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' + // DUMP-NEXT: -OMPDefaultClause + // DUMP-NEXT: -OMPFirstprivateClause {{.*}} + // DUMP-NEXT: -DeclRefExpr {{.*}} 'f' + // DUMP: -CXXThisExpr {{.*}} 'A *' implicit this + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' + // DUMP-NEXT: -DeclRefExpr {{.*}} 'f' + void bar() { +#pragma omp parallel firstprivate(z) default(firstprivate) + { +#pragma omp parallel private(z) default(firstprivate) + { + z++; + f++; + zoo(z + f); + f++; + } + } + } + // PRINT: #pragma omp parallel firstprivate(this->z) default(firstprivate) + // PRINT: #pragma omp parallel private(this->z) default(firstprivate) + // DUMP: -OMPParallelDirective + // DUMP-NEXT: -OMPFirstprivateClause + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' + // DUMP-NEXT: -OMPDefaultClause + // DUMP: -OMPParallelDirective + // DUMP-NEXT: -OMPPrivateClaus + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' + // DUMP-NEXT: -OMPDefaultClause + // DUMP-NEXT: -OMPFirstprivateClause {{.*}} + // DUMP-NEXT: -DeclRefExpr {{.*}} 'f' + // DUMP: -CXXThisExpr {{.*}} 'A *' implicit this + // DUMP-NEXT: -DeclRefExpr {{.*}} 'f' + // DUMP: -MemberExpr {{.*}} + // DUMP-NEXT: -CXXThisExpr + // DUMP: -CXXThisExpr {{.*}} 'A *' implicit this + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' +}; #endif // HEADER diff --git a/clang/test/OpenMP/default_private_ast_print.cpp b/clang/test/OpenMP/default_private_ast_print.cpp --- a/clang/test/OpenMP/default_private_ast_print.cpp +++ b/clang/test/OpenMP/default_private_ast_print.cpp @@ -96,4 +96,57 @@ // DUMP-NEXT: -DeclRefExpr {{.*}} 'a' // DUMP-NEXT: -DeclRefExpr {{.*}} 'yy' } + +void zoo(int); +struct A { + int z; + int f; + A(); + ~A(); + void foo() { +#pragma omp parallel private(z) default(private) + { + z++; + f++; + zoo(z + f); + f++; + } + } + // PRINT: #pragma omp parallel private(this->z) default(private) + // DUMP: -OMPParallelDirective + // DUMP-NEXT: -OMPPrivateClause + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' + // DUMP-NEXT: -OMPDefaultClause + // DUMP-NEXT: -OMPPrivateClause + // DUMP-NEXT: -DeclRefExpr {{.*}} 'f' + // DUMP: -CXXThisExpr {{.*}} 'A *' implicit this + void bar() { +#pragma omp parallel private(z) default(private) + { +#pragma omp parallel private(z) default(private) + { + z++; + f++; + zoo(z + f); + f++; + } + } + } + // PRINT: #pragma omp parallel private(this->z) default(private) + // PRINT: #pragma omp parallel private(this->z) default(private) + // DUMP: -OMPParallelDirective + // DUMP-NEXT: -OMPPrivateClause + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' + // DUMP-NEXT: -OMPDefaultClause + // DUMP: -OMPParallelDirective + // DUMP-NEXT: -OMPPrivateClause + // DUMP-NEXT: -DeclRefExpr {{.*}} 'z' + // DUMP-NEXT: -OMPDefaultClause + // DUMP-NEXT: -OMPPrivateClause {{.*}} + // DUMP-NEXT: -DeclRefExpr {{.*}} 'f' + // DUMP: -CXXThisExpr + // DUMP: -MemberExpr + // DUMP-NEXT: -CXXThisExpr + // DUMP: -CXXThisExpr +}; #endif // HEADER