diff options
Diffstat (limited to 'test_ffi/tests/test.js')
-rw-r--r-- | test_ffi/tests/test.js | 105 |
1 files changed, 90 insertions, 15 deletions
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 |