From: Juergen Ributzka Date: Fri, 16 May 2014 02:33:15 +0000 (+0000) Subject: Add C API for thread yielding callback. X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=9bc1b73c9e30197d18b64fb9cf09dd45eaef256b;p=oota-llvm.git Add C API for thread yielding callback. Sometimes a LLVM compilation may take more time then a client would like to wait for. The problem is that it is not possible to safely suspend the LLVM thread from the outside. When the timing is bad it might be possible that the LLVM thread holds a global mutex and this would block any progress in any other thread. This commit adds a new yield callback function that can be registered with a context. LLVM will try to yield by calling this callback function, but there is no guaranteed frequency. LLVM will only do so if it can guarantee that suspending the thread won't block any forward progress in other LLVM contexts in the same process. Once the client receives the call back it can suspend the thread safely and resume it at another time. Related to git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@208945 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm-c/Core.h b/include/llvm-c/Core.h index a86079e0ec0..151165e704d 100644 --- a/include/llvm-c/Core.h +++ b/include/llvm-c/Core.h @@ -467,6 +467,7 @@ void LLVMEnablePrettyStackTrace(void); */ typedef void (*LLVMDiagnosticHandler)(LLVMDiagnosticInfoRef, void *); +typedef void (*LLVMYieldCallback)(LLVMContextRef, void *); /** * Create a new context. @@ -488,6 +489,14 @@ void LLVMContextSetDiagnosticHandler(LLVMContextRef C, LLVMDiagnosticHandler Handler, void *DiagnosticContext); +/** + * Set the yield callback function for this context. + * + * @see LLVMContext::setYieldCallback() + */ +void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback, + void *OpaqueHandle); + /** * Destroy a context instance. * diff --git a/include/llvm/IR/LLVMContext.h b/include/llvm/IR/LLVMContext.h index 7356c17027e..f9644aca6b4 100644 --- a/include/llvm/IR/LLVMContext.h +++ b/include/llvm/IR/LLVMContext.h @@ -72,6 +72,10 @@ public: /// \see LLVMContext::diagnose. typedef void (*DiagnosticHandlerTy)(const DiagnosticInfo &DI, void *Context); + /// Defines the type of a yield callback. + /// \see LLVMContext::setYieldCallback. + typedef void (*YieldCallbackTy)(LLVMContext *Context, void *OpaqueHandle); + /// setInlineAsmDiagnosticHandler - This method sets a handler that is invoked /// when problems with inline asm are detected by the backend. The first /// argument is a function pointer and the second is a context pointer that @@ -118,6 +122,32 @@ public: /// for RS_Error, "warning: " for RS_Warning, and "note: " for RS_Note. void diagnose(const DiagnosticInfo &DI); + /// \brief Registers a yield callback with the given context. + /// + /// The yield callback function may be called by LLVM to transfer control back + /// to the client that invoked the LLVM compilation. This can be used to yield + /// control of the thread, or perform periodic work needed by the client. + /// There is no guaranteed frequency at which callbacks must occur; in fact, + /// the client is not guaranteed to ever receive this callback. It is at the + /// sole discretion of LLVM to do so and only if it can guarantee that + /// suspending the thread won't block any forward progress in other LLVM + /// contexts in the same process. + /// + /// At a suspend point, the state of the current LLVM context is intentionally + /// undefined. No assumptions about it can or should be made. Only LLVM + /// context API calls that explicitly state that they can be used during a + /// yield callback are allowed to be used. Any other API calls into the + /// context are not supported until the yield callback function returns + /// control to LLVM. Other LLVM contexts are unaffected by this restriction. + void setYieldCallback(YieldCallbackTy Callback, void *OpaqueHandle); + + /// \brief Calls the yield callback (if applicable). + /// + /// This transfers control of the current thread back to the client, which may + /// suspend the current thread. Only call this method when LLVM doesn't hold + /// any global mutex or cannot block the execution in another LLVM context. + void yield(); + /// emitError - Emit an error message to the currently installed error handler /// with optional location information. This function returns, so code should /// be prepared to drop the erroneous construct on the floor and "not crash". diff --git a/lib/Analysis/IPA/CallGraphSCCPass.cpp b/lib/Analysis/IPA/CallGraphSCCPass.cpp index fa9159d13d4..bfab744d479 100644 --- a/lib/Analysis/IPA/CallGraphSCCPass.cpp +++ b/lib/Analysis/IPA/CallGraphSCCPass.cpp @@ -22,6 +22,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LegacyPassManagers.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Timer.h" @@ -145,8 +146,11 @@ bool CGPassManager::RunPassOnSCC(Pass *P, CallGraphSCC &CurSCC, I != E; ++I) { if (Function *F = (*I)->getFunction()) { dumpPassInfo(P, EXECUTION_MSG, ON_FUNCTION_MSG, F->getName()); - TimeRegion PassTimer(getPassTimer(FPP)); - Changed |= FPP->runOnFunction(*F); + { + TimeRegion PassTimer(getPassTimer(FPP)); + Changed |= FPP->runOnFunction(*F); + } + F->getContext().yield(); } } diff --git a/lib/Analysis/LoopPass.cpp b/lib/Analysis/LoopPass.cpp index 683d6d74ab4..8df18e75c64 100644 --- a/lib/Analysis/LoopPass.cpp +++ b/lib/Analysis/LoopPass.cpp @@ -15,6 +15,7 @@ #include "llvm/Analysis/LoopPass.h" #include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Timer.h" using namespace llvm; @@ -253,6 +254,8 @@ bool LPPassManager::runOnFunction(Function &F) { // Then call the regular verifyAnalysis functions. verifyPreservedAnalysis(P); + + F.getContext().yield(); } removeNotPreservedAnalysis(P); diff --git a/lib/IR/Core.cpp b/lib/IR/Core.cpp index b1b0abb9eff..81d137edfe0 100644 --- a/lib/IR/Core.cpp +++ b/lib/IR/Core.cpp @@ -89,6 +89,13 @@ void LLVMContextSetDiagnosticHandler(LLVMContextRef C, DiagnosticContext); } +void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback, + void *OpaqueHandle) { + auto YieldCallback = + LLVM_EXTENSION reinterpret_cast(Callback); + unwrap(C)->setYieldCallback(YieldCallback, OpaqueHandle); +} + void LLVMContextDispose(LLVMContextRef C) { delete unwrap(C); } diff --git a/lib/IR/LLVMContext.cpp b/lib/IR/LLVMContext.cpp index 588e1217bd4..5f94dca1eb9 100644 --- a/lib/IR/LLVMContext.cpp +++ b/lib/IR/LLVMContext.cpp @@ -115,6 +115,17 @@ void *LLVMContext::getDiagnosticContext() const { return pImpl->DiagnosticContext; } +void LLVMContext::setYieldCallback(YieldCallbackTy Callback, void *OpaqueHandle) +{ + pImpl->YieldCallback = Callback; + pImpl->YieldOpaqueHandle = OpaqueHandle; +} + +void LLVMContext::yield() { + if (pImpl->YieldCallback) + pImpl->YieldCallback(this, pImpl->YieldOpaqueHandle); +} + void LLVMContext::emitError(const Twine &ErrorStr) { diagnose(DiagnosticInfoInlineAsm(ErrorStr)); } diff --git a/lib/IR/LLVMContextImpl.cpp b/lib/IR/LLVMContextImpl.cpp index 78a3c2d304b..2042374647d 100644 --- a/lib/IR/LLVMContextImpl.cpp +++ b/lib/IR/LLVMContextImpl.cpp @@ -41,6 +41,8 @@ LLVMContextImpl::LLVMContextImpl(LLVMContext &C) InlineAsmDiagContext = nullptr; DiagnosticHandler = nullptr; DiagnosticContext = nullptr; + YieldCallback = nullptr; + YieldOpaqueHandle = nullptr; NamedStructTypesUniqueID = 0; } diff --git a/lib/IR/LLVMContextImpl.h b/lib/IR/LLVMContextImpl.h index a6a65e6cacd..b1ad9ff4a5a 100644 --- a/lib/IR/LLVMContextImpl.h +++ b/lib/IR/LLVMContextImpl.h @@ -242,6 +242,9 @@ public: LLVMContext::DiagnosticHandlerTy DiagnosticHandler; void *DiagnosticContext; + LLVMContext::YieldCallbackTy YieldCallback; + void *YieldOpaqueHandle; + typedef DenseMap IntMapTy; IntMapTy IntConstants; diff --git a/lib/IR/LegacyPassManager.cpp b/lib/IR/LegacyPassManager.cpp index b6d75b483f8..d3f3482dc02 100644 --- a/lib/IR/LegacyPassManager.cpp +++ b/lib/IR/LegacyPassManager.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassManagers.h" @@ -1489,8 +1490,10 @@ bool FunctionPassManagerImpl::run(Function &F) { TimingInfo::createTheTimeInfo(); initializeAllAnalysisInfo(); - for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) + for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) { Changed |= getContainedManager(Index)->runOnFunction(F); + F.getContext().yield(); + } for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) getContainedManager(Index)->cleanup(); @@ -1723,8 +1726,10 @@ bool PassManagerImpl::run(Module &M) { } initializeAllAnalysisInfo(); - for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) + for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) { Changed |= getContainedManager(Index)->runOnModule(M); + M.getContext().yield(); + } for (SmallVectorImpl::const_iterator I = IPV.begin(), E = IPV.end(); I != E; ++I) { diff --git a/lib/IR/PassManager.cpp b/lib/IR/PassManager.cpp index 3344b099537..0defb6ab3e4 100644 --- a/lib/IR/PassManager.cpp +++ b/lib/IR/PassManager.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/STLExtras.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/PassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -32,6 +33,8 @@ PreservedAnalyses ModulePassManager::run(Module *M, ModuleAnalysisManager *AM) { if (AM) AM->invalidate(M, PassPA); PA.intersect(std::move(PassPA)); + + M->getContext().yield(); } if (DebugPM) @@ -92,6 +95,8 @@ PreservedAnalyses FunctionPassManager::run(Function *F, if (AM) AM->invalidate(F, PassPA); PA.intersect(std::move(PassPA)); + + F->getContext().yield(); } if (DebugPM) diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp index 3813d59dbd1..20d3f139b4c 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp @@ -28,6 +28,7 @@ using namespace llvm; static bool didCallAllocateCodeSection; static bool didAllocateCompactUnwindSection; +static bool didCallYield; static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size, unsigned alignment, @@ -64,6 +65,10 @@ static void roundTripDestroy(void *object) { delete static_cast(object); } +static void yield(LLVMContextRef, void *) { + didCallYield = true; +} + namespace { // memory manager to test reserve allocation space callback @@ -142,6 +147,7 @@ protected: virtual void SetUp() { didCallAllocateCodeSection = false; didAllocateCompactUnwindSection = false; + didCallYield = false; Module = 0; Function = 0; Engine = 0; @@ -429,3 +435,24 @@ TEST_F(MCJITCAPITest, reserve_allocation_space) { EXPECT_TRUE(MM->UsedCodeSize > 0); EXPECT_TRUE(MM->UsedDataSizeRW > 0); } + +TEST_F(MCJITCAPITest, yield) { + SKIP_UNSUPPORTED_PLATFORM; + + buildSimpleFunction(); + buildMCJITOptions(); + buildMCJITEngine(); + LLVMContextRef C = LLVMGetGlobalContext(); + LLVMContextSetYieldCallback(C, yield, NULL); + buildAndRunPasses(); + + union { + void *raw; + int (*usable)(); + } functionPointer; + functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); + + EXPECT_EQ(42, functionPointer.usable()); + EXPECT_TRUE(didCallYield); +} +