Skip to content

Commit

Permalink
deps: V8: backport d89d185f
Browse files Browse the repository at this point in the history
Original commit message:

    [fastcall] expose wasm memory to cfunction

    Load current Memory start/size off of the wasm instance when entering
    fast calls, so they can use that info for whatever they need to do.
    Fast calls from JS set the memory to null, and the memory does not
    need to be piped from wasm to slow callbacks as wasm always calls
    the fast function.

    Change-Id: Ibfa33cdd7dba85300f95cbdacc9a56b3f7181663
    Bug: chromium:1052746
    Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3719005
    Reviewed-by: Maya Lekova <mslekova@chromium.org>
    Commit-Queue: snek <snek@chromium.org>
    Reviewed-by: Manos Koukoutos <manoskouk@chromium.org>
    Reviewed-by: Toon Verwaest <verwaest@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#81538}
  • Loading branch information
devsnek committed Jul 8, 2022
1 parent 2dc1451 commit a016775
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 19 deletions.
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.9',
'v8_embedder_string': '-node.10',

##### V8 defaults for Node.js #####

Expand Down
7 changes: 6 additions & 1 deletion deps/v8/include/v8-fast-api-calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ struct FastApiCallbackOptions {
* returned instance may be filled with mock data.
*/
static FastApiCallbackOptions CreateForTesting(Isolate* isolate) {
return {false, {0}};
return {false, {0}, nullptr};
}

/**
Expand All @@ -568,6 +568,11 @@ struct FastApiCallbackOptions {
uintptr_t data_ptr;
v8::Value data;
};

/**
* When called from WebAssembly, a view of the calling module's memory.
*/
FastApiTypedArray<uint8_t>* const wasm_memory;
};

namespace internal {
Expand Down
9 changes: 9 additions & 0 deletions deps/v8/src/compiler/effect-control-linearizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5171,6 +5171,15 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
CheckForMinusZeroMode::kCheckForMinusZero);
}
},
// Initialize js-specific callback options.
[this](Node* options_stack_slot) {
__ Store(
StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
options_stack_slot,
static_cast<int>(offsetof(v8::FastApiCallbackOptions, wasm_memory)),
__ IntPtrConstant(0));
},
// Generate slow fallback if fast call fails
[this, node]() -> Node* { return GenerateSlowApiCall(node); });
}
Expand Down
15 changes: 11 additions & 4 deletions deps/v8/src/compiler/fast-api-calls.cc
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,14 @@ class FastApiCallBuilder {
GraphAssembler* graph_assembler,
const GetParameter& get_parameter,
const ConvertReturnValue& convert_return_value,
const InitializeOptions& initialize_options,
const GenerateSlowApiCall& generate_slow_api_call)
: isolate_(isolate),
graph_(graph),
graph_assembler_(graph_assembler),
get_parameter_(get_parameter),
convert_return_value_(convert_return_value),
initialize_options_(initialize_options),
generate_slow_api_call_(generate_slow_api_call) {}

Node* Build(const FastApiCallFunctionVector& c_functions,
Expand All @@ -150,6 +152,7 @@ class FastApiCallBuilder {
GraphAssembler* graph_assembler_;
const GetParameter& get_parameter_;
const ConvertReturnValue& convert_return_value_;
const InitializeOptions& initialize_options_;
const GenerateSlowApiCall& generate_slow_api_call_;
};

Expand Down Expand Up @@ -291,12 +294,12 @@ Node* FastApiCallBuilder::Build(const FastApiCallFunctionVector& c_functions,

Node* stack_slot = nullptr;
if (c_signature->HasOptions()) {
int kAlign = alignof(v8::FastApiCallbackOptions);
int kSize = sizeof(v8::FastApiCallbackOptions);
const int kAlign = alignof(v8::FastApiCallbackOptions);
const int kSize = sizeof(v8::FastApiCallbackOptions);
// If this check fails, you've probably added new fields to
// v8::FastApiCallbackOptions, which means you'll need to write code
// that initializes and reads from them too.
CHECK_EQ(kSize, sizeof(uintptr_t) * 2);
static_assert(kSize == sizeof(uintptr_t) * 3);
stack_slot = __ StackSlot(kSize, kAlign);

__ Store(
Expand All @@ -310,6 +313,8 @@ Node* FastApiCallBuilder::Build(const FastApiCallFunctionVector& c_functions,
static_cast<int>(offsetof(v8::FastApiCallbackOptions, data)),
data_argument);

initialize_options_(stack_slot);

builder.AddParam(MachineType::Pointer()); // stack_slot
}

Expand Down Expand Up @@ -366,9 +371,11 @@ Node* BuildFastApiCall(Isolate* isolate, Graph* graph,
const CFunctionInfo* c_signature, Node* data_argument,
const GetParameter& get_parameter,
const ConvertReturnValue& convert_return_value,
const InitializeOptions& initialize_options,
const GenerateSlowApiCall& generate_slow_api_call) {
FastApiCallBuilder builder(isolate, graph, graph_assembler, get_parameter,
convert_return_value, generate_slow_api_call);
convert_return_value, initialize_options,
generate_slow_api_call);
return builder.Build(c_functions, c_signature, data_argument);
}

Expand Down
2 changes: 2 additions & 0 deletions deps/v8/src/compiler/fast-api-calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ bool CanOptimizeFastSignature(const CFunctionInfo* c_signature);
using GetParameter = std::function<Node*(int, OverloadsResolutionResult&,
GraphAssemblerLabel<0>*)>;
using ConvertReturnValue = std::function<Node*(const CFunctionInfo*, Node*)>;
using InitializeOptions = std::function<void(Node*)>;
using GenerateSlowApiCall = std::function<Node*()>;

Node* BuildFastApiCall(Isolate* isolate, Graph* graph,
Expand All @@ -57,6 +58,7 @@ Node* BuildFastApiCall(Isolate* isolate, Graph* graph,
const CFunctionInfo* c_signature, Node* data_argument,
const GetParameter& get_parameter,
const ConvertReturnValue& convert_return_value,
const InitializeOptions& initialize_options,
const GenerateSlowApiCall& generate_slow_api_call);

} // namespace fast_api_call
Expand Down
38 changes: 35 additions & 3 deletions deps/v8/src/compiler/wasm-compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -560,9 +560,9 @@ void WasmGraphBuilder::Start(unsigned params) {
Param(Linkage::kJSCallClosureParamIndex, "%closure")));
break;
case kWasmApiFunctionRefMode:
// We need an instance node anyway, because FromJS() needs to pass it to
// the WasmIsValidRefValue runtime function.
instance_node_ = UndefinedValue();
instance_node_ = gasm_->Load(
MachineType::TaggedPointer(), Param(0),
wasm::ObjectAccess::ToTagged(WasmApiFunctionRef::kInstanceOffset));
break;
}
graph()->SetEnd(graph()->NewNode(mcgraph()->common()->End(0)));
Expand Down Expand Up @@ -7454,6 +7454,38 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
[](const CFunctionInfo* signature, Node* c_return_value) {
return c_return_value;
},
// Initialize wasm-specific callback options fields
[this](Node* options_stack_slot) {
#ifdef V8_SANDBOXED_POINTERS
Node* mem_start = LOAD_INSTANCE_FIELD_NO_ELIMINATION(
MemoryStart, MachineType::SandboxedPointer());
#else
Node* mem_start = LOAD_INSTANCE_FIELD_NO_ELIMINATION(
MemoryStart, MachineType::UintPtr());
#endif

Node* mem_size = LOAD_INSTANCE_FIELD_NO_ELIMINATION(
MemorySize, MachineType::UintPtr());

constexpr int kSize = sizeof(FastApiTypedArray<uint8_t>);
constexpr int kAlign = alignof(FastApiTypedArray<uint8_t>);

Node* stack_slot = gasm_->StackSlot(kSize, kAlign);

gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
stack_slot, 0, mem_size);
gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
stack_slot, sizeof(size_t), mem_start);

gasm_->Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
options_stack_slot,
static_cast<int>(
offsetof(v8::FastApiCallbackOptions, wasm_memory)),
stack_slot);
},
// Generate fallback slow call if fast call fails
[this, callable_node, native_context, receiver_node]() -> Node* {
int wasm_count = static_cast<int>(sig_->parameter_count());
Expand Down
35 changes: 35 additions & 0 deletions deps/v8/src/d8/d8-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ class FastCApiObject {
CHECK_SELF_OR_FALLBACK(0);
self->fast_call_count_++;

CHECK_NULL(options.wasm_memory);

if (should_fallback) {
options.fallback = true;
return 0;
Expand Down Expand Up @@ -598,6 +600,30 @@ class FastCApiObject {
args.GetReturnValue().Set(Boolean::New(isolate, result));
}

static bool TestWasmMemoryFastCallback(Local<Object> receiver,
uint32_t address,
FastApiCallbackOptions& options) {
FastCApiObject* self = UnwrapObject(receiver);
CHECK_SELF_OR_FALLBACK(false);
self->fast_call_count_++;

CHECK_NOT_NULL(options.wasm_memory);
uint8_t* memory = nullptr;
CHECK(options.wasm_memory->getStorageIfAligned(&memory));
memory[address] = 42;

return true;
}

static void TestWasmMemorySlowCallback(
const FunctionCallbackInfo<Value>& args) {
FastCApiObject* self = UnwrapObject(args.This());
CHECK_SELF_OR_THROW();
self->slow_call_count_++;

args.GetIsolate()->ThrowError("should be unreachable from wasm");
}

static void FastCallCount(const FunctionCallbackInfo<Value>& args) {
FastCApiObject* self = UnwrapObject(args.This());
CHECK_SELF_OR_THROW();
Expand Down Expand Up @@ -856,6 +882,15 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
SideEffectType::kHasSideEffect, &is_valid_api_object_c_func));

CFunction test_wasm_memory_c_func =
CFunction::Make(FastCApiObject::TestWasmMemoryFastCallback);
api_obj_ctor->PrototypeTemplate()->Set(
isolate, "test_wasm_memory",
FunctionTemplate::New(
isolate, FastCApiObject::TestWasmMemorySlowCallback, Local<Value>(),
Local<Signature>(), 1, ConstructorBehavior::kThrow,
SideEffectType::kHasSideEffect, &test_wasm_memory_c_func));

api_obj_ctor->PrototypeTemplate()->Set(
isolate, "fast_call_count",
FunctionTemplate::New(
Expand Down
15 changes: 11 additions & 4 deletions deps/v8/src/heap/factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1521,7 +1521,8 @@ Handle<WasmTypeInfo> Factory::NewWasmTypeInfo(
}

Handle<WasmApiFunctionRef> Factory::NewWasmApiFunctionRef(
Handle<JSReceiver> callable, Handle<HeapObject> suspender) {
Handle<JSReceiver> callable, Handle<HeapObject> suspender,
Handle<WasmInstanceObject> instance) {
Map map = *wasm_api_function_ref_map();
auto result = WasmApiFunctionRef::cast(AllocateRawWithImmortalMap(
map.instance_size(), AllocationType::kOld, map));
Expand All @@ -1538,6 +1539,11 @@ Handle<WasmApiFunctionRef> Factory::NewWasmApiFunctionRef(
} else {
result.set_suspender(*undefined_value());
}
if (!instance.is_null()) {
result.set_instance(*instance);
} else {
result.set_instance(*undefined_value());
}
return handle(result, isolate());
}

Expand All @@ -1560,7 +1566,8 @@ Handle<WasmJSFunctionData> Factory::NewWasmJSFunctionData(
Address opt_call_target, Handle<JSReceiver> callable, int return_count,
int parameter_count, Handle<PodArray<wasm::ValueType>> serialized_sig,
Handle<CodeT> wrapper_code, Handle<Map> rtt, Handle<HeapObject> suspender) {
Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(callable, suspender);
Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(
callable, suspender, Handle<WasmInstanceObject>());
Handle<WasmInternalFunction> internal =
NewWasmInternalFunction(opt_call_target, ref, rtt);
Map map = *wasm_js_function_data_map();
Expand Down Expand Up @@ -1621,8 +1628,8 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
Address call_target, Handle<Foreign> embedder_data,
Handle<CodeT> wrapper_code, Handle<Map> rtt,
Handle<PodArray<wasm::ValueType>> serialized_sig) {
Handle<WasmApiFunctionRef> ref =
NewWasmApiFunctionRef(Handle<JSReceiver>(), Handle<HeapObject>());
Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(
Handle<JSReceiver>(), Handle<HeapObject>(), Handle<WasmInstanceObject>());
Handle<WasmInternalFunction> internal =
NewWasmInternalFunction(call_target, ref, rtt);
Map map = *wasm_capi_function_data_map();
Expand Down
3 changes: 2 additions & 1 deletion deps/v8/src/heap/factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Address call_target, Handle<Object> ref, int func_index,
Address sig_address, int wrapper_budget, Handle<Map> rtt);
Handle<WasmApiFunctionRef> NewWasmApiFunctionRef(
Handle<JSReceiver> callable, Handle<HeapObject> suspender);
Handle<JSReceiver> callable, Handle<HeapObject> suspender,
Handle<WasmInstanceObject> instance);
// {opt_call_target} is kNullAddress for JavaScript functions, and
// non-null for exported Wasm functions.
Handle<WasmJSFunctionData> NewWasmJSFunctionData(
Expand Down
4 changes: 2 additions & 2 deletions deps/v8/src/wasm/wasm-objects.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,7 @@ void ImportedFunctionEntry::SetWasmToJs(
DCHECK(wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToJsWrapper ||
wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper);
Handle<WasmApiFunctionRef> ref =
isolate->factory()->NewWasmApiFunctionRef(callable, suspender);
isolate->factory()->NewWasmApiFunctionRef(callable, suspender, instance_);
instance_->imported_function_refs().set(index_, *ref);
instance_->imported_function_targets()[index_] =
wasm_to_js_wrapper->instruction_start();
Expand Down Expand Up @@ -1502,7 +1502,7 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
// Update the dispatch table.
Handle<HeapObject> suspender = handle(js_function->GetSuspender(), isolate);
Handle<WasmApiFunctionRef> ref =
isolate->factory()->NewWasmApiFunctionRef(callable, suspender);
isolate->factory()->NewWasmApiFunctionRef(callable, suspender, instance);
WasmIndirectFunctionTable::cast(
instance->indirect_function_tables().get(table_index))
.Set(entry_index, sig_id, call_target, *ref);
Expand Down
3 changes: 3 additions & 0 deletions deps/v8/src/wasm/wasm-objects.tq
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ extern class WasmApiFunctionRef extends HeapObject {
native_context: NativeContext;
callable: JSReceiver|Undefined;
suspender: WasmSuspenderObject|Undefined;
// Present when compiling JSFastApiCall wrappers, needed
// to load memory start/size fields.
instance: WasmInstanceObject|Undefined;
}

// This is the representation that is used internally by wasm to represent
Expand Down
9 changes: 6 additions & 3 deletions deps/v8/test/cctest/test-api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27841,7 +27841,8 @@ struct BasicApiChecker {
}
static Ret FastCallbackNoFallback(v8::Local<v8::Object> receiver,
Value argument) {
v8::FastApiCallbackOptions options = {false, {0}};
v8::FastApiCallbackOptions options =
v8::FastApiCallbackOptions::CreateForTesting(v8::Isolate::GetCurrent());
return Impl::FastCallback(receiver, argument, options);
}

Expand Down Expand Up @@ -27876,7 +27877,8 @@ template <typename Value, typename Impl, typename Ret,
typename = std::enable_if_t<!std::is_void<Ret>::value>>
static v8::AnyCType FastCallbackNoFallbackWrapper(v8::AnyCType receiver,
v8::AnyCType argument) {
v8::FastApiCallbackOptions options = {false, {0}};
v8::FastApiCallbackOptions options =
v8::FastApiCallbackOptions::CreateForTesting(v8::Isolate::GetCurrent());
v8::AnyCType ret = PrimitiveToMixedType<Ret>(Impl::FastCallback(
receiver.object_value, PrimitiveFromMixedType<Value>(argument), options));
return ret;
Expand All @@ -27893,7 +27895,8 @@ template <typename Value, typename Impl, typename Ret,
typename = std::enable_if_t<std::is_void<Ret>::value>>
static void FastCallbackNoFallbackWrapper(v8::AnyCType receiver,
v8::AnyCType argument) {
v8::FastApiCallbackOptions options = {false, {0}};
v8::FastApiCallbackOptions options =
v8::FastApiCallbackOptions::CreateForTesting(v8::Isolate::GetCurrent());
return Impl::FastCallback(receiver.object_value,
PrimitiveFromMixedType<Value>(argument), options);
}
Expand Down
26 changes: 26 additions & 0 deletions deps/v8/test/mjsunit/compiler/fast-api-calls-wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,20 @@ function buildWasm(name, sig, body) {
[kWasmI32],
),
);
const test_wasm_memory = builder.addImport(
'fast_c_api',
'test_wasm_memory',
makeSig([kWasmI32], [kWasmI32]),
);
builder
.addMemory(1, 1)
.addFunction(name, sig)
.addBody(body({
add_all_no_options,
add_all_no_options_mismatch,
add_all_nested_bound,
overloaded_add_all_32bit_int,
test_wasm_memory,
}))
.exportFunc();
const x = {};
Expand All @@ -61,6 +68,7 @@ function buildWasm(name, sig, body) {
.bind(fast_c_api)
.bind(x),
overloaded_add_all_32bit_int: fast_c_api.overloaded_add_all_32bit_int_no_sig.bind(fast_c_api),
test_wasm_memory: fast_c_api.test_wasm_memory.bind(fast_c_api),
},
});
return module.exports[name];
Expand Down Expand Up @@ -179,3 +187,21 @@ fast_c_api.reset_counts();
assertEquals(overload_result, overloaded_add_all_32bit_int_wasm(true));
assertEquals(1, fast_c_api.fast_call_count());
assertEquals(1, fast_c_api.slow_call_count());

// ------------- Test test_wasm_memory ---------------
const test_wasm_memory_wasm = buildWasm(
'test_wasm_memory_wasm', makeSig([], [kWasmI32]),
({ test_wasm_memory }) => [
...wasmI32Const(12),
kExprCallFunction, test_wasm_memory,
kExprDrop,
...wasmI32Const(12),
kExprI32LoadMem8U, 0, 0,
],
);

// Test hits fast path.
fast_c_api.reset_counts();
assertEquals(42, test_wasm_memory_wasm())
assertEquals(1, fast_c_api.fast_call_count());
assertEquals(0, fast_c_api.slow_call_count());

0 comments on commit a016775

Please sign in to comment.