mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 04:32:44 +01:00
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> llvm-svn: 208945
This commit is contained in:
parent
7e39763d14
commit
271cad0970
@ -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.
|
||||
*
|
||||
|
@ -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".
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -89,6 +89,13 @@ void LLVMContextSetDiagnosticHandler(LLVMContextRef C,
|
||||
DiagnosticContext);
|
||||
}
|
||||
|
||||
void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback,
|
||||
void *OpaqueHandle) {
|
||||
auto YieldCallback =
|
||||
LLVM_EXTENSION reinterpret_cast<LLVMContext::YieldCallbackTy>(Callback);
|
||||
unwrap(C)->setYieldCallback(YieldCallback, OpaqueHandle);
|
||||
}
|
||||
|
||||
void LLVMContextDispose(LLVMContextRef C) {
|
||||
delete unwrap(C);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ LLVMContextImpl::LLVMContextImpl(LLVMContext &C)
|
||||
InlineAsmDiagContext = nullptr;
|
||||
DiagnosticHandler = nullptr;
|
||||
DiagnosticContext = nullptr;
|
||||
YieldCallback = nullptr;
|
||||
YieldOpaqueHandle = nullptr;
|
||||
NamedStructTypesUniqueID = 0;
|
||||
}
|
||||
|
||||
|
@ -242,6 +242,9 @@ public:
|
||||
LLVMContext::DiagnosticHandlerTy DiagnosticHandler;
|
||||
void *DiagnosticContext;
|
||||
|
||||
LLVMContext::YieldCallbackTy YieldCallback;
|
||||
void *YieldOpaqueHandle;
|
||||
|
||||
typedef DenseMap<DenseMapAPIntKeyInfo::KeyTy, ConstantInt *,
|
||||
DenseMapAPIntKeyInfo> IntMapTy;
|
||||
IntMapTy IntConstants;
|
||||
|
@ -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<ImmutablePass *>::const_iterator I = IPV.begin(),
|
||||
E = IPV.end(); I != E; ++I) {
|
||||
|
@ -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)
|
||||
|
@ -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<SectionMemoryManager*>(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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user