Commit 258371bd authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Support anyref stack parameters

Anyref parameters can exist across GC runs. Therefore the GC has to
know where anyref parameters are on the stack so that it can mark them
in its marking phase, and update them in the compaction phase.

Already in a previous CL we grouped all anyref parameters so that they
can be found more easily in a stack frame, see
https://crrev.com/c/1371827. In this CL we implement the stack scanning
itself.

Note that anyref parameters are not scanned while iterating over the
caller's frame (to which they actually belong), but while iterating
over the callee's frame. The reason is that with tail-calls, only the
callee knows how many tagged stack parameters (aka anyref parameters)
there are.

R=mstarzinger@chromium.org
also-by=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: I7a41ce11d06c0d420146fdb0bb8d5606f28824d7
Reviewed-on: https://chromium-review.googlesource.com/c/1424955
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59099}
parent 76394d6b
......@@ -119,6 +119,17 @@ int CallDescriptor::GetStackParameterDelta(
return stack_param_delta;
}
int CallDescriptor::GetTaggedParameterSlots() const {
int result = 0;
for (size_t i = 0; i < InputCount(); ++i) {
LinkageLocation operand = GetInputLocation(i);
if (!operand.IsRegister() && operand.GetType().IsTagged()) {
++result;
}
}
return result;
}
bool CallDescriptor::CanTailCall(const Node* node) const {
return HasSameReturnLocationsAs(CallDescriptorOf(node->op()));
}
......
......@@ -315,6 +315,8 @@ class V8_EXPORT_PRIVATE CallDescriptor final
int GetStackParameterDelta(const CallDescriptor* tail_caller) const;
int GetTaggedParameterSlots() const;
bool CanTailCall(const Node* call) const;
int CalculateFixedFrameSize() const;
......
......@@ -2199,9 +2199,11 @@ wasm::WasmCode* Pipeline::GenerateCodeForWasmNativeStub(
CodeDesc code_desc;
code_generator->tasm()->GetCode(nullptr, &code_desc);
// We do not expect any tagged parameters being passed to stubs.
DCHECK_EQ(0, call_descriptor->GetTaggedParameterSlots());
wasm::WasmCode* code = native_module->AddCode(
wasm::WasmCode::kAnonymousFuncIndex, code_desc,
code_generator->frame()->GetTotalFrameSlotCount(),
code_generator->frame()->GetTotalFrameSlotCount(), 0,
code_generator->GetSafepointTableOffset(),
code_generator->GetHandlerTableOffset(),
code_generator->GetProtectedInstructions(),
......@@ -2442,6 +2444,7 @@ void Pipeline::GenerateCodeForWasmFunction(
result->instr_buffer = instruction_buffer->ReleaseBuffer();
result->frame_slot_count = code_generator->frame()->GetTotalFrameSlotCount();
result->tagged_parameter_slots = call_descriptor->GetTaggedParameterSlots();
result->safepoint_table_offset = code_generator->GetSafepointTableOffset();
result->handler_table_offset = code_generator->GetHandlerTableOffset();
result->source_positions = code_generator->GetSourcePositionTable();
......
......@@ -852,6 +852,7 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
uint32_t stack_slots;
Code code;
bool has_tagged_params = false;
uint32_t tagged_parameter_slots = 0;
if (wasm_code != nullptr) {
SafepointTable table(wasm_code->instruction_start(),
wasm_code->safepoint_table_offset(),
......@@ -859,6 +860,7 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
safepoint_entry = table.FindEntry(inner_pointer);
stack_slots = wasm_code->stack_slots();
has_tagged_params = wasm_code->kind() != wasm::WasmCode::kFunction;
tagged_parameter_slots = wasm_code->tagged_parameter_slots();
} else {
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
......@@ -970,6 +972,19 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
}
}
// Visit tagged parameters that have been passed to the function of this
// frame. Conceptionally these parameters belong to the parent frame. However,
// the exact count is only known by this frame (in the presence of tail calls,
// this information cannot be derived from the call site).
if (tagged_parameter_slots > 0) {
FullObjectSlot tagged_parameter_base(&Memory<Address>(caller_sp()));
FullObjectSlot tagged_parameter_limit =
tagged_parameter_base + tagged_parameter_slots;
v->VisitRootPointers(Root::kTop, nullptr, tagged_parameter_base,
tagged_parameter_limit);
}
// For the off-heap code cases, we can skip this.
if (!code.is_null()) {
// Visit the return address in the callee and incoming arguments.
......
......@@ -1998,6 +1998,7 @@ WasmCompilationResult LiftoffCompilationUnit::ExecuteCompilation(
result.source_positions = compiler->GetSourcePositionTable();
result.protected_instructions = compiler->GetProtectedInstructions();
result.frame_slot_count = compiler->GetTotalFrameSlotCount();
result.tagged_parameter_slots = call_descriptor->GetTaggedParameterSlots();
result.safepoint_table_offset = compiler->GetSafepointTableOffset();
DCHECK(result.succeeded());
......
......@@ -202,8 +202,8 @@ WasmCode* WasmCompilationUnit::Publish(WasmCompilationResult result,
DCHECK_EQ(result.code_desc.buffer, result.instr_buffer.get());
WasmCode* code = native_module->AddCode(
func_index_, result.code_desc, result.frame_slot_count,
result.safepoint_table_offset, result.handler_table_offset,
std::move(result.protected_instructions),
result.tagged_parameter_slots, result.safepoint_table_offset,
result.handler_table_offset, std::move(result.protected_instructions),
std::move(result.source_positions), WasmCode::kFunction, code_tier);
// TODO(clemensh): Merge this into {AddCode}?
native_module->PublishCode(code);
......
......@@ -60,6 +60,7 @@ struct WasmCompilationResult {
CodeDesc code_desc;
std::unique_ptr<uint8_t[]> instr_buffer;
uint32_t frame_slot_count = 0;
uint32_t tagged_parameter_slots = 0;
size_t safepoint_table_offset = 0;
size_t handler_table_offset = 0;
OwnedVector<byte> source_positions;
......
......@@ -436,9 +436,9 @@ CompilationEnv NativeModule::CreateCompilationEnv() const {
WasmCode* NativeModule::AddOwnedCode(
uint32_t index, Vector<const byte> instructions, uint32_t stack_slots,
size_t safepoint_table_offset, size_t handler_table_offset,
size_t constant_pool_offset, size_t code_comments_offset,
size_t unpadded_binary_size,
uint32_t tagged_parameter_slots, size_t safepoint_table_offset,
size_t handler_table_offset, size_t constant_pool_offset,
size_t code_comments_offset, size_t unpadded_binary_size,
OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,
OwnedVector<const byte> reloc_info,
OwnedVector<const byte> source_position_table, WasmCode::Kind kind,
......@@ -451,10 +451,11 @@ WasmCode* NativeModule::AddOwnedCode(
Vector<byte> executable_buffer = AllocateForCode(instructions.size());
// Ownership will be transferred to {owned_code_} below.
code = new WasmCode(
this, index, executable_buffer, stack_slots, safepoint_table_offset,
handler_table_offset, constant_pool_offset, code_comments_offset,
unpadded_binary_size, std::move(protected_instructions),
std::move(reloc_info), std::move(source_position_table), kind, tier);
this, index, executable_buffer, stack_slots, tagged_parameter_slots,
safepoint_table_offset, handler_table_offset, constant_pool_offset,
code_comments_offset, unpadded_binary_size,
std::move(protected_instructions), std::move(reloc_info),
std::move(source_position_table), kind, tier);
if (owned_code_.empty() ||
code->instruction_start() > owned_code_.back()->instruction_start()) {
......@@ -535,6 +536,7 @@ WasmCode* NativeModule::AddAnonymousCode(Handle<Code> code, WasmCode::Kind kind,
AddOwnedCode(WasmCode::kAnonymousFuncIndex, // index
instructions, // instructions
stack_slots, // stack_slots
0, // tagged_parameter_slots
safepoint_table_offset, // safepoint_table_offset
code->handler_table_offset(), // handler_table_offset
code->constant_pool_offset(), // constant_pool_offset
......@@ -578,7 +580,8 @@ WasmCode* NativeModule::AddAnonymousCode(Handle<Code> code, WasmCode::Kind kind,
WasmCode* NativeModule::AddCode(
uint32_t index, const CodeDesc& desc, uint32_t stack_slots,
size_t safepoint_table_offset, size_t handler_table_offset,
uint32_t tagged_parameter_slots, size_t safepoint_table_offset,
size_t handler_table_offset,
OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,
OwnedVector<const byte> source_pos_table, WasmCode::Kind kind,
WasmCode::Tier tier) {
......@@ -588,8 +591,8 @@ WasmCode* NativeModule::AddCode(
WasmCode* ret = AddOwnedCode(
index, {desc.buffer, static_cast<size_t>(desc.instr_size)}, stack_slots,
safepoint_table_offset, handler_table_offset, desc.constant_pool_offset(),
desc.code_comments_offset(), desc.instr_size,
tagged_parameter_slots, safepoint_table_offset, handler_table_offset,
desc.constant_pool_offset(), desc.code_comments_offset(), desc.instr_size,
std::move(protected_instructions), std::move(reloc_info),
std::move(source_pos_table), kind, tier);
......@@ -629,18 +632,18 @@ WasmCode* NativeModule::AddCode(
WasmCode* NativeModule::AddDeserializedCode(
uint32_t index, Vector<const byte> instructions, uint32_t stack_slots,
size_t safepoint_table_offset, size_t handler_table_offset,
size_t constant_pool_offset, size_t code_comments_offset,
size_t unpadded_binary_size,
uint32_t tagged_parameter_slots, size_t safepoint_table_offset,
size_t handler_table_offset, size_t constant_pool_offset,
size_t code_comments_offset, size_t unpadded_binary_size,
OwnedVector<trap_handler::ProtectedInstructionData> protected_instructions,
OwnedVector<const byte> reloc_info,
OwnedVector<const byte> source_position_table, WasmCode::Tier tier) {
WasmCode* code =
AddOwnedCode(index, instructions, stack_slots, safepoint_table_offset,
handler_table_offset, constant_pool_offset,
code_comments_offset, unpadded_binary_size,
std::move(protected_instructions), std::move(reloc_info),
std::move(source_position_table), WasmCode::kFunction, tier);
WasmCode* code = AddOwnedCode(
index, instructions, stack_slots, tagged_parameter_slots,
safepoint_table_offset, handler_table_offset, constant_pool_offset,
code_comments_offset, unpadded_binary_size,
std::move(protected_instructions), std::move(reloc_info),
std::move(source_position_table), WasmCode::kFunction, tier);
if (!code->protected_instructions_.is_empty()) {
code->RegisterTrapHandlerData();
......@@ -689,6 +692,7 @@ WasmCode* NativeModule::CreateEmptyJumpTable(uint32_t num_wasm_functions) {
return AddOwnedCode(WasmCode::kAnonymousFuncIndex, // index
instructions.as_vector(), // instructions
0, // stack_slots
0, // tagged_parameter_slots
instructions.size(), // safepoint_table_offset
instructions.size(), // handler_table_offset
instructions.size(), // constant_pool_offset
......
......@@ -121,6 +121,7 @@ class V8_EXPORT_PRIVATE WasmCode final {
size_t code_comments_offset() const { return code_comments_offset_; }
size_t unpadded_binary_size() const { return unpadded_binary_size_; }
uint32_t stack_slots() const { return stack_slots_; }
uint32_t tagged_parameter_slots() const { return tagged_parameter_slots_; }
bool is_liftoff() const { return tier_ == kLiftoff; }
bool contains(Address pc) const {
return reinterpret_cast<Address>(instructions_.start()) <= pc &&
......@@ -155,9 +156,9 @@ class V8_EXPORT_PRIVATE WasmCode final {
WasmCode(NativeModule* native_module, uint32_t index,
Vector<byte> instructions, uint32_t stack_slots,
size_t safepoint_table_offset, size_t handler_table_offset,
size_t constant_pool_offset, size_t code_comments_offset,
size_t unpadded_binary_size,
uint32_t tagged_parameter_slots, size_t safepoint_table_offset,
size_t handler_table_offset, size_t constant_pool_offset,
size_t code_comments_offset, size_t unpadded_binary_size,
OwnedVector<trap_handler::ProtectedInstructionData>
protected_instructions,
OwnedVector<const byte> reloc_info,
......@@ -170,6 +171,7 @@ class V8_EXPORT_PRIVATE WasmCode final {
kind_(kind),
constant_pool_offset_(constant_pool_offset),
stack_slots_(stack_slots),
tagged_parameter_slots_(tagged_parameter_slots),
safepoint_table_offset_(safepoint_table_offset),
handler_table_offset_(handler_table_offset),
code_comments_offset_(code_comments_offset),
......@@ -200,6 +202,9 @@ class V8_EXPORT_PRIVATE WasmCode final {
Kind kind_;
size_t constant_pool_offset_ = 0;
uint32_t stack_slots_ = 0;
// Number of tagged parameters passed to this function via the stack. This
// value is used by the stack walker (e.g. GC) to find references.
uint32_t tagged_parameter_slots_ = 0;
// we care about safepoint data for wasm-to-js functions,
// since there may be stack/register tagged values for large number
// conversions.
......@@ -228,6 +233,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
// {AddCode} is thread safe w.r.t. other calls to {AddCode} or methods adding
// code below, i.e. it can be called concurrently from background threads.
WasmCode* AddCode(uint32_t index, const CodeDesc& desc, uint32_t stack_slots,
uint32_t tagged_parameter_slots,
size_t safepoint_table_offset, size_t handler_table_offset,
OwnedVector<trap_handler::ProtectedInstructionData>
protected_instructions,
......@@ -236,9 +242,9 @@ class V8_EXPORT_PRIVATE NativeModule final {
WasmCode* AddDeserializedCode(
uint32_t index, Vector<const byte> instructions, uint32_t stack_slots,
size_t safepoint_table_offset, size_t handler_table_offset,
size_t constant_pool_offset, size_t code_comments_offset,
size_t unpadded_binary_size,
uint32_t tagged_parameter_slots, size_t safepoint_table_offset,
size_t handler_table_offset, size_t constant_pool_offset,
size_t code_comments_offset, size_t unpadded_binary_size,
OwnedVector<trap_handler::ProtectedInstructionData>
protected_instructions,
OwnedVector<const byte> reloc_info,
......@@ -377,7 +383,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
// code is obtained (CodeDesc vs, as a point in time, Code), the kind,
// whether it has an index or is anonymous, etc.
WasmCode* AddOwnedCode(uint32_t index, Vector<const byte> instructions,
uint32_t stack_slots, size_t safepoint_table_offset,
uint32_t stack_slots, uint32_t tagged_parameter_slots,
size_t safepoint_table_offset,
size_t handler_table_offset,
size_t constant_pool_offset,
size_t code_comments_offset,
......
......@@ -195,6 +195,7 @@ constexpr size_t kCodeHeaderSize =
sizeof(size_t) + // offset of code comments
sizeof(size_t) + // unpadded binary size
sizeof(uint32_t) + // stack slots
sizeof(uint32_t) + // tagged parameter slots
sizeof(size_t) + // code size
sizeof(size_t) + // reloc size
sizeof(size_t) + // source positions size
......@@ -335,6 +336,7 @@ void NativeModuleSerializer::WriteCode(const WasmCode* code, Writer* writer) {
writer->Write(code->code_comments_offset());
writer->Write(code->unpadded_binary_size());
writer->Write(code->stack_slots());
writer->Write(code->tagged_parameter_slots());
writer->Write(code->instructions().size());
writer->Write(code->reloc_info().size());
writer->Write(code->source_positions().size());
......@@ -495,6 +497,7 @@ bool NativeModuleDeserializer::ReadCode(uint32_t fn_index, Reader* reader) {
size_t code_comment_offset = reader->Read<size_t>();
size_t unpadded_binary_size = reader->Read<size_t>();
uint32_t stack_slot_count = reader->Read<uint32_t>();
uint32_t tagged_parameter_slots = reader->Read<uint32_t>();
size_t code_size = reader->Read<size_t>();
size_t reloc_size = reader->Read<size_t>();
size_t source_position_size = reader->Read<size_t>();
......@@ -514,10 +517,11 @@ bool NativeModuleDeserializer::ReadCode(uint32_t fn_index, Reader* reader) {
reader->ReadVector(Vector<byte>::cast(protected_instructions.as_vector()));
WasmCode* code = native_module_->AddDeserializedCode(
fn_index, code_buffer, stack_slot_count, safepoint_table_offset,
handler_table_offset, constant_pool_offset, code_comment_offset,
unpadded_binary_size, std::move(protected_instructions),
std::move(reloc_info), std::move(source_pos), tier);
fn_index, code_buffer, stack_slot_count, tagged_parameter_slots,
safepoint_table_offset, handler_table_offset, constant_pool_offset,
code_comment_offset, unpadded_binary_size,
std::move(protected_instructions), std::move(reloc_info),
std::move(source_pos), tier);
// Relocate the code.
int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
......
......@@ -3471,7 +3471,7 @@ TEST(Liftoff_tier_up) {
desc.buffer = buffer.get();
desc.instr_size = static_cast<int>(sub_size);
WasmCode* code = native_module->AddCode(
add.function_index(), desc, 0, 0, 0, {}, OwnedVector<byte>(),
add.function_index(), desc, 0, 0, 0, 0, {}, OwnedVector<byte>(),
WasmCode::kFunction, WasmCode::kOther);
native_module->PublishCode(code);
......
......@@ -104,6 +104,42 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
instance.exports.main({hello: 'world'});
})();
(function testPassAnyRefWithGCWithStackParameters() {
print(arguments.callee.name);
const num_params = 15;
for (let index = 0; index < num_params; index++) {
const builder = new WasmModuleBuilder();
// Make a signature with {num_params} many anyref parameters.
const mysig = makeSig(Array(num_params).fill(kWasmAnyRef), []);
const main_sig = builder.addType(mysig);
const ref_sig = builder.addType(kSig_v_r);
const void_sig = builder.addType(kSig_v_v);
const imp_index = builder.addImport('q', 'func', ref_sig);
const gc_index = builder.addImport('q', 'gc', void_sig);
// First call the gc, then check if the object still exists.
builder.addFunction('main', main_sig)
.addBody([
kExprCallFunction, gc_index, // call gc
kExprGetLocal, index, kExprCallFunction, imp_index // call import
])
.exportFunc();
function checkFunction(value) {
assertEquals(index, value.hello);
}
const instance = builder.instantiate({q: {func: checkFunction, gc: gc}});
// Pass {num_params} many parameters to main. Note that it is important
// that no other references to these objects exist. They are kept alive
// only through references stored in the parameters slots of a stack frame.
instance.exports.main(
{hello: 0}, {hello: 1}, {hello: 2}, {hello: 3}, {hello: 4}, {hello: 5},
{hello: 6}, {hello: 7}, {hello: 8}, {hello: 9}, {hello: 10},
{hello: 11}, {hello: 12}, {hello: 13}, {hello: 14});
}
})();
(function testPassAnyRefWithGCInWrapper() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
......
......@@ -175,8 +175,9 @@ class WasmCodeManagerTest : public TestWithContext,
std::unique_ptr<byte[]> exec_buff(new byte[size]);
desc.buffer = exec_buff.get();
desc.instr_size = static_cast<int>(size);
return native_module->AddCode(index, desc, 0, 0, 0, {}, OwnedVector<byte>(),
WasmCode::kFunction, WasmCode::kOther);
return native_module->AddCode(index, desc, 0, 0, 0, 0, {},
OwnedVector<byte>(), WasmCode::kFunction,
WasmCode::kOther);
}
size_t page() const { return AllocatePageSize(); }
......
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