llvm.org GIT mirror llvm / 9bc1b73
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 <rdar://problem/16728690> git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@208945 91177308-0d34-0410-b5e6-96231b3b80d8 Juergen Ributzka 5 years ago
11 changed file(s) with 110 addition(s) and 4 deletion(s). Raw diff Collapse all Expand all
7171 /// \see LLVMContext::diagnose.
7272 typedef void (*DiagnosticHandlerTy)(const DiagnosticInfo &DI, void *Context);
7373
74 /// Defines the type of a yield callback.
75 /// \see LLVMContext::setYieldCallback.
76 typedef void (*YieldCallbackTy)(LLVMContext *Context, void *OpaqueHandle);
77
7478 /// setInlineAsmDiagnosticHandler - This method sets a handler that is invoked
7579 /// when problems with inline asm are detected by the backend. The first
7680 /// argument is a function pointer and the second is a context pointer that
117121 /// for RS_Error, "warning: " for RS_Warning, and "note: " for RS_Note.
118122 void diagnose(const DiagnosticInfo &DI);
119123
124 /// \brief Registers a yield callback with the given context.
125 ///
126 /// The yield callback function may be called by LLVM to transfer control back
127 /// to the client that invoked the LLVM compilation. This can be used to yield
128 /// control of the thread, or perform periodic work needed by the client.
129 /// There is no guaranteed frequency at which callbacks must occur; in fact,
130 /// the client is not guaranteed to ever receive this callback. It is at the
131 /// sole discretion of LLVM to do so and only if it can guarantee that
132 /// suspending the thread won't block any forward progress in other LLVM
133 /// contexts in the same process.
134 ///
135 /// At a suspend point, the state of the current LLVM context is intentionally
136 /// undefined. No assumptions about it can or should be made. Only LLVM
137 /// context API calls that explicitly state that they can be used during a
138 /// yield callback are allowed to be used. Any other API calls into the
139 /// context are not supported until the yield callback function returns
140 /// control to LLVM. Other LLVM contexts are unaffected by this restriction.
141 void setYieldCallback(YieldCallbackTy Callback, void *OpaqueHandle);
142
143 /// \brief Calls the yield callback (if applicable).
144 ///
145 /// This transfers control of the current thread back to the client, which may
146 /// suspend the current thread. Only call this method when LLVM doesn't hold
147 /// any global mutex or cannot block the execution in another LLVM context.
148 void yield();
149
120150 /// emitError - Emit an error message to the currently installed error handler
121151 /// with optional location information. This function returns, so code should
122152 /// be prepared to drop the erroneous construct on the floor and "not crash".
466466 */
467467
468468 typedef void (*LLVMDiagnosticHandler)(LLVMDiagnosticInfoRef, void *);
469 typedef void (*LLVMYieldCallback)(LLVMContextRef, void *);
469470
470471 /**
471472 * Create a new context.
486487 void LLVMContextSetDiagnosticHandler(LLVMContextRef C,
487488 LLVMDiagnosticHandler Handler,
488489 void *DiagnosticContext);
490
491 /**
492 * Set the yield callback function for this context.
493 *
494 * @see LLVMContext::setYieldCallback()
495 */
496 void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback,
497 void *OpaqueHandle);
489498
490499 /**
491500 * Destroy a context instance.
2121 #include "llvm/IR/Function.h"
2222 #include "llvm/IR/IntrinsicInst.h"
2323 #include "llvm/IR/LegacyPassManagers.h"
24 #include "llvm/IR/LLVMContext.h"
2425 #include "llvm/Support/CommandLine.h"
2526 #include "llvm/Support/Debug.h"
2627 #include "llvm/Support/Timer.h"
144145 I != E; ++I) {
145146 if (Function *F = (*I)->getFunction()) {
146147 dumpPassInfo(P, EXECUTION_MSG, ON_FUNCTION_MSG, F->getName());
147 TimeRegion PassTimer(getPassTimer(FPP));
148 Changed |= FPP->runOnFunction(*F);
148 {
149 TimeRegion PassTimer(getPassTimer(FPP));
150 Changed |= FPP->runOnFunction(*F);
151 }
152 F->getContext().yield();
149153 }
150154 }
151155
1414
1515 #include "llvm/Analysis/LoopPass.h"
1616 #include "llvm/IR/IRPrintingPasses.h"
17 #include "llvm/IR/LLVMContext.h"
1718 #include "llvm/Support/Debug.h"
1819 #include "llvm/Support/Timer.h"
1920 using namespace llvm;
252253
253254 // Then call the regular verifyAnalysis functions.
254255 verifyPreservedAnalysis(P);
256
257 F.getContext().yield();
255258 }
256259
257260 removeNotPreservedAnalysis(P);
8686 unwrap(C)->setDiagnosticHandler(
8787 LLVM_EXTENSION reinterpret_cast(Handler),
8888 DiagnosticContext);
89 }
90
91 void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback,
92 void *OpaqueHandle) {
93 auto YieldCallback =
94 LLVM_EXTENSION reinterpret_cast(Callback);
95 unwrap(C)->setYieldCallback(YieldCallback, OpaqueHandle);
8996 }
9097
9198 void LLVMContextDispose(LLVMContextRef C) {
112112
113113 void *LLVMContext::getDiagnosticContext() const {
114114 return pImpl->DiagnosticContext;
115 }
116
117 void LLVMContext::setYieldCallback(YieldCallbackTy Callback, void *OpaqueHandle)
118 {
119 pImpl->YieldCallback = Callback;
120 pImpl->YieldOpaqueHandle = OpaqueHandle;
121 }
122
123 void LLVMContext::yield() {
124 if (pImpl->YieldCallback)
125 pImpl->YieldCallback(this, pImpl->YieldOpaqueHandle);
115126 }
116127
117128 void LLVMContext::emitError(const Twine &ErrorStr) {
4040 InlineAsmDiagContext = nullptr;
4141 DiagnosticHandler = nullptr;
4242 DiagnosticContext = nullptr;
43 YieldCallback = nullptr;
44 YieldOpaqueHandle = nullptr;
4345 NamedStructTypesUniqueID = 0;
4446 }
4547
241241 LLVMContext::DiagnosticHandlerTy DiagnosticHandler;
242242 void *DiagnosticContext;
243243
244 LLVMContext::YieldCallbackTy YieldCallback;
245 void *YieldOpaqueHandle;
246
244247 typedef DenseMap
245248 DenseMapAPIntKeyInfo> IntMapTy;
246249 IntMapTy IntConstants;
1111 //===----------------------------------------------------------------------===//
1212
1313
14 #include "llvm/IR/LLVMContext.h"
1415 #include "llvm/IR/IRPrintingPasses.h"
1516 #include "llvm/IR/LegacyPassManager.h"
1617 #include "llvm/IR/LegacyPassManagers.h"
14881489 TimingInfo::createTheTimeInfo();
14891490
14901491 initializeAllAnalysisInfo();
1491 for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index)
1492 for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) {
14921493 Changed |= getContainedManager(Index)->runOnFunction(F);
1494 F.getContext().yield();
1495 }
14931496
14941497 for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index)
14951498 getContainedManager(Index)->cleanup();
17221725 }
17231726
17241727 initializeAllAnalysisInfo();
1725 for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index)
1728 for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) {
17261729 Changed |= getContainedManager(Index)->runOnModule(M);
1730 M.getContext().yield();
1731 }
17271732
17281733 for (SmallVectorImpl::const_iterator I = IPV.begin(),
17291734 E = IPV.end(); I != E; ++I) {
77 //===----------------------------------------------------------------------===//
88
99 #include "llvm/ADT/STLExtras.h"
10 #include "llvm/IR/LLVMContext.h"
1011 #include "llvm/IR/PassManager.h"
1112 #include "llvm/Support/CommandLine.h"
1213 #include "llvm/Support/Debug.h"
3132 if (AM)
3233 AM->invalidate(M, PassPA);
3334 PA.intersect(std::move(PassPA));
35
36 M->getContext().yield();
3437 }
3538
3639 if (DebugPM)
9194 if (AM)
9295 AM->invalidate(F, PassPA);
9396 PA.intersect(std::move(PassPA));
97
98 F->getContext().yield();
9499 }
95100
96101 if (DebugPM)
2727
2828 static bool didCallAllocateCodeSection;
2929 static bool didAllocateCompactUnwindSection;
30 static bool didCallYield;
3031
3132 static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size,
3233 unsigned alignment,
6162
6263 static void roundTripDestroy(void *object) {
6364 delete static_cast(object);
65 }
66
67 static void yield(LLVMContextRef, void *) {
68 didCallYield = true;
6469 }
6570
6671 namespace {
141146 virtual void SetUp() {
142147 didCallAllocateCodeSection = false;
143148 didAllocateCompactUnwindSection = false;
149 didCallYield = false;
144150 Module = 0;
145151 Function = 0;
146152 Engine = 0;
428434 EXPECT_TRUE(MM->UsedCodeSize > 0);
429435 EXPECT_TRUE(MM->UsedDataSizeRW > 0);
430436 }
437
438 TEST_F(MCJITCAPITest, yield) {
439 SKIP_UNSUPPORTED_PLATFORM;
440
441 buildSimpleFunction();
442 buildMCJITOptions();
443 buildMCJITEngine();
444 LLVMContextRef C = LLVMGetGlobalContext();
445 LLVMContextSetYieldCallback(C, yield, NULL);
446 buildAndRunPasses();
447
448 union {
449 void *raw;
450 int (*usable)();
451 } functionPointer;
452 functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function);
453
454 EXPECT_EQ(42, functionPointer.usable());
455 EXPECT_TRUE(didCallYield);
456 }
457