Index: include/clang/AST/DataRecursiveASTVisitor.h =================================================================== --- include/clang/AST/DataRecursiveASTVisitor.h +++ include/clang/AST/DataRecursiveASTVisitor.h @@ -2519,6 +2519,18 @@ bool RecursiveASTVisitor::VisitOMPLastprivateClause( OMPLastprivateClause *C) { TRY_TO(VisitOMPClauseList(C)); + for (auto *E : C->private_copies()) { + TRY_TO(TraverseStmt(E)); + } + for (auto *E : C->source_exprs()) { + TRY_TO(TraverseStmt(E)); + } + for (auto *E : C->destination_exprs()) { + TRY_TO(TraverseStmt(E)); + } + for (auto *E : C->assignment_ops()) { + TRY_TO(TraverseStmt(E)); + } return true; } Index: include/clang/AST/OpenMPClause.h =================================================================== --- include/clang/AST/OpenMPClause.h +++ include/clang/AST/OpenMPClause.h @@ -1139,6 +1139,7 @@ /// with the variables 'a' and 'b'. /// class OMPLastprivateClause : public OMPVarListClause { + friend class OMPClauseReader; /// \brief Build clause with number of variables \a N. /// /// \param StartLoc Starting location of the clause. @@ -1160,6 +1161,56 @@ OMPC_lastprivate, SourceLocation(), SourceLocation(), SourceLocation(), N) {} + /// \brief Get the list of helper expressions for initialization of private + /// copies for lastprivate variables. + MutableArrayRef getPrivateCopies() { + return MutableArrayRef(varlist_end(), varlist_size()); + } + ArrayRef getPrivateCopies() const { + return llvm::makeArrayRef(varlist_end(), varlist_size()); + } + + /// \brief Set list of helper expressions, required for proper codegen of the + /// clause. These expressions represent private variables (for arrays, single + /// array element) in the final assignment statement performed by the + /// lastprivate clause. + void setSourceExprs(ArrayRef SrcExprs); + + /// \brief Get the list of helper source expressions. + MutableArrayRef getSourceExprs() { + return MutableArrayRef(getPrivateCopies().end(), varlist_size()); + } + ArrayRef getSourceExprs() const { + return llvm::makeArrayRef(getPrivateCopies().end(), varlist_size()); + } + + /// \brief Set list of helper expressions, required for proper codegen of the + /// clause. These expressions represent original variables (for arrays, single + /// array element) in the final assignment statement performed by the + /// lastprivate clause. + void setDestinationExprs(ArrayRef DstExprs); + + /// \brief Get the list of helper destination expressions. + MutableArrayRef getDestinationExprs() { + return MutableArrayRef(getSourceExprs().end(), varlist_size()); + } + ArrayRef getDestinationExprs() const { + return llvm::makeArrayRef(getSourceExprs().end(), varlist_size()); + } + + /// \brief Set list of helper assignment expressions, required for proper + /// codegen of the clause. These expressions are assignment expressions that + /// assign private copy of the variable to original variable. + void setAssignmentOps(ArrayRef AssignmentOps); + + /// \brief Get the list of helper assignment expressions. + MutableArrayRef getAssignmentOps() { + return MutableArrayRef(getDestinationExprs().end(), varlist_size()); + } + ArrayRef getAssignmentOps() const { + return llvm::makeArrayRef(getDestinationExprs().end(), varlist_size()); + } + public: /// \brief Creates clause with a list of variables \a VL. /// @@ -1168,10 +1219,25 @@ /// \param LParenLoc Location of '('. /// \param EndLoc Ending location of the clause. /// \param VL List of references to the variables. + /// \param SrcExprs List of helper expressions for proper generation of + /// assignment operation required for lastprivate clause. This list represents + /// private variables (for arrays, single array element). + /// \param DstExprs List of helper expressions for proper generation of + /// assignment operation required for lastprivate clause. This list represents + /// original variables (for arrays, single array element). + /// \param AssignmentOps List of helper expressions that represents assignment + /// operation: + /// \code + /// DstExprs = SrcExprs; + /// \endcode + /// Required for proper codegen of final assignment performed by the + /// lastprivate clause. + /// /// static OMPLastprivateClause * Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, - SourceLocation EndLoc, ArrayRef VL); + SourceLocation EndLoc, ArrayRef VL, ArrayRef SrcExprs, + ArrayRef DstExprs, ArrayRef AssignmentOps); /// \brief Creates an empty clause with the place for \a N variables. /// /// \param C AST context. @@ -1179,6 +1245,48 @@ /// static OMPLastprivateClause *CreateEmpty(const ASTContext &C, unsigned N); + typedef MutableArrayRef::iterator helper_expr_iterator; + typedef ArrayRef::iterator helper_expr_const_iterator; + typedef llvm::iterator_range helper_expr_range; + typedef llvm::iterator_range + helper_expr_const_range; + + /// \brief Set list of helper expressions, required for generation of private + /// copies of original lastprivate variables. + void setPrivateCopies(ArrayRef PrivateCopies); + + helper_expr_const_range private_copies() const { + return helper_expr_const_range(getPrivateCopies().begin(), + getPrivateCopies().end()); + } + helper_expr_range private_copies() { + return helper_expr_range(getPrivateCopies().begin(), + getPrivateCopies().end()); + } + helper_expr_const_range source_exprs() const { + return helper_expr_const_range(getSourceExprs().begin(), + getSourceExprs().end()); + } + helper_expr_range source_exprs() { + return helper_expr_range(getSourceExprs().begin(), getSourceExprs().end()); + } + helper_expr_const_range destination_exprs() const { + return helper_expr_const_range(getDestinationExprs().begin(), + getDestinationExprs().end()); + } + helper_expr_range destination_exprs() { + return helper_expr_range(getDestinationExprs().begin(), + getDestinationExprs().end()); + } + helper_expr_const_range assignment_ops() const { + return helper_expr_const_range(getAssignmentOps().begin(), + getAssignmentOps().end()); + } + helper_expr_range assignment_ops() { + return helper_expr_range(getAssignmentOps().begin(), + getAssignmentOps().end()); + } + StmtRange children() { return StmtRange(reinterpret_cast(varlist_begin()), reinterpret_cast(varlist_end())); Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2549,6 +2549,18 @@ bool RecursiveASTVisitor::VisitOMPLastprivateClause( OMPLastprivateClause *C) { TRY_TO(VisitOMPClauseList(C)); + for (auto *E : C->private_copies()) { + TRY_TO(TraverseStmt(E)); + } + for (auto *E : C->source_exprs()) { + TRY_TO(TraverseStmt(E)); + } + for (auto *E : C->destination_exprs()) { + TRY_TO(TraverseStmt(E)); + } + for (auto *E : C->assignment_ops()) { + TRY_TO(TraverseStmt(E)); + } return true; } Index: lib/AST/Stmt.cpp =================================================================== --- lib/AST/Stmt.cpp +++ lib/AST/Stmt.cpp @@ -1246,17 +1246,47 @@ return new (Mem) OMPFirstprivateClause(N); } -OMPLastprivateClause *OMPLastprivateClause::Create(const ASTContext &C, - SourceLocation StartLoc, - SourceLocation LParenLoc, - SourceLocation EndLoc, - ArrayRef VL) { +void OMPLastprivateClause::setPrivateCopies(ArrayRef PrivateCopies) { + assert(PrivateCopies.size() == varlist_size() && + "Number of private copies is not the same as the preallocated buffer"); + std::copy(PrivateCopies.begin(), PrivateCopies.end(), varlist_end()); +} + +void OMPLastprivateClause::setSourceExprs(ArrayRef SrcExprs) { + assert(SrcExprs.size() == varlist_size() && "Number of source expressions is " + "not the same as the " + "preallocated buffer"); + std::copy(SrcExprs.begin(), SrcExprs.end(), getPrivateCopies().end()); +} + +void OMPLastprivateClause::setDestinationExprs(ArrayRef DstExprs) { + assert(DstExprs.size() == varlist_size() && "Number of destination " + "expressions is not the same as " + "the preallocated buffer"); + std::copy(DstExprs.begin(), DstExprs.end(), getSourceExprs().end()); +} + +void OMPLastprivateClause::setAssignmentOps(ArrayRef AssignmentOps) { + assert(AssignmentOps.size() == varlist_size() && + "Number of assignment expressions is not the same as the preallocated " + "buffer"); + std::copy(AssignmentOps.begin(), AssignmentOps.end(), + getDestinationExprs().end()); +} + +OMPLastprivateClause *OMPLastprivateClause::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc, ArrayRef VL, ArrayRef SrcExprs, + ArrayRef DstExprs, ArrayRef AssignmentOps) { void *Mem = C.Allocate(llvm::RoundUpToAlignment(sizeof(OMPLastprivateClause), llvm::alignOf()) + - sizeof(Expr *) * VL.size()); + 5 * sizeof(Expr *) * VL.size()); OMPLastprivateClause *Clause = new (Mem) OMPLastprivateClause(StartLoc, LParenLoc, EndLoc, VL.size()); Clause->setVarRefs(VL); + Clause->setSourceExprs(SrcExprs); + Clause->setDestinationExprs(DstExprs); + Clause->setAssignmentOps(AssignmentOps); return Clause; } @@ -1264,7 +1294,7 @@ unsigned N) { void *Mem = C.Allocate(llvm::RoundUpToAlignment(sizeof(OMPLastprivateClause), llvm::alignOf()) + - sizeof(Expr *) * N); + 5 * sizeof(Expr *) * N); return new (Mem) OMPLastprivateClause(N); } Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -346,6 +346,12 @@ void OMPClauseProfiler::VisitOMPLastprivateClause(const OMPLastprivateClause *C) { VisitOMPClauseList(C); + for (auto *E : C->destination_exprs()) { + Profiler->VisitStmt(E); + } + for (auto *E : C->assignment_ops()) { + Profiler->VisitStmt(E); + } } void OMPClauseProfiler::VisitOMPSharedClause(const OMPSharedClause *C) { VisitOMPClauseList(C); Index: lib/CodeGen/CGStmtOpenMP.cpp =================================================================== --- lib/CodeGen/CGStmtOpenMP.cpp +++ lib/CodeGen/CGStmtOpenMP.cpp @@ -69,72 +69,93 @@ CGF.EmitBlock(ContBlock, /*IsFinished*/ true); } -void CodeGenFunction::EmitOMPAggregateAssign(LValue OriginalAddr, - llvm::Value *PrivateAddr, - const Expr *AssignExpr, - QualType OriginalType, - const VarDecl *VDInit) { - EmitBlock(createBasicBlock(".omp.assign.begin.")); - if (!isa(AssignExpr) || isTrivialInitializer(AssignExpr)) { - // Perform simple memcpy. - EmitAggregateAssign(PrivateAddr, OriginalAddr.getAddress(), - AssignExpr->getType()); +void CodeGenFunction::EmitOMPAggregateAssign( + llvm::Value *DestAddr, llvm::Value *SrcAddr, QualType OriginalType, + const llvm::function_ref &CopyGen) { + // Perform element-by-element initialization. + QualType ElementTy; + auto SrcBegin = SrcAddr; + auto DestBegin = DestAddr; + auto ArrayTy = OriginalType->getAsArrayTypeUnsafe(); + auto SrcNumElements = emitArrayLength(ArrayTy, ElementTy, SrcBegin); + auto DestNumElements = emitArrayLength(ArrayTy, ElementTy, DestBegin); + auto *NumElements = Builder.CreateSelect( + Builder.CreateICmpULT(SrcNumElements, DestNumElements), SrcNumElements, + DestNumElements); + auto DestEnd = Builder.CreateGEP(DestBegin, NumElements); + // The basic structure here is a do-while loop, because we don't + // need to check for the zero-element case. + auto BodyBB = createBasicBlock("omp.arraycpy.body"); + auto DoneBB = createBasicBlock("omp.arraycpy.done"); + auto IsEmpty = + Builder.CreateICmpEQ(DestBegin, DestEnd, "omp.arraycpy.isempty"); + Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB); + + // Enter the loop body, making that address the current address. + auto EntryBB = Builder.GetInsertBlock(); + EmitBlock(BodyBB); + auto SrcElementCurrent = + Builder.CreatePHI(SrcBegin->getType(), 2, "omp.arraycpy.srcElementPast"); + SrcElementCurrent->addIncoming(SrcBegin, EntryBB); + auto DestElementCurrent = Builder.CreatePHI(DestBegin->getType(), 2, + "omp.arraycpy.destElementPast"); + DestElementCurrent->addIncoming(DestBegin, EntryBB); + + // Shift the address back by one element. + auto DestElement = Builder.CreateConstGEP1_32(DestElementCurrent, /*Idx0=*/1, + "omp.arraycpy.dest.element"); + auto SrcElement = Builder.CreateConstGEP1_32(SrcElementCurrent, /*Idx0=*/1, + "omp.arraycpy.src.element"); + // Emit copy. + CopyGen(DestElement, SrcElement); + // Check whether we've reached the end. + auto Done = Builder.CreateICmpEQ(DestElement, DestEnd, "omp.arraycpy.done"); + Builder.CreateCondBr(Done, DoneBB, BodyBB); + DestElementCurrent->addIncoming(DestElement, Builder.GetInsertBlock()); + SrcElementCurrent->addIncoming(SrcElement, Builder.GetInsertBlock()); + + // Done. + EmitBlock(DoneBB, true); +} + +void CodeGenFunction::EmitOMPCopy(CodeGenFunction &CGF, + QualType OriginalType, llvm::Value *DestAddr, + llvm::Value *SrcAddr, const VarDecl *DestVD, + const VarDecl *SrcVD, const Expr *Copy) { + if (OriginalType->isArrayType()) { + auto *BO = dyn_cast(Copy); + if (BO && BO->getOpcode() == BO_Assign) { + // Perform simple memcpy for simple copying. + CGF.EmitAggregateAssign(DestAddr, SrcAddr, OriginalType); + } else { + // For arrays with complex element types perform element by element + // copying. + CGF.EmitOMPAggregateAssign( + DestAddr, SrcAddr, OriginalType, + [&CGF, Copy, SrcVD, DestVD](llvm::Value *DestElement, + llvm::Value *SrcElement) { + // Working with the single array element, so have to remap + // destination and source variables to corresponding array + // elements. + CodeGenFunction::OMPPrivateScope Remap(CGF); + Remap.addPrivate(DestVD, [DestElement]() -> llvm::Value *{ + return DestElement; + }); + Remap.addPrivate( + SrcVD, [SrcElement]() -> llvm::Value *{ return SrcElement; }); + (void)Remap.Privatize(); + CGF.EmitIgnoredExpr(Copy); + }); + } } else { - // Perform element-by-element initialization. - QualType ElementTy; - auto SrcBegin = OriginalAddr.getAddress(); - auto DestBegin = PrivateAddr; - auto ArrayTy = OriginalType->getAsArrayTypeUnsafe(); - auto SrcNumElements = emitArrayLength(ArrayTy, ElementTy, SrcBegin); - auto DestNumElements = emitArrayLength(ArrayTy, ElementTy, DestBegin); - auto SrcEnd = Builder.CreateGEP(SrcBegin, SrcNumElements); - auto DestEnd = Builder.CreateGEP(DestBegin, DestNumElements); - // The basic structure here is a do-while loop, because we don't - // need to check for the zero-element case. - auto BodyBB = createBasicBlock("omp.arraycpy.body"); - auto DoneBB = createBasicBlock("omp.arraycpy.done"); - auto IsEmpty = - Builder.CreateICmpEQ(DestBegin, DestEnd, "omp.arraycpy.isempty"); - Builder.CreateCondBr(IsEmpty, DoneBB, BodyBB); - - // Enter the loop body, making that address the current address. - auto EntryBB = Builder.GetInsertBlock(); - EmitBlock(BodyBB); - auto SrcElementPast = Builder.CreatePHI(SrcBegin->getType(), 2, - "omp.arraycpy.srcElementPast"); - SrcElementPast->addIncoming(SrcEnd, EntryBB); - auto DestElementPast = Builder.CreatePHI(DestBegin->getType(), 2, - "omp.arraycpy.destElementPast"); - DestElementPast->addIncoming(DestEnd, EntryBB); - - // Shift the address back by one element. - auto NegativeOne = llvm::ConstantInt::get(SizeTy, -1, true); - auto DestElement = Builder.CreateGEP(DestElementPast, NegativeOne, - "omp.arraycpy.dest.element"); - auto SrcElement = Builder.CreateGEP(SrcElementPast, NegativeOne, - "omp.arraycpy.src.element"); - { - // Create RunCleanScope to cleanup possible temps. - CodeGenFunction::RunCleanupsScope Init(*this); - // Emit initialization for single element. - LocalDeclMap[VDInit] = SrcElement; - EmitAnyExprToMem(AssignExpr, DestElement, - AssignExpr->getType().getQualifiers(), - /*IsInitializer*/ false); - LocalDeclMap.erase(VDInit); - } - - // Check whether we've reached the end. - auto Done = - Builder.CreateICmpEQ(DestElement, DestBegin, "omp.arraycpy.done"); - Builder.CreateCondBr(Done, DoneBB, BodyBB); - DestElementPast->addIncoming(DestElement, Builder.GetInsertBlock()); - SrcElementPast->addIncoming(SrcElement, Builder.GetInsertBlock()); - - // Done. - EmitBlock(DoneBB, true); + // Remap pseudo source variable to private copy. + CodeGenFunction::OMPPrivateScope Remap(CGF); + Remap.addPrivate(SrcVD, [SrcAddr]() -> llvm::Value *{ return SrcAddr; }); + Remap.addPrivate(DestVD, [DestAddr]() -> llvm::Value *{ return DestAddr; }); + (void)Remap.Privatize(); + // Emit copying of the whole variable. + CGF.EmitIgnoredExpr(Copy); } - EmitBlock(createBasicBlock(".omp.assign.end.")); } void CodeGenFunction::EmitOMPFirstprivateClause( @@ -158,18 +179,37 @@ LValue Base = MakeNaturalAlignAddrLValue( CapturedStmtInfo->getContextValue(), getContext().getTagDeclType(FD->getParent())); - auto OriginalAddr = EmitLValueForField(Base, FD); + auto *OriginalAddr = EmitLValueForField(Base, FD).getAddress(); auto VDInit = cast(cast(*InitsRef)->getDecl()); - IsRegistered = PrivateScope.addPrivate(OrigVD, [&]() -> llvm::Value * { + IsRegistered = PrivateScope.addPrivate(OrigVD, [&]() -> llvm::Value *{ auto Emission = EmitAutoVarAlloca(*VD); // Emit initialization of aggregate firstprivate vars. - EmitOMPAggregateAssign(OriginalAddr, Emission.getAllocatedAddress(), - VD->getInit(), (*IRef)->getType(), VDInit); + auto *Init = VD->getInit(); + if (!isa(Init) || isTrivialInitializer(Init)) { + // Perform simple memcpy. + EmitAggregateAssign(Emission.getAllocatedAddress(), OriginalAddr, + (*IRef)->getType()); + } else { + EmitOMPAggregateAssign( + Emission.getAllocatedAddress(), OriginalAddr, + (*IRef)->getType(), + [this, VDInit, Init](llvm::Value *DestElement, + llvm::Value *SrcElement) { + // Clean up any temporaries needed by the initialization. + RunCleanupsScope InitScope(*this); + // Emit initialization for single element. + LocalDeclMap[VDInit] = SrcElement; + EmitAnyExprToMem(Init, DestElement, + Init->getType().getQualifiers(), + /*IsInitializer*/ false); + LocalDeclMap.erase(VDInit); + }); + } EmitAutoVarCleanups(Emission); return Emission.getAllocatedAddress(); }); } else - IsRegistered = PrivateScope.addPrivate(OrigVD, [&]() -> llvm::Value * { + IsRegistered = PrivateScope.addPrivate(OrigVD, [&]() -> llvm::Value *{ // Emit private VarDecl with copy init. EmitDecl(*VD); return GetAddrOfLocalVar(VD); @@ -209,6 +249,91 @@ } } +bool CodeGenFunction::EmitOMPLastprivateClauseInit( + const OMPExecutableDirective &D, OMPPrivateScope &PrivateScope) { + auto LastprivateFilter = [](const OMPClause *C) -> bool { + return C->getClauseKind() == OMPC_lastprivate; + }; + bool HasAtLeastOneLastprivate = false; + for (OMPExecutableDirective::filtered_clause_iterator I(D.clauses(), LastprivateFilter); + I; ++I) { + auto *C = cast(*I); + auto IRef = C->varlist_begin(); + auto IDestRef = C->destination_exprs().begin(); + for (auto *IInit : C->private_copies()) { + // Keep the address of the original variable for future update at the end + // of the loop. + auto *OrigVD = cast(cast(*IRef)->getDecl()); + auto *DestVD = cast(cast(*IDestRef)->getDecl()); + PrivateScope.addPrivate(DestVD, [this, OrigVD, IRef]() -> llvm::Value *{ + DeclRefExpr DRE(const_cast(OrigVD), + CapturedStmtInfo->lookup(OrigVD) != nullptr, + (*IRef)->getType(), VK_LValue, (*IRef)->getExprLoc()); + return EmitLValue(&DRE).getAddress(); + }); + // Check if the variable is also a firstprivate: in this case IInit is not + // generated. Initialization of this variable will happen in codegen for + // 'firstprivate' clause. + if (!IInit) + continue; + auto *VD = cast(cast(IInit)->getDecl()); + bool IsRegistered = + PrivateScope.addPrivate(OrigVD, [&]() -> llvm::Value * { + // Emit private VarDecl with copy init. + EmitDecl(*VD); + return GetAddrOfLocalVar(VD); + }); + assert(IsRegistered && "lastprivate var already registered as private"); + HasAtLeastOneLastprivate = HasAtLeastOneLastprivate || IsRegistered; + ++IRef, ++IDestRef; + } + } + return HasAtLeastOneLastprivate; +} + +void CodeGenFunction::EmitOMPLastprivateClauseFinal( + const OMPExecutableDirective &D, llvm::Value *IsLastIterCond) { + // Emit following code: + // if () { + // orig_var1 = private_orig_var1; + // ... + // orig_varn = private_orig_varn; + // } + auto *ThenBB = createBasicBlock(".omp.lastprivate.then"); + auto *DoneBB = createBasicBlock(".omp.lastprivate.done"); + Builder.CreateCondBr(IsLastIterCond, ThenBB, DoneBB); + EmitBlock(ThenBB); + { + auto LastprivateFilter = [](const OMPClause *C) -> bool { + return C->getClauseKind() == OMPC_lastprivate; + }; + for (OMPExecutableDirective::filtered_clause_iterator I(D.clauses(), LastprivateFilter); + I; ++I) { + auto *C = cast(*I); + auto IRef = C->varlist_begin(); + auto ISrcRef = C->source_exprs().begin(); + auto IDestRef = C->destination_exprs().begin(); + for (auto *AssignOp : C->assignment_ops()) { + auto *PrivateVD = cast(cast(*IRef)->getDecl()); + auto *SrcVD = cast(cast(*ISrcRef)->getDecl()); + auto *DestVD = cast(cast(*IDestRef)->getDecl()); + // Get the address of the original variable. + auto *OriginalAddr = GetAddrOfLocalVar(DestVD); + // Get the address of the private variable. + auto *PrivateAddr = GetAddrOfLocalVar(PrivateVD); + EmitOMPCopy(*this, (*IRef)->getType(), OriginalAddr, PrivateAddr, + DestVD, SrcVD, AssignOp); + ++IRef; + ++ISrcRef; + ++IDestRef; + } + } + } + EmitBlock(DoneBB, /*IsFinished=*/true); +} + void CodeGenFunction::EmitOMPReductionClauseInit( const OMPExecutableDirective &D, CodeGenFunction::OMPPrivateScope &PrivateScope) { @@ -765,7 +890,7 @@ return CGF.EmitLValue(Helper); } -void CodeGenFunction::EmitOMPWorksharingLoop(const OMPLoopDirective &S) { +bool CodeGenFunction::EmitOMPWorksharingLoop(const OMPLoopDirective &S) { // Emit the loop iteration variable. auto IVExpr = cast(S.getIterationVariable()); auto IVDecl = cast(IVExpr->getDecl()); @@ -782,6 +907,7 @@ auto &RT = CGM.getOpenMPRuntime(); + bool HasLastprivateClause; // Check pre-condition. { // Skip the entire loop if we don't meet the precondition. @@ -804,6 +930,7 @@ EmitOMPHelperVar(*this, cast(S.getIsLastIterVariable())); OMPPrivateScope LoopScope(*this); + HasLastprivateClause = EmitOMPLastprivateClauseInit(S, LoopScope); EmitPrivateLoopCounters(*this, LoopScope, S.counters()); (void)LoopScope.Privatize(); @@ -851,21 +978,28 @@ UB.getAddress(), ST.getAddress(), IL.getAddress(), Chunk); } + // Emit final copy of the lastprivate variables if IsLastIter != 0. + if (HasLastprivateClause) + EmitOMPLastprivateClauseFinal( + S, Builder.CreateIsNotNull(EmitLoadOfScalar(IL, S.getLocStart()))); } // We're now done with the loop, so jump to the continuation block. EmitBranch(ContBlock); EmitBlock(ContBlock, true); } + return HasLastprivateClause; } void CodeGenFunction::EmitOMPForDirective(const OMPForDirective &S) { LexicalScope Scope(*this, S.getSourceRange()); - auto &&CodeGen = - [&S](CodeGenFunction &CGF) { CGF.EmitOMPWorksharingLoop(S); }; + bool HasLastprivates = false; + auto &&CodeGen = [&S, &HasLastprivates](CodeGenFunction &CGF) { + HasLastprivates = CGF.EmitOMPWorksharingLoop(S); + }; CGM.getOpenMPRuntime().emitInlinedDirective(*this, CodeGen); // Emit an implicit barrier at the end. - if (!S.getSingleClause(OMPC_nowait)) { + if (!S.getSingleClause(OMPC_nowait) || HasLastprivates) { CGM.getOpenMPRuntime().emitBarrierCall(*this, S.getLocStart(), OMPD_for); } } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -2029,9 +2029,33 @@ llvm::Function *GenerateCapturedStmtFunctionEpilog(const CapturedStmt &S); llvm::Function *GenerateCapturedStmtFunction(const CapturedStmt &S); llvm::Value *GenerateCapturedStmtArgument(const CapturedStmt &S); - void EmitOMPAggregateAssign(LValue OriginalAddr, llvm::Value *PrivateAddr, - const Expr *AssignExpr, QualType Type, - const VarDecl *VDInit); + /// \brief Perform element by element copying of arrays with type \a + /// OriginalType from \a SrcAddr to \a DestAddr using copying procedure + /// generated by \a CopyGen. + /// + /// \param DestAddr Address of the destination array. + /// \param SrcAddr Address of the source array. + /// \param OriginalType Type of destination and source arrays. + /// \param CopyGen Copying procedure that copies value of single array element + /// to another single array element. + void EmitOMPAggregateAssign( + llvm::Value *DestAddr, llvm::Value *SrcAddr, QualType OriginalType, + const llvm::function_ref &CopyGen); + /// \brief Emit proper copying of data from one variable to another. + /// + /// \param OriginalType Original type of the copied variables. + /// \param DestAddr Destination address. + /// \param SrcAddr Source address. + /// \param DestVD Destination variable used in \a CopyExpr (for arrays, has + /// type of the base array element). + /// \param SrcVD Source variable used in \a CopyExpr (for arrays, has type of + /// the base array element). + /// \param Copy Actual copygin expression for copying data from \a SrcVD to \a + /// DestVD. + void EmitOMPCopy(CodeGenFunction &CGF, QualType OriginalType, + llvm::Value *DestAddr, llvm::Value *SrcAddr, + const VarDecl *DestVD, const VarDecl *SrcVD, + const Expr *Copy); /// \brief Emit atomic update code for constructs: \a X = \a X \a BO \a E or /// \a X = \a E \a BO \a E. /// @@ -2051,6 +2075,28 @@ OMPPrivateScope &PrivateScope); void EmitOMPPrivateClause(const OMPExecutableDirective &D, OMPPrivateScope &PrivateScope); + /// \brief Emit initial code for lastprivate variables. If some variable is + /// not also firstprivate, then the default initialization is used. Otherwise + /// initialization of this variable is performed by EmitOMPFirstprivateClause + /// method. + /// + /// \param D Directive that may have 'lastprivate' directives. + /// \param PrivateScope Private scope for capturing lastprivate variables for + /// proper codegen in internal captured statement. + /// + /// \returns true if there is at least one lastprivate variable, false + /// otherwise. + bool EmitOMPLastprivateClauseInit(const OMPExecutableDirective &D, + OMPPrivateScope &PrivateScope); + /// \brief Emit final copying of lastprivate values to original variables at + /// the end of the worksharing or simd directive. + /// + /// \param D Directive that has at least one 'lastprivate' directives. + /// \param IsLastIterCond Boolean condition that must be set to 'i1 true' if + /// it is the last iteration of the loop code in associated directive, or to + /// 'i1 false' otherwise. + void EmitOMPLastprivateClauseFinal(const OMPExecutableDirective &D, + llvm::Value *IsLastIterCond); /// \brief Emit initial code for reduction variables. Creates reduction copies /// and initializes them with the values according to OpenMP standard. /// @@ -2098,7 +2144,10 @@ const Expr *IncExpr, const llvm::function_ref &BodyGen); void EmitOMPSimdFinal(const OMPLoopDirective &S); - void EmitOMPWorksharingLoop(const OMPLoopDirective &S); + /// \brief Emit code for the worksharing loop-based directive. + /// \return true, if this construct has any lastprivate clause, false - + /// otherwise. + bool EmitOMPWorksharingLoop(const OMPLoopDirective &S); void EmitOMPForOuterLoop(OpenMPScheduleClauseKind ScheduleKind, const OMPLoopDirective &S, OMPPrivateScope &LoopScope, llvm::Value *LB, Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -573,46 +573,48 @@ // class type, unless the list item is also specified in a firstprivate // clause. if (auto D = dyn_cast_or_null(CurDirective)) { - for (auto C : D->clauses()) { - if (auto Clause = dyn_cast(C)) { - for (auto VarRef : Clause->varlists()) { - if (VarRef->isValueDependent() || VarRef->isTypeDependent()) + for (auto *C : D->clauses()) { + if (auto *Clause = dyn_cast(C)) { + SmallVector PrivateCopies; + for (auto *DE : Clause->varlists()) { + if (DE->isValueDependent() || DE->isTypeDependent()) { + PrivateCopies.push_back(nullptr); continue; - auto VD = cast(cast(VarRef)->getDecl()); + } + auto *VD = cast(cast(DE)->getDecl()); auto DVar = DSAStack->getTopDSA(VD, false); if (DVar.CKind == OMPC_lastprivate) { - SourceLocation ELoc = VarRef->getExprLoc(); - auto Type = VarRef->getType(); - if (Type->isArrayType()) - Type = QualType(Type->getArrayElementTypeNoTypeQual(), 0); - CXXRecordDecl *RD = - getLangOpts().CPlusPlus ? Type->getAsCXXRecordDecl() : nullptr; - // FIXME This code must be replaced by actual constructing of the - // lastprivate variable. - if (RD) { - CXXConstructorDecl *CD = LookupDefaultConstructor(RD); - PartialDiagnostic PD = - PartialDiagnostic(PartialDiagnostic::NullDiagnostic()); - if (!CD || - CheckConstructorAccess( - ELoc, CD, InitializedEntity::InitializeTemporary(Type), - CD->getAccess(), PD) == AR_inaccessible || - CD->isDeleted()) { - Diag(ELoc, diag::err_omp_required_method) - << getOpenMPClauseName(OMPC_lastprivate) << 0; - bool IsDecl = VD->isThisDeclarationADefinition(Context) == - VarDecl::DeclarationOnly; - Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl - : diag::note_defined_here) - << VD; - Diag(RD->getLocation(), diag::note_previous_decl) << RD; - continue; - } - MarkFunctionReferenced(ELoc, CD); - DiagnoseUseOfDecl(CD, ELoc); - } + // Generate helper private variable and initialize it with the + // default + // value. The address of the original variable is replaced by the + // address of + // the new private variable in CodeGen. This new variable is not + // added to + // IdResolver, so the code in the OpenMP region uses original + // variable for + // proper diagnostics. + auto *VDPrivate = VarDecl::Create( + Context, CurContext, DE->getLocStart(), DE->getExprLoc(), + VD->getIdentifier(), VD->getType(), VD->getTypeSourceInfo(), + SC_Auto); + ActOnUninitializedDecl(VDPrivate, /*TypeMayContainAuto=*/false); + if (VDPrivate->isInvalidDecl()) + continue; + CurContext->addDecl(VDPrivate); + PrivateCopies.push_back(DeclRefExpr::Create( + Context, NestedNameSpecifierLoc(), SourceLocation(), VDPrivate, + /*RefersToEnclosingVariableOrCapture=*/false, SourceLocation(), + DE->getType(), VK_LValue)); + } else { + // The variable is also a firstprivate, so initialization sequence + // for private copy is generated already. + PrivateCopies.push_back(nullptr); } } + // Set initializers to private copies if no errors was found. + if (PrivateCopies.size() == Clause->varlist_size()) { + Clause->setPrivateCopies(PrivateCopies); + } } } } @@ -4967,11 +4969,17 @@ SourceLocation LParenLoc, SourceLocation EndLoc) { SmallVector Vars; + SmallVector SrcExprs; + SmallVector DstExprs; + SmallVector AssignmentOps; for (auto &RefExpr : VarList) { assert(RefExpr && "NULL expr in OpenMP lastprivate clause."); if (isa(RefExpr)) { // It will be analyzed later. Vars.push_back(RefExpr); + SrcExprs.push_back(nullptr); + DstExprs.push_back(nullptr); + AssignmentOps.push_back(nullptr); continue; } @@ -4993,6 +5001,9 @@ if (Type->isDependentType() || Type->isInstantiationDependentType()) { // It will be analyzed later. Vars.push_back(DE); + SrcExprs.push_back(nullptr); + DstExprs.push_back(nullptr); + AssignmentOps.push_back(nullptr); continue; } @@ -5056,65 +5067,39 @@ // A variable of class type (or array thereof) that appears in a // lastprivate clause requires an accessible, unambiguous copy assignment // operator for the class type. - while (Type.getNonReferenceType()->isArrayType()) - Type = cast(Type.getNonReferenceType().getTypePtr()) - ->getElementType(); - CXXRecordDecl *RD = getLangOpts().CPlusPlus - ? Type.getNonReferenceType()->getAsCXXRecordDecl() - : nullptr; - // FIXME This code must be replaced by actual copying and destructing of the - // lastprivate variable. - if (RD) { - CXXMethodDecl *MD = LookupCopyingAssignment(RD, 0, false, 0); - DeclAccessPair FoundDecl = DeclAccessPair::make(MD, MD->getAccess()); - if (MD) { - if (CheckMemberAccess(ELoc, RD, FoundDecl) == AR_inaccessible || - MD->isDeleted()) { - Diag(ELoc, diag::err_omp_required_method) - << getOpenMPClauseName(OMPC_lastprivate) << 2; - bool IsDecl = VD->isThisDeclarationADefinition(Context) == - VarDecl::DeclarationOnly; - Diag(VD->getLocation(), - IsDecl ? diag::note_previous_decl : diag::note_defined_here) - << VD; - Diag(RD->getLocation(), diag::note_previous_decl) << RD; - continue; - } - MarkFunctionReferenced(ELoc, MD); - DiagnoseUseOfDecl(MD, ELoc); - } - - CXXDestructorDecl *DD = RD->getDestructor(); - if (DD) { - PartialDiagnostic PD = - PartialDiagnostic(PartialDiagnostic::NullDiagnostic()); - if (CheckDestructorAccess(ELoc, DD, PD) == AR_inaccessible || - DD->isDeleted()) { - Diag(ELoc, diag::err_omp_required_method) - << getOpenMPClauseName(OMPC_lastprivate) << 4; - bool IsDecl = VD->isThisDeclarationADefinition(Context) == - VarDecl::DeclarationOnly; - Diag(VD->getLocation(), - IsDecl ? diag::note_previous_decl : diag::note_defined_here) - << VD; - Diag(RD->getLocation(), diag::note_previous_decl) << RD; - continue; - } - MarkFunctionReferenced(ELoc, DD); - DiagnoseUseOfDecl(DD, ELoc); - } - } + Type = Context.getBaseElementType(Type).getNonReferenceType(); + auto *SrcVD = BuildVarDecl(*this, DE->getLocStart(), + Type.getUnqualifiedType(), ".lastprivate.src"); + auto *PseudoSrcExpr = BuildDeclRefExpr(SrcVD, Type.getUnqualifiedType(), + VK_LValue, DE->getExprLoc()).get(); + auto *DstVD = + BuildVarDecl(*this, DE->getLocStart(), Type, ".lastprivate.dst"); + auto *PseudoDstExpr = + BuildDeclRefExpr(DstVD, Type, VK_LValue, DE->getExprLoc()).get(); + // For arrays generate assignment operation for single element and replace + // it by the original array element in CodeGen. + auto AssignmentOp = BuildBinOp(/*S=*/nullptr, DE->getExprLoc(), BO_Assign, + PseudoDstExpr, PseudoSrcExpr); + if (AssignmentOp.isInvalid()) + continue; + AssignmentOp = ActOnFinishFullExpr(AssignmentOp.get(), DE->getExprLoc(), + /*DiscardedValue=*/true); + if (AssignmentOp.isInvalid()) + continue; if (DVar.CKind != OMPC_firstprivate) DSAStack->addDSA(VD, DE, OMPC_lastprivate); Vars.push_back(DE); + SrcExprs.push_back(PseudoSrcExpr); + DstExprs.push_back(PseudoDstExpr); + AssignmentOps.push_back(AssignmentOp.get()); } if (Vars.empty()) return nullptr; return OMPLastprivateClause::Create(Context, StartLoc, LParenLoc, EndLoc, - Vars); + Vars, SrcExprs, DstExprs, AssignmentOps); } OMPClause *Sema::ActOnOpenMPSharedClause(ArrayRef VarList, Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -1889,6 +1889,22 @@ for (unsigned i = 0; i != NumVars; ++i) Vars.push_back(Reader->Reader.ReadSubExpr()); C->setVarRefs(Vars); + Vars.clear(); + for (unsigned i = 0; i != NumVars; ++i) + Vars.push_back(Reader->Reader.ReadSubExpr()); + C->setPrivateCopies(Vars); + Vars.clear(); + for (unsigned i = 0; i != NumVars; ++i) + Vars.push_back(Reader->Reader.ReadSubExpr()); + C->setSourceExprs(Vars); + Vars.clear(); + for (unsigned i = 0; i != NumVars; ++i) + Vars.push_back(Reader->Reader.ReadSubExpr()); + C->setDestinationExprs(Vars); + Vars.clear(); + for (unsigned i = 0; i != NumVars; ++i) + Vars.push_back(Reader->Reader.ReadSubExpr()); + C->setAssignmentOps(Vars); } void OMPClauseReader::VisitOMPSharedClause(OMPSharedClause *C) { Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -1798,6 +1798,14 @@ Writer->Writer.AddSourceLocation(C->getLParenLoc(), Record); for (auto *VE : C->varlists()) Writer->Writer.AddStmt(VE); + for (auto *E : C->private_copies()) + Writer->Writer.AddStmt(E); + for (auto *E : C->source_exprs()) + Writer->Writer.AddStmt(E); + for (auto *E : C->destination_exprs()) + Writer->Writer.AddStmt(E); + for (auto *E : C->assignment_ops()) + Writer->Writer.AddStmt(E); } void OMPClauseWriter::VisitOMPSharedClause(OMPSharedClause *C) { Index: test/OpenMP/for_lastprivate_codegen.cpp =================================================================== --- test/OpenMP/for_lastprivate_codegen.cpp +++ test/OpenMP/for_lastprivate_codegen.cpp @@ -0,0 +1,317 @@ +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -std=c++11 -triple x86_64-unknown-unknown -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -triple x86_64-unknown-unknown -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -std=c++11 -DLAMBDA -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck -check-prefix=LAMBDA %s +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -fblocks -DBLOCKS -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck -check-prefix=BLOCKS %s +// expected-no-diagnostics +#ifndef HEADER +#define HEADER + +template +struct S { + T f; + S(T a) : f(a) {} + S() : f() {} + S &operator=(const S &); + operator T() { return T(); } + ~S() {} +}; + +volatile int g = 1212; + +// CHECK: [[S_FLOAT_TY:%.+]] = type { float } +// CHECK: [[CAP_MAIN_TY:%.+]] = type { i{{[0-9]+}}*, [2 x i{{[0-9]+}}]*, [2 x [[S_FLOAT_TY]]]*, [[S_FLOAT_TY]]* } +// CHECK: [[S_INT_TY:%.+]] = type { i{{[0-9]+}} } +// CHECK: [[CAP_TMAIN_TY:%.+]] = type { i{{[0-9]+}}*, [2 x i{{[0-9]+}}]*, [2 x [[S_INT_TY]]]*, [[S_INT_TY]]* } +// CHECK: [[IMPLICIT_BARRIER_LOC:@.+]] = private unnamed_addr constant %{{.+}} { i32 0, i32 66, i32 0, i32 0, i8* +template +T tmain() { + S test; + T t_var = T(); + T vec[] = {1, 2}; + S s_arr[] = {1, 2}; + S var(3); +#pragma omp parallel +#pragma omp for lastprivate(t_var, vec, s_arr, var) + for (int i = 0; i < 2; ++i) { + vec[i] = t_var; + s_arr[i] = var; + } + return T(); +} + +int main() { +#ifdef LAMBDA + // LAMBDA: [[G:@.+]] = global i{{[0-9]+}} 1212, + // LAMBDA-LABEL: @main + // LAMBDA: call void [[OUTER_LAMBDA:@.+]]( + [&]() { + // LAMBDA: define{{.*}} internal{{.*}} void [[OUTER_LAMBDA]]( + // LAMBDA: call void {{.+}}* @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i8* %{{.+}}) +#pragma omp parallel +#pragma omp for lastprivate(g) + for (int i = 0; i < 2; ++i) { + // LAMBDA: define{{.*}} internal{{.*}} void [[OMP_REGION]](i32* %{{.+}}, i32* %{{.+}}, %{{.+}}* [[ARG:%.+]]) + // LAMBDA: alloca i{{[0-9]+}}, + // LAMBDA: alloca i{{[0-9]+}}, + // LAMBDA: alloca i{{[0-9]+}}, + // LAMBDA: alloca i{{[0-9]+}}, + // LAMBDA: alloca i{{[0-9]+}}, + // LAMBDA: [[G_PRIVATE_ADDR:%.+]] = alloca i{{[0-9]+}}, + // LAMBDA: store %{{.+}}* [[ARG]], %{{.+}}** [[ARG_REF:%.+]], + // LAMBDA: call {{.+}} @__kmpc_for_static_init_4(%{{.+}}* @{{.+}}, i32 %{{.+}}, i32 34, i32* [[IS_LAST_ADDR:%.+]], i32* %{{.+}}, i32* %{{.+}}, i32* %{{.+}}, i32 1, i32 1) + // LAMBDA: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // LAMBDA: [[G_PRIVATE_ADDR_REF:%.+]] = getelementptr inbounds %{{.+}}, %{{.+}}* [[ARG:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: store i{{[0-9]+}}* [[G_PRIVATE_ADDR]], i{{[0-9]+}}** [[G_PRIVATE_ADDR_REF]] + // LAMBDA: call void [[INNER_LAMBDA:@.+]](%{{.+}}* [[ARG]]) + // LAMBDA: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 %{{.+}}) + g = 1; + // Check for final copying of private values back to original vars. + // LAMBDA: [[IS_LAST_VAL:%.+]] = load i32, i32* [[IS_LAST_ADDR]], + // LAMBDA: [[IS_LAST_ITER:%.+]] = icmp ne i32 [[IS_LAST_VAL]], 0 + // LAMBDA: br i1 [[IS_LAST_ITER:%.+]], label %[[LAST_THEN:.+]], label %[[LAST_DONE:.+]] + // LAMBDA: [[LAST_THEN]] + // Actual copying. + + // original g=private_g; + // LAMBDA: [[G_VAL:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // LAMBDA: store volatile i{{[0-9]+}} [[G_VAL]], i{{[0-9]+}}* [[G]], + // LAMBDA: br label %[[LAST_DONE]] + // LAMBDA: [[LAST_DONE]] + // LAMBDA: [[GTID_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** %{{.+}} + // LAMBDA: [[GTID:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[GTID_REF]] + // LAMBDA: call i32 @__kmpc_cancel_barrier(%{{.+}}* @{{.+}}, i{{[0-9]+}} [[GTID]]) + [&]() { + // LAMBDA: define {{.+}} void [[INNER_LAMBDA]](%{{.+}}* [[ARG_PTR:%.+]]) + // LAMBDA: store %{{.+}}* [[ARG_PTR]], %{{.+}}** [[ARG_PTR_REF:%.+]], + g = 2; + // LAMBDA: [[ARG_PTR:%.+]] = load %{{.+}}*, %{{.+}}** [[ARG_PTR_REF]] + // LAMBDA: [[G_PTR_REF:%.+]] = getelementptr inbounds %{{.+}}, %{{.+}}* [[ARG_PTR]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 + // LAMBDA: [[G_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** [[G_PTR_REF]] + // LAMBDA: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* [[G_REF]] + }(); + } + }(); + return 0; +#elif defined(BLOCKS) + // BLOCKS: [[G:@.+]] = global i{{[0-9]+}} 1212, + // BLOCKS-LABEL: @main + // BLOCKS: call void {{%.+}}(i8* + ^{ + // BLOCKS: define{{.*}} internal{{.*}} void {{.+}}(i8* + // BLOCKS: call void {{.+}}* @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i8* %{{.+}}) +#pragma omp parallel +#pragma omp for lastprivate(g) + for (int i = 0; i < 2; ++i) { + // BLOCKS: define{{.*}} internal{{.*}} void [[OMP_REGION]](i32* %{{.+}}, i32* %{{.+}}, %{{.+}}* [[ARG:%.+]]) + // BLOCKS: alloca i{{[0-9]+}}, + // BLOCKS: alloca i{{[0-9]+}}, + // BLOCKS: alloca i{{[0-9]+}}, + // BLOCKS: alloca i{{[0-9]+}}, + // BLOCKS: alloca i{{[0-9]+}}, + // BLOCKS: [[G_PRIVATE_ADDR:%.+]] = alloca i{{[0-9]+}}, + // BLOCKS: store %{{.+}}* [[ARG]], %{{.+}}** [[ARG_REF:%.+]], + // BLOCKS: call {{.+}} @__kmpc_for_static_init_4(%{{.+}}* @{{.+}}, i32 %{{.+}}, i32 34, i32* [[IS_LAST_ADDR:%.+]], i32* %{{.+}}, i32* %{{.+}}, i32* %{{.+}}, i32 1, i32 1) + // BLOCKS: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: i{{[0-9]+}}* [[G_PRIVATE_ADDR]] + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: call void {{%.+}}(i8* + // BLOCKS: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 %{{.+}}) + g = 1; + // Check for final copying of private values back to original vars. + // BLOCKS: [[IS_LAST_VAL:%.+]] = load i32, i32* [[IS_LAST_ADDR]], + // BLOCKS: [[IS_LAST_ITER:%.+]] = icmp ne i32 [[IS_LAST_VAL]], 0 + // BLOCKS: br i1 [[IS_LAST_ITER:%.+]], label %[[LAST_THEN:.+]], label %[[LAST_DONE:.+]] + // BLOCKS: [[LAST_THEN]] + // Actual copying. + + // original g=private_g; + // BLOCKS: [[G_VAL:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[G_PRIVATE_ADDR]], + // BLOCKS: store volatile i{{[0-9]+}} [[G_VAL]], i{{[0-9]+}}* [[G]], + // BLOCKS: br label %[[LAST_DONE]] + // BLOCKS: [[LAST_DONE]] + // BLOCKS: [[GTID_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** %{{.+}} + // BLOCKS: [[GTID:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[GTID_REF]] + // BLOCKS: call i32 @__kmpc_cancel_barrier(%{{.+}}* @{{.+}}, i{{[0-9]+}} [[GTID]]) + g = 1; + ^{ + // BLOCKS: define {{.+}} void {{@.+}}(i8* + g = 2; + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* + // BLOCKS-NOT: [[G]]{{[[^:word:]]}} + // BLOCKS: ret + }(); + } + }(); + return 0; +#else + S test; + int t_var = 0; + int vec[] = {1, 2}; + S s_arr[] = {1, 2}; + S var(3); +#pragma omp parallel +#pragma omp for lastprivate(t_var, vec, s_arr, var) + for (int i = 0; i < 2; ++i) { + vec[i] = t_var; + s_arr[i] = var; + } + return tmain(); +#endif +} + +// CHECK: define i{{[0-9]+}} @main() +// CHECK: [[TEST:%.+]] = alloca [[S_FLOAT_TY]], +// CHECK: call {{.*}} [[S_FLOAT_TY_DEF_CONSTR:@.+]]([[S_FLOAT_TY]]* [[TEST]]) +// CHECK: %{{.+}} = bitcast [[CAP_MAIN_TY]]* +// CHECK: call void (%{{.+}}*, i{{[0-9]+}}, void (i{{[0-9]+}}*, i{{[0-9]+}}*, ...)*, ...)* @__kmpc_fork_call(%{{.+}}* @{{.+}}, i{{[0-9]+}} 1, void (i{{[0-9]+}}*, i{{[0-9]+}}*, ...)* bitcast (void (i{{[0-9]+}}*, i{{[0-9]+}}*, [[CAP_MAIN_TY]]*)* [[MAIN_MICROTASK:@.+]] to void +// CHECK: = call {{.+}} [[TMAIN_INT:@.+]]() +// CHECK: call void [[S_FLOAT_TY_DESTR:@.+]]([[S_FLOAT_TY]]* +// CHECK: ret +// +// CHECK: define internal void [[MAIN_MICROTASK]](i{{[0-9]+}}* [[GTID_ADDR:%.+]], i{{[0-9]+}}* %{{.+}}, [[CAP_MAIN_TY]]* %{{.+}}) +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: [[T_VAR_PRIV:%.+]] = alloca i{{[0-9]+}}, +// CHECK: [[VEC_PRIV:%.+]] = alloca [2 x i{{[0-9]+}}], +// CHECK: [[S_ARR_PRIV:%.+]] = alloca [2 x [[S_FLOAT_TY]]], +// CHECK: [[VAR_PRIV:%.+]] = alloca [[S_FLOAT_TY]], +// CHECK: store i{{[0-9]+}}* [[GTID_ADDR]], i{{[0-9]+}}** [[GTID_ADDR_REF:%.+]] + +// Check for default initialization. +// CHECK: [[T_VAR_PTR_REF:%.+]] = getelementptr inbounds [[CAP_MAIN_TY]], [[CAP_MAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[T_VAR_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** [[T_VAR_PTR_REF]], +// CHECK-NOT: [[T_VAR_PRIV]] +// CHECK: [[VEC_PTR_REF:%.+]] = getelementptr inbounds [[CAP_MAIN_TY]], [[CAP_MAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 1 +// CHECK: [[VEC_REF:%.+]] = load [2 x i{{[0-9]+}}]*, [2 x i{{[0-9]+}}]** [[VEC_PTR_REF:%.+]], +// CHECK-NOT: [[VEC_PRIV]] +// CHECK: [[S_ARR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_MAIN_TY]], [[CAP_MAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 2 +// CHECK: [[S_ARR_REF:%.+]] = load [2 x [[S_FLOAT_TY]]]*, [2 x [[S_FLOAT_TY]]]** [[S_ARR_REF_PTR]], +// CHECK: [[S_ARR_PRIV_ITEM:%.+]] = phi [[S_FLOAT_TY]]* +// CHECK: call {{.*}} [[S_FLOAT_TY_DEF_CONSTR]]([[S_FLOAT_TY]]* [[S_ARR_PRIV_ITEM]]) +// CHECK: [[VAR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_MAIN_TY]], [[CAP_MAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 3 +// CHECK: [[VAR_REF:%.+]] = load [[S_FLOAT_TY]]*, [[S_FLOAT_TY]]** [[VAR_REF_PTR]], +// CHECK: call {{.*}} [[S_FLOAT_TY_DEF_CONSTR]]([[S_FLOAT_TY]]* [[VAR_PRIV]]) +// CHECK: call {{.+}} @__kmpc_for_static_init_4(%{{.+}}* @{{.+}}, i32 %{{.+}}, i32 34, i32* [[IS_LAST_ADDR:%.+]], i32* %{{.+}}, i32* %{{.+}}, i32* %{{.+}}, i32 1, i32 1) +// +// CHECK: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 %{{.+}}) + +// Check for final copying of private values back to original vars. +// CHECK: [[IS_LAST_VAL:%.+]] = load i32, i32* [[IS_LAST_ADDR]], +// CHECK: [[IS_LAST_ITER:%.+]] = icmp ne i32 [[IS_LAST_VAL]], 0 +// CHECK: br i1 [[IS_LAST_ITER:%.+]], label %[[LAST_THEN:.+]], label %[[LAST_DONE:.+]] +// CHECK: [[LAST_THEN]] +// Actual copying. + +// original t_var=private_t_var; +// CHECK: [[T_VAR_VAL:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[T_VAR_PRIV]], +// CHECK: store i{{[0-9]+}} [[T_VAR_VAL]], i{{[0-9]+}}* [[T_VAR_REF]], + +// original vec[]=private_vec[]; +// CHECK: [[VEC_DEST:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_REF]] to i8* +// CHECK: [[VEC_SRC:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_PRIV]] to i8* +// CHECK: call void @llvm.memcpy.{{.+}}(i8* [[VEC_DEST]], i8* [[VEC_SRC]], + +// original s_arr[]=private_s_arr[]; +// CHECK: [[S_ARR_PRIV_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_FLOAT_TY]]], [2 x [[S_FLOAT_TY]]]* [[S_ARR_PRIV]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[S_ARR_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_FLOAT_TY]]], [2 x [[S_FLOAT_TY]]]* [[S_ARR_REF]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[S_ARR_END:%.+]] = getelementptr [[S_FLOAT_TY]], [[S_FLOAT_TY]]* [[S_ARR_BEGIN]], i{{[0-9]+}} 2 +// CHECK: [[IS_EMPTY:%.+]] = icmp eq [[S_FLOAT_TY]]* [[S_ARR_BEGIN]], [[S_ARR_END]] +// CHECK: br i1 [[IS_EMPTY]], label %[[S_ARR_BODY_DONE:.+]], label %[[S_ARR_BODY:.+]] +// CHECK: [[S_ARR_BODY]] +// CHECK: call {{.*}} [[S_FLOAT_TY_COPY_ASSIGN:@.+]]([[S_FLOAT_TY]]* {{.+}}, [[S_FLOAT_TY]]* {{.+}}) +// CHECK: br i1 {{.+}}, label %[[S_ARR_BODY_DONE]], label %[[S_ARR_BODY]] +// CHECK: [[S_ARR_BODY_DONE]] + +// original var=private_var; +// CHECK: call {{.*}} [[S_FLOAT_TY_COPY_ASSIGN:@.+]]([[S_FLOAT_TY]]* [[VAR_REF]], [[S_FLOAT_TY]]* {{.*}} [[VAR_PRIV]]) +// CHECK: br label %[[LAST_DONE]] +// CHECK: [[LAST_DONE]] +// CHECK-DAG: call void [[S_FLOAT_TY_DESTR]]([[S_FLOAT_TY]]* [[VAR_PRIV]]) +// CHECK-DAG: call void [[S_FLOAT_TY_DESTR]]([[S_FLOAT_TY]]* +// CHECK: [[GTID_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** [[GTID_ADDR_REF]] +// CHECK: [[GTID:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[GTID_REF]] +// CHECK: call i32 @__kmpc_cancel_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i{{[0-9]+}} [[GTID]]) +// CHECK: ret void + +// CHECK: define {{.*}} i{{[0-9]+}} [[TMAIN_INT]]() +// CHECK: [[TEST:%.+]] = alloca [[S_INT_TY]], +// CHECK: call {{.*}} [[S_INT_TY_DEF_CONSTR:@.+]]([[S_INT_TY]]* [[TEST]]) +// CHECK: call void (%{{.+}}*, i{{[0-9]+}}, void (i{{[0-9]+}}*, i{{[0-9]+}}*, ...)*, ...)* @__kmpc_fork_call(%{{.+}}* @{{.+}}, i{{[0-9]+}} 1, void (i{{[0-9]+}}*, i{{[0-9]+}}*, ...)* bitcast (void (i{{[0-9]+}}*, i{{[0-9]+}}*, [[CAP_TMAIN_TY]]*)* [[TMAIN_MICROTASK:@.+]] to void +// CHECK: call void [[S_INT_TY_DESTR:@.+]]([[S_INT_TY]]* +// CHECK: ret +// +// CHECK: define internal void [[TMAIN_MICROTASK]](i{{[0-9]+}}* [[GTID_ADDR:%.+]], i{{[0-9]+}}* %{{.+}}, [[CAP_TMAIN_TY]]* %{{.+}}) +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: alloca i{{[0-9]+}}, +// CHECK: [[T_VAR_PRIV:%.+]] = alloca i{{[0-9]+}}, +// CHECK: [[VEC_PRIV:%.+]] = alloca [2 x i{{[0-9]+}}], +// CHECK: [[S_ARR_PRIV:%.+]] = alloca [2 x [[S_INT_TY]]], +// CHECK: [[VAR_PRIV:%.+]] = alloca [[S_INT_TY]], +// CHECK: store i{{[0-9]+}}* [[GTID_ADDR]], i{{[0-9]+}}** [[GTID_ADDR_REF:%.+]] + +// Check for default initialization. +// CHECK: [[T_VAR_PTR_REF:%.+]] = getelementptr inbounds [[CAP_TMAIN_TY]], [[CAP_TMAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[T_VAR_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** [[T_VAR_PTR_REF]], +// CHECK-NOT: [[T_VAR_PRIV]] +// CHECK: [[VEC_PTR_REF:%.+]] = getelementptr inbounds [[CAP_TMAIN_TY]], [[CAP_TMAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 1 +// CHECK: [[VEC_REF:%.+]] = load [2 x i{{[0-9]+}}]*, [2 x i{{[0-9]+}}]** [[VEC_PTR_REF:%.+]], +// CHECK-NOT: [[VEC_PRIV]] +// CHECK: [[S_ARR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_TMAIN_TY]], [[CAP_TMAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 2 +// CHECK: [[S_ARR_REF:%.+]] = load [2 x [[S_INT_TY]]]*, [2 x [[S_INT_TY]]]** [[S_ARR_REF_PTR]], +// CHECK: [[S_ARR_PRIV_ITEM:%.+]] = phi [[S_INT_TY]]* +// CHECK: call {{.*}} [[S_INT_TY_DEF_CONSTR]]([[S_INT_TY]]* [[S_ARR_PRIV_ITEM]]) +// CHECK: [[VAR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_TMAIN_TY]], [[CAP_TMAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 3 +// CHECK: [[VAR_REF:%.+]] = load [[S_INT_TY]]*, [[S_INT_TY]]** [[VAR_REF_PTR]], +// CHECK: call {{.*}} [[S_INT_TY_DEF_CONSTR]]([[S_INT_TY]]* [[VAR_PRIV]]) +// CHECK: call {{.+}} @__kmpc_for_static_init_4(%{{.+}}* @{{.+}}, i32 %{{.+}}, i32 34, i32* [[IS_LAST_ADDR:%.+]], i32* %{{.+}}, i32* %{{.+}}, i32* %{{.+}}, i32 1, i32 1) +// +// CHECK: call void @__kmpc_for_static_fini(%{{.+}}* @{{.+}}, i32 %{{.+}}) + +// Check for final copying of private values back to original vars. +// CHECK: [[IS_LAST_VAL:%.+]] = load i32, i32* [[IS_LAST_ADDR]], +// CHECK: [[IS_LAST_ITER:%.+]] = icmp ne i32 [[IS_LAST_VAL]], 0 +// CHECK: br i1 [[IS_LAST_ITER:%.+]], label %[[LAST_THEN:.+]], label %[[LAST_DONE:.+]] +// CHECK: [[LAST_THEN]] +// Actual copying. + +// original t_var=private_t_var; +// CHECK: [[T_VAR_VAL:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[T_VAR_PRIV]], +// CHECK: store i{{[0-9]+}} [[T_VAR_VAL]], i{{[0-9]+}}* [[T_VAR_REF]], + +// original vec[]=private_vec[]; +// CHECK: [[VEC_DEST:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_REF]] to i8* +// CHECK: [[VEC_SRC:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_PRIV]] to i8* +// CHECK: call void @llvm.memcpy.{{.+}}(i8* [[VEC_DEST]], i8* [[VEC_SRC]], + +// original s_arr[]=private_s_arr[]; +// CHECK: [[S_ARR_PRIV_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_INT_TY]]], [2 x [[S_INT_TY]]]* [[S_ARR_PRIV]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[S_ARR_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_INT_TY]]], [2 x [[S_INT_TY]]]* [[S_ARR_REF]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[S_ARR_END:%.+]] = getelementptr [[S_INT_TY]], [[S_INT_TY]]* [[S_ARR_BEGIN]], i{{[0-9]+}} 2 +// CHECK: [[IS_EMPTY:%.+]] = icmp eq [[S_INT_TY]]* [[S_ARR_BEGIN]], [[S_ARR_END]] +// CHECK: br i1 [[IS_EMPTY]], label %[[S_ARR_BODY_DONE:.+]], label %[[S_ARR_BODY:.+]] +// CHECK: [[S_ARR_BODY]] +// CHECK: call {{.*}} [[S_INT_TY_COPY_ASSIGN:@.+]]([[S_INT_TY]]* {{.+}}, [[S_INT_TY]]* {{.+}}) +// CHECK: br i1 {{.+}}, label %[[S_ARR_BODY_DONE]], label %[[S_ARR_BODY]] +// CHECK: [[S_ARR_BODY_DONE]] + +// original var=private_var; +// CHECK: call {{.*}} [[S_INT_TY_COPY_ASSIGN:@.+]]([[S_INT_TY]]* [[VAR_REF]], [[S_INT_TY]]* {{.*}} [[VAR_PRIV]]) +// CHECK: br label %[[LAST_DONE]] +// CHECK: [[LAST_DONE]] +// CHECK-DAG: call void [[S_INT_TY_DESTR]]([[S_INT_TY]]* [[VAR_PRIV]]) +// CHECK-DAG: call void [[S_INT_TY_DESTR]]([[S_INT_TY]]* +// CHECK: [[GTID_REF:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** [[GTID_ADDR_REF]] +// CHECK: [[GTID:%.+]] = load i{{[0-9]+}}, i{{[0-9]+}}* [[GTID_REF]] +// CHECK: call i32 @__kmpc_cancel_barrier(%{{.+}}* [[IMPLICIT_BARRIER_LOC]], i{{[0-9]+}} [[GTID]]) +// CHECK: ret void +#endif + Index: test/OpenMP/for_lastprivate_messages.cpp =================================================================== --- test/OpenMP/for_lastprivate_messages.cpp +++ test/OpenMP/for_lastprivate_messages.cpp @@ -15,15 +15,17 @@ public: S2() : a(0) {} S2(S2 &s2) : a(s2.a) {} + const S2 &operator =(const S2&) const; + S2 &operator =(const S2&); static float S2s; // expected-note {{static data member is predetermined as shared}} static const float S2sc; }; const float S2::S2sc = 0; // expected-note {{static data member is predetermined as shared}} const S2 b; const S2 ba[5]; -class S3 { // expected-note 2 {{'S3' declared here}} +class S3 { int a; - S3 &operator=(const S3 &s3); + S3 &operator=(const S3 &s3); // expected-note 2 {{implicitly declared private here}} public: S3() : a(0) {} @@ -32,17 +34,17 @@ const S3 c; // expected-note {{global variable is predetermined as shared}} const S3 ca[5]; // expected-note {{global variable is predetermined as shared}} extern const int f; // expected-note {{global variable is predetermined as shared}} -class S4 { // expected-note 3 {{'S4' declared here}} +class S4 { int a; - S4(); + S4(); // expected-note 3 {{implicitly declared private here}} S4(const S4 &s4); public: S4(int v) : a(v) {} }; -class S5 { // expected-note {{'S5' declared here}} +class S5 { int a; - S5() : a(0) {} + S5() : a(0) {} // expected-note {{implicitly declared private here}} public: S5(const S5 &s5) : a(s5.a) {} @@ -62,8 +64,8 @@ template int foomain(int argc, char **argv) { - I e(4); // expected-note {{'e' defined here}} - I g(5); // expected-note {{'g' defined here}} + I e(4); + I g(5); int i; int &j = i; // expected-note {{'j' defined here}} #pragma omp parallel @@ -107,7 +109,7 @@ for (int k = 0; k < argc; ++k) ++k; #pragma omp parallel -#pragma omp for lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp for lastprivate(e, g) // expected-error 2 {{calling a private constructor of class 'S4'}} for (int k = 0; k < argc; ++k) ++k; #pragma omp parallel @@ -143,9 +145,9 @@ int main(int argc, char **argv) { const int d = 5; // expected-note {{constant variable is predetermined as shared}} const int da[5] = {0}; // expected-note {{constant variable is predetermined as shared}} - S4 e(4); // expected-note {{'e' defined here}} - S5 g(5); // expected-note {{'g' defined here}} - S3 m; // expected-note 2 {{'m' defined here}} + S4 e(4); + S5 g(5); + S3 m; S6 n(2); int i; int &j = i; // expected-note {{'j' defined here}} @@ -223,11 +225,11 @@ for (i = 0; i < argc; ++i) foo(); #pragma omp parallel -#pragma omp for lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp for lastprivate(e, g) // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel -#pragma omp for lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp for lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel @@ -255,7 +257,7 @@ for (i = 0; i < argc; ++i) foo(); #pragma omp parallel -#pragma omp for firstprivate(m) lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp for firstprivate(m) lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel Index: test/OpenMP/for_simd_lastprivate_messages.cpp =================================================================== --- test/OpenMP/for_simd_lastprivate_messages.cpp +++ test/OpenMP/for_simd_lastprivate_messages.cpp @@ -15,15 +15,17 @@ public: S2() : a(0) {} S2(S2 &s2) : a(s2.a) {} + S2 &operator =(const S2&); + const S2 &operator =(const S2&) const; static float S2s; // expected-note {{static data member is predetermined as shared}} static const float S2sc; }; const float S2::S2sc = 0; // expected-note {{static data member is predetermined as shared}} const S2 b; const S2 ba[5]; -class S3 { // expected-note 2 {{'S3' declared here}} +class S3 { int a; - S3 &operator=(const S3 &s3); + S3 &operator=(const S3 &s3); // expected-note 2 {{implicitly declared private here}} public: S3() : a(0) {} @@ -32,17 +34,17 @@ const S3 c; // expected-note {{global variable is predetermined as shared}} const S3 ca[5]; // expected-note {{global variable is predetermined as shared}} extern const int f; // expected-note {{global variable is predetermined as shared}} -class S4 { // expected-note 3 {{'S4' declared here}} +class S4 { int a; - S4(); + S4(); // expected-note 3 {{implicitly declared private here}} S4(const S4 &s4); public: S4(int v) : a(v) {} }; -class S5 { // expected-note {{'S5' declared here}} +class S5 { int a; - S5() : a(0) {} + S5() : a(0) {} // expected-note {{implicitly declared private here}} public: S5(const S5 &s5) : a(s5.a) {} @@ -62,8 +64,8 @@ template int foomain(int argc, char **argv) { - I e(4); // expected-note {{'e' defined here}} - I g(5); // expected-note {{'g' defined here}} + I e(4); + I g(5); int i; int &j = i; // expected-note {{'j' defined here}} #pragma omp parallel @@ -107,7 +109,7 @@ for (int k = 0; k < argc; ++k) ++k; #pragma omp parallel -#pragma omp for simd lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp for simd lastprivate(e, g) // expected-error 2 {{calling a private constructor of class 'S4'}} for (int k = 0; k < argc; ++k) ++k; #pragma omp parallel @@ -143,9 +145,9 @@ int main(int argc, char **argv) { const int d = 5; // expected-note {{constant variable is predetermined as shared}} const int da[5] = {0}; // expected-note {{constant variable is predetermined as shared}} - S4 e(4); // expected-note {{'e' defined here}} - S5 g(5); // expected-note {{'g' defined here}} - S3 m; // expected-note 2 {{'m' defined here}} + S4 e(4); + S5 g(5); + S3 m; S6 n(2); int i; int &j = i; // expected-note {{'j' defined here}} @@ -223,11 +225,11 @@ for (i = 0; i < argc; ++i) foo(); #pragma omp parallel -#pragma omp for simd lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp for simd lastprivate(e, g) // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel -#pragma omp for simd lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp for simd lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel @@ -255,7 +257,7 @@ for (i = 0; i < argc; ++i) foo(); #pragma omp parallel -#pragma omp for simd firstprivate(m) lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp for simd firstprivate(m) lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel Index: test/OpenMP/parallel_firstprivate_codegen.cpp =================================================================== --- test/OpenMP/parallel_firstprivate_codegen.cpp +++ test/OpenMP/parallel_firstprivate_codegen.cpp @@ -165,20 +165,13 @@ // CHECK: store i{{[0-9]+}} [[T_VAR_VAL]], i{{[0-9]+}}* [[T_VAR_PRIV]], // CHECK: [[VEC_PTR_REF:%.+]] = getelementptr inbounds [[CAP_MAIN_TY]], [[CAP_MAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 0 // CHECK: [[VEC_REF:%.+]] = load [2 x i{{[0-9]+}}]*, [2 x i{{[0-9]+}}]** [[VEC_PTR_REF:%.+]], -// CHECK: br label %[[VEC_PRIV_INIT:.+]] -// CHECK: [[VEC_PRIV_INIT]] // CHECK: [[VEC_DEST:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_PRIV]] to i8* // CHECK: [[VEC_SRC:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_REF]] to i8* // CHECK: call void @llvm.memcpy.{{.+}}(i8* [[VEC_DEST]], i8* [[VEC_SRC]], -// CHECK: br label %[[VEC_PRIV_INIT_END:.+]] -// CHECK: [[VEC_PRIV_INIT_END]] // CHECK: [[S_ARR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_MAIN_TY]], [[CAP_MAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 2 // CHECK: [[S_ARR_REF:%.+]] = load [2 x [[S_FLOAT_TY]]]*, [2 x [[S_FLOAT_TY]]]** [[S_ARR_REF_PTR]], -// CHECK: br label %[[S_ARR_PRIV_INIT:.+]] -// CHECK: [[S_ARR_PRIV_INIT]] // CHECK: [[S_ARR_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_FLOAT_TY]]], [2 x [[S_FLOAT_TY]]]* [[S_ARR_REF]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 // CHECK: [[S_ARR_PRIV_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_FLOAT_TY]]], [2 x [[S_FLOAT_TY]]]* [[S_ARR_PRIV]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 -// CHECK: [[S_ARR_END:%.+]] = getelementptr [[S_FLOAT_TY]], [[S_FLOAT_TY]]* [[S_ARR_BEGIN]], i{{[0-9]+}} 2 // CHECK: [[S_ARR_PRIV_END:%.+]] = getelementptr [[S_FLOAT_TY]], [[S_FLOAT_TY]]* [[S_ARR_PRIV_BEGIN]], i{{[0-9]+}} 2 // CHECK: [[IS_EMPTY:%.+]] = icmp eq [[S_FLOAT_TY]]* [[S_ARR_PRIV_BEGIN]], [[S_ARR_PRIV_END]] // CHECK: br i1 [[IS_EMPTY]], label %[[S_ARR_BODY_DONE:.+]], label %[[S_ARR_BODY:.+]] @@ -187,8 +180,6 @@ // CHECK: call {{.*}} [[S_FLOAT_TY_COPY_CONSTR:@.+]]([[S_FLOAT_TY]]* {{.+}}, [[S_FLOAT_TY]]* {{.+}}, [[ST_TY]]* [[ST_TY_TEMP]]) // CHECK: call {{.*}} [[ST_TY_DESTR:@.+]]([[ST_TY]]* [[ST_TY_TEMP]]) // CHECK: br i1 {{.+}}, label %{{.+}}, label %[[S_ARR_BODY]] -// CHECK: br label %[[S_ARR_PRIV_INIT_END:.+]] -// CHECK: [[S_ARR_PRIV_INIT_END]] // CHECK: [[VAR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_MAIN_TY]], [[CAP_MAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 3 // CHECK: [[VAR_REF:%.+]] = load [[S_FLOAT_TY]]*, [[S_FLOAT_TY]]** [[VAR_REF_PTR]], // CHECK: call {{.*}} [[ST_TY_DEFAULT_CONSTR]]([[ST_TY]]* [[ST_TY_TEMP:%.+]]) @@ -220,20 +211,13 @@ // CHECK: store i{{[0-9]+}} [[T_VAR_VAL]], i{{[0-9]+}}* [[T_VAR_PRIV]], // CHECK: [[VEC_PTR_REF:%.+]] = getelementptr inbounds [[CAP_TMAIN_TY]], [[CAP_TMAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 0 // CHECK: [[VEC_REF:%.+]] = load [2 x i{{[0-9]+}}]*, [2 x i{{[0-9]+}}]** [[VEC_PTR_REF:%.+]], -// CHECK: br label %[[VEC_PRIV_INIT:.+]] -// CHECK: [[VEC_PRIV_INIT]] // CHECK: [[VEC_DEST:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_PRIV]] to i8* // CHECK: [[VEC_SRC:%.+]] = bitcast [2 x i{{[0-9]+}}]* [[VEC_REF]] to i8* // CHECK: call void @llvm.memcpy.{{.+}}(i8* [[VEC_DEST]], i8* [[VEC_SRC]], -// CHECK: br label %[[VEC_PRIV_INIT_END:.+]] -// CHECK: [[VEC_PRIV_INIT_END]] // CHECK: [[S_ARR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_TMAIN_TY]], [[CAP_TMAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 2 // CHECK: [[S_ARR_REF:%.+]] = load [2 x [[S_INT_TY]]]*, [2 x [[S_INT_TY]]]** [[S_ARR_REF_PTR]], -// CHECK: br label %[[S_ARR_PRIV_INIT:.+]] -// CHECK: [[S_ARR_PRIV_INIT]] // CHECK: [[S_ARR_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_INT_TY]]], [2 x [[S_INT_TY]]]* [[S_ARR_REF]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 // CHECK: [[S_ARR_PRIV_BEGIN:%.+]] = getelementptr inbounds [2 x [[S_INT_TY]]], [2 x [[S_INT_TY]]]* [[S_ARR_PRIV]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 -// CHECK: [[S_ARR_END:%.+]] = getelementptr [[S_INT_TY]], [[S_INT_TY]]* [[S_ARR_BEGIN]], i{{[0-9]+}} 2 // CHECK: [[S_ARR_PRIV_END:%.+]] = getelementptr [[S_INT_TY]], [[S_INT_TY]]* [[S_ARR_PRIV_BEGIN]], i{{[0-9]+}} 2 // CHECK: [[IS_EMPTY:%.+]] = icmp eq [[S_INT_TY]]* [[S_ARR_PRIV_BEGIN]], [[S_ARR_PRIV_END]] // CHECK: br i1 [[IS_EMPTY]], label %[[S_ARR_BODY_DONE:.+]], label %[[S_ARR_BODY:.+]] @@ -242,8 +226,6 @@ // CHECK: call {{.*}} [[S_INT_TY_COPY_CONSTR:@.+]]([[S_INT_TY]]* {{.+}}, [[S_INT_TY]]* {{.+}}, [[ST_TY]]* [[ST_TY_TEMP]]) // CHECK: call {{.*}} [[ST_TY_DESTR]]([[ST_TY]]* [[ST_TY_TEMP]]) // CHECK: br i1 {{.+}}, label %{{.+}}, label %[[S_ARR_BODY]] -// CHECK: br label %[[S_ARR_PRIV_INIT_END:.+]] -// CHECK: [[S_ARR_PRIV_INIT_END]] // CHECK: [[VAR_REF_PTR:%.+]] = getelementptr inbounds [[CAP_TMAIN_TY]], [[CAP_TMAIN_TY]]* %{{.+}}, i{{[0-9]+}} 0, i{{[0-9]+}} 3 // CHECK: [[VAR_REF:%.+]] = load [[S_INT_TY]]*, [[S_INT_TY]]** [[VAR_REF_PTR]], // CHECK: call {{.*}} [[ST_TY_DEFAULT_CONSTR]]([[ST_TY]]* [[ST_TY_TEMP:%.+]]) Index: test/OpenMP/parallel_for_lastprivate_messages.cpp =================================================================== --- test/OpenMP/parallel_for_lastprivate_messages.cpp +++ test/OpenMP/parallel_for_lastprivate_messages.cpp @@ -15,15 +15,17 @@ public: S2() : a(0) {} S2(S2 &s2) : a(s2.a) {} + S2 &operator=(const S2 &); + const S2 &operator=(const S2 &) const; static float S2s; static const float S2sc; }; const float S2::S2sc = 0; // expected-note {{static data member is predetermined as shared}} const S2 b; const S2 ba[5]; -class S3 { // expected-note 2 {{'S3' declared here}} +class S3 { int a; - S3 &operator=(const S3 &s3); + S3 &operator=(const S3 &s3); // expected-note 2 {{implicitly declared private here}} public: S3() : a(0) {} @@ -32,17 +34,17 @@ const S3 c; // expected-note {{global variable is predetermined as shared}} const S3 ca[5]; // expected-note {{global variable is predetermined as shared}} extern const int f; // expected-note {{global variable is predetermined as shared}} -class S4 { // expected-note 3 {{'S4' declared here}} +class S4 { int a; - S4(); + S4(); // expected-note 3 {{implicitly declared private here}} S4(const S4 &s4); public: S4(int v) : a(v) {} }; -class S5 { // expected-note {{'S5' declared here}} +class S5 { int a; - S5() : a(0) {} + S5() : a(0) {} // expected-note {{implicitly declared private here}} public: S5(const S5 &s5) : a(s5.a) {} @@ -62,8 +64,8 @@ template int foomain(int argc, char **argv) { - I e(4); // expected-note {{'e' defined here}} - I g(5); // expected-note {{'g' defined here}} + I e(4); + I g(5); int i; int &j = i; // expected-note {{'j' defined here}} #pragma omp parallel for lastprivate // expected-error {{expected '(' after 'lastprivate'}} @@ -96,7 +98,7 @@ #pragma omp parallel for lastprivate(argv[1]) // expected-error {{expected variable name}} for (int k = 0; k < argc; ++k) ++k; -#pragma omp parallel for lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp parallel for lastprivate(e, g) // expected-error 2 {{calling a private constructor of class 'S4'}} for (int k = 0; k < argc; ++k) ++k; #pragma omp parallel for lastprivate(h) // expected-error {{threadprivate or thread local variable cannot be lastprivate}} @@ -129,9 +131,9 @@ int main(int argc, char **argv) { const int d = 5; // expected-note {{constant variable is predetermined as shared}} const int da[5] = {0}; // expected-note {{constant variable is predetermined as shared}} - S4 e(4); // expected-note {{'e' defined here}} - S5 g(5); // expected-note {{'g' defined here}} - S3 m; // expected-note 2 {{'m' defined here}} + S4 e(4); + S5 g(5); + S3 m; S6 n(2); int i; int &j = i; // expected-note {{'j' defined here}} @@ -190,10 +192,10 @@ #pragma omp parallel for safelen(5) // expected-error {{unexpected OpenMP clause 'safelen' in directive '#pragma omp parallel for'}} for (i = 0; i < argc; ++i) foo(); -#pragma omp parallel for lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp parallel for lastprivate(e, g) // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} for (i = 0; i < argc; ++i) foo(); -#pragma omp parallel for lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp parallel for lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel for lastprivate(h) // expected-error {{threadprivate or thread local variable cannot be lastprivate}} @@ -216,7 +218,7 @@ #pragma omp parallel for lastprivate(j) // expected-error {{arguments of OpenMP clause 'lastprivate' cannot be of reference type}} for (i = 0; i < argc; ++i) foo(); -#pragma omp parallel for firstprivate(m) lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp parallel for firstprivate(m) lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel for lastprivate(n) firstprivate(n) // OK Index: test/OpenMP/parallel_for_simd_lastprivate_messages.cpp =================================================================== --- test/OpenMP/parallel_for_simd_lastprivate_messages.cpp +++ test/OpenMP/parallel_for_simd_lastprivate_messages.cpp @@ -15,15 +15,16 @@ public: S2() : a(0) {} S2(S2 &s2) : a(s2.a) {} + const S2 &operator=(const S2 &) const; static float S2s; static const float S2sc; }; const float S2::S2sc = 0; // expected-note {{static data member is predetermined as shared}} const S2 b; const S2 ba[5]; -class S3 { // expected-note 2 {{'S3' declared here}} +class S3 { int a; - S3 &operator=(const S3 &s3); + S3 &operator=(const S3 &s3); // expected-note 2 {{implicitly declared private here}} public: S3() : a(0) {} @@ -32,17 +33,17 @@ const S3 c; // expected-note {{global variable is predetermined as shared}} const S3 ca[5]; // expected-note {{global variable is predetermined as shared}} extern const int f; // expected-note {{global variable is predetermined as shared}} -class S4 { // expected-note 3 {{'S4' declared here}} +class S4 { int a; - S4(); + S4(); // expected-note 3 {{implicitly declared private here}} S4(const S4 &s4); public: S4(int v) : a(v) {} }; -class S5 { // expected-note {{'S5' declared here}} +class S5 { int a; - S5() : a(0) {} + S5() : a(0) {} // expected-note {{implicitly declared private here}} public: S5(const S5 &s5) : a(s5.a) {} @@ -62,8 +63,8 @@ template int foomain(int argc, char **argv) { - I e(4); // expected-note {{'e' defined here}} - I g(5); // expected-note {{'g' defined here}} + I e(4); + I g(5); int i; int &j = i; // expected-note {{'j' defined here}} #pragma omp parallel for simd lastprivate // expected-error {{expected '(' after 'lastprivate'}} @@ -96,7 +97,7 @@ #pragma omp parallel for simd lastprivate(argv[1]) // expected-error {{expected variable name}} for (int k = 0; k < argc; ++k) ++k; -#pragma omp parallel for simd lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp parallel for simd lastprivate(e, g) // expected-error 2 {{calling a private constructor of class 'S4'}} for (int k = 0; k < argc; ++k) ++k; #pragma omp parallel for simd lastprivate(h) // expected-error {{threadprivate or thread local variable cannot be lastprivate}} @@ -129,9 +130,9 @@ int main(int argc, char **argv) { const int d = 5; // expected-note {{constant variable is predetermined as shared}} const int da[5] = {0}; // expected-note {{constant variable is predetermined as shared}} - S4 e(4); // expected-note {{'e' defined here}} - S5 g(5); // expected-note {{'g' defined here}} - S3 m; // expected-note 2 {{'m' defined here}} + S4 e(4); + S5 g(5); + S3 m; S6 n(2); int i; int &j = i; // expected-note {{'j' defined here}} @@ -190,10 +191,10 @@ #pragma omp parallel for simd safelen(5) for (i = 0; i < argc; ++i) foo(); -#pragma omp parallel for simd lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp parallel for simd lastprivate(e, g) // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} for (i = 0; i < argc; ++i) foo(); -#pragma omp parallel for simd lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp parallel for simd lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel for simd lastprivate(h) // expected-error {{threadprivate or thread local variable cannot be lastprivate}} @@ -216,7 +217,7 @@ #pragma omp parallel for simd lastprivate(j) // expected-error {{arguments of OpenMP clause 'lastprivate' cannot be of reference type}} for (i = 0; i < argc; ++i) foo(); -#pragma omp parallel for simd firstprivate(m) lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp parallel for simd firstprivate(m) lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp parallel for simd lastprivate(n) firstprivate(n) // OK Index: test/OpenMP/parallel_sections_lastprivate_messages.cpp =================================================================== --- test/OpenMP/parallel_sections_lastprivate_messages.cpp +++ test/OpenMP/parallel_sections_lastprivate_messages.cpp @@ -15,15 +15,16 @@ public: S2() : a(0) {} S2(S2 &s2) : a(s2.a) {} + const S2 &operator=(const S2 &) const; static float S2s; static const float S2sc; }; const float S2::S2sc = 0; // expected-note {{static data member is predetermined as shared}} const S2 b; const S2 ba[5]; -class S3 { // expected-note 2 {{'S3' declared here}} +class S3 { int a; - S3 &operator=(const S3 &s3); + S3 &operator=(const S3 &s3); // expected-note 2 {{implicitly declared private here}} public: S3() : a(0) {} @@ -32,17 +33,17 @@ const S3 c; // expected-note {{global variable is predetermined as shared}} const S3 ca[5]; // expected-note {{global variable is predetermined as shared}} extern const int f; // expected-note {{global variable is predetermined as shared}} -class S4 { // expected-note 3 {{'S4' declared here}} +class S4 { int a; - S4(); + S4(); // expected-note 3 {{implicitly declared private here}} S4(const S4 &s4); public: S4(int v) : a(v) {} }; -class S5 { // expected-note {{'S5' declared here}} +class S5 { int a; - S5() : a(0) {} + S5() : a(0) {} // expected-note {{implicitly declared private here}} public: S5(const S5 &s5) : a(s5.a) {} @@ -62,8 +63,8 @@ template int foomain(int argc, char **argv) { - I e(4); // expected-note {{'e' defined here}} - I g(5); // expected-note {{'g' defined here}} + I e(4); + I g(5); int i; int &j = i; // expected-note {{'j' defined here}} #pragma omp parallel sections lastprivate // expected-error {{expected '(' after 'lastprivate'}} @@ -106,7 +107,7 @@ { foo(); } -#pragma omp parallel sections lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp parallel sections lastprivate(e, g) // expected-error 2 {{calling a private constructor of class 'S4'}} { foo(); } @@ -144,9 +145,9 @@ int main(int argc, char **argv) { const int d = 5; // expected-note {{constant variable is predetermined as shared}} const int da[5] = {0}; // expected-note {{constant variable is predetermined as shared}} - S4 e(4); // expected-note {{'e' defined here}} - S5 g(5); // expected-note {{'g' defined here}} - S3 m; // expected-note 2 {{'m' defined here}} + S4 e(4); + S5 g(5); + S3 m; S6 n(2); int i; int &j = i; // expected-note {{'j' defined here}} @@ -223,11 +224,11 @@ { foo(); } -#pragma omp parallel sections lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp parallel sections lastprivate(e, g) // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} { foo(); } -#pragma omp parallel sections lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp parallel sections lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} { foo(); } @@ -257,7 +258,7 @@ { foo(); } -#pragma omp parallel sections firstprivate(m) lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp parallel sections firstprivate(m) lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} { foo(); } Index: test/OpenMP/sections_lastprivate_messages.cpp =================================================================== --- test/OpenMP/sections_lastprivate_messages.cpp +++ test/OpenMP/sections_lastprivate_messages.cpp @@ -15,15 +15,16 @@ public: S2() : a(0) {} S2(S2 &s2) : a(s2.a) {} + const S2 &operator=(const S2 &) const; static float S2s; // expected-note {{static data member is predetermined as shared}} static const float S2sc; }; const float S2::S2sc = 0; // expected-note {{static data member is predetermined as shared}} const S2 b; const S2 ba[5]; -class S3 { // expected-note 2 {{'S3' declared here}} +class S3 { int a; - S3 &operator=(const S3 &s3); + S3 &operator=(const S3 &s3); // expected-note 2 {{implicitly declared private here}} public: S3() : a(0) {} @@ -32,17 +33,17 @@ const S3 c; // expected-note {{global variable is predetermined as shared}} const S3 ca[5]; // expected-note {{global variable is predetermined as shared}} extern const int f; // expected-note {{global variable is predetermined as shared}} -class S4 { // expected-note 3 {{'S4' declared here}} +class S4 { int a; - S4(); + S4(); // expected-note 3 {{implicitly declared private here}} S4(const S4 &s4); public: S4(int v) : a(v) {} }; -class S5 { // expected-note {{'S5' declared here}} +class S5 { int a; - S5() : a(0) {} + S5() : a(0) {} // expected-note {{implicitly declared private here}} public: S5(const S5 &s5) : a(s5.a) {} @@ -62,8 +63,8 @@ template int foomain(int argc, char **argv) { - I e(4); // expected-note {{'e' defined here}} - I g(5); // expected-note {{'g' defined here}} + I e(4); + I g(5); int i; int &j = i; // expected-note {{'j' defined here}} #pragma omp parallel @@ -117,7 +118,7 @@ foo(); } #pragma omp parallel -#pragma omp sections lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp sections lastprivate(e, g) // expected-error 2 {{calling a private constructor of class 'S4'}} { foo(); } @@ -158,9 +159,9 @@ int main(int argc, char **argv) { const int d = 5; // expected-note {{constant variable is predetermined as shared}} const int da[5] = {0}; // expected-note {{constant variable is predetermined as shared}} - S4 e(4); // expected-note {{'e' defined here}} - S5 g(5); // expected-note {{'g' defined here}} - S3 m; // expected-note 2 {{'m' defined here}} + S4 e(4); + S5 g(5); + S3 m; S6 n(2); int i; int &j = i; // expected-note {{'j' defined here}} @@ -256,12 +257,12 @@ foo(); } #pragma omp parallel -#pragma omp sections lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp sections lastprivate(e, g) // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} { foo(); } #pragma omp parallel -#pragma omp sections lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp sections lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} { foo(); } @@ -296,7 +297,7 @@ foo(); } #pragma omp parallel -#pragma omp sections firstprivate(m) lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp sections firstprivate(m) lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} { foo(); } Index: test/OpenMP/simd_lastprivate_messages.cpp =================================================================== --- test/OpenMP/simd_lastprivate_messages.cpp +++ test/OpenMP/simd_lastprivate_messages.cpp @@ -15,15 +15,16 @@ public: S2() : a(0) {} S2(S2 &s2) : a(s2.a) {} + const S2 &operator=(const S2 &) const; static float S2s; // expected-note {{static data member is predetermined as shared}} static const float S2sc; }; const float S2::S2sc = 0; // expected-note {{static data member is predetermined as shared}} const S2 b; const S2 ba[5]; -class S3 { // expected-note {{'S3' declared here}} +class S3 { int a; - S3 &operator=(const S3 &s3); + S3 &operator=(const S3 &s3); // expected-note {{implicitly declared private here}} public: S3() : a(0) {} @@ -32,17 +33,17 @@ const S3 c; // expected-note {{global variable is predetermined as shared}} const S3 ca[5]; // expected-note {{global variable is predetermined as shared}} extern const int f; // expected-note {{global variable is predetermined as shared}} -class S4 { // expected-note {{'S4' declared here}} +class S4 { int a; - S4(); + S4(); // expected-note {{implicitly declared private here}} S4(const S4 &s4); public: S4(int v) : a(v) {} }; -class S5 { // expected-note {{'S5' declared here}} +class S5 { int a; - S5() : a(0) {} + S5() : a(0) {} // expected-note {{implicitly declared private here}} public: S5(const S5 &s5) : a(s5.a) {} @@ -121,9 +122,9 @@ int main(int argc, char **argv) { const int d = 5; // expected-note {{constant variable is predetermined as shared}} const int da[5] = {0}; // expected-note {{constant variable is predetermined as shared}} - S4 e(4); // expected-note {{'e' defined here}} - S5 g(5); // expected-note {{'g' defined here}} - S3 m; // expected-note {{'m' defined here}} + S4 e(4); + S5 g(5); + S3 m; int i; int &j = i; // expected-note {{'j' defined here}} #pragma omp simd lastprivate // expected-error {{expected '(' after 'lastprivate'}} @@ -181,10 +182,10 @@ #pragma omp simd firstprivate(g) // expected-error {{unexpected OpenMP clause 'firstprivate' in directive '#pragma omp simd'}} for (i = 0; i < argc; ++i) foo(); -#pragma omp simd lastprivate(e, g) // expected-error 2 {{lastprivate variable must have an accessible, unambiguous default constructor}} +#pragma omp simd lastprivate(e, g) // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} for (i = 0; i < argc; ++i) foo(); -#pragma omp simd lastprivate(m) // expected-error {{lastprivate variable must have an accessible, unambiguous copy assignment operator}} +#pragma omp simd lastprivate(m) // expected-error {{'operator=' is a private member of 'S3'}} for (i = 0; i < argc; ++i) foo(); #pragma omp simd lastprivate(h) // expected-error {{threadprivate or thread local variable cannot be lastprivate}} Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -2022,6 +2022,18 @@ void OMPClauseEnqueue::VisitOMPLastprivateClause( const OMPLastprivateClause *C) { VisitOMPClauseList(C); + for (auto *E : C->private_copies()) { + Visitor->AddStmt(E); + } + for (auto *E : C->source_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->destination_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->assignment_ops()) { + Visitor->AddStmt(E); + } } void OMPClauseEnqueue::VisitOMPSharedClause(const OMPSharedClause *C) { VisitOMPClauseList(C);