API on JIT, code snippets

Hi all,

I think there is still too few docs/samples for those,
who'd like to write JIT-based interpreters.

Today, the real examples to learn from are rather:

- lli.cpp
- ModuleMaker.cpp
- Stacker

which is still unfortunatelly not that much
about JITing :frowning:

Well, what I am going to sell:

What about very small JIT-based example similar
to ModuleMaker?

I mean example, where, say, two simple functions are
created, inserted to a Module/BasicBlock, then executed
via JIT and result is passed to a driver.
It would be really great.

Thanks.

Valery,

I agree that there could be more examples of JIT-based interpreters, and
I like your idea. When can you have it ready? :slight_smile:

Reid.

Hi Reid,

I agree that there could be more examples of JIT-based interpreters, and
I like your idea. When can you have it ready? :slight_smile:

soon, if you could just help me to get through some
sticky places :slight_smile:

Agreed? (if "yes" then you could simply answer me privately
and I will send you my concrete questions)

Thank you.

Your example would essentially take lli.cpp and hack out the part where
the bytecode is read from a file to instead construct a Module directly.
See the part where there's a ModuleProvider created from an InputFile
and then ExecutionEngine::create() is called on the ModuleProvider.
That's about all that would need to change.

You can also use ExecutionEngine::runFunctionAsMain() if you wish.

For a trivial example, if you look through the Module, ModuleProvider,
and ExecutionEngine interfaces, you can probably simplify lli.cpp a
great deal to work with a single main() function in a Module with one
return statement.

HTH,

Please feel free to send all LLVM-related questions to LLVM-dev. You
will get your response faster and possibly with more details than if you
send to any single person. :slight_smile:

Valery,

I have to agree with Misha on this. None of us knows "everything" about
LLVM and as you can see, Misha responded three hours before I did :).
Asking questions here is encouraged, so please feel free to post them on
LLVMdev. We'll always help where we can.

Thanks,

Reid.

Reid wrote:

I have to agree with Misha on this. None of us knows "everything" about
LLVM and as you can see, Misha responded three hours before I did :).
Asking questions here is encouraged, so please feel free to post them on
LLVMdev. We'll always help where we can.

well, OK :slight_smile:

Please find the attachment with the first approach to
such an example i've meant. This snippet is based on
ModuleMaker as you could see.

You will find "FIXME!" at positions where I fail to find
answer in 10-15 minutes. Code is compilable at least.

So... PING!
Who gives PONG?
:slight_smile:

t.cpp (4.06 KB)

Valery,

First response of several.

I don't know why the demo page at UIUC is unavailable but there is an enhanced copy of it running on the mirror at http://llvm.x10sys.com/demo/ if you ever need it. Running it produced the following LLVM equivalent for the C code in your example.

implementation ; Functions:

int %add1(int %x) {
entry:
  %tmp.1 = add int %x, 1 ; <int> [#uses=1]
  ret int %tmp.1
}

int %foo() {
entry:
  ret int 11
}

Reid
Valery A.Khamenya wrote:

I don't know why the demo page at UIUC is unavailable but there is an enhanced

FWIW, the demo page has been fixed. It was broken as a result of the
recent system upgrades and I guess noone noticed.

Sorry about that.

-Chris

copy of it running on the mirror at http://llvm.x10sys.com/demo/ if you ever
need it. Running it produced the following LLVM equivalent for the C code in
your example.

implementation ; Functions:

int %add1(int %x) {
entry:
  %tmp.1 = add int %x, 1 ; <int> [#uses=1]
  ret int %tmp.1
}

int %foo() {
entry:
  ret int 11
}

Reid
Valery A.Khamenya wrote:

> Reid wrote:
>
>
>>I have to agree with Misha on this. None of us knows "everything" about
>>LLVM and as you can see, Misha responded three hours before I did :).
>>Asking questions here is encouraged, so please feel free to post them on
>>LLVMdev. We'll always help where we can.
>
>
> well, OK :slight_smile:
>
> Please find the attachment with the first approach to
> such an example i've meant. This snippet is based on
> ModuleMaker as you could see.
>
> You will find "FIXME!" at positions where I fail to find
> answer in 10-15 minutes. Code is compilable at least.
>
> So... PING!
> Who gives PONG?
> :slight_smile:
>
> --
> Valery.
>
>
> ------------------------------------------------------------------------
>
> /*
> Goal:
> The goal of this snippet is to create in the memory
> the LLVM module consisting of two functions as follow:
>
>
> int add1(int x) {
> return x+1;
> }
>
> int foo() {
> return add1(10);
> }
>
>
> then compile the module via JIT, then execute the `foo'
> function and return result to a driver, i.e. to a "host program".
>
> Some remarks and questions:
>
> - could we invoke some code using noname functions too?
> e.g. evaluate "foo()+foo()" without fears to introduce
> conflict of temporary function name with some real
> existing function name?
>
> - it would be nice to copy-paste here a LLVM code of the
> above given C-portion, ...but see the next comment :slight_smile:
>
> - http://llvm.cs.uiuc.edu/demo/index.cgi is the shortest way
> to obtain LLVM code :slight_smile: but it is down:
> "The demo page is currently unavailable.
> [tools: ( gccas llvm-dis gccld ) failed sanity check]"
> LOL
>
>
> */
>
> #include <iostream>
>
> #include <llvm/Module.h>
> #include <llvm/DerivedTypes.h>
> #include <llvm/Constants.h>
> #include <llvm/Instructions.h>
> #include <llvm/ModuleProvider.h>
>
> #include "llvm/ExecutionEngine/ExecutionEngine.h"
> #include "llvm/ExecutionEngine/GenericValue.h"
>
>
> using namespace llvm;
>
> int main() {
>
> // Create some module to put our function into it.
> Module *M = new Module("test");
>
>
> // We are about to create the add1 function:
> Function *Add1F;
>
> {
> // first create type for the single argument of add1 function:
> // the type is 'int ()'
> std::vector<const Type*> ArgT(1);
> ArgT[0] = Type::IntTy;
>
> // now create full type of the add1 function:
> FunctionType *Add1T = FunctionType::get(Type::IntTy, // type of result: int ()'
> ArgT,
> /*not vararg*/false);
>
> // Now create the add1 function entry and
> // insert this entry into module M
> // (By passing a module as the last parameter to the Function constructor,
> // it automatically gets appended to the Module.)
> Function *Add1F = new Function(Add1T,
> Function::ExternalLinkage, // maybe too much
> "add1", M);
>
> // Add a basic block to the function... (again, it automatically inserts
> // because of the last argument.)
> BasicBlock *BB = new BasicBlock("EntryBlock of add1 function", Add1F);
>
> // Get pointers to the constant `1'...
> Value *One = ConstantSInt::get(Type::IntTy, 1);
>
> // Get pointers to the integer argument of the add1 function...
> Value *ArgX = 0; // FIXME!!
>
>
> // Create the add instruction... does not insert...
> Instruction *Add = BinaryOperator::create(Instruction::Add, One, ArgX,
> "addresult");
>
> // explicitly insert it into the basic block...
> BB->getInstList().push_back(Add);
>
> // Create the return instruction and add it to the basic block
> BB->getInstList().push_back(new ReturnInst(Add));
>
> // function add1 is ready
> }
>
>
> // now we going to create function `foo':
> Function *Foo;
>
> {
> // Create the foo function type:
> FunctionType *FooT =
> FunctionType::get(Type::IntTy, // result has type: 'int ()'
> std::vector<const Type*>(), // no arguments
> /*not vararg*/false);
>
> // create the entry for function `foo' and insert
> // this entry into module M:
> Function *FooF =
> new Function(FooT,
> Function::ExternalLinkage, // too wide?
> "foo", M);
>
> // Add a basic block to the FooF function...
> BasicBlock *BB = new BasicBlock("EntryBlock of add1 function", FooF);
>
> // Get pointers to the constant `10'...
> Value *Ten = ConstantSInt::get(Type::IntTy, 10);
>
> // Put the argument Ten on stack and make call:
> // ...
> Instruction *Add1CallRes = 0; // FIXME!!!
>
> // Create the return instruction and add it to the basic block
> BB->getInstList().push_back(new ReturnInst(Add1CallRes));
>
> }
>
> // Now we going to create JIT ??
> // FIXME!
>
>
> // compile M module ??
> // FIXME!
>
> // create Execution Engine ??
> ModuleProvider *MP = 0;
> ExecutionEngine *EE = ExecutionEngine::create(MP, true);
>
> // Call the `foo' function with no arguments:
> std::vector<GenericValue> noargs;
> GenericValue gv = EE->runFunction(Foo, noargs);
>
> // import result of execution:
> // FIXME!
> }
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> LLVM Developers mailing list
> LLVMdev@cs.uiuc.edu http://llvm.cs.uiuc.edu
> http://mail.cs.uiuc.edu/mailman/listinfo/llvmdev

_______________________________________________
LLVM Developers mailing list
LLVMdev@cs.uiuc.edu http://llvm.cs.uiuc.edu
http://mail.cs.uiuc.edu/mailman/listinfo/llvmdev

-Chris

Valery,

Attached are three files: "valery.cpp" which contains your original, "reid.cpp" which contains corrections to most of the FIXME items and "diffs" which shows the differences between them. The differences should be instructive on what to do. You were really, really close .. just a few details changing. The code in "reid.cpp" compiles but I haven't tested it. I'll leave that to you.

There is only one remaining item that I don't know how to do. After ExecutionEngine::runFunction, you want to extract the result int value from the function. The runFunction method returns a GenericValue but I don't see an obvious way to get an actual value from this class.

Misha: can you help out with "GenericValue" ?

Thanks,

Reid.

Valery A.Khamenya wrote:

t.cpp (4.06 KB)

diffs (2.01 KB)

reid.cpp (4.37 KB)

Attached are three files: "valery.cpp" which contains your original, "reid.cpp"
which contains corrections to most of the FIXME items and "diffs" which shows
the differences between them. The differences should be instructive on what to
do. You were really, really close .. just a few details changing. The code in
"reid.cpp" compiles but I haven't tested it. I'll leave that to you.

Cool, thanks Reid!

There is only one remaining item that I don't know how to do. After
ExecutionEngine::runFunction, you want to extract the result int value from the
function. The runFunction method returns a GenericValue but I don't see an
obvious way to get an actual value from this class.

I'm not Misha, but if you have a GenericValue that is an int, just use
GV.IntVal to get at it. GenericValue is just a union (defined in
include/llvm/ExecutionEngine/GenericValue.h), which can represent any
first-class value that LLVM functions can return.

-Chris

> Reid wrote:
>
>
>>I have to agree with Misha on this. None of us knows "everything" about
>>LLVM and as you can see, Misha responded three hours before I did :).
>>Asking questions here is encouraged, so please feel free to post them on
>>LLVMdev. We'll always help where we can.
>
>
> well, OK :slight_smile:
>
> Please find the attachment with the first approach to
> such an example i've meant. This snippet is based on
> ModuleMaker as you could see.
>
> You will find "FIXME!" at positions where I fail to find
> answer in 10-15 minutes. Code is compilable at least.
>
> So... PING!
> Who gives PONG?
> :slight_smile:
>
> --
> Valery.
>
>
> ------------------------------------------------------------------------
>
> /*
> Goal:
> The goal of this snippet is to create in the memory
> the LLVM module consisting of two functions as follow:
>
>
> int add1(int x) {
> return x+1;
> }
>
> int foo() {
> return add1(10);
> }
>
>
> then compile the module via JIT, then execute the `foo'
> function and return result to a driver, i.e. to a "host program".
>
> Some remarks and questions:
>
> - could we invoke some code using noname functions too?
> e.g. evaluate "foo()+foo()" without fears to introduce
> conflict of temporary function name with some real
> existing function name?
>
> - it would be nice to copy-paste here a LLVM code of the
> above given C-portion, ...but see the next comment :slight_smile:
>
> - http://llvm.cs.uiuc.edu/demo/index.cgi is the shortest way
> to obtain LLVM code :slight_smile: but it is down:
> "The demo page is currently unavailable.
> [tools: ( gccas llvm-dis gccld ) failed sanity check]"
> LOL
>
>
> */
>
> #include <iostream>
>
> #include <llvm/Module.h>
> #include <llvm/DerivedTypes.h>
> #include <llvm/Constants.h>
> #include <llvm/Instructions.h>
> #include <llvm/ModuleProvider.h>
>
> #include "llvm/ExecutionEngine/ExecutionEngine.h"
> #include "llvm/ExecutionEngine/GenericValue.h"
>
>
> using namespace llvm;
>
> int main() {
>
> // Create some module to put our function into it.
> Module *M = new Module("test");
>
>
> // We are about to create the add1 function:
> Function *Add1F;
>
> {
> // first create type for the single argument of add1 function:
> // the type is 'int ()'
> std::vector<const Type*> ArgT(1);
> ArgT[0] = Type::IntTy;
>
> // now create full type of the add1 function:
> FunctionType *Add1T = FunctionType::get(Type::IntTy, // type of result: int ()'
> ArgT,
> /*not vararg*/false);
>
> // Now create the add1 function entry and
> // insert this entry into module M
> // (By passing a module as the last parameter to the Function constructor,
> // it automatically gets appended to the Module.)
> Function *Add1F = new Function(Add1T,
> Function::ExternalLinkage, // maybe too much
> "add1", M);
>
> // Add a basic block to the function... (again, it automatically inserts
> // because of the last argument.)
> BasicBlock *BB = new BasicBlock("EntryBlock of add1 function", Add1F);
>
> // Get pointers to the constant `1'...
> Value *One = ConstantSInt::get(Type::IntTy, 1);
>
> // Get pointers to the integer argument of the add1 function...
> Value *ArgX = 0; // FIXME!!
>
>
> // Create the add instruction... does not insert...
> Instruction *Add = BinaryOperator::create(Instruction::Add, One, ArgX,
> "addresult");
>
> // explicitly insert it into the basic block...
> BB->getInstList().push_back(Add);
>
> // Create the return instruction and add it to the basic block
> BB->getInstList().push_back(new ReturnInst(Add));
>
> // function add1 is ready
> }
>
>
> // now we going to create function `foo':
> Function *Foo;
>
> {
> // Create the foo function type:
> FunctionType *FooT =
> FunctionType::get(Type::IntTy, // result has type: 'int ()'
> std::vector<const Type*>(), // no arguments
> /*not vararg*/false);
>
> // create the entry for function `foo' and insert
> // this entry into module M:
> Function *FooF =
> new Function(FooT,
> Function::ExternalLinkage, // too wide?
> "foo", M);
>
> // Add a basic block to the FooF function...
> BasicBlock *BB = new BasicBlock("EntryBlock of add1 function", FooF);
>
> // Get pointers to the constant `10'...
> Value *Ten = ConstantSInt::get(Type::IntTy, 10);
>
> // Put the argument Ten on stack and make call:
> // ...
> Instruction *Add1CallRes = 0; // FIXME!!!
>
> // Create the return instruction and add it to the basic block
> BB->getInstList().push_back(new ReturnInst(Add1CallRes));
>
> }
>
> // Now we going to create JIT ??
> // FIXME!
>
>
> // compile M module ??
> // FIXME!
>
> // create Execution Engine ??
> ModuleProvider *MP = 0;
> ExecutionEngine *EE = ExecutionEngine::create(MP, true);
>
> // Call the `foo' function with no arguments:
> std::vector<GenericValue> noargs;
> GenericValue gv = EE->runFunction(Foo, noargs);
>
> // import result of execution:
> // FIXME!
> }
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> LLVM Developers mailing list
> LLVMdev@cs.uiuc.edu http://llvm.cs.uiuc.edu
> http://mail.cs.uiuc.edu/mailman/listinfo/llvmdev

-Chris

Chris: I guess if I'd have looked into it for more than 30 seconds, I might have discovered the fact that GenericValue was a union. Anyway, thanks for the help.

Valery: hopefully that should do it .. let us know if there's more help you need.

Reid.

Chris Lattner wrote:

Reid Spencer,

thank you for your quick responce, finally i got to my PC at home.

You wrote:

Attached are three files: "valery.cpp" which contains your original, "reid.cpp" which contains corrections to most of the FIXME items and "diffs" which shows the differences between them. The differences should be instructive on what to do. You were really, really close .. just a few details changing. The code in "reid.cpp" compiles but I haven't tested it. I'll leave that to you.

hm, were the linkage requisites the problem by your side too?..

this is the magic string:

g++ -L/usr/local/lib -L/usr/local/lib -o reid reid.o /usr/local/lib/lli-interpreter.o /usr/local/lib/lli-jit.o /usr/local/lib/codegen.o /usr/local/lib/executionengine.o /usr/local/lib/x86.o /usr/local/lib/selectiondag.o /usr/local/lib/scalaropts.o -lanalysis -ltransformutils /usr/local/lib/bcreader.o /usr/local/lib/vmcore.o /usr/local/lib/support.o -ltarget -export-dynamic -ldl

(don't ask me why some modules are left as .o)

Well. Compiled. Tested. Infinite loop in:

GenericValue gv = EE->runFunction(FooF, noargs);

you were really, really close, Reid, just a one mistake:

< CallInst * Add1CallRes = new CallInst(FooF, Params, "foo", BB);

> CallInst * Add1CallRes = new CallInst(Add1F, Params, "add1", BB);

:stuck_out_tongue:

There is only one remaining item that I don't know how to do. After ExecutionEngine::runFunction, you want to extract the result int value from the function. The runFunction method returns a GenericValue but I don't see an obvious way to get an actual value from this class.

Misha: can you help out with "GenericValue" ?

I got advice from Chris, thx.

AND NOW IT WORKSSSSSS!!!

HowToUseJIT.cpp is attached as finall version.

...well could anyone tell me whether JIT was really invoked in this code?
What kind of optimization were applied by default (if any)?

thanks.

oh, and great thanks to Reid of course!

HowToUseJIT.cpp (4.43 KB)

vak wrote:

Well. Compiled. Tested. Infinite loop in:

GenericValue gv = EE->runFunction(FooF, noargs);

you were really, really close, Reid, just a one mistake:

< CallInst * Add1CallRes = new CallInst(FooF, Params, "foo", BB);

> CallInst * Add1CallRes = new CallInst(Add1F, Params, "add1", BB);

Duh. Oops!

I got advice from Chris, thx.

AND NOW IT WORKSSSSSS!!!

HowToUseJIT.cpp is attached as finall version.

...well could anyone tell me whether JIT was really invoked in this code?
What kind of optimization were applied by default (if any)?

JIT should have been invoked (its a subclass of ExecutionEngine). I doubt any optimizations were applied. I'll leave that as an exercise for you to play with. Have a look at the PassManager.

thanks.

oh, and great thanks to Reid of course!

Glad to help.

Reid.

Valery,

Your JIT sample program has been added to projects/HowToUseJIT.

I have defaulted the license to the standard UIUC license. Let me know if that's not okay and I'll fix it.

If you continue to work on this (providing a command line option to use either interpreter or JIT would be nice), please provide patches against these files and I'll commit them for you.

Here's the commit list:

http://mail.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20040809/016952.html
http://mail.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20040809/016955.html
http://mail.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20040809/016956.html

Thanks again for contributing this!

Reid.

vak wrote:

Reid,

Your JIT sample program has been added to projects/HowToUseJIT.

oh, thanks, I am happy :slight_smile:

I have defaulted the license to the standard UIUC license. Let me know if
that's not okay and I'll fix it.

(you think it is time to bargain about royalties and I gonna be
as rich as Bill G.? :stuck_out_tongue: )

Reid, of course it is OK, thank you for keeping things
accurate and as appropriate. (No jokes)

If you continue to work on this (providing a command line option to use either
interpreter or JIT would be nice), please provide patches against these files
and I'll commit them for you.
[...]
Thanks again for contributing this!

I will be happy to contribute some other snippets,
thank you too, Reid.

best regards, I'll be back :slight_smile: