Hi all,
I’m authoring a C interface to the LLVM IR type system. Since this is Really Quite Tedious, I would like to solicit opinions before I get too far down any paths that seem offensive. I’ve attached the header, where I’ve mapped a portion of Module and most of Type and its subclasses. This is working, and I’ve built ocaml bindings on top of it.[1] My intent is to extend this work (only) far enough to author a language front-end. The C bindings should help other languages which want to have self-hosting front-ends, and probably a C interface to the JIT would be well-received.
My naming conventions are similar to the Carbon interfaces in OS X. (Should I prefer a Unixy flavor instead?) Naming prefix is LLVM, which may be a bit long. (Would LL be better?) Pointers are opaque, obviously. I find myself copying enums, which is mildly scary.
I’m using C strings instead of const char*, size_t tuples. This avoids having to write things like “tmp”, strlen(“tmp”) in C, and is well-supported for language bindings. Nevertheless, most languages other than C have binary-safe string types, so I’m certainly willing to have my mind changed if we want to prefer correctness over inconvenience to the C programmer. (Providing overloads is silly, though.)
I’m putting the headers in include/llvm-c. I created a new library called Interop to house the C bindings—but it might make more sense to implement the C bindings in each library instead. They’re just glue which the linker will trivially DCE, so that approach may have merit.
— Gordon
[1]
$ cat emit_bc.ml
open Llvm
let emit_bc filename =
let m = create_module filename in
let big_fn_ty = make_pointer_type
(make_function_type (void_type ())
[| make_vector_type (float_type ()) 4;
make_pointer_type
(make_struct_type [| double_type ();
x86fp80_type ();
fp128_type ();
ppc_fp128_type () |] true);
make_pointer_type
(make_struct_type [| make_integer_type 1;
make_integer_type 3;
i8_type ();
i32_type () |] false);
make_pointer_type
(make_array_type (make_opaque_type ()) 4) |]
false) in
(* string_of_lltype is implemented in ocaml, so the info on stdout
shows that make_*_type isn’t a write-once/read-never interface. *)
print_endline ("big_fn_ty = " ^ (string_of_lltype big_fn_ty));
ignore(add_type_name m “big_fn_ty” big_fn_ty);
if not (write_bitcode_file m filename)
then print_endline ("write failed: " ^ filename);
dispose_module m
let _ =
if 2 = *Array.*length *Sys.*argv
then emit_bc *Sys.*argv.(1)
else print_endline “Usage: emit_bc FILE”
$ make emit_bc
ocamlc -cc g++ -I …/llvm/Release/lib/ocaml llvm_ml.cma -o emit_bc emit_bc.ml
$ ./emit_bc test.bc
big_fn_ty = void (< 4 x float >, { double, x86fp80, fp128, ppc_fp128 }, { i1, i3, i8, i32 }, [ 4 x opaque ])
$ llvm-dis -o - test.bc
; ModuleID = ‘test.bc’
%big_fn_ty = type void (<4 x float>, <{ double, x86_fp80, fp128, ppc_fp128 }>, { i1, i3, i8, i32 }, [4 x opaque])
VMCore.h (5.25 KB)