summaryrefslogtreecommitdiff
path: root/ext/ffi/jit_trampoline.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ffi/jit_trampoline.rs')
-rw-r--r--ext/ffi/jit_trampoline.rs153
1 files changed, 153 insertions, 0 deletions
diff --git a/ext/ffi/jit_trampoline.rs b/ext/ffi/jit_trampoline.rs
new file mode 100644
index 000000000..40c14dfb0
--- /dev/null
+++ b/ext/ffi/jit_trampoline.rs
@@ -0,0 +1,153 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use crate::NativeType;
+use crate::{tcc::Compiler, Symbol};
+use std::ffi::c_void;
+use std::ffi::CString;
+use std::fmt::Write as _;
+
+pub(crate) struct Allocation {
+ pub addr: *mut c_void,
+ _ctx: Compiler,
+ _sym: Box<Symbol>,
+}
+
+macro_rules! cstr {
+ ($st:expr) => {
+ &CString::new($st).unwrap()
+ };
+}
+
+fn native_arg_to_c(ty: &NativeType) -> &'static str {
+ match ty {
+ NativeType::U8 | NativeType::U16 | NativeType::U32 => "uint32_t",
+ NativeType::I8 | NativeType::I16 | NativeType::I32 => "int32_t",
+ NativeType::Void => "void",
+ NativeType::F32 => "float",
+ NativeType::F64 => "double",
+ _ => unimplemented!(),
+ }
+}
+
+fn native_to_c(ty: &NativeType) -> &'static str {
+ match ty {
+ NativeType::U8 => "uint8_t",
+ NativeType::U16 => "uint16_t",
+ NativeType::U32 => "uint32_t",
+ NativeType::I8 => "int8_t",
+ NativeType::I16 => "uint16_t",
+ NativeType::I32 => "int32_t",
+ NativeType::Void => "void",
+ NativeType::F32 => "float",
+ NativeType::F64 => "double",
+ _ => unimplemented!(),
+ }
+}
+
+pub(crate) fn codegen(sym: &crate::Symbol) -> String {
+ let mut c = String::from("#include <stdint.h>\n");
+ let ret = native_to_c(&sym.result_type);
+
+ // extern <return_type> func(
+ c += "\nextern ";
+ c += ret;
+ c += " func(";
+ // <param_type> p0, <param_type> p1, ...);
+ for (i, ty) in sym.parameter_types.iter().enumerate() {
+ if i > 0 {
+ c += ", ";
+ }
+ c += native_to_c(ty);
+ let _ = write!(c, " p{i}");
+ }
+ c += ");\n\n";
+
+ // void* recv, <param_type> p0, <param_type> p1, ...);
+ c += ret;
+ c += " func_trampoline(";
+ c += "void* recv";
+ for (i, ty) in sym.parameter_types.iter().enumerate() {
+ c += ", ";
+ c += native_arg_to_c(ty);
+ let _ = write!(c, " p{i}");
+ }
+ c += ") {\n";
+ // return func(p0, p1, ...);
+ c += " return func(";
+ for (i, _) in sym.parameter_types.iter().enumerate() {
+ if i > 0 {
+ c += ", ";
+ }
+ let _ = write!(c, "p{i}");
+ }
+ c += ");\n}\n\n";
+ c
+}
+
+pub(crate) fn gen_trampoline(
+ sym: Box<crate::Symbol>,
+) -> Result<Box<Allocation>, ()> {
+ let mut ctx = Compiler::new()?;
+ ctx.set_options(cstr!("-nostdlib"));
+ // SAFETY: symbol satisfies ABI requirement.
+ unsafe { ctx.add_symbol(cstr!("func"), sym.ptr.0 as *const c_void) };
+ let c = codegen(&sym);
+
+ ctx.compile_string(cstr!(c))?;
+ let alloc = Allocation {
+ addr: ctx.relocate_and_get_symbol(cstr!("func_trampoline"))?,
+ _ctx: ctx,
+ _sym: sym,
+ };
+ Ok(Box::new(alloc))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use libffi::middle::Type;
+ use std::ptr::null_mut;
+
+ fn codegen(parameters: Vec<NativeType>, ret: NativeType) -> String {
+ let sym = Box::new(crate::Symbol {
+ cif: libffi::middle::Cif::new(vec![], Type::void()),
+ ptr: libffi::middle::CodePtr(null_mut()),
+ parameter_types: parameters,
+ result_type: ret,
+ can_callback: false,
+ });
+ super::codegen(&sym)
+ }
+
+ #[test]
+ fn test_gen_trampoline() {
+ assert_eq!(
+ codegen(vec![], NativeType::Void),
+ "#include <stdint.h>\n\nextern void func();\n\nvoid func_trampoline(void* recv) {\n return func();\n}\n\n"
+ );
+ assert_eq!(
+ codegen(vec![NativeType::U32, NativeType::U32], NativeType::U32),
+ "#include <stdint.h>\n\nextern uint32_t func(uint32_t p0, uint32_t p1);\n\nuint32_t func_trampoline(void* recv, uint32_t p0, uint32_t p1) {\n return func(p0, p1);\n}\n\n"
+ );
+ assert_eq!(
+ codegen(vec![NativeType::I32, NativeType::I32], NativeType::I32),
+ "#include <stdint.h>\n\nextern int32_t func(int32_t p0, int32_t p1);\n\nint32_t func_trampoline(void* recv, int32_t p0, int32_t p1) {\n return func(p0, p1);\n}\n\n"
+ );
+ assert_eq!(
+ codegen(vec![NativeType::F32, NativeType::F32], NativeType::F32),
+ "#include <stdint.h>\n\nextern float func(float p0, float p1);\n\nfloat func_trampoline(void* recv, float p0, float p1) {\n return func(p0, p1);\n}\n\n"
+ );
+ assert_eq!(
+ codegen(vec![NativeType::F64, NativeType::F64], NativeType::F64),
+ "#include <stdint.h>\n\nextern double func(double p0, double p1);\n\ndouble func_trampoline(void* recv, double p0, double p1) {\n return func(p0, p1);\n}\n\n"
+ );
+ }
+
+ #[test]
+ fn test_gen_trampoline_implicit_cast() {
+ assert_eq!(
+ codegen(vec![NativeType::I8, NativeType::U8], NativeType::I8),
+ "#include <stdint.h>\n\nextern int8_t func(int8_t p0, uint8_t p1);\n\nint8_t func_trampoline(void* recv, int32_t p0, uint32_t p1) {\n return func(p0, p1);\n}\n\n"
+ )
+ }
+}