Open carlokok opened 4 years ago
https://github.com/eqrion/cbindgen may be useful for this.
@bjorn3 once there IS a public api then yes. cbindgen requires the Rust side to already define a nomangle interface.
If it's of any interest to anyone, I wrote a C api that allows this.
api: https://gist.github.com/carlokok/d0d079dba76ee253a1571d58f2e791fb
#include <cranelift.h>
#include <stdio.h>
void cb1(uintptr_t userdata, const char* err, const char* fn) {
printf("Error %s: %s\n", fn, err);
}
void cb2(uintptr_t userdata, const char* err, const char* fn) {
printf("Message %s: %s\n", fn, err);
}
void cb(uintptr_t userdata, FunctionData* fd)
{
BlockCode entry = cranelift_create_block(fd);
BlockCode larger = cranelift_create_block(fd);
BlockCode exit = cranelift_create_block(fd);
cranelift_append_block_params_for_function_params(fd, entry);
cranelift_switch_to_block(fd, entry);
ValueCode res[2];
cranelift_block_params(fd, entry, res);
VariableCode a = cranelift_declare_var(fd, TypeI32);
cranelift_def_var(fd, a, res[0]);
VariableCode b = cranelift_declare_var(fd, TypeI32);
cranelift_def_var(fd, b, res[1]);
VariableCode retvar = cranelift_declare_var(fd, TypeI32);
cranelift_def_var(fd, retvar, cranelift_iconst(fd, TypeI32, 0));
cranelift_ins_br_icmp(fd, CraneliftIntCCSignedLessThan, a, b, larger, 0, NULL);
cranelift_def_var(fd, retvar, cranelift_use_var(fd, a));
cranelift_ins_jump(fd, exit, 0, NULL);
cranelift_switch_to_block(fd, larger);
cranelift_def_var(fd, retvar, cranelift_use_var(fd, b));
cranelift_ins_jump(fd, exit, 0, NULL);
cranelift_switch_to_block(fd, exit);
ValueCode retval = cranelift_use_var(fd, retvar);
cranelift_return(fd, 1, &retval);
cranelift_seal_all_blocks(fd);
}
void cbp(uintptr_t ud, char* data)
{
printf("\n%s\n", data);
}
int main()
{
ModuleData* mod = cranelift_module_new("x86_64-pc-windows", "is_pic,enable_simd,enable_atomics,enable_verifier", "mymodule", 0, cb1, cb2);
cranelift_signature_builder_add_param(mod, TypeI32);
cranelift_signature_builder_add_param(mod, TypeI32);
cranelift_signature_builder_add_result(mod, TypeI32);
cranelift_build_function(mod, 0, cb);
cranelift_function_to_string(mod, 0, cbp);
uint32_t id;
cranelift_declare_function(mod, "max", Export, &id);
cranelift_define_function(mod, id);
uint32_t id2;
uint8_t c[4] = { 1,2,3,4 };
cranelift_set_data_value(mod, c, 4);
cranelift_define_data(mod, "testdata_w", Export, Writable, 0, &id2);
cranelift_assign_data_to_global(mod, id2);
cranelift_clear_data(mod);
cranelift_set_data_value(mod, c, 4);
cranelift_define_data(mod, "testdata_r", Export, None, 0, &id2);
cranelift_assign_data_to_global(mod, id2);
cranelift_clear_data(mod);
cranelift_set_data_value(mod, c, 4);
cranelift_define_data(mod, "testdata_w2", Export, Writable, 0, &id2);
cranelift_set_data_section(mod, "SEG", "SEC");
cranelift_assign_data_to_global(mod, id2);
// cranelift_clear_data(mod);
cranelift_module_emit_object(mod, "c:\\projects\\test.o");
cranelift_module_delete(mod);
}
I think cranelift_block_params
should take the expected param count as argument to prevent accidental out-of-bounds memory accesses.
It would also be nice if cranelift-codegen-meta
automatically generated all cranelift_ins_*
functions.
agreed :) but if there's no interest or if the interfaces don't want to be tied down then I won't bother. Also this was my very first foray into Rust.
Is this likely to happen? I'd really like to try out cranelift for my toy language, but the lack of a simple c api will mean that I will have to stick with llvm instead.
I personally think it would be nice to have this, but it's not a small task to build, and no one currently has the time to do it. If you (or anyone) would like to contribute this, we'd be happy to talk further -- the first step would probably be gathering feedback in an RFC.
This is way out of my league so likely a stupid question; but if I build a so/dll of all the cranelift crates like cranelift = "0.89.0"
using cargo, can I use this .so file in other languages which support FFI?
No, rust's ABI is not stable, so you can't call any of the methods in this dylib. You have to write a C interface for every function you want to call from non-rust code and then use this C interface.
This is way out of my league so likely a stupid question; but if I build a so/dll of all the cranelift crates like cranelift = "0.89.0" using cargo, can I use this .so file in other languages which support FFI?
To add a little more nuance: (i) Rust does have the ability to produce .so
s that can be called with a C ABI; but (ii) that requires explicit work, in defining the C-ABI-compatible exports (#[no_mangle]
and extern "C" fn
are the relevant search terms, as well as care about data structure layout and use of #[repr(C)]
on structs where needed).
Is there a place for a C API in-tree, or is that solidly out of scope for the time being?
@coffeebe4code did some reworking of @carlokok's gist, https://github.com/coffeebe4code/craneliftc. If the methodology looks correct enough, that could likely be a reasonable starting point.
What I'd like to see is a way to use Cranelift apis from another (non rust) language. Currently the cranelift apis are rust only, but having a layer on top of that like "llvm-c" is, would make the cranelift api usable from all languages, and thus a much wider range of compilers could use it as a backend.