Reland "[fastcall] Add fast API testing facilities to d8"
This is a reland of 9eba2d85f4
.
The reland fixes a global state variable which was incompatible
with the --isolate flag in d8, which runs the same script in a
different isolate.
Original change's description:
> [fastcall] Add fast API testing facilities to d8
>
> This CL provides the minimum necessary functionality to expose fast API
> for testing in mjsunit, exposing the fast path for fuzzing. It exposes
> a d8.test.fast_c_api with an `add_all` method, which exercises primitive
> types. On x64, all integer and floating point types are supported. On
> other platforms currently only 32-bit integers are included in the test.
>
> Design doc:
> https://docs.google.com/document/d/1KUKPfXkSRZTA2gMwaWbpQKlYfw0C-T6AE3XzC4viHbo/
>
> Bug: chromium:1052746
> Change-Id: Icc824199a26dd2abd2b869f5483a39d38e4dce3e
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2749154
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
> Commit-Queue: Maya Lekova <mslekova@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#73670}
Bug: chromium:1052746
Change-Id: I33b265b97bf7c797eee7d4cce5066999358a8c66
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2790174
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73801}
This commit is contained in:
parent
501482cbc7
commit
7a17ddf4c0
1
BUILD.gn
1
BUILD.gn
@ -5495,6 +5495,7 @@ v8_executable("d8") {
|
||||
"src/d8/d8-js.cc",
|
||||
"src/d8/d8-platforms.cc",
|
||||
"src/d8/d8-platforms.h",
|
||||
"src/d8/d8-test.cc",
|
||||
"src/d8/d8.cc",
|
||||
"src/d8/d8.h",
|
||||
]
|
||||
|
@ -187,6 +187,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "v8config.h" // NOLINT(build/include_directory)
|
||||
|
222
src/d8/d8-test.cc
Normal file
222
src/d8/d8-test.cc
Normal file
@ -0,0 +1,222 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#include "src/d8/d8.h"
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
|
||||
// This file exposes a d8.test.fast_c_api object, which adds testing facility
|
||||
// for writing mjsunit tests that exercise fast API calls. The fast_c_api object
|
||||
// contains an `add_all` method with the following signature:
|
||||
// double add_all(bool /*should_fallback*/, int32_t, uint32_t,
|
||||
// int64_t, uint64_t, float, double), that is wired as a "fast API" method.
|
||||
// The fast_c_api object also supports querying the number of fast/slow calls
|
||||
// and resetting these counters.
|
||||
|
||||
// Make sure to sync the following with src/compiler/globals.h.
|
||||
#if defined(V8_TARGET_ARCH_X64)
|
||||
#define V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
|
||||
#endif
|
||||
|
||||
namespace v8 {
|
||||
namespace {
|
||||
class FastCApiObject {
|
||||
public:
|
||||
static double AddAllFastCallback(ApiObject receiver, bool should_fallback,
|
||||
int32_t arg_i32, uint32_t arg_u32,
|
||||
int64_t arg_i64, uint64_t arg_u64,
|
||||
float arg_f32, double arg_f64,
|
||||
FastApiCallbackOptions& options) {
|
||||
Value* receiver_value = reinterpret_cast<Value*>(&receiver);
|
||||
CHECK(receiver_value->IsObject());
|
||||
FastCApiObject* self = UnwrapObject(Object::Cast(receiver_value));
|
||||
self->fast_call_count_++;
|
||||
|
||||
if (should_fallback) {
|
||||
options.fallback = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return static_cast<double>(arg_i32) + static_cast<double>(arg_u32) +
|
||||
static_cast<double>(arg_i64) + static_cast<double>(arg_u64) +
|
||||
static_cast<double>(arg_f32) + arg_f64;
|
||||
}
|
||||
static void AddAllSlowCallback(const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
FastCApiObject* self = UnwrapObject(*args.This());
|
||||
self->slow_call_count_++;
|
||||
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
double sum = 0;
|
||||
if (args.Length() > 1) {
|
||||
sum += args[1]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||||
}
|
||||
if (args.Length() > 2) {
|
||||
sum += args[2]->Uint32Value(isolate->GetCurrentContext()).FromJust();
|
||||
}
|
||||
if (args.Length() > 3) {
|
||||
sum += args[3]->IntegerValue(isolate->GetCurrentContext()).FromJust();
|
||||
}
|
||||
if (args.Length() > 4) {
|
||||
sum += args[4]->IntegerValue(isolate->GetCurrentContext()).FromJust();
|
||||
}
|
||||
if (args.Length() > 5) {
|
||||
sum += args[5]->NumberValue(isolate->GetCurrentContext()).FromJust();
|
||||
} else {
|
||||
sum += std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
if (args.Length() > 6) {
|
||||
sum += args[6]->NumberValue(isolate->GetCurrentContext()).FromJust();
|
||||
} else {
|
||||
sum += std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(Number::New(isolate, sum));
|
||||
}
|
||||
|
||||
static int Add32BitIntFastCallback(ApiObject receiver, bool should_fallback,
|
||||
int32_t arg_i32, uint32_t arg_u32,
|
||||
FastApiCallbackOptions& options) {
|
||||
Value* receiver_value = reinterpret_cast<Value*>(&receiver);
|
||||
CHECK(receiver_value->IsObject());
|
||||
FastCApiObject* self = UnwrapObject(Object::Cast(receiver_value));
|
||||
self->fast_call_count_++;
|
||||
|
||||
if (should_fallback) {
|
||||
options.fallback = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return arg_i32 + arg_u32;
|
||||
}
|
||||
static void Add32BitIntSlowCallback(const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
FastCApiObject* self = UnwrapObject(*args.This());
|
||||
self->slow_call_count_++;
|
||||
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
double sum = 0;
|
||||
if (args.Length() > 1) {
|
||||
sum += args[1]->Int32Value(isolate->GetCurrentContext()).FromJust();
|
||||
}
|
||||
if (args.Length() > 2) {
|
||||
sum += args[2]->Uint32Value(isolate->GetCurrentContext()).FromJust();
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(Number::New(isolate, sum));
|
||||
}
|
||||
|
||||
static void FastCallCount(const FunctionCallbackInfo<Value>& args) {
|
||||
FastCApiObject* self = UnwrapObject(*args.This());
|
||||
args.GetReturnValue().Set(
|
||||
Number::New(args.GetIsolate(), self->fast_call_count()));
|
||||
}
|
||||
static void SlowCallCount(const FunctionCallbackInfo<Value>& args) {
|
||||
FastCApiObject* self = UnwrapObject(*args.This());
|
||||
args.GetReturnValue().Set(
|
||||
Number::New(args.GetIsolate(), self->slow_call_count()));
|
||||
}
|
||||
static void ResetCounts(const FunctionCallbackInfo<Value>& args) {
|
||||
FastCApiObject* self = UnwrapObject(*args.This());
|
||||
self->reset_counts();
|
||||
args.GetReturnValue().Set(Undefined(args.GetIsolate()));
|
||||
}
|
||||
static void SupportsFPParams(const FunctionCallbackInfo<Value>& info) {
|
||||
FastCApiObject* self = UnwrapObject(*info.This());
|
||||
info.GetReturnValue().Set(self->supports_fp_params_);
|
||||
}
|
||||
|
||||
int fast_call_count() const { return fast_call_count_; }
|
||||
int slow_call_count() const { return slow_call_count_; }
|
||||
void reset_counts() {
|
||||
fast_call_count_ = 0;
|
||||
slow_call_count_ = 0;
|
||||
}
|
||||
|
||||
static const int kV8WrapperObjectIndex = 1;
|
||||
|
||||
private:
|
||||
static FastCApiObject* UnwrapObject(Object* object) {
|
||||
i::Address addr = *reinterpret_cast<i::Address*>(object);
|
||||
auto instance_type = i::Internals::GetInstanceType(addr);
|
||||
if (instance_type != i::Internals::kJSObjectType &&
|
||||
instance_type != i::Internals::kJSApiObjectType &&
|
||||
instance_type != i::Internals::kJSSpecialApiObjectType) {
|
||||
return nullptr;
|
||||
}
|
||||
FastCApiObject* wrapped = reinterpret_cast<FastCApiObject*>(
|
||||
object->GetAlignedPointerFromInternalField(kV8WrapperObjectIndex));
|
||||
CHECK_NOT_NULL(wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
int fast_call_count_ = 0, slow_call_count_ = 0;
|
||||
#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
|
||||
bool supports_fp_params_ = true;
|
||||
#else // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
|
||||
bool supports_fp_params_ = false;
|
||||
#endif // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
|
||||
};
|
||||
|
||||
// The object is statically initialized for simplicity, typically the embedder
|
||||
// will take care of managing their C++ objects lifetime.
|
||||
thread_local FastCApiObject kFastCApiObject;
|
||||
} // namespace
|
||||
|
||||
void CreateObject(const FunctionCallbackInfo<Value>& info) {
|
||||
Local<Object> api_object = info.Holder();
|
||||
api_object->SetAlignedPointerInInternalField(
|
||||
FastCApiObject::kV8WrapperObjectIndex,
|
||||
reinterpret_cast<void*>(&kFastCApiObject));
|
||||
api_object->SetAccessorProperty(
|
||||
String::NewFromUtf8Literal(info.GetIsolate(), "supports_fp_params"),
|
||||
FunctionTemplate::New(info.GetIsolate(), FastCApiObject::SupportsFPParams)
|
||||
->GetFunction(api_object->GetCreationContext().ToLocalChecked())
|
||||
.ToLocalChecked());
|
||||
}
|
||||
|
||||
Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
|
||||
Local<FunctionTemplate> api_obj_ctor =
|
||||
FunctionTemplate::New(isolate, CreateObject);
|
||||
Local<Signature> signature = Signature::New(isolate, api_obj_ctor);
|
||||
{
|
||||
CFunction add_all_c_func =
|
||||
CFunction::Make(FastCApiObject::AddAllFastCallback);
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "add_all",
|
||||
FunctionTemplate::New(isolate, FastCApiObject::AddAllSlowCallback,
|
||||
Local<Value>(), signature, 1,
|
||||
ConstructorBehavior::kThrow,
|
||||
SideEffectType::kHasSideEffect, &add_all_c_func));
|
||||
CFunction add_32bit_int_c_func =
|
||||
CFunction::Make(FastCApiObject::Add32BitIntFastCallback);
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "add_32bit_int",
|
||||
FunctionTemplate::New(
|
||||
isolate, FastCApiObject::Add32BitIntSlowCallback, Local<Value>(),
|
||||
signature, 1, ConstructorBehavior::kThrow,
|
||||
SideEffectType::kHasSideEffect, &add_32bit_int_c_func));
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "fast_call_count",
|
||||
FunctionTemplate::New(isolate, FastCApiObject::FastCallCount,
|
||||
Local<Value>(), signature));
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "slow_call_count",
|
||||
FunctionTemplate::New(isolate, FastCApiObject::SlowCallCount,
|
||||
Local<Value>(), signature));
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "reset_counts",
|
||||
FunctionTemplate::New(isolate, FastCApiObject::ResetCounts,
|
||||
Local<Value>(), signature));
|
||||
}
|
||||
api_obj_ctor->InstanceTemplate()->SetInternalFieldCount(
|
||||
FastCApiObject::kV8WrapperObjectIndex + 1);
|
||||
|
||||
return api_obj_ctor;
|
||||
}
|
||||
|
||||
} // namespace v8
|
@ -2662,6 +2662,7 @@ Local<ObjectTemplate> Shell::CreateTestRunnerTemplate(Isolate* isolate) {
|
||||
// installed on the global object can be hidden with the --omit-quit flag
|
||||
// (e.g. on asan bots).
|
||||
test_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit));
|
||||
|
||||
return test_template;
|
||||
}
|
||||
|
||||
@ -2717,6 +2718,11 @@ Local<ObjectTemplate> Shell::CreateD8Template(Isolate* isolate) {
|
||||
test_template->Set(
|
||||
isolate, "verifySourcePositions",
|
||||
FunctionTemplate::New(isolate, TestVerifySourcePositions));
|
||||
if (i::FLAG_turbo_fast_api_calls) {
|
||||
test_template->Set(isolate, "fast_c_api",
|
||||
Shell::CreateTestFastCApiTemplate(isolate));
|
||||
}
|
||||
|
||||
d8_template->Set(isolate, "test", test_template);
|
||||
}
|
||||
return d8_template;
|
||||
|
@ -632,6 +632,7 @@ class Shell : public i::AllStatic {
|
||||
static Local<ObjectTemplate> CreatePerformanceTemplate(Isolate* isolate);
|
||||
static Local<ObjectTemplate> CreateRealmTemplate(Isolate* isolate);
|
||||
static Local<ObjectTemplate> CreateD8Template(Isolate* isolate);
|
||||
static Local<FunctionTemplate> CreateTestFastCApiTemplate(Isolate* isolate);
|
||||
|
||||
static MaybeLocal<Context> CreateRealm(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args, int index,
|
||||
|
146
test/mjsunit/compiler/fast-api-calls.js
Normal file
146
test/mjsunit/compiler/fast-api-calls.js
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright 2021 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 --allow-natives-syntax --opt
|
||||
// --always-opt is disabled because we rely on particular feedback for
|
||||
// optimizing to the fastest path.
|
||||
// Flags: --no-always-opt
|
||||
const fast_c_api = new d8.test.fast_c_api();
|
||||
|
||||
// ----------- add_all -----------
|
||||
// `add_all` has the following signature:
|
||||
// double add_all(bool /*should_fallback*/, int32_t, uint32_t,
|
||||
// int64_t, uint64_t, float, double)
|
||||
|
||||
const max_safe_float = 2**24 - 1;
|
||||
const add_all_result = -42 + 45 + Number.MIN_SAFE_INTEGER + Number.MAX_SAFE_INTEGER +
|
||||
max_safe_float * 0.5 + Math.PI;
|
||||
|
||||
function add_all(should_fallback = false) {
|
||||
return fast_c_api.add_all(should_fallback,
|
||||
-42, 45, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER,
|
||||
max_safe_float * 0.5, Math.PI);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(add_all);
|
||||
assertEquals(add_all_result, add_all());
|
||||
%OptimizeFunctionOnNextCall(add_all);
|
||||
|
||||
if (fast_c_api.supports_fp_params) {
|
||||
// Test that regular call hits the fast path.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_all_result, add_all());
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(0, fast_c_api.slow_call_count());
|
||||
|
||||
// Test fallback to slow path.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_all_result, add_all(true));
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(1, fast_c_api.slow_call_count());
|
||||
|
||||
// Test that no fallback hits the fast path again.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_all_result, add_all());
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(0, fast_c_api.slow_call_count());
|
||||
} else {
|
||||
// Test that calling with unsupported types hits the slow path.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_all_result, add_all());
|
||||
assertEquals(0, fast_c_api.fast_call_count());
|
||||
assertEquals(1, fast_c_api.slow_call_count());
|
||||
}
|
||||
|
||||
// ----------- Test add_all signature mismatche -----------
|
||||
function add_all_mismatch() {
|
||||
return fast_c_api.add_all(false /*should_fallback*/,
|
||||
45, -42, Number.MAX_SAFE_INTEGER, max_safe_float * 0.5,
|
||||
Number.MIN_SAFE_INTEGER, Math.PI);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(add_all_mismatch);
|
||||
const add_all_mismatch_result = add_all_mismatch();
|
||||
%OptimizeFunctionOnNextCall(add_all_mismatch);
|
||||
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_all_mismatch_result, add_all_mismatch());
|
||||
assertEquals(1, fast_c_api.slow_call_count());
|
||||
assertEquals(0, fast_c_api.fast_call_count());
|
||||
// If the function was ever optimized to the fast path, it should
|
||||
// have been deoptimized due to the argument types mismatch. If it
|
||||
// wasn't optimized due to lack of support for FP params, it will
|
||||
// stay optimized.
|
||||
if (fast_c_api.supports_fp_params) {
|
||||
assertUnoptimized(add_all_mismatch);
|
||||
}
|
||||
|
||||
// ----------- add_32bit_int -----------
|
||||
// `add_32bit_int` has the following signature:
|
||||
// int add_32bit_int(bool /*should_fallback*/, int32_t, uint32_t)
|
||||
|
||||
const add_32bit_int_result = -42 + 45;
|
||||
|
||||
function add_32bit_int(should_fallback = false) {
|
||||
return fast_c_api.add_32bit_int(should_fallback, -42, 45);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(add_32bit_int);
|
||||
assertEquals(add_32bit_int_result, add_32bit_int());
|
||||
%OptimizeFunctionOnNextCall(add_32bit_int);
|
||||
|
||||
// Test that regular call hits the fast path.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_32bit_int_result, add_32bit_int());
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(0, fast_c_api.slow_call_count());
|
||||
|
||||
// Test fallback to slow path.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_32bit_int_result, add_32bit_int(true));
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(1, fast_c_api.slow_call_count());
|
||||
|
||||
// Test that no fallback hits the fast path again.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_32bit_int_result, add_32bit_int());
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(0, fast_c_api.slow_call_count());
|
||||
|
||||
// ----------- Test various signature mismatches -----------
|
||||
function add_32bit_int_mismatch(arg0, arg1, arg2, arg3) {
|
||||
return fast_c_api.add_32bit_int(arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(add_32bit_int_mismatch);
|
||||
assertEquals(add_32bit_int_result, add_32bit_int_mismatch(false, -42, 45));
|
||||
%OptimizeFunctionOnNextCall(add_32bit_int_mismatch);
|
||||
|
||||
// Test that passing extra argument stays on the fast path.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_32bit_int_result, add_32bit_int_mismatch(false, -42, 45, -42));
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
|
||||
// Test that passing wrong argument types stays on the fast path.
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(Math.round(-42 + 3.14), add_32bit_int_mismatch(false, -42, 3.14));
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
|
||||
// Test that passing too few argument falls down the slow path,
|
||||
// because it's an argument type mismatch (undefined vs. int).
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(-42, add_32bit_int_mismatch(false, -42));
|
||||
assertEquals(1, fast_c_api.slow_call_count());
|
||||
assertEquals(0, fast_c_api.fast_call_count());
|
||||
assertUnoptimized(add_32bit_int_mismatch);
|
||||
|
||||
// Test that the function can be optimized again.
|
||||
%PrepareFunctionForOptimization(add_32bit_int_mismatch);
|
||||
%OptimizeFunctionOnNextCall(add_32bit_int_mismatch);
|
||||
fast_c_api.reset_counts();
|
||||
assertEquals(add_32bit_int_result, add_32bit_int_mismatch(false, -42, 45));
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
@ -351,6 +351,9 @@
|
||||
'regexp-tier-up-multiple': [SKIP],
|
||||
'regress/regress-996234': [SKIP],
|
||||
|
||||
# This test relies on TurboFan being enabled.
|
||||
'compiler/fast-api-calls': [SKIP],
|
||||
|
||||
# These tests check that we can trace the compiler.
|
||||
'tools/compiler-trace-flags': [SKIP],
|
||||
|
||||
@ -1316,6 +1319,13 @@
|
||||
'compiler/number-toboolean': [SKIP],
|
||||
}], # variant == assert_types
|
||||
|
||||
##############################################################################
|
||||
['variant == stress_snapshot', {
|
||||
# This test initializes an embedder object that never needs to be serialized
|
||||
# to the snapshot, so we don't have a SerializeInternalFieldsCallback for it.
|
||||
'compiler/fast-api-calls': [SKIP],
|
||||
}], # variant == stress_snapshot
|
||||
|
||||
##############################################################################
|
||||
['variant == stress_snapshot and arch != x64', {
|
||||
# Deserialization fails due to read-only snapshot checksum verification.
|
||||
|
Loading…
Reference in New Issue
Block a user