v8/test/mjsunit/compiler/fast-api-calls-pointer.js
Aapo Alasuutari 8d5630c7d6 [fastcall] Fix test code crash in ExternalPointerTable::RelaxedLoad
Bug: chromium:1395617, chromium:1395521
Change-Id: I23355e14c879532c699084fff7d9d1fcf6489941
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4075578
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84677}
2022-12-06 09:52:29 +00:00

141 lines
5.1 KiB
JavaScript

// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file excercises basic fast API calls and enables fuzzing of this
// functionality.
// Flags: --turbo-fast-api-calls --expose-fast-api --allow-natives-syntax --turbofan
// --always-turbofan is disabled because we rely on particular feedback for
// optimizing to the fastest path.
// Flags: --no-always-turbofan
// The test relies on optimizing/deoptimizing at predictable moments, so
// it's not suitable for deoptimization fuzzing.
// Flags: --deopt-every-n-times=0
assertThrows(() => d8.test.FastCAPI());
const fast_c_api = new d8.test.FastCAPI();
// ---------- Test external pointer passing -----------
function reset_counts() {
return fast_c_api.reset_counts();
}
function fast_call_count() {
return fast_c_api.fast_call_count();
}
function slow_call_count() {
return fast_c_api.slow_call_count();
}
function assertIsExternal(pointer) {
return fast_c_api.assert_is_external(pointer);
}
function get_pointer_a() {
return fast_c_api.get_pointer();
}
function get_pointer_b() {
return fast_c_api.get_null_pointer();
}
function pass_pointer(pointer) {
return fast_c_api.pass_pointer(pointer);
}
function compare_pointers(pointer_a, pointer_b) {
return fast_c_api.compare_pointers(pointer_a, pointer_b);
}
%PrepareFunctionForOptimization(get_pointer_a);
reset_counts();
const external_a_slow = get_pointer_a();
const external_a_slow_clone = get_pointer_a();
assertEquals(slow_call_count(), 2);
assertEquals(fast_call_count(), 0);
assertIsExternal(external_a_slow);
assertIsExternal(external_a_slow_clone);
// Slow call that returns the same pointer from a new `External::New()`
// will still create a new / different object.
// Note that we cannot use `assertEquals(external_a_slow, external_a_slow_clone)`
// as it's a deep equality comparison and will return true for all empty object comparsions.
assertFalse(external_a_slow === external_a_slow_clone);
%PrepareFunctionForOptimization(pass_pointer);
reset_counts();
const external_a_slow_passed = pass_pointer(external_a_slow);
// If slow call returns the same External object, then object identity is
// preserved.
assertEquals(slow_call_count(), 1);
assertEquals(fast_call_count(), 0);
assertTrue(external_a_slow_passed === external_a_slow, true);
%OptimizeFunctionOnNextCall(pass_pointer);
const external_a_fast_passed = pass_pointer(external_a_slow);
assertEquals(slow_call_count(), 1);
assertEquals(fast_call_count(), 1);
assertIsExternal(external_a_slow);
assertIsExternal(external_a_fast_passed);
// Fast call always creates a new External object, as they cannot
// return the same External object given that they do not see it.
assertFalse(external_a_fast_passed === external_a_slow);
// An object that looks like an External is still not an External.
const emptyObject = Object.create(null);
// An object that internally carries a pointer is still not an External.
const alsoInternallyPointer = new Uint8Array();
assertThrows(() => pass_pointer(emptyObject));
assertThrows(() => pass_pointer(alsoInternallyPointer));
// Show off deep equality comparsions between various External objects and
// the empty object to show that all Externals work properly as objects.
assertEquals(external_a_slow, external_a_fast_passed);
assertEquals(external_a_fast_passed, emptyObject);
%OptimizeFunctionOnNextCall(get_pointer_a);
reset_counts();
const external_a_fast = get_pointer_a();
assertEquals(slow_call_count(), 0);
assertEquals(fast_call_count(), 1);
assertIsExternal(external_a_fast);
assertFalse(external_a_fast === external_a_slow);
%PrepareFunctionForOptimization(get_pointer_b);
fast_c_api.reset_counts();
const external_b_slow = get_pointer_b();
assertEquals(slow_call_count(), 1);
assertEquals(fast_call_count(), 0);
assertEquals(external_b_slow, null);
%OptimizeFunctionOnNextCall(get_pointer_b);
const external_b_fast = get_pointer_b();
assertEquals(slow_call_count(), 1);
assertEquals(fast_call_count(), 1);
assertEquals(external_b_fast, null);
const external_b_fast_passed = pass_pointer(external_b_slow);
assertEquals(external_b_fast_passed, null);
assertTrue(external_b_fast_passed === external_b_slow, true);
%PrepareFunctionForOptimization(compare_pointers);
assertUnoptimized(compare_pointers);
reset_counts();
assertFalse(compare_pointers(external_a_slow, external_b_slow));
assertEquals(slow_call_count(), 1);
assertEquals(fast_call_count(), 0);
%OptimizeFunctionOnNextCall(compare_pointers);
assertFalse(compare_pointers(external_a_slow, external_b_slow));
assertEquals(slow_call_count(), 1);
assertEquals(fast_call_count(), 1);
assertTrue(compare_pointers(external_a_slow, external_a_slow), true);
assertTrue(compare_pointers(external_a_slow, external_a_fast), true);
assertTrue(compare_pointers(external_b_slow, external_b_slow), true);
assertTrue(compare_pointers(external_b_slow, external_b_fast), true);
assertTrue(compare_pointers(external_b_slow, external_b_fast_passed), true);
// Assert that the ComparePointers call can safely be called with non-Externals
// and that it will throw an error instead of crashing.
assertThrows(() => compare_pointers(123, "foo"));