[LLVMdev] [PATCH] [OCaml] Expose getFunctionAddress, as a modern alternative
Ramkumar Ramachandra
artagnon at gmail.com
Sun Dec 21 21:13:59 PST 2014
The OCaml bindings use getPointerToGlobal as the primary way to get a
handle on functions to run. This function is broken and deprecated, and
we want to transition to the modern getFunctionAddress alternative. This
is a two step process: first, there needs to be a C wrapper that OCaml
can leverage. This is fine, with a passing test. Second, there needs to
be an OCaml interface to the new LLVMGetFunctionAddress C function.
I've verified that the LLVMGetFunctionAddress is receiving the right
string, and calling down to the MCJIT implementation of
getFunctionAddress. However, getSymbolAddress does not seem to want to
cooperate with us, and returns 0 for a perfectly valid function. Why
LLVMGetFunctionAddress works via the C API, but not via the OCaml API
after having verified that the arguments are right stumps me.
Perhaps there is some name mangling going on when OCaml emits the IR? I
can't imagine why, but it's the only explanation I've been able to reach
so far.
Cc: Peter Zotov <whitequark at whitequark.org>
Signed-off-by: Ramkumar Ramachandra <artagnon at gmail.com>
---
.../ocaml/executionengine/executionengine_ocaml.c | 5 +++++
.../ocaml/executionengine/llvm_executionengine.ml | 9 ++++++++
.../ocaml/executionengine/llvm_executionengine.mli | 7 ++++++
include/llvm-c/ExecutionEngine.h | 2 ++
lib/ExecutionEngine/ExecutionEngineBindings.cpp | 6 ++++++
lib/ExecutionEngine/MCJIT/MCJIT.cpp | 1 +
test/Bindings/OCaml/executionengine.ml | 25 ++++++++++++++++++++++
unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp | 17 +++++++++++++++
8 files changed, 72 insertions(+)
diff --git a/bindings/ocaml/executionengine/executionengine_ocaml.c b/bindings/ocaml/executionengine/executionengine_ocaml.c
index 0557efc..83faada 100644
--- a/bindings/ocaml/executionengine/executionengine_ocaml.c
+++ b/bindings/ocaml/executionengine/executionengine_ocaml.c
@@ -120,3 +120,8 @@ CAMLprim value llvm_ee_get_pointer_to_global(LLVMValueRef Global,
LLVMExecutionEngineRef EE) {
return caml_copy_int64((int64_t) LLVMGetPointerToGlobal(EE, Global));
}
+
+CAMLprim value llvm_ee_get_function_address(value Name,
+ LLVMExecutionEngineRef EE) {
+ return caml_copy_int64((int64_t) LLVMGetFunctionAddress(EE, String_val(Name)));
+}
diff --git a/bindings/ocaml/executionengine/llvm_executionengine.ml b/bindings/ocaml/executionengine/llvm_executionengine.ml
index c0ff330..7fbb5fb 100644
--- a/bindings/ocaml/executionengine/llvm_executionengine.ml
+++ b/bindings/ocaml/executionengine/llvm_executionengine.ml
@@ -47,6 +47,8 @@ external add_global_mapping_ : Llvm.llvalue -> int64 -> llexecutionengine -> uni
= "llvm_ee_add_global_mapping"
external get_pointer_to_global_ : Llvm.llvalue -> llexecutionengine -> int64
= "llvm_ee_get_pointer_to_global"
+external get_function_address_ : string -> llexecutionengine -> int64
+ = "llvm_ee_get_function_address"
let add_global_mapping llval ptr ee =
add_global_mapping_ llval (Ctypes.raw_address_of_ptr (Ctypes.to_voidp ptr)) ee
@@ -55,6 +57,13 @@ let get_pointer_to_global llval typ ee =
Ctypes.coerce (let open Ctypes in ptr void) typ
(Ctypes.ptr_of_raw_address (get_pointer_to_global_ llval ee))
+let get_function_address name typ ee =
+ let ptr = get_function_address_ name ee in
+ if Int64.to_int ptr <> 0 then
+ Ctypes.coerce (let open Ctypes in ptr void) typ (Ctypes.ptr_of_raw_address ptr)
+ else
+ raise (Error ("Function " ^ name ^ " not found"))
+
(* The following are not bound. Patches are welcome.
target_machine : llexecutionengine -> Llvm_target.TargetMachine.t
*)
diff --git a/bindings/ocaml/executionengine/llvm_executionengine.mli b/bindings/ocaml/executionengine/llvm_executionengine.mli
index b07151d..11c30c3 100644
--- a/bindings/ocaml/executionengine/llvm_executionengine.mli
+++ b/bindings/ocaml/executionengine/llvm_executionengine.mli
@@ -82,3 +82,10 @@ val add_global_mapping : Llvm.llvalue -> 'a Ctypes.ptr -> llexecutionengine -> u
a function (e.g. [(int -> int) typ]) type for functions, and which
will be live as long as [gv] and [ee] are. *)
val get_pointer_to_global : Llvm.llvalue -> 'a Ctypes.typ -> llexecutionengine -> 'a
+
+(** [get_function_address name typ ee] returns the value of the global
+ variable [gv] in the execution engine [ee] as type [typ], which may
+ be a pointer type (e.g. [int ptr typ]) for global variables or
+ a function (e.g. [(int -> int) typ]) type for functions, and which
+ will be live as long as [gv] and [ee] are. *)
+val get_function_address : string -> 'a Ctypes.typ -> llexecutionengine -> 'a
diff --git a/include/llvm-c/ExecutionEngine.h b/include/llvm-c/ExecutionEngine.h
index f1f4cad..46b6d07 100644
--- a/include/llvm-c/ExecutionEngine.h
+++ b/include/llvm-c/ExecutionEngine.h
@@ -170,6 +170,8 @@ void LLVMAddGlobalMapping(LLVMExecutionEngineRef EE, LLVMValueRef Global,
void *LLVMGetPointerToGlobal(LLVMExecutionEngineRef EE, LLVMValueRef Global);
+uint64_t LLVMGetFunctionAddress(LLVMExecutionEngineRef EE, const char *Name);
+
/*===-- Operations on memory managers -------------------------------------===*/
typedef uint8_t *(*LLVMMemoryManagerAllocateCodeSectionCallback)(
diff --git a/lib/ExecutionEngine/ExecutionEngineBindings.cpp b/lib/ExecutionEngine/ExecutionEngineBindings.cpp
index 7fc72ae..173063c 100644
--- a/lib/ExecutionEngine/ExecutionEngineBindings.cpp
+++ b/lib/ExecutionEngine/ExecutionEngineBindings.cpp
@@ -328,6 +328,12 @@ void *LLVMGetPointerToGlobal(LLVMExecutionEngineRef EE, LLVMValueRef Global) {
return unwrap(EE)->getPointerToGlobal(unwrap<GlobalValue>(Global));
}
+uint64_t LLVMGetFunctionAddress(LLVMExecutionEngineRef EE, const char *Name) {
+ unwrap(EE)->finalizeObject();
+
+ return unwrap(EE)->getFunctionAddress(Name);
+}
+
/*===-- Operations on memory managers -------------------------------------===*/
namespace {
diff --git a/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/lib/ExecutionEngine/MCJIT/MCJIT.cpp
index f2d53f5..786262a 100644
--- a/lib/ExecutionEngine/MCJIT/MCJIT.cpp
+++ b/lib/ExecutionEngine/MCJIT/MCJIT.cpp
@@ -27,6 +27,7 @@
#include "llvm/Support/MutexGuard.h"
#include "llvm/Target/TargetLowering.h"
#include "llvm/Target/TargetSubtargetInfo.h"
+#include <iostream>
using namespace llvm;
diff --git a/test/Bindings/OCaml/executionengine.ml b/test/Bindings/OCaml/executionengine.ml
index 893f988..a729d9e 100644
--- a/test/Bindings/OCaml/executionengine.ml
+++ b/test/Bindings/OCaml/executionengine.ml
@@ -42,6 +42,22 @@ let define_plus m =
ignore (build_ret add b);
fn
+let define_ooo1 m =
+ let fn = define_function "ooo1" (function_type i32_type [| i32_type;
+ i32_type |]) m in
+ let b = builder_at_end (global_context ()) (entry_block fn) in
+ let add = build_add (param fn 0) (param fn 1) "sum" b in
+ ignore (build_ret add b);
+ fn
+
+let define_ooo2 m =
+ let fn = define_function "ooo2" (function_type i32_type [| i32_type;
+ i32_type |]) m in
+ let b = builder_at_end (global_context ()) (entry_block fn) in
+ let add = build_sub (param fn 0) (param fn 1) "diff" b in
+ ignore (build_ret add b);
+ fn
+
let test_executionengine () =
let open Ctypes in
@@ -78,6 +94,15 @@ let test_executionengine () =
let cplus = get_pointer_to_global plus cplusty ee in
if 4l <> cplus 2l 2l then bomb "plus didn't work";
+ (* define and execute out of order *)
+ ignore (define_ooo1 m);
+ dump_module m;
+ let cooo1 = get_function_address "ooo1" cplusty ee in
+ if 4l <> cooo1 2l 2l then bomb "ooo1 didn't work";
+ ignore (define_ooo2 m);
+ let cooo2 = get_function_address "ooo2" cplusty ee in
+ if 0l <> cooo2 2l 2l then bomb "ooo2 didn't work";
+
(* call getglobal *)
(* let cgetglobalty = Foreign.funptr (void @-> returning int32_t) in
let cgetglobal = get_pointer_to_global getglobal cgetglobalty ee in
diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
index c80b88b..f4b6eca 100644
--- a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
+++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
@@ -347,6 +347,23 @@ TEST_F(MCJITCAPITest, simple_function) {
EXPECT_EQ(42, functionPointer.usable());
}
+TEST_F(MCJITCAPITest, new_gfa) {
+ SKIP_UNSUPPORTED_PLATFORM;
+
+ buildSimpleFunction();
+ buildMCJITOptions();
+ buildMCJITEngine();
+ // buildAndRunPasses();
+
+ union {
+ uint64_t raw;
+ int (*usable)();
+ } functionPointer;
+ functionPointer.raw = LLVMGetFunctionAddress(Engine, "simple_function");
+
+ EXPECT_EQ(42, functionPointer.usable());
+}
+
TEST_F(MCJITCAPITest, custom_memory_manager) {
SKIP_UNSUPPORTED_PLATFORM;
--
2.1.3
More information about the llvm-dev
mailing list