<div dir="ltr">Just want to say, thanks for all the work figuring out the right shape for this. =] This looks really, really nice.</div><div class="gmail_extra"><br><br><div class="gmail_quote">On Thu, May 15, 2014 at 8:33 PM, Juergen Ributzka <span dir="ltr"><<a href="mailto:juergen@apple.com" target="_blank">juergen@apple.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: ributzka<br>
Date: Thu May 15 21:33:15 2014<br>
New Revision: 208945<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=208945&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=208945&view=rev</a><br>
Log:<br>
Add C API for thread yielding callback.<br>
<br>
Sometimes a LLVM compilation may take more time then a client would like to<br>
wait for. The problem is that it is not possible to safely suspend the LLVM<br>
thread from the outside. When the timing is bad it might be possible that the<br>
LLVM thread holds a global mutex and this would block any progress in any other<br>
thread.<br>
<br>
This commit adds a new yield callback function that can be registered with a<br>
context. LLVM will try to yield by calling this callback function, but there is<br>
no guaranteed frequency. LLVM will only do so if it can guarantee that<br>
suspending the thread won't block any forward progress in other LLVM contexts<br>
in the same process.<br>
<br>
Once the client receives the call back it can suspend the thread safely and<br>
resume it at another time.<br>
<br>
Related to <rdar://problem/16728690><br>
<br>
Modified:<br>
llvm/trunk/include/llvm-c/Core.h<br>
llvm/trunk/include/llvm/IR/LLVMContext.h<br>
llvm/trunk/lib/Analysis/IPA/CallGraphSCCPass.cpp<br>
llvm/trunk/lib/Analysis/LoopPass.cpp<br>
llvm/trunk/lib/IR/Core.cpp<br>
llvm/trunk/lib/IR/LLVMContext.cpp<br>
llvm/trunk/lib/IR/LLVMContextImpl.cpp<br>
llvm/trunk/lib/IR/LLVMContextImpl.h<br>
llvm/trunk/lib/IR/LegacyPassManager.cpp<br>
llvm/trunk/lib/IR/PassManager.cpp<br>
llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp<br>
<br>
Modified: llvm/trunk/include/llvm-c/Core.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm-c/Core.h?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm-c/Core.h?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm-c/Core.h (original)<br>
+++ llvm/trunk/include/llvm-c/Core.h Thu May 15 21:33:15 2014<br>
@@ -467,6 +467,7 @@ void LLVMEnablePrettyStackTrace(void);<br>
*/<br>
<br>
typedef void (*LLVMDiagnosticHandler)(LLVMDiagnosticInfoRef, void *);<br>
+typedef void (*LLVMYieldCallback)(LLVMContextRef, void *);<br>
<br>
/**<br>
* Create a new context.<br>
@@ -489,6 +490,14 @@ void LLVMContextSetDiagnosticHandler(LLV<br>
void *DiagnosticContext);<br>
<br>
/**<br>
+ * Set the yield callback function for this context.<br>
+ *<br>
+ * @see LLVMContext::setYieldCallback()<br>
+ */<br>
+void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback,<br>
+ void *OpaqueHandle);<br>
+<br>
+/**<br>
* Destroy a context instance.<br>
*<br>
* This should be called for every call to LLVMContextCreate() or memory<br>
<br>
Modified: llvm/trunk/include/llvm/IR/LLVMContext.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/LLVMContext.h?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/LLVMContext.h?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/IR/LLVMContext.h (original)<br>
+++ llvm/trunk/include/llvm/IR/LLVMContext.h Thu May 15 21:33:15 2014<br>
@@ -72,6 +72,10 @@ public:<br>
/// \see LLVMContext::diagnose.<br>
typedef void (*DiagnosticHandlerTy)(const DiagnosticInfo &DI, void *Context);<br>
<br>
+ /// Defines the type of a yield callback.<br>
+ /// \see LLVMContext::setYieldCallback.<br>
+ typedef void (*YieldCallbackTy)(LLVMContext *Context, void *OpaqueHandle);<br>
+<br>
/// setInlineAsmDiagnosticHandler - This method sets a handler that is invoked<br>
/// when problems with inline asm are detected by the backend. The first<br>
/// argument is a function pointer and the second is a context pointer that<br>
@@ -118,6 +122,32 @@ public:<br>
/// for RS_Error, "warning: " for RS_Warning, and "note: " for RS_Note.<br>
void diagnose(const DiagnosticInfo &DI);<br>
<br>
+ /// \brief Registers a yield callback with the given context.<br>
+ ///<br>
+ /// The yield callback function may be called by LLVM to transfer control back<br>
+ /// to the client that invoked the LLVM compilation. This can be used to yield<br>
+ /// control of the thread, or perform periodic work needed by the client.<br>
+ /// There is no guaranteed frequency at which callbacks must occur; in fact,<br>
+ /// the client is not guaranteed to ever receive this callback. It is at the<br>
+ /// sole discretion of LLVM to do so and only if it can guarantee that<br>
+ /// suspending the thread won't block any forward progress in other LLVM<br>
+ /// contexts in the same process.<br>
+ ///<br>
+ /// At a suspend point, the state of the current LLVM context is intentionally<br>
+ /// undefined. No assumptions about it can or should be made. Only LLVM<br>
+ /// context API calls that explicitly state that they can be used during a<br>
+ /// yield callback are allowed to be used. Any other API calls into the<br>
+ /// context are not supported until the yield callback function returns<br>
+ /// control to LLVM. Other LLVM contexts are unaffected by this restriction.<br>
+ void setYieldCallback(YieldCallbackTy Callback, void *OpaqueHandle);<br>
+<br>
+ /// \brief Calls the yield callback (if applicable).<br>
+ ///<br>
+ /// This transfers control of the current thread back to the client, which may<br>
+ /// suspend the current thread. Only call this method when LLVM doesn't hold<br>
+ /// any global mutex or cannot block the execution in another LLVM context.<br>
+ void yield();<br>
+<br>
/// emitError - Emit an error message to the currently installed error handler<br>
/// with optional location information. This function returns, so code should<br>
/// be prepared to drop the erroneous construct on the floor and "not crash".<br>
<br>
Modified: llvm/trunk/lib/Analysis/IPA/CallGraphSCCPass.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/IPA/CallGraphSCCPass.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/IPA/CallGraphSCCPass.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Analysis/IPA/CallGraphSCCPass.cpp (original)<br>
+++ llvm/trunk/lib/Analysis/IPA/CallGraphSCCPass.cpp Thu May 15 21:33:15 2014<br>
@@ -22,6 +22,7 @@<br>
#include "llvm/IR/Function.h"<br>
#include "llvm/IR/IntrinsicInst.h"<br>
#include "llvm/IR/LegacyPassManagers.h"<br>
+#include "llvm/IR/LLVMContext.h"<br>
#include "llvm/Support/CommandLine.h"<br>
#include "llvm/Support/Debug.h"<br>
#include "llvm/Support/Timer.h"<br>
@@ -145,8 +146,11 @@ bool CGPassManager::RunPassOnSCC(Pass *P<br>
I != E; ++I) {<br>
if (Function *F = (*I)->getFunction()) {<br>
dumpPassInfo(P, EXECUTION_MSG, ON_FUNCTION_MSG, F->getName());<br>
- TimeRegion PassTimer(getPassTimer(FPP));<br>
- Changed |= FPP->runOnFunction(*F);<br>
+ {<br>
+ TimeRegion PassTimer(getPassTimer(FPP));<br>
+ Changed |= FPP->runOnFunction(*F);<br>
+ }<br>
+ F->getContext().yield();<br>
}<br>
}<br>
<br>
<br>
Modified: llvm/trunk/lib/Analysis/LoopPass.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/LoopPass.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/LoopPass.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Analysis/LoopPass.cpp (original)<br>
+++ llvm/trunk/lib/Analysis/LoopPass.cpp Thu May 15 21:33:15 2014<br>
@@ -15,6 +15,7 @@<br>
<br>
#include "llvm/Analysis/LoopPass.h"<br>
#include "llvm/IR/IRPrintingPasses.h"<br>
+#include "llvm/IR/LLVMContext.h"<br>
#include "llvm/Support/Debug.h"<br>
#include "llvm/Support/Timer.h"<br>
using namespace llvm;<br>
@@ -253,6 +254,8 @@ bool LPPassManager::runOnFunction(Functi<br>
<br>
// Then call the regular verifyAnalysis functions.<br>
verifyPreservedAnalysis(P);<br>
+<br>
+ F.getContext().yield();<br>
}<br>
<br>
removeNotPreservedAnalysis(P);<br>
<br>
Modified: llvm/trunk/lib/IR/Core.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Core.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Core.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/IR/Core.cpp (original)<br>
+++ llvm/trunk/lib/IR/Core.cpp Thu May 15 21:33:15 2014<br>
@@ -89,6 +89,13 @@ void LLVMContextSetDiagnosticHandler(LLV<br>
DiagnosticContext);<br>
}<br>
<br>
+void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback,<br>
+ void *OpaqueHandle) {<br>
+ auto YieldCallback =<br>
+ LLVM_EXTENSION reinterpret_cast<LLVMContext::YieldCallbackTy>(Callback);<br>
+ unwrap(C)->setYieldCallback(YieldCallback, OpaqueHandle);<br>
+}<br>
+<br>
void LLVMContextDispose(LLVMContextRef C) {<br>
delete unwrap(C);<br>
}<br>
<br>
Modified: llvm/trunk/lib/IR/LLVMContext.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LLVMContext.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LLVMContext.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/IR/LLVMContext.cpp (original)<br>
+++ llvm/trunk/lib/IR/LLVMContext.cpp Thu May 15 21:33:15 2014<br>
@@ -115,6 +115,17 @@ void *LLVMContext::getDiagnosticContext(<br>
return pImpl->DiagnosticContext;<br>
}<br>
<br>
+void LLVMContext::setYieldCallback(YieldCallbackTy Callback, void *OpaqueHandle)<br>
+{<br>
+ pImpl->YieldCallback = Callback;<br>
+ pImpl->YieldOpaqueHandle = OpaqueHandle;<br>
+}<br>
+<br>
+void LLVMContext::yield() {<br>
+ if (pImpl->YieldCallback)<br>
+ pImpl->YieldCallback(this, pImpl->YieldOpaqueHandle);<br>
+}<br>
+<br>
void LLVMContext::emitError(const Twine &ErrorStr) {<br>
diagnose(DiagnosticInfoInlineAsm(ErrorStr));<br>
}<br>
<br>
Modified: llvm/trunk/lib/IR/LLVMContextImpl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LLVMContextImpl.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LLVMContextImpl.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/IR/LLVMContextImpl.cpp (original)<br>
+++ llvm/trunk/lib/IR/LLVMContextImpl.cpp Thu May 15 21:33:15 2014<br>
@@ -41,6 +41,8 @@ LLVMContextImpl::LLVMContextImpl(LLVMCon<br>
InlineAsmDiagContext = nullptr;<br>
DiagnosticHandler = nullptr;<br>
DiagnosticContext = nullptr;<br>
+ YieldCallback = nullptr;<br>
+ YieldOpaqueHandle = nullptr;<br>
NamedStructTypesUniqueID = 0;<br>
}<br>
<br>
<br>
Modified: llvm/trunk/lib/IR/LLVMContextImpl.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LLVMContextImpl.h?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LLVMContextImpl.h?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/IR/LLVMContextImpl.h (original)<br>
+++ llvm/trunk/lib/IR/LLVMContextImpl.h Thu May 15 21:33:15 2014<br>
@@ -242,6 +242,9 @@ public:<br>
LLVMContext::DiagnosticHandlerTy DiagnosticHandler;<br>
void *DiagnosticContext;<br>
<br>
+ LLVMContext::YieldCallbackTy YieldCallback;<br>
+ void *YieldOpaqueHandle;<br>
+<br>
typedef DenseMap<DenseMapAPIntKeyInfo::KeyTy, ConstantInt *,<br>
DenseMapAPIntKeyInfo> IntMapTy;<br>
IntMapTy IntConstants;<br>
<br>
Modified: llvm/trunk/lib/IR/LegacyPassManager.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LegacyPassManager.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/LegacyPassManager.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/IR/LegacyPassManager.cpp (original)<br>
+++ llvm/trunk/lib/IR/LegacyPassManager.cpp Thu May 15 21:33:15 2014<br>
@@ -12,6 +12,7 @@<br>
//===----------------------------------------------------------------------===//<br>
<br>
<br>
+#include "llvm/IR/LLVMContext.h"<br>
#include "llvm/IR/IRPrintingPasses.h"<br>
#include "llvm/IR/LegacyPassManager.h"<br>
#include "llvm/IR/LegacyPassManagers.h"<br>
@@ -1489,8 +1490,10 @@ bool FunctionPassManagerImpl::run(Functi<br>
TimingInfo::createTheTimeInfo();<br>
<br>
initializeAllAnalysisInfo();<br>
- for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index)<br>
+ for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) {<br>
Changed |= getContainedManager(Index)->runOnFunction(F);<br>
+ F.getContext().yield();<br>
+ }<br>
<br>
for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index)<br>
getContainedManager(Index)->cleanup();<br>
@@ -1723,8 +1726,10 @@ bool PassManagerImpl::run(Module &M) {<br>
}<br>
<br>
initializeAllAnalysisInfo();<br>
- for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index)<br>
+ for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) {<br>
Changed |= getContainedManager(Index)->runOnModule(M);<br>
+ M.getContext().yield();<br>
+ }<br>
<br>
for (SmallVectorImpl<ImmutablePass *>::const_iterator I = IPV.begin(),<br>
E = IPV.end(); I != E; ++I) {<br>
<br>
Modified: llvm/trunk/lib/IR/PassManager.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/PassManager.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/PassManager.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/IR/PassManager.cpp (original)<br>
+++ llvm/trunk/lib/IR/PassManager.cpp Thu May 15 21:33:15 2014<br>
@@ -8,6 +8,7 @@<br>
//===----------------------------------------------------------------------===//<br>
<br>
#include "llvm/ADT/STLExtras.h"<br>
+#include "llvm/IR/LLVMContext.h"<br>
#include "llvm/IR/PassManager.h"<br>
#include "llvm/Support/CommandLine.h"<br>
#include "llvm/Support/Debug.h"<br>
@@ -32,6 +33,8 @@ PreservedAnalyses ModulePassManager::run<br>
if (AM)<br>
AM->invalidate(M, PassPA);<br>
PA.intersect(std::move(PassPA));<br>
+<br>
+ M->getContext().yield();<br>
}<br>
<br>
if (DebugPM)<br>
@@ -92,6 +95,8 @@ PreservedAnalyses FunctionPassManager::r<br>
if (AM)<br>
AM->invalidate(F, PassPA);<br>
PA.intersect(std::move(PassPA));<br>
+<br>
+ F->getContext().yield();<br>
}<br>
<br>
if (DebugPM)<br>
<br>
Modified: llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp?rev=208945&r1=208944&r2=208945&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp?rev=208945&r1=208944&r2=208945&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp (original)<br>
+++ llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp Thu May 15 21:33:15 2014<br>
@@ -28,6 +28,7 @@ using namespace llvm;<br>
<br>
static bool didCallAllocateCodeSection;<br>
static bool didAllocateCompactUnwindSection;<br>
+static bool didCallYield;<br>
<br>
static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size,<br>
unsigned alignment,<br>
@@ -64,6 +65,10 @@ static void roundTripDestroy(void *objec<br>
delete static_cast<SectionMemoryManager*>(object);<br>
}<br>
<br>
+static void yield(LLVMContextRef, void *) {<br>
+ didCallYield = true;<br>
+}<br>
+<br>
namespace {<br>
<br>
// memory manager to test reserve allocation space callback<br>
@@ -142,6 +147,7 @@ protected:<br>
virtual void SetUp() {<br>
didCallAllocateCodeSection = false;<br>
didAllocateCompactUnwindSection = false;<br>
+ didCallYield = false;<br>
Module = 0;<br>
Function = 0;<br>
Engine = 0;<br>
@@ -429,3 +435,24 @@ TEST_F(MCJITCAPITest, reserve_allocation<br>
EXPECT_TRUE(MM->UsedCodeSize > 0);<br>
EXPECT_TRUE(MM->UsedDataSizeRW > 0);<br>
}<br>
+<br>
+TEST_F(MCJITCAPITest, yield) {<br>
+ SKIP_UNSUPPORTED_PLATFORM;<br>
+<br>
+ buildSimpleFunction();<br>
+ buildMCJITOptions();<br>
+ buildMCJITEngine();<br>
+ LLVMContextRef C = LLVMGetGlobalContext();<br>
+ LLVMContextSetYieldCallback(C, yield, NULL);<br>
+ buildAndRunPasses();<br>
+<br>
+ union {<br>
+ void *raw;<br>
+ int (*usable)();<br>
+ } functionPointer;<br>
+ functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function);<br>
+<br>
+ EXPECT_EQ(42, functionPointer.usable());<br>
+ EXPECT_TRUE(didCallYield);<br>
+}<br>
+<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div>