Index: bindings/ocaml/linker/linker_ocaml.c =================================================================== --- bindings/ocaml/linker/linker_ocaml.c +++ bindings/ocaml/linker/linker_ocaml.c @@ -31,3 +31,65 @@ return Val_unit; } + +/* llmodule -> lllinker */ +CAMLprim LLVMLinkerCtx llvm_get_linker(LLVMModuleRef Dest) { + return LLVMGetLinkerCtx(Dest); +} + +static value *internalize_trampoline_callback_value = NULL; + +void internalize_callback_trampoline(LLVMModuleRef M, const char **names, + int names_size) { + CAMLparam0(); + CAMLlocal2(names_list, cons); + names_list = Val_emptylist; + for (int i = 0; i < names_size; i++) { + cons = caml_alloc(2, 0); + Store_field(cons, 0, caml_copy_string(names[i])); + Store_field(cons, 1, names_list); + names_list = cons; + } + caml_callback2(*internalize_trampoline_callback_value, (value)M, names_list); + CAMLreturn0; +} + +/* LinkFlags option -> (llmodule -> string list -> unit) option -> lllinker -> + * llmodule -> unit */ +CAMLprim value llvm_link_in(value Flags, value Callback, LLVMLinkerCtx Dest, + LLVMModuleRef Src) { + CAMLparam2(Flags, Callback); + + unsigned flags; + if (Flags == Val_int(0)) { + flags = None; + } else { + flags = Int_val(Field(Flags, 0)); + } + + void (*callback_ptr)(LLVMModuleRef, const char **, int); + if (Callback == Val_int(0)) { + callback_ptr = NULL; + } else { + /*Since this call back is only called while linking happens, + its lifetime doesn't need to extend beyong this function. + Therefore we don't need any GC trickery */ + internalize_trampoline_callback_value = malloc(sizeof(value)); + *internalize_trampoline_callback_value = Field(Callback, 0); + caml_register_global_root(internalize_trampoline_callback_value); + + callback_ptr = &internalize_callback_trampoline; + } + + if (LLVMLinkInModule(Dest, Src, flags, callback_ptr)) + llvm_raise(*caml_named_value("Llvm_linker.Error"), + LLVMCreateMessage("Linking failed")); + + CAMLreturn(Val_unit); +} + +/* lllinker -> unit */ +CAMLprim value llvm_linker_dispose(LLVMLinkerCtx L) { + LLVMDisposeLinkerCtx(L); + return Val_unit; +} Index: bindings/ocaml/linker/llvm_linker.ml =================================================================== --- bindings/ocaml/linker/llvm_linker.ml +++ bindings/ocaml/linker/llvm_linker.ml @@ -8,7 +8,28 @@ exception Error of string +module LinkerFlags = struct + type t = + | None + | OverrideFromSrc + | LinkOnlyNeeded + | OverrideAndLinkOnlyNeeded +end + let () = Callback.register_exception "Llvm_linker.Error" (Error "") external link_modules' : Llvm.llmodule -> Llvm.llmodule -> unit = "llvm_link_modules" + +external get_linker : Llvm.llmodule -> Llvm.lllinker = "llvm_get_linker" + +external linker_dispose : Llvm.lllinker -> unit = "llvm_linker_dispose" + + +external link_in : + ?flags:LinkerFlags.t + -> ?internalizeCallback:(Llvm.llmodule -> string list -> unit) + -> Llvm.lllinker + -> Llvm.llmodule + -> unit + = "llvm_link_in" Index: bindings/ocaml/linker/llvm_linker.mli =================================================================== --- bindings/ocaml/linker/llvm_linker.mli +++ bindings/ocaml/linker/llvm_linker.mli @@ -13,6 +13,32 @@ exception Error of string +module LinkerFlags : sig +type t = + None + | OverrideFromSrc + | LinkOnlyNeeded + | OverrideAndLinkOnlyNeeded +end + (** [link_modules' dst src] links [src] into [dst], raising [Error] if the linking fails. The src module is destroyed. *) -val link_modules' : Llvm.llmodule -> Llvm.llmodule -> unit \ No newline at end of file +val link_modules' : Llvm.llmodule -> Llvm.llmodule -> unit + +(** [get_linker dst] creates a linker context used to link into module [dst]. + See the [llvm::Linker] class. *) +val get_linker : Llvm.llmodule -> Llvm.lllinker + +(** [link_in dst src] links [src] into [dst], raising [Error] if the linking + fails. The [src] module is destroyed. [flags] argument changes the behaviour of + the linker. [internalizeCallback] gets passed the new module and a list of names + of symbols to internalize. See the [llvm::Linker::linkInModule] method. *) +val link_in : + ?flags:LinkerFlags.t + -> ?internalizeCallback:(Llvm.llmodule -> string list -> unit) + -> Llvm.lllinker + -> Llvm.llmodule + -> unit + +(** [linker_dispose linker] frees up linker from [get_linker]. *) +val linker_dispose : Llvm.lllinker -> unit Index: bindings/ocaml/llvm/llvm.ml =================================================================== --- bindings/ocaml/llvm/llvm.ml +++ bindings/ocaml/llvm/llvm.ml @@ -9,6 +9,7 @@ type llcontext type llmodule +type lllinker type lltype type llvalue type lluse Index: bindings/ocaml/llvm/llvm.mli =================================================================== --- bindings/ocaml/llvm/llvm.mli +++ bindings/ocaml/llvm/llvm.mli @@ -24,6 +24,10 @@ objects. See the [llvm::Module] class. *) type llmodule +(** A linker used to link in other LLVM IR modules. See the [llvm::Linker] + class. *) +type lllinker + (** Each value in the LLVM IR has a type, an instance of [lltype]. See the [llvm::Type] class. *) type lltype Index: include/llvm-c/Linker.h =================================================================== --- include/llvm-c/Linker.h +++ include/llvm-c/Linker.h @@ -27,6 +27,13 @@ should not be used. */ } LLVMLinkerMode; +typedef enum { + None = 0, + OverrideFromSrc = 1, + LinkOnlyNeeded = 2 + +} LinkerFlags; + /* Links the source module into the destination module. The source module is * destroyed. * The return value is true if an error occurred, false otherwise. @@ -34,6 +41,28 @@ */ LLVMBool LLVMLinkModules2(LLVMModuleRef Dest, LLVMModuleRef Src); +/** + * Construct a linker for a module. + * @see llvm::Linker(Module&) + */ +LLVMLinkerCtx LLVMGetLinkerCtx(LLVMModuleRef Dest); + +/** + * Link Src into the composite Dest. The source module is destroyed. + * Flags argument can be a combination of LinkerFlags enum, see llvm::Linker:Flags. + * InternalizeCallback gets called with the new module and an array of strings containing + * the names to be internalized. The array size is passed as a third parameter. + * @see llvm::Linker::linkInModule + */ +LLVMBool LLVMLinkInModule(LLVMLinkerCtx Dest, LLVMModuleRef Src, unsigned Flags, + void (*InternalizeCallback)(LLVMModuleRef, + const char **, int)); + +/** + * Destroy a linker. + */ +void LLVMDisposeLinkerCtx(LLVMLinkerCtx L); + #ifdef __cplusplus } #endif Index: include/llvm-c/Types.h =================================================================== --- include/llvm-c/Types.h +++ include/llvm-c/Types.h @@ -48,6 +48,13 @@ */ typedef struct LLVMOpaqueMemoryBuffer *LLVMMemoryBufferRef; +/** + * Used to pass linkers through LLVM interfaces. + * + * @see llvm::Linker + */ +typedef struct LLVMOpaqueLinkerCtx *LLVMLinkerCtx; + /** * The top-level container for all LLVM global data. See the LLVMContext class. */ Index: lib/Linker/LinkModules.cpp =================================================================== --- lib/Linker/LinkModules.cpp +++ lib/Linker/LinkModules.cpp @@ -603,3 +603,36 @@ std::unique_ptr M(unwrap(Src)); return Linker::linkModules(*D, std::move(M)); } + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Linker, LLVMLinkerCtx) + +LLVMLinkerCtx LLVMGetLinkerCtx(LLVMModuleRef Dest) { + Module *D = unwrap(Dest); + return wrap(new Linker(*D)); +} + +LLVMBool LLVMLinkInModule(LLVMLinkerCtx Dest, LLVMModuleRef Src, unsigned Flags, + void (*InternalizeCallback)(LLVMModuleRef, + const char **, int)) { + Linker *L = unwrap(Dest); + std::unique_ptr M(unwrap(Src)); + if (InternalizeCallback == nullptr) + return L->linkInModule(std::move(M), Flags); + else { + return L->linkInModule( + std::move(M), Flags, [=](Module &M, const StringSet<> &names) { + unsigned size = names.size(); + const char **c_names = + (const char **)malloc(size * sizeof(const char *)); + int i = 0; + for (StringRef s : names.keys()) { + c_names[i] = s.data(); + i++; + } + InternalizeCallback(wrap(&M), c_names, size); + free(c_names); + }); + } +} + +void LLVMDisposeLinkerCtx(LLVMLinkerCtx L) { delete unwrap(L); }