Batata
Batata

Reputation: 133

Link C in llvmlite

Im writing an compiler in Python, using llvmlite to generate intermediate LLVM IR. Lexer and parser are finished, now im doing code generation. The compiler will be dynamic and weakly typed, so i will need to do somethings at runtime, like allocation. For this, i've already implemented some functions in C, and now i want to call these functions using builder.call from llvmlite.

I have not found documentation or examples of how to do this.

This function its just an simple example, the real ones are much larger.

C:

int some_function(int a)
{
     return a + 4;
}

Python:

...

    main_ty = ir.FunctionType(ir.IntType(32), [])
    func = ir.Function(module, main_ty, 'main')
    block = func.append_basic_block('entry')
    builder = ir.IRBuilder(block)

    # I want to do something like this...

    ret = builder.call(some_function, [ir.Constant(ir.IntType(32), 34)]);

...

I could write the functions directly using llvmlite builders, but will be much quick, cleaner and easy do it in C. Any help are welcome!

Upvotes: 5

Views: 2036

Answers (4)

Eugenos_Programos
Eugenos_Programos

Reputation: 15

You can link your C file with LLVM LITE library. I can't find any reference on how llvmlite.binding.load_library_permanently("runtime.so") command works. And how can I call functions from this shared file.

But there is alternative way - you can tranform your C file into LLVM binary format with clang -c -emit-llvm src/main.c -o main.bc command. And then run clang [*.bc] -o executable.

Upvotes: 0

WireBlock
WireBlock

Reputation: 39

@Coder3000 and @Ismail Badawi answer is perfect,but there is no code.There is my code

    #!/usr/bin/env python
    # coding=utf-8
    from __future__ import print_function
    from ctypes import CFUNCTYPE, c_double,cdll,c_int
    import llvmlite.binding as llvm
    import llvmlite.ir as  ir

    llvm.initialize()
    llvm.initialize_native_target()
    llvm.initialize_native_asmprinter()
    #load shared library
    llvm.load_library_permanently('./TestLib.so')
    # Create some useful types
    double = ir.DoubleType()
    fnty = ir.FunctionType(double, (double, double))

    # Create an empty module...
    module = ir.Module("fdadd")
    # and declare a function named "fpadd" inside it
    func = ir.Function(module, fnty, name="fpadd")

    # Now implement the function
    block = func.append_basic_block(name="entry")
    builder = ir.IRBuilder(block)
    a, b = func.args
    result = builder.fadd(a, b, name="res")
    builder.ret(result)
    #function call in llvm ir internal
    func2=ir.Function(module,fnty,name="fdadd")
    block2=func2.append_basic_block(name="entry")
    builder=ir.IRBuilder(block2)
    a,b=func2.args
    result2=builder.call(func,(a,b))
    builder.ret(result2)
    # llvm IR call external C++ function
    outfunc=ir.Function(module,fnty,name="SampleAddDouble")
    #just declare shared library function in module
    outaddfunc=ir.Function(module,fnty,name="outadd")
    builder=ir.IRBuilder(outaddfunc.append_basic_block(name="entry"))
    a,b=outaddfunc.args
    outresult=builder.call(outfunc,(a,b))
    builder.ret(outresult)
    strmod=str(module)
    # Print the module IR
    print(strmod)
    print("-----------------------------------------")
    #assembly llvm ir
    assmod = llvm.parse_assembly(strmod)
    assmod.verify()
    print("--parse assembly")
    target = llvm.Target.from_default_triple()
    target_machine = target.create_target_machine()
    engine = llvm.create_mcjit_compiler(assmod, target_machine)
    engine.finalize_object()
    print(engine)

    # Look up the function pointer (a Python int) 
    #llvm execution engine call llvm IR function(or dsl function)
    func_ptr = engine.get_function_address("fpadd")
    print('func_ptr is:',func_ptr)

    # Run the function via ctypes
    cfunc = CFUNCTYPE(c_double, c_double, c_double)(func_ptr)
    res = cfunc(1.0, 3.5)
    print("fpadd(...) =", res)

    #llvm binding layer call shared library function
    add_int_addr = llvm.address_of_symbol("SampleAddInt")
    print(add_int_addr)
    add_int_func=CFUNCTYPE(c_int,c_int,c_int)(add_int_addr)
    res2=add_int_func(23,34)
    print(res2)

    #llvm execution engine call shared library function ,by llvm IR 
    add_double_addr=engine.get_function_address("outadd")
    print(add_double_addr)
    add_double_func=CFUNCTYPE(c_double,c_double,c_double)(add_double_addr)
    res3=add_double_func(1.21,1.12)
    print(res3)

And the Testlib.cpp $reference https://helloacm.com/calling-c-shared-library-from-python-code-linux-version/

About this problem here is other ref

-Call C/C++ functions from the ExecutionEngine -Call Python code from LLVM JIT -http://eli.thegreenplace.net/2015/calling-back-into-python-from-llvmlite-jited-code/

Upvotes: 3

Ismail Badawi
Ismail Badawi

Reputation: 37227

On the LLVM IR side you can just declare the functions with the right signature (and no body), and insert calls to them like any other function. This is just like how in C you might call a function which is defined in another file.

From there, you would have to somehow link against your C functions. The details here depend on how you intend to use your generated IR code. For example, you could use clang to turn it into object files, and then link it like any other program. Or you could use the llvm JIT, in which case @Coder3000's answer (llvmlite.binding.load_library_permanently) should work to let LLVM resolve your function calls.

Upvotes: 4

Coder3000
Coder3000

Reputation: 130

You could import a dynamic library containing the runtime.

llvmlite.binding.load_library_permanently("runtime.so")

Then you could simply generate normal function calls.

Upvotes: 5

Related Questions