summaryrefslogtreecommitdiff
path: root/test_ffi/tests/test.js
diff options
context:
space:
mode:
Diffstat (limited to 'test_ffi/tests/test.js')
-rw-r--r--test_ffi/tests/test.js105
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