Including a global variable lookup in a JIT compiler

Hi,

I was getting started with writing my own JIT compiler, starting with the KaleidoscopeJIT example available from the LLVM git repository ( https://github.com/llvm-mirror/llvm/blob/master/examples/Kaleidoscope/include/KaleidoscopeJIT.h ).

However, even without any modifications to the example, I noticed that global variables are not being found by the lookup function in KaleidoscopeJIT.h

Is the way to lookup global variables somewhat different from that of the lookup for local variables and functions that works in the KaleidoscopeJIT example?

I have an IR code generated that looks like this


ModuleID = ‘my jit module’
source_filename = “my jit module”
target datalayout = “e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128”

%g = type { double, double }

@r = common global %g

define double @b() {
entry_b:
%p = alloca %g, align 8
%0 = getelementptr %g, %g* %p, i32 0, i32 1
store double 1.170000e+02, double* %0, align 8
%1 = load %g, %g* %p, align 8
store %g %1, %g* @r, align 8
%2 = load double, double* %0, align 8
ret double %2
}


However, when the JIT compiler tries to compile the function “b”, I get an error saying


Failure value returned from cantFail wrapped call
Symbols not found: [ _r ]


The error occurs when trying to compile the IR code line


store %g %1, %g* @r, align 8


as the JIT is not able to find the symbol corresponding to the global variable “r” in the symbol table of the JIT.

The relevant lines from the Kaleidoscope JIT seem to be


JITSymbol findSymbol(const std::string Name) {
return findMangledSymbol(mangle(Name));
}

private:
std::string mangle(const std::string &Name) {
std::string MangledName;
{
raw_string_ostream MangledNameStream(MangledName);
Mangler::getNameWithPrefix(MangledNameStream, Name, DL);
}
return MangledName;
}

JITSymbol findMangledSymbol(const std::string &Name) {

// ...
// Search modules in reverse order: from last added to first added.
// This is the opposite of the usual search order for dlsym, but makes more
// sense in a REPL where we want to bind to the newest available definition.
for (auto H : make_range(ModuleKeys.rbegin(), ModuleKeys.rend()))
  if (auto Sym = CompileLayer.findSymbolIn(H, Name, ExportedSymbolsOnly))
    return Sym;

// ... 

return nullptr;

}


The symbol name for “r”, gets mangled to “_r” and is looked up in the module. When not found, nullptr is returned by the findMangledSymbol function. When debugging with breakpoints, I can see that nullptr is indeed being returned.

Is there some modification that needs to be done to the above JIT lookup in order to find global variables, is there a separate symbol table or different mangling that is required?

Could you point me to the right document sources or the right modifications that might be required?

The way I call the JIT compiler on the module is:

    TheJIT->addModule(std::move(TheModule));
    // evaluate things from the module
    auto ExprSymbol = TheJIT->findSymbol("b");
    assert(ExprSymbol && "Function not found");

    // Get the symbol's address and cast it to the right type
    // call the function b
    double (*FP)() = (double (*)())(intptr_t)llvm::cantFail(ExprSymbol.getAddress()); // (THIS IS WHERE THE ERROR IS ENCOUNTERED)