Commit cf84dd04 authored by Maya Lekova's avatar Maya Lekova Committed by Commit Bot

[turbofan] Add docs and tests for fast calls with fallback

Bug: chromium:1052746
Change-Id: I6c1f888ed9a7f27d43872e24f8d8cf353a103f1a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2461740
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70479}
parent a0f4b396
......@@ -18,6 +18,38 @@
* &v8::CFunction::Make(FastMethod));
* \endcode
*
* By design, fast calls are limited by the following requirements, which
* the embedder should enforce themselves:
* - they should not allocate on the JS heap;
* - they should not trigger JS execution.
* To enforce them, the embedder could use the existing
* v8::Isolate::DisallowJavascriptExecutionScope and a utility similar to
* Blink's NoAllocationScope:
* https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/heap/thread_state_scopes.h;l=16
*
* Due to these limitations, it's not directly possible to report errors by
* throwing a JS exception or to otherwise do an allocation. There is an
* alternative way of creating fast calls that supports falling back to the
* slow call and then performing the necessary allocation. When one creates
* the fast method by using CFunction::MakeWithFallbackSupport instead of
* CFunction::Make, the fast callback gets as last parameter an output variable,
* through which it can request falling back to the slow call. So one might
* declare their method like:
*
* \code
* void FastMethodWithFallback(int param, bool* fallback);
* \endcode
*
* If the callback wants to signal an error condition or to perform an
* allocation, it must set *fallback to true and do an early return from
* the fast method. Then V8 checks the value of *fallback and if it's true,
* falls back to executing the SlowCallback, which is capable of
* reporting the error (either by throwing a JS exception or logging to the
* console) or doing the allocation. It's the embedder's responsibility to
* ensure that the fast callback is idempotent up to the point where error
* and fallback conditions are checked, because otherwise executing the slow
* callback might produce visible side-effects twice.
*
* An example for custom embedder type support might employ a way to wrap/
* unwrap various C++ types in JSObject instances, e.g:
*
......@@ -299,14 +331,14 @@ struct GetCType<T*> : public GetCTypePointerImpl<T> {};
template <typename R, bool RaisesException, typename... Args>
class CFunctionInfoImpl : public CFunctionInfo {
public:
static constexpr int kHasErrorArgCount = (RaisesException ? 1 : 0);
static constexpr int kFallbackArgCount = (RaisesException ? 1 : 0);
static constexpr int kReceiverCount = 1;
CFunctionInfoImpl()
: return_info_(internal::GetCType<R>::Get()),
arg_count_(sizeof...(Args) - kHasErrorArgCount),
arg_count_(sizeof...(Args) - kFallbackArgCount),
arg_info_{internal::GetCType<Args>::Get()...} {
static_assert(sizeof...(Args) >= kHasErrorArgCount + kReceiverCount,
"The receiver or the has_error argument is missing.");
static_assert(sizeof...(Args) >= kFallbackArgCount + kReceiverCount,
"The receiver or the fallback argument is missing.");
static_assert(
internal::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid,
"Only void return types are currently supported.");
......@@ -350,8 +382,8 @@ class V8_EXPORT CFunction {
}
template <typename F>
static CFunction MakeWithErrorSupport(F* func) {
return ArgUnwrap<F*>::MakeWithErrorSupport(func);
static CFunction MakeWithFallbackSupport(F* func) {
return ArgUnwrap<F*>::MakeWithFallbackSupport(func);
}
template <typename F>
......@@ -384,7 +416,7 @@ class V8_EXPORT CFunction {
return CFunction(reinterpret_cast<const void*>(func),
GetCFunctionInfo<R, false, Args...>());
}
static CFunction MakeWithErrorSupport(R (*func)(Args...)) {
static CFunction MakeWithFallbackSupport(R (*func)(Args...)) {
return CFunction(reinterpret_cast<const void*>(func),
GetCFunctionInfo<R, true, Args...>());
}
......
......@@ -27683,7 +27683,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
v8::CFunction c_func;
if (supports_fallback) {
c_func = v8::CFunction::MakeWithErrorSupport(
c_func = v8::CFunction::MakeWithFallbackSupport(
BasicApiChecker<Value, Impl>::FastCallback);
} else {
c_func = v8::CFunction::Make(
......@@ -27753,16 +27753,12 @@ void CheckEqual<double>(double actual, double expected) {
}
template <typename T>
void CallAndCheck(T expected_value, Behavior expected_behavior,
ApiCheckerResultFlags expected_path,
v8::Local<v8::Value> initial_value,
Behavior raise_exception = Behavior::kNoException) {
void CallAndCheck(
T expected_value, Behavior expected_behavior,
ApiCheckerResultFlags expected_path, v8::Local<v8::Value> initial_value,
Behavior raise_exception = Behavior::kNoException,
FallbackPolicy write_to_fallback = FallbackPolicy::kDontRequestFallback) {
LocalContext env;
// The default embedder behaviour in case of exception should be
// to write to the fallback variable.
FallbackPolicy write_to_fallback = (raise_exception == Behavior::kException)
? FallbackPolicy::kRequestFallback
: FallbackPolicy::kDontRequestFallback;
ApiNumberChecker<T> checker(expected_value, raise_exception,
write_to_fallback);
......@@ -28379,16 +28375,22 @@ TEST(FastApiCalls) {
CheckDynamicTypeInfo();
// Fallback to slow call
// Fallback to slow call and throw an exception.
CallAndCheck<int32_t>(
42, Behavior::kException,
ApiCheckerResult::kFastCalled | ApiCheckerResult::kSlowCalled, v8_num(42),
Behavior::kException);
Behavior::kException, FallbackPolicy::kRequestFallback);
// Doesn't fallback to slow call
CallAndCheck<int32_t>(42, Behavior::kNoException,
ApiCheckerResult::kFastCalled, v8_num(42),
Behavior::kNoException);
// Fallback to slow call and don't throw an exception.
CallAndCheck<int32_t>(
42, Behavior::kNoException,
ApiCheckerResult::kFastCalled | ApiCheckerResult::kSlowCalled, v8_num(42),
Behavior::kNoException, FallbackPolicy::kRequestFallback);
// Doesn't fallback to slow call, so don't throw an exception.
CallAndCheck<int32_t>(
42, Behavior::kNoException, ApiCheckerResult::kFastCalled, v8_num(42),
Behavior::kNoException, FallbackPolicy::kDontRequestFallback);
// Wrong number of arguments
CallWithLessArguments();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment