7
votes

I'm working on LLVM tutorial in Rust now. I have implemented some parts of Kaleidoscope REPL already. It worked for me well, but suddenly it stopped to work and every attempt to calculate a value ends in LLVM ERROR: Target does not support MC emission! now. It seems it happend after updating of Rust compiler to the latest nightly (but I am not sure here).

Relevant pieces of code follow.

Initialization function:

#[allow(non_snake_case)]
pub unsafe fn LLVMInitializeNativeTarget() {
    llvm::LLVMInitializeX86TargetInfo();
    llvm::LLVMInitializeX86Target();
    llvm::LLVMInitializeX86TargetMC();
}

Module creation:

        let module = llvm::LLVMModuleCreateWithNameInContext(module_name.to_c_str().as_ptr(), context);

Execution engine creation:

        let mut exec_engine = 0 as llvm::ExecutionEngineRef;
        let mut error = 0 as *const c_char;
        LLVMCreateExecutionEngineForModule(&mut exec_engine, module, &mut error);
        assert!(exec_engine != 0 as llvm::ExecutionEngineRef);

Compilation and running of function:

pub fn run(value: llvm::ValueRef, context: &Context) -> f64 {
    unsafe {
        let result = LLVMRunFunction(context.exec_engine,
                                     value,
                                     0,
                                     0 as *const GenericValueRef);
        let ty = llvm::LLVMDoubleTypeInContext(context.context);
        LLVMGenericValueToFloat(ty, result)
    }
}

LLVM functions prefexed by llvm:: are imported by rustc, those not prefexed by llvm:: are imported by my code, see https://github.com/jauhien/iron-kaleidoscope/blob/master/src/missing_llvm_bindings/mod.rs.

To see full code listing, look at https://github.com/jauhien/iron-kaleidoscope/blob/master/src/builder.rs

I'm using the latest nightly Rust and LLVM 3.5.0.

UPD: After commenting out of the call to LLVMInitializeNativeTarget, JIT started to work again. But I am still wondering what is the reason of the problem and how JIT should be used correctly.

UPD2: after commenting out line with initialization not everything started to work again: calls to the function defined in the Rust code fail with LLVM ERROR: Tried to execute an unknown external function now.

Function I'm trying to call (this worked before):

#[no_mangle]
pub extern fn print(x: f64) -> f64 {
    println!("> {} <", x);
    x
}

Example session:

jauhien@zcj iron-repl % ./target/iron_kaleidoscope
>extern print(x)

declare double @print(double)

>print(1)
LLVM ERROR: Tried to execute an unknown external function: print
1
Not sure whether it's the cause of your problem, but module_name.to_c_str().as_ptr() is memory unsafe. You are not ensuring that the C string outlives the pointer. See the docs. I recommend using with_c_str instead. - Dylan
Also, I recommend avoiding the existing LLVM API in Rust. It's based on the LLVM C API, which is not as well supported as the C++ API, and it's completely unsafe. You will also find that some parts of the API are missing. For a project I am working on I am building my own safe bindings for C++ classes/functions as I go. It's not that hard, and the end result is much easier to use. - Dylan
I known, but that is a tutorial. So I use what is already available. If there would exist some safe API, I would use it. What about with_c_str I see no reason to use it here. Or can string be deallocated before the function where it is passed as parameter is called? Anyway, it has nothing to do with my problem. - Jaŭhien Piatlicki
It can be deallocated at the wrong time, because you are not passing it, or anything that has an explicit reference to it to the function. as_ptr() gives you a *const u8, which as far as the Rust type system and borrow checker is concerned is a value type with no references to anything with a lifetime. So by the point that the LLVM function is called the C string you created is not referred to by anything, so it is unsafe to use a pointer into it as it may be dead. - Dylan
Thanks for explanation. - Jaŭhien Piatlicki

1 Answers

11
votes

I had the same problem and could solve it by also running

LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeAsmParser();

after initializing X86Target. I found this in the source code of lli.