Change the JIT to compile eagerly by default as agreed in

http://llvm.org/PR5184, and beef up the comments to describe what both options
do and the risks of lazy compilation in the presence of threads.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@85295 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Jeffrey Yasskin
2009-10-27 20:30:28 +00:00
parent 5f75cf511b
commit dc85724f70
8 changed files with 59 additions and 59 deletions

View File

@@ -388,24 +388,19 @@ entry:
</pre> </pre>
</div> </div>
<p>This illustrates that we can now call user code, but there is something a bit subtle <p>This illustrates that we can now call user code, but there is something a bit
going on here. Note that we only invoke the JIT on the anonymous functions subtle going on here. Note that we only invoke the JIT on the anonymous
that <em>call testfunc</em>, but we never invoked it on <em>testfunc functions that <em>call testfunc</em>, but we never invoked it
</em>itself.</p> on <em>testfunc</em> itself. What actually happened here is that the JIT
scanned for all non-JIT'd functions transitively called from the anonymous
function and compiled all of them before returning
from <tt>getPointerToFunction()</tt>.</p>
<p>What actually happened here is that the anonymous function was <p>The JIT provides a number of other more advanced interfaces for things like
JIT'd when requested. When the Kaleidoscope app calls through the function freeing allocated machine code, rejit'ing functions to update them, etc.
pointer that is returned, the anonymous function starts executing. It ends up However, even with this simple code, we get some surprisingly powerful
making the call to the "testfunc" function, and ends up in a stub that invokes capabilities - check this out (I removed the dump of the anonymous functions,
the JIT, lazily, on testfunc. Once the JIT finishes lazily compiling testfunc, you should get the idea by now :) :</p>
it returns and the code re-executes the call.</p>
<p>In summary, the JIT will lazily JIT code, on the fly, as it is needed. The
JIT provides a number of other more advanced interfaces for things like freeing
allocated machine code, rejit'ing functions to update them, etc. However, even
with this simple code, we get some surprisingly powerful capabilities - check
this out (I removed the dump of the anonymous functions, you should get the idea
by now :) :</p>
<div class="doc_code"> <div class="doc_code">
<pre> <pre>
@@ -453,8 +448,8 @@ directly.</p>
resolved. It allows you to establish explicit mappings between IR objects and resolved. It allows you to establish explicit mappings between IR objects and
addresses (useful for LLVM global variables that you want to map to static addresses (useful for LLVM global variables that you want to map to static
tables, for example), allows you to dynamically decide on the fly based on the tables, for example), allows you to dynamically decide on the fly based on the
function name, and even allows you to have the JIT abort itself if any lazy function name, and even allows you to have the JIT compile functions lazily the
compilation is attempted.</p> first time they're called.</p>
<p>One interesting application of this is that we can now extend the language <p>One interesting application of this is that we can now extend the language
by writing arbitrary C++ code to implement operations. For example, if we add: by writing arbitrary C++ code to implement operations. For example, if we add:

View File

@@ -406,22 +406,17 @@ entry:
<p>This illustrates that we can now call user code, but there is something a bit <p>This illustrates that we can now call user code, but there is something a bit
subtle going on here. Note that we only invoke the JIT on the anonymous subtle going on here. Note that we only invoke the JIT on the anonymous
functions that <em>call testfunc</em>, but we never invoked it on <em>testfunc functions that <em>call testfunc</em>, but we never invoked it
</em>itself.</p> on <em>testfunc</em> itself. What actually happened here is that the JIT
scanned for all non-JIT'd functions transitively called from the anonymous
function and compiled all of them before returning
from <tt>run_function</tt>.</p>
<p>What actually happened here is that the anonymous function was JIT'd when <p>The JIT provides a number of other more advanced interfaces for things like
requested. When the Kaleidoscope app calls through the function pointer that is freeing allocated machine code, rejit'ing functions to update them, etc.
returned, the anonymous function starts executing. It ends up making the call However, even with this simple code, we get some surprisingly powerful
to the "testfunc" function, and ends up in a stub that invokes the JIT, lazily, capabilities - check this out (I removed the dump of the anonymous functions,
on testfunc. Once the JIT finishes lazily compiling testfunc, you should get the idea by now :) :</p>
it returns and the code re-executes the call.</p>
<p>In summary, the JIT will lazily JIT code, on the fly, as it is needed. The
JIT provides a number of other more advanced interfaces for things like freeing
allocated machine code, rejit'ing functions to update them, etc. However, even
with this simple code, we get some surprisingly powerful capabilities - check
this out (I removed the dump of the anonymous functions, you should get the idea
by now :) :</p>
<div class="doc_code"> <div class="doc_code">
<pre> <pre>
@@ -467,8 +462,8 @@ calls in the module to call the libm version of <tt>sin</tt> directly.</p>
get resolved. It allows you to establish explicit mappings between IR objects get resolved. It allows you to establish explicit mappings between IR objects
and addresses (useful for LLVM global variables that you want to map to static and addresses (useful for LLVM global variables that you want to map to static
tables, for example), allows you to dynamically decide on the fly based on the tables, for example), allows you to dynamically decide on the fly based on the
function name, and even allows you to have the JIT abort itself if any lazy function name, and even allows you to have the JIT compile functions lazily the
compilation is attempted.</p> first time they're called.</p>
<p>One interesting application of this is that we can now extend the language <p>One interesting application of this is that we can now extend the language
by writing arbitrary C code to implement operations. For example, if we add: by writing arbitrary C code to implement operations. For example, if we add:

View File

@@ -88,7 +88,7 @@ public:
class ExecutionEngine { class ExecutionEngine {
const TargetData *TD; const TargetData *TD;
ExecutionEngineState EEState; ExecutionEngineState EEState;
bool LazyCompilationDisabled; bool CompilingLazily;
bool GVCompilationDisabled; bool GVCompilationDisabled;
bool SymbolSearchingDisabled; bool SymbolSearchingDisabled;
bool DlsymStubsEnabled; bool DlsymStubsEnabled;
@@ -319,13 +319,24 @@ public:
virtual void RegisterJITEventListener(JITEventListener *) {} virtual void RegisterJITEventListener(JITEventListener *) {}
virtual void UnregisterJITEventListener(JITEventListener *) {} virtual void UnregisterJITEventListener(JITEventListener *) {}
/// DisableLazyCompilation - If called, the JIT will abort if lazy compilation /// EnableLazyCompilation - When lazy compilation is off (the default), the
/// is ever attempted. /// JIT will eagerly compile every function reachable from the argument to
void DisableLazyCompilation(bool Disabled = true) { /// getPointerToFunction. If lazy compilation is turned on, the JIT will only
LazyCompilationDisabled = Disabled; /// compile the one function and emit stubs to compile the rest when they're
/// first called. If lazy compilation is turned off again while some lazy
/// stubs are still around, and one of those stubs is called, the program will
/// abort.
///
/// In order to safely compile lazily in a threaded program, the user must
/// ensure that 1) only one thread at a time can call any particular lazy
/// stub, and 2) any thread modifying LLVM IR must hold the JIT's lock
/// (ExecutionEngine::lock) or otherwise ensure that no other thread calls a
/// lazy stub. See http://llvm.org/PR5184 for details.
void EnableLazyCompilation(bool Enabled = true) {
CompilingLazily = Enabled;
} }
bool isLazyCompilationDisabled() const { bool isCompilingLazily() const {
return LazyCompilationDisabled; return CompilingLazily;
} }
/// DisableGVCompilation - If called, the JIT will abort if it's asked to /// DisableGVCompilation - If called, the JIT will abort if it's asked to

View File

@@ -49,7 +49,7 @@ ExecutionEngine::EERegisterFn ExecutionEngine::ExceptionTableRegister = 0;
ExecutionEngine::ExecutionEngine(ModuleProvider *P) ExecutionEngine::ExecutionEngine(ModuleProvider *P)
: EEState(*this), : EEState(*this),
LazyFunctionCreator(0) { LazyFunctionCreator(0) {
LazyCompilationDisabled = false; CompilingLazily = false;
GVCompilationDisabled = false; GVCompilationDisabled = false;
SymbolSearchingDisabled = false; SymbolSearchingDisabled = false;
DlsymStubsEnabled = false; DlsymStubsEnabled = false;

View File

@@ -599,7 +599,7 @@ void JIT::runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked) {
isAlreadyCodeGenerating = false; isAlreadyCodeGenerating = false;
// If the function referred to another function that had not yet been // If the function referred to another function that had not yet been
// read from bitcode, but we are jitting non-lazily, emit it now. // read from bitcode, and we are jitting non-lazily, emit it now.
while (!jitstate->getPendingFunctions(locked).empty()) { while (!jitstate->getPendingFunctions(locked).empty()) {
Function *PF = jitstate->getPendingFunctions(locked).back(); Function *PF = jitstate->getPendingFunctions(locked).back();
jitstate->getPendingFunctions(locked).pop_back(); jitstate->getPendingFunctions(locked).pop_back();
@@ -616,7 +616,7 @@ void JIT::runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked) {
// If the JIT is configured to emit info so that dlsym can be used to // If the JIT is configured to emit info so that dlsym can be used to
// rewrite stubs to external globals, do so now. // rewrite stubs to external globals, do so now.
if (areDlsymStubsEnabled() && isLazyCompilationDisabled()) if (areDlsymStubsEnabled() && !isCompilingLazily())
updateDlsymStubTable(); updateDlsymStubTable();
} }

View File

@@ -295,10 +295,10 @@ void *JITResolver::getFunctionStub(Function *F) {
void *&Stub = state.getFunctionToStubMap(locked)[F]; void *&Stub = state.getFunctionToStubMap(locked)[F];
if (Stub) return Stub; if (Stub) return Stub;
// Call the lazy resolver function unless we are JIT'ing non-lazily, in which // Call the lazy resolver function if we are JIT'ing lazily. Otherwise we
// case we must resolve the symbol now. // must resolve the symbol now.
void *Actual = TheJIT->isLazyCompilationDisabled() void *Actual = TheJIT->isCompilingLazily()
? (void *)0 : (void *)(intptr_t)LazyResolverFn; ? (void *)(intptr_t)LazyResolverFn : (void *)0;
// If this is an external declaration, attempt to resolve the address now // If this is an external declaration, attempt to resolve the address now
// to place in the stub. // to place in the stub.
@@ -334,7 +334,7 @@ void *JITResolver::getFunctionStub(Function *F) {
// If we are JIT'ing non-lazily but need to call a function that does not // If we are JIT'ing non-lazily but need to call a function that does not
// exist yet, add it to the JIT's work list so that we can fill in the stub // exist yet, add it to the JIT's work list so that we can fill in the stub
// address later. // address later.
if (!Actual && TheJIT->isLazyCompilationDisabled()) if (!Actual && !TheJIT->isCompilingLazily())
if (!F->isDeclaration() || F->hasNotBeenReadFromBitcode()) if (!F->isDeclaration() || F->hasNotBeenReadFromBitcode())
TheJIT->addPendingFunction(F); TheJIT->addPendingFunction(F);
@@ -471,7 +471,7 @@ void *JITResolver::JITCompilerFn(void *Stub) {
// Otherwise we don't have it, do lazy compilation now. // Otherwise we don't have it, do lazy compilation now.
// If lazy compilation is disabled, emit a useful error message and abort. // If lazy compilation is disabled, emit a useful error message and abort.
if (TheJIT->isLazyCompilationDisabled()) { if (!TheJIT->isCompilingLazily()) {
llvm_report_error("LLVM JIT requested to do lazy compilation of function '" llvm_report_error("LLVM JIT requested to do lazy compilation of function '"
+ F->getName() + "' when lazy compiles are disabled!"); + F->getName() + "' when lazy compiles are disabled!");
} }
@@ -769,7 +769,7 @@ void *JITEmitter::getPointerToGlobal(GlobalValue *V, void *Reference,
// mechanism is capable of rewriting the instruction directly, prefer to do // mechanism is capable of rewriting the instruction directly, prefer to do
// that instead of emitting a stub. This uses the lazy resolver, so is not // that instead of emitting a stub. This uses the lazy resolver, so is not
// legal if lazy compilation is disabled. // legal if lazy compilation is disabled.
if (DoesntNeedStub && !TheJIT->isLazyCompilationDisabled()) if (DoesntNeedStub && TheJIT->isCompilingLazily())
return Resolver.AddCallbackAtLocation(F, Reference); return Resolver.AddCallbackAtLocation(F, Reference);
// Otherwise, we have to emit a stub. // Otherwise, we have to emit a stub.

View File

@@ -165,8 +165,7 @@ int main(int argc, char **argv, char * const *envp) {
EE->RegisterJITEventListener(createOProfileJITEventListener()); EE->RegisterJITEventListener(createOProfileJITEventListener());
if (NoLazyCompilation) EE->EnableLazyCompilation(!NoLazyCompilation);
EE->DisableLazyCompilation();
// If the user specifically requested an argv[0] to pass into the program, // If the user specifically requested an argv[0] to pass into the program,
// do it now. // do it now.

View File

@@ -304,7 +304,7 @@ TEST_F(JITTest, FarCallToKnownFunction) {
Builder.CreateRet(result); Builder.CreateRet(result);
TheJIT->EnableDlsymStubs(false); TheJIT->EnableDlsymStubs(false);
TheJIT->DisableLazyCompilation(); TheJIT->EnableLazyCompilation(false);
int (*TestFunctionPtr)() = reinterpret_cast<int(*)()>( int (*TestFunctionPtr)() = reinterpret_cast<int(*)()>(
(intptr_t)TheJIT->getPointerToFunction(TestFunction)); (intptr_t)TheJIT->getPointerToFunction(TestFunction));
// This used to crash in trying to call PlusOne(). // This used to crash in trying to call PlusOne().
@@ -314,7 +314,7 @@ TEST_F(JITTest, FarCallToKnownFunction) {
#if !defined(__arm__) && !defined(__powerpc__) && !defined(__ppc__) #if !defined(__arm__) && !defined(__powerpc__) && !defined(__ppc__)
// Test a function C which calls A and B which call each other. // Test a function C which calls A and B which call each other.
TEST_F(JITTest, NonLazyCompilationStillNeedsStubs) { TEST_F(JITTest, NonLazyCompilationStillNeedsStubs) {
TheJIT->DisableLazyCompilation(); TheJIT->EnableLazyCompilation(false);
const FunctionType *Func1Ty = const FunctionType *Func1Ty =
cast<FunctionType>(TypeBuilder<void(void), false>::get(Context)); cast<FunctionType>(TypeBuilder<void(void), false>::get(Context));
@@ -370,7 +370,7 @@ TEST_F(JITTest, NonLazyCompilationStillNeedsStubs) {
// Regression test for PR5162. This used to trigger an AssertingVH inside the // Regression test for PR5162. This used to trigger an AssertingVH inside the
// JIT's Function to stub mapping. // JIT's Function to stub mapping.
TEST_F(JITTest, NonLazyLeaksNoStubs) { TEST_F(JITTest, NonLazyLeaksNoStubs) {
TheJIT->DisableLazyCompilation(); TheJIT->EnableLazyCompilation(false);
// Create two functions with a single basic block each. // Create two functions with a single basic block each.
const FunctionType *FuncTy = const FunctionType *FuncTy =