Revert "Revert "[wasm] JIT using WasmCodeManager""
This reverts commitb301203e5a
. Reason for revert: Fixed issues on arm. Original change's description: > Revert "[wasm] JIT using WasmCodeManager" > > This reverts commitd4c8393c1c
. > > Reason for revert: Breaks ARM hardware: > https://build.chromium.org/p/client.v8.ports/builders/V8%20Arm%20-%20debug/builds/5268 > > Original change's description: > > [wasm] JIT using WasmCodeManager > > > > This is the first step towards wasm code sharing. This CL moves wasm > > code generation outside the JavaScript GC heap using the previously - > > introduced WasmCodeManager (all this, behind the --wasm-jit-to-native > > flag). > > > > See design document: go/wasm-on-native-heap-stage-1 > > > > This CL doesn't change other wasm architectural invariants. We still > > have per-Isolate wasm code generation, and per-wasm module instance > > code specialization. > > > > Bug:v8:6876 > > > > Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng > > Change-Id: I1e08cecad75f93fb081545c31228a4568be276d3 > > Reviewed-on: https://chromium-review.googlesource.com/674086 > > Reviewed-by: Ben Titzer <titzer@chromium.org> > > Reviewed-by: Eric Holk <eholk@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#49689} > > TBR=bradnelson@chromium.org,titzer@chromium.org,mtrofin@chromium.org,eholk@chromium.org > > Change-Id: I89af1ea5decd841bc12cd2ceaf74d32bc4433885 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: v8:6876 > Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng > Reviewed-on: https://chromium-review.googlesource.com/794690 > Reviewed-by: Michael Achenbach <machenbach@chromium.org> > Commit-Queue: Michael Achenbach <machenbach@chromium.org> > Cr-Commit-Position: refs/heads/master@{#49691} TBR=bradnelson@chromium.org,machenbach@chromium.org,titzer@chromium.org,mtrofin@chromium.org,eholk@chromium.org Change-Id: I1b07638d1bb2ba0664305b4b2dcfc1342dc8444f No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: v8:6876 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Reviewed-on: https://chromium-review.googlesource.com/794434 Commit-Queue: Mircea Trofin <mtrofin@chromium.org> Reviewed-by: Mircea Trofin <mtrofin@chromium.org> Cr-Commit-Position: refs/heads/master@{#49692}
This commit is contained in:
parent
b301203e5a
commit
b03b1bd9a8
4
BUILD.gn
4
BUILD.gn
@ -2091,6 +2091,8 @@ v8_source_set("v8_base") {
|
||||
"src/wasm/wasm-api.h",
|
||||
"src/wasm/wasm-code-specialization.cc",
|
||||
"src/wasm/wasm-code-specialization.h",
|
||||
"src/wasm/wasm-code-wrapper.cc",
|
||||
"src/wasm/wasm-code-wrapper.h",
|
||||
"src/wasm/wasm-debug.cc",
|
||||
"src/wasm/wasm-external-refs.cc",
|
||||
"src/wasm/wasm-external-refs.h",
|
||||
@ -2114,6 +2116,8 @@ v8_source_set("v8_base") {
|
||||
"src/wasm/wasm-opcodes.h",
|
||||
"src/wasm/wasm-result.cc",
|
||||
"src/wasm/wasm-result.h",
|
||||
"src/wasm/wasm-serialization.cc",
|
||||
"src/wasm/wasm-serialization.h",
|
||||
"src/wasm/wasm-text.cc",
|
||||
"src/wasm/wasm-text.h",
|
||||
"src/wasm/wasm-value.h",
|
||||
|
37
src/api.cc
37
src/api.cc
@ -86,6 +86,7 @@
|
||||
#include "src/wasm/streaming-decoder.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
#include "src/wasm/wasm-serialization.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
@ -7781,26 +7782,40 @@ WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
|
||||
i::Handle<i::WasmModuleObject>::cast(Utils::OpenHandle(this));
|
||||
i::Handle<i::WasmCompiledModule> compiled_part =
|
||||
i::handle(i::WasmCompiledModule::cast(obj->compiled_module()));
|
||||
if (i::FLAG_wasm_jit_to_native) {
|
||||
i::Isolate* isolate = obj->GetIsolate();
|
||||
|
||||
std::unique_ptr<i::ScriptData> script_data =
|
||||
i::WasmCompiledModuleSerializer::SerializeWasmModule(obj->GetIsolate(),
|
||||
compiled_part);
|
||||
script_data->ReleaseDataOwnership();
|
||||
return i::wasm::NativeModuleSerializer::SerializeWholeModule(isolate,
|
||||
compiled_part);
|
||||
} else {
|
||||
std::unique_ptr<i::ScriptData> script_data =
|
||||
i::WasmCompiledModuleSerializer::SerializeWasmModule(obj->GetIsolate(),
|
||||
compiled_part);
|
||||
script_data->ReleaseDataOwnership();
|
||||
|
||||
size_t size = static_cast<size_t>(script_data->length());
|
||||
return {std::unique_ptr<const uint8_t[]>(script_data->data()), size};
|
||||
size_t size = static_cast<size_t>(script_data->length());
|
||||
return {std::unique_ptr<const uint8_t[]>(script_data->data()), size};
|
||||
}
|
||||
}
|
||||
|
||||
MaybeLocal<WasmCompiledModule> WasmCompiledModule::Deserialize(
|
||||
Isolate* isolate,
|
||||
const WasmCompiledModule::CallerOwnedBuffer& serialized_module,
|
||||
const WasmCompiledModule::CallerOwnedBuffer& wire_bytes) {
|
||||
int size = static_cast<int>(serialized_module.second);
|
||||
i::ScriptData sc(serialized_module.first, size);
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i::MaybeHandle<i::FixedArray> maybe_compiled_part =
|
||||
i::WasmCompiledModuleSerializer::DeserializeWasmModule(
|
||||
i_isolate, &sc, {wire_bytes.first, wire_bytes.second});
|
||||
i::MaybeHandle<i::FixedArray> maybe_compiled_part;
|
||||
if (i::FLAG_wasm_jit_to_native) {
|
||||
maybe_compiled_part =
|
||||
i::wasm::NativeModuleDeserializer::DeserializeFullBuffer(
|
||||
i_isolate, {serialized_module.first, serialized_module.second},
|
||||
{wire_bytes.first, wire_bytes.second});
|
||||
} else {
|
||||
int size = static_cast<int>(serialized_module.second);
|
||||
i::ScriptData sc(serialized_module.first, size);
|
||||
maybe_compiled_part =
|
||||
i::WasmCompiledModuleSerializer::DeserializeWasmModule(
|
||||
i_isolate, &sc, {wire_bytes.first, wire_bytes.second});
|
||||
}
|
||||
i::Handle<i::FixedArray> compiled_part;
|
||||
if (!maybe_compiled_part.ToHandle(&compiled_part)) {
|
||||
return MaybeLocal<WasmCompiledModule>();
|
||||
|
@ -54,6 +54,9 @@ namespace v8 {
|
||||
class ApiFunction;
|
||||
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
class WasmCode;
|
||||
}
|
||||
|
||||
// Forward declarations.
|
||||
class Isolate;
|
||||
@ -622,6 +625,9 @@ class RelocInfo {
|
||||
byte* pc_;
|
||||
Mode rmode_;
|
||||
intptr_t data_;
|
||||
// TODO(mtrofin): try remove host_, if all we need is the constant_pool_ or
|
||||
// other few attributes, like start address, etc. This is so that we can reuse
|
||||
// RelocInfo for WasmCode without having a modal design.
|
||||
Code* host_;
|
||||
Address constant_pool_ = nullptr;
|
||||
friend class RelocIterator;
|
||||
|
@ -52,6 +52,18 @@ class V8_EXPORT_PRIVATE CompilationInfo final {
|
||||
kLoopPeelingEnabled = 1 << 10,
|
||||
};
|
||||
|
||||
// TODO(mtrofin): investigate if this might be generalized outside wasm, with
|
||||
// the goal of better separating the compiler from where compilation lands. At
|
||||
// that point, the Handle<Code> member of CompilationInfo would also be
|
||||
// removed.
|
||||
struct WasmCodeDesc {
|
||||
CodeDesc code_desc;
|
||||
size_t safepoint_table_offset = 0;
|
||||
uint32_t frame_slot_count = 0;
|
||||
Handle<ByteArray> source_positions_table;
|
||||
MaybeHandle<HandlerTable> handler_table;
|
||||
};
|
||||
|
||||
// Construct a compilation info for unoptimized compilation.
|
||||
CompilationInfo(Zone* zone, ParseInfo* parse_info, FunctionLiteral* literal);
|
||||
// Construct a compilation info for optimized compilation.
|
||||
@ -255,6 +267,8 @@ class V8_EXPORT_PRIVATE CompilationInfo final {
|
||||
coverage_info_ = coverage_info;
|
||||
}
|
||||
|
||||
WasmCodeDesc* wasm_code_desc() { return &wasm_code_desc_; }
|
||||
|
||||
private:
|
||||
// Compilation mode.
|
||||
// BASE is generated by the full codegen, optionally prepared for bailouts.
|
||||
@ -289,6 +303,7 @@ class V8_EXPORT_PRIVATE CompilationInfo final {
|
||||
|
||||
// The compiled code.
|
||||
Handle<Code> code_;
|
||||
WasmCodeDesc wasm_code_desc_;
|
||||
|
||||
// Compilation mode flag and whether deoptimization is allowed.
|
||||
Mode mode_;
|
||||
|
@ -290,6 +290,25 @@ void CodeGenerator::AssembleCode() {
|
||||
result_ = kSuccess;
|
||||
}
|
||||
|
||||
Handle<ByteArray> CodeGenerator::GetSourcePositionTable() {
|
||||
return source_position_table_builder_.ToSourcePositionTable(isolate());
|
||||
}
|
||||
|
||||
MaybeHandle<HandlerTable> CodeGenerator::GetHandlerTable() const {
|
||||
if (!handlers_.empty()) {
|
||||
Handle<HandlerTable> table =
|
||||
Handle<HandlerTable>::cast(isolate()->factory()->NewFixedArray(
|
||||
HandlerTable::LengthForReturn(static_cast<int>(handlers_.size())),
|
||||
TENURED));
|
||||
for (size_t i = 0; i < handlers_.size(); ++i) {
|
||||
table->SetReturnOffset(static_cast<int>(i), handlers_[i].pc_offset);
|
||||
table->SetReturnHandler(static_cast<int>(i), handlers_[i].handler->pos());
|
||||
}
|
||||
return table;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Handle<Code> CodeGenerator::FinalizeCode() {
|
||||
if (result_ != kSuccess) return Handle<Code>();
|
||||
|
||||
|
@ -94,6 +94,9 @@ class CodeGenerator final : public GapResolver::Assembler {
|
||||
void AssembleCode(); // Does not need to run on main thread.
|
||||
Handle<Code> FinalizeCode();
|
||||
|
||||
Handle<ByteArray> GetSourcePositionTable();
|
||||
MaybeHandle<HandlerTable> GetHandlerTable() const;
|
||||
|
||||
InstructionSequence* code() const { return code_; }
|
||||
FrameAccessState* frame_access_state() const { return frame_access_state_; }
|
||||
const Frame* frame() const { return frame_access_state_->frame(); }
|
||||
@ -117,9 +120,10 @@ class CodeGenerator final : public GapResolver::Assembler {
|
||||
int arguments, Safepoint::DeoptMode deopt_mode);
|
||||
|
||||
Zone* zone() const { return zone_; }
|
||||
TurboAssembler* tasm() { return &tasm_; }
|
||||
size_t GetSafepointTableOffset() const { return safepoints_.GetCodeOffset(); }
|
||||
|
||||
private:
|
||||
TurboAssembler* tasm() { return &tasm_; }
|
||||
GapResolver* resolver() { return &resolver_; }
|
||||
SafepointTableBuilder* safepoints() { return &safepoints_; }
|
||||
CompilationInfo* info() const { return info_; }
|
||||
|
@ -975,8 +975,22 @@ size_t PipelineWasmCompilationJob::AllocatedMemory() const {
|
||||
|
||||
PipelineWasmCompilationJob::Status PipelineWasmCompilationJob::FinalizeJobImpl(
|
||||
Isolate* isolate) {
|
||||
pipeline_.FinalizeCode();
|
||||
ValidateImmovableEmbeddedObjects();
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
pipeline_.FinalizeCode();
|
||||
ValidateImmovableEmbeddedObjects();
|
||||
} else {
|
||||
CodeGenerator* code_generator = pipeline_.data_->code_generator();
|
||||
CompilationInfo::WasmCodeDesc* wasm_code_desc =
|
||||
compilation_info()->wasm_code_desc();
|
||||
code_generator->tasm()->GetCode(isolate, &wasm_code_desc->code_desc);
|
||||
wasm_code_desc->safepoint_table_offset =
|
||||
code_generator->GetSafepointTableOffset();
|
||||
wasm_code_desc->frame_slot_count =
|
||||
code_generator->frame()->GetTotalFrameSlotCount();
|
||||
wasm_code_desc->source_positions_table =
|
||||
code_generator->GetSourcePositionTable();
|
||||
wasm_code_desc->handler_table = code_generator->GetHandlerTable();
|
||||
}
|
||||
return SUCCEEDED;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-factory.h"
|
||||
#include "src/compiler/access-builder.h"
|
||||
#include "src/compiler/code-generator.h"
|
||||
#include "src/compiler/common-operator.h"
|
||||
#include "src/compiler/compiler-source-position-table.h"
|
||||
#include "src/compiler/diamond.h"
|
||||
@ -2364,15 +2365,21 @@ Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
|
||||
Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
|
||||
wasm::WasmCodePosition position) {
|
||||
DCHECK_NULL(args[0]);
|
||||
|
||||
// Add code object as constant.
|
||||
Handle<Code> code = index < env_->function_code.size()
|
||||
? env_->function_code[index]
|
||||
: env_->default_function_code;
|
||||
|
||||
DCHECK(!code.is_null());
|
||||
args[0] = HeapConstant(code);
|
||||
wasm::FunctionSig* sig = env_->module->functions[index].sig;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
// Simply encode the index of the target.
|
||||
Address code = reinterpret_cast<Address>(index);
|
||||
args[0] = jsgraph()->RelocatableIntPtrConstant(
|
||||
reinterpret_cast<intptr_t>(code), RelocInfo::WASM_CALL);
|
||||
} else {
|
||||
// Add code object as constant.
|
||||
Handle<Code> code = index < env_->function_code.size()
|
||||
? env_->function_code[index]
|
||||
: env_->default_function_code;
|
||||
|
||||
DCHECK(!code.is_null());
|
||||
args[0] = HeapConstant(code);
|
||||
}
|
||||
|
||||
return BuildWasmCall(sig, args, rets, position);
|
||||
}
|
||||
@ -2424,16 +2431,23 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args,
|
||||
TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position);
|
||||
}
|
||||
|
||||
// Load code object from the table.
|
||||
Node* load_code = graph()->NewNode(
|
||||
// Load code object from the table. It is held by a Foreign.
|
||||
Node* entry = graph()->NewNode(
|
||||
machine->Load(MachineType::AnyTagged()), table,
|
||||
graph()->NewNode(machine->Int32Add(),
|
||||
graph()->NewNode(machine->Word32Shl(), key,
|
||||
Int32Constant(kPointerSizeLog2)),
|
||||
Uint32Constant(fixed_offset)),
|
||||
*effect_, *control_);
|
||||
|
||||
args[0] = load_code;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
Node* address = graph()->NewNode(
|
||||
machine->Load(MachineType::Pointer()), entry,
|
||||
Int32Constant(Foreign::kForeignAddressOffset - kHeapObjectTag),
|
||||
*effect_, *control_);
|
||||
args[0] = address;
|
||||
} else {
|
||||
args[0] = entry;
|
||||
}
|
||||
return BuildWasmCall(sig, args, rets, position);
|
||||
}
|
||||
|
||||
@ -2767,7 +2781,7 @@ Node* WasmGraphBuilder::BuildHeapNumberValueIndexConstant() {
|
||||
return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag);
|
||||
}
|
||||
|
||||
void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
||||
void WasmGraphBuilder::BuildJSToWasmWrapper(WasmCodeWrapper wasm_code,
|
||||
Address wasm_context_address) {
|
||||
const int wasm_count = static_cast<int>(sig_->parameter_count());
|
||||
const int count =
|
||||
@ -2793,6 +2807,16 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
||||
reinterpret_cast<uintptr_t>(wasm_context_address),
|
||||
RelocInfo::WASM_CONTEXT_REFERENCE);
|
||||
|
||||
Node* wasm_code_node = nullptr;
|
||||
if (!wasm_code.IsCodeObject()) {
|
||||
const wasm::WasmCode* code = wasm_code.GetWasmCode();
|
||||
Address instr_start =
|
||||
code == nullptr ? nullptr : code->instructions().start();
|
||||
wasm_code_node = jsgraph()->RelocatableIntPtrConstant(
|
||||
reinterpret_cast<intptr_t>(instr_start), RelocInfo::JS_TO_WASM_CALL);
|
||||
} else {
|
||||
wasm_code_node = HeapConstant(wasm_code.GetCode());
|
||||
}
|
||||
if (!wasm::IsJSCompatibleSignature(sig_)) {
|
||||
// Throw a TypeError. Use the js_context of the calling javascript function
|
||||
// (passed as a parameter), such that the generated code is js_context
|
||||
@ -2804,7 +2828,7 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
||||
// contains a reference to the wrapped wasm function. Without this reference
|
||||
// the wasm function could not be re-imported into another wasm module.
|
||||
int pos = 0;
|
||||
args[pos++] = HeapConstant(wasm_code);
|
||||
args[pos++] = wasm_code_node;
|
||||
args[pos++] = wasm_context_;
|
||||
args[pos++] = *effect_;
|
||||
args[pos++] = *control_;
|
||||
@ -2819,7 +2843,7 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
args[pos++] = HeapConstant(wasm_code);
|
||||
args[pos++] = wasm_code_node;
|
||||
args[pos++] = wasm_context_;
|
||||
|
||||
// Convert JS parameters to wasm numbers.
|
||||
@ -3018,7 +3042,7 @@ bool HasInt64ParamOrReturn(wasm::FunctionSig* sig) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void WasmGraphBuilder::BuildWasmToWasmWrapper(Handle<Code> target,
|
||||
void WasmGraphBuilder::BuildWasmToWasmWrapper(WasmCodeWrapper wasm_code,
|
||||
Address new_context_address) {
|
||||
int wasm_count = static_cast<int>(sig_->parameter_count());
|
||||
int count = wasm_count + 4; // wasm_code, wasm_context, effect, and control.
|
||||
@ -3031,7 +3055,15 @@ void WasmGraphBuilder::BuildWasmToWasmWrapper(Handle<Code> target,
|
||||
|
||||
int pos = 0;
|
||||
// Add the wasm code target.
|
||||
args[pos++] = jsgraph()->HeapConstant(target);
|
||||
if (!wasm_code.IsCodeObject()) {
|
||||
const wasm::WasmCode* code = wasm_code.GetWasmCode();
|
||||
Address instr_start =
|
||||
code == nullptr ? nullptr : code->instructions().start();
|
||||
args[pos++] = jsgraph()->RelocatableIntPtrConstant(
|
||||
reinterpret_cast<intptr_t>(instr_start), RelocInfo::JS_TO_WASM_CALL);
|
||||
} else {
|
||||
args[pos++] = jsgraph()->HeapConstant(wasm_code.GetCode());
|
||||
}
|
||||
// Add the wasm_context of the other instance.
|
||||
args[pos++] = jsgraph()->IntPtrConstant(
|
||||
reinterpret_cast<uintptr_t>(new_context_address));
|
||||
@ -3129,7 +3161,17 @@ void WasmGraphBuilder::BuildCWasmEntry(Address wasm_context_address) {
|
||||
reinterpret_cast<uintptr_t>(wasm_context_address));
|
||||
|
||||
// Create parameter nodes (offset by 1 for the receiver parameter).
|
||||
Node* code_obj = Param(CWasmEntryParameters::kCodeObject + 1);
|
||||
Node* code_obj = nullptr;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
Node* foreign_code_obj = Param(CWasmEntryParameters::kCodeObject + 1);
|
||||
MachineOperatorBuilder* machine = jsgraph()->machine();
|
||||
code_obj = graph()->NewNode(
|
||||
machine->Load(MachineType::Pointer()), foreign_code_obj,
|
||||
Int32Constant(Foreign::kForeignAddressOffset - kHeapObjectTag),
|
||||
*effect_, *control_);
|
||||
} else {
|
||||
code_obj = Param(CWasmEntryParameters::kCodeObject + 1);
|
||||
}
|
||||
Node* arg_buffer = Param(CWasmEntryParameters::kArgumentsBuffer + 1);
|
||||
|
||||
int wasm_arg_count = static_cast<int>(sig_->parameter_count());
|
||||
@ -4203,7 +4245,7 @@ void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag,
|
||||
} // namespace
|
||||
|
||||
Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
|
||||
Handle<Code> wasm_code, uint32_t index,
|
||||
WasmCodeWrapper wasm_code, uint32_t index,
|
||||
Address wasm_context_address) {
|
||||
const wasm::WasmFunction* func = &module->functions[index];
|
||||
|
||||
@ -4225,8 +4267,10 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
|
||||
// TODO(titzer): compile JS to WASM wrappers without a {ModuleEnv}.
|
||||
ModuleEnv env = {
|
||||
module,
|
||||
std::vector<Address>(), // function_tables
|
||||
std::vector<Address>(), // signature_tables
|
||||
std::vector<Address>(), // function_tables
|
||||
std::vector<Address>(), // signature_tables
|
||||
// TODO(mtrofin): remove these 2 lines when we don't need
|
||||
// FLAG_wasm_jit_to_native
|
||||
std::vector<Handle<Code>>(), // function_code
|
||||
BUILTIN_CODE(isolate, Illegal) // default_function_code
|
||||
};
|
||||
@ -4411,9 +4455,8 @@ Handle<Code> CompileWasmToJSWrapper(
|
||||
return code;
|
||||
}
|
||||
|
||||
Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
|
||||
Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, WasmCodeWrapper target,
|
||||
wasm::FunctionSig* sig,
|
||||
uint32_t func_index,
|
||||
Address new_wasm_context_address) {
|
||||
//----------------------------------------------------------------------------
|
||||
// Create the Graph
|
||||
@ -4543,11 +4586,13 @@ Handle<Code> CompileWasmInterpreterEntry(Isolate* isolate, uint32_t func_index,
|
||||
}
|
||||
}
|
||||
|
||||
Handle<FixedArray> deopt_data = isolate->factory()->NewFixedArray(1, TENURED);
|
||||
Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
|
||||
deopt_data->set(0, *weak_instance);
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
Handle<FixedArray> deopt_data =
|
||||
isolate->factory()->NewFixedArray(1, TENURED);
|
||||
Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
|
||||
deopt_data->set(0, *weak_instance);
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
@ -4691,9 +4736,9 @@ WasmCompilationUnit::GetDefaultCompilationMode() {
|
||||
}
|
||||
|
||||
WasmCompilationUnit::WasmCompilationUnit(
|
||||
Isolate* isolate, ModuleEnv* env, wasm::FunctionBody body,
|
||||
wasm::WasmName name, int index, Handle<Code> centry_stub,
|
||||
CompilationMode mode, Counters* counters,
|
||||
Isolate* isolate, ModuleEnv* env, wasm::NativeModule* native_module,
|
||||
wasm::FunctionBody body, wasm::WasmName name, int index,
|
||||
Handle<Code> centry_stub, CompilationMode mode, Counters* counters,
|
||||
RuntimeExceptionSupport exception_support, bool lower_simd)
|
||||
: isolate_(isolate),
|
||||
env_(env),
|
||||
@ -4703,7 +4748,10 @@ WasmCompilationUnit::WasmCompilationUnit(
|
||||
centry_stub_(centry_stub),
|
||||
func_index_(index),
|
||||
runtime_exception_support_(exception_support),
|
||||
native_module_(native_module),
|
||||
lower_simd_(lower_simd),
|
||||
protected_instructions_(
|
||||
new std::vector<trap_handler::ProtectedInstructionData>()),
|
||||
mode_(mode) {
|
||||
switch (mode_) {
|
||||
case WasmCompilationUnit::CompilationMode::kLiftoff:
|
||||
@ -4803,7 +4851,7 @@ void WasmCompilationUnit::ExecuteTurbofanCompilation() {
|
||||
|
||||
tf_.job_.reset(Pipeline::NewWasmCompilationJob(
|
||||
tf_.info_.get(), isolate_, tf_.jsgraph_, descriptor, source_positions,
|
||||
&protected_instructions_, env_->module->origin()));
|
||||
protected_instructions_.get(), env_->module->origin()));
|
||||
ok_ = tf_.job_->ExecuteJob() == CompilationJob::SUCCEEDED;
|
||||
// TODO(bradnelson): Improve histogram handling of size_t.
|
||||
counters()->wasm_compile_function_peak_memory_bytes()->AddSample(
|
||||
@ -4829,18 +4877,26 @@ void WasmCompilationUnit::ExecuteTurbofanCompilation() {
|
||||
// WasmCompilationUnit::ExecuteLiftoffCompilation() is defined in
|
||||
// liftoff-compiler.cc.
|
||||
|
||||
MaybeHandle<Code> WasmCompilationUnit::FinishCompilation(
|
||||
WasmCodeWrapper WasmCompilationUnit::FinishCompilation(
|
||||
wasm::ErrorThrower* thrower) {
|
||||
WasmCodeWrapper ret;
|
||||
switch (mode_) {
|
||||
case WasmCompilationUnit::CompilationMode::kLiftoff:
|
||||
return FinishLiftoffCompilation(thrower);
|
||||
ret = FinishLiftoffCompilation(thrower);
|
||||
break;
|
||||
case WasmCompilationUnit::CompilationMode::kTurbofan:
|
||||
return FinishTurbofanCompilation(thrower);
|
||||
ret = FinishTurbofanCompilation(thrower);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
if (!ret.IsCodeObject() && ret.is_null()) {
|
||||
thrower->RuntimeError("Error finalizing code.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
MaybeHandle<Code> WasmCompilationUnit::FinishTurbofanCompilation(
|
||||
WasmCodeWrapper WasmCompilationUnit::FinishTurbofanCompilation(
|
||||
wasm::ErrorThrower* thrower) {
|
||||
if (!ok_) {
|
||||
if (tf_.graph_construction_result_.failed()) {
|
||||
@ -4862,39 +4918,78 @@ MaybeHandle<Code> WasmCompilationUnit::FinishTurbofanCompilation(
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
codegen_timer.Start();
|
||||
}
|
||||
|
||||
if (tf_.job_->FinalizeJob(isolate_) != CompilationJob::SUCCEEDED) {
|
||||
return Handle<Code>::null();
|
||||
return {};
|
||||
}
|
||||
Handle<Code> code = tf_.info_->code();
|
||||
DCHECK(!code.is_null());
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
Handle<Code> code = tf_.info_->code();
|
||||
DCHECK(!code.is_null());
|
||||
|
||||
if (must_record_function_compilation(isolate_)) {
|
||||
wasm::TruncatedUserString<> trunc_name(func_name_);
|
||||
RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, isolate_, code,
|
||||
"wasm_function#%d:%.*s", func_index_,
|
||||
trunc_name.length(), trunc_name.start());
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
double codegen_ms = codegen_timer.Elapsed().InMillisecondsF();
|
||||
PrintF("wasm-code-generation ok: %u bytes, %0.3f ms code generation\n",
|
||||
static_cast<unsigned>(func_body_.end - func_body_.start),
|
||||
codegen_ms);
|
||||
}
|
||||
|
||||
PackProtectedInstructions(code);
|
||||
return WasmCodeWrapper(code);
|
||||
} else {
|
||||
// TODO(mtrofin): when we crystalize a design in lieu of WasmCodeDesc, that
|
||||
// works for both wasm and non-wasm, we can simplify AddCode to just take
|
||||
// that as a parameter.
|
||||
const CodeDesc& desc =
|
||||
tf_.job_->compilation_info()->wasm_code_desc()->code_desc;
|
||||
wasm::WasmCode* code = native_module_->AddCode(
|
||||
desc, tf_.job_->compilation_info()->wasm_code_desc()->frame_slot_count,
|
||||
func_index_,
|
||||
tf_.job_->compilation_info()->wasm_code_desc()->safepoint_table_offset,
|
||||
protected_instructions_);
|
||||
if (!code) {
|
||||
return WasmCodeWrapper(code);
|
||||
}
|
||||
// TODO(mtrofin): add CodeEventListener call - see the non-native case.
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
double codegen_ms = codegen_timer.Elapsed().InMillisecondsF();
|
||||
PrintF("wasm-code-generation ok: %u bytes, %0.3f ms code generation\n",
|
||||
static_cast<unsigned>(func_body_.end - func_body_.start),
|
||||
codegen_ms);
|
||||
}
|
||||
|
||||
Handle<ByteArray> source_positions =
|
||||
tf_.job_->compilation_info()->wasm_code_desc()->source_positions_table;
|
||||
MaybeHandle<HandlerTable> handler_table =
|
||||
tf_.job_->compilation_info()->wasm_code_desc()->handler_table;
|
||||
|
||||
int function_index_as_int = static_cast<int>(func_index_);
|
||||
native_module_->compiled_module()->source_positions()->set(
|
||||
function_index_as_int, *source_positions);
|
||||
if (!handler_table.is_null()) {
|
||||
native_module_->compiled_module()->handler_table()->set(
|
||||
function_index_as_int, *handler_table.ToHandleChecked());
|
||||
}
|
||||
// TODO(mtrofin): this should probably move up in the common caller,
|
||||
// once liftoff has source positions. Until then, we'd need to handle
|
||||
// undefined values, which is complicating the code.
|
||||
LOG_CODE_EVENT(isolate_,
|
||||
CodeLinePosInfoRecordEvent(code->instructions().start(),
|
||||
*source_positions));
|
||||
return WasmCodeWrapper(code);
|
||||
}
|
||||
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
double codegen_ms = codegen_timer.Elapsed().InMillisecondsF();
|
||||
PrintF("wasm-code-generation ok: %u bytes, %0.3f ms code generation\n",
|
||||
static_cast<unsigned>(func_body_.end - func_body_.start),
|
||||
codegen_ms);
|
||||
}
|
||||
|
||||
PackProtectedInstructions(code);
|
||||
return code;
|
||||
}
|
||||
|
||||
// TODO(mtrofin): remove when FLAG_wasm_jit_to_native is not needed
|
||||
void WasmCompilationUnit::PackProtectedInstructions(Handle<Code> code) const {
|
||||
if (protected_instructions_.empty()) return;
|
||||
DCHECK_LT(protected_instructions_.size(), std::numeric_limits<int>::max());
|
||||
const int num_instructions = static_cast<int>(protected_instructions_.size());
|
||||
if (protected_instructions_->empty()) return;
|
||||
DCHECK_LT(protected_instructions_->size(), std::numeric_limits<int>::max());
|
||||
const int num_instructions =
|
||||
static_cast<int>(protected_instructions_->size());
|
||||
Handle<FixedArray> fn_protected = isolate_->factory()->NewFixedArray(
|
||||
num_instructions * Code::kTrapDataSize, TENURED);
|
||||
for (int i = 0; i < num_instructions; ++i) {
|
||||
const trap_handler::ProtectedInstructionData& instruction =
|
||||
protected_instructions_[i];
|
||||
protected_instructions_->at(i);
|
||||
fn_protected->set(Code::kTrapDataSize * i + Code::kTrapCodeOffset,
|
||||
Smi::FromInt(instruction.instr_offset));
|
||||
fn_protected->set(Code::kTrapDataSize * i + Code::kTrapLandingOffset,
|
||||
@ -4903,46 +4998,57 @@ void WasmCompilationUnit::PackProtectedInstructions(Handle<Code> code) const {
|
||||
code->set_protected_instructions(*fn_protected);
|
||||
}
|
||||
|
||||
MaybeHandle<Code> WasmCompilationUnit::FinishLiftoffCompilation(
|
||||
WasmCodeWrapper WasmCompilationUnit::FinishLiftoffCompilation(
|
||||
wasm::ErrorThrower* thrower) {
|
||||
CodeDesc desc;
|
||||
liftoff_.asm_.GetCode(isolate_, &desc);
|
||||
Handle<Code> code;
|
||||
code = isolate_->factory()->NewCode(desc, Code::WASM_FUNCTION, code);
|
||||
WasmCodeWrapper ret;
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
Handle<Code> code;
|
||||
code = isolate_->factory()->NewCode(desc, Code::WASM_FUNCTION, code);
|
||||
#ifdef ENABLE_DISASSEMBLER
|
||||
if (FLAG_print_code || FLAG_print_wasm_code) {
|
||||
// TODO(wasm): Use proper log files, here and elsewhere.
|
||||
OFStream os(stdout);
|
||||
os << "--- Wasm liftoff code ---\n";
|
||||
EmbeddedVector<char, 32> func_name;
|
||||
func_name.Truncate(SNPrintF(func_name, "wasm#%d-liftoff", func_index_));
|
||||
code->Disassemble(func_name.start(), os);
|
||||
os << "--- End code ---\n";
|
||||
}
|
||||
if (FLAG_print_code || FLAG_print_wasm_code) {
|
||||
// TODO(wasm): Use proper log files, here and elsewhere.
|
||||
OFStream os(stdout);
|
||||
os << "--- Wasm liftoff code ---\n";
|
||||
EmbeddedVector<char, 32> func_name;
|
||||
func_name.Truncate(SNPrintF(func_name, "wasm#%d-liftoff", func_index_));
|
||||
code->Disassemble(func_name.start(), os);
|
||||
os << "--- End code ---\n";
|
||||
}
|
||||
#endif
|
||||
if (isolate_->logger()->is_logging_code_events() ||
|
||||
isolate_->is_profiling()) {
|
||||
RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, isolate_, code,
|
||||
"wasm#%d-liftoff", func_index_);
|
||||
}
|
||||
|
||||
if (isolate_->logger()->is_logging_code_events() ||
|
||||
isolate_->is_profiling()) {
|
||||
RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, isolate_, code,
|
||||
"wasm#%d-liftoff", func_index_);
|
||||
PackProtectedInstructions(code);
|
||||
return WasmCodeWrapper(code);
|
||||
} else {
|
||||
// TODO(mtrofin): figure a way to raise events; also, disassembly.
|
||||
// Consider lifting them both to FinishCompilation.
|
||||
return WasmCodeWrapper(native_module_->AddCode(
|
||||
desc, liftoff_.asm_.GetTotalFrameSlotCount(), func_index_,
|
||||
liftoff_.asm_.GetSafepointTableOffset(), protected_instructions_,
|
||||
true));
|
||||
}
|
||||
|
||||
PackProtectedInstructions(code);
|
||||
return code;
|
||||
}
|
||||
|
||||
// static
|
||||
MaybeHandle<Code> WasmCompilationUnit::CompileWasmFunction(
|
||||
wasm::ErrorThrower* thrower, Isolate* isolate,
|
||||
const wasm::ModuleWireBytes& wire_bytes, ModuleEnv* env,
|
||||
WasmCodeWrapper WasmCompilationUnit::CompileWasmFunction(
|
||||
wasm::NativeModule* native_module, wasm::ErrorThrower* thrower,
|
||||
Isolate* isolate, const wasm::ModuleWireBytes& wire_bytes, ModuleEnv* env,
|
||||
const wasm::WasmFunction* function, CompilationMode mode) {
|
||||
wasm::FunctionBody function_body{
|
||||
function->sig, function->code.offset(),
|
||||
wire_bytes.start() + function->code.offset(),
|
||||
wire_bytes.start() + function->code.end_offset()};
|
||||
WasmCompilationUnit unit(
|
||||
isolate, env, function_body, wire_bytes.GetNameOrNull(function),
|
||||
function->func_index, CEntryStub(isolate, 1).GetCode(), mode);
|
||||
|
||||
WasmCompilationUnit unit(isolate, env, native_module, function_body,
|
||||
wire_bytes.GetNameOrNull(function),
|
||||
function->func_index,
|
||||
CEntryStub(isolate, 1).GetCode(), mode);
|
||||
unit.ExecuteCompilation();
|
||||
return unit.FinishCompilation(thrower);
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ class SignatureMap;
|
||||
// Expose {Node} and {Graph} opaquely as {wasm::TFNode} and {wasm::TFGraph}.
|
||||
typedef compiler::Node TFNode;
|
||||
typedef compiler::JSGraph TFGraph;
|
||||
class NativeModule;
|
||||
class WasmCode;
|
||||
} // namespace wasm
|
||||
|
||||
namespace compiler {
|
||||
@ -60,6 +62,8 @@ struct ModuleEnv {
|
||||
// (the same length as module.function_tables)
|
||||
// We use the address to a global handle to the FixedArray.
|
||||
const std::vector<Address> signature_tables;
|
||||
|
||||
// TODO(mtrofin): remove these 2 once we don't need FLAG_wasm_jit_to_native
|
||||
// Contains the code objects to call for each direct call.
|
||||
// (the same length as module.functions)
|
||||
const std::vector<Handle<Code>> function_code;
|
||||
@ -82,8 +86,9 @@ class WasmCompilationUnit final {
|
||||
// typically means to hold a std::shared_ptr<Counters>).
|
||||
// If no such pointer is passed, Isolate::counters() will be called. This is
|
||||
// only allowed to happen on the foreground thread.
|
||||
WasmCompilationUnit(Isolate*, ModuleEnv*, wasm::FunctionBody, wasm::WasmName,
|
||||
int index, Handle<Code> centry_stub,
|
||||
WasmCompilationUnit(Isolate*, ModuleEnv*, wasm::NativeModule*,
|
||||
wasm::FunctionBody, wasm::WasmName, int index,
|
||||
Handle<Code> centry_stub,
|
||||
CompilationMode = GetDefaultCompilationMode(),
|
||||
Counters* = nullptr,
|
||||
RuntimeExceptionSupport = kRuntimeExceptionSupport,
|
||||
@ -94,11 +99,13 @@ class WasmCompilationUnit final {
|
||||
int func_index() const { return func_index_; }
|
||||
|
||||
void ExecuteCompilation();
|
||||
MaybeHandle<Code> FinishCompilation(wasm::ErrorThrower*);
|
||||
WasmCodeWrapper FinishCompilation(wasm::ErrorThrower* thrower);
|
||||
|
||||
static MaybeHandle<Code> CompileWasmFunction(
|
||||
wasm::ErrorThrower*, Isolate*, const wasm::ModuleWireBytes&, ModuleEnv*,
|
||||
const wasm::WasmFunction*, CompilationMode = GetDefaultCompilationMode());
|
||||
static WasmCodeWrapper CompileWasmFunction(
|
||||
wasm::NativeModule* native_module, wasm::ErrorThrower* thrower,
|
||||
Isolate* isolate, const wasm::ModuleWireBytes& wire_bytes, ModuleEnv* env,
|
||||
const wasm::WasmFunction* function,
|
||||
CompilationMode = GetDefaultCompilationMode());
|
||||
|
||||
size_t memory_cost() const { return memory_cost_; }
|
||||
|
||||
@ -125,11 +132,11 @@ class WasmCompilationUnit final {
|
||||
// Turbofan.
|
||||
SourcePositionTable* BuildGraphForWasmFunction(double* decode_ms);
|
||||
void ExecuteTurbofanCompilation();
|
||||
MaybeHandle<Code> FinishTurbofanCompilation(wasm::ErrorThrower*);
|
||||
WasmCodeWrapper FinishTurbofanCompilation(wasm::ErrorThrower*);
|
||||
|
||||
// Liftoff.
|
||||
bool ExecuteLiftoffCompilation();
|
||||
MaybeHandle<Code> FinishLiftoffCompilation(wasm::ErrorThrower*);
|
||||
WasmCodeWrapper FinishLiftoffCompilation(wasm::ErrorThrower*);
|
||||
|
||||
Isolate* isolate_;
|
||||
ModuleEnv* env_;
|
||||
@ -142,8 +149,10 @@ class WasmCompilationUnit final {
|
||||
RuntimeExceptionSupport runtime_exception_support_;
|
||||
bool ok_ = true;
|
||||
size_t memory_cost_ = 0;
|
||||
wasm::NativeModule* native_module_;
|
||||
bool lower_simd_;
|
||||
std::vector<trap_handler::ProtectedInstructionData> protected_instructions_;
|
||||
std::shared_ptr<std::vector<trap_handler::ProtectedInstructionData>>
|
||||
protected_instructions_;
|
||||
CompilationMode mode_;
|
||||
// {liftoff_} is valid if mode_ == kLiftoff, tf_ if mode_ == kTurbofan.
|
||||
union {
|
||||
@ -166,15 +175,14 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
|
||||
Handle<FixedArray> global_js_imports_table);
|
||||
|
||||
// Wraps a given wasm code object, producing a code object.
|
||||
Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
|
||||
Handle<Code> wasm_code, uint32_t index,
|
||||
Address wasm_context_address);
|
||||
V8_EXPORT_PRIVATE Handle<Code> CompileJSToWasmWrapper(
|
||||
Isolate* isolate, wasm::WasmModule* module, WasmCodeWrapper wasm_code,
|
||||
uint32_t index, Address wasm_context_address);
|
||||
|
||||
// Wraps a wasm function, producing a code object that can be called from other
|
||||
// wasm instances (the WasmContext address must be changed).
|
||||
Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, Handle<Code> target,
|
||||
Handle<Code> CompileWasmToWasmWrapper(Isolate* isolate, WasmCodeWrapper target,
|
||||
wasm::FunctionSig* sig,
|
||||
uint32_t func_index,
|
||||
Address new_wasm_context_address);
|
||||
|
||||
// Compiles a stub that redirects a call to a wasm function to the wasm
|
||||
@ -293,7 +301,7 @@ class WasmGraphBuilder {
|
||||
Node* CallIndirect(uint32_t index, Node** args, Node*** rets,
|
||||
wasm::WasmCodePosition position);
|
||||
|
||||
void BuildJSToWasmWrapper(Handle<Code> wasm_code,
|
||||
void BuildJSToWasmWrapper(WasmCodeWrapper wasm_code_start,
|
||||
Address wasm_context_address);
|
||||
enum ImportDataType {
|
||||
kFunction = 1,
|
||||
@ -306,7 +314,7 @@ class WasmGraphBuilder {
|
||||
bool BuildWasmToJSWrapper(Handle<JSReceiver> target,
|
||||
Handle<FixedArray> global_js_imports_table,
|
||||
int index);
|
||||
void BuildWasmToWasmWrapper(Handle<Code> target,
|
||||
void BuildWasmToWasmWrapper(WasmCodeWrapper wasm_code_start,
|
||||
Address new_wasm_context_address);
|
||||
void BuildWasmInterpreterEntry(uint32_t func_index);
|
||||
void BuildCWasmEntry(Address wasm_context_address);
|
||||
|
@ -252,13 +252,18 @@ CallDescriptor* GetWasmCallDescriptor(Zone* zone, wasm::FunctionSig* fsig,
|
||||
const RegList kCalleeSaveFPRegisters = 0;
|
||||
|
||||
// The target for wasm calls is always a code object.
|
||||
MachineType target_type = MachineType::AnyTagged();
|
||||
MachineType target_type = FLAG_wasm_jit_to_native ? MachineType::Pointer()
|
||||
: MachineType::AnyTagged();
|
||||
LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
|
||||
|
||||
CallDescriptor::Flags flags = CallDescriptor::kUseNativeStack;
|
||||
if (supports_tail_calls) flags |= CallDescriptor::kSupportsTailCalls;
|
||||
CallDescriptor::Kind kind = FLAG_wasm_jit_to_native
|
||||
? CallDescriptor::kCallWasmFunction
|
||||
: CallDescriptor::kCallCodeObject;
|
||||
|
||||
return new (zone) CallDescriptor( // --
|
||||
CallDescriptor::kCallCodeObject, // kind
|
||||
kind, // kind
|
||||
target_type, // target MachineType
|
||||
target_loc, // target location
|
||||
locations.Build(), // location_sig
|
||||
|
288
src/frames.cc
288
src/frames.cc
@ -16,6 +16,7 @@
|
||||
#include "src/string-stream.h"
|
||||
#include "src/visitors.h"
|
||||
#include "src/vm-state-inl.h"
|
||||
#include "src/wasm/wasm-heap.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/zone/zone-containers.h"
|
||||
|
||||
@ -432,45 +433,68 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Look up the code object to figure out the type of the stack frame.
|
||||
Code* code_obj =
|
||||
GetContainingCode(iterator->isolate(), *(state->pc_address));
|
||||
if (code_obj != nullptr) {
|
||||
switch (code_obj->kind()) {
|
||||
case Code::BUILTIN:
|
||||
if (StackFrame::IsTypeMarker(marker)) break;
|
||||
if (code_obj->is_interpreter_trampoline_builtin()) {
|
||||
return INTERPRETED;
|
||||
}
|
||||
if (code_obj->is_turbofanned()) {
|
||||
// TODO(bmeurer): We treat frames for BUILTIN Code objects as
|
||||
// OptimizedFrame for now (all the builtins with JavaScript
|
||||
// linkage are actually generated with TurboFan currently, so
|
||||
// this is sound).
|
||||
return OPTIMIZED;
|
||||
}
|
||||
return BUILTIN;
|
||||
case Code::OPTIMIZED_FUNCTION:
|
||||
return OPTIMIZED;
|
||||
case Code::WASM_FUNCTION:
|
||||
return WASM_COMPILED;
|
||||
case Code::WASM_TO_JS_FUNCTION:
|
||||
return WASM_TO_JS;
|
||||
case Code::JS_TO_WASM_FUNCTION:
|
||||
return JS_TO_WASM;
|
||||
case Code::WASM_INTERPRETER_ENTRY:
|
||||
Address pc = *(state->pc_address);
|
||||
// If FLAG_wasm_jit_to_native is disabled, we still have an empty
|
||||
// wasm_code_manager, and this test will be false. This is easier to read
|
||||
// than checking the flag, then getting the code, and then, if both are true
|
||||
// (non-null, respectivelly), going down the wasm_code path.
|
||||
wasm::WasmCode* wasm_code =
|
||||
iterator->isolate()->wasm_code_manager()->LookupCode(pc);
|
||||
if (wasm_code != nullptr) {
|
||||
switch (wasm_code->kind()) {
|
||||
case wasm::WasmCode::InterpreterStub:
|
||||
return WASM_INTERPRETER_ENTRY;
|
||||
case Code::C_WASM_ENTRY:
|
||||
return C_WASM_ENTRY;
|
||||
case wasm::WasmCode::Function:
|
||||
case wasm::WasmCode::CopiedStub:
|
||||
return WASM_COMPILED;
|
||||
case wasm::WasmCode::LazyStub:
|
||||
if (StackFrame::IsTypeMarker(marker)) break;
|
||||
return BUILTIN;
|
||||
case wasm::WasmCode::WasmToJsWrapper:
|
||||
case wasm::WasmCode::WasmToWasmWrapper:
|
||||
return WASM_TO_JS;
|
||||
default:
|
||||
// All other types should have an explicit marker
|
||||
break;
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
return NONE;
|
||||
// Look up the code object to figure out the type of the stack frame.
|
||||
Code* code_obj = GetContainingCode(iterator->isolate(), pc);
|
||||
if (code_obj != nullptr) {
|
||||
switch (code_obj->kind()) {
|
||||
case Code::BUILTIN:
|
||||
if (StackFrame::IsTypeMarker(marker)) break;
|
||||
if (code_obj->is_interpreter_trampoline_builtin()) {
|
||||
return INTERPRETED;
|
||||
}
|
||||
if (code_obj->is_turbofanned()) {
|
||||
// TODO(bmeurer): We treat frames for BUILTIN Code objects as
|
||||
// OptimizedFrame for now (all the builtins with JavaScript
|
||||
// linkage are actually generated with TurboFan currently, so
|
||||
// this is sound).
|
||||
return OPTIMIZED;
|
||||
}
|
||||
return BUILTIN;
|
||||
case Code::OPTIMIZED_FUNCTION:
|
||||
return OPTIMIZED;
|
||||
case Code::WASM_FUNCTION:
|
||||
return WASM_COMPILED;
|
||||
case Code::WASM_TO_JS_FUNCTION:
|
||||
return WASM_TO_JS;
|
||||
case Code::JS_TO_WASM_FUNCTION:
|
||||
return JS_TO_WASM;
|
||||
case Code::WASM_INTERPRETER_ENTRY:
|
||||
return WASM_INTERPRETER_ENTRY;
|
||||
case Code::C_WASM_ENTRY:
|
||||
return C_WASM_ENTRY;
|
||||
default:
|
||||
// All other types should have an explicit marker
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DCHECK(StackFrame::IsTypeMarker(marker));
|
||||
StackFrame::Type candidate = StackFrame::MarkerToType(marker);
|
||||
switch (candidate) {
|
||||
@ -751,20 +775,38 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
|
||||
|
||||
// Find the code and compute the safepoint information.
|
||||
Address inner_pointer = pc();
|
||||
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
|
||||
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
|
||||
if (!entry->safepoint_entry.is_valid()) {
|
||||
entry->safepoint_entry = entry->code->GetSafepointEntry(inner_pointer);
|
||||
DCHECK(entry->safepoint_entry.is_valid());
|
||||
const wasm::WasmCode* wasm_code =
|
||||
FLAG_wasm_jit_to_native
|
||||
? isolate()->wasm_code_manager()->LookupCode(inner_pointer)
|
||||
: nullptr;
|
||||
SafepointEntry safepoint_entry;
|
||||
uint32_t stack_slots;
|
||||
Code* code = nullptr;
|
||||
bool has_tagged_params = false;
|
||||
if (wasm_code != nullptr) {
|
||||
SafepointTable table(wasm_code->instructions().start(),
|
||||
wasm_code->safepoint_table_offset(),
|
||||
wasm_code->stack_slots());
|
||||
safepoint_entry = table.FindEntry(inner_pointer);
|
||||
stack_slots = wasm_code->stack_slots();
|
||||
has_tagged_params = wasm_code->kind() != wasm::WasmCode::Function;
|
||||
} else {
|
||||
DCHECK(entry->safepoint_entry.Equals(
|
||||
entry->code->GetSafepointEntry(inner_pointer)));
|
||||
}
|
||||
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
|
||||
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
|
||||
if (!entry->safepoint_entry.is_valid()) {
|
||||
entry->safepoint_entry = entry->code->GetSafepointEntry(inner_pointer);
|
||||
DCHECK(entry->safepoint_entry.is_valid());
|
||||
} else {
|
||||
DCHECK(entry->safepoint_entry.Equals(
|
||||
entry->code->GetSafepointEntry(inner_pointer)));
|
||||
}
|
||||
|
||||
Code* code = entry->code;
|
||||
SafepointEntry safepoint_entry = entry->safepoint_entry;
|
||||
unsigned stack_slots = code->stack_slots();
|
||||
unsigned slot_space = stack_slots * kPointerSize;
|
||||
code = entry->code;
|
||||
safepoint_entry = entry->safepoint_entry;
|
||||
stack_slots = code->stack_slots();
|
||||
has_tagged_params = code->has_tagged_params();
|
||||
}
|
||||
uint32_t slot_space = stack_slots * kPointerSize;
|
||||
|
||||
// Determine the fixed header and spill slot area size.
|
||||
int frame_header_size = StandardFrameConstants::kFixedFrameSizeFromFp;
|
||||
@ -847,7 +889,7 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
|
||||
safepoint_bits += kNumSafepointRegisters >> kBitsPerByteLog2;
|
||||
|
||||
// Visit the rest of the parameters if they are tagged.
|
||||
if (code->has_tagged_params()) {
|
||||
if (has_tagged_params) {
|
||||
v->VisitRootPointers(Root::kTop, parameters_base, parameters_limit);
|
||||
}
|
||||
|
||||
@ -860,8 +902,11 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
|
||||
}
|
||||
}
|
||||
|
||||
// Visit the return address in the callee and incoming arguments.
|
||||
IteratePc(v, pc_address(), constant_pool_address(), code);
|
||||
// For wasm-to-js cases, we can skip this.
|
||||
if (code != nullptr) {
|
||||
// Visit the return address in the callee and incoming arguments.
|
||||
IteratePc(v, pc_address(), constant_pool_address(), code);
|
||||
}
|
||||
|
||||
if (!is_wasm() && !is_wasm_to_js()) {
|
||||
// If this frame has JavaScript ABI, visit the context (in stub and JS
|
||||
@ -1215,7 +1260,7 @@ Handle<Context> FrameSummary::WasmFrameSummary::native_context() const {
|
||||
}
|
||||
|
||||
FrameSummary::WasmCompiledFrameSummary::WasmCompiledFrameSummary(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<Code> code,
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, WasmCodeWrapper code,
|
||||
int code_offset, bool at_to_number_conversion)
|
||||
: WasmFrameSummary(isolate, WASM_COMPILED, instance,
|
||||
at_to_number_conversion),
|
||||
@ -1223,16 +1268,38 @@ FrameSummary::WasmCompiledFrameSummary::WasmCompiledFrameSummary(
|
||||
code_offset_(code_offset) {}
|
||||
|
||||
uint32_t FrameSummary::WasmCompiledFrameSummary::function_index() const {
|
||||
FixedArray* deopt_data = code()->deoptimization_data();
|
||||
DCHECK_EQ(2, deopt_data->length());
|
||||
DCHECK(deopt_data->get(1)->IsSmi());
|
||||
int val = Smi::ToInt(deopt_data->get(1));
|
||||
DCHECK_LE(0, val);
|
||||
return static_cast<uint32_t>(val);
|
||||
if (code().IsCodeObject()) {
|
||||
FixedArray* deopt_data = code().GetCode()->deoptimization_data();
|
||||
DCHECK_EQ(2, deopt_data->length());
|
||||
DCHECK(deopt_data->get(1)->IsSmi());
|
||||
int val = Smi::ToInt(deopt_data->get(1));
|
||||
DCHECK_LE(0, val);
|
||||
return static_cast<uint32_t>(val);
|
||||
}
|
||||
return code().GetWasmCode()->index();
|
||||
}
|
||||
|
||||
int FrameSummary::WasmCompiledFrameSummary::GetWasmSourcePosition(
|
||||
const wasm::WasmCode* code, int offset) {
|
||||
int position = 0;
|
||||
// Subtract one because the current PC is one instruction after the call site.
|
||||
offset--;
|
||||
Handle<ByteArray> source_position_table(
|
||||
ByteArray::cast(code->owner()->compiled_module()->source_positions()->get(
|
||||
code->index())));
|
||||
for (SourcePositionTableIterator iterator(source_position_table);
|
||||
!iterator.done() && iterator.code_offset() <= offset;
|
||||
iterator.Advance()) {
|
||||
position = iterator.source_position().ScriptOffset();
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
int FrameSummary::WasmCompiledFrameSummary::byte_offset() const {
|
||||
return AbstractCode::cast(*code())->SourcePosition(code_offset());
|
||||
if (code().IsCodeObject()) {
|
||||
return AbstractCode::cast(*code().GetCode())->SourcePosition(code_offset());
|
||||
}
|
||||
return GetWasmSourcePosition(code_.GetWasmCode(), code_offset());
|
||||
}
|
||||
|
||||
FrameSummary::WasmInterpretedFrameSummary::WasmInterpretedFrameSummary(
|
||||
@ -1658,7 +1725,11 @@ Address WasmCompiledFrame::GetCallerStackPointer() const {
|
||||
}
|
||||
|
||||
WasmInstanceObject* WasmCompiledFrame::wasm_instance() const {
|
||||
WasmInstanceObject* obj = WasmInstanceObject::GetOwningInstance(LookupCode());
|
||||
WasmInstanceObject* obj =
|
||||
FLAG_wasm_jit_to_native
|
||||
? WasmInstanceObject::GetOwningInstance(
|
||||
isolate()->wasm_code_manager()->LookupCode(pc()))
|
||||
: WasmInstanceObject::GetOwningInstanceGC(LookupCode());
|
||||
// This is a live stack frame; it must have a live instance.
|
||||
DCHECK_NOT_NULL(obj);
|
||||
return obj;
|
||||
@ -1678,9 +1749,25 @@ int WasmCompiledFrame::position() const {
|
||||
|
||||
void WasmCompiledFrame::Summarize(std::vector<FrameSummary>* functions) const {
|
||||
DCHECK(functions->empty());
|
||||
Handle<Code> code(LookupCode(), isolate());
|
||||
int offset = static_cast<int>(pc() - code->instruction_start());
|
||||
Handle<WasmInstanceObject> instance(wasm_instance(), isolate());
|
||||
WasmCodeWrapper code;
|
||||
Handle<WasmInstanceObject> instance;
|
||||
int offset = -1;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
code = WasmCodeWrapper(isolate()->wasm_code_manager()->LookupCode(pc()));
|
||||
offset =
|
||||
static_cast<int>(pc() - code.GetWasmCode()->instructions().start());
|
||||
instance = Handle<WasmInstanceObject>(
|
||||
WasmInstanceObject::cast(code.GetWasmCode()
|
||||
->owner()
|
||||
->compiled_module()
|
||||
->weak_owning_instance()
|
||||
->value()),
|
||||
isolate());
|
||||
} else {
|
||||
code = WasmCodeWrapper(Handle<Code>(LookupCode(), isolate()));
|
||||
offset = static_cast<int>(pc() - code.GetCode()->instruction_start());
|
||||
instance = Handle<WasmInstanceObject>(wasm_instance(), isolate());
|
||||
}
|
||||
FrameSummary::WasmCompiledFrameSummary summary(
|
||||
isolate(), instance, code, offset, at_to_number_conversion());
|
||||
functions->push_back(summary);
|
||||
@ -1690,10 +1777,21 @@ bool WasmCompiledFrame::at_to_number_conversion() const {
|
||||
// Check whether our callee is a WASM_TO_JS frame, and this frame is at the
|
||||
// ToNumber conversion call.
|
||||
Address callee_pc = reinterpret_cast<Address>(this->callee_pc());
|
||||
Code* code = callee_pc ? isolate()->FindCodeObject(callee_pc) : nullptr;
|
||||
if (!code || code->kind() != Code::WASM_TO_JS_FUNCTION) return false;
|
||||
int offset = static_cast<int>(callee_pc - code->instruction_start());
|
||||
int pos = AbstractCode::cast(code)->SourcePosition(offset);
|
||||
int pos = -1;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
wasm::WasmCode* code =
|
||||
callee_pc ? isolate()->wasm_code_manager()->LookupCode(callee_pc)
|
||||
: nullptr;
|
||||
if (!code || code->kind() != wasm::WasmCode::WasmToJsWrapper) return false;
|
||||
int offset = static_cast<int>(callee_pc - code->instructions().start());
|
||||
pos = FrameSummary::WasmCompiledFrameSummary::GetWasmSourcePosition(code,
|
||||
offset);
|
||||
} else {
|
||||
Code* code = callee_pc ? isolate()->FindCodeObject(callee_pc) : nullptr;
|
||||
if (!code || code->kind() != Code::WASM_TO_JS_FUNCTION) return false;
|
||||
int offset = static_cast<int>(callee_pc - code->instruction_start());
|
||||
pos = AbstractCode::cast(code)->SourcePosition(offset);
|
||||
}
|
||||
DCHECK(pos == 0 || pos == 1);
|
||||
// The imported call has position 0, ToNumber has position 1.
|
||||
return !!pos;
|
||||
@ -1701,11 +1799,26 @@ bool WasmCompiledFrame::at_to_number_conversion() const {
|
||||
|
||||
int WasmCompiledFrame::LookupExceptionHandlerInTable(int* stack_slots) {
|
||||
DCHECK_NOT_NULL(stack_slots);
|
||||
Code* code = LookupCode();
|
||||
HandlerTable* table = HandlerTable::cast(code->handler_table());
|
||||
int pc_offset = static_cast<int>(pc() - code->entry());
|
||||
*stack_slots = code->stack_slots();
|
||||
return table->LookupReturn(pc_offset);
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
Code* code = LookupCode();
|
||||
HandlerTable* table = HandlerTable::cast(code->handler_table());
|
||||
int pc_offset = static_cast<int>(pc() - code->entry());
|
||||
*stack_slots = code->stack_slots();
|
||||
return table->LookupReturn(pc_offset);
|
||||
}
|
||||
wasm::WasmCode* code = isolate()->wasm_code_manager()->LookupCode(pc());
|
||||
if (!code->IsAnonymous()) {
|
||||
Object* table_entry =
|
||||
code->owner()->compiled_module()->ptr_to_handler_table()->get(
|
||||
code->index());
|
||||
if (table_entry->IsHandlerTable()) {
|
||||
HandlerTable* table = HandlerTable::cast(table_entry);
|
||||
int pc_offset = static_cast<int>(pc() - code->instructions().start());
|
||||
*stack_slots = static_cast<int>(code->stack_slots());
|
||||
return table->LookupReturn(pc_offset);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void WasmInterpreterEntryFrame::Iterate(RootVisitor* v) const {
|
||||
@ -1736,11 +1849,19 @@ void WasmInterpreterEntryFrame::Summarize(
|
||||
}
|
||||
|
||||
Code* WasmInterpreterEntryFrame::unchecked_code() const {
|
||||
return isolate()->FindCodeObject(pc());
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
UNIMPLEMENTED();
|
||||
} else {
|
||||
return isolate()->FindCodeObject(pc());
|
||||
}
|
||||
}
|
||||
|
||||
WasmInstanceObject* WasmInterpreterEntryFrame::wasm_instance() const {
|
||||
WasmInstanceObject* ret = WasmInstanceObject::GetOwningInstance(LookupCode());
|
||||
WasmInstanceObject* ret =
|
||||
FLAG_wasm_jit_to_native
|
||||
? WasmInstanceObject::GetOwningInstance(
|
||||
isolate()->wasm_code_manager()->LookupCode(pc()))
|
||||
: WasmInstanceObject::GetOwningInstanceGC(LookupCode());
|
||||
// This is a live stack frame, there must be a live wasm instance available.
|
||||
DCHECK_NOT_NULL(ret);
|
||||
return ret;
|
||||
@ -1965,15 +2086,22 @@ void JavaScriptFrame::Iterate(RootVisitor* v) const {
|
||||
}
|
||||
|
||||
void InternalFrame::Iterate(RootVisitor* v) const {
|
||||
Code* code = LookupCode();
|
||||
IteratePc(v, pc_address(), constant_pool_address(), code);
|
||||
// Internal frames typically do not receive any arguments, hence their stack
|
||||
// only contains tagged pointers.
|
||||
// We are misusing the has_tagged_params flag here to tell us whether
|
||||
// the full stack frame contains only tagged pointers or only raw values.
|
||||
// This is used for the WasmCompileLazy builtin, where we actually pass
|
||||
// untagged arguments and also store untagged values on the stack.
|
||||
if (code->has_tagged_params()) IterateExpressions(v);
|
||||
wasm::WasmCode* wasm_code =
|
||||
FLAG_wasm_jit_to_native ? isolate()->wasm_code_manager()->LookupCode(pc())
|
||||
: nullptr;
|
||||
if (wasm_code != nullptr) {
|
||||
DCHECK(wasm_code->kind() == wasm::WasmCode::LazyStub);
|
||||
} else {
|
||||
Code* code = LookupCode();
|
||||
IteratePc(v, pc_address(), constant_pool_address(), code);
|
||||
// Internal frames typically do not receive any arguments, hence their stack
|
||||
// only contains tagged pointers.
|
||||
// We are misusing the has_tagged_params flag here to tell us whether
|
||||
// the full stack frame contains only tagged pointers or only raw values.
|
||||
// This is used for the WasmCompileLazy builtin, where we actually pass
|
||||
// untagged arguments and also store untagged values on the stack.
|
||||
if (code->has_tagged_params()) IterateExpressions(v);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
13
src/frames.h
13
src/frames.h
@ -14,6 +14,9 @@
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
class WasmCode;
|
||||
}
|
||||
|
||||
class AbstractCode;
|
||||
class Debug;
|
||||
@ -526,15 +529,17 @@ class FrameSummary BASE_EMBEDDED {
|
||||
|
||||
class WasmCompiledFrameSummary : public WasmFrameSummary {
|
||||
public:
|
||||
WasmCompiledFrameSummary(Isolate*, Handle<WasmInstanceObject>, Handle<Code>,
|
||||
int code_offset, bool at_to_number_conversion);
|
||||
WasmCompiledFrameSummary(Isolate*, Handle<WasmInstanceObject>,
|
||||
WasmCodeWrapper, int code_offset,
|
||||
bool at_to_number_conversion);
|
||||
uint32_t function_index() const;
|
||||
Handle<Code> code() const { return code_; }
|
||||
WasmCodeWrapper code() const { return code_; }
|
||||
int code_offset() const { return code_offset_; }
|
||||
int byte_offset() const;
|
||||
static int GetWasmSourcePosition(const wasm::WasmCode* code, int offset);
|
||||
|
||||
private:
|
||||
Handle<Code> code_;
|
||||
WasmCodeWrapper const code_;
|
||||
int code_offset_;
|
||||
};
|
||||
|
||||
|
@ -177,6 +177,7 @@ const int kElidedFrameSlots = 0;
|
||||
#endif
|
||||
|
||||
const int kDoubleSizeLog2 = 3;
|
||||
const size_t kMaxWasmCodeMemory = 256 * MB;
|
||||
|
||||
#if V8_HOST_ARCH_64_BIT
|
||||
const int kPointerSizeLog2 = 3;
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "src/visitors.h"
|
||||
#include "src/vm-state-inl.h"
|
||||
#include "src/wasm/compilation-manager.h"
|
||||
#include "src/wasm/wasm-heap.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "src/zone/accounting-allocator.h"
|
||||
|
||||
@ -427,6 +428,10 @@ class FrameArrayBuilder {
|
||||
// Handle a WASM compiled frame.
|
||||
//====================================================================
|
||||
const auto& summary = summ.AsWasmCompiled();
|
||||
if (!summary.code().IsCodeObject() &&
|
||||
summary.code().GetWasmCode()->kind() != wasm::WasmCode::Function) {
|
||||
continue;
|
||||
}
|
||||
Handle<WasmInstanceObject> instance = summary.wasm_instance();
|
||||
int flags = 0;
|
||||
if (instance->compiled_module()->is_asm_js()) {
|
||||
@ -439,9 +444,8 @@ class FrameArrayBuilder {
|
||||
}
|
||||
|
||||
elements_ = FrameArray::AppendWasmFrame(
|
||||
elements_, instance, summary.function_index(),
|
||||
Handle<AbstractCode>::cast(summary.code()), summary.code_offset(),
|
||||
flags);
|
||||
elements_, instance, summary.function_index(), summary.code(),
|
||||
summary.code_offset(), flags);
|
||||
} else if (summ.IsWasmInterpreted()) {
|
||||
//====================================================================
|
||||
// Handle a WASM interpreted frame.
|
||||
@ -450,9 +454,9 @@ class FrameArrayBuilder {
|
||||
Handle<WasmInstanceObject> instance = summary.wasm_instance();
|
||||
int flags = FrameArray::kIsWasmInterpretedFrame;
|
||||
DCHECK(!instance->compiled_module()->is_asm_js());
|
||||
elements_ = FrameArray::AppendWasmFrame(
|
||||
elements_, instance, summary.function_index(),
|
||||
Handle<AbstractCode>::null(), summary.byte_offset(), flags);
|
||||
elements_ = FrameArray::AppendWasmFrame(elements_, instance,
|
||||
summary.function_index(), {},
|
||||
summary.byte_offset(), flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1295,9 +1299,17 @@ Object* Isolate::UnwindAndFindHandler() {
|
||||
trap_handler::SetThreadInWasm();
|
||||
|
||||
set_wasm_caught_exception(exception);
|
||||
Code* code = frame->LookupCode();
|
||||
return FoundHandler(nullptr, code->instruction_start(), offset,
|
||||
code->constant_pool(), return_sp, frame->fp());
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
wasm::WasmCode* wasm_code =
|
||||
wasm_code_manager()->LookupCode(frame->pc());
|
||||
return FoundHandler(nullptr, wasm_code->instructions().start(),
|
||||
offset, wasm_code->constant_pool(), return_sp,
|
||||
frame->fp());
|
||||
} else {
|
||||
Code* code = frame->LookupCode();
|
||||
return FoundHandler(nullptr, code->instruction_start(), offset,
|
||||
code->constant_pool(), return_sp, frame->fp());
|
||||
}
|
||||
}
|
||||
|
||||
case StackFrame::OPTIMIZED: {
|
||||
@ -1673,9 +1685,19 @@ bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target,
|
||||
Handle<WasmCompiledModule> compiled_module(
|
||||
WasmInstanceObject::cast(elements->WasmInstance(i))
|
||||
->compiled_module());
|
||||
int func_index = elements->WasmFunctionIndex(i)->value();
|
||||
uint32_t func_index =
|
||||
static_cast<uint32_t>(elements->WasmFunctionIndex(i)->value());
|
||||
int code_offset = elements->Offset(i)->value();
|
||||
int byte_offset = elements->Code(i)->SourcePosition(code_offset);
|
||||
|
||||
// TODO(titzer): store a reference to the code object in FrameArray;
|
||||
// a second lookup here could lead to inconsistency.
|
||||
int byte_offset =
|
||||
FLAG_wasm_jit_to_native
|
||||
? FrameSummary::WasmCompiledFrameSummary::GetWasmSourcePosition(
|
||||
compiled_module->GetNativeModule()->GetCode(func_index),
|
||||
code_offset)
|
||||
: elements->Code(i)->SourcePosition(code_offset);
|
||||
|
||||
bool is_at_number_conversion =
|
||||
elements->IsAsmJsWasmFrame(i) &&
|
||||
elements->Flags(i)->value() & FrameArray::kAsmJsAtNumberConversion;
|
||||
@ -2815,6 +2837,17 @@ bool Isolate::Init(StartupDeserializer* des) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup the wasm code manager. Currently, there's one per Isolate.
|
||||
if (!wasm_code_manager_) {
|
||||
size_t max_code_size = kMaxWasmCodeMemory;
|
||||
if (kRequiresCodeRange) {
|
||||
max_code_size = std::min(max_code_size,
|
||||
heap_.memory_allocator()->code_range()->size());
|
||||
}
|
||||
wasm_code_manager_.reset(new wasm::WasmCodeManager(
|
||||
reinterpret_cast<v8::Isolate*>(this), max_code_size));
|
||||
}
|
||||
|
||||
// Initialize the interface descriptors ahead of time.
|
||||
#define INTERFACE_DESCRIPTOR(Name, ...) \
|
||||
{ Name##Descriptor(this); }
|
||||
@ -3854,6 +3887,10 @@ void Isolate::PrintWithTimestamp(const char* format, ...) {
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
wasm::WasmCodeManager* Isolate::wasm_code_manager() {
|
||||
return wasm_code_manager_.get();
|
||||
}
|
||||
|
||||
bool StackLimitCheck::JsHasOverflowed(uintptr_t gap) const {
|
||||
StackGuard* stack_guard = isolate_->stack_guard();
|
||||
#ifdef USE_SIMULATOR
|
||||
|
@ -110,6 +110,7 @@ class Interpreter;
|
||||
|
||||
namespace wasm {
|
||||
class CompilationManager;
|
||||
class WasmCodeManager;
|
||||
}
|
||||
|
||||
#define RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate) \
|
||||
@ -905,6 +906,7 @@ class Isolate {
|
||||
}
|
||||
StackGuard* stack_guard() { return &stack_guard_; }
|
||||
Heap* heap() { return &heap_; }
|
||||
V8_EXPORT_PRIVATE wasm::WasmCodeManager* wasm_code_manager();
|
||||
StubCache* load_stub_cache() { return load_stub_cache_; }
|
||||
StubCache* store_stub_cache() { return store_stub_cache_; }
|
||||
DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; }
|
||||
@ -1652,6 +1654,8 @@ class Isolate {
|
||||
|
||||
size_t elements_deletion_counter_ = 0;
|
||||
|
||||
std::unique_ptr<wasm::WasmCodeManager> wasm_code_manager_;
|
||||
|
||||
// The top entry of the v8::Context::BackupIncumbentScope stack.
|
||||
const v8::Context::BackupIncumbentScope* top_backup_incumbent_scope_ =
|
||||
nullptr;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "src/keys.h"
|
||||
#include "src/objects/frame-array-inl.h"
|
||||
#include "src/string-builder.h"
|
||||
#include "src/wasm/wasm-heap.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -649,9 +650,18 @@ void WasmStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
|
||||
wasm_instance_ = handle(array->WasmInstance(frame_ix), isolate);
|
||||
wasm_func_index_ = array->WasmFunctionIndex(frame_ix)->value();
|
||||
if (array->IsWasmInterpretedFrame(frame_ix)) {
|
||||
code_ = Handle<AbstractCode>::null();
|
||||
code_ = {};
|
||||
} else {
|
||||
code_ = handle(array->Code(frame_ix), isolate);
|
||||
code_ =
|
||||
FLAG_wasm_jit_to_native
|
||||
? WasmCodeWrapper(
|
||||
wasm_instance_->compiled_module()->GetNativeModule()->GetCode(
|
||||
wasm_func_index_))
|
||||
: WasmCodeWrapper(handle(
|
||||
Code::cast(
|
||||
wasm_instance_->compiled_module()->code_table()->get(
|
||||
wasm_func_index_)),
|
||||
isolate));
|
||||
}
|
||||
offset_ = array->Offset(frame_ix)->value();
|
||||
}
|
||||
@ -712,7 +722,13 @@ MaybeHandle<String> WasmStackFrame::ToString() {
|
||||
}
|
||||
|
||||
int WasmStackFrame::GetPosition() const {
|
||||
return IsInterpreted() ? offset_ : code_->SourcePosition(offset_);
|
||||
return IsInterpreted()
|
||||
? offset_
|
||||
: (code_.IsCodeObject()
|
||||
? Handle<AbstractCode>::cast(code_.GetCode())
|
||||
->SourcePosition(offset_)
|
||||
: FrameSummary::WasmCompiledFrameSummary::
|
||||
GetWasmSourcePosition(code_.GetWasmCode(), offset_));
|
||||
}
|
||||
|
||||
Handle<Object> WasmStackFrame::Null() const {
|
||||
@ -759,7 +775,11 @@ Handle<Object> AsmJsWasmStackFrame::GetScriptNameOrSourceUrl() {
|
||||
|
||||
int AsmJsWasmStackFrame::GetPosition() const {
|
||||
DCHECK_LE(0, offset_);
|
||||
int byte_offset = code_->SourcePosition(offset_);
|
||||
int byte_offset =
|
||||
code_.IsCodeObject()
|
||||
? Handle<AbstractCode>::cast(code_.GetCode())->SourcePosition(offset_)
|
||||
: FrameSummary::WasmCompiledFrameSummary::GetWasmSourcePosition(
|
||||
code_.GetWasmCode(), offset_);
|
||||
Handle<WasmCompiledModule> compiled_module(wasm_instance_->compiled_module(),
|
||||
isolate_);
|
||||
DCHECK_LE(0, byte_offset);
|
||||
|
@ -13,9 +13,13 @@
|
||||
#include <memory>
|
||||
|
||||
#include "src/handles.h"
|
||||
#include "src/wasm/wasm-code-wrapper.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
class WasmCode;
|
||||
}
|
||||
|
||||
// Forward declarations.
|
||||
class AbstractCode;
|
||||
@ -161,7 +165,7 @@ class WasmStackFrame : public StackFrameBase {
|
||||
|
||||
Handle<WasmInstanceObject> wasm_instance_;
|
||||
uint32_t wasm_func_index_;
|
||||
Handle<AbstractCode> code_; // null handle for interpreted frames.
|
||||
WasmCodeWrapper code_; // null for interpreted frames.
|
||||
int offset_;
|
||||
|
||||
private:
|
||||
|
@ -10344,14 +10344,19 @@ Handle<FrameArray> FrameArray::AppendJSFrame(Handle<FrameArray> in,
|
||||
// static
|
||||
Handle<FrameArray> FrameArray::AppendWasmFrame(
|
||||
Handle<FrameArray> in, Handle<WasmInstanceObject> wasm_instance,
|
||||
int wasm_function_index, Handle<AbstractCode> code, int offset, int flags) {
|
||||
int wasm_function_index, WasmCodeWrapper code, int offset, int flags) {
|
||||
const int frame_count = in->FrameCount();
|
||||
const int new_length = LengthFor(frame_count + 1);
|
||||
Handle<FrameArray> array = EnsureSpace(in, new_length);
|
||||
array->SetWasmInstance(frame_count, *wasm_instance);
|
||||
array->SetWasmFunctionIndex(frame_count, Smi::FromInt(wasm_function_index));
|
||||
// code will be a null handle for interpreted wasm frames.
|
||||
if (!code.is_null()) array->SetCode(frame_count, *code);
|
||||
if (!code.IsCodeObject()) {
|
||||
array->SetIsWasmInterpreterFrame(frame_count, Smi::FromInt(code.is_null()));
|
||||
} else {
|
||||
if (!code.is_null())
|
||||
array->SetCode(frame_count, AbstractCode::cast(*code.GetCode()));
|
||||
}
|
||||
array->SetOffset(frame_count, Smi::FromInt(offset));
|
||||
array->SetFlags(frame_count, Smi::FromInt(flags));
|
||||
array->set(kFrameCountIndex, Smi::FromInt(frame_count + 1));
|
||||
|
@ -160,6 +160,7 @@ class Code : public HeapObject {
|
||||
DECL_ACCESSORS(source_position_table, Object)
|
||||
inline ByteArray* SourcePositionTable() const;
|
||||
|
||||
// TODO(mtrofin): remove when we don't need FLAG_wasm_jit_to_native
|
||||
// [protected instructions]: Array containing list of protected
|
||||
// instructions and corresponding landing pad offset.
|
||||
DECL_ACCESSORS(protected_instructions, FixedArray)
|
||||
|
@ -20,6 +20,7 @@ class Handle;
|
||||
#define FRAME_ARRAY_FIELD_LIST(V) \
|
||||
V(WasmInstance, WasmInstanceObject) \
|
||||
V(WasmFunctionIndex, Smi) \
|
||||
V(IsWasmInterpreterFrame, Smi) \
|
||||
V(Receiver, Object) \
|
||||
V(Function, JSFunction) \
|
||||
V(Code, AbstractCode) \
|
||||
@ -59,8 +60,7 @@ class FrameArray : public FixedArray {
|
||||
int flags);
|
||||
static Handle<FrameArray> AppendWasmFrame(
|
||||
Handle<FrameArray> in, Handle<WasmInstanceObject> wasm_instance,
|
||||
int wasm_function_index, Handle<AbstractCode> code, int offset,
|
||||
int flags);
|
||||
int wasm_function_index, WasmCodeWrapper code, int offset, int flags);
|
||||
|
||||
DECL_CAST(FrameArray)
|
||||
|
||||
@ -74,6 +74,7 @@ class FrameArray : public FixedArray {
|
||||
|
||||
static const int kWasmInstanceOffset = 0;
|
||||
static const int kWasmFunctionIndexOffset = 1;
|
||||
static const int kIsWasmInterpreterFrameOffset = 2;
|
||||
|
||||
static const int kReceiverOffset = 0;
|
||||
static const int kFunctionOffset = 1;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "src/wasm/memory-tracing.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-serialization.h"
|
||||
|
||||
namespace {
|
||||
struct WasmCompileControls {
|
||||
@ -481,48 +482,100 @@ RUNTIME_FUNCTION(Runtime_CheckWasmWrapperElision) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(Smi, type, 1);
|
||||
Handle<Code> export_code = handle(function->code());
|
||||
CHECK(export_code->kind() == Code::JS_TO_WASM_FUNCTION);
|
||||
int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
|
||||
int const mask =
|
||||
RelocInfo::ModeMask(FLAG_wasm_jit_to_native ? RelocInfo::JS_TO_WASM_CALL
|
||||
: RelocInfo::CODE_TARGET);
|
||||
// check the type of the $export_fct
|
||||
Handle<Code> export_fct;
|
||||
wasm::WasmCode* export_fct = nullptr;
|
||||
Handle<Code> export_fct_handle;
|
||||
wasm::WasmCode* intermediate_fct = nullptr;
|
||||
Handle<Code> intermediate_fct_handle;
|
||||
|
||||
int count = 0;
|
||||
for (RelocIterator it(*export_code, mask); !it.done(); it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == Code::WASM_FUNCTION) {
|
||||
++count;
|
||||
export_fct = handle(target);
|
||||
Address target_address = FLAG_wasm_jit_to_native
|
||||
? rinfo->js_to_wasm_address()
|
||||
: rinfo->target_address();
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
wasm::WasmCode* target =
|
||||
isolate->wasm_code_manager()->LookupCode(target_address);
|
||||
if (target->kind() == wasm::WasmCode::Function) {
|
||||
++count;
|
||||
export_fct = target;
|
||||
}
|
||||
} else {
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == Code::WASM_FUNCTION) {
|
||||
++count;
|
||||
export_fct_handle = handle(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_EQ(count, 1);
|
||||
// check the type of the intermediate_fct
|
||||
Handle<Code> intermediate_fct;
|
||||
count = 0;
|
||||
for (RelocIterator it(*export_fct, mask); !it.done(); it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == Code::WASM_FUNCTION) {
|
||||
++count;
|
||||
intermediate_fct = handle(target);
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
for (RelocIterator it(export_fct->instructions(), export_fct->reloc_info(),
|
||||
export_fct->constant_pool(),
|
||||
RelocInfo::ModeMask(RelocInfo::WASM_CALL));
|
||||
!it.done(); it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
wasm::WasmCode* target =
|
||||
isolate->wasm_code_manager()->LookupCode(target_address);
|
||||
if (target->kind() == wasm::WasmCode::Function) {
|
||||
++count;
|
||||
intermediate_fct = target;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
count = 0;
|
||||
for (RelocIterator it(*export_fct_handle, mask); !it.done(); it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == Code::WASM_FUNCTION) {
|
||||
++count;
|
||||
intermediate_fct_handle = handle(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_EQ(count, 1);
|
||||
// Check the type of the imported exported function, it should be also a wasm
|
||||
// function in our case.
|
||||
Handle<Code> imported_fct;
|
||||
CHECK(type->value() == 0 || type->value() == 1);
|
||||
|
||||
Code::Kind target_kind = type->value() == 0 ? Code::WASM_TO_WASM_FUNCTION
|
||||
: Code::WASM_TO_JS_FUNCTION;
|
||||
count = 0;
|
||||
for (RelocIterator it(*intermediate_fct, mask); !it.done(); it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == target_kind) {
|
||||
++count;
|
||||
imported_fct = handle(target);
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
wasm::WasmCode::Kind target_kind = type->value() == 0
|
||||
? wasm::WasmCode::WasmToWasmWrapper
|
||||
: wasm::WasmCode::WasmToJsWrapper;
|
||||
for (RelocIterator it(intermediate_fct->instructions(),
|
||||
intermediate_fct->reloc_info(),
|
||||
intermediate_fct->constant_pool(),
|
||||
RelocInfo::ModeMask(RelocInfo::WASM_CALL));
|
||||
!it.done(); it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
wasm::WasmCode* target =
|
||||
isolate->wasm_code_manager()->LookupCode(target_address);
|
||||
if (target->kind() == target_kind) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Code::Kind target_kind = type->value() == 0 ? Code::WASM_TO_WASM_FUNCTION
|
||||
: Code::WASM_TO_JS_FUNCTION;
|
||||
count = 0;
|
||||
for (RelocIterator it(*intermediate_fct_handle, mask); !it.done();
|
||||
it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == target_kind) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_LE(count, 1);
|
||||
@ -940,13 +993,24 @@ RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0);
|
||||
|
||||
Handle<WasmCompiledModule> orig(module_obj->compiled_module());
|
||||
std::unique_ptr<ScriptData> data =
|
||||
WasmCompiledModuleSerializer::SerializeWasmModule(isolate, orig);
|
||||
void* buff = isolate->array_buffer_allocator()->Allocate(data->length());
|
||||
Handle<JSArrayBuffer> ret = isolate->factory()->NewJSArrayBuffer();
|
||||
JSArrayBuffer::Setup(ret, isolate, false, buff, data->length());
|
||||
memcpy(buff, data->data(), data->length());
|
||||
return *ret;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
std::pair<std::unique_ptr<byte[]>, size_t> serialized_module =
|
||||
wasm::NativeModuleSerializer::SerializeWholeModule(isolate, orig);
|
||||
int data_size = static_cast<int>(serialized_module.second);
|
||||
void* buff = isolate->array_buffer_allocator()->Allocate(data_size);
|
||||
Handle<JSArrayBuffer> ret = isolate->factory()->NewJSArrayBuffer();
|
||||
JSArrayBuffer::Setup(ret, isolate, false, buff, data_size);
|
||||
memcpy(buff, serialized_module.first.get(), data_size);
|
||||
return *ret;
|
||||
} else {
|
||||
std::unique_ptr<ScriptData> data =
|
||||
WasmCompiledModuleSerializer::SerializeWasmModule(isolate, orig);
|
||||
void* buff = isolate->array_buffer_allocator()->Allocate(data->length());
|
||||
Handle<JSArrayBuffer> ret = isolate->factory()->NewJSArrayBuffer();
|
||||
JSArrayBuffer::Setup(ret, isolate, false, buff, data->length());
|
||||
memcpy(buff, data->data(), data->length());
|
||||
return *ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Take an array buffer and attempt to reconstruct a compiled wasm module.
|
||||
@ -958,22 +1022,31 @@ RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, wire_bytes, 1);
|
||||
|
||||
Address mem_start = static_cast<Address>(buffer->backing_store());
|
||||
int mem_size = static_cast<int>(buffer->byte_length()->Number());
|
||||
size_t mem_size = static_cast<size_t>(buffer->byte_length()->Number());
|
||||
|
||||
// DeserializeWasmModule will allocate. We assume JSArrayBuffer doesn't
|
||||
// get relocated.
|
||||
ScriptData sc(mem_start, mem_size);
|
||||
bool already_external = wire_bytes->is_external();
|
||||
if (!already_external) {
|
||||
wire_bytes->set_is_external(true);
|
||||
isolate->heap()->UnregisterArrayBuffer(*wire_bytes);
|
||||
}
|
||||
MaybeHandle<FixedArray> maybe_compiled_module =
|
||||
WasmCompiledModuleSerializer::DeserializeWasmModule(
|
||||
isolate, &sc,
|
||||
Vector<const uint8_t>(
|
||||
reinterpret_cast<uint8_t*>(wire_bytes->backing_store()),
|
||||
static_cast<int>(wire_bytes->byte_length()->Number())));
|
||||
MaybeHandle<FixedArray> maybe_compiled_module;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
maybe_compiled_module =
|
||||
wasm::NativeModuleDeserializer::DeserializeFullBuffer(
|
||||
isolate, {mem_start, mem_size},
|
||||
Vector<const uint8_t>(
|
||||
reinterpret_cast<uint8_t*>(wire_bytes->backing_store()),
|
||||
static_cast<int>(wire_bytes->byte_length()->Number())));
|
||||
} else {
|
||||
ScriptData sc(mem_start, static_cast<int>(mem_size));
|
||||
maybe_compiled_module = WasmCompiledModuleSerializer::DeserializeWasmModule(
|
||||
isolate, &sc,
|
||||
Vector<const uint8_t>(
|
||||
reinterpret_cast<uint8_t*>(wire_bytes->backing_store()),
|
||||
static_cast<int>(wire_bytes->byte_length()->Number())));
|
||||
}
|
||||
if (!already_external) {
|
||||
wire_bytes->set_is_external(false);
|
||||
isolate->heap()->RegisterNewArrayBuffer(*wire_bytes);
|
||||
@ -1086,8 +1159,15 @@ RUNTIME_FUNCTION(Runtime_IsLiftoffFunction) {
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
||||
CHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
|
||||
Handle<Code> wasm_code = WasmExportedFunction::cast(*function)->GetWasmCode();
|
||||
return isolate->heap()->ToBoolean(!wasm_code->is_turbofanned());
|
||||
WasmCodeWrapper wrapper =
|
||||
WasmExportedFunction::cast(*function)->GetWasmCode();
|
||||
if (!wrapper.IsCodeObject()) {
|
||||
const wasm::WasmCode* wasm_code = wrapper.GetWasmCode();
|
||||
return isolate->heap()->ToBoolean(wasm_code->is_liftoff());
|
||||
} else {
|
||||
Handle<Code> wasm_code = wrapper.GetCode();
|
||||
return isolate->heap()->ToBoolean(!wasm_code->is_turbofanned());
|
||||
}
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CompleteInobjectSlackTracking) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/v8memory.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-heap.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
||||
|
||||
@ -29,12 +30,18 @@ WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
|
||||
const Address entry = Isolate::c_entry_fp(isolate->thread_local_top());
|
||||
Address pc =
|
||||
Memory::Address_at(entry + StandardFrameConstants::kCallerPCOffset);
|
||||
Code* code = isolate->inner_pointer_to_code_cache()->GetCacheEntry(pc)->code;
|
||||
WasmInstanceObject* owning_instance =
|
||||
WasmInstanceObject::GetOwningInstance(code);
|
||||
WasmInstanceObject* owning_instance = nullptr;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
owning_instance = WasmInstanceObject::GetOwningInstance(
|
||||
isolate->wasm_code_manager()->LookupCode(pc));
|
||||
} else {
|
||||
owning_instance = WasmInstanceObject::GetOwningInstanceGC(
|
||||
isolate->inner_pointer_to_code_cache()->GetCacheEntry(pc)->code);
|
||||
}
|
||||
CHECK_NOT_NULL(owning_instance);
|
||||
return owning_instance;
|
||||
}
|
||||
|
||||
Context* GetWasmContextOnStackTop(Isolate* isolate) {
|
||||
return GetWasmInstanceOnStackTop(isolate)
|
||||
->compiled_module()
|
||||
@ -287,7 +294,15 @@ RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
|
||||
DCHECK_EQ(0, args.length());
|
||||
HandleScope scope(isolate);
|
||||
|
||||
return *wasm::CompileLazy(isolate);
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
Address new_func = wasm::CompileLazy(isolate);
|
||||
// The alternative to this is having 2 lazy compile builtins. The builtins
|
||||
// are part of the snapshot, so the flag has no impact on the codegen there.
|
||||
return reinterpret_cast<Object*>(new_func - Code::kHeaderSize +
|
||||
kHeapObjectTag);
|
||||
} else {
|
||||
return *wasm::CompileLazyOnGCHeap(isolate);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -115,7 +115,7 @@ void ValidateCodeObjects() {
|
||||
|
||||
CodeProtectionInfo* CreateHandlerData(
|
||||
void* base, size_t size, size_t num_protected_instructions,
|
||||
ProtectedInstructionData* protected_instructions) {
|
||||
const ProtectedInstructionData* protected_instructions) {
|
||||
const size_t alloc_size = HandlerDataSize(num_protected_instructions);
|
||||
CodeProtectionInfo* data =
|
||||
reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));
|
||||
@ -143,9 +143,9 @@ void UpdateHandlerDataCodePointer(int index, void* base) {
|
||||
data->base = base;
|
||||
}
|
||||
|
||||
int RegisterHandlerData(void* base, size_t size,
|
||||
size_t num_protected_instructions,
|
||||
ProtectedInstructionData* protected_instructions) {
|
||||
int RegisterHandlerData(
|
||||
void* base, size_t size, size_t num_protected_instructions,
|
||||
const ProtectedInstructionData* protected_instructions) {
|
||||
// TODO(eholk): in debug builds, make sure this data isn't already registered.
|
||||
|
||||
CodeProtectionInfo* data = CreateHandlerData(
|
||||
|
@ -50,9 +50,11 @@ void UpdateHandlerDataCodePointer(int index, void* base);
|
||||
/// UpdateHandlerDataCodePointer and ReleaseHandlerData, or -1 on failure.
|
||||
int RegisterHandlerData(void* base, size_t size,
|
||||
size_t num_protected_instructions,
|
||||
ProtectedInstructionData* protected_instructions);
|
||||
const ProtectedInstructionData* protected_instructions);
|
||||
|
||||
/// Removes the data from the master list and frees any memory, if necessary.
|
||||
/// TODO(mtrofin): once FLAG_wasm_jit_to_native is not needed, we can switch
|
||||
/// to using size_t for index and not need kInvalidIndex.
|
||||
void ReleaseHandlerData(int index);
|
||||
|
||||
#if V8_OS_WIN
|
||||
|
@ -1462,6 +1462,8 @@
|
||||
'wasm/wasm-api.h',
|
||||
'wasm/wasm-code-specialization.cc',
|
||||
'wasm/wasm-code-specialization.h',
|
||||
'wasm/wasm-code-wrapper.cc',
|
||||
'wasm/wasm-code-wrapper.h',
|
||||
'wasm/wasm-debug.cc',
|
||||
'wasm/wasm-external-refs.cc',
|
||||
'wasm/wasm-external-refs.h',
|
||||
@ -1485,6 +1487,8 @@
|
||||
'wasm/wasm-opcodes.h',
|
||||
'wasm/wasm-result.cc',
|
||||
'wasm/wasm-result.h',
|
||||
'wasm/wasm-serialization.cc',
|
||||
'wasm/wasm-serialization.h',
|
||||
'wasm/wasm-text.cc',
|
||||
'wasm/wasm-text.h',
|
||||
'wasm/wasm-value.h',
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
#include "src/wasm/wasm-serialization.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -855,13 +856,20 @@ Maybe<bool> ValueSerializer::WriteWasmModule(Handle<WasmModuleObject> object) {
|
||||
String::WriteToFlat(*wire_bytes, destination, 0, wire_bytes_length);
|
||||
}
|
||||
|
||||
std::unique_ptr<ScriptData> script_data =
|
||||
WasmCompiledModuleSerializer::SerializeWasmModule(isolate_,
|
||||
compiled_part);
|
||||
int script_data_length = script_data->length();
|
||||
WriteVarint<uint32_t>(script_data_length);
|
||||
WriteRawBytes(script_data->data(), script_data_length);
|
||||
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
std::pair<std::unique_ptr<byte[]>, size_t> serialized_module =
|
||||
wasm::NativeModuleSerializer::SerializeWholeModule(isolate_,
|
||||
compiled_part);
|
||||
WriteVarint<uint32_t>(static_cast<uint32_t>(serialized_module.second));
|
||||
WriteRawBytes(serialized_module.first.get(), serialized_module.second);
|
||||
} else {
|
||||
std::unique_ptr<ScriptData> script_data =
|
||||
WasmCompiledModuleSerializer::SerializeWasmModule(isolate_,
|
||||
compiled_part);
|
||||
int script_data_length = script_data->length();
|
||||
WriteVarint<uint32_t>(script_data_length);
|
||||
WriteRawBytes(script_data->data(), script_data_length);
|
||||
}
|
||||
return ThrowIfOutOfMemory();
|
||||
}
|
||||
|
||||
@ -1708,15 +1716,25 @@ MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
|
||||
}
|
||||
|
||||
// Try to deserialize the compiled module first.
|
||||
ScriptData script_data(compiled_bytes.start(), compiled_bytes.length());
|
||||
Handle<FixedArray> compiled_part;
|
||||
MaybeHandle<JSObject> result;
|
||||
if (WasmCompiledModuleSerializer::DeserializeWasmModule(
|
||||
isolate_, &script_data, wire_bytes)
|
||||
.ToHandle(&compiled_part)) {
|
||||
result = WasmModuleObject::New(
|
||||
isolate_, Handle<WasmCompiledModule>::cast(compiled_part));
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
if (wasm::NativeModuleDeserializer::DeserializeFullBuffer(
|
||||
isolate_, compiled_bytes, wire_bytes)
|
||||
.ToHandle(&compiled_part)) {
|
||||
result = WasmModuleObject::New(
|
||||
isolate_, Handle<WasmCompiledModule>::cast(compiled_part));
|
||||
}
|
||||
} else {
|
||||
ScriptData script_data(compiled_bytes.start(), compiled_bytes.length());
|
||||
if (WasmCompiledModuleSerializer::DeserializeWasmModule(
|
||||
isolate_, &script_data, wire_bytes)
|
||||
.ToHandle(&compiled_part)) {
|
||||
result = WasmModuleObject::New(
|
||||
isolate_, Handle<WasmCompiledModule>::cast(compiled_part));
|
||||
}
|
||||
}
|
||||
if (result.is_null()) {
|
||||
wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule");
|
||||
result = wasm::SyncCompile(isolate_, &thrower,
|
||||
wasm::ModuleWireBytes(wire_bytes));
|
||||
|
@ -123,7 +123,7 @@ class Vector {
|
||||
}
|
||||
|
||||
inline Vector<T> operator+(size_t offset) {
|
||||
DCHECK_LT(offset, length_);
|
||||
DCHECK_LE(offset, length_);
|
||||
return Vector<T>(start_ + offset, length_ - offset);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@ namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
class ModuleCompiler;
|
||||
class WasmCode;
|
||||
|
||||
V8_EXPORT_PRIVATE bool SyncValidate(Isolate* isolate,
|
||||
const ModuleWireBytes& bytes);
|
||||
@ -53,6 +54,9 @@ V8_EXPORT_PRIVATE void CompileJsToWasmWrappers(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
|
||||
Counters* counters);
|
||||
|
||||
V8_EXPORT_PRIVATE Handle<Script> CreateWasmScript(
|
||||
Isolate* isolate, const ModuleWireBytes& wire_bytes);
|
||||
|
||||
// Triggered by the WasmCompileLazy builtin.
|
||||
// Walks the stack (top three frames) to determine the wasm instance involved
|
||||
// and which function to compile.
|
||||
@ -62,7 +66,8 @@ V8_EXPORT_PRIVATE void CompileJsToWasmWrappers(
|
||||
// an error occurred. In the latter case, a pending exception has been set,
|
||||
// which will be triggered when returning from the runtime function, i.e. the
|
||||
// Illegal builtin will never be called.
|
||||
Handle<Code> CompileLazy(Isolate* isolate);
|
||||
Address CompileLazy(Isolate* isolate);
|
||||
Handle<Code> CompileLazyOnGCHeap(Isolate* isolate);
|
||||
|
||||
// This class orchestrates the lazy compilation of wasm functions. It is
|
||||
// triggered by the WasmCompileLazy builtin.
|
||||
@ -72,12 +77,24 @@ Handle<Code> CompileLazy(Isolate* isolate);
|
||||
// logic to actually orchestrate parallel execution of wasm compilation jobs.
|
||||
// TODO(clemensh): Implement concurrent lazy compilation.
|
||||
class LazyCompilationOrchestrator {
|
||||
void CompileFunction(Isolate*, Handle<WasmInstanceObject>, int func_index);
|
||||
const WasmCode* CompileFunction(Isolate*, Handle<WasmInstanceObject>,
|
||||
int func_index);
|
||||
|
||||
public:
|
||||
Handle<Code> CompileLazy(Isolate*, Handle<WasmInstanceObject>,
|
||||
Handle<Code> caller, int call_offset,
|
||||
int exported_func_index, bool patch_caller);
|
||||
Handle<Code> CompileLazyOnGCHeap(Isolate*, Handle<WasmInstanceObject>,
|
||||
Handle<Code> caller, int call_offset,
|
||||
int exported_func_index, bool patch_caller);
|
||||
const wasm::WasmCode* CompileFromJsToWasm(Isolate*,
|
||||
Handle<WasmInstanceObject>,
|
||||
Handle<Code> caller,
|
||||
uint32_t exported_func_index);
|
||||
const wasm::WasmCode* CompileDirectCall(Isolate*, Handle<WasmInstanceObject>,
|
||||
Maybe<uint32_t>,
|
||||
const WasmCode* caller,
|
||||
int call_offset);
|
||||
const wasm::WasmCode* CompileIndirectCall(Isolate*,
|
||||
Handle<WasmInstanceObject>,
|
||||
uint32_t func_index);
|
||||
};
|
||||
|
||||
// Encapsulates all the state and steps of an asynchronous compilation.
|
||||
|
@ -17,13 +17,13 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
int ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc) {
|
||||
uint32_t ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc) {
|
||||
DCHECK_EQ(static_cast<int>(kExprCallFunction), static_cast<int>(*pc));
|
||||
decoder.Reset(pc + 1, pc + 6);
|
||||
uint32_t call_idx = decoder.consume_u32v("call index");
|
||||
DCHECK(decoder.ok());
|
||||
DCHECK_GE(kMaxInt, call_idx);
|
||||
return static_cast<int>(call_idx);
|
||||
return call_idx;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -43,6 +43,17 @@ int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator,
|
||||
|
||||
class PatchDirectCallsHelper {
|
||||
public:
|
||||
PatchDirectCallsHelper(WasmInstanceObject* instance, const WasmCode* code)
|
||||
: source_pos_it(ByteArray::cast(
|
||||
instance->compiled_module()->source_positions()->get(
|
||||
static_cast<int>(code->index())))),
|
||||
decoder(nullptr, nullptr) {
|
||||
uint32_t func_index = code->index();
|
||||
WasmCompiledModule* comp_mod = instance->compiled_module();
|
||||
func_bytes = comp_mod->module_bytes()->GetChars() +
|
||||
comp_mod->module()->functions[func_index].code.offset();
|
||||
}
|
||||
|
||||
PatchDirectCallsHelper(WasmInstanceObject* instance, Code* code)
|
||||
: source_pos_it(code->SourcePositionTable()), decoder(nullptr, nullptr) {
|
||||
FixedArray* deopt_data = code->deoptimization_data();
|
||||
@ -71,7 +82,8 @@ bool IsAtWasmDirectCallTarget(RelocIterator& it) {
|
||||
|
||||
} // namespace
|
||||
|
||||
CodeSpecialization::CodeSpecialization(Isolate* isolate, Zone* zone) {}
|
||||
CodeSpecialization::CodeSpecialization(Isolate* isolate, Zone* zone)
|
||||
: isolate_(isolate) {}
|
||||
|
||||
CodeSpecialization::~CodeSpecialization() {}
|
||||
|
||||
@ -102,11 +114,11 @@ bool CodeSpecialization::ApplyToWholeInstance(
|
||||
WasmInstanceObject* instance, ICacheFlushMode icache_flush_mode) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
WasmCompiledModule* compiled_module = instance->compiled_module();
|
||||
NativeModule* native_module = compiled_module->GetNativeModule();
|
||||
FixedArray* code_table = compiled_module->ptr_to_code_table();
|
||||
WasmModule* module = compiled_module->module();
|
||||
std::vector<WasmFunction>* wasm_functions =
|
||||
&compiled_module->module()->functions;
|
||||
DCHECK_EQ(wasm_functions->size(), code_table->length());
|
||||
DCHECK_EQ(compiled_module->export_wrappers()->length(),
|
||||
compiled_module->module()->num_exported_functions);
|
||||
|
||||
@ -116,9 +128,19 @@ bool CodeSpecialization::ApplyToWholeInstance(
|
||||
// Patch all wasm functions.
|
||||
for (int num_wasm_functions = static_cast<int>(wasm_functions->size());
|
||||
func_index < num_wasm_functions; ++func_index) {
|
||||
Code* wasm_function = Code::cast(code_table->get(func_index));
|
||||
if (wasm_function->kind() != Code::WASM_FUNCTION) continue;
|
||||
changed |= ApplyToWasmCode(wasm_function, icache_flush_mode);
|
||||
WasmCodeWrapper wrapper;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
const WasmCode* wasm_function = native_module->GetCode(func_index);
|
||||
if (wasm_function->kind() != WasmCode::Function) {
|
||||
continue;
|
||||
}
|
||||
wrapper = WasmCodeWrapper(wasm_function);
|
||||
} else {
|
||||
Code* wasm_function = Code::cast(code_table->get(func_index));
|
||||
if (wasm_function->kind() != Code::WASM_FUNCTION) continue;
|
||||
wrapper = WasmCodeWrapper(handle(wasm_function));
|
||||
}
|
||||
changed |= ApplyToWasmCode(wrapper, icache_flush_mode);
|
||||
}
|
||||
|
||||
// Patch all exported functions (JS_TO_WASM_FUNCTION).
|
||||
@ -132,7 +154,9 @@ bool CodeSpecialization::ApplyToWholeInstance(
|
||||
// should match the instance we currently patch (instance).
|
||||
if (!relocate_direct_calls_instance.is_null()) {
|
||||
DCHECK_EQ(instance, *relocate_direct_calls_instance);
|
||||
reloc_mode |= RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
|
||||
reloc_mode |=
|
||||
RelocInfo::ModeMask(FLAG_wasm_jit_to_native ? RelocInfo::JS_TO_WASM_CALL
|
||||
: RelocInfo::CODE_TARGET);
|
||||
}
|
||||
if (!reloc_mode) return changed;
|
||||
int wrapper_index = 0;
|
||||
@ -149,7 +173,14 @@ bool CodeSpecialization::ApplyToWholeInstance(
|
||||
new_wasm_context_address,
|
||||
icache_flush_mode);
|
||||
break;
|
||||
case RelocInfo::JS_TO_WASM_CALL: {
|
||||
DCHECK(FLAG_wasm_jit_to_native);
|
||||
const WasmCode* new_code = native_module->GetCode(exp.index);
|
||||
it.rinfo()->set_js_to_wasm_address(
|
||||
nullptr, new_code->instructions().start(), SKIP_ICACHE_FLUSH);
|
||||
} break;
|
||||
case RelocInfo::CODE_TARGET: {
|
||||
DCHECK(!FLAG_wasm_jit_to_native);
|
||||
// Ignore calls to other builtins like ToNumber.
|
||||
if (!IsAtWasmDirectCallTarget(it)) continue;
|
||||
Code* new_code = Code::cast(code_table->get(exp.index));
|
||||
@ -164,15 +195,19 @@ bool CodeSpecialization::ApplyToWholeInstance(
|
||||
changed = true;
|
||||
++wrapper_index;
|
||||
}
|
||||
DCHECK_EQ(code_table->length(), func_index);
|
||||
DCHECK_EQ(module->functions.size(), func_index);
|
||||
DCHECK_EQ(compiled_module->export_wrappers()->length(), wrapper_index);
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CodeSpecialization::ApplyToWasmCode(Code* code,
|
||||
bool CodeSpecialization::ApplyToWasmCode(WasmCodeWrapper code,
|
||||
ICacheFlushMode icache_flush_mode) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
DCHECK_EQ(Code::WASM_FUNCTION, code->kind());
|
||||
if (code.IsCodeObject()) {
|
||||
DCHECK_EQ(Code::WASM_FUNCTION, code.GetCode()->kind());
|
||||
} else {
|
||||
DCHECK_EQ(wasm::WasmCode::Function, code.GetWasmCode()->kind());
|
||||
}
|
||||
|
||||
bool patch_table_size = old_function_table_size || new_function_table_size;
|
||||
bool reloc_direct_calls = !relocate_direct_calls_instance.is_null();
|
||||
@ -183,16 +218,30 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
|
||||
if (cond) reloc_mode |= RelocInfo::ModeMask(mode);
|
||||
};
|
||||
add_mode(patch_table_size, RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE);
|
||||
add_mode(reloc_direct_calls, RelocInfo::CODE_TARGET);
|
||||
if (code.IsCodeObject()) {
|
||||
add_mode(reloc_direct_calls, RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
add_mode(reloc_direct_calls, RelocInfo::WASM_CALL);
|
||||
}
|
||||
add_mode(reloc_pointers, RelocInfo::WASM_GLOBAL_HANDLE);
|
||||
|
||||
base::Optional<PatchDirectCallsHelper> patch_direct_calls_helper;
|
||||
bool changed = false;
|
||||
|
||||
for (RelocIterator it(code, reloc_mode); !it.done(); it.next()) {
|
||||
NativeModule* native_module =
|
||||
code.IsCodeObject() ? nullptr : code.GetWasmCode()->owner();
|
||||
|
||||
RelocIterator it =
|
||||
code.IsCodeObject()
|
||||
? RelocIterator(*code.GetCode(), reloc_mode)
|
||||
: RelocIterator(code.GetWasmCode()->instructions(),
|
||||
code.GetWasmCode()->reloc_info(),
|
||||
code.GetWasmCode()->constant_pool(), reloc_mode);
|
||||
for (; !it.done(); it.next()) {
|
||||
RelocInfo::Mode mode = it.rinfo()->rmode();
|
||||
switch (mode) {
|
||||
case RelocInfo::CODE_TARGET: {
|
||||
DCHECK(!FLAG_wasm_jit_to_native);
|
||||
DCHECK(reloc_direct_calls);
|
||||
// Skip everything which is not a wasm call (stack checks, traps, ...).
|
||||
if (!IsAtWasmDirectCallTarget(it)) continue;
|
||||
@ -201,10 +250,10 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
|
||||
// position iterator forward to that position to find the byte offset of
|
||||
// the respective call. Then extract the call index from the module wire
|
||||
// bytes to find the new compiled function.
|
||||
size_t offset = it.rinfo()->pc() - code->instruction_start();
|
||||
size_t offset = it.rinfo()->pc() - code.GetCode()->instruction_start();
|
||||
if (!patch_direct_calls_helper) {
|
||||
patch_direct_calls_helper.emplace(*relocate_direct_calls_instance,
|
||||
code);
|
||||
*code.GetCode());
|
||||
}
|
||||
int byte_pos = AdvanceSourcePositionTableIterator(
|
||||
patch_direct_calls_helper->source_pos_it, offset);
|
||||
@ -220,21 +269,44 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
|
||||
UPDATE_WRITE_BARRIER, icache_flush_mode);
|
||||
changed = true;
|
||||
} break;
|
||||
case RelocInfo::WASM_CALL: {
|
||||
DCHECK(FLAG_wasm_jit_to_native);
|
||||
DCHECK(reloc_direct_calls);
|
||||
// Iterate simultaneously over the relocation information and the source
|
||||
// position table. For each call in the reloc info, move the source
|
||||
// position iterator forward to that position to find the byte offset of
|
||||
// the respective call. Then extract the call index from the module wire
|
||||
// bytes to find the new compiled function.
|
||||
size_t offset =
|
||||
it.rinfo()->pc() - code.GetWasmCode()->instructions().start();
|
||||
if (!patch_direct_calls_helper) {
|
||||
patch_direct_calls_helper.emplace(*relocate_direct_calls_instance,
|
||||
code.GetWasmCode());
|
||||
}
|
||||
int byte_pos = AdvanceSourcePositionTableIterator(
|
||||
patch_direct_calls_helper->source_pos_it, offset);
|
||||
uint32_t called_func_index = ExtractDirectCallIndex(
|
||||
patch_direct_calls_helper->decoder,
|
||||
patch_direct_calls_helper->func_bytes + byte_pos);
|
||||
const WasmCode* new_code = native_module->GetCode(called_func_index);
|
||||
it.rinfo()->set_wasm_call_address(
|
||||
isolate_, new_code->instructions().start(), icache_flush_mode);
|
||||
changed = true;
|
||||
} break;
|
||||
case RelocInfo::WASM_GLOBAL_HANDLE: {
|
||||
DCHECK(reloc_pointers);
|
||||
Address old_ptr = it.rinfo()->global_handle();
|
||||
if (pointers_to_relocate.count(old_ptr) == 1) {
|
||||
Address new_ptr = pointers_to_relocate[old_ptr];
|
||||
it.rinfo()->set_global_handle(code->GetIsolate(), new_ptr,
|
||||
icache_flush_mode);
|
||||
it.rinfo()->set_global_handle(isolate_, new_ptr, icache_flush_mode);
|
||||
changed = true;
|
||||
}
|
||||
} break;
|
||||
case RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE:
|
||||
DCHECK(patch_table_size);
|
||||
it.rinfo()->update_wasm_function_table_size_reference(
|
||||
code->GetIsolate(), old_function_table_size,
|
||||
new_function_table_size, icache_flush_mode);
|
||||
isolate_, old_function_table_size, new_function_table_size,
|
||||
icache_flush_mode);
|
||||
changed = true;
|
||||
break;
|
||||
default:
|
||||
|
@ -14,7 +14,7 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
int ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc);
|
||||
uint32_t ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc);
|
||||
|
||||
// Helper class to specialize wasm code for a specific instance, or to update
|
||||
// code when memory / globals / tables change.
|
||||
@ -43,9 +43,11 @@ class CodeSpecialization {
|
||||
bool ApplyToWholeInstance(WasmInstanceObject*,
|
||||
ICacheFlushMode = FLUSH_ICACHE_IF_NEEDED);
|
||||
// Apply all relocations and patching to one wasm code object.
|
||||
bool ApplyToWasmCode(Code*, ICacheFlushMode = FLUSH_ICACHE_IF_NEEDED);
|
||||
bool ApplyToWasmCode(WasmCodeWrapper,
|
||||
ICacheFlushMode = FLUSH_ICACHE_IF_NEEDED);
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
Address new_wasm_context_address = 0;
|
||||
|
||||
uint32_t old_function_table_size = 0;
|
||||
|
38
src/wasm/wasm-code-wrapper.cc
Normal file
38
src/wasm/wasm-code-wrapper.cc
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2017 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/wasm/wasm-code-wrapper.h"
|
||||
|
||||
#include "src/objects.h"
|
||||
#include "src/objects/code.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// When constructing, we check the flag. After that, we just
|
||||
// check using the member.
|
||||
WasmCodeWrapper::WasmCodeWrapper(Handle<Code> code) {
|
||||
DCHECK(!FLAG_wasm_jit_to_native);
|
||||
code_ptr_.code_handle_ = code.location();
|
||||
}
|
||||
|
||||
WasmCodeWrapper::WasmCodeWrapper(const wasm::WasmCode* code) {
|
||||
DCHECK(FLAG_wasm_jit_to_native);
|
||||
code_ptr_.wasm_code_ = code;
|
||||
}
|
||||
|
||||
Handle<Code> WasmCodeWrapper::GetCode() const {
|
||||
DCHECK(IsCodeObject());
|
||||
return Handle<Code>(code_ptr_.code_handle_);
|
||||
}
|
||||
|
||||
const wasm::WasmCode* WasmCodeWrapper::GetWasmCode() const {
|
||||
DCHECK(!IsCodeObject());
|
||||
return code_ptr_.wasm_code_;
|
||||
}
|
||||
|
||||
bool WasmCodeWrapper::IsCodeObject() const { return !FLAG_wasm_jit_to_native; }
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
38
src/wasm/wasm-code-wrapper.h
Normal file
38
src/wasm/wasm-code-wrapper.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2017 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.
|
||||
#ifndef V8_WASM_CODE_WRAPPER_H_
|
||||
#define V8_WASM_CODE_WRAPPER_H_
|
||||
|
||||
#include "src/handles.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
class WasmCode;
|
||||
} // namespace wasm
|
||||
|
||||
class Code;
|
||||
|
||||
// TODO(mtrofin): remove once we remove FLAG_wasm_jit_to_native
|
||||
class WasmCodeWrapper {
|
||||
public:
|
||||
WasmCodeWrapper() {}
|
||||
|
||||
explicit WasmCodeWrapper(Handle<Code> code);
|
||||
explicit WasmCodeWrapper(const wasm::WasmCode* code);
|
||||
Handle<Code> GetCode() const;
|
||||
const wasm::WasmCode* GetWasmCode() const;
|
||||
bool is_null() const { return code_ptr_.wasm_code_ == nullptr; }
|
||||
bool IsCodeObject() const;
|
||||
|
||||
private:
|
||||
union {
|
||||
const wasm::WasmCode* wasm_code_;
|
||||
Code** code_handle_;
|
||||
} code_ptr_ = {};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
#endif // V8_WASM_CODE_WRAPPER_H_
|
@ -574,9 +574,11 @@ Handle<FixedArray> GetOrCreateInterpretedFunctions(
|
||||
return new_arr;
|
||||
}
|
||||
|
||||
using CodeRelocationMap = IdentityMap<Handle<Code>, FreeStoreAllocationPolicy>;
|
||||
using CodeRelocationMap = std::map<Address, Address>;
|
||||
using CodeRelocationMapGC =
|
||||
IdentityMap<Handle<Code>, FreeStoreAllocationPolicy>;
|
||||
|
||||
void RedirectCallsitesInCode(Code* code, CodeRelocationMap& map) {
|
||||
void RedirectCallsitesInCodeGC(Code* code, CodeRelocationMapGC& map) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done();
|
||||
it.next()) {
|
||||
@ -589,13 +591,40 @@ void RedirectCallsitesInCode(Code* code, CodeRelocationMap& map) {
|
||||
}
|
||||
}
|
||||
|
||||
void RedirectCallsitesInInstance(Isolate* isolate, WasmInstanceObject* instance,
|
||||
CodeRelocationMap& map) {
|
||||
void RedirectCallsitesInCode(Isolate* isolate, const wasm::WasmCode* code,
|
||||
CodeRelocationMap* map) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
for (RelocIterator it(code->instructions(), code->reloc_info(),
|
||||
code->constant_pool(),
|
||||
RelocInfo::ModeMask(RelocInfo::WASM_CALL));
|
||||
!it.done(); it.next()) {
|
||||
Address target = it.rinfo()->target_address();
|
||||
auto new_target = map->find(target);
|
||||
if (new_target == map->end()) continue;
|
||||
it.rinfo()->set_wasm_call_address(isolate, new_target->second);
|
||||
}
|
||||
}
|
||||
|
||||
void RedirectCallsitesInJSWrapperCode(Isolate* isolate, Code* code,
|
||||
CodeRelocationMap* map) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
for (RelocIterator it(code, RelocInfo::ModeMask(RelocInfo::JS_TO_WASM_CALL));
|
||||
!it.done(); it.next()) {
|
||||
Address target = it.rinfo()->js_to_wasm_address();
|
||||
auto new_target = map->find(target);
|
||||
if (new_target == map->end()) continue;
|
||||
it.rinfo()->set_js_to_wasm_address(isolate, new_target->second);
|
||||
}
|
||||
}
|
||||
|
||||
void RedirectCallsitesInInstanceGC(Isolate* isolate,
|
||||
WasmInstanceObject* instance,
|
||||
CodeRelocationMapGC& map) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
// Redirect all calls in wasm functions.
|
||||
FixedArray* code_table = instance->compiled_module()->ptr_to_code_table();
|
||||
for (int i = 0, e = GetNumFunctions(instance); i < e; ++i) {
|
||||
RedirectCallsitesInCode(Code::cast(code_table->get(i)), map);
|
||||
RedirectCallsitesInCodeGC(Code::cast(code_table->get(i)), map);
|
||||
}
|
||||
// TODO(6668): Find instances that imported our code and also patch those.
|
||||
|
||||
@ -606,7 +635,29 @@ void RedirectCallsitesInInstance(Isolate* isolate, WasmInstanceObject* instance,
|
||||
WeakCell* weak_function = WeakCell::cast(weak_exported_functions->get(i));
|
||||
if (weak_function->cleared()) continue;
|
||||
Code* code = JSFunction::cast(weak_function->value())->code();
|
||||
RedirectCallsitesInCode(code, map);
|
||||
RedirectCallsitesInCodeGC(code, map);
|
||||
}
|
||||
}
|
||||
|
||||
void RedirectCallsitesInInstance(Isolate* isolate, WasmInstanceObject* instance,
|
||||
CodeRelocationMap* map) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
// Redirect all calls in wasm functions.
|
||||
for (uint32_t i = 0, e = GetNumFunctions(instance); i < e; ++i) {
|
||||
wasm::WasmCode* code =
|
||||
instance->compiled_module()->GetNativeModule()->GetCode(i);
|
||||
RedirectCallsitesInCode(isolate, code, map);
|
||||
}
|
||||
// TODO(6668): Find instances that imported our code and also patch those.
|
||||
|
||||
// Redirect all calls in exported functions.
|
||||
FixedArray* weak_exported_functions =
|
||||
instance->compiled_module()->ptr_to_weak_exported_functions();
|
||||
for (int i = 0, e = weak_exported_functions->length(); i != e; ++i) {
|
||||
WeakCell* weak_function = WeakCell::cast(weak_exported_functions->get(i));
|
||||
if (weak_function->cleared()) continue;
|
||||
Code* code = JSFunction::cast(weak_function->value())->code();
|
||||
RedirectCallsitesInJSWrapperCode(isolate, code, map);
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,8 +721,12 @@ void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
|
||||
Handle<FixedArray> interpreted_functions =
|
||||
GetOrCreateInterpretedFunctions(isolate, debug_info);
|
||||
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
|
||||
wasm::NativeModule* native_module =
|
||||
instance->compiled_module()->GetNativeModule();
|
||||
CodeRelocationMap code_to_relocate;
|
||||
|
||||
Handle<FixedArray> code_table = instance->compiled_module()->code_table();
|
||||
CodeRelocationMap code_to_relocate(isolate->heap());
|
||||
CodeRelocationMapGC code_to_relocate_gc(isolate->heap());
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
|
||||
for (int func_index : func_indexes) {
|
||||
@ -684,13 +739,30 @@ void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
|
||||
isolate, func_index,
|
||||
instance->compiled_module()->module()->functions[func_index].sig,
|
||||
instance);
|
||||
|
||||
Code* old_code = Code::cast(code_table->get(func_index));
|
||||
interpreted_functions->set(func_index, *new_code);
|
||||
DCHECK_NULL(code_to_relocate.Find(old_code));
|
||||
code_to_relocate.Set(old_code, new_code);
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
const wasm::WasmCode* wasm_new_code =
|
||||
native_module->AddInterpreterWrapper(new_code, func_index);
|
||||
const wasm::WasmCode* old_code =
|
||||
native_module->GetCode(static_cast<uint32_t>(func_index));
|
||||
Handle<Foreign> foreign_holder = isolate->factory()->NewForeign(
|
||||
wasm_new_code->instructions().start(), TENURED);
|
||||
interpreted_functions->set(func_index, *foreign_holder);
|
||||
DCHECK_EQ(0, code_to_relocate.count(old_code->instructions().start()));
|
||||
code_to_relocate.insert(
|
||||
std::make_pair(old_code->instructions().start(),
|
||||
wasm_new_code->instructions().start()));
|
||||
} else {
|
||||
Code* old_code = Code::cast(code_table->get(func_index));
|
||||
interpreted_functions->set(func_index, *new_code);
|
||||
DCHECK_NULL(code_to_relocate_gc.Find(old_code));
|
||||
code_to_relocate_gc.Set(old_code, new_code);
|
||||
}
|
||||
}
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
RedirectCallsitesInInstance(isolate, *instance, &code_to_relocate);
|
||||
} else {
|
||||
RedirectCallsitesInInstanceGC(isolate, *instance, code_to_relocate_gc);
|
||||
}
|
||||
RedirectCallsitesInInstance(isolate, *instance, code_to_relocate);
|
||||
}
|
||||
|
||||
void WasmDebugInfo::PrepareStep(StepAction step_action) {
|
||||
|
@ -359,8 +359,6 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
|
||||
intptr_t remaining_uncommitted() const;
|
||||
|
||||
private:
|
||||
static const size_t kMaxWasmCodeMemory = 256 * MB;
|
||||
|
||||
friend class NativeModule;
|
||||
|
||||
WasmCodeManager(const WasmCodeManager&) = delete;
|
||||
|
@ -627,17 +627,29 @@ const char* OpcodeName(uint32_t val) {
|
||||
// Unwrap a wasm to js wrapper, return the callable heap object.
|
||||
// If the wrapper would throw a TypeError, return a null handle.
|
||||
Handle<HeapObject> UnwrapWasmToJSWrapper(Isolate* isolate,
|
||||
Handle<Code> js_wrapper) {
|
||||
DCHECK_EQ(Code::WASM_TO_JS_FUNCTION, js_wrapper->kind());
|
||||
Handle<FixedArray> deopt_data(js_wrapper->deoptimization_data(), isolate);
|
||||
DCHECK_EQ(2, deopt_data->length());
|
||||
intptr_t js_imports_table_loc = static_cast<intptr_t>(
|
||||
HeapNumber::cast(deopt_data->get(0))->value_as_bits());
|
||||
Handle<FixedArray> js_imports_table(
|
||||
reinterpret_cast<FixedArray**>(js_imports_table_loc));
|
||||
WasmCodeWrapper wrapper) {
|
||||
Handle<FixedArray> js_imports_table;
|
||||
int index = 0;
|
||||
CHECK(deopt_data->get(1)->ToInt32(&index));
|
||||
DCHECK_GT(js_imports_table->length(), index);
|
||||
if (wrapper.IsCodeObject()) {
|
||||
Handle<Code> js_wrapper = wrapper.GetCode();
|
||||
DCHECK(Code::WASM_TO_JS_FUNCTION == js_wrapper->kind());
|
||||
Handle<FixedArray> deopt_data(js_wrapper->deoptimization_data(), isolate);
|
||||
DCHECK_EQ(2, deopt_data->length());
|
||||
intptr_t js_imports_table_loc = static_cast<intptr_t>(
|
||||
HeapNumber::cast(deopt_data->get(0))->value_as_bits());
|
||||
js_imports_table = Handle<FixedArray>(
|
||||
reinterpret_cast<FixedArray**>(js_imports_table_loc));
|
||||
CHECK(deopt_data->get(1)->ToInt32(&index));
|
||||
DCHECK_GT(js_imports_table->length(), index);
|
||||
} else {
|
||||
const wasm::WasmCode* wasm_code = wrapper.GetWasmCode();
|
||||
DCHECK_EQ(wasm::WasmCode::WasmToJsWrapper, wasm_code->kind());
|
||||
js_imports_table = Handle<FixedArray>(wasm_code->owner()
|
||||
->compiled_module()
|
||||
->owning_instance()
|
||||
->js_imports_table());
|
||||
index = 1 + 3 * static_cast<int>(wasm_code->index());
|
||||
}
|
||||
Handle<Object> obj(js_imports_table->get(index), isolate);
|
||||
if (obj->IsCallable()) {
|
||||
return Handle<HeapObject>::cast(obj);
|
||||
@ -982,7 +994,15 @@ class CodeMap {
|
||||
: MaybeHandle<WasmInstanceObject>();
|
||||
}
|
||||
|
||||
Code* GetImportedFunction(uint32_t function_index) {
|
||||
const wasm::WasmCode* GetImportedFunction(uint32_t function_index) {
|
||||
DCHECK(has_instance());
|
||||
DCHECK_GT(module_->num_imported_functions, function_index);
|
||||
const wasm::NativeModule* native_module =
|
||||
instance()->compiled_module()->GetNativeModule();
|
||||
return native_module->GetCode(function_index);
|
||||
}
|
||||
|
||||
Code* GetImportedFunctionGC(uint32_t function_index) {
|
||||
DCHECK(has_instance());
|
||||
DCHECK_GT(module_->num_imported_functions, function_index);
|
||||
FixedArray* code_table = instance()->compiled_module()->ptr_to_code_table();
|
||||
@ -2238,9 +2258,8 @@ class ThreadImpl {
|
||||
return {ExternalCallResult::EXTERNAL_RETURNED};
|
||||
}
|
||||
|
||||
// TODO(clemensh): Remove this, call JS via existing wasm-to-js wrapper, using
|
||||
// CallExternalWasmFunction.
|
||||
ExternalCallResult CallExternalJSFunction(Isolate* isolate, Handle<Code> code,
|
||||
ExternalCallResult CallExternalJSFunction(Isolate* isolate,
|
||||
WasmCodeWrapper code,
|
||||
FunctionSig* signature) {
|
||||
Handle<HeapObject> target = UnwrapWasmToJSWrapper(isolate, code);
|
||||
|
||||
@ -2295,7 +2314,7 @@ class ThreadImpl {
|
||||
}
|
||||
|
||||
ExternalCallResult CallExternalWasmFunction(Isolate* isolate,
|
||||
Handle<Code> code,
|
||||
WasmCodeWrapper code,
|
||||
FunctionSig* sig) {
|
||||
Handle<WasmDebugInfo> debug_info(codemap()->instance()->debug_info(),
|
||||
isolate);
|
||||
@ -2356,7 +2375,11 @@ class ThreadImpl {
|
||||
DCHECK(!arg_buffer_obj->IsHeapObject());
|
||||
|
||||
Handle<Object> args[compiler::CWasmEntryParameters::kNumParameters];
|
||||
args[compiler::CWasmEntryParameters::kCodeObject] = code;
|
||||
args[compiler::CWasmEntryParameters::kCodeObject] =
|
||||
code.IsCodeObject()
|
||||
? Handle<Object>::cast(code.GetCode())
|
||||
: Handle<Object>::cast(isolate->factory()->NewForeign(
|
||||
code.GetWasmCode()->instructions().start(), TENURED));
|
||||
args[compiler::CWasmEntryParameters::kArgumentsBuffer] = arg_buffer_obj;
|
||||
|
||||
Handle<Object> receiver = isolate->factory()->undefined_value();
|
||||
@ -2408,14 +2431,34 @@ class ThreadImpl {
|
||||
code->kind() == Code::WASM_TO_WASM_FUNCTION) {
|
||||
auto func_info = GetWasmFunctionInfo(isolate, code);
|
||||
if (*func_info.instance.ToHandleChecked() != codemap()->instance()) {
|
||||
return CallExternalWasmFunction(isolate, code, signature);
|
||||
return CallExternalWasmFunction(isolate, WasmCodeWrapper(code),
|
||||
signature);
|
||||
}
|
||||
DCHECK_LE(0, func_info.func_index);
|
||||
return {ExternalCallResult::INTERNAL,
|
||||
codemap()->GetCode(func_info.func_index)};
|
||||
}
|
||||
|
||||
return CallExternalJSFunction(isolate, code, signature);
|
||||
return CallExternalJSFunction(isolate, WasmCodeWrapper(code), signature);
|
||||
}
|
||||
|
||||
ExternalCallResult CallWasmCode(Isolate* isolate, const wasm::WasmCode* code,
|
||||
FunctionSig* signature) {
|
||||
DCHECK(AllowHandleAllocation::IsAllowed());
|
||||
DCHECK(AllowHeapAllocation::IsAllowed());
|
||||
|
||||
if (code->kind() == wasm::WasmCode::Function) {
|
||||
DCHECK_EQ(*code->owner()->compiled_module()->owning_instance(),
|
||||
codemap()->instance());
|
||||
return {ExternalCallResult::INTERNAL, codemap()->GetCode(code->index())};
|
||||
}
|
||||
if (code->kind() == wasm::WasmCode::WasmToJsWrapper) {
|
||||
return CallExternalJSFunction(isolate, WasmCodeWrapper(code), signature);
|
||||
} else if (code->kind() == wasm::WasmCode::WasmToWasmWrapper) {
|
||||
return CallExternalWasmFunction(isolate, WasmCodeWrapper(code),
|
||||
signature);
|
||||
}
|
||||
return {ExternalCallResult::INVALID_FUNC};
|
||||
}
|
||||
|
||||
ExternalCallResult CallImportedFunction(uint32_t function_index) {
|
||||
@ -2424,17 +2467,36 @@ class ThreadImpl {
|
||||
Isolate* isolate = codemap()->instance()->GetIsolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
Handle<Code> target(codemap()->GetImportedFunction(function_index),
|
||||
isolate);
|
||||
return CallCodeObject(isolate, target,
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
const wasm::WasmCode* target =
|
||||
codemap()->GetImportedFunction(function_index);
|
||||
return CallWasmCode(isolate, target,
|
||||
codemap()->module()->functions[function_index].sig);
|
||||
} else {
|
||||
Handle<Code> target(codemap()->GetImportedFunctionGC(function_index),
|
||||
isolate);
|
||||
return CallCodeObject(isolate, target,
|
||||
codemap()->module()->functions[function_index].sig);
|
||||
}
|
||||
}
|
||||
|
||||
ExternalCallResult CallIndirectFunction(uint32_t table_index,
|
||||
uint32_t entry_index,
|
||||
uint32_t sig_index) {
|
||||
if (!codemap()->has_instance() ||
|
||||
!codemap()->instance()->compiled_module()->has_function_tables()) {
|
||||
bool no_func_tables = !codemap()->has_instance();
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
no_func_tables = no_func_tables || codemap()
|
||||
->instance()
|
||||
->compiled_module()
|
||||
->GetNativeModule()
|
||||
->function_tables()
|
||||
.empty();
|
||||
} else {
|
||||
no_func_tables =
|
||||
no_func_tables ||
|
||||
!codemap()->instance()->compiled_module()->has_function_tables();
|
||||
}
|
||||
if (no_func_tables) {
|
||||
// No instance. Rely on the information stored in the WasmModule.
|
||||
// TODO(wasm): This is only needed for testing. Refactor testing to use
|
||||
// the same paths as production.
|
||||
@ -2459,7 +2521,8 @@ class ThreadImpl {
|
||||
codemap()->instance()->compiled_module();
|
||||
Isolate* isolate = compiled_module->GetIsolate();
|
||||
|
||||
Code* target;
|
||||
const wasm::WasmCode* target = nullptr;
|
||||
Code* target_gc = nullptr;
|
||||
{
|
||||
DisallowHeapAllocation no_gc;
|
||||
// Get function to be called directly from the live instance to see latest
|
||||
@ -2470,39 +2533,80 @@ class ThreadImpl {
|
||||
DCHECK_EQ(canonical_sig_index,
|
||||
module()->signature_map.Find(module()->signatures[sig_index]));
|
||||
|
||||
// Check signature.
|
||||
FixedArray* sig_tables = compiled_module->ptr_to_signature_tables();
|
||||
if (table_index >= static_cast<uint32_t>(sig_tables->length())) {
|
||||
return {ExternalCallResult::INVALID_FUNC};
|
||||
}
|
||||
// Reconstitute the global handle to sig_table, and, further below,
|
||||
// to the function table, from the address stored in the
|
||||
// respective table of tables.
|
||||
int table_index_as_int = static_cast<int>(table_index);
|
||||
Handle<FixedArray> sig_table(reinterpret_cast<FixedArray**>(
|
||||
WasmCompiledModule::GetTableValue(sig_tables, table_index_as_int)));
|
||||
if (entry_index >= static_cast<uint32_t>(sig_table->length())) {
|
||||
return {ExternalCallResult::INVALID_FUNC};
|
||||
}
|
||||
int found_sig = Smi::ToInt(sig_table->get(static_cast<int>(entry_index)));
|
||||
if (static_cast<uint32_t>(found_sig) != canonical_sig_index) {
|
||||
return {ExternalCallResult::SIGNATURE_MISMATCH};
|
||||
}
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
// Check signature.
|
||||
FixedArray* sig_tables = compiled_module->ptr_to_signature_tables();
|
||||
if (table_index >= static_cast<uint32_t>(sig_tables->length())) {
|
||||
return {ExternalCallResult::INVALID_FUNC};
|
||||
}
|
||||
// Reconstitute the global handle to sig_table, and, further below,
|
||||
// to the function table, from the address stored in the
|
||||
// respective table of tables.
|
||||
int table_index_as_int = static_cast<int>(table_index);
|
||||
Handle<FixedArray> sig_table(reinterpret_cast<FixedArray**>(
|
||||
WasmCompiledModule::GetTableValue(sig_tables, table_index_as_int)));
|
||||
if (entry_index >= static_cast<uint32_t>(sig_table->length())) {
|
||||
return {ExternalCallResult::INVALID_FUNC};
|
||||
}
|
||||
int found_sig =
|
||||
Smi::ToInt(sig_table->get(static_cast<int>(entry_index)));
|
||||
if (static_cast<uint32_t>(found_sig) != canonical_sig_index) {
|
||||
return {ExternalCallResult::SIGNATURE_MISMATCH};
|
||||
}
|
||||
|
||||
// Get code object.
|
||||
FixedArray* fun_tables = compiled_module->ptr_to_function_tables();
|
||||
DCHECK_EQ(sig_tables->length(), fun_tables->length());
|
||||
Handle<FixedArray> fun_table(reinterpret_cast<FixedArray**>(
|
||||
WasmCompiledModule::GetTableValue(fun_tables, table_index_as_int)));
|
||||
DCHECK_EQ(sig_table->length(), fun_table->length());
|
||||
target = Code::cast(fun_table->get(static_cast<int>(entry_index)));
|
||||
// Get code object.
|
||||
FixedArray* fun_tables = compiled_module->ptr_to_function_tables();
|
||||
DCHECK_EQ(sig_tables->length(), fun_tables->length());
|
||||
Handle<FixedArray> fun_table(reinterpret_cast<FixedArray**>(
|
||||
WasmCompiledModule::GetTableValue(fun_tables, table_index_as_int)));
|
||||
DCHECK_EQ(sig_table->length(), fun_table->length());
|
||||
target_gc = Code::cast(fun_table->get(static_cast<int>(entry_index)));
|
||||
} else {
|
||||
// Check signature.
|
||||
std::vector<GlobalHandleAddress>& sig_tables =
|
||||
compiled_module->GetNativeModule()->signature_tables();
|
||||
if (table_index >= sig_tables.size()) {
|
||||
return {ExternalCallResult::INVALID_FUNC};
|
||||
}
|
||||
// Reconstitute the global handle to sig_table, and, further below,
|
||||
// to the function table, from the address stored in the
|
||||
// respective table of tables.
|
||||
int table_index_as_int = static_cast<int>(table_index);
|
||||
Handle<FixedArray> sig_table(
|
||||
reinterpret_cast<FixedArray**>(sig_tables[table_index_as_int]));
|
||||
if (entry_index >= static_cast<uint32_t>(sig_table->length())) {
|
||||
return {ExternalCallResult::INVALID_FUNC};
|
||||
}
|
||||
int found_sig =
|
||||
Smi::ToInt(sig_table->get(static_cast<int>(entry_index)));
|
||||
if (static_cast<uint32_t>(found_sig) != canonical_sig_index) {
|
||||
return {ExternalCallResult::SIGNATURE_MISMATCH};
|
||||
}
|
||||
|
||||
// Get code object.
|
||||
std::vector<GlobalHandleAddress>& fun_tables =
|
||||
compiled_module->GetNativeModule()->function_tables();
|
||||
DCHECK_EQ(sig_tables.size(), fun_tables.size());
|
||||
Handle<FixedArray> fun_table(
|
||||
reinterpret_cast<FixedArray**>(fun_tables[table_index_as_int]));
|
||||
DCHECK_EQ(sig_table->length(), fun_table->length());
|
||||
Address first_instr =
|
||||
Foreign::cast(fun_table->get(static_cast<int>(entry_index)))
|
||||
->foreign_address();
|
||||
target =
|
||||
isolate->wasm_code_manager()->GetCodeFromStartAddress(first_instr);
|
||||
}
|
||||
}
|
||||
|
||||
// Call the code object. Use a new HandleScope to avoid leaking /
|
||||
// accumulating handles in the outer scope.
|
||||
HandleScope handle_scope(isolate);
|
||||
FunctionSig* signature = module()->signatures[sig_index];
|
||||
return CallCodeObject(isolate, handle(target, isolate), signature);
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
return CallWasmCode(isolate, target, signature);
|
||||
} else {
|
||||
return CallCodeObject(isolate, handle(target_gc, isolate), signature);
|
||||
}
|
||||
}
|
||||
|
||||
inline Activation current_activation() {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-code-specialization.h"
|
||||
#include "src/wasm/wasm-heap.h"
|
||||
#include "src/wasm/wasm-js.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
@ -54,8 +55,8 @@ constexpr const char* WasmException::kRuntimeIdStr;
|
||||
// static
|
||||
constexpr const char* WasmException::kRuntimeValuesStr;
|
||||
|
||||
void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
|
||||
Handle<FixedArray> code_table) {
|
||||
void UnpackAndRegisterProtectedInstructionsGC(Isolate* isolate,
|
||||
Handle<FixedArray> code_table) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
std::vector<trap_handler::ProtectedInstructionData> unpacked;
|
||||
|
||||
@ -108,6 +109,37 @@ void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
|
||||
wasm::NativeModule* native_module) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
for (uint32_t i = native_module->num_imported_functions(),
|
||||
e = native_module->FunctionCount();
|
||||
i < e; ++i) {
|
||||
wasm::WasmCode* code = native_module->GetCode(i);
|
||||
|
||||
if (code == nullptr || code->kind() != wasm::WasmCode::Function) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code->HasTrapHandlerIndex()) continue;
|
||||
|
||||
Address base = code->instructions().start();
|
||||
|
||||
size_t size = code->instructions().size();
|
||||
const int index =
|
||||
RegisterHandlerData(base, size, code->protected_instructions().size(),
|
||||
code->protected_instructions().data());
|
||||
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
|
||||
|
||||
// TODO(eholk): if index is negative, fail.
|
||||
CHECK_LE(0, index);
|
||||
code->set_trap_handler_index(static_cast<size_t>(index));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name) {
|
||||
os << "#" << name.function_->func_index;
|
||||
if (name.function_->name.is_set()) {
|
||||
@ -140,7 +172,7 @@ WasmFunction* GetWasmFunctionForExport(Isolate* isolate,
|
||||
|
||||
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
int index, WasmFunction* function,
|
||||
Handle<Code> code) {
|
||||
Handle<Object> code_or_foreign) {
|
||||
DCHECK_EQ(0, dispatch_tables->length() % 4);
|
||||
for (int i = 0; i < dispatch_tables->length(); i += 4) {
|
||||
Handle<FixedArray> function_table(
|
||||
@ -154,7 +186,7 @@ void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
// not found; it will simply never match any check.
|
||||
auto sig_index = instance->module()->signature_map.Find(function->sig);
|
||||
signature_table->set(index, Smi::FromInt(sig_index));
|
||||
function_table->set(index, *code);
|
||||
function_table->set(index, *code_or_foreign);
|
||||
} else {
|
||||
signature_table->set(index, Smi::FromInt(-1));
|
||||
function_table->set(index, Smi::kZero);
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "src/wasm/decoder.h"
|
||||
#include "src/wasm/signature-map.h"
|
||||
#include "src/wasm/wasm-heap.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -284,10 +285,14 @@ Handle<FixedArray> DecodeLocalNames(Isolate*, Handle<WasmCompiledModule>);
|
||||
WasmFunction* GetWasmFunctionForExport(Isolate* isolate, Handle<Object> target);
|
||||
|
||||
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
int index, WasmFunction* function, Handle<Code> code);
|
||||
int index, WasmFunction* function,
|
||||
Handle<Object> code_or_foreign);
|
||||
|
||||
void UnpackAndRegisterProtectedInstructionsGC(Isolate* isolate,
|
||||
Handle<FixedArray> code_table);
|
||||
|
||||
void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
|
||||
Handle<FixedArray> code_table);
|
||||
wasm::NativeModule* native_module);
|
||||
|
||||
const char* ExternalKindName(WasmExternalKind);
|
||||
|
||||
|
@ -151,6 +151,14 @@ bool IsBreakablePosition(Handle<WasmCompiledModule> compiled_module,
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void CompiledModuleFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
|
||||
WasmCompiledModule* compiled_module = WasmCompiledModule::cast(*p);
|
||||
compiled_module->reset_native_module();
|
||||
GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle<WasmModuleObject> WasmModuleObject::New(
|
||||
@ -253,7 +261,27 @@ void WasmTableObject::Grow(Isolate* isolate, uint32_t count) {
|
||||
dispatch_tables->set(i + 3, *new_signature_table);
|
||||
|
||||
// Patch the code of the respective instance.
|
||||
{
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
wasm::CodeSpecialization code_specialization(isolate,
|
||||
&specialization_zone);
|
||||
WasmInstanceObject* instance =
|
||||
WasmInstanceObject::cast(dispatch_tables->get(i));
|
||||
WasmCompiledModule* compiled_module = instance->compiled_module();
|
||||
wasm::NativeModule* native_module = compiled_module->GetNativeModule();
|
||||
GlobalHandleAddress old_function_table_addr =
|
||||
native_module->function_tables()[table_index];
|
||||
GlobalHandleAddress old_signature_table_addr =
|
||||
native_module->signature_tables()[table_index];
|
||||
code_specialization.PatchTableSize(old_size, old_size + count);
|
||||
code_specialization.RelocatePointer(old_function_table_addr,
|
||||
new_function_table_addr);
|
||||
code_specialization.RelocatePointer(old_signature_table_addr,
|
||||
new_signature_table_addr);
|
||||
code_specialization.ApplyToWholeInstance(instance);
|
||||
native_module->function_tables()[table_index] = new_function_table_addr;
|
||||
native_module->signature_tables()[table_index] = new_signature_table_addr;
|
||||
} else {
|
||||
DisallowHeapAllocation no_gc;
|
||||
wasm::CodeSpecialization code_specialization(isolate,
|
||||
&specialization_zone);
|
||||
@ -289,7 +317,7 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
|
||||
|
||||
WasmFunction* wasm_function = nullptr;
|
||||
Handle<Code> code = Handle<Code>::null();
|
||||
Handle<Object> code = Handle<Object>::null();
|
||||
Handle<Object> value = isolate->factory()->null_value();
|
||||
|
||||
if (!function.is_null()) {
|
||||
@ -301,18 +329,35 @@ void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
value = function;
|
||||
// TODO(titzer): Make JSToWasm wrappers just call the WASM to WASM wrapper,
|
||||
// and then we can just reuse the WASM to WASM wrapper.
|
||||
Handle<WasmInstanceObject> instance(exported_function->instance(), isolate);
|
||||
int func_index = exported_function->function_index();
|
||||
Address new_context_address =
|
||||
reinterpret_cast<Address>(instance->wasm_context()->get());
|
||||
code = compiler::CompileWasmToWasmWrapper(
|
||||
isolate, exported_function->GetWasmCode(), wasm_function->sig,
|
||||
func_index, new_context_address);
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
|
||||
AttachWasmFunctionInfo(isolate, code, instance, func_index);
|
||||
Address new_context_address = reinterpret_cast<Address>(
|
||||
exported_function->instance()->wasm_context()->get());
|
||||
WasmCodeWrapper wasm_code = exported_function->GetWasmCode();
|
||||
if (!wasm_code.IsCodeObject()) {
|
||||
wasm::NativeModule* native_module = wasm_code.GetWasmCode()->owner();
|
||||
// we create the wrapper on the module exporting the function. This
|
||||
// wrapper will only be called as indirect call.
|
||||
wasm::WasmCode* exported_wrapper =
|
||||
native_module->GetExportedWrapper(wasm_code.GetWasmCode()->index());
|
||||
if (exported_wrapper == nullptr) {
|
||||
Handle<Code> new_wrapper = compiler::CompileWasmToWasmWrapper(
|
||||
isolate, wasm_code, wasm_function->sig, new_context_address);
|
||||
exported_wrapper = native_module->AddExportedWrapper(
|
||||
new_wrapper, wasm_code.GetWasmCode()->index());
|
||||
}
|
||||
Address target = exported_wrapper->instructions().start();
|
||||
code = isolate->factory()->NewForeign(target, TENURED);
|
||||
} else {
|
||||
Handle<WasmInstanceObject> instance(exported_function->instance(),
|
||||
isolate);
|
||||
int func_index = exported_function->function_index();
|
||||
code = compiler::CompileWasmToWasmWrapper(
|
||||
isolate, wasm_code, wasm_function->sig, new_context_address);
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
|
||||
AttachWasmFunctionInfo(isolate, Handle<Code>::cast(code), instance,
|
||||
func_index);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDispatchTables(isolate, dispatch_tables, index, wasm_function, code);
|
||||
array->set(index, *value);
|
||||
}
|
||||
@ -565,7 +610,20 @@ uint32_t WasmInstanceObject::GetMaxMemoryPages() {
|
||||
return FLAG_wasm_max_mem_pages;
|
||||
}
|
||||
|
||||
WasmInstanceObject* WasmInstanceObject::GetOwningInstance(Code* code) {
|
||||
WasmInstanceObject* WasmInstanceObject::GetOwningInstance(
|
||||
const wasm::WasmCode* code) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Object* weak_link = nullptr;
|
||||
DCHECK(code->kind() == wasm::WasmCode::Function ||
|
||||
code->kind() == wasm::WasmCode::InterpreterStub);
|
||||
weak_link = code->owner()->compiled_module()->ptr_to_weak_owning_instance();
|
||||
DCHECK(weak_link->IsWeakCell());
|
||||
WeakCell* cell = WeakCell::cast(weak_link);
|
||||
if (cell->cleared()) return nullptr;
|
||||
return WasmInstanceObject::cast(cell->value());
|
||||
}
|
||||
|
||||
WasmInstanceObject* WasmInstanceObject::GetOwningInstanceGC(Code* code) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
DCHECK(code->kind() == Code::WASM_FUNCTION ||
|
||||
code->kind() == Code::WASM_INTERPRETER_ENTRY);
|
||||
@ -682,11 +740,13 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
|
||||
return Handle<WasmExportedFunction>::cast(js_function);
|
||||
}
|
||||
|
||||
Handle<Code> WasmExportedFunction::GetWasmCode() {
|
||||
WasmCodeWrapper WasmExportedFunction::GetWasmCode() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Handle<Code> export_wrapper_code = handle(this->code());
|
||||
DCHECK_EQ(export_wrapper_code->kind(), Code::JS_TO_WASM_FUNCTION);
|
||||
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
|
||||
int mask =
|
||||
RelocInfo::ModeMask(FLAG_wasm_jit_to_native ? RelocInfo::JS_TO_WASM_CALL
|
||||
: RelocInfo::CODE_TARGET);
|
||||
auto IsWasmFunctionCode = [](Code* code) {
|
||||
return code->kind() == Code::WASM_FUNCTION ||
|
||||
code->kind() == Code::WASM_TO_JS_FUNCTION ||
|
||||
@ -694,18 +754,31 @@ Handle<Code> WasmExportedFunction::GetWasmCode() {
|
||||
code->kind() == Code::WASM_INTERPRETER_ENTRY ||
|
||||
code->builtin_index() == Builtins::kWasmCompileLazy;
|
||||
};
|
||||
|
||||
for (RelocIterator it(*export_wrapper_code, mask);; it.next()) {
|
||||
DCHECK(!it.done());
|
||||
Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (!IsWasmFunctionCode(target)) continue;
|
||||
WasmCodeWrapper target;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
target = WasmCodeWrapper(GetIsolate()->wasm_code_manager()->LookupCode(
|
||||
it.rinfo()->js_to_wasm_address()));
|
||||
} else {
|
||||
Code* code = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (!IsWasmFunctionCode(code)) continue;
|
||||
target = WasmCodeWrapper(handle(code));
|
||||
}
|
||||
// There should only be this one call to wasm code.
|
||||
#ifdef DEBUG
|
||||
for (it.next(); !it.done(); it.next()) {
|
||||
Code* code = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
DCHECK(!IsWasmFunctionCode(code));
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
UNREACHABLE();
|
||||
} else {
|
||||
Code* code =
|
||||
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
DCHECK(!IsWasmFunctionCode(code));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return handle(target);
|
||||
return target;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -943,8 +1016,8 @@ void WasmSharedModuleData::PrepareForLazyCompilation(
|
||||
}
|
||||
|
||||
Handle<WasmCompiledModule> WasmCompiledModule::New(
|
||||
Isolate* isolate, Handle<WasmSharedModuleData> shared,
|
||||
Handle<FixedArray> code_table, Handle<FixedArray> export_wrappers,
|
||||
Isolate* isolate, WasmModule* module, Handle<FixedArray> code_table,
|
||||
Handle<FixedArray> export_wrappers,
|
||||
const std::vector<GlobalHandleAddress>& function_tables,
|
||||
const std::vector<GlobalHandleAddress>& signature_tables) {
|
||||
DCHECK_EQ(function_tables.size(), signature_tables.size());
|
||||
@ -953,40 +1026,75 @@ Handle<WasmCompiledModule> WasmCompiledModule::New(
|
||||
// WasmCompiledModule::cast would fail since fields are not set yet.
|
||||
Handle<WasmCompiledModule> compiled_module(
|
||||
reinterpret_cast<WasmCompiledModule*>(*ret), isolate);
|
||||
compiled_module->InitId();
|
||||
compiled_module->set_shared(shared);
|
||||
compiled_module->set_native_context(isolate->native_context());
|
||||
compiled_module->set_code_table(code_table);
|
||||
compiled_module->set_export_wrappers(export_wrappers);
|
||||
// TODO(mtrofin): we copy these because the order of finalization isn't
|
||||
// reliable, and we need these at Reset (which is called at
|
||||
// finalization). If the order were reliable, and top-down, we could instead
|
||||
// just get them from shared().
|
||||
compiled_module->set_initial_pages(shared->module()->initial_pages);
|
||||
compiled_module->set_num_imported_functions(
|
||||
shared->module()->num_imported_functions);
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
compiled_module->InitId();
|
||||
compiled_module->set_native_context(isolate->native_context());
|
||||
compiled_module->set_code_table(code_table);
|
||||
compiled_module->set_export_wrappers(export_wrappers);
|
||||
// TODO(mtrofin): we copy these because the order of finalization isn't
|
||||
// reliable, and we need these at Reset (which is called at
|
||||
// finalization). If the order were reliable, and top-down, we could instead
|
||||
// just get them from shared().
|
||||
compiled_module->set_initial_pages(module->initial_pages);
|
||||
compiled_module->set_num_imported_functions(module->num_imported_functions);
|
||||
|
||||
int num_function_tables = static_cast<int>(function_tables.size());
|
||||
if (num_function_tables > 0) {
|
||||
Handle<FixedArray> st =
|
||||
isolate->factory()->NewFixedArray(num_function_tables, TENURED);
|
||||
Handle<FixedArray> ft =
|
||||
isolate->factory()->NewFixedArray(num_function_tables, TENURED);
|
||||
for (int i = 0; i < num_function_tables; ++i) {
|
||||
size_t index = static_cast<size_t>(i);
|
||||
SetTableValue(isolate, ft, i, function_tables[index]);
|
||||
SetTableValue(isolate, st, i, signature_tables[index]);
|
||||
int num_function_tables = static_cast<int>(function_tables.size());
|
||||
if (num_function_tables > 0) {
|
||||
Handle<FixedArray> st =
|
||||
isolate->factory()->NewFixedArray(num_function_tables, TENURED);
|
||||
Handle<FixedArray> ft =
|
||||
isolate->factory()->NewFixedArray(num_function_tables, TENURED);
|
||||
for (int i = 0; i < num_function_tables; ++i) {
|
||||
size_t index = static_cast<size_t>(i);
|
||||
SetTableValue(isolate, ft, i, function_tables[index]);
|
||||
SetTableValue(isolate, st, i, signature_tables[index]);
|
||||
}
|
||||
// TODO(wasm): setting the empty tables here this way is OK under the
|
||||
// assumption that we compile and then instantiate. It needs rework if we
|
||||
// do direct instantiation. The empty tables are used as a default when
|
||||
// resetting the compiled module.
|
||||
compiled_module->set_signature_tables(st);
|
||||
compiled_module->set_empty_signature_tables(st);
|
||||
compiled_module->set_function_tables(ft);
|
||||
compiled_module->set_empty_function_tables(ft);
|
||||
}
|
||||
// TODO(wasm): setting the empty tables here this way is OK under the
|
||||
// assumption that we compile and then instantiate. It needs rework if we do
|
||||
// direct instantiation. The empty tables are used as a default when
|
||||
// resetting the compiled module.
|
||||
compiled_module->set_signature_tables(st);
|
||||
compiled_module->set_empty_signature_tables(st);
|
||||
compiled_module->set_function_tables(ft);
|
||||
compiled_module->set_empty_function_tables(ft);
|
||||
}
|
||||
} else {
|
||||
if (!export_wrappers.is_null()) {
|
||||
compiled_module->set_export_wrappers(export_wrappers);
|
||||
}
|
||||
wasm::NativeModule* native_module = nullptr;
|
||||
{
|
||||
std::unique_ptr<wasm::NativeModule> native_module_ptr =
|
||||
isolate->wasm_code_manager()->NewNativeModule(*module);
|
||||
native_module = native_module_ptr.release();
|
||||
Handle<Foreign> native_module_wrapper =
|
||||
Managed<wasm::NativeModule>::From(isolate, native_module);
|
||||
compiled_module->set_native_module(native_module_wrapper);
|
||||
Handle<WasmCompiledModule> weak_link =
|
||||
isolate->global_handles()->Create(*compiled_module);
|
||||
GlobalHandles::MakeWeak(Handle<Object>::cast(weak_link).location(),
|
||||
Handle<Object>::cast(weak_link).location(),
|
||||
&CompiledModuleFinalizer,
|
||||
v8::WeakCallbackType::kFinalizer);
|
||||
compiled_module->GetNativeModule()->SetCompiledModule(weak_link);
|
||||
}
|
||||
// This is here just because it's easier for APIs that need to work with
|
||||
// either code_table or native_module. Otherwise we need to check if
|
||||
// has_code_table and pass undefined.
|
||||
compiled_module->set_code_table(code_table);
|
||||
|
||||
native_module->function_tables() = function_tables;
|
||||
native_module->signature_tables() = signature_tables;
|
||||
native_module->empty_function_tables() = function_tables;
|
||||
native_module->empty_signature_tables() = signature_tables;
|
||||
|
||||
int function_count = static_cast<int>(module->functions.size());
|
||||
compiled_module->set_handler_table(
|
||||
isolate->factory()->NewFixedArray(function_count, TENURED));
|
||||
compiled_module->set_source_positions(
|
||||
isolate->factory()->NewFixedArray(function_count, TENURED));
|
||||
}
|
||||
// TODO(mtrofin): copy the rest of the specialization parameters over.
|
||||
// We're currently OK because we're only using defaults.
|
||||
return compiled_module;
|
||||
@ -994,16 +1102,42 @@ Handle<WasmCompiledModule> WasmCompiledModule::New(
|
||||
|
||||
Handle<WasmCompiledModule> WasmCompiledModule::Clone(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> module) {
|
||||
Handle<FixedArray> code_copy =
|
||||
isolate->factory()->CopyFixedArray(module->code_table());
|
||||
Handle<FixedArray> code_copy;
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
code_copy = isolate->factory()->CopyFixedArray(module->code_table());
|
||||
}
|
||||
Handle<WasmCompiledModule> ret = Handle<WasmCompiledModule>::cast(
|
||||
isolate->factory()->CopyFixedArray(module));
|
||||
ret->InitId();
|
||||
ret->set_code_table(code_copy);
|
||||
ret->reset_weak_owning_instance();
|
||||
ret->reset_next_instance();
|
||||
ret->reset_prev_instance();
|
||||
ret->reset_weak_exported_functions();
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
ret->InitId();
|
||||
ret->set_code_table(code_copy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr<wasm::NativeModule> native_module =
|
||||
module->GetNativeModule()->Clone();
|
||||
// construct the wrapper in 2 steps, because its construction may trigger GC,
|
||||
// which would shift the this pointer in set_native_module.
|
||||
Handle<Foreign> native_module_wrapper =
|
||||
Managed<wasm::NativeModule>::From(isolate, native_module.release());
|
||||
ret->set_native_module(native_module_wrapper);
|
||||
Handle<WasmCompiledModule> weak_link =
|
||||
isolate->global_handles()->Create(*ret);
|
||||
GlobalHandles::MakeWeak(Handle<Object>::cast(weak_link).location(),
|
||||
Handle<Object>::cast(weak_link).location(),
|
||||
&CompiledModuleFinalizer,
|
||||
v8::WeakCallbackType::kFinalizer);
|
||||
ret->GetNativeModule()->SetCompiledModule(weak_link);
|
||||
|
||||
if (module->has_lazy_compile_data()) {
|
||||
Handle<FixedArray> lazy_comp_data = isolate->factory()->NewFixedArray(
|
||||
module->lazy_compile_data()->length(), TENURED);
|
||||
ret->set_lazy_compile_data(lazy_comp_data);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1028,8 +1162,13 @@ Address WasmCompiledModule::GetTableValue(FixedArray* table, int index) {
|
||||
return reinterpret_cast<Address>(static_cast<size_t>(value));
|
||||
}
|
||||
|
||||
void WasmCompiledModule::Reset(Isolate* isolate,
|
||||
WasmCompiledModule* compiled_module) {
|
||||
wasm::NativeModule* WasmCompiledModule::GetNativeModule() const {
|
||||
if (!has_native_module()) return nullptr;
|
||||
return Managed<wasm::NativeModule>::cast(ptr_to_native_module())->get();
|
||||
}
|
||||
|
||||
void WasmCompiledModule::ResetGCModel(Isolate* isolate,
|
||||
WasmCompiledModule* compiled_module) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
TRACE("Resetting %d\n", compiled_module->instance_id());
|
||||
Object* undefined = *isolate->factory()->undefined_value();
|
||||
@ -1084,8 +1223,8 @@ void WasmCompiledModule::Reset(Isolate* isolate,
|
||||
}
|
||||
break;
|
||||
}
|
||||
bool changed =
|
||||
code_specialization.ApplyToWasmCode(code, SKIP_ICACHE_FLUSH);
|
||||
bool changed = code_specialization.ApplyToWasmCode(
|
||||
WasmCodeWrapper(handle(code)), SKIP_ICACHE_FLUSH);
|
||||
// TODO(wasm): Check if this is faster than passing FLUSH_ICACHE_IF_NEEDED
|
||||
// above.
|
||||
if (changed) {
|
||||
@ -1104,6 +1243,82 @@ void WasmCompiledModule::InitId() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void WasmCompiledModule::Reset(Isolate* isolate,
|
||||
WasmCompiledModule* compiled_module) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
compiled_module->reset_prev_instance();
|
||||
compiled_module->reset_next_instance();
|
||||
wasm::NativeModule* native_module = compiled_module->GetNativeModule();
|
||||
if (native_module == nullptr) return;
|
||||
TRACE("Resetting %zu\n", native_module->instance_id);
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
for (uint32_t i = native_module->num_imported_functions(),
|
||||
e = native_module->FunctionCount();
|
||||
i < e; ++i) {
|
||||
wasm::WasmCode* wasm_code = native_module->GetCode(i);
|
||||
if (wasm_code->HasTrapHandlerIndex()) {
|
||||
CHECK_LT(wasm_code->trap_handler_index(),
|
||||
static_cast<size_t>(std::numeric_limits<int>::max()));
|
||||
trap_handler::ReleaseHandlerData(
|
||||
static_cast<int>(wasm_code->trap_handler_index()));
|
||||
wasm_code->ResetTrapHandlerIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Patch code to update memory references, global references, and function
|
||||
// table references.
|
||||
Zone specialization_zone(isolate->allocator(), ZONE_NAME);
|
||||
wasm::CodeSpecialization code_specialization(isolate, &specialization_zone);
|
||||
|
||||
if (compiled_module->has_lazy_compile_data()) {
|
||||
for (int i = 0, e = compiled_module->lazy_compile_data()->length(); i < e;
|
||||
++i) {
|
||||
compiled_module->lazy_compile_data()->set(
|
||||
i, isolate->heap()->undefined_value());
|
||||
}
|
||||
}
|
||||
// Reset function tables.
|
||||
if (native_module->function_tables().size() > 0) {
|
||||
std::vector<GlobalHandleAddress>& function_tables =
|
||||
native_module->function_tables();
|
||||
std::vector<GlobalHandleAddress>& signature_tables =
|
||||
native_module->signature_tables();
|
||||
std::vector<GlobalHandleAddress>& empty_function_tables =
|
||||
native_module->empty_function_tables();
|
||||
std::vector<GlobalHandleAddress>& empty_signature_tables =
|
||||
native_module->empty_signature_tables();
|
||||
|
||||
if (function_tables != empty_function_tables) {
|
||||
DCHECK_EQ(function_tables.size(), empty_function_tables.size());
|
||||
for (size_t i = 0, e = function_tables.size(); i < e; ++i) {
|
||||
code_specialization.RelocatePointer(function_tables[i],
|
||||
empty_function_tables[i]);
|
||||
code_specialization.RelocatePointer(signature_tables[i],
|
||||
empty_signature_tables[i]);
|
||||
}
|
||||
native_module->function_tables() = empty_function_tables;
|
||||
native_module->signature_tables() = empty_signature_tables;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = native_module->num_imported_functions(),
|
||||
end = native_module->FunctionCount();
|
||||
i < end; ++i) {
|
||||
wasm::WasmCode* code = native_module->GetCode(i);
|
||||
// Skip lazy compile stubs.
|
||||
if (code == nullptr || code->kind() != wasm::WasmCode::Function) continue;
|
||||
bool changed = code_specialization.ApplyToWasmCode(WasmCodeWrapper(code),
|
||||
SKIP_ICACHE_FLUSH);
|
||||
// TODO(wasm): Check if this is faster than passing FLUSH_ICACHE_IF_NEEDED
|
||||
// above.
|
||||
if (changed) {
|
||||
Assembler::FlushICache(isolate, code->instructions().start(),
|
||||
code->instructions().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaybeHandle<String> WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
|
||||
wasm::WireBytesRef ref) {
|
||||
@ -1149,7 +1364,7 @@ bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
|
||||
#define WCM_CHECK_CONST_OBJECT(TYPE, NAME) \
|
||||
WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->Is##TYPE())
|
||||
#define WCM_CHECK_WASM_OBJECT(TYPE, NAME) \
|
||||
WCM_CHECK_TYPE(NAME, TYPE::Is##TYPE(obj))
|
||||
WCM_CHECK_TYPE(NAME, obj->IsFixedArray() || obj->IsUndefined(isolate))
|
||||
#define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT(WeakCell, NAME)
|
||||
#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) \
|
||||
WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->IsSmi())
|
||||
@ -1173,7 +1388,11 @@ void WasmCompiledModule::PrintInstancesChain() {
|
||||
#if DEBUG
|
||||
if (!FLAG_trace_wasm_instances) return;
|
||||
for (WasmCompiledModule* current = this; current != nullptr;) {
|
||||
PrintF("->%d", current->instance_id());
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
PrintF("->%zu", current->GetNativeModule()->instance_id);
|
||||
} else {
|
||||
PrintF("->%d", current->instance_id());
|
||||
}
|
||||
if (!current->has_next_instance()) break;
|
||||
current = current->ptr_to_next_instance();
|
||||
}
|
||||
@ -1204,6 +1423,11 @@ void WasmCompiledModule::RemoveFromChain() {
|
||||
}
|
||||
}
|
||||
|
||||
void WasmCompiledModule::OnWasmModuleDecodingComplete(
|
||||
Handle<WasmSharedModuleData> shared) {
|
||||
set_shared(shared);
|
||||
}
|
||||
|
||||
void WasmCompiledModule::ReinitializeAfterDeserialization(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
|
||||
// This method must only be called immediately after deserialization.
|
||||
@ -1212,31 +1436,48 @@ void WasmCompiledModule::ReinitializeAfterDeserialization(
|
||||
Handle<WasmSharedModuleData> shared(
|
||||
static_cast<WasmSharedModuleData*>(compiled_module->get(kID_shared)),
|
||||
isolate);
|
||||
DCHECK(!WasmSharedModuleData::IsWasmSharedModuleData(*shared));
|
||||
WasmSharedModuleData::ReinitializeAfterDeserialization(isolate, shared);
|
||||
int function_table_count =
|
||||
static_cast<int>(compiled_module->module()->function_tables.size());
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
DCHECK(!WasmSharedModuleData::IsWasmSharedModuleData(*shared));
|
||||
WasmSharedModuleData::ReinitializeAfterDeserialization(isolate, shared);
|
||||
}
|
||||
size_t function_table_count =
|
||||
compiled_module->module()->function_tables.size();
|
||||
wasm::NativeModule* native_module = compiled_module->GetNativeModule();
|
||||
|
||||
if (function_table_count > 0) {
|
||||
// The tables are of the right size, but contain bogus global handle
|
||||
// addresses. Produce new global handles for the empty tables, then reset,
|
||||
// which will relocate the code. We end up with a WasmCompiledModule as-if
|
||||
// it were just compiled.
|
||||
DCHECK(compiled_module->has_function_tables());
|
||||
DCHECK(compiled_module->has_signature_tables());
|
||||
DCHECK(compiled_module->has_empty_signature_tables());
|
||||
DCHECK(compiled_module->has_empty_function_tables());
|
||||
|
||||
for (int i = 0; i < function_table_count; ++i) {
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
DCHECK(compiled_module->has_function_tables());
|
||||
DCHECK(compiled_module->has_signature_tables());
|
||||
DCHECK(compiled_module->has_empty_signature_tables());
|
||||
DCHECK(compiled_module->has_empty_function_tables());
|
||||
} else {
|
||||
DCHECK_GT(native_module->function_tables().size(), 0);
|
||||
DCHECK_GT(native_module->signature_tables().size(), 0);
|
||||
DCHECK_EQ(native_module->empty_signature_tables().size(),
|
||||
native_module->function_tables().size());
|
||||
DCHECK_EQ(native_module->empty_function_tables().size(),
|
||||
native_module->function_tables().size());
|
||||
}
|
||||
for (size_t i = 0; i < function_table_count; ++i) {
|
||||
Handle<Object> global_func_table_handle =
|
||||
isolate->global_handles()->Create(isolate->heap()->undefined_value());
|
||||
Handle<Object> global_sig_table_handle =
|
||||
isolate->global_handles()->Create(isolate->heap()->undefined_value());
|
||||
GlobalHandleAddress new_func_table = global_func_table_handle.address();
|
||||
GlobalHandleAddress new_sig_table = global_sig_table_handle.address();
|
||||
SetTableValue(isolate, compiled_module->empty_function_tables(), i,
|
||||
new_func_table);
|
||||
SetTableValue(isolate, compiled_module->empty_signature_tables(), i,
|
||||
new_sig_table);
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
SetTableValue(isolate, compiled_module->empty_function_tables(),
|
||||
static_cast<int>(i), new_func_table);
|
||||
SetTableValue(isolate, compiled_module->empty_signature_tables(),
|
||||
static_cast<int>(i), new_sig_table);
|
||||
} else {
|
||||
native_module->empty_function_tables()[i] = new_func_table;
|
||||
native_module->empty_signature_tables()[i] = new_sig_table;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1596,18 +1837,6 @@ MaybeHandle<FixedArray> WasmCompiledModule::CheckBreakPoints(int position) {
|
||||
return isolate->debug()->GetHitBreakPointObjects(breakpoint_objects);
|
||||
}
|
||||
|
||||
Handle<Code> WasmCompiledModule::CompileLazy(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<Code> caller,
|
||||
int offset, int func_index, bool patch_caller) {
|
||||
isolate->set_context(*instance->compiled_module()->native_context());
|
||||
Object* orch_obj =
|
||||
instance->compiled_module()->shared()->lazy_compilation_orchestrator();
|
||||
auto* orch =
|
||||
Managed<wasm::LazyCompilationOrchestrator>::cast(orch_obj)->get();
|
||||
return orch->CompileLazy(isolate, instance, caller, offset, func_index,
|
||||
patch_caller);
|
||||
}
|
||||
|
||||
void AttachWasmFunctionInfo(Isolate* isolate, Handle<Code> code,
|
||||
MaybeHandle<WeakCell> weak_instance,
|
||||
int func_index) {
|
||||
|
@ -22,6 +22,8 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
class InterpretedFrame;
|
||||
class NativeModule;
|
||||
class WasmCode;
|
||||
class WasmInterpreter;
|
||||
struct WasmModule;
|
||||
class SignatureMap;
|
||||
@ -228,7 +230,8 @@ class WasmInstanceObject : public JSObject {
|
||||
// Assumed to be called with a code object associated to a wasm module
|
||||
// instance. Intended to be called from runtime functions. Returns nullptr on
|
||||
// failing to get owning instance.
|
||||
static WasmInstanceObject* GetOwningInstance(Code* code);
|
||||
static WasmInstanceObject* GetOwningInstance(const wasm::WasmCode* code);
|
||||
static WasmInstanceObject* GetOwningInstanceGC(Code* code);
|
||||
|
||||
static void ValidateInstancesChainForTesting(
|
||||
Isolate* isolate, Handle<WasmModuleObject> module_obj,
|
||||
@ -253,7 +256,7 @@ class WasmExportedFunction : public JSFunction {
|
||||
int func_index, int arity,
|
||||
Handle<Code> export_wrapper);
|
||||
|
||||
Handle<Code> GetWasmCode();
|
||||
WasmCodeWrapper GetWasmCode();
|
||||
};
|
||||
|
||||
// Information shared by all WasmCompiledModule objects for the same module.
|
||||
@ -305,9 +308,7 @@ class WasmSharedModuleData : public FixedArray {
|
||||
Handle<SeqOneByteString> module_bytes, Handle<Script> script,
|
||||
Handle<ByteArray> asm_js_offset_table);
|
||||
|
||||
private:
|
||||
DECL_OPTIONAL_ACCESSORS(lazy_compilation_orchestrator, Foreign)
|
||||
friend class WasmCompiledModule;
|
||||
};
|
||||
|
||||
// This represents the set of wasm compiled functions, together
|
||||
@ -403,6 +404,8 @@ class WasmCompiledModule : public FixedArray {
|
||||
MACRO(CONST_OBJECT, FixedArray, empty_signature_tables) \
|
||||
MACRO(SMALL_CONST_NUMBER, uint32_t, initial_pages)
|
||||
|
||||
// TODO(mtrofin): this is unnecessary when we stop needing
|
||||
// FLAG_wasm_jit_to_native, because we have instance_id on NativeModule.
|
||||
#if DEBUG
|
||||
#define DEBUG_ONLY_TABLE(MACRO) MACRO(SMALL_CONST_NUMBER, uint32_t, instance_id)
|
||||
#else
|
||||
@ -426,8 +429,8 @@ class WasmCompiledModule : public FixedArray {
|
||||
|
||||
public:
|
||||
static Handle<WasmCompiledModule> New(
|
||||
Isolate* isolate, Handle<WasmSharedModuleData> shared,
|
||||
Handle<FixedArray> code_table, Handle<FixedArray> export_wrappers,
|
||||
Isolate* isolate, wasm::WasmModule* module, Handle<FixedArray> code_table,
|
||||
Handle<FixedArray> export_wrappers,
|
||||
const std::vector<wasm::GlobalHandleAddress>& function_tables,
|
||||
const std::vector<wasm::GlobalHandleAddress>& signature_tables);
|
||||
|
||||
@ -435,9 +438,15 @@ class WasmCompiledModule : public FixedArray {
|
||||
Handle<WasmCompiledModule> module);
|
||||
static void Reset(Isolate* isolate, WasmCompiledModule* module);
|
||||
|
||||
// TODO(mtrofin): delete this when we don't need FLAG_wasm_jit_to_native
|
||||
static void ResetGCModel(Isolate* isolate, WasmCompiledModule* module);
|
||||
|
||||
uint32_t default_mem_size() const;
|
||||
|
||||
wasm::NativeModule* GetNativeModule() const;
|
||||
void InsertInChain(WasmModuleObject*);
|
||||
void RemoveFromChain();
|
||||
void OnWasmModuleDecodingComplete(Handle<WasmSharedModuleData>);
|
||||
|
||||
#define DECLARATION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME)
|
||||
WCM_PROPERTY_TABLE(DECLARATION)
|
||||
@ -539,23 +548,16 @@ class WasmCompiledModule : public FixedArray {
|
||||
// FixedArray with all hit breakpoint objects.
|
||||
MaybeHandle<FixedArray> CheckBreakPoints(int position);
|
||||
|
||||
// Compile lazily the function called in the given caller code object at the
|
||||
// given offset.
|
||||
// If the called function cannot be determined from the caller (indirect
|
||||
// call / exported function), func_index must be set. Otherwise it can be -1.
|
||||
// If patch_caller is set, then all direct calls to functions which were
|
||||
// already lazily compiled are patched (at least the given call site).
|
||||
// Returns the Code to be called at the given call site.
|
||||
static Handle<Code> CompileLazy(Isolate*, Handle<WasmInstanceObject>,
|
||||
Handle<Code> caller, int offset,
|
||||
int func_index, bool patch_caller);
|
||||
|
||||
inline void ReplaceCodeTableForTesting(Handle<FixedArray> testing_table);
|
||||
inline void ReplaceCodeTableForTesting(
|
||||
std::vector<wasm::WasmCode*>&& testing_table);
|
||||
|
||||
// TODO(mtrofin): following 4 unnecessary after we're done with
|
||||
// FLAG_wasm_jit_to_native
|
||||
static void SetTableValue(Isolate* isolate, Handle<FixedArray> table,
|
||||
int index, Address value);
|
||||
static void UpdateTableValue(FixedArray* table, int index, Address value);
|
||||
static Address GetTableValue(FixedArray* table, int index);
|
||||
inline void ReplaceCodeTableForTesting(Handle<FixedArray> testing_table);
|
||||
|
||||
private:
|
||||
void InitId();
|
||||
|
687
src/wasm/wasm-serialization.cc
Normal file
687
src/wasm/wasm-serialization.cc
Normal file
@ -0,0 +1,687 @@
|
||||
// Copyright 2017 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/wasm/wasm-serialization.h"
|
||||
|
||||
#include "src/assembler-inl.h"
|
||||
#include "src/code-stubs.h"
|
||||
#include "src/external-reference-table.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/snapshot/serializer-common.h"
|
||||
#include "src/version.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
namespace {
|
||||
void SetRawTargetData(RelocInfo* rinfo, uint32_t value) {
|
||||
if (rinfo->target_address_size() == sizeof(uint32_t)) {
|
||||
*(reinterpret_cast<uint32_t*>(rinfo->target_address_address())) = value;
|
||||
return;
|
||||
} else {
|
||||
DCHECK_EQ(rinfo->target_address_size(), sizeof(intptr_t));
|
||||
DCHECK_EQ(rinfo->target_address_size(), 8);
|
||||
*(reinterpret_cast<intptr_t*>(rinfo->target_address_address())) =
|
||||
static_cast<intptr_t>(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class Writer {
|
||||
public:
|
||||
explicit Writer(Vector<byte> buffer) : buffer_(buffer) {}
|
||||
template <typename T>
|
||||
void Write(const T& value) {
|
||||
if (FLAG_wasm_trace_serialization) {
|
||||
OFStream os(stdout);
|
||||
os << "wrote: " << (size_t)value << " sized: " << sizeof(T) << std::endl;
|
||||
}
|
||||
DCHECK_GE(buffer_.size(), sizeof(T));
|
||||
memcpy(buffer_.start(), reinterpret_cast<const byte*>(&value), sizeof(T));
|
||||
buffer_ = buffer_ + sizeof(T);
|
||||
}
|
||||
|
||||
void WriteVector(const Vector<const byte> data) {
|
||||
DCHECK_GE(buffer_.size(), data.size());
|
||||
if (data.size() > 0) {
|
||||
memcpy(buffer_.start(), data.start(), data.size());
|
||||
buffer_ = buffer_ + data.size();
|
||||
}
|
||||
if (FLAG_wasm_trace_serialization) {
|
||||
OFStream os(stdout);
|
||||
os << "wrote vector of " << data.size() << " elements" << std::endl;
|
||||
}
|
||||
}
|
||||
Vector<byte> current_buffer() const { return buffer_; }
|
||||
|
||||
private:
|
||||
Vector<byte> buffer_;
|
||||
};
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
explicit Reader(Vector<const byte> buffer) : buffer_(buffer) {}
|
||||
|
||||
template <typename T>
|
||||
T Read() {
|
||||
DCHECK_GE(buffer_.size(), sizeof(T));
|
||||
T ret;
|
||||
memcpy(reinterpret_cast<byte*>(&ret), buffer_.start(), sizeof(T));
|
||||
buffer_ = buffer_ + sizeof(T);
|
||||
if (FLAG_wasm_trace_serialization) {
|
||||
OFStream os(stdout);
|
||||
os << "read: " << (size_t)ret << " sized: " << sizeof(T) << std::endl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector<const byte> GetSubvector(size_t size) {
|
||||
Vector<const byte> ret = {buffer_.start(), size};
|
||||
buffer_ = buffer_ + size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ReadIntoVector(const Vector<byte> data) {
|
||||
if (data.size() > 0) {
|
||||
DCHECK_GE(buffer_.size(), data.size());
|
||||
memcpy(data.start(), buffer_.start(), data.size());
|
||||
buffer_ = buffer_ + data.size();
|
||||
}
|
||||
if (FLAG_wasm_trace_serialization) {
|
||||
OFStream os(stdout);
|
||||
os << "read vector of " << data.size() << " elements" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<const byte> current_buffer() const { return buffer_; }
|
||||
|
||||
private:
|
||||
Vector<const byte> buffer_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
size_t WasmSerializedFormatVersion::GetVersionSize() { return kVersionSize; }
|
||||
|
||||
bool WasmSerializedFormatVersion::WriteVersion(Isolate* isolate,
|
||||
Vector<byte> buffer) {
|
||||
if (buffer.size() < GetVersionSize()) return false;
|
||||
Writer writer(buffer);
|
||||
writer.Write(SerializedData::ComputeMagicNumber(
|
||||
ExternalReferenceTable::instance(isolate)));
|
||||
writer.Write(Version::Hash());
|
||||
writer.Write(static_cast<uint32_t>(CpuFeatures::SupportedFeatures()));
|
||||
writer.Write(FlagList::Hash());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WasmSerializedFormatVersion::IsSupportedVersion(
|
||||
Isolate* isolate, const Vector<const byte> buffer) {
|
||||
if (buffer.size() < kVersionSize) return false;
|
||||
byte version[kVersionSize];
|
||||
CHECK(WriteVersion(isolate, {version, kVersionSize}));
|
||||
if (memcmp(buffer.start(), version, kVersionSize) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
NativeModuleSerializer::NativeModuleSerializer(Isolate* isolate,
|
||||
const NativeModule* module)
|
||||
: isolate_(isolate), native_module_(module) {
|
||||
DCHECK_NOT_NULL(isolate_);
|
||||
DCHECK_NOT_NULL(native_module_);
|
||||
DCHECK_NULL(native_module_->lazy_builtin_);
|
||||
// TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist
|
||||
// the unique ones, i.e. the cache.
|
||||
ExternalReferenceTable* table = ExternalReferenceTable::instance(isolate_);
|
||||
for (uint32_t i = 0; i < table->size(); ++i) {
|
||||
Address addr = table->address(i);
|
||||
reference_table_lookup_.insert(std::make_pair(addr, i));
|
||||
}
|
||||
// defer populating the stub_lookup_ to when we buffer the stubs
|
||||
for (auto pair : native_module_->trampolines_) {
|
||||
v8::internal::Code* code = Code::GetCodeFromTargetAddress(pair.first);
|
||||
int builtin_index = code->builtin_index();
|
||||
if (builtin_index >= 0) {
|
||||
uint32_t tag = static_cast<uint32_t>(builtin_index);
|
||||
builtin_lookup_.insert(std::make_pair(pair.second, tag));
|
||||
}
|
||||
}
|
||||
BufferHeader();
|
||||
state_ = Metadata;
|
||||
}
|
||||
|
||||
size_t NativeModuleSerializer::MeasureHeader() const {
|
||||
return sizeof(uint32_t) + // total wasm fct count
|
||||
sizeof(
|
||||
uint32_t) + // imported fcts - i.e. index of first wasm function
|
||||
sizeof(uint32_t) + // table count
|
||||
native_module_->specialization_data_.function_tables.size() *
|
||||
2 // 2 same-sized tables, containing pointers
|
||||
* sizeof(GlobalHandleAddress);
|
||||
}
|
||||
|
||||
void NativeModuleSerializer::BufferHeader() {
|
||||
size_t metadata_size = MeasureHeader();
|
||||
scratch_.resize(metadata_size);
|
||||
remaining_ = {scratch_.data(), metadata_size};
|
||||
Writer writer(remaining_);
|
||||
writer.Write(native_module_->FunctionCount());
|
||||
writer.Write(native_module_->num_imported_functions());
|
||||
writer.Write(static_cast<uint32_t>(
|
||||
native_module_->specialization_data_.function_tables.size()));
|
||||
for (size_t i = 0,
|
||||
e = native_module_->specialization_data_.function_tables.size();
|
||||
i < e; ++i) {
|
||||
writer.Write(native_module_->specialization_data_.function_tables[i]);
|
||||
writer.Write(native_module_->specialization_data_.signature_tables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
size_t NativeModuleSerializer::GetCodeHeaderSize() {
|
||||
return sizeof(size_t) + // size of this section
|
||||
sizeof(size_t) + // offset of constant pool
|
||||
sizeof(size_t) + // offset of safepoint table
|
||||
sizeof(uint32_t) + // stack slots
|
||||
sizeof(size_t) + // code size
|
||||
sizeof(size_t) + // reloc size
|
||||
sizeof(uint32_t) + // handler size
|
||||
sizeof(uint32_t) + // source positions size
|
||||
sizeof(size_t) + // protected instructions size
|
||||
sizeof(bool); // is_liftoff
|
||||
}
|
||||
|
||||
size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const {
|
||||
FixedArray* handler_table = GetHandlerTable(code);
|
||||
ByteArray* source_positions = GetSourcePositions(code);
|
||||
return GetCodeHeaderSize() + code->instructions().size() + // code
|
||||
code->reloc_info().size() + // reloc info
|
||||
(handler_table == nullptr
|
||||
? 0
|
||||
: static_cast<uint32_t>(
|
||||
handler_table->length())) + // handler table
|
||||
(source_positions == nullptr
|
||||
? 0
|
||||
: static_cast<uint32_t>(
|
||||
source_positions->length())) + // source positions
|
||||
code->protected_instructions().size() *
|
||||
sizeof(trap_handler::ProtectedInstructionData);
|
||||
}
|
||||
|
||||
size_t NativeModuleSerializer::Measure() const {
|
||||
size_t ret = MeasureHeader() + MeasureCopiedStubs();
|
||||
for (uint32_t i = native_module_->num_imported_functions(),
|
||||
e = native_module_->FunctionCount();
|
||||
i < e; ++i) {
|
||||
ret += MeasureCode(native_module_->GetCode(i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t NativeModuleSerializer::DrainBuffer(Vector<byte> dest) {
|
||||
size_t to_write = std::min(dest.size(), remaining_.size());
|
||||
memcpy(dest.start(), remaining_.start(), to_write);
|
||||
DCHECK_GE(remaining_.size(), to_write);
|
||||
remaining_ = remaining_ + to_write;
|
||||
return to_write;
|
||||
}
|
||||
|
||||
size_t NativeModuleSerializer::MeasureCopiedStubs() const {
|
||||
size_t ret = sizeof(uint32_t) + // number of stubs
|
||||
native_module_->stubs_.size() * sizeof(uint32_t); // stub keys
|
||||
for (auto pair : native_module_->trampolines_) {
|
||||
v8::internal::Code* code = Code::GetCodeFromTargetAddress(pair.first);
|
||||
int builtin_index = code->builtin_index();
|
||||
if (builtin_index < 0) ret += sizeof(uint32_t);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void NativeModuleSerializer::BufferCopiedStubs() {
|
||||
// We buffer all the stubs together, because they are very likely
|
||||
// few and small. Each stub is buffered like a WasmCode would,
|
||||
// and in addition prefaced by its stub key. The whole section is prefaced
|
||||
// by the number of stubs.
|
||||
size_t buff_size = MeasureCopiedStubs();
|
||||
scratch_.resize(buff_size);
|
||||
remaining_ = {scratch_.data(), buff_size};
|
||||
Writer writer(remaining_);
|
||||
writer.Write(
|
||||
static_cast<uint32_t>((buff_size - sizeof(uint32_t)) / sizeof(uint32_t)));
|
||||
uint32_t stub_id = 0;
|
||||
|
||||
for (auto pair : native_module_->stubs_) {
|
||||
uint32_t key = pair.first;
|
||||
writer.Write(key);
|
||||
stub_lookup_.insert(
|
||||
std::make_pair(pair.second->instructions().start(), stub_id));
|
||||
++stub_id;
|
||||
}
|
||||
|
||||
for (auto pair : native_module_->trampolines_) {
|
||||
v8::internal::Code* code = Code::GetCodeFromTargetAddress(pair.first);
|
||||
int builtin_index = code->builtin_index();
|
||||
if (builtin_index < 0) {
|
||||
stub_lookup_.insert(std::make_pair(pair.second, stub_id));
|
||||
writer.Write(code->stub_key());
|
||||
++stub_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FixedArray* NativeModuleSerializer::GetHandlerTable(
|
||||
const WasmCode* code) const {
|
||||
if (code->kind() != WasmCode::Function) return nullptr;
|
||||
uint32_t index = code->index();
|
||||
// We write the address, the size, and then copy the code as-is, followed
|
||||
// by reloc info, followed by handler table and source positions.
|
||||
Object* handler_table_entry =
|
||||
native_module_->compiled_module()->handler_table()->get(
|
||||
static_cast<int>(index));
|
||||
if (handler_table_entry->IsFixedArray()) {
|
||||
return FixedArray::cast(handler_table_entry);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ByteArray* NativeModuleSerializer::GetSourcePositions(
|
||||
const WasmCode* code) const {
|
||||
if (code->kind() != WasmCode::Function) return nullptr;
|
||||
uint32_t index = code->index();
|
||||
Object* source_positions_entry =
|
||||
native_module_->compiled_module()->source_positions()->get(
|
||||
static_cast<int>(index));
|
||||
if (source_positions_entry->IsByteArray()) {
|
||||
return ByteArray::cast(source_positions_entry);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NativeModuleSerializer::BufferCurrentWasmCode() {
|
||||
const WasmCode* code = native_module_->GetCode(index_);
|
||||
size_t size = MeasureCode(code);
|
||||
scratch_.resize(size);
|
||||
remaining_ = {scratch_.data(), size};
|
||||
BufferCodeInAllocatedScratch(code);
|
||||
}
|
||||
|
||||
void NativeModuleSerializer::BufferCodeInAllocatedScratch(
|
||||
const WasmCode* code) {
|
||||
// We write the address, the size, and then copy the code as-is, followed
|
||||
// by reloc info, followed by handler table and source positions.
|
||||
FixedArray* handler_table_entry = GetHandlerTable(code);
|
||||
uint32_t handler_table_size = 0;
|
||||
Address handler_table = nullptr;
|
||||
if (handler_table_entry != nullptr) {
|
||||
handler_table_size = static_cast<uint32_t>(handler_table_entry->length());
|
||||
handler_table = reinterpret_cast<Address>(
|
||||
handler_table_entry->GetFirstElementAddress());
|
||||
}
|
||||
ByteArray* source_positions_entry = GetSourcePositions(code);
|
||||
Address source_positions = nullptr;
|
||||
uint32_t source_positions_size = 0;
|
||||
if (source_positions_entry != nullptr) {
|
||||
source_positions = source_positions_entry->GetDataStartAddress();
|
||||
source_positions_size =
|
||||
static_cast<uint32_t>(source_positions_entry->length());
|
||||
}
|
||||
Writer writer(remaining_);
|
||||
// write the header
|
||||
writer.Write(MeasureCode(code));
|
||||
writer.Write(code->constant_pool_offset());
|
||||
writer.Write(code->safepoint_table_offset());
|
||||
writer.Write(code->stack_slots());
|
||||
writer.Write(code->instructions().size());
|
||||
writer.Write(code->reloc_info().size());
|
||||
writer.Write(handler_table_size);
|
||||
writer.Write(source_positions_size);
|
||||
writer.Write(code->protected_instructions().size());
|
||||
writer.Write(code->is_liftoff());
|
||||
// next is the code, which we have to reloc.
|
||||
Address serialized_code_start = writer.current_buffer().start();
|
||||
// write the code and everything else
|
||||
writer.WriteVector(code->instructions());
|
||||
writer.WriteVector(code->reloc_info());
|
||||
writer.WriteVector({handler_table, handler_table_size});
|
||||
writer.WriteVector({source_positions, source_positions_size});
|
||||
writer.WriteVector(
|
||||
{reinterpret_cast<const byte*>(code->protected_instructions().data()),
|
||||
sizeof(trap_handler::ProtectedInstructionData) *
|
||||
code->protected_instructions().size()});
|
||||
// now relocate the code
|
||||
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
|
||||
RelocInfo::ModeMask(RelocInfo::WASM_CALL) |
|
||||
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
|
||||
RelocIterator orig_iter(code->instructions(), code->reloc_info(),
|
||||
code->constant_pool(), mask);
|
||||
for (RelocIterator
|
||||
iter({serialized_code_start, code->instructions().size()},
|
||||
code->reloc_info(),
|
||||
serialized_code_start + code->constant_pool_offset(), mask);
|
||||
!iter.done(); iter.next(), orig_iter.next()) {
|
||||
RelocInfo::Mode mode = orig_iter.rinfo()->rmode();
|
||||
switch (mode) {
|
||||
case RelocInfo::CODE_TARGET: {
|
||||
Address orig_target = orig_iter.rinfo()->target_address();
|
||||
uint32_t tag = EncodeBuiltinOrStub(orig_target);
|
||||
SetRawTargetData(iter.rinfo(), tag);
|
||||
} break;
|
||||
case RelocInfo::WASM_CALL: {
|
||||
Address orig_target = orig_iter.rinfo()->wasm_call_address();
|
||||
uint32_t tag = wasm_targets_lookup_[orig_target];
|
||||
SetRawTargetData(iter.rinfo(), tag);
|
||||
} break;
|
||||
case RelocInfo::RUNTIME_ENTRY: {
|
||||
Address orig_target = orig_iter.rinfo()->target_address();
|
||||
uint32_t tag = reference_table_lookup_[orig_target];
|
||||
SetRawTargetData(iter.rinfo(), tag);
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t NativeModuleSerializer::EncodeBuiltinOrStub(Address address) {
|
||||
auto builtin_iter = builtin_lookup_.find(address);
|
||||
uint32_t tag = 0;
|
||||
if (builtin_iter != builtin_lookup_.end()) {
|
||||
uint32_t id = builtin_iter->second;
|
||||
DCHECK_LT(id, std::numeric_limits<uint16_t>::max());
|
||||
tag = id << 16;
|
||||
} else {
|
||||
auto stub_iter = stub_lookup_.find(address);
|
||||
DCHECK(stub_iter != stub_lookup_.end());
|
||||
uint32_t id = stub_iter->second;
|
||||
DCHECK_LT(id, std::numeric_limits<uint16_t>::max());
|
||||
tag = id & 0x0000ffff;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
size_t NativeModuleSerializer::Write(Vector<byte> dest) {
|
||||
Vector<byte> original = dest;
|
||||
while (dest.size() > 0) {
|
||||
switch (state_) {
|
||||
case Metadata: {
|
||||
dest = dest + DrainBuffer(dest);
|
||||
if (remaining_.size() == 0) {
|
||||
BufferCopiedStubs();
|
||||
state_ = Stubs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Stubs: {
|
||||
dest = dest + DrainBuffer(dest);
|
||||
if (remaining_.size() == 0) {
|
||||
index_ = native_module_->num_imported_functions();
|
||||
BufferCurrentWasmCode();
|
||||
state_ = CodeSection;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CodeSection: {
|
||||
dest = dest + DrainBuffer(dest);
|
||||
if (remaining_.size() == 0) {
|
||||
if (++index_ < native_module_->FunctionCount()) {
|
||||
BufferCurrentWasmCode();
|
||||
} else {
|
||||
state_ = Done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
DCHECK_GE(original.size(), dest.size());
|
||||
return original.size() - dest.size();
|
||||
}
|
||||
|
||||
// static
|
||||
std::pair<std::unique_ptr<byte[]>, size_t>
|
||||
NativeModuleSerializer::SerializeWholeModule(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
|
||||
NativeModule* native_module = compiled_module->GetNativeModule();
|
||||
NativeModuleSerializer serializer(isolate, native_module);
|
||||
size_t version_size = WasmSerializedFormatVersion::GetVersionSize();
|
||||
size_t buff_size = serializer.Measure() + version_size;
|
||||
std::unique_ptr<byte[]> ret(new byte[buff_size]);
|
||||
if (!WasmSerializedFormatVersion::WriteVersion(isolate,
|
||||
{ret.get(), buff_size})) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t written =
|
||||
serializer.Write({ret.get() + version_size, buff_size - version_size});
|
||||
if (written != buff_size - version_size) return {};
|
||||
|
||||
return {std::move(ret), buff_size};
|
||||
}
|
||||
|
||||
NativeModuleDeserializer::NativeModuleDeserializer(Isolate* isolate,
|
||||
NativeModule* native_module)
|
||||
: isolate_(isolate), native_module_(native_module) {}
|
||||
|
||||
void NativeModuleDeserializer::Expect(size_t size) {
|
||||
scratch_.resize(size);
|
||||
current_expectation_ = size;
|
||||
unread_ = {scratch_.data(), size};
|
||||
}
|
||||
|
||||
bool NativeModuleDeserializer::Read(Vector<const byte> data) {
|
||||
unread_ = data;
|
||||
if (!ReadHeader()) return false;
|
||||
if (!ReadStubs()) return false;
|
||||
index_ = native_module_->num_imported_functions();
|
||||
for (; index_ < native_module_->FunctionCount(); ++index_) {
|
||||
if (!ReadCode()) return false;
|
||||
}
|
||||
native_module_->LinkAll();
|
||||
return data.size() - unread_.size();
|
||||
}
|
||||
|
||||
bool NativeModuleDeserializer::ReadHeader() {
|
||||
size_t start_size = unread_.size();
|
||||
Reader reader(unread_);
|
||||
size_t functions = reader.Read<uint32_t>();
|
||||
size_t imports = reader.Read<uint32_t>();
|
||||
bool ok = functions == native_module_->FunctionCount() &&
|
||||
imports == native_module_->num_imported_functions();
|
||||
if (!ok) return false;
|
||||
size_t table_count = reader.Read<uint32_t>();
|
||||
|
||||
std::vector<GlobalHandleAddress> sigs(table_count);
|
||||
std::vector<GlobalHandleAddress> funcs(table_count);
|
||||
for (size_t i = 0; i < table_count; ++i) {
|
||||
funcs[i] = reader.Read<GlobalHandleAddress>();
|
||||
sigs[i] = reader.Read<GlobalHandleAddress>();
|
||||
}
|
||||
native_module_->signature_tables() = sigs;
|
||||
native_module_->function_tables() = funcs;
|
||||
// resize, so that from here on the native module can be
|
||||
// asked about num_function_tables().
|
||||
native_module_->empty_function_tables().resize(table_count);
|
||||
native_module_->empty_signature_tables().resize(table_count);
|
||||
|
||||
unread_ = unread_ + (start_size - reader.current_buffer().size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeModuleDeserializer::ReadStubs() {
|
||||
size_t start_size = unread_.size();
|
||||
Reader reader(unread_);
|
||||
size_t nr_stubs = reader.Read<uint32_t>();
|
||||
stubs_.reserve(nr_stubs);
|
||||
for (size_t i = 0; i < nr_stubs; ++i) {
|
||||
uint32_t key = reader.Read<uint32_t>();
|
||||
v8::internal::Code* stub =
|
||||
*(v8::internal::CodeStub::GetCode(isolate_, key).ToHandleChecked());
|
||||
stubs_.push_back(native_module_->GetLocalAddressFor(handle(stub)));
|
||||
}
|
||||
unread_ = unread_ + (start_size - reader.current_buffer().size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeModuleDeserializer::ReadCode() {
|
||||
size_t start_size = unread_.size();
|
||||
Reader reader(unread_);
|
||||
size_t code_section_size = reader.Read<size_t>();
|
||||
USE(code_section_size);
|
||||
size_t constant_pool_offset = reader.Read<size_t>();
|
||||
size_t safepoint_table_offset = reader.Read<size_t>();
|
||||
uint32_t stack_slot_count = reader.Read<uint32_t>();
|
||||
size_t code_size = reader.Read<size_t>();
|
||||
size_t reloc_size = reader.Read<size_t>();
|
||||
uint32_t handler_size = reader.Read<uint32_t>();
|
||||
uint32_t source_position_size = reader.Read<uint32_t>();
|
||||
size_t protected_instructions_size = reader.Read<size_t>();
|
||||
bool is_liftoff = reader.Read<bool>();
|
||||
std::shared_ptr<ProtectedInstructions> protected_instructions(
|
||||
new ProtectedInstructions(protected_instructions_size));
|
||||
DCHECK_EQ(protected_instructions_size, protected_instructions->size());
|
||||
|
||||
Vector<const byte> code_buffer = reader.GetSubvector(code_size);
|
||||
std::unique_ptr<byte[]> reloc_info;
|
||||
if (reloc_size > 0) {
|
||||
reloc_info.reset(new byte[reloc_size]);
|
||||
reader.ReadIntoVector({reloc_info.get(), reloc_size});
|
||||
}
|
||||
WasmCode* ret = native_module_->AddOwnedCode(
|
||||
code_buffer, std::move(reloc_info), reloc_size, Just(index_),
|
||||
WasmCode::Function, constant_pool_offset, stack_slot_count,
|
||||
safepoint_table_offset, protected_instructions, is_liftoff);
|
||||
if (ret == nullptr) return false;
|
||||
native_module_->SetCodeTable(index_, ret);
|
||||
|
||||
// now relocate the code
|
||||
int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
|
||||
RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
|
||||
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
|
||||
for (RelocIterator iter(ret->instructions(), ret->reloc_info(),
|
||||
ret->constant_pool(), mask);
|
||||
!iter.done(); iter.next()) {
|
||||
RelocInfo::Mode mode = iter.rinfo()->rmode();
|
||||
switch (mode) {
|
||||
case RelocInfo::EMBEDDED_OBJECT: {
|
||||
// We only expect {undefined}. We check for that when we add code.
|
||||
iter.rinfo()->set_target_object(isolate_->heap()->undefined_value(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
}
|
||||
case RelocInfo::CODE_TARGET: {
|
||||
uint32_t tag = *(reinterpret_cast<uint32_t*>(
|
||||
iter.rinfo()->target_address_address()));
|
||||
Address target = GetTrampolineOrStubFromTag(tag);
|
||||
iter.rinfo()->set_target_address(nullptr, target, SKIP_WRITE_BARRIER,
|
||||
SKIP_ICACHE_FLUSH);
|
||||
} break;
|
||||
case RelocInfo::RUNTIME_ENTRY: {
|
||||
uint32_t orig_target = static_cast<uint32_t>(
|
||||
reinterpret_cast<intptr_t>(iter.rinfo()->target_address()));
|
||||
Address address =
|
||||
ExternalReferenceTable::instance(isolate_)->address(orig_target);
|
||||
iter.rinfo()->set_target_runtime_entry(
|
||||
nullptr, address, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handler_size > 0) {
|
||||
Handle<FixedArray> handler_table = isolate_->factory()->NewFixedArray(
|
||||
static_cast<int>(handler_size), TENURED);
|
||||
reader.ReadIntoVector(
|
||||
{reinterpret_cast<Address>(handler_table->GetFirstElementAddress()),
|
||||
handler_size});
|
||||
native_module_->compiled_module()->handler_table()->set(
|
||||
static_cast<int>(index_), *handler_table);
|
||||
}
|
||||
if (source_position_size > 0) {
|
||||
Handle<ByteArray> source_positions = isolate_->factory()->NewByteArray(
|
||||
static_cast<int>(source_position_size), TENURED);
|
||||
reader.ReadIntoVector(
|
||||
{source_positions->GetDataStartAddress(), source_position_size});
|
||||
native_module_->compiled_module()->source_positions()->set(
|
||||
static_cast<int>(index_), *source_positions);
|
||||
}
|
||||
if (protected_instructions_size > 0) {
|
||||
reader.ReadIntoVector(
|
||||
{reinterpret_cast<byte*>(protected_instructions->data()),
|
||||
sizeof(trap_handler::ProtectedInstructionData) *
|
||||
protected_instructions->size()});
|
||||
}
|
||||
unread_ = unread_ + (start_size - reader.current_buffer().size());
|
||||
return true;
|
||||
}
|
||||
|
||||
Address NativeModuleDeserializer::GetTrampolineOrStubFromTag(uint32_t tag) {
|
||||
if ((tag & 0x0000ffff) == 0) {
|
||||
int builtin_id = static_cast<int>(tag >> 16);
|
||||
v8::internal::Code* builtin = isolate_->builtins()->builtin(builtin_id);
|
||||
return native_module_->GetLocalAddressFor(handle(builtin));
|
||||
} else {
|
||||
DCHECK_EQ(tag & 0xffff0000, 0);
|
||||
return stubs_[tag];
|
||||
}
|
||||
}
|
||||
|
||||
MaybeHandle<WasmCompiledModule> NativeModuleDeserializer::DeserializeFullBuffer(
|
||||
Isolate* isolate, Vector<const byte> data, Vector<const byte> wire_bytes) {
|
||||
if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) {
|
||||
return {};
|
||||
}
|
||||
if (!WasmSerializedFormatVersion::IsSupportedVersion(isolate, data)) {
|
||||
return {};
|
||||
}
|
||||
data = data + WasmSerializedFormatVersion::GetVersionSize();
|
||||
ModuleResult decode_result =
|
||||
SyncDecodeWasmModule(isolate, wire_bytes.start(), wire_bytes.end(), false,
|
||||
i::wasm::kWasmOrigin);
|
||||
if (!decode_result.ok()) return {};
|
||||
CHECK_NOT_NULL(decode_result.val);
|
||||
Handle<String> module_bytes =
|
||||
isolate->factory()
|
||||
->NewStringFromOneByte(
|
||||
{wire_bytes.start(), static_cast<size_t>(wire_bytes.length())},
|
||||
TENURED)
|
||||
.ToHandleChecked();
|
||||
DCHECK(module_bytes->IsSeqOneByteString());
|
||||
// The {module_wrapper} will take ownership of the {WasmModule} object,
|
||||
// and it will be destroyed when the GC reclaims the wrapper object.
|
||||
Handle<WasmModuleWrapper> module_wrapper =
|
||||
WasmModuleWrapper::From(isolate, decode_result.val.release());
|
||||
Handle<Script> script = CreateWasmScript(isolate, wire_bytes);
|
||||
Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New(
|
||||
isolate, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes),
|
||||
script, Handle<ByteArray>::null());
|
||||
int export_wrappers_size =
|
||||
static_cast<int>(shared->module()->num_exported_functions);
|
||||
Handle<FixedArray> export_wrappers = isolate->factory()->NewFixedArray(
|
||||
static_cast<int>(export_wrappers_size), TENURED);
|
||||
|
||||
Handle<WasmCompiledModule> compiled_module = WasmCompiledModule::New(
|
||||
isolate, shared->module(), isolate->factory()->NewFixedArray(0, TENURED),
|
||||
export_wrappers, {}, {});
|
||||
compiled_module->OnWasmModuleDecodingComplete(shared);
|
||||
NativeModuleDeserializer deserializer(isolate,
|
||||
compiled_module->GetNativeModule());
|
||||
if (!deserializer.Read(data)) return {};
|
||||
|
||||
CompileJsToWasmWrappers(isolate, compiled_module, isolate->counters());
|
||||
WasmCompiledModule::ReinitializeAfterDeserialization(isolate,
|
||||
compiled_module);
|
||||
return compiled_module;
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
96
src/wasm/wasm-serialization.h
Normal file
96
src/wasm/wasm-serialization.h
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
#ifndef V8_WASM_SERIALIZATION_H_
|
||||
#define V8_WASM_SERIALIZATION_H_
|
||||
|
||||
#include "src/wasm/wasm-heap.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
class WasmSerializedFormatVersion {
|
||||
public:
|
||||
static size_t GetVersionSize();
|
||||
static bool WriteVersion(Isolate* isolate, Vector<byte>);
|
||||
static bool IsSupportedVersion(Isolate* isolate, const Vector<const byte>);
|
||||
|
||||
private:
|
||||
static constexpr size_t kVersionSize = 4 * sizeof(uint32_t);
|
||||
};
|
||||
|
||||
enum SerializationSection { Init, Metadata, Stubs, CodeSection, Done };
|
||||
|
||||
class V8_EXPORT_PRIVATE NativeModuleSerializer {
|
||||
public:
|
||||
explicit NativeModuleSerializer(Isolate*, const NativeModule*);
|
||||
size_t Measure() const;
|
||||
size_t Write(Vector<byte>);
|
||||
bool IsDone() const { return state_ == Done; }
|
||||
static std::pair<std::unique_ptr<byte[]>, size_t> SerializeWholeModule(
|
||||
Isolate*, Handle<WasmCompiledModule>);
|
||||
|
||||
private:
|
||||
size_t MeasureHeader() const;
|
||||
static size_t GetCodeHeaderSize();
|
||||
size_t MeasureCode(const WasmCode*) const;
|
||||
size_t MeasureCopiedStubs() const;
|
||||
FixedArray* GetHandlerTable(const WasmCode*) const;
|
||||
ByteArray* GetSourcePositions(const WasmCode*) const;
|
||||
|
||||
void BufferHeader();
|
||||
// we buffer all the stubs because they are small
|
||||
void BufferCopiedStubs();
|
||||
void BufferCodeInAllocatedScratch(const WasmCode*);
|
||||
void BufferCurrentWasmCode();
|
||||
size_t DrainBuffer(Vector<byte> dest);
|
||||
uint32_t EncodeBuiltinOrStub(Address);
|
||||
|
||||
Isolate* const isolate_ = nullptr;
|
||||
const NativeModule* const native_module_ = nullptr;
|
||||
SerializationSection state_ = Init;
|
||||
uint32_t index_ = 0;
|
||||
std::vector<byte> scratch_;
|
||||
Vector<byte> remaining_;
|
||||
// wasm and copied stubs reverse lookup
|
||||
std::map<Address, uint32_t> wasm_targets_lookup_;
|
||||
// immovable builtins and runtime entries lookup
|
||||
std::map<Address, uint32_t> reference_table_lookup_;
|
||||
std::map<Address, uint32_t> stub_lookup_;
|
||||
std::map<Address, uint32_t> builtin_lookup_;
|
||||
};
|
||||
|
||||
class V8_EXPORT_PRIVATE NativeModuleDeserializer {
|
||||
public:
|
||||
explicit NativeModuleDeserializer(Isolate*, NativeModule*);
|
||||
// Currently, we don't support streamed reading, yet albeit the
|
||||
// API suggests that.
|
||||
bool Read(Vector<const byte>);
|
||||
static MaybeHandle<WasmCompiledModule> DeserializeFullBuffer(
|
||||
Isolate*, Vector<const byte> data, Vector<const byte> wire_bytes);
|
||||
|
||||
private:
|
||||
void ExpectHeader();
|
||||
void Expect(size_t size);
|
||||
bool ReadHeader();
|
||||
bool ReadCode();
|
||||
bool ReadStubs();
|
||||
Address GetTrampolineOrStubFromTag(uint32_t);
|
||||
|
||||
Isolate* const isolate_ = nullptr;
|
||||
NativeModule* const native_module_ = nullptr;
|
||||
std::vector<byte> scratch_;
|
||||
std::vector<Address> stubs_;
|
||||
Vector<const byte> unread_;
|
||||
size_t current_expectation_ = 0;
|
||||
uint32_t index_ = 0;
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif
|
@ -62,7 +62,12 @@ class CWasmEntryArgTester {
|
||||
Handle<Object> buffer_obj(reinterpret_cast<Object*>(arg_buffer.data()),
|
||||
isolate_);
|
||||
CHECK(!buffer_obj->IsHeapObject());
|
||||
Handle<Object> call_args[]{wasm_code_, buffer_obj};
|
||||
Handle<Object> call_args[]{
|
||||
(FLAG_wasm_jit_to_native
|
||||
? Handle<Object>::cast(isolate_->factory()->NewForeign(
|
||||
wasm_code_.GetWasmCode()->instructions().start(), TENURED))
|
||||
: Handle<Object>::cast(wasm_code_.GetCode())),
|
||||
buffer_obj};
|
||||
static_assert(
|
||||
arraysize(call_args) == compiler::CWasmEntryParameters::kNumParameters,
|
||||
"adapt this test");
|
||||
@ -88,7 +93,7 @@ class CWasmEntryArgTester {
|
||||
std::function<ReturnType(Args...)> expected_fn_;
|
||||
FunctionSig* sig_;
|
||||
Handle<JSFunction> c_wasm_entry_fn_;
|
||||
Handle<Code> wasm_code_;
|
||||
WasmCodeWrapper wasm_code_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -256,7 +256,7 @@ class WasmSerializationTest {
|
||||
uint32_t* slot = reinterpret_cast<uint32_t*>(
|
||||
const_cast<uint8_t*>(serialized_bytes_.first) +
|
||||
SerializedCodeData::kPayloadLengthOffset);
|
||||
*slot = 0xfefefefeu;
|
||||
*slot = FLAG_wasm_jit_to_native ? 0u : 0xfefefefeu;
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::WasmCompiledModule> Deserialize() {
|
||||
|
@ -47,6 +47,7 @@ class ArgPassingHelper {
|
||||
runner.Build(outer_code.data(), outer_code.data() + outer_code.size());
|
||||
|
||||
int funcs_to_redict[] = {static_cast<int>(inner_compiler.function_index())};
|
||||
runner.builder().Link();
|
||||
WasmDebugInfo::RedirectToInterpreter(debug_info_,
|
||||
ArrayVector(funcs_to_redict));
|
||||
main_fun_wrapper_ = runner.builder().WrapCode(runner.function_index());
|
||||
|
@ -66,14 +66,16 @@ byte* TestingModuleBuilder::AddMemory(uint32_t size) {
|
||||
return mem_start_;
|
||||
}
|
||||
|
||||
uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, Handle<Code> code,
|
||||
const char* name) {
|
||||
uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name) {
|
||||
if (test_module_.functions.size() == 0) {
|
||||
// TODO(titzer): Reserving space here to avoid the underlying WasmFunction
|
||||
// structs from moving.
|
||||
test_module_.functions.reserve(kMaxFunctions);
|
||||
}
|
||||
uint32_t index = static_cast<uint32_t>(test_module_.functions.size());
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
native_module_->ResizeCodeTableForTest(index);
|
||||
}
|
||||
test_module_.functions.push_back(
|
||||
{sig, index, 0, {0, 0}, {0, 0}, false, false});
|
||||
if (name) {
|
||||
@ -81,7 +83,7 @@ uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, Handle<Code> code,
|
||||
test_module_.functions.back().name = {
|
||||
AddBytes(name_vec), static_cast<uint32_t>(name_vec.length())};
|
||||
}
|
||||
function_code_.push_back(code);
|
||||
function_code_.push_back(Handle<Code>::null());
|
||||
if (interpreter_) {
|
||||
interpreter_->AddFunctionForTesting(&test_module_.functions.back());
|
||||
}
|
||||
@ -93,19 +95,30 @@ uint32_t TestingModuleBuilder::AddJsFunction(
|
||||
FunctionSig* sig, const char* source, Handle<FixedArray> js_imports_table) {
|
||||
Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
|
||||
*v8::Local<v8::Function>::Cast(CompileRun(source))));
|
||||
uint32_t index = AddFunction(sig, Handle<Code>::null(), nullptr);
|
||||
uint32_t index = AddFunction(sig, nullptr);
|
||||
js_imports_table->set(0, *isolate_->native_context());
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
|
||||
Handle<Code> code = compiler::CompileWasmToJSWrapper(
|
||||
isolate_, jsfunc, sig, index, test_module_.origin(), js_imports_table);
|
||||
function_code_[index] = code;
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
native_module_->ResizeCodeTableForTest(index);
|
||||
Handle<Code> wrapper = compiler::CompileWasmToJSWrapper(
|
||||
isolate_, jsfunc, sig, index, test_module_.origin(), js_imports_table);
|
||||
native_module_->AddCodeCopy(wrapper, wasm::WasmCode::WasmToJsWrapper,
|
||||
index);
|
||||
} else {
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
|
||||
Handle<Code> code = compiler::CompileWasmToJSWrapper(
|
||||
isolate_, jsfunc, sig, index, test_module_.origin(), js_imports_table);
|
||||
function_code_[index] = code;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
|
||||
// Wrap the code so it can be called as a JS function.
|
||||
Handle<Code> code = function_code_[index];
|
||||
Link();
|
||||
WasmCodeWrapper code = FLAG_wasm_jit_to_native
|
||||
? WasmCodeWrapper(native_module_->GetCode(index))
|
||||
: WasmCodeWrapper(function_code_[index]);
|
||||
byte* context_address =
|
||||
test_module_.has_memory
|
||||
? reinterpret_cast<byte*>(instance_object_->wasm_context())
|
||||
@ -167,7 +180,16 @@ void TestingModuleBuilder::PopulateIndirectFunctionTable() {
|
||||
WasmFunction& function = test_module_.functions[table.values[j]];
|
||||
signature_table->set(
|
||||
j, Smi::FromInt(test_module_.signature_map.Find(function.sig)));
|
||||
function_table->set(j, *function_code_[function.func_index]);
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
Handle<Foreign> foreign_holder = isolate_->factory()->NewForeign(
|
||||
native_module_->GetCode(function.func_index)
|
||||
->instructions()
|
||||
.start(),
|
||||
TENURED);
|
||||
function_table->set(j, *foreign_holder);
|
||||
} else {
|
||||
function_table->set(j, *function_code_[function.func_index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,14 +243,22 @@ Handle<WasmInstanceObject> TestingModuleBuilder::InitInstanceObject() {
|
||||
Handle<FixedArray> code_table = isolate_->factory()->NewFixedArray(0);
|
||||
Handle<FixedArray> export_wrappers = isolate_->factory()->NewFixedArray(0);
|
||||
Handle<WasmCompiledModule> compiled_module = WasmCompiledModule::New(
|
||||
isolate_, shared_module_data, code_table, export_wrappers,
|
||||
function_tables_, signature_tables_);
|
||||
isolate_, test_module_ptr_, code_table, export_wrappers, function_tables_,
|
||||
signature_tables_);
|
||||
compiled_module->OnWasmModuleDecodingComplete(shared_module_data);
|
||||
// This method is called when we initialize TestEnvironment. We don't
|
||||
// have a memory yet, so we won't create it here. We'll update the
|
||||
// interpreter when we get a memory. We do have globals, though.
|
||||
native_module_ = compiled_module->GetNativeModule();
|
||||
|
||||
Handle<FixedArray> weak_exported = isolate_->factory()->NewFixedArray(0);
|
||||
compiled_module->set_weak_exported_functions(weak_exported);
|
||||
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
|
||||
script->set_wasm_compiled_module(*compiled_module);
|
||||
auto instance = WasmInstanceObject::New(isolate_, compiled_module);
|
||||
instance->wasm_context()->get()->globals_start = globals_data_;
|
||||
Handle<WeakCell> weak_instance = isolate()->factory()->NewWeakCell(instance);
|
||||
compiled_module->set_weak_owning_instance(weak_instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ -395,6 +425,10 @@ void WasmFunctionCompiler::Build(const byte* start, const byte* end) {
|
||||
|
||||
Handle<WasmCompiledModule> compiled_module(
|
||||
builder_->instance_object()->compiled_module(), isolate());
|
||||
NativeModule* native_module = compiled_module->GetNativeModule();
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
native_module->ResizeCodeTableForTest(function_->func_index);
|
||||
}
|
||||
Handle<SeqOneByteString> wire_bytes(compiled_module->module_bytes(),
|
||||
isolate());
|
||||
|
||||
@ -415,46 +449,52 @@ void WasmFunctionCompiler::Build(const byte* start, const byte* end) {
|
||||
? compiler::WasmCompilationUnit::CompilationMode::kLiftoff
|
||||
: compiler::WasmCompilationUnit::CompilationMode::kTurbofan;
|
||||
compiler::WasmCompilationUnit unit(
|
||||
isolate(), &module_env, func_body, func_name, function_->func_index,
|
||||
CEntryStub(isolate(), 1).GetCode(), comp_mode, isolate()->counters(),
|
||||
builder_->runtime_exception_support(), builder_->lower_simd());
|
||||
isolate(), &module_env, native_module, func_body, func_name,
|
||||
function_->func_index, CEntryStub(isolate(), 1).GetCode(), comp_mode,
|
||||
isolate()->counters(), builder_->runtime_exception_support(),
|
||||
builder_->lower_simd());
|
||||
unit.ExecuteCompilation();
|
||||
MaybeHandle<Code> maybe_code = unit.FinishCompilation(&thrower);
|
||||
WasmCodeWrapper code_wrapper = unit.FinishCompilation(&thrower);
|
||||
CHECK(!thrower.error());
|
||||
Handle<Code> code = maybe_code.ToHandleChecked();
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
Handle<Code> code = code_wrapper.GetCode();
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate()->heap());
|
||||
|
||||
// TODO(6792): No longer needed once WebAssembly code is off heap.
|
||||
CodeSpaceMemoryModificationScope modification_scope(isolate()->heap());
|
||||
// Manually add the deoptimization info that would otherwise be added
|
||||
// during instantiation. Deopt data holds <WeakCell<wasm_instance>,
|
||||
// func_index>.
|
||||
DCHECK_EQ(0, code->deoptimization_data()->length());
|
||||
Handle<FixedArray> deopt_data =
|
||||
isolate()->factory()->NewFixedArray(2, TENURED);
|
||||
Handle<Object> weak_instance =
|
||||
isolate()->factory()->NewWeakCell(builder_->instance_object());
|
||||
deopt_data->set(0, *weak_instance);
|
||||
deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index())));
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
|
||||
// Manually add the deoptimization info that would otherwise be added
|
||||
// during instantiation. Deopt data holds <WeakCell<wasm_instance>,
|
||||
// func_index>.
|
||||
DCHECK_EQ(0, code->deoptimization_data()->length());
|
||||
Handle<FixedArray> deopt_data =
|
||||
isolate()->factory()->NewFixedArray(2, TENURED);
|
||||
Handle<Object> weak_instance =
|
||||
isolate()->factory()->NewWeakCell(builder_->instance_object());
|
||||
deopt_data->set(0, *weak_instance);
|
||||
deopt_data->set(1, Smi::FromInt(static_cast<int>(function_index())));
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
// Build the TurboFan graph.
|
||||
builder_->SetFunctionCode(function_index(), code);
|
||||
|
||||
// Build the TurboFan graph.
|
||||
builder_->SetFunctionCode(function_index(), code);
|
||||
|
||||
// Add to code table.
|
||||
Handle<FixedArray> code_table = compiled_module->code_table();
|
||||
if (static_cast<int>(function_index()) >= code_table->length()) {
|
||||
Handle<FixedArray> new_arr = isolate()->factory()->NewFixedArray(
|
||||
static_cast<int>(function_index()) + 1);
|
||||
code_table->CopyTo(0, *new_arr, 0, code_table->length());
|
||||
code_table = new_arr;
|
||||
compiled_module->ReplaceCodeTableForTesting(code_table);
|
||||
}
|
||||
DCHECK(code_table->get(static_cast<int>(function_index()))
|
||||
->IsUndefined(isolate()));
|
||||
code_table->set(static_cast<int>(function_index()), *code);
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
UnpackAndRegisterProtectedInstructions(isolate(), code_table);
|
||||
// Add to code table.
|
||||
Handle<FixedArray> code_table = compiled_module->code_table();
|
||||
if (static_cast<int>(function_index()) >= code_table->length()) {
|
||||
Handle<FixedArray> new_arr = isolate()->factory()->NewFixedArray(
|
||||
static_cast<int>(function_index()) + 1);
|
||||
code_table->CopyTo(0, *new_arr, 0, code_table->length());
|
||||
code_table = new_arr;
|
||||
compiled_module->ReplaceCodeTableForTesting(code_table);
|
||||
}
|
||||
DCHECK(code_table->get(static_cast<int>(function_index()))
|
||||
->IsUndefined(isolate()));
|
||||
code_table->set(static_cast<int>(function_index()), *code);
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
UnpackAndRegisterProtectedInstructionsGC(isolate(), code_table);
|
||||
}
|
||||
} else {
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
UnpackAndRegisterProtectedInstructions(isolate(), native_module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -471,17 +511,20 @@ WasmFunctionCompiler::WasmFunctionCompiler(Zone* zone, FunctionSig* sig,
|
||||
source_position_table_(this->graph()),
|
||||
interpreter_(builder->interpreter()) {
|
||||
// Get a new function from the testing module.
|
||||
int index = builder->AddFunction(sig, Handle<Code>::null(), name);
|
||||
int index = builder->AddFunction(sig, name);
|
||||
function_ = builder_->GetFunctionAt(index);
|
||||
}
|
||||
|
||||
WasmFunctionCompiler::~WasmFunctionCompiler() {
|
||||
if (trap_handler::UseTrapHandler() &&
|
||||
!builder_->GetFunctionCode(function_index()).is_null()) {
|
||||
const int handler_index = builder_->GetFunctionCode(function_index())
|
||||
->trap_handler_index()
|
||||
->value();
|
||||
trap_handler::ReleaseHandlerData(handler_index);
|
||||
if (!FLAG_wasm_jit_to_native) {
|
||||
if (trap_handler::UseTrapHandler() &&
|
||||
!builder_->GetFunctionCode(function_index()).is_null()) {
|
||||
const int handler_index = builder_->GetFunctionCode(function_index())
|
||||
.GetCode()
|
||||
->trap_handler_index()
|
||||
->value();
|
||||
trap_handler::ReleaseHandlerData(handler_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ using compiler::Node;
|
||||
r.Build(code, code + arraysize(code)); \
|
||||
} while (false)
|
||||
|
||||
// A buildable ModuleEnv. Globals are pre-set, however, memory and code may be
|
||||
// A Wasm module builder. Globals are pre-set, however, memory and code may be
|
||||
// progressively added by a test. In turn, we piecemeal update the runtime
|
||||
// objects, i.e. {WasmInstanceObject}, {WasmCompiledModule} and, if necessary,
|
||||
// the interpreter.
|
||||
@ -90,7 +90,13 @@ class TestingModuleBuilder {
|
||||
|
||||
byte* AddMemory(uint32_t size);
|
||||
|
||||
size_t CodeTableLength() const { return function_code_.size(); }
|
||||
size_t CodeTableLength() const {
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
return native_module_->FunctionCount();
|
||||
} else {
|
||||
return function_code_.size();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* AddMemoryElems(uint32_t count) {
|
||||
@ -173,7 +179,7 @@ class TestingModuleBuilder {
|
||||
|
||||
void SetHasSharedMemory() { test_module_.has_shared_memory = true; }
|
||||
|
||||
uint32_t AddFunction(FunctionSig* sig, Handle<Code> code, const char* name);
|
||||
uint32_t AddFunction(FunctionSig* sig, const char* name);
|
||||
|
||||
uint32_t AddJsFunction(FunctionSig* sig, const char* source,
|
||||
Handle<FixedArray> js_imports_table);
|
||||
@ -200,11 +206,24 @@ class TestingModuleBuilder {
|
||||
bool lower_simd() { return lower_simd_; }
|
||||
Isolate* isolate() { return isolate_; }
|
||||
Handle<WasmInstanceObject> instance_object() { return instance_object_; }
|
||||
Handle<Code> GetFunctionCode(int index) { return function_code_[index]; }
|
||||
WasmCodeWrapper GetFunctionCode(uint32_t index) {
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
return WasmCodeWrapper(native_module_->GetCode(index));
|
||||
} else {
|
||||
return WasmCodeWrapper(function_code_[index]);
|
||||
}
|
||||
}
|
||||
void SetFunctionCode(int index, Handle<Code> code) {
|
||||
function_code_[index] = code;
|
||||
}
|
||||
Address globals_start() { return reinterpret_cast<Address>(globals_data_); }
|
||||
void Link() {
|
||||
if (!FLAG_wasm_jit_to_native) return;
|
||||
if (!linked_) {
|
||||
native_module_->LinkAll();
|
||||
linked_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
compiler::ModuleEnv CreateModuleEnv();
|
||||
|
||||
@ -228,6 +247,8 @@ class TestingModuleBuilder {
|
||||
WasmInterpreter* interpreter_;
|
||||
WasmExecutionMode execution_mode_;
|
||||
Handle<WasmInstanceObject> instance_object_;
|
||||
NativeModule* native_module_;
|
||||
bool linked_ = false;
|
||||
compiler::RuntimeExceptionSupport runtime_exception_support_;
|
||||
bool lower_simd_;
|
||||
|
||||
@ -258,9 +279,21 @@ class WasmFunctionWrapper : private compiler::GraphAndBuilders {
|
||||
Init(descriptor, MachineTypeForC<ReturnType>(), param_vec);
|
||||
}
|
||||
|
||||
void SetInnerCode(Handle<Code> code_handle) {
|
||||
compiler::NodeProperties::ChangeOp(inner_code_node_,
|
||||
common()->HeapConstant(code_handle));
|
||||
void SetInnerCode(WasmCodeWrapper code) {
|
||||
if (FLAG_wasm_jit_to_native) {
|
||||
intptr_t address = reinterpret_cast<intptr_t>(
|
||||
code.GetWasmCode()->instructions().start());
|
||||
compiler::NodeProperties::ChangeOp(
|
||||
inner_code_node_,
|
||||
kPointerSize == 8
|
||||
? common()->RelocatableInt64Constant(address,
|
||||
RelocInfo::WASM_CALL)
|
||||
: common()->RelocatableInt32Constant(static_cast<int>(address),
|
||||
RelocInfo::WASM_CALL));
|
||||
} else {
|
||||
compiler::NodeProperties::ChangeOp(
|
||||
inner_code_node_, common()->HeapConstant(code.GetCode()));
|
||||
}
|
||||
}
|
||||
|
||||
const compiler::Operator* IntPtrConstant(intptr_t value) {
|
||||
@ -444,6 +477,7 @@ class WasmRunner : public WasmRunnerBase {
|
||||
WasmContext* wasm_context =
|
||||
builder().instance_object()->wasm_context()->get();
|
||||
wrapper_.SetContextAddress(reinterpret_cast<uintptr_t>(wasm_context));
|
||||
builder().Link();
|
||||
Handle<Code> wrapper_code = wrapper_.GetWrapperCode();
|
||||
compiler::CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
|
||||
wrapper_code, wrapper_.signature());
|
||||
|
@ -16,7 +16,7 @@ ALL_VARIANT_FLAGS = {
|
||||
# https://chromium-review.googlesource.com/c/452620/ for more discussion.
|
||||
"nooptimization": [["--noopt"]],
|
||||
"stress_background_compile": [["--background-compile", "--stress-background-compile"]],
|
||||
"wasm_traps": [["--wasm_trap_handler", "--invoke-weak-callbacks"]],
|
||||
"wasm_traps": [["--wasm_trap_handler", "--invoke-weak-callbacks", "--wasm-jit-to-native"]],
|
||||
}
|
||||
|
||||
# FAST_VARIANTS implies no --always-opt.
|
||||
@ -33,7 +33,7 @@ FAST_VARIANT_FLAGS = {
|
||||
# https://chromium-review.googlesource.com/c/452620/ for more discussion.
|
||||
"nooptimization": [["--noopt"]],
|
||||
"stress_background_compile": [["--background-compile", "--stress-background-compile"]],
|
||||
"wasm_traps": [["--wasm_trap_handler", "--invoke-weak-callbacks"]],
|
||||
"wasm_traps": [["--wasm_trap_handler", "--invoke-weak-callbacks", "--wasm-jit-to-native"]],
|
||||
}
|
||||
|
||||
ALL_VARIANTS = set(["default", "future", "liftoff", "stress",
|
||||
|
Loading…
Reference in New Issue
Block a user