[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: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#70479}
This commit is contained in:
parent
a0f4b396af
commit
cf84dd04a3
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user