Skip to content

Commit

Permalink
Merge pull request #50 from tizoc/bytecode-functions
Browse files Browse the repository at this point in the history
Add support for generating bytecode-callable exported function wrappers
  • Loading branch information
tizoc authored Jul 12, 2023
2 parents d860c58 + 96dbb5d commit 2dcc278
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Support for OCaml `Bigarray.Array1` values (PR #26 by @g2p)
- Support for callable closure values with more automatic conversion between Rust and OCaml values (PR #44 and PR #45 by @sebastiencs)
- Conversions for `<Box<[u8]>` (PR #47 by @sebastiencs)
- Support for defining bytecode-callable wrappers for functions with `ocaml_export!` macro (PR #50)

## [0.8.8] - 2022-03-23

Expand Down
65 changes: 62 additions & 3 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ macro_rules! ocaml {
///
/// The return type defaults to [`OCaml`]`<()>` when omitted.
///
/// To generate a bytecode-callable version of the function (needed when the function has 6 or more arguments),
/// add a second name to the function separated by `|` (see last example).
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -153,6 +156,26 @@ macro_rules! ocaml {
/// let tuple = (fst, snd);
/// tuple.to_ocaml(cr)
/// }
///
/// fn rust_rust_add_7ints|rust_rust_add_7ints_byte(
/// cr,
/// int1: OCamlRef<OCamlInt>,
/// int2: OCamlRef<OCamlInt>,
/// int3: OCamlRef<OCamlInt>,
/// int4: OCamlRef<OCamlInt>,
/// int5: OCamlRef<OCamlInt>,
/// int6: OCamlRef<OCamlInt>,
/// int7: OCamlRef<OCamlInt>,
/// ) -> OCaml<OCamlInt> {
/// let int1: i64 = int1.to_rust(cr);
/// let int2: i64 = int2.to_rust(cr);
/// let int3: i64 = int3.to_rust(cr);
/// let int4: i64 = int4.to_rust(cr);
/// let int5: i64 = int5.to_rust(cr);
/// let int6: i64 = int6.to_rust(cr);
/// let int7: i64 = int7.to_rust(cr);
/// unsafe { OCaml::of_i64_unchecked(int1 + int2 + int3 + int4 + int5 + int6 + int7) }
/// }
/// }
/// ```
#[macro_export]
Expand All @@ -161,13 +184,14 @@ macro_rules! ocaml_export {

// Unboxed float return
{
fn $name:ident( $cr:ident, $($args:tt)*) -> f64
fn $name:ident$(|$byte_name:ident)?( $cr:ident, $($args:tt)*) -> f64
$body:block

$($t:tt)*
} => {
$crate::expand_exported_function!(
@name $name
@byte_name $($byte_name)?
@cr $cr
@final_args { }
@proc_args { $($args)*, }
Expand All @@ -181,13 +205,14 @@ macro_rules! ocaml_export {

// Other (or empty) return value type
{
fn $name:ident( $cr:ident, $($args:tt)*) $(-> $rtyp:ty)?
fn $name:ident$(|$byte_name:ident)?( $cr:ident, $($args:tt)*) $(-> $rtyp:ty)?
$body:block

$($t:tt)*
} => {
$crate::expand_exported_function!(
@name $name
@byte_name $($byte_name)?
@cr $cr
@final_args { }
@proc_args { $($args)*, }
Expand All @@ -202,7 +227,7 @@ macro_rules! ocaml_export {
// Invalid arguments

{
fn $name:ident( $($invalid_args:tt)* ) $(-> $rtyp:ty)?
fn $name:ident$(|$byte_name:ident)?( $($invalid_args:tt)* ) $(-> $rtyp:ty)?
$body:block

$($t:tt)*
Expand Down Expand Up @@ -1505,13 +1530,37 @@ macro_rules! expand_rooted_args_init {
};
}

#[doc(hidden)]
#[macro_export]
macro_rules! expand_exported_byte_function {
{
@name $name:ident
@byte_name $byte_name:ident
@final_args { $($arg:ident : $typ:ty),+ }
@return { $($rtyp:tt)* }
} => {
#[no_mangle]
pub extern "C" fn $byte_name( $($arg: $typ),* ) -> $crate::expand_exported_function_return!($($rtyp)*) {
$name($($arg),*)
}
};

{
@name $name:ident
@byte_name
@final_args { $($arg:ident : $typ:ty),+ }
@return { $($rtyp:tt)* }
} => {};
}

#[doc(hidden)]
#[macro_export]
macro_rules! expand_exported_function {
// Final expansions, with all argument types converted

{
@name $name:ident
@byte_name $($byte_name:ident)?
@cr $cr:ident
@final_args { $($arg:ident : $typ:ty,)+ }
@proc_args { $(,)? }
Expand All @@ -1528,6 +1577,12 @@ macro_rules! expand_exported_function {
@return $($rtyp)*
)
}

$crate::expand_exported_byte_function!(
@name $name
@byte_name $($byte_name)?
@final_args { $($arg: $typ),* }
@return { $($rtyp)* });
};

// Args processing
Expand All @@ -1536,6 +1591,7 @@ macro_rules! expand_exported_function {

{
@name $name:ident
@byte_name $($byte_name:ident)?
@cr $cr:ident
@final_args { $($final_args:tt)* }
@proc_args { $next_arg:ident : f64, $($proc_args:tt)* }
Expand All @@ -1545,6 +1601,7 @@ macro_rules! expand_exported_function {
} => {
$crate::expand_exported_function!{
@name $name
@byte_name $($byte_name)?
@cr $cr
@final_args { $($final_args)* $next_arg : f64, }
@proc_args { $($proc_args)* }
Expand All @@ -1558,6 +1615,7 @@ macro_rules! expand_exported_function {

{
@name $name:ident
@byte_name $($byte_name:ident)?
@cr $cr:ident
@final_args { $($final_args:tt)* }
@proc_args { $next_arg:ident : $typ:ty, $($proc_args:tt)* }
Expand All @@ -1567,6 +1625,7 @@ macro_rules! expand_exported_function {
} => {
$crate::expand_exported_function!{
@name $name
@byte_name $($byte_name)?
@cr $cr
@final_args { $($final_args)* $next_arg : $crate::RawOCaml, }
@proc_args { $($proc_args)* }
Expand Down
5 changes: 1 addition & 4 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ pub struct OCaml<'a, T: 'a> {

impl<'a, T> Clone for OCaml<'a, T> {
fn clone(&self) -> Self {
OCaml {
_marker: PhantomData,
raw: self.raw,
}
*self
}
}

Expand Down
9 changes: 9 additions & 0 deletions testing/ocaml-caller/ocaml_rust_caller.ml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ module Rust = struct

external string_of_polymorphic_movement : movement_polymorphic -> string
= "rust_string_of_polymorphic_movement"

external rust_rust_add_7ints : int -> int -> int -> int -> int -> int -> int -> int
= "rust_rust_add_7ints" "rust_rust_add_7ints_byte"
end

let test_twice () = Alcotest.(check int) "Multiply by 2" 20 (Rust.twice 10)
Expand Down Expand Up @@ -144,6 +147,11 @@ let test_interpret_polymorphic_movement () =
Alcotest.(check (list string))
"Interpret a polymorphic variant" expected result

let test_byte_function () =
let expected = 1 + 2 + 3 + 4 + 5 + 6 + 7 in
let result = Rust.rust_rust_add_7ints 1 2 3 4 5 6 7 in
Alcotest.(check int) "Call a bytecode function" expected result

(* Sleeps on the Rust thread releasing the OCaml runtime lock *)
let test_blocking_section () =
let before = Unix.gettimeofday () in
Expand Down Expand Up @@ -193,6 +201,7 @@ let () =
test_case "Rust.string_of_movement" `Quick test_interpret_movement;
test_case "Rust.string_of_polymorphic_movement" `Quick
test_interpret_polymorphic_movement;
test_case "Rust.rust_rust_add_7ints" `Quick test_byte_function
] );
];
Rust.tests_teardown ()
20 changes: 20 additions & 0 deletions testing/ocaml-caller/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,24 @@ ocaml_export! {
};
s.to_ocaml(cr)
}

fn rust_rust_add_7ints|rust_rust_add_7ints_byte(
cr,
int1: OCamlRef<OCamlInt>,
int2: OCamlRef<OCamlInt>,
int3: OCamlRef<OCamlInt>,
int4: OCamlRef<OCamlInt>,
int5: OCamlRef<OCamlInt>,
int6: OCamlRef<OCamlInt>,
int7: OCamlRef<OCamlInt>,
) -> OCaml<OCamlInt> {
let int1: i64 = int1.to_rust(cr);
let int2: i64 = int2.to_rust(cr);
let int3: i64 = int3.to_rust(cr);
let int4: i64 = int4.to_rust(cr);
let int5: i64 = int5.to_rust(cr);
let int6: i64 = int6.to_rust(cr);
let int7: i64 = int7.to_rust(cr);
unsafe { OCaml::of_i64_unchecked(int1 + int2 + int3 + int4 + int5 + int6 + int7) }
}
}

0 comments on commit 2dcc278

Please sign in to comment.