experimenting with partial evaluation

Hello !

I wanted to experiment with partial evaluation and llvm seems to be the
right tool for this, but since I'm new to it of course I'm a bit lost !

I'll try to explain what I want to do in the simplest possible way :

I have a C program.
In this program, there is a function f( a,b ).
I have a value A for a.
I want to specialise f() so I get a function fA( b ) which is the same as
f( a=A, b ).
It looks like a futamura projection (think about the classical adder
problem).

So, in my program, at runtime, I'd like to get a llvm Function* on the
function I want to specialize, so I can write code that hacks on the
arguments and bitcode of f and JITs a new version of it.

I have found examples about how to generate llvm instructions at runtime
and JIT that into a function, but that's not what I'm interested in : I
want to modify the code of an existing function, not generate a function
   from nothing.

I succeeded in compiling to bitcode, loading the program's bitcode,
extracting a Function *, JITting it and executing it, but then whatever is
in my function can't call the rest of the program, because when I load the
bitcode, what is in it has no relation to the rest of my program, even
though it is the same code, there are actually 2 copies of it which are
not linked.

And I'm a llvm newbie so I have a few questions :

- In a running program, can I get pointers to the llvm Module that
contains the code currently being executed ?
     Using this I could do a module->getFunction() to get the Function, clone
it, tweak it, JIT it and I'd be happy.

- When I JIT something how can I link it to my running program so it can
call functions and access globals ?

- How should I compile my program so I can do all that ? (I guess I
shouldn't compile it to x86-64 native but bitcode instead, but what
options ?..)

Maybe I'm asking the wrong questions, feel free to object :wink:

Thanks for your time.

Good evening, Pierre.

Here is an example to you. attached. run with -disable-lazy-comilation.
It is *as-is* for Win32 but might be applicable to *-linux-elf more smartly.
It does get llvm::Function* by function ptr(not name!),
and clone a specialized function,
and execute specialized one
in the same context as parent lli.

- In a running program, can I get pointers to the llvm Module that
contains the code currently being executed ?
    Using this I could do a module->getFunction() to get the Function,
clone
it, tweak it, JIT it and I'd be happy.

AFAIK, current version of lli does not support.
I let lli to export current llvm::Module* and llvm::ExecutionEngine*.

Current llvm::Module const* could be gotten from llvm::Function const*,
but you must need not Module const* but writable one.
(or, clone a module to optimize)

- When I JIT something how can I link it to my running program so it can
call functions and access globals ?

- to export some symbols in lli and llvm/lib. It would be difficult in Win32.
   I believe it possible to work with ELF.

- to build llvm with shared object. Some APIs might be neede to
   your application. lli should resolve symbols when he reads *.bc.

- How should I compile my program so I can do all that ? (I guess I
shouldn't compile it to x86-64 native but bitcode instead, but what
options ?..)

I guess you will need a special version of lli.

mata ne, Takumi

j.txt (1.72 KB)

j.cpp (1.38 KB)

lli.diff (2.99 KB)

Here is an example to you. attached. run with -disable-lazy-comilation.
It is *as-is* for Win32 but might be applicable to *-linux-elf more smartly.
It does get llvm::Function* by function ptr(not name!),
and clone a specialized function,
and execute specialized one
in the same context as parent lli.

Many thanks. Using your code I was able to get useful results ! Many thanks to the helpful people on IRC, too.

AFAIK, current version of lli does not support.
I let lli to export current llvm::Module* and llvm::ExecutionEngine*.

I've tried to patch lli like you did but the problem was that I got lots of "LLVM ERROR: Program used external function" which is why, I guess, you had to enumerate all the symbols you used as externs in your patch... I'd rather avoid this solution :wink:

So I've tried something else : I've written a minimalistic launcher (like a bare bones lli) which loads the module which contains the code, passes it its own Module* and ExecutionEngine*, so the code inside the module has those, then runs it.

It works nicely, except I have to launch my launcher with lli, if I compile it native and run it, I get the same undefined external functions errors. I've probably done something wrong somewhere...

I'd like to use the patched lli (passing the pointers to the code in the Module) since this is the most elegant solution, if anyone has ideas on how to get rid of the missing symbols. I guess i'd need to link all libraries used by the Module inside lli which could get ugly.

Anyway, I can run partial evaluation on a function like you did, but i have a little problem : if the function gets a bit complex, then the optimization pass crashes. I get this :

0 libLLVM-2.7.so.1 0x00007fefb5274d2f
1 libLLVM-2.7.so.1 0x00007fefb527538d
2 libpthread.so.0 0x00007fefb46668f0
3 libLLVM-2.7.so.1 0x00007fefb4e90b09 llvm::ConstantExpr::getSizeOf(llvm::Type const*) + 9
4 libLLVM-2.7.so.1 0x00007fefb4c82c31 llvm::ScalarEvolution::getSizeOfExpr(llvm::Type const*) + 33
5 libLLVM-2.7.so.1 0x00007fefb4c72693 llvm::ScalarEvolution::createNodeForGEP(llvm::GEPOperator*) + 483
6 libLLVM-2.7.so.1 0x00007fefb4c731fd llvm::ScalarEvolution::createSCEV(llvm::Value*) + 1565
7 libLLVM-2.7.so.1 0x00007fefb4c73ad3 llvm::ScalarEvolution::getSCEV(llvm::Value*) + 227
8 libLLVM-2.7.so.1 0x00007fefb4c73482 llvm::ScalarEvolution::createSCEV(llvm::Value*) + 2210
9 libLLVM-2.7.so.1 0x00007fefb4c73ad3 llvm::ScalarEvolution::getSCEV(llvm::Value*) + 227
10 libLLVM-2.7.so.1 0x00007fefb4c82864 llvm::ScalarEvolution::createNodeForPHI(llvm::PHINode*) + 1060
11 libLLVM-2.7.so.1 0x00007fefb4c72f10 llvm::ScalarEvolution::createSCEV(llvm::Value*) + 816
12 libLLVM-2.7.so.1 0x00007fefb4c73ad3 llvm::ScalarEvolution::getSCEV(llvm::Value*) + 227
13 libLLVM-2.7.so.1 0x00007fefb4c20d58 llvm::IVUsers::AddUsersIfInteresting(llvm::Instruction*) + 312
14 libLLVM-2.7.so.1 0x00007fefb4c21ffb llvm::IVUsers::runOnLoop(llvm::Loop*, llvm::LPPassManager&) + 331
15 libLLVM-2.7.so.1 0x00007fefb4c3aa67 llvm::LPPassManager::runOnFunction(llvm::Function&) + 1015
16 libLLVM-2.7.so.1 0x00007fefb4ef6172 llvm::FPPassManager::runOnFunction(llvm::Function&) + 498
17 libLLVM-2.7.so.1 0x00007fefb4ef62eb llvm::FunctionPassManagerImpl::run(llvm::Function&) + 91
18 libLLVM-2.7.so.1 0x00007fefb4ef649e llvm::FunctionPassManager::run(llvm::Function&) + 110
19 libLLVM-2.7.so.1 0x00007fefb4f9821c llvm::JIT::runJITOnFunctionUnlocked(llvm::Function*, llvm::MutexGuard const&) + 28
20 libLLVM-2.7.so.1 0x00007fefb4f985e3 llvm::JIT::getPointerToFunction(llvm::Function*) + 387
21 libLLVM-2.7.so.1 0x00007fefb59a08c9 llvm::JIT::getPointerToFunction(llvm::Function*) + 10519657
Stack dump:
0. Program arguments: lli -disable-lazy-compilation launcher_jit.bc
1. Running pass 'Loop Pass Manager' on function '@_Z14rpn_exec_stackPK15RPN_InstructioniPdPK13RPN_Variables_1'
2. Running pass 'Induction Variable Users' on basic block '%bb24'
Erreur de segmentation

The partial evaluation on another simpler function works, but if I add an Inlining pass, then the optimization pass crashes too. I get this :

0 libLLVM-2.7.so.1 0x00007f72d543dd2f
1 libLLVM-2.7.so.1 0x00007f72d543e38d
2 libpthread.so.0 0x00007f72d482f8f0
3 libLLVM-2.7.so.1 0x00007f72d50bde91 llvm::ModulePass::assignPassManager(llvm::PMStack&, llvm::PassManagerType) + 113
4 libLLVM-2.7.so.1 0x00007f72d50baef9 llvm::PMTopLevelManager::schedulePass(llvm::Pass*) + 425
5 libLLVM-2.7.so.1 0x00007f72d5b69854 llvm::PMTopLevelManager::schedulePass(llvm::Pass*) + 11201284
Stack dump:
0. Program arguments: lli -disable-lazy-compilation launcher_jit.bc
Erreur de segmentation

Argh. Any ideas ?