diff options
Diffstat (limited to 'test_ffi')
-rw-r--r-- | test_ffi/Cargo.toml | 1 | ||||
-rw-r--r-- | test_ffi/src/lib.rs | 54 | ||||
-rw-r--r-- | test_ffi/tests/integration_tests.rs | 14 | ||||
-rw-r--r-- | test_ffi/tests/test.js | 105 |
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 |