
The OCaml bindings currently return pointers to LLVM objects as-is to OCaml. These "naked pointers" end up appearing as values of local variables in OCaml code, stored as part of other OCaml values, etc. The safety of this design relies on the OCaml runtime system's ability to distinguish these pointers from pointers to memory on the OCaml garbage collected heap. In particular, when the OCaml GC encounters a pointer to memory known to not be part of the OCaml heap, it does not follow it. In OCaml 4.02 an optimized "no naked pointers" mode was introduced where the runtime system does not perform such checks and requires that no such naked pointers be passed to OCaml code, instead one of several encodings needs to be used. In OCaml 5, the no naked pointers mode is now the only mode. This diff uses one of the potential encodings to eliminate naked pointers, making the LLVM OCaml bindings compatible with the "no naked pointers" mode of OCaml >= 4.02 and 5. The encoding implemented in this diff relies on LLVM objects to be at least 2-byte aligned, meaning that the lsb of pointers will necessarily be clear. The encoding sets the lsb when passing LLVM pointers to OCaml, and clears it on the return path. Setting the lsb causes the OCaml runtime system to interpret the resulting value as a tagged integer, which does not participate in garbage collection. In some cases, particularly functions that receive an OCaml array of LLVM pointers, this encoding requires allocation of a temporary array, but otherwise this diff aims to preserve the existing performance characteristics of the OCaml bindings. Reviewed By: jberdine Differential Revision: https://reviews.llvm.org/D136400
131 lines
4.8 KiB
C
131 lines
4.8 KiB
C
/*===-- executionengine_ocaml.c - LLVM OCaml Glue ---------------*- C++ -*-===*\
|
|
|* *|
|
|
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
|
|
|* Exceptions. *|
|
|
|* See https://llvm.org/LICENSE.txt for license information. *|
|
|
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
|
|
|* *|
|
|
|*===----------------------------------------------------------------------===*|
|
|
|* *|
|
|
|* This file glues LLVM's OCaml interface to its C interface. These functions *|
|
|
|* are by and large transparent wrappers to the corresponding C functions. *|
|
|
|* *|
|
|
|* Note that these functions intentionally take liberties with the CAMLparamX *|
|
|
|* macros, since most of the parameters are not GC heap objects. *|
|
|
|* *|
|
|
\*===----------------------------------------------------------------------===*/
|
|
|
|
#include "caml/alloc.h"
|
|
#include "caml/callback.h"
|
|
#include "caml/custom.h"
|
|
#include "caml/fail.h"
|
|
#include "caml/memory.h"
|
|
#include "llvm_ocaml.h"
|
|
#include "llvm-c/Core.h"
|
|
#include "llvm-c/ExecutionEngine.h"
|
|
#include "llvm-c/Target.h"
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#define ExecutionEngine_val(v) ((LLVMExecutionEngineRef)from_val(v))
|
|
|
|
void llvm_raise(value Prototype, char *Message);
|
|
|
|
/* unit -> bool */
|
|
value llvm_ee_initialize(value Unit) {
|
|
LLVMLinkInMCJIT();
|
|
|
|
return Val_bool(!LLVMInitializeNativeTarget() &&
|
|
!LLVMInitializeNativeAsmParser() &&
|
|
!LLVMInitializeNativeAsmPrinter());
|
|
}
|
|
|
|
/* llcompileroption -> llmodule -> ExecutionEngine.t */
|
|
value llvm_ee_create(value OptRecordOpt, value M) {
|
|
LLVMExecutionEngineRef MCJIT;
|
|
char *Error;
|
|
struct LLVMMCJITCompilerOptions Options;
|
|
|
|
LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options));
|
|
if (OptRecordOpt != Val_int(0)) {
|
|
value OptRecord = Field(OptRecordOpt, 0);
|
|
Options.OptLevel = Int_val(Field(OptRecord, 0));
|
|
Options.CodeModel = Int_val(Field(OptRecord, 1));
|
|
Options.NoFramePointerElim = Int_val(Field(OptRecord, 2));
|
|
Options.EnableFastISel = Int_val(Field(OptRecord, 3));
|
|
Options.MCJMM = NULL;
|
|
}
|
|
|
|
if (LLVMCreateMCJITCompilerForModule(&MCJIT, Module_val(M), &Options,
|
|
sizeof(Options), &Error))
|
|
llvm_raise(*caml_named_value("Llvm_executionengine.Error"), Error);
|
|
return to_val(MCJIT);
|
|
}
|
|
|
|
/* ExecutionEngine.t -> unit */
|
|
value llvm_ee_dispose(value EE) {
|
|
LLVMDisposeExecutionEngine(ExecutionEngine_val(EE));
|
|
return Val_unit;
|
|
}
|
|
|
|
/* llmodule -> ExecutionEngine.t -> unit */
|
|
value llvm_ee_add_module(value M, value EE) {
|
|
LLVMAddModule(ExecutionEngine_val(EE), Module_val(M));
|
|
return Val_unit;
|
|
}
|
|
|
|
/* llmodule -> ExecutionEngine.t -> llmodule */
|
|
value llvm_ee_remove_module(value M, value EE) {
|
|
LLVMModuleRef RemovedModule;
|
|
char *Error;
|
|
if (LLVMRemoveModule(ExecutionEngine_val(EE), Module_val(M), &RemovedModule,
|
|
&Error))
|
|
llvm_raise(*caml_named_value("Llvm_executionengine.Error"), Error);
|
|
return Val_unit;
|
|
}
|
|
|
|
/* ExecutionEngine.t -> unit */
|
|
value llvm_ee_run_static_ctors(value EE) {
|
|
LLVMRunStaticConstructors(ExecutionEngine_val(EE));
|
|
return Val_unit;
|
|
}
|
|
|
|
/* ExecutionEngine.t -> unit */
|
|
value llvm_ee_run_static_dtors(value EE) {
|
|
LLVMRunStaticDestructors(ExecutionEngine_val(EE));
|
|
return Val_unit;
|
|
}
|
|
|
|
extern value llvm_alloc_data_layout(LLVMTargetDataRef TargetData);
|
|
|
|
/* ExecutionEngine.t -> Llvm_target.DataLayout.t */
|
|
value llvm_ee_get_data_layout(value EE) {
|
|
value DataLayout;
|
|
LLVMTargetDataRef OrigDataLayout;
|
|
char *TargetDataCStr;
|
|
|
|
OrigDataLayout = LLVMGetExecutionEngineTargetData(ExecutionEngine_val(EE));
|
|
TargetDataCStr = LLVMCopyStringRepOfTargetData(OrigDataLayout);
|
|
DataLayout = llvm_alloc_data_layout(LLVMCreateTargetData(TargetDataCStr));
|
|
LLVMDisposeMessage(TargetDataCStr);
|
|
|
|
return DataLayout;
|
|
}
|
|
|
|
/* Llvm.llvalue -> int64 -> llexecutionengine -> unit */
|
|
value llvm_ee_add_global_mapping(value Global, value Ptr, value EE) {
|
|
LLVMAddGlobalMapping(ExecutionEngine_val(EE), Value_val(Global),
|
|
(void *)(Int64_val(Ptr)));
|
|
return Val_unit;
|
|
}
|
|
|
|
value llvm_ee_get_global_value_address(value Name, value EE) {
|
|
return caml_copy_int64((int64_t)LLVMGetGlobalValueAddress(
|
|
ExecutionEngine_val(EE), String_val(Name)));
|
|
}
|
|
|
|
value llvm_ee_get_function_address(value Name, value EE) {
|
|
return caml_copy_int64((int64_t)LLVMGetFunctionAddress(
|
|
ExecutionEngine_val(EE), String_val(Name)));
|
|
}
|