Adriaan Callaerts
2013-Apr-24 11:23 UTC
[LLVMdev] JIT pass runtime struct on to subroutines
Hi
For a research project at my university, I'm working on incorporating JIT in
Prolog, which is basically an interpreted virtual machine.
The VM uses logical units of functionality called 'predicates' which are
composed of bytecode that represents the machine instructions the VM supports.
At execution time, the VM infinitately loops over these bytecodes and, using a
giant switch statement, executes the functionality for each instruction.
So far, this is all basic VM stuff. I wanted to speed this up by using
JIT-compilation on some often-used predicates.
The way I went about this was to first change the code so it uses subroutines.
This means I seperated the code for each machine instruction and turned it into
a function.
These functions take a pointer to a struct representing the active machine as a
parameter and any number of additional parameters that represent the arguments
of that particular machine instruction.
The runtime execution then goes as follows: infinitely get the bytecode and
related arguments, then execute the appropriate function with the current
machine and the fetched arguments as parameters.
The next step I had in mind would be to construct LLVM-functions at runtime when
I wanted to JIT, and then add these (instruction-)function calls to the LLVM
function.
For this to be possible, I turned all the machine-instruction functions into a
Module by compiling to LLVM IR code.
At runtime I'd create a new llvm::Function for the predicate I wanted to
JIT-compile and then loop over every instruction associated with that predicate,
adding their respective calls to the predicate-function.
However I'm having some problems to figure out how to do certain things...
I give some code to show what I've got already, some parts are pseudo-code
but these are not the functionality I'm having problems with
---------------------------------------------------------------------------------------
// I want to generate an LLVM function for a given predicate
// at this point there is no struct *machine available
// create the function for the predicate. This should take one parameter: the
machine-struct that will be provided at runtime when the function is called
llvm::Function *llvm_function = llvm::cast<llvm::Function>(
llvm_module->getOrInsertFunction(*name,
llvm::Type::getVoidTy(&llvm_context),
llvm::PointerType::getUnqual(llvm::StructType::create(&llvm_context)),
(llvm::Type *)0));
// prepare for the construction of this function
llvm::BasicBlock *llvm_basic_block = BasicBlock::Create(&llvm_context,
"EntryBlock", llvm_function);
llvm::IRBuilder<> llvm_builder(llvm_basic_block);
// pseudo: I'll loop over all the instructions in a predicate. This sets
instruction_function_name to the appropriate value in each iteration and makes
sure I can iterate over the instruction's parameters
foreach (instruction in predicate) {
// Fetch the instruction from the already initialised execution engine. I
can do this because I compiled all instruction-functions to an LLVM Module and
loaded it.
llvm::Function *machine_instr =
llvm_execution_engine->FindFunctionNamed(instruction_function_name);
// Prepare for setting the parameters of a call instruction
llvm::Argument *llvm_arg = machine_instr->arg_begin();
// Add a pointer to the machine-struct as a first parameter to every
instruction-function call.
// This should be the same pointer that is passed at runtime to the
predicate-function (llvm_function)
llvm_arg++ = ??;
// pseudo: for any additional parameters of the machine instruction
foreach (param in instruction_parameters) {
// add any additional parameters of the machine instruction to the
function call
llvm_arg++ = param;
}
// add the call to the function we're creating
llvm_builder.CreateCall(machine_instr, llvm_arg);
}
// finish the function by adding return
llvm_builder.CreateRetVoid();
// and finish with generating the function and saving it for later use
llvm_function_pass_manager->run(*llvm_function);
jit_functions[predicate_id] =
(void*)(llvm_execution_engine->getPointerToFunction(llvm_function));
---------------------------------------------------------------------------------------
Could you direct me in the right direction as to how I might do some of the
stuff I'm trying to do here? Or correct me if I'm making mistakes or
incorrect assumptions?
Kind regards,
Adriaan Callaerts
Hi Adriaan, if I understand correctly what you want to do, then you might use llvm::Argument in a wrong way. In order to build a CallInst you should not refer in any way to the arguments (in terms of llvm::Argument) of the Callee (in your case the machine_instr). The arguments of a function that you get via arg_begin() and arg_end() are used from WITHIN the function. So in your case that would mean that you would replace parts of your code as follows: On 24.04.13 13:23, Adriaan Callaerts wrote:> // create the function for the predicate. This should take one parameter: the machine-struct that will be provided at runtime when the function is called > llvm::Function *llvm_function = llvm::cast<llvm::Function>( > llvm_module->getOrInsertFunction(*name, llvm::Type::getVoidTy(&llvm_context), llvm::PointerType::getUnqual(llvm::StructType::create(&llvm_context)), (llvm::Type *)0)); > // prepare for the construction of this function > llvm::BasicBlock *llvm_basic_block = BasicBlock::Create(&llvm_context, "EntryBlock", llvm_function); > llvm::IRBuilder<> llvm_builder(llvm_basic_block); > > // pseudo: I'll loop over all the instructions in a predicate. This sets instruction_function_name to the appropriate value in each iteration and makes sure I can iterate over the instruction's parameters > foreach (instruction in predicate) { > // Fetch the instruction from the already initialised execution engine. I can do this because I compiled all instruction-functions to an LLVM Module and loaded it. > llvm::Function *machine_instr = llvm_execution_engine->FindFunctionNamed(instruction_function_name); > // Prepare for setting the parameters of a call instruction > llvm::Argument *llvm_arg = machine_instr->arg_begin();llvm::SmallVector<llvm::Value*, 4> llvm_arg;> // Add a pointer to the machine-struct as a first parameter to every instruction-function call. > // This should be the same pointer that is passed at runtime to the predicate-function (llvm_function) > llvm_arg++ = ??;llvm_arg.push_back(llvm_function->arg_begin());> > // pseudo: for any additional parameters of the machine instruction > foreach (param in instruction_parameters) { > // add any additional parameters of the machine instruction to the function call > llvm_arg++ = param;llvm_arg.push_back(param); // Whatever param here is...> } > > // add the call to the function we're creating > llvm_builder.CreateCall(machine_instr, llvm_arg); > } > > // finish the function by adding return > llvm_builder.CreateRetVoid(); > // and finish with generating the function and saving it for later use > llvm_function_pass_manager->run(*llvm_function); > jit_functions[predicate_id] = (void*)(llvm_execution_engine->getPointerToFunction(llvm_function));