Commit c71fd20c authored by Mircea Trofin's avatar Mircea Trofin Committed by Commit Bot

[wasm] Data structures for JIT-ing wasm to native memory.

This CL introduces the structures for JIT-ing wasm on the native heap.
They are described in detail at go/wasm-on-native-heap-stage-1

Briefly:
- WasmCodeManager manages memory for modules and offers an interior
pointer lookup (i.e. PC -> WasmCode)
- WasmCode represents code, including reloc info. It holds wasm
specific data, like function index, and runtime information, like trap
handler info.
- NativeModule manages memory for one module.

Tests cover the allocation and lookup aspects, following that current
regression tests cover the JITed code. A separate CL will enable JITing
using the new data structures.

Bug: v8:6876
Change-Id: I1731238409001fe97c97eafb7a12fd3922da6a42
Reviewed-on: https://chromium-review.googlesource.com/767581
Commit-Queue: Mircea Trofin <mtrofin@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49501}
parent 8c68bc83
...@@ -312,6 +312,18 @@ void RelocInfo::set_global_handle(Isolate* isolate, Address address, ...@@ -312,6 +312,18 @@ void RelocInfo::set_global_handle(Isolate* isolate, Address address,
set_embedded_address(isolate, address, icache_flush_mode); set_embedded_address(isolate, address, icache_flush_mode);
} }
Address RelocInfo::wasm_call_address() const {
DCHECK_EQ(rmode_, WASM_CALL);
return Assembler::target_address_at(pc_, constant_pool_);
}
void RelocInfo::set_wasm_call_address(Isolate* isolate, Address address,
ICacheFlushMode icache_flush_mode) {
DCHECK_EQ(rmode_, WASM_CALL);
Assembler::set_target_address_at(isolate, pc_, constant_pool_, address,
icache_flush_mode);
}
Address RelocInfo::global_handle() const { Address RelocInfo::global_handle() const {
DCHECK_EQ(rmode_, WASM_GLOBAL_HANDLE); DCHECK_EQ(rmode_, WASM_GLOBAL_HANDLE);
return embedded_address(); return embedded_address();
...@@ -337,7 +349,7 @@ void RelocInfo::update_wasm_function_table_size_reference( ...@@ -337,7 +349,7 @@ void RelocInfo::update_wasm_function_table_size_reference(
void RelocInfo::set_target_address(Isolate* isolate, Address target, void RelocInfo::set_target_address(Isolate* isolate, Address target,
WriteBarrierMode write_barrier_mode, WriteBarrierMode write_barrier_mode,
ICacheFlushMode icache_flush_mode) { ICacheFlushMode icache_flush_mode) {
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) || IsWasmCall(rmode_));
Assembler::set_target_address_at(isolate, pc_, host_, target, Assembler::set_target_address_at(isolate, pc_, host_, target,
icache_flush_mode); icache_flush_mode);
if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != nullptr && if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != nullptr &&
...@@ -644,6 +656,8 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) { ...@@ -644,6 +656,8 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
return "wasm function table size reference"; return "wasm function table size reference";
case WASM_GLOBAL_HANDLE: case WASM_GLOBAL_HANDLE:
return "global handle"; return "global handle";
case WASM_CALL:
return "internal wasm call";
case NUMBER_OF_MODES: case NUMBER_OF_MODES:
case PC_JUMP: case PC_JUMP:
UNREACHABLE(); UNREACHABLE();
...@@ -725,6 +739,7 @@ void RelocInfo::Verify(Isolate* isolate) { ...@@ -725,6 +739,7 @@ void RelocInfo::Verify(Isolate* isolate) {
case WASM_CONTEXT_REFERENCE: case WASM_CONTEXT_REFERENCE:
case WASM_FUNCTION_TABLE_SIZE_REFERENCE: case WASM_FUNCTION_TABLE_SIZE_REFERENCE:
case WASM_GLOBAL_HANDLE: case WASM_GLOBAL_HANDLE:
case WASM_CALL:
case NONE32: case NONE32:
case NONE64: case NONE64:
break; break;
......
...@@ -366,6 +366,7 @@ class RelocInfo { ...@@ -366,6 +366,7 @@ class RelocInfo {
WASM_CONTEXT_REFERENCE, WASM_CONTEXT_REFERENCE,
WASM_FUNCTION_TABLE_SIZE_REFERENCE, WASM_FUNCTION_TABLE_SIZE_REFERENCE,
WASM_GLOBAL_HANDLE, WASM_GLOBAL_HANDLE,
WASM_CALL,
RUNTIME_ENTRY, RUNTIME_ENTRY,
COMMENT, COMMENT,
...@@ -421,6 +422,7 @@ class RelocInfo { ...@@ -421,6 +422,7 @@ class RelocInfo {
static inline bool IsRuntimeEntry(Mode mode) { static inline bool IsRuntimeEntry(Mode mode) {
return mode == RUNTIME_ENTRY; return mode == RUNTIME_ENTRY;
} }
static inline bool IsWasmCall(Mode mode) { return mode == WASM_CALL; }
// Is the relocation mode affected by GC? // Is the relocation mode affected by GC?
static inline bool IsGCRelocMode(Mode mode) { static inline bool IsGCRelocMode(Mode mode) {
return mode <= LAST_GCED_ENUM; return mode <= LAST_GCED_ENUM;
...@@ -498,6 +500,7 @@ class RelocInfo { ...@@ -498,6 +500,7 @@ class RelocInfo {
Address wasm_context_reference() const; Address wasm_context_reference() const;
uint32_t wasm_function_table_size_reference() const; uint32_t wasm_function_table_size_reference() const;
Address global_handle() const; Address global_handle() const;
Address wasm_call_address() const;
void set_wasm_context_reference( void set_wasm_context_reference(
Isolate* isolate, Address address, Isolate* isolate, Address address,
...@@ -513,6 +516,9 @@ class RelocInfo { ...@@ -513,6 +516,9 @@ class RelocInfo {
void set_global_handle( void set_global_handle(
Isolate* isolate, Address address, Isolate* isolate, Address address,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED); ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
void set_wasm_call_address(
Isolate*, Address,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// this relocation applies to; // this relocation applies to;
// can only be called if IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) // can only be called if IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
......
...@@ -476,6 +476,11 @@ DEFINE_BOOL(wasm_disable_structured_cloning, false, ...@@ -476,6 +476,11 @@ DEFINE_BOOL(wasm_disable_structured_cloning, false,
"disable wasm structured cloning") "disable wasm structured cloning")
DEFINE_INT(wasm_num_compilation_tasks, 10, DEFINE_INT(wasm_num_compilation_tasks, 10,
"number of parallel compilation tasks for wasm") "number of parallel compilation tasks for wasm")
DEFINE_BOOL(wasm_trace_native_heap, false, "trace wasm native heap events")
DEFINE_BOOL(wasm_jit_to_native, false,
"JIT wasm code to native (not JS GC) memory")
DEFINE_BOOL(wasm_trace_serialization, false,
"trace serialization/deserialization")
DEFINE_BOOL(wasm_async_compilation, true, DEFINE_BOOL(wasm_async_compilation, true,
"enable actual asynchronous compilation for WebAssembly.compile") "enable actual asynchronous compilation for WebAssembly.compile")
DEFINE_BOOL(wasm_stream_compilation, false, DEFINE_BOOL(wasm_stream_compilation, false,
......
...@@ -1456,8 +1456,8 @@ ...@@ -1456,8 +1456,8 @@
'wasm/streaming-decoder.h', 'wasm/streaming-decoder.h',
'wasm/wasm-api.cc', 'wasm/wasm-api.cc',
'wasm/wasm-api.h', 'wasm/wasm-api.h',
'wasm/wasm-code-specialization.h',
'wasm/wasm-code-specialization.cc', 'wasm/wasm-code-specialization.cc',
'wasm/wasm-code-specialization.h',
'wasm/wasm-debug.cc', 'wasm/wasm-debug.cc',
'wasm/wasm-external-refs.cc', 'wasm/wasm-external-refs.cc',
'wasm/wasm-external-refs.h', 'wasm/wasm-external-refs.h',
......
This diff is collapsed.
This diff is collapsed.
...@@ -381,22 +381,28 @@ class WasmCompiledModule : public FixedArray { ...@@ -381,22 +381,28 @@ class WasmCompiledModule : public FixedArray {
// for deserialization, and if they are serializable. // for deserialization, and if they are serializable.
// By default, instance values go to WasmInstanceObject, however, if // By default, instance values go to WasmInstanceObject, however, if
// we embed the generated code with a value, then we track that value here. // we embed the generated code with a value, then we track that value here.
#define CORE_WCM_PROPERTY_TABLE(MACRO) \ #define CORE_WCM_PROPERTY_TABLE(MACRO) \
MACRO(WASM_OBJECT, WasmSharedModuleData, shared) \ MACRO(WASM_OBJECT, WasmSharedModuleData, shared) \
MACRO(OBJECT, Context, native_context) \ MACRO(OBJECT, Context, native_context) \
MACRO(CONST_OBJECT, FixedArray, export_wrappers) \
MACRO(OBJECT, FixedArray, weak_exported_functions) \
MACRO(WASM_OBJECT, WasmCompiledModule, next_instance) \
MACRO(WASM_OBJECT, WasmCompiledModule, prev_instance) \
MACRO(WEAK_LINK, WasmInstanceObject, owning_instance) \
MACRO(WEAK_LINK, WasmModuleObject, wasm_module) \
MACRO(OBJECT, FixedArray, handler_table) \
MACRO(OBJECT, FixedArray, source_positions) \
MACRO(OBJECT, Foreign, native_module) \
MACRO(OBJECT, FixedArray, lazy_compile_data)
#define GC_WCM_PROPERTY_TABLE(MACRO) \
MACRO(SMALL_CONST_NUMBER, uint32_t, num_imported_functions) \ MACRO(SMALL_CONST_NUMBER, uint32_t, num_imported_functions) \
MACRO(CONST_OBJECT, FixedArray, code_table) \ MACRO(CONST_OBJECT, FixedArray, code_table) \
MACRO(CONST_OBJECT, FixedArray, export_wrappers) \
MACRO(OBJECT, FixedArray, weak_exported_functions) \
MACRO(OBJECT, FixedArray, function_tables) \ MACRO(OBJECT, FixedArray, function_tables) \
MACRO(OBJECT, FixedArray, signature_tables) \ MACRO(OBJECT, FixedArray, signature_tables) \
MACRO(CONST_OBJECT, FixedArray, empty_function_tables) \ MACRO(CONST_OBJECT, FixedArray, empty_function_tables) \
MACRO(CONST_OBJECT, FixedArray, empty_signature_tables) \ MACRO(CONST_OBJECT, FixedArray, empty_signature_tables) \
MACRO(SMALL_CONST_NUMBER, uint32_t, initial_pages) \ MACRO(SMALL_CONST_NUMBER, uint32_t, initial_pages)
MACRO(WASM_OBJECT, WasmCompiledModule, next_instance) \
MACRO(WASM_OBJECT, WasmCompiledModule, prev_instance) \
MACRO(WEAK_LINK, JSObject, owning_instance) \
MACRO(WEAK_LINK, WasmModuleObject, wasm_module)
#if DEBUG #if DEBUG
#define DEBUG_ONLY_TABLE(MACRO) MACRO(SMALL_CONST_NUMBER, uint32_t, instance_id) #define DEBUG_ONLY_TABLE(MACRO) MACRO(SMALL_CONST_NUMBER, uint32_t, instance_id)
...@@ -409,6 +415,7 @@ class WasmCompiledModule : public FixedArray { ...@@ -409,6 +415,7 @@ class WasmCompiledModule : public FixedArray {
#define WCM_PROPERTY_TABLE(MACRO) \ #define WCM_PROPERTY_TABLE(MACRO) \
CORE_WCM_PROPERTY_TABLE(MACRO) \ CORE_WCM_PROPERTY_TABLE(MACRO) \
GC_WCM_PROPERTY_TABLE(MACRO) \
DEBUG_ONLY_TABLE(MACRO) DEBUG_ONLY_TABLE(MACRO)
private: private:
......
...@@ -151,6 +151,241 @@ TEST_F(DisjointAllocationPoolTest, MergingSkipLargerSrcWithGap) { ...@@ -151,6 +151,241 @@ TEST_F(DisjointAllocationPoolTest, MergingSkipLargerSrcWithGap) {
CheckLooksLike(a, {{10, 15}, {20, 35}, {36, 40}}); CheckLooksLike(a, {{10, 15}, {20, 35}, {36, 40}});
} }
class WasmCodeManagerTest : public TestWithIsolate {
public:
using NativeModulePtr = std::unique_ptr<NativeModule>;
enum ModuleStyle : int { Fixed = 0, Growable = 1 };
const std::vector<ModuleStyle> styles() const {
return std::vector<ModuleStyle>({Fixed, Growable});
}
// We pretend all our modules have 10 functions and no imports, just so
// we can size up the code_table.
NativeModulePtr AllocFixedModule(WasmCodeManager* manager, size_t size) {
return manager->NewNativeModule(size, 10, 0, false);
}
NativeModulePtr AllocGrowableModule(WasmCodeManager* manager, size_t size) {
return manager->NewNativeModule(size, 10, 0, true);
}
NativeModulePtr AllocModule(WasmCodeManager* manager, size_t size,
ModuleStyle style) {
switch (style) {
case Fixed:
return AllocFixedModule(manager, size);
case Growable:
return AllocGrowableModule(manager, size);
default:
UNREACHABLE();
}
}
WasmCode* AddCode(NativeModule* native_module, uint32_t index, size_t size) {
CodeDesc desc;
memset(reinterpret_cast<void*>(&desc), 0, sizeof(CodeDesc));
std::unique_ptr<byte[]> exec_buff(new byte[size]);
desc.buffer = exec_buff.get();
desc.instr_size = static_cast<int>(size);
return native_module->AddCode(desc, 0, index, 0, {}, false);
}
size_t page() const { return base::OS::AllocatePageSize(); }
v8::Isolate* v8_isolate() const {
return reinterpret_cast<v8::Isolate*>(isolate());
}
};
TEST_F(WasmCodeManagerTest, EmptyCase) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 0 * page());
CHECK_EQ(0, manager.remaining_uncommitted());
NativeModulePtr native_module = AllocModule(&manager, 1 * page(), style);
CHECK(native_module);
WasmCode* code = AddCode(native_module.get(), 0, 10);
CHECK_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
native_module.reset();
CHECK_EQ(0, manager.remaining_uncommitted());
}
}
TEST_F(WasmCodeManagerTest, AllocateAndGoOverLimit) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 1 * page());
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
NativeModulePtr native_module = AllocModule(&manager, 1 * page(), style);
CHECK(native_module);
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
uint32_t index = 0;
WasmCode* code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, 3 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, page() - 4 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
CHECK_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
native_module.reset();
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
}
}
TEST_F(WasmCodeManagerTest, TotalLimitIrrespectiveOfModuleCount) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 1 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager, 1 * page(), style);
CHECK(nm1);
CHECK(nm2);
WasmCode* code = AddCode(nm1.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
code = AddCode(nm2.get(), 0, 1 * page());
CHECK_NULL(code);
}
}
TEST_F(WasmCodeManagerTest, DifferentHeapsApplyLimitsIndependently) {
for (auto style : styles()) {
WasmCodeManager manager1(v8_isolate(), 1 * page());
WasmCodeManager manager2(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager1, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager2, 1 * page(), style);
CHECK(nm1);
CHECK(nm2);
WasmCode* code = AddCode(nm1.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager1.remaining_uncommitted());
code = AddCode(nm2.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
}
}
TEST_F(WasmCodeManagerTest, GrowingVsFixedModule) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 3 * page());
NativeModulePtr nm = AllocModule(&manager, 1 * page(), style);
WasmCode* code = AddCode(nm.get(), 0, 1 * page() + kCodeAlignment);
if (style == Fixed) {
CHECK_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 3 * page());
} else {
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 1 * page());
}
}
}
TEST_F(WasmCodeManagerTest, CommitIncrements) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 10 * page());
NativeModulePtr nm = AllocModule(&manager, 3 * page(), style);
WasmCode* code = AddCode(nm.get(), 0, kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 9 * page());
code = AddCode(nm.get(), 1, 2 * page());
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 7 * page());
code = AddCode(nm.get(), 2, page() - kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 7 * page());
}
}
TEST_F(WasmCodeManagerTest, Lookup) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager, 1 * page(), style);
WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
CHECK_EQ(nm1.get(), code1_0->owner());
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
CHECK_EQ(nm2.get(), code2_1->owner());
CHECK_EQ(0, code1_0->index());
CHECK_EQ(1, code1_1->index());
CHECK_EQ(0, code2_0->index());
CHECK_EQ(1, code2_1->index());
// we know the manager object is allocated here, so we shouldn't
// find any WasmCode* associated with that ptr.
WasmCode* not_found =
manager.LookupCode(reinterpret_cast<Address>(&manager));
CHECK_NULL(not_found);
WasmCode* found = manager.LookupCode(code1_0->instructions().start());
CHECK_EQ(found, code1_0);
found = manager.LookupCode(code2_1->instructions().start() +
(code2_1->instructions().size() / 2));
CHECK_EQ(found, code2_1);
found = manager.LookupCode(code2_1->instructions().start() +
code2_1->instructions().size() - 1);
CHECK_EQ(found, code2_1);
found = manager.LookupCode(code2_1->instructions().start() +
code2_1->instructions().size());
CHECK_NULL(found);
Address mid_code1_1 =
code1_1->instructions().start() + (code1_1->instructions().size() / 2);
CHECK_EQ(code1_1, manager.LookupCode(mid_code1_1));
nm1.reset();
CHECK_NULL(manager.LookupCode(mid_code1_1));
}
}
TEST_F(WasmCodeManagerTest, MultiManagerLookup) {
for (auto style : styles()) {
WasmCodeManager manager1(v8_isolate(), 2 * page());
WasmCodeManager manager2(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager1, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager2, 1 * page(), style);
WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
CHECK_EQ(nm1.get(), code1_0->owner());
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
CHECK_EQ(nm2.get(), code2_1->owner());
CHECK_EQ(0, code1_0->index());
CHECK_EQ(1, code1_1->index());
CHECK_EQ(0, code2_0->index());
CHECK_EQ(1, code2_1->index());
CHECK_EQ(code1_0, manager1.LookupCode(code1_0->instructions().start()));
CHECK_NULL(manager2.LookupCode(code1_0->instructions().start()));
}
}
TEST_F(WasmCodeManagerTest, LookupWorksAfterRewrite) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), style);
WasmCode* code0 = AddCode(nm1.get(), 0, kCodeAlignment);
WasmCode* code1 = AddCode(nm1.get(), 1, kCodeAlignment);
CHECK_EQ(0, code0->index());
CHECK_EQ(1, code1->index());
CHECK_EQ(code1, manager.LookupCode(code1->instructions().start()));
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
CHECK_EQ(1, code1_1->index());
CHECK_EQ(code1, manager.LookupCode(code1->instructions().start()));
CHECK_EQ(code1_1, manager.LookupCode(code1_1->instructions().start()));
}
}
} // namespace wasm_heap_unittest } // namespace wasm_heap_unittest
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment