summaryrefslogtreecommitdiff
path: root/ext/ffi/tcc.rs
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2022-07-12 06:33:05 +0530
committerGitHub <noreply@github.com>2022-07-12 06:33:05 +0530
commit77d065e034db7ed21a0e110bbbfc5eb5287d009c (patch)
tree4f2ad3195f2f4d52b69432fcc5bf72496c0eaf6a /ext/ffi/tcc.rs
parent5db16d122914336124620a5152655917e58f05a6 (diff)
fix(ext/ffi): trampoline for fast calls (#15139)
Diffstat (limited to 'ext/ffi/tcc.rs')
-rw-r--r--ext/ffi/tcc.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/ext/ffi/tcc.rs b/ext/ffi/tcc.rs
new file mode 100644
index 000000000..edc30c893
--- /dev/null
+++ b/ext/ffi/tcc.rs
@@ -0,0 +1,143 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use std::{
+ ffi::CStr,
+ marker::PhantomData,
+ os::raw::{c_char, c_int, c_void},
+ ptr::null_mut,
+};
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct TCCState {
+ _unused: [u8; 0],
+}
+pub const TCC_OUTPUT_MEMORY: i32 = 1;
+
+extern "C" {
+ pub fn tcc_new() -> *mut TCCState;
+ pub fn tcc_delete(s: *mut TCCState);
+ pub fn tcc_set_options(s: *mut TCCState, str: *const c_char);
+ pub fn tcc_compile_string(s: *mut TCCState, buf: *const c_char) -> c_int;
+ pub fn tcc_add_symbol(
+ s: *mut TCCState,
+ name: *const c_char,
+ val: *const c_void,
+ ) -> c_int;
+ pub fn tcc_set_output_type(s: *mut TCCState, output_type: c_int) -> c_int;
+ pub fn tcc_relocate(s1: *mut TCCState, ptr: *mut c_void) -> c_int;
+ pub fn tcc_get_symbol(s: *mut TCCState, name: *const c_char) -> *mut c_void;
+}
+
+/// Compilation context.
+pub struct Compiler {
+ inner: *mut TCCState,
+ _phantom: PhantomData<TCCState>,
+ pub bin: Option<Vec<u8>>,
+}
+
+impl Compiler {
+ pub fn new() -> Result<Self, ()> {
+ // SAFETY: There is one context per thread.
+ let inner = unsafe { tcc_new() };
+ if inner.is_null() {
+ Err(())
+ } else {
+ let ret =
+ // SAFETY: set output to memory.
+ unsafe { tcc_set_output_type(inner, TCC_OUTPUT_MEMORY as c_int) };
+ assert_eq!(ret, 0);
+ Ok(Self {
+ inner,
+ _phantom: PhantomData,
+ bin: None,
+ })
+ }
+ }
+
+ pub fn set_options(&mut self, option: &CStr) -> &mut Self {
+ // SAFETY: option is a null-terminated C string.
+ unsafe {
+ tcc_set_options(self.inner, option.as_ptr());
+ }
+ self
+ }
+
+ pub fn compile_string(&mut self, p: &CStr) -> Result<(), ()> {
+ // SAFETY: p is a null-terminated C string.
+ let ret = unsafe { tcc_compile_string(self.inner, p.as_ptr()) };
+ if ret == 0 {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ /// # Safety
+ /// Symbol need satisfy ABI requirement.
+ pub unsafe fn add_symbol(&mut self, sym: &CStr, val: *const c_void) {
+ // SAFETY: sym is a null-terminated C string.
+ let ret = tcc_add_symbol(self.inner, sym.as_ptr(), val);
+ assert_eq!(ret, 0);
+ }
+
+ pub fn relocate_and_get_symbol(
+ &mut self,
+ sym: &CStr,
+ ) -> Result<*mut c_void, ()> {
+ // SAFETY: pass null ptr to get required length
+ let len = unsafe { tcc_relocate(self.inner, null_mut()) };
+ if len == -1 {
+ return Err(());
+ };
+ let mut bin = Vec::with_capacity(len as usize);
+ let ret =
+ // SAFETY: bin is allocated up to len.
+ unsafe { tcc_relocate(self.inner, bin.as_mut_ptr() as *mut c_void) };
+ if ret != 0 {
+ return Err(());
+ }
+ // SAFETY: if ret == 0, bin is initialized.
+ unsafe {
+ bin.set_len(len as usize);
+ }
+ self.bin = Some(bin);
+ // SAFETY: sym is a null-terminated C string.
+ let addr = unsafe { tcc_get_symbol(self.inner, sym.as_ptr()) };
+ Ok(addr)
+ }
+}
+
+impl Drop for Compiler {
+ fn drop(&mut self) {
+ // SAFETY: delete state from tcc_new()
+ unsafe { tcc_delete(self.inner) };
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::ffi::CString;
+
+ #[test]
+ fn test_compiler_jit() {
+ let p = CString::new(
+ r#"
+ #include <stdint.h>
+ int32_t add(int32_t a, int32_t b) {
+ return a + b;
+ }
+ "#
+ .as_bytes(),
+ )
+ .unwrap();
+ let sym = CString::new("add".as_bytes()).unwrap();
+
+ let mut ctx = Compiler::new().unwrap();
+ let ops = CString::new("-nostdlib").unwrap();
+ ctx.set_options(&ops);
+ assert!(ctx.compile_string(&p).is_ok());
+ ctx.relocate_and_get_symbol(&sym).unwrap();
+ }
+}