1
0
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:
Juergen Ributzka 2014-05-16 02:33:15 +00:00
parent 7e39763d14
commit 271cad0970
11 changed files with 110 additions and 4 deletions

View File

@ -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.
*

View File

@ -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".

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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));
}

View File

@ -41,6 +41,8 @@ LLVMContextImpl::LLVMContextImpl(LLVMContext &C)
InlineAsmDiagContext = nullptr;
DiagnosticHandler = nullptr;
DiagnosticContext = nullptr;
YieldCallback = nullptr;
YieldOpaqueHandle = nullptr;
NamedStructTypesUniqueID = 0;
}

View File

@ -242,6 +242,9 @@ public:
LLVMContext::DiagnosticHandlerTy DiagnosticHandler;
void *DiagnosticContext;
LLVMContext::YieldCallbackTy YieldCallback;
void *YieldOpaqueHandle;
typedef DenseMap<DenseMapAPIntKeyInfo::KeyTy, ConstantInt *,
DenseMapAPIntKeyInfo> IntMapTy;
IntMapTy IntConstants;

View File

@ -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) {

View File

@ -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)

View File

@ -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);
}