Skip to content

Commit

Permalink
BREAKING(ext/ffi): specialized buffer type (denoland#15518)
Browse files Browse the repository at this point in the history
  • Loading branch information
littledivy authored Aug 23, 2022
1 parent d0c5477 commit e34260c
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 101 deletions.
9 changes: 7 additions & 2 deletions cli/dts/lib.deno.unstable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ declare namespace Deno {

type NativePointerType = "pointer";

type NativeBufferType = "buffer";

type NativeFunctionType = "function";

type NativeVoidType = "void";
Expand All @@ -407,6 +409,7 @@ declare namespace Deno {
| NativeNumberType
| NativeBigIntType
| NativePointerType
| NativeBufferType
| NativeFunctionType;

/** @category FFI */
Expand All @@ -416,8 +419,9 @@ declare namespace Deno {
type ToNativeTypeMap =
& Record<NativeNumberType, number>
& Record<NativeBigIntType, PointerValue>
& Record<NativePointerType, TypedArray | PointerValue | null>
& Record<NativeFunctionType, PointerValue | null>;
& Record<NativePointerType, PointerValue | null>
& Record<NativeFunctionType, PointerValue | null>
& Record<NativeBufferType, TypedArray>;

/** Type conversion for foreign symbol parameters and unsafe callback return
* types.
Expand Down Expand Up @@ -452,6 +456,7 @@ declare namespace Deno {
& Record<NativeNumberType, number>
& Record<NativeBigIntType, PointerValue>
& Record<NativePointerType, PointerValue>
& Record<NativeBufferType, PointerValue>
& Record<NativeFunctionType, PointerValue>;

/** Type conversion for foreign symbol return types and unsafe callback
Expand Down
2 changes: 1 addition & 1 deletion ext/ffi/00_ffi.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
}

function isPointerType(type) {
return type === "pointer" ||
return type === "buffer" || type === "pointer" ||
typeof type === "object" && type !== null && "function" in type;
}

Expand Down
22 changes: 15 additions & 7 deletions ext/ffi/jit_trampoline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ fn native_arg_to_c(ty: &NativeType) -> &'static str {
NativeType::I64 => "int64_t",
NativeType::ISize => "intptr_t",
NativeType::USize => "uintptr_t",
NativeType::Pointer => "struct FastApiTypedArray*",
NativeType::Function => "void*",
NativeType::Buffer => "struct FastApiTypedArray*",
NativeType::Function | NativeType::Pointer => "void*",
}
}

Expand All @@ -52,7 +52,7 @@ fn native_to_c(ty: &NativeType) -> &'static str {
NativeType::I64 => "int64_t",
NativeType::ISize => "intptr_t",
NativeType::USize => "uintptr_t",
NativeType::Pointer | NativeType::Function => "void*",
NativeType::Pointer | NativeType::Buffer | NativeType::Function => "void*",
}
}

Expand Down Expand Up @@ -97,7 +97,7 @@ pub(crate) fn codegen(sym: &crate::Symbol) -> String {
if i > 0 {
call_s += ", ";
}
if matches!(ty, NativeType::Pointer) {
if matches!(ty, NativeType::Buffer) {
let _ = write!(call_s, "p{i}->data");
} else {
let _ = write!(call_s, "p{i}");
Expand Down Expand Up @@ -195,14 +195,14 @@ mod tests {
}\n\n",
);
assert_codegen(
codegen(vec![NativeType::Pointer, NativeType::U32], NativeType::U32),
codegen(vec![NativeType::Buffer, NativeType::U32], NativeType::U32),
"extern uint32_t func(void* p0, uint32_t p1);\n\n\
uint32_t func_trampoline(void* recv, struct FastApiTypedArray* p0, uint32_t p1) {\
\n return func(p0->data, p1);\n\
}\n\n",
);
assert_codegen(
codegen(vec![NativeType::Pointer, NativeType::Pointer], NativeType::U32),
codegen(vec![NativeType::Buffer, NativeType::Buffer], NativeType::U32),
"extern uint32_t func(void* p0, void* p1);\n\n\
uint32_t func_trampoline(void* recv, struct FastApiTypedArray* p0, struct FastApiTypedArray* p1) {\
\n return func(p0->data, p1->data);\n\
Expand All @@ -217,13 +217,21 @@ mod tests {
}\n\n",
);
assert_codegen(
codegen(vec![NativeType::Pointer, NativeType::Pointer], NativeType::U64),
codegen(vec![NativeType::Buffer, NativeType::Buffer], NativeType::U64),
"extern uint64_t func(void* p0, void* p1);\n\n\
void func_trampoline(void* recv, struct FastApiTypedArray* p0, struct FastApiTypedArray* p1, struct FastApiTypedArray* const p_ret) {\
\n uint64_t r = func(p0->data, p1->data);\
\n ((uint64_t*)p_ret->data)[0] = r;\n\
}\n\n",
);
assert_codegen(
codegen(vec![NativeType::Pointer, NativeType::Pointer], NativeType::U64),
"extern uint64_t func(void* p0, void* p1);\n\n\
void func_trampoline(void* recv, void* p0, void* p1, struct FastApiTypedArray* const p_ret) {\
\n uint64_t r = func(p0, p1);\
\n ((uint64_t*)p_ret->data)[0] = r;\n\
}\n\n",
);
}

#[test]
Expand Down
93 changes: 64 additions & 29 deletions ext/ffi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ enum NativeType {
F32,
F64,
Pointer,
Buffer,
Function,
}

Expand All @@ -285,8 +286,9 @@ impl From<NativeType> for libffi::middle::Type {
NativeType::ISize => libffi::middle::Type::isize(),
NativeType::F32 => libffi::middle::Type::f32(),
NativeType::F64 => libffi::middle::Type::f64(),
NativeType::Pointer => libffi::middle::Type::pointer(),
NativeType::Function => libffi::middle::Type::pointer(),
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
libffi::middle::Type::pointer()
}
}
}
}
Expand Down Expand Up @@ -327,7 +329,9 @@ impl NativeValue {
NativeType::ISize => Arg::new(&self.isize_value),
NativeType::F32 => Arg::new(&self.f32_value),
NativeType::F64 => Arg::new(&self.f64_value),
NativeType::Pointer | NativeType::Function => Arg::new(&self.pointer),
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
Arg::new(&self.pointer)
}
}
}

Expand Down Expand Up @@ -375,7 +379,7 @@ impl NativeValue {
}
NativeType::F32 => Value::from(self.f32_value),
NativeType::F64 => Value::from(self.f64_value),
NativeType::Pointer | NativeType::Function => {
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
let value = self.pointer as usize;
if value > MAX_SAFE_INTEGER as usize {
json!(U32x2::from(value as u64))
Expand Down Expand Up @@ -479,7 +483,7 @@ impl NativeValue {
v8::Number::new(scope, self.f64_value).into();
local_value.into()
}
NativeType::Pointer | NativeType::Function => {
NativeType::Pointer | NativeType::Buffer | NativeType::Function => {
let value = self.pointer as u64;
let local_value: v8::Local<v8::Value> =
if value > MAX_SAFE_INTEGER as u64 {
Expand Down Expand Up @@ -751,8 +755,10 @@ impl From<&NativeType> for fast_api::Type {
NativeType::I64 => fast_api::Type::Int64,
NativeType::U64 => fast_api::Type::Uint64,
NativeType::ISize => fast_api::Type::Int64,
NativeType::USize | NativeType::Function => fast_api::Type::Uint64,
NativeType::Pointer => fast_api::Type::TypedArray(fast_api::CType::Uint8),
NativeType::USize | NativeType::Pointer | NativeType::Function => {
fast_api::Type::Uint64
}
NativeType::Buffer => fast_api::Type::TypedArray(fast_api::CType::Uint8),
}
}
}
Expand All @@ -762,6 +768,7 @@ fn needs_unwrap(rv: NativeType) -> bool {
rv,
NativeType::Function
| NativeType::Pointer
| NativeType::Buffer
| NativeType::I64
| NativeType::ISize
| NativeType::U64
Expand Down Expand Up @@ -1064,14 +1071,37 @@ fn ffi_parse_pointer_arg(
arg: v8::Local<v8::Value>,
) -> Result<NativeValue, AnyError> {
// Order of checking:
// 1. ArrayBufferView: Common and not supported by Fast API, optimise this case.
// 2. BigInt: Uncommon and not supported by Fast API, optimise this case as second.
// 3. Number: Common and supported by Fast API, optimise the common case third.
// 4. ArrayBuffer: Fairly common and not supported by Fast API.
// 1. BigInt: Uncommon and not supported by Fast API, optimise this case.
// 2. Number: Common and supported by Fast API.
// 3. Null: Very uncommon / can be represented by a 0.
let pointer = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
value.u64_value().0 as usize as *const u8
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
value.integer_value(scope).unwrap() as usize as *const u8
} else if arg.is_null() {
ptr::null()
} else {
return Err(type_error(
"Invalid FFI pointer type, expected null, integer or BigInt",
));
};
Ok(NativeValue { pointer })
}

#[inline]
fn ffi_parse_buffer_arg(
scope: &mut v8::HandleScope,
arg: v8::Local<v8::Value>,
) -> Result<NativeValue, AnyError> {
// Order of checking:
// 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case.
// 2. ArrayBufferView: Common and supported by Fast API
// 5. Null: Very uncommon / can be represented by a 0.
let pointer = if let Ok(value) =
v8::Local::<v8::ArrayBufferView>::try_from(arg)
{

let pointer = if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(arg) {
let backing_store = value.get_backing_store();
&backing_store[..] as *const _ as *const u8
} else if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(arg) {
let byte_offset = value.byte_offset();
let backing_store = value
.buffer(scope)
Expand All @@ -1080,17 +1110,12 @@ fn ffi_parse_pointer_arg(
})?
.get_backing_store();
&backing_store[byte_offset..] as *const _ as *const u8
} else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) {
value.u64_value().0 as usize as *const u8
} else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) {
value.integer_value(scope).unwrap() as usize as *const u8
} else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(arg) {
let backing_store = value.get_backing_store();
&backing_store[..] as *const _ as *const u8
} else if arg.is_null() {
ptr::null()
} else {
return Err(type_error("Invalid FFI pointer type, expected null, integer, BigInt, ArrayBuffer, or ArrayBufferView"));
return Err(type_error(
"Invalid FFI buffer type, expected null, ArrayBuffer, or ArrayBufferView",
));
};
Ok(NativeValue { pointer })
}
Expand Down Expand Up @@ -1174,6 +1199,9 @@ where
NativeType::F64 => {
ffi_args.push(ffi_parse_f64_arg(value)?);
}
NativeType::Buffer => {
ffi_args.push(ffi_parse_buffer_arg(scope, value)?);
}
NativeType::Pointer => {
ffi_args.push(ffi_parse_pointer_arg(scope, value)?);
}
Expand Down Expand Up @@ -1247,6 +1275,9 @@ where
NativeType::F64 => {
ffi_args.push(ffi_parse_f64_arg(value)?);
}
NativeType::Buffer => {
ffi_args.push(ffi_parse_buffer_arg(scope, value)?);
}
NativeType::Pointer => {
ffi_args.push(ffi_parse_pointer_arg(scope, value)?);
}
Expand Down Expand Up @@ -1302,9 +1333,11 @@ where
NativeType::F64 => NativeValue {
f64_value: cif.call::<f64>(*fun_ptr, &call_args),
},
NativeType::Pointer | NativeType::Function => NativeValue {
pointer: cif.call::<*const u8>(*fun_ptr, &call_args),
},
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
NativeValue {
pointer: cif.call::<*const u8>(*fun_ptr, &call_args),
}
}
})
}
}
Expand Down Expand Up @@ -1368,9 +1401,11 @@ fn ffi_call(
NativeType::F64 => NativeValue {
f64_value: cif.call::<f64>(fun_ptr, &call_args),
},
NativeType::Pointer | NativeType::Function => NativeValue {
pointer: cif.call::<*const u8>(fun_ptr, &call_args),
},
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
NativeValue {
pointer: cif.call::<*const u8>(fun_ptr, &call_args),
}
}
})
}
}
Expand Down Expand Up @@ -2004,7 +2039,7 @@ fn op_ffi_get_static<'scope>(
let number: v8::Local<v8::Value> = v8::Number::new(scope, result).into();
number.into()
}
NativeType::Pointer | NativeType::Function => {
NativeType::Pointer | NativeType::Function | NativeType::Buffer => {
let result = data_ptr as u64;
let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 {
v8::BigInt::new_from_u64(scope, result).into()
Expand Down
Loading

0 comments on commit e34260c

Please sign in to comment.