llvm-6502/lib/ExecutionEngine/JIT/JIT.h
Reid Kleckner 2763217fbd Implement the JIT side of the GDB JIT debugging interface. To enable this
feature, either build the JIT in debug mode to enable it by default or pass
-jit-emit-debug to lli.

Right now, the only debug information that this communicates to GDB is call
frame information, since it's already being generated to support exceptions in
the JIT.  Eventually, when DWARF generation isn't tied so tightly to AsmPrinter,
it will be easy to push that information to GDB through this interface.

Here's a step-by-step breakdown of how the feature works:

- The JIT generates the machine code and DWARF call frame info
  (.eh_frame/.debug_frame) for a function into memory.
- The JIT copies that info into an in-memory ELF file with a symbol for the
  function.
- The JIT creates a code entry pointing to the ELF buffer and adds it to a
  linked list hanging off of a global descriptor at a special symbol that GDB
  knows about.
- The JIT calls a function marked noinline that GDB knows about and has put an
  internal breakpoint in.
- GDB catches the breakpoint and reads the global descriptor to look for new
  code.
- When sees there is new code, it reads the ELF from the inferior's memory and
  adds it to itself as an object file.
- The JIT continues, and the next time we stop the program, we are able to
  produce a proper backtrace.

Consider running the following program through the JIT:

#include <stdio.h>
void baz(short z) {
  long w = z + 1;
  printf("%d, %x\n", w, *((int*)NULL));  // SEGFAULT here
}
void bar(short y) {
  int z = y + 1;
  baz(z);
}
void foo(char x) {
  short y = x + 1;
  bar(y);
}
int main(int argc, char** argv) {
  char x = 1;
  foo(x);
}

Here is a backtrace before this patch:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x2aaaabdfbd10 (LWP 25476)]
0x00002aaaabe7d1a8 in ?? ()
(gdb) bt
#0  0x00002aaaabe7d1a8 in ?? ()
#1  0x0000000000000003 in ?? ()
#2  0x0000000000000004 in ?? ()
#3  0x00032aaaabe7cfd0 in ?? ()
#4  0x00002aaaabe7d12c in ?? ()
#5  0x00022aaa00000003 in ?? ()
#6  0x00002aaaabe7d0aa in ?? ()
#7  0x01000002abe7cff0 in ?? ()
#8  0x00002aaaabe7d02c in ?? ()
#9  0x0100000000000001 in ?? ()
#10 0x00000000014388e0 in ?? ()
#11 0x00007fff00000001 in ?? ()
#12 0x0000000000b870a2 in llvm::JIT::runFunction (this=0x1405b70,
F=0x14024e0, ArgValues=@0x7fffffffe050)
   at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:395
#13 0x0000000000baa4c5 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x1405b70, Fn=0x14024e0, argv=@0x13f06f8, envp=0x7fffffffe3b0)
   at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:377
#14 0x00000000007ebd52 in main (argc=2, argv=0x7fffffffe398,
envp=0x7fffffffe3b0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:208

And a backtrace after this patch:
Program received signal SIGSEGV, Segmentation fault.
0x00002aaaabe7d1a8 in baz ()
(gdb) bt
#0  0x00002aaaabe7d1a8 in baz ()
#1  0x00002aaaabe7d12c in bar ()
#2  0x00002aaaabe7d0aa in foo ()
#3  0x00002aaaabe7d02c in main ()
#4  0x0000000000b870a2 in llvm::JIT::runFunction (this=0x1405b70,
F=0x14024e0, ArgValues=...)
   at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:395
#5  0x0000000000baa4c5 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x1405b70, Fn=0x14024e0, argv=..., envp=0x7fffffffe3c0)
   at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:377
#6  0x00000000007ebd52 in main (argc=2, argv=0x7fffffffe3a8,
envp=0x7fffffffe3c0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:208


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@82418 91177308-0d34-0410-b5e6-96231b3b80d8
2009-09-20 23:52:43 +00:00

204 lines
7.5 KiB
C++

//===-- JIT.h - Class definition for the JIT --------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the top-level JIT data structure.
//
//===----------------------------------------------------------------------===//
#ifndef JIT_H
#define JIT_H
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/PassManager.h"
namespace llvm {
class Function;
struct JITEvent_EmittedFunctionDetails;
class MachineCodeEmitter;
class MachineCodeInfo;
class TargetJITInfo;
class TargetMachine;
class JITState {
private:
FunctionPassManager PM; // Passes to compile a function
ModuleProvider *MP; // ModuleProvider used to create the PM
/// PendingFunctions - Functions which have not been code generated yet, but
/// were called from a function being code generated.
std::vector<Function*> PendingFunctions;
public:
explicit JITState(ModuleProvider *MP) : PM(MP), MP(MP) {}
FunctionPassManager &getPM(const MutexGuard &L) {
return PM;
}
ModuleProvider *getMP() const { return MP; }
std::vector<Function*> &getPendingFunctions(const MutexGuard &L) {
return PendingFunctions;
}
};
class JIT : public ExecutionEngine {
TargetMachine &TM; // The current target we are compiling to
TargetJITInfo &TJI; // The JITInfo for the target we are compiling to
JITCodeEmitter *JCE; // JCE object
std::vector<JITEventListener*> EventListeners;
/// AllocateGVsWithCode - Some applications require that global variables and
/// code be allocated into the same region of memory, in which case this flag
/// should be set to true. Doing so breaks freeMachineCodeForFunction.
bool AllocateGVsWithCode;
JITState *jitstate;
JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji,
JITMemoryManager *JMM, CodeGenOpt::Level OptLevel,
bool AllocateGVsWithCode);
public:
~JIT();
static void Register() {
JITCtor = create;
}
/// getJITInfo - Return the target JIT information structure.
///
TargetJITInfo &getJITInfo() const { return TJI; }
/// create - Create an return a new JIT compiler if there is one available
/// for the current target. Otherwise, return null.
///
static ExecutionEngine *create(ModuleProvider *MP,
std::string *Err,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel =
CodeGenOpt::Default,
bool GVsWithCode = true) {
return ExecutionEngine::createJIT(MP, Err, JMM, OptLevel, GVsWithCode);
}
virtual void addModuleProvider(ModuleProvider *MP);
/// removeModuleProvider - Remove a ModuleProvider from the list of modules.
/// Relases the Module from the ModuleProvider, materializing it in the
/// process, and returns the materialized Module.
virtual Module *removeModuleProvider(ModuleProvider *MP,
std::string *ErrInfo = 0);
/// deleteModuleProvider - Remove a ModuleProvider from the list of modules,
/// and deletes the ModuleProvider and owned Module. Avoids materializing
/// the underlying module.
virtual void deleteModuleProvider(ModuleProvider *P,std::string *ErrInfo = 0);
/// runFunction - Start execution with the specified function and arguments.
///
virtual GenericValue runFunction(Function *F,
const std::vector<GenericValue> &ArgValues);
/// getPointerToNamedFunction - This method returns the address of the
/// specified function by using the dlsym function call. As such it is only
/// useful for resolving library symbols, not code generated symbols.
///
/// If AbortOnFailure is false and no function with the given name is
/// found, this function silently returns a null pointer. Otherwise,
/// it prints a message to stderr and aborts.
///
void *getPointerToNamedFunction(const std::string &Name,
bool AbortOnFailure = true);
// CompilationCallback - Invoked the first time that a call site is found,
// which causes lazy compilation of the target function.
//
static void CompilationCallback();
/// getPointerToFunction - This returns the address of the specified function,
/// compiling it if necessary.
///
void *getPointerToFunction(Function *F);
/// getOrEmitGlobalVariable - Return the address of the specified global
/// variable, possibly emitting it to memory if needed. This is used by the
/// Emitter.
void *getOrEmitGlobalVariable(const GlobalVariable *GV);
/// getPointerToFunctionOrStub - If the specified function has been
/// code-gen'd, return a pointer to the function. If not, compile it, or use
/// a stub to implement lazy compilation if available.
///
void *getPointerToFunctionOrStub(Function *F);
/// recompileAndRelinkFunction - This method is used to force a function
/// which has already been compiled, to be compiled again, possibly
/// after it has been modified. Then the entry to the old copy is overwritten
/// with a branch to the new copy. If there was no old copy, this acts
/// just like JIT::getPointerToFunction().
///
void *recompileAndRelinkFunction(Function *F);
/// freeMachineCodeForFunction - deallocate memory used to code-generate this
/// Function.
///
void freeMachineCodeForFunction(Function *F);
/// addPendingFunction - while jitting non-lazily, a called but non-codegen'd
/// function was encountered. Add it to a pending list to be processed after
/// the current function.
///
void addPendingFunction(Function *F);
/// getCodeEmitter - Return the code emitter this JIT is emitting into.
///
JITCodeEmitter *getCodeEmitter() const { return JCE; }
/// selectTarget - Pick a target either via -march or by guessing the native
/// arch. Add any CPU features specified via -mcpu or -mattr.
static TargetMachine *selectTarget(ModuleProvider *MP, std::string *Err);
static ExecutionEngine *createJIT(ModuleProvider *MP,
std::string *ErrorStr,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel,
bool GVsWithCode);
// Run the JIT on F and return information about the generated code
void runJITOnFunction(Function *F, MachineCodeInfo *MCI = 0);
virtual void RegisterJITEventListener(JITEventListener *L);
virtual void UnregisterJITEventListener(JITEventListener *L);
/// These functions correspond to the methods on JITEventListener. They
/// iterate over the registered listeners and call the corresponding method on
/// each.
void NotifyFunctionEmitted(
const Function &F, void *Code, size_t Size,
const JITEvent_EmittedFunctionDetails &Details);
void NotifyFreeingMachineCode(const Function &F, void *OldPtr);
private:
static JITCodeEmitter *createEmitter(JIT &J, JITMemoryManager *JMM,
TargetMachine &tm);
void runJITOnFunctionUnlocked(Function *F, const MutexGuard &locked);
void updateFunctionStub(Function *F);
void updateDlsymStubTable();
protected:
/// getMemoryforGV - Allocate memory for a global variable.
virtual char* getMemoryForGV(const GlobalVariable* GV);
};
} // End llvm namespace
#endif