summaryrefslogtreecommitdiff
path: root/test_ffi
diff options
context:
space:
mode:
Diffstat (limited to 'test_ffi')
-rw-r--r--test_ffi/Cargo.toml1
-rw-r--r--test_ffi/src/lib.rs54
-rw-r--r--test_ffi/tests/integration_tests.rs14
-rw-r--r--test_ffi/tests/test.js105
4 files changed, 159 insertions, 15 deletions
diff --git a/test_ffi/Cargo.toml b/test_ffi/Cargo.toml
index cc7708fbc..38dd86b3e 100644
--- a/test_ffi/Cargo.toml
+++ b/test_ffi/Cargo.toml
@@ -11,4 +11,5 @@ publish = false
crate-type = ["cdylib"]
[dev-dependencies]
+pretty_assertions = "1.2.1"
test_util = { path = "../test_util" }
diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs
index 812b563ef..be0d2e42f 100644
--- a/test_ffi/src/lib.rs
+++ b/test_ffi/src/lib.rs
@@ -258,6 +258,60 @@ pub extern "C" fn call_stored_function_thread_safe_and_log() {
});
}
+#[no_mangle]
+pub extern "C" fn log_many_parameters(
+ a: u8,
+ b: u16,
+ c: u32,
+ d: u64,
+ e: f64,
+ f: f32,
+ g: i64,
+ h: i32,
+ i: i16,
+ j: i8,
+ k: isize,
+ l: usize,
+ m: f64,
+ n: f32,
+ o: f64,
+ p: f32,
+ q: f64,
+ r: f32,
+ s: f64,
+) {
+ println!("{a} {b} {c} {d} {e} {f} {g} {h} {i} {j} {k} {l} {m} {n} {o} {p} {q} {r} {s}");
+}
+
+#[no_mangle]
+pub extern "C" fn cast_u8_u32(x: u8) -> u32 {
+ x as u32
+}
+
+#[no_mangle]
+pub extern "C" fn cast_u32_u8(x: u32) -> u8 {
+ x as u8
+}
+
+#[no_mangle]
+pub extern "C" fn add_many_u16(
+ a: u16,
+ b: u16,
+ c: u16,
+ d: u16,
+ e: u16,
+ f: u16,
+ g: u16,
+ h: u16,
+ i: u16,
+ j: u16,
+ k: u16,
+ l: u16,
+ m: u16,
+) -> u16 {
+ a + b + c + d + e + f + g + h + i + j + k + l + m
+}
+
// FFI performance helper functions
#[no_mangle]
pub extern "C" fn nop() {}
diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs
index 88da8a0b9..6b4853453 100644
--- a/test_ffi/tests/integration_tests.rs
+++ b/test_ffi/tests/integration_tests.rs
@@ -80,6 +80,10 @@ fn basic() {
579.912\n\
true\n\
false\n\
+ 579.9119873046875\n\
+ 579.9119873046875\n\
+ 579.912\n\
+ 579.912\n\
579\n\
8589934590\n\
-8589934590\n\
@@ -105,6 +109,14 @@ fn basic() {
buf: [1, 2, 3, 4, 5, 6, 7, 8]\n\
logCallback\n\
30\n\
+ 255 65535 4294967295 4294967296 123.456 789.876 -1 -2 -3 -4 -1000 1000 12345.67891 12345.679 12345.67891 12345.679 12345.67891 12345.679 12345.67891\n\
+ 255 65535 4294967295 4294967296 123.456 789.876 -1 -2 -3 -4 -1000 1000 12345.67891 12345.679 12345.67891 12345.679 12345.67891 12345.679 12345.67891\n\
+ 0\n\
+ 0\n\
+ 0\n\
+ 0\n\
+ 78\n\
+ 78\n\
STORED_FUNCTION cleared\n\
STORED_FUNCTION_2 cleared\n\
Thread safe call counter: 0\n\
@@ -120,6 +132,8 @@ fn basic() {
uint32Array[0]: 42\n\
uint32Array[0] after mutation: 55\n\
Static ptr value after mutation: 55\n\
+ 2264956937\n\
+ 2264956937\n\
Correct number of resources\n";
assert_eq!(stdout, expected);
assert_eq!(stderr, "");
diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js
index f71f23adc..6bf3c47f8 100644
--- a/test_ffi/tests/test.js
+++ b/test_ffi/tests/test.js
@@ -6,6 +6,7 @@
import { assertEquals } from "https://deno.land/std@0.149.0/testing/asserts.ts";
import {
assertThrows,
+ assert,
} from "../../test_util/std/testing/asserts.ts";
const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
@@ -175,6 +176,22 @@ const dylib = Deno.dlopen(libPath, {
result: "void",
callback: true,
},
+ log_many_parameters: {
+ parameters: ["u8", "u16", "u32", "u64", "f64", "f32", "i64", "i32", "i16", "i8", "isize", "usize", "f64", "f32", "f64", "f32", "f64", "f32", "f64"],
+ result: "void",
+ },
+ cast_u8_u32: {
+ parameters: ["u8"],
+ result: "u32",
+ },
+ cast_u32_u8: {
+ parameters: ["u32"],
+ result: "u8",
+ },
+ add_many_u16: {
+ parameters: ["u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16"],
+ result: "u16",
+ },
// Statics
"static_u32": {
type: "u32",
@@ -191,6 +208,7 @@ const dylib = Deno.dlopen(libPath, {
"static_char": {
type: "pointer",
},
+ "hash": { parameters: ["buffer", "u32"], result: "u32" },
});
const { symbols } = dylib;
@@ -210,11 +228,7 @@ function returnBuffer() { return return_buffer(); };
returnBuffer();
%OptimizeFunctionOnNextCall(returnBuffer);
const ptr0 = returnBuffer();
-
-const status = %GetOptimizationStatus(returnBuffer);
-if (!(status & (1 << 4))) {
- throw new Error("returnBuffer is not optimized");
-}
+assertIsOptimized(returnBuffer);
dylib.symbols.print_pointer(ptr0, 8);
const ptrView = new Deno.UnsafePointerView(ptr0);
@@ -266,17 +280,10 @@ const { add_u32, add_usize_fast } = symbols;
function addU32Fast(a, b) {
return add_u32(a, b);
};
-
-%PrepareFunctionForOptimization(addU32Fast);
-console.log(addU32Fast(123, 456));
-%OptimizeFunctionOnNextCall(addU32Fast);
-console.log(addU32Fast(123, 456));
+testOptimized(addU32Fast, () => addU32Fast(123, 456));
function addU64Fast(a, b) { return add_usize_fast(a, b); };
-%PrepareFunctionForOptimization(addU64Fast);
-console.log(addU64Fast(2, 3));
-%OptimizeFunctionOnNextCall(addU64Fast);
-console.log(addU64Fast(2, 3));
+testOptimized(addU64Fast, () => addU64Fast(2, 3));
console.log(dylib.symbols.add_i32(123, 456));
console.log(dylib.symbols.add_u64(0xffffffffn, 0xffffffffn));
@@ -294,6 +301,16 @@ console.log(dylib.symbols.add_f64(123.123, 456.789));
console.log(dylib.symbols.and(true, true));
console.log(dylib.symbols.and(true, false));
+function addF32Fast(a, b) {
+ return dylib.symbols.add_f32(a, b);
+};
+testOptimized(addF32Fast, () => addF32Fast(123.123, 456.789));
+
+function addF64Fast(a, b) {
+ return dylib.symbols.add_f64(a, b);
+};
+testOptimized(addF64Fast, () => addF64Fast(123.123, 456.789));
+
// Test adders as nonblocking calls
console.log(await dylib.symbols.add_i32_nonblocking(123, 456));
console.log(await dylib.symbols.add_u64_nonblocking(0xffffffffn, 0xffffffffn));
@@ -437,6 +454,39 @@ call_stored_function();
dylib.symbols.store_function_2(add10Callback.pointer);
dylib.symbols.call_stored_function_2(20);
+function logManyParametersFast(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) {
+ return symbols.log_many_parameters(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s);
+};
+testOptimized(
+ logManyParametersFast,
+ () => logManyParametersFast(
+ 255, 65535, 4294967295, 4294967296, 123.456, 789.876, -1, -2, -3, -4, -1000, 1000,
+ 12345.678910, 12345.678910, 12345.678910, 12345.678910, 12345.678910, 12345.678910, 12345.678910
+ )
+);
+
+// Some ABIs rely on the convention to zero/sign-extend arguments by the caller to optimize the callee function.
+// If the trampoline did not zero/sign-extend arguments, this would return 256 instead of the expected 0 (in optimized builds)
+function castU8U32Fast(x) { return symbols.cast_u8_u32(x); };
+testOptimized(castU8U32Fast, () => castU8U32Fast(256));
+
+// Some ABIs rely on the convention to expect garbage in the bits beyond the size of the return value to optimize the callee function.
+// If the trampoline did not zero/sign-extend the return value, this would return 256 instead of the expected 0 (in optimized builds)
+function castU32U8Fast(x) { return symbols.cast_u32_u8(x); };
+testOptimized(castU32U8Fast, () => castU32U8Fast(256));
+
+// Generally the trampoline tail-calls into the FFI function, but in certain cases (e.g. when returning 8 or 16 bit integers)
+// the tail call is not possible and a new stack frame must be created. We need enough parameters to have some on the stack
+function addManyU16Fast(a, b, c, d, e, f, g, h, i, j, k, l, m) {
+ return symbols.add_many_u16(a, b, c, d, e, f, g, h, i, j, k, l, m);
+};
+// N.B. V8 does not currently follow Aarch64 Apple's calling convention.
+// The current implementation of the JIT trampoline follows the V8 incorrect calling convention. This test covers the use-case
+// and is expected to fail once Deno uses a V8 version with the bug fixed.
+// The V8 bug is being tracked in https://bugs.chromium.org/p/v8/issues/detail?id=13171
+testOptimized(addManyU16Fast, () => addManyU16Fast(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
+
+
const nestedCallback = new Deno.UnsafeCallback(
{ parameters: [], result: "void" },
() => {
@@ -502,6 +552,12 @@ try {
console.log("Invalid UTF-8 characters to `v8::String`:", charView.getCString());
}
+
+const bytes = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+function hash() { return dylib.symbols.hash(bytes, bytes.byteLength); };
+
+testOptimized(hash, () => hash());
+
(function cleanup() {
dylib.close();
throwCallback.close();
@@ -526,4 +582,23 @@ After: ${postStr}`,
}
console.log("Correct number of resources");
-})(); \ No newline at end of file
+})();
+
+function assertIsOptimized(fn) {
+ const status = % GetOptimizationStatus(fn);
+ assert(status & (1 << 4), `expected ${fn.name} to be optimized, but wasn't`);
+}
+
+function testOptimized(fn, callback) {
+ %PrepareFunctionForOptimization(fn);
+ const r1 = callback();
+ if (r1 !== undefined) {
+ console.log(r1);
+ }
+ %OptimizeFunctionOnNextCall(fn);
+ const r2 = callback();
+ if (r2 !== undefined) {
+ console.log(r2);
+ }
+ assertIsOptimized(fn);
+} \ No newline at end of file