diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -1438,6 +1438,17 @@ return UDRMap.lookup(D); } +// Temporary RAII solution to perform a pop stack even on the OpenMP IR Builder +// if one is present. +struct PopStackRAII { + PopStackRAII(llvm::OpenMPIRBuilder *OMPBuilder) : OMPBuilder(OMPBuilder) {} + ~PopStackRAII() { + if (OMPBuilder) + OMPBuilder->popFinalizationCB(); + } + llvm::OpenMPIRBuilder *OMPBuilder; +}; + static llvm::Function *emitParallelOrTeamsOutlinedFunction( CodeGenModule &CGM, const OMPExecutableDirective &D, const CapturedStmt *CS, const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind, @@ -1462,6 +1473,41 @@ else if (const auto *OPFD = dyn_cast(&D)) HasCancel = OPFD->hasCancel(); + + // TODO: Temporarily inform the OpenMPIRBuilder, if any, about the new + // parallel region to make cancellation barriers work properly. + llvm::OpenMPIRBuilder *OMPBuilder = CGM.getOpenMPIRBuilder(); + PopStackRAII PSR(OMPBuilder); + if (OMPBuilder) { + + // The following callback is the crucial part of clangs cleanup process. + // + // NOTE: + // Once the OpenMPIRBuilder is used to create parallel regions (and + // similar), the cancellation destination (Dest below) is determined via + // IP. That means if we have variables to finalize we split the block at IP, + // use the new block (=BB) as destination to build a JumpDest (via + // getJumpDestInCurrentScope(BB)) which then is fed to + // EmitBranchThroughCleanup. Furhtermore, there will not be the need + // to push & pop an FinalizationInfo object. + // The FiniCB will still be needed otherwise but at the point where the + // OpenMPIRBuilder is asked to construct a parallel (or similar) construct. + auto FiniCB = [&CGF](llvm::OpenMPIRBuilder::InsertPointTy IP) { + assert(IP.getBlock()->end() == IP.getPoint() && + "Clang CG should cause non-terminated block!"); + CGBuilderTy::InsertPointGuard IPG(CGF.Builder); + CodeGenFunction::JumpDest Dest = + CGF.getOMPCancelDestination(OMPD_parallel); + CGF.EmitBranchThroughCleanup(Dest); + }; + + // TODO: Remove this once we emit parallel regions through the + // OpenMPIRBuilder as it can do this setup internally. + llvm::OpenMPIRBuilder::FinalizationInfo FI( + {FiniCB, OMPD_parallel, HasCancel}); + OMPBuilder->pushFinalizationCB(std::move(FI)); + } + CGOpenMPOutlinedRegionInfo CGInfo(*CS, ThreadIDVar, CodeGen, InnermostKind, HasCancel, OutlinedHelperName); CodeGenFunction::CGCapturedStmtRAII CapInfoRAII(CGF, &CGInfo); @@ -3483,21 +3529,8 @@ dyn_cast_or_null(CGF.CapturedStmtInfo); llvm::OpenMPIRBuilder *OMPBuilder = CGF.CGM.getOpenMPIRBuilder(); if (OMPBuilder) { - // TODO: Move cancelation point handling into the IRBuilder. - if (EmitChecks && !ForceSimpleCall && OMPRegionInfo && - OMPRegionInfo->hasCancel()) { - CGBuilderTy::InsertPointGuard IPG(CGF.Builder); - llvm::BasicBlock *ExitBB = - CGF.createBasicBlock(".cancel.exit", IP.getBlock()->getParent()); - OMPBuilder->setCancellationBlock(ExitBB); - CGF.Builder.SetInsertPoint(ExitBB); - CodeGenFunction::JumpDest CancelDestination = - CGF.getOMPCancelDestination(OMPRegionInfo->getDirectiveKind()); - CGF.EmitBranchThroughCleanup(CancelDestination); - } - auto IP = OMPBuilder->CreateBarrier(CGF.Builder, Kind, ForceSimpleCall, - EmitChecks); - CGF.Builder.restoreIP(IP); + CGF.Builder.restoreIP(OMPBuilder->CreateBarrier( + CGF.Builder, Kind, ForceSimpleCall, EmitChecks)); return; } diff --git a/llvm/include/llvm/Frontend/OpenMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMPIRBuilder.h --- a/llvm/include/llvm/Frontend/OpenMPIRBuilder.h +++ b/llvm/include/llvm/Frontend/OpenMPIRBuilder.h @@ -37,12 +37,44 @@ /// Add attributes known for \p FnID to \p Fn. void addAttributes(omp::RuntimeFunction FnID, Function &Fn); - /// Set the cancellation block to \p CBB. - void setCancellationBlock(BasicBlock *CBB) { CancellationBlock = CBB; } - /// Type used throughout for insertion points. using InsertPointTy = IRBuilder<>::InsertPoint; + /// Callback type for variable finalization (think destructors). + /// + /// \param CodeGenIP is the insertion point at which the finalization code + /// should be placed. + /// + /// A finalize callback knows about all objects that need finalization, e.g. + /// destruction, when the scope of the currently generated construct is left + /// at the time, and location, the callback is invoked. + using FinalizeCallbackTy = function_ref; + + struct FinalizationInfo { + /// The finalization callback provided by the last in-flight invocation of + /// CreateXXXX for the directive of kind DK. + FinalizeCallbackTy FiniCB; + + /// The directive kind of the innermost directive that has an associated + /// region which might require finalization when it is left. + omp::Directive DK; + + /// Flag to indicate if the directive is cancellable. + bool IsCancellable; + }; + + /// Push a finalization callback on the finalization stack. + /// + /// NOTE: Temporary solution until Clang CG is gone. + void pushFinalizationCB(FinalizationInfo &&FI) { + FinalizationStack.emplace_back(std::move(FI)); + } + + /// Pop the last finalization callback from the finalization stack. + /// + /// NOTE: Temporary solution until Clang CG is gone. + void popFinalizationCB() { FinalizationStack.pop_back(); } + /// Description of a LLVM-IR insertion point (IP) and a debug/source location /// (filename, line, column, ...). struct LocationDescription { @@ -112,6 +144,19 @@ omp::Directive DK, bool ForceSimpleCall, bool CheckCancelFlag); + /// The finalization stack made up of finalize callbacks currently in-flight, + /// wrapped into FinalizationInfo objects that reference also the finalization + /// target block and the kind of cancellable directive. + SmallVector FinalizationStack; + + /// Return true if the last entry in the finalization stack is of kind \p DK + /// and cancellable. + bool isLastFinalizationInfoCancellable(omp::Directive DK) { + return !FinalizationStack.empty() && + FinalizationStack.back().IsCancellable && + FinalizationStack.back().DK == DK; + } + /// Return the current thread ID. /// /// \param Ident The ident (ident_t*) describing the query origin. @@ -123,9 +168,6 @@ /// The LLVM-IR Builder used to create IR. IRBuilder<> Builder; - /// TODO: Stub for a cancellation block stack. - BasicBlock *CancellationBlock = nullptr; - /// Map to remember the thread in a function. DenseMap ThreadIDMap; diff --git a/llvm/lib/Frontend/OpenMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMPIRBuilder.cpp --- a/llvm/lib/Frontend/OpenMPIRBuilder.cpp +++ b/llvm/lib/Frontend/OpenMPIRBuilder.cpp @@ -228,7 +228,8 @@ // If we are in a cancellable parallel region, barriers are cancellation // points. // TODO: Check why we would force simple calls or to ignore the cancel flag. - bool UseCancelBarrier = !ForceSimpleCall && CancellationBlock; + bool UseCancelBarrier = + !ForceSimpleCall && isLastFinalizationInfoCancellable(OMPD_parallel); Value *Result = Builder.CreateCall( getOrCreateRuntimeFunction(UseCancelBarrier ? OMPRTL___kmpc_cancel_barrier @@ -248,13 +249,14 @@ Builder.CreateCondBr(Cmp, NonCancellationBlock, CancellationBlock, /* TODO weight */ nullptr, nullptr); - Builder.SetInsertPoint(NonCancellationBlock); - assert(CancellationBlock->getParent() == BB->getParent() && - "Unexpected cancellation block parent!"); + // From the cancellation block we finalize all variables and go to the + // post finalization block that is known to the FiniCB callback. + Builder.SetInsertPoint(CancellationBlock); + auto &FI = FinalizationStack.back(); + FI.FiniCB(Builder.saveIP()); - // TODO: This is a workaround for now, we always reset the cancellation - // block until we manage it ourselves here. - CancellationBlock = nullptr; + // The continuation block is where code generation continues. + Builder.SetInsertPoint(NonCancellationBlock); } return Builder.saveIP();