OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <memory> | 5 #include <memory> |
6 | 6 |
7 #include "src/assembler-inl.h" | 7 #include "src/assembler-inl.h" |
8 #include "src/base/adapters.h" | 8 #include "src/base/adapters.h" |
9 #include "src/base/atomic-utils.h" | 9 #include "src/base/atomic-utils.h" |
10 #include "src/code-stubs.h" | 10 #include "src/code-stubs.h" |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 #if V8_TARGET_ARCH_64_BIT | 69 #if V8_TARGET_ARCH_64_BIT |
70 const bool kGuardRegionsSupported = true; | 70 const bool kGuardRegionsSupported = true; |
71 #else | 71 #else |
72 const bool kGuardRegionsSupported = false; | 72 const bool kGuardRegionsSupported = false; |
73 #endif | 73 #endif |
74 | 74 |
75 bool EnableGuardRegions() { | 75 bool EnableGuardRegions() { |
76 return FLAG_wasm_guard_pages && kGuardRegionsSupported; | 76 return FLAG_wasm_guard_pages && kGuardRegionsSupported; |
77 } | 77 } |
78 | 78 |
| 79 static void RecordStats(Isolate* isolate, Code* code) { |
| 80 isolate->counters()->wasm_generated_code_size()->Increment(code->body_size()); |
| 81 isolate->counters()->wasm_reloc_size()->Increment( |
| 82 code->relocation_info()->length()); |
| 83 } |
| 84 |
| 85 static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) { |
| 86 DisallowHeapAllocation no_gc; |
| 87 for (int i = 0; i < functions->length(); ++i) { |
| 88 RecordStats(isolate, Code::cast(functions->get(i))); |
| 89 } |
| 90 } |
| 91 |
79 void* TryAllocateBackingStore(Isolate* isolate, size_t size, | 92 void* TryAllocateBackingStore(Isolate* isolate, size_t size, |
80 bool enable_guard_regions, bool& is_external) { | 93 bool enable_guard_regions, bool& is_external) { |
81 is_external = false; | 94 is_external = false; |
82 // TODO(eholk): Right now enable_guard_regions has no effect on 32-bit | 95 // TODO(eholk): Right now enable_guard_regions has no effect on 32-bit |
83 // systems. It may be safer to fail instead, given that other code might do | 96 // systems. It may be safer to fail instead, given that other code might do |
84 // things that would be unsafe if they expected guard pages where there | 97 // things that would be unsafe if they expected guard pages where there |
85 // weren't any. | 98 // weren't any. |
86 if (enable_guard_regions && kGuardRegionsSupported) { | 99 if (enable_guard_regions && kGuardRegionsSupported) { |
87 // TODO(eholk): On Windows we want to make sure we don't commit the guard | 100 // TODO(eholk): On Windows we want to make sure we don't commit the guard |
88 // pages yet. | 101 // pages yet. |
(...skipping 25 matching lines...) Expand all Loading... |
114 } | 127 } |
115 | 128 |
116 void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) { | 129 void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) { |
117 for (int i = 0; i < code_table->length(); ++i) { | 130 for (int i = 0; i < code_table->length(); ++i) { |
118 Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i); | 131 Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i); |
119 Assembler::FlushICache(isolate, code->instruction_start(), | 132 Assembler::FlushICache(isolate, code->instruction_start(), |
120 code->instruction_size()); | 133 code->instruction_size()); |
121 } | 134 } |
122 } | 135 } |
123 | 136 |
124 // Fetches the compilation unit of a wasm function and executes its parallel | 137 Handle<Script> CreateWasmScript(Isolate* isolate, |
125 // phase. | 138 const ModuleWireBytes& wire_bytes) { |
126 bool FetchAndExecuteCompilationUnit( | 139 Handle<Script> script = |
127 Isolate* isolate, | 140 isolate->factory()->NewScript(isolate->factory()->empty_string()); |
128 std::vector<compiler::WasmCompilationUnit*>* compilation_units, | 141 FixedArray* array = isolate->native_context()->embedder_data(); |
129 std::queue<compiler::WasmCompilationUnit*>* executed_units, | 142 script->set_context_data(array->get(v8::Context::kDebugIdIndex)); |
130 base::Mutex* result_mutex, base::AtomicNumber<size_t>* next_unit) { | 143 script->set_type(Script::TYPE_WASM); |
131 DisallowHeapAllocation no_allocation; | 144 |
132 DisallowHandleAllocation no_handles; | 145 int hash = StringHasher::HashSequentialString( |
133 DisallowHandleDereference no_deref; | 146 reinterpret_cast<const char*>(wire_bytes.start()), wire_bytes.length(), |
134 DisallowCodeDependencyChange no_dependency_change; | 147 kZeroHashSeed); |
135 | 148 |
136 // - 1 because AtomicIncrement returns the value after the atomic increment. | 149 const int kBufferSize = 32; |
137 size_t index = next_unit->Increment(1) - 1; | 150 char buffer[kBufferSize]; |
138 if (index >= compilation_units->size()) { | 151 int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash); |
139 return false; | 152 DCHECK(url_chars >= 0 && url_chars < kBufferSize); |
140 } | 153 MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte( |
141 | 154 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars), |
142 compiler::WasmCompilationUnit* unit = compilation_units->at(index); | 155 TENURED); |
143 if (unit != nullptr) { | 156 script->set_source_url(*url_str.ToHandleChecked()); |
144 unit->ExecuteCompilation(); | 157 |
145 base::LockGuard<base::Mutex> guard(result_mutex); | 158 int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash); |
146 executed_units->push(unit); | 159 DCHECK(name_chars >= 0 && name_chars < kBufferSize); |
147 } | 160 MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte( |
148 return true; | 161 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars), |
| 162 TENURED); |
| 163 script->set_name(*name_str.ToHandleChecked()); |
| 164 |
| 165 return script; |
149 } | 166 } |
150 | 167 |
151 class WasmCompilationTask : public CancelableTask { | 168 class JSToWasmWrapperCache { |
152 public: | 169 public: |
153 WasmCompilationTask( | 170 Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate, |
154 Isolate* isolate, | 171 const wasm::WasmModule* module, |
155 std::vector<compiler::WasmCompilationUnit*>* compilation_units, | 172 Handle<Code> wasm_code, |
156 std::queue<compiler::WasmCompilationUnit*>* executed_units, | 173 uint32_t index) { |
157 base::Semaphore* on_finished, base::Mutex* result_mutex, | 174 const wasm::WasmFunction* func = &module->functions[index]; |
158 base::AtomicNumber<size_t>* next_unit) | 175 int cached_idx = sig_map_.Find(func->sig); |
159 : CancelableTask(isolate), | 176 if (cached_idx >= 0) { |
160 isolate_(isolate), | 177 Handle<Code> code = isolate->factory()->CopyCode(code_cache_[cached_idx]); |
161 compilation_units_(compilation_units), | 178 // Now patch the call to wasm code. |
162 executed_units_(executed_units), | 179 for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) { |
163 on_finished_(on_finished), | 180 DCHECK(!it.done()); |
164 result_mutex_(result_mutex), | 181 Code* target = |
165 next_unit_(next_unit) {} | 182 Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); |
166 | 183 if (target->kind() == Code::WASM_FUNCTION || |
167 void RunInternal() override { | 184 target->kind() == Code::WASM_TO_JS_FUNCTION || |
168 while (FetchAndExecuteCompilationUnit(isolate_, compilation_units_, | 185 target->builtin_index() == Builtins::kIllegal) { |
169 executed_units_, result_mutex_, | 186 it.rinfo()->set_target_address(wasm_code->instruction_start()); |
170 next_unit_)) { | 187 break; |
171 } | 188 } |
172 on_finished_->Signal(); | 189 } |
173 } | 190 return code; |
| 191 } |
| 192 |
| 193 Handle<Code> code = |
| 194 compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index); |
| 195 uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig); |
| 196 DCHECK_EQ(code_cache_.size(), new_cache_idx); |
| 197 USE(new_cache_idx); |
| 198 code_cache_.push_back(code); |
| 199 return code; |
| 200 } |
| 201 |
| 202 private: |
| 203 // sig_map_ maps signatures to an index in code_cache_. |
| 204 wasm::SignatureMap sig_map_; |
| 205 std::vector<Handle<Code>> code_cache_; |
| 206 }; |
| 207 |
| 208 // A helper for compiling an entire module. |
| 209 class CompilationHelper { |
| 210 public: |
| 211 CompilationHelper(Isolate* isolate, WasmModule* module) |
| 212 : isolate_(isolate), module_(module) {} |
| 213 |
| 214 // The actual runnable task that performs compilations in the background. |
| 215 class CompilationTask : public CancelableTask { |
| 216 public: |
| 217 CompilationHelper* helper_; |
| 218 explicit CompilationTask(CompilationHelper* helper) |
| 219 : CancelableTask(helper->isolate_), helper_(helper) {} |
| 220 |
| 221 void RunInternal() override { |
| 222 while (helper_->FetchAndExecuteCompilationUnit()) { |
| 223 } |
| 224 helper_->module_->pending_tasks.get()->Signal(); |
| 225 } |
| 226 }; |
174 | 227 |
175 Isolate* isolate_; | 228 Isolate* isolate_; |
176 std::vector<compiler::WasmCompilationUnit*>* compilation_units_; | 229 WasmModule* module_; |
177 std::queue<compiler::WasmCompilationUnit*>* executed_units_; | 230 std::vector<compiler::WasmCompilationUnit*> compilation_units_; |
178 base::Semaphore* on_finished_; | 231 std::queue<compiler::WasmCompilationUnit*> executed_units_; |
179 base::Mutex* result_mutex_; | 232 base::Mutex result_mutex_; |
180 base::AtomicNumber<size_t>* next_unit_; | 233 base::AtomicNumber<size_t> next_unit_; |
181 }; | 234 |
182 | 235 // Run by each compilation task and by the main thread. |
183 static void RecordStats(Isolate* isolate, Code* code) { | 236 bool FetchAndExecuteCompilationUnit() { |
184 isolate->counters()->wasm_generated_code_size()->Increment(code->body_size()); | 237 DisallowHeapAllocation no_allocation; |
185 isolate->counters()->wasm_reloc_size()->Increment( | 238 DisallowHandleAllocation no_handles; |
186 code->relocation_info()->length()); | 239 DisallowHandleDereference no_deref; |
187 } | 240 DisallowCodeDependencyChange no_dependency_change; |
188 | 241 |
189 static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) { | 242 // - 1 because AtomicIncrement returns the value after the atomic increment. |
190 DisallowHeapAllocation no_gc; | 243 size_t index = next_unit_.Increment(1) - 1; |
191 for (int i = 0; i < functions->length(); ++i) { | 244 if (index >= compilation_units_.size()) { |
192 RecordStats(isolate, Code::cast(functions->get(i))); | 245 return false; |
193 } | 246 } |
194 } | 247 |
195 | 248 compiler::WasmCompilationUnit* unit = compilation_units_.at(index); |
196 void InitializeParallelCompilation( | 249 if (unit != nullptr) { |
197 Isolate* isolate, const std::vector<WasmFunction>& functions, | 250 unit->ExecuteCompilation(); |
198 std::vector<compiler::WasmCompilationUnit*>& compilation_units, | 251 base::LockGuard<base::Mutex> guard(&result_mutex_); |
199 ModuleBytesEnv& module_env, ErrorThrower* thrower) { | 252 executed_units_.push(unit); |
200 for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) { | 253 } |
201 const WasmFunction* func = &functions[i]; | 254 return true; |
202 compilation_units[i] = | 255 } |
203 func->imported ? nullptr : new compiler::WasmCompilationUnit( | 256 |
204 thrower, isolate, &module_env, func, i); | 257 void InitializeParallelCompilation(const std::vector<WasmFunction>& functions, |
205 } | 258 ModuleBytesEnv& module_env, |
206 } | 259 ErrorThrower* thrower) { |
207 | 260 compilation_units_.reserve(functions.size()); |
208 uint32_t* StartCompilationTasks( | 261 for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); |
209 Isolate* isolate, | 262 ++i) { |
210 std::vector<compiler::WasmCompilationUnit*>& compilation_units, | 263 const WasmFunction* func = &functions[i]; |
211 std::queue<compiler::WasmCompilationUnit*>& executed_units, | 264 compilation_units_.push_back( |
212 base::Semaphore* pending_tasks, base::Mutex& result_mutex, | 265 func->imported ? nullptr |
213 base::AtomicNumber<size_t>& next_unit) { | 266 : new compiler::WasmCompilationUnit( |
214 const size_t num_tasks = | 267 thrower, isolate_, &module_env, func, i)); |
215 Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), | 268 } |
216 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads()); | 269 } |
217 uint32_t* task_ids = new uint32_t[num_tasks]; | 270 |
218 for (size_t i = 0; i < num_tasks; ++i) { | 271 uint32_t* StartCompilationTasks() { |
219 WasmCompilationTask* task = | 272 const size_t num_tasks = |
220 new WasmCompilationTask(isolate, &compilation_units, &executed_units, | 273 Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), |
221 pending_tasks, &result_mutex, &next_unit); | 274 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads()); |
222 task_ids[i] = task->id(); | 275 uint32_t* task_ids = new uint32_t[num_tasks]; |
223 V8::GetCurrentPlatform()->CallOnBackgroundThread( | 276 for (size_t i = 0; i < num_tasks; ++i) { |
224 task, v8::Platform::kShortRunningTask); | 277 CompilationTask* task = new CompilationTask(this); |
225 } | 278 task_ids[i] = task->id(); |
226 return task_ids; | 279 V8::GetCurrentPlatform()->CallOnBackgroundThread( |
227 } | 280 task, v8::Platform::kShortRunningTask); |
228 | 281 } |
229 void WaitForCompilationTasks(Isolate* isolate, uint32_t* task_ids, | 282 return task_ids; |
230 base::Semaphore* pending_tasks) { | 283 } |
231 const size_t num_tasks = | 284 |
232 Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), | 285 void WaitForCompilationTasks(uint32_t* task_ids) { |
233 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads()); | 286 const size_t num_tasks = |
234 for (size_t i = 0; i < num_tasks; ++i) { | 287 Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), |
235 // If the task has not started yet, then we abort it. Otherwise we wait for | 288 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads()); |
236 // it to finish. | 289 for (size_t i = 0; i < num_tasks; ++i) { |
237 if (isolate->cancelable_task_manager()->TryAbort(task_ids[i]) != | 290 // If the task has not started yet, then we abort it. Otherwise we wait |
238 CancelableTaskManager::kTaskAborted) { | 291 // for |
239 pending_tasks->Wait(); | 292 // it to finish. |
240 } | 293 if (isolate_->cancelable_task_manager()->TryAbort(task_ids[i]) != |
241 } | 294 CancelableTaskManager::kTaskAborted) { |
242 } | 295 module_->pending_tasks.get()->Wait(); |
243 | 296 } |
244 void FinishCompilationUnits( | 297 } |
245 std::queue<compiler::WasmCompilationUnit*>& executed_units, | 298 } |
246 std::vector<Handle<Code>>& results, base::Mutex& result_mutex) { | 299 |
247 while (true) { | 300 void FinishCompilationUnits(std::vector<Handle<Code>>& results) { |
248 compiler::WasmCompilationUnit* unit = nullptr; | 301 while (true) { |
249 { | 302 compiler::WasmCompilationUnit* unit = nullptr; |
250 base::LockGuard<base::Mutex> guard(&result_mutex); | 303 { |
251 if (executed_units.empty()) { | 304 base::LockGuard<base::Mutex> guard(&result_mutex_); |
| 305 if (executed_units_.empty()) { |
| 306 break; |
| 307 } |
| 308 unit = executed_units_.front(); |
| 309 executed_units_.pop(); |
| 310 } |
| 311 int j = unit->index(); |
| 312 results[j] = unit->FinishCompilation(); |
| 313 delete unit; |
| 314 } |
| 315 } |
| 316 |
| 317 void CompileInParallel(ModuleBytesEnv* module_env, |
| 318 std::vector<Handle<Code>>& results, |
| 319 ErrorThrower* thrower) { |
| 320 const WasmModule* module = module_env->module_env.module; |
| 321 // Data structures for the parallel compilation. |
| 322 |
| 323 //----------------------------------------------------------------------- |
| 324 // For parallel compilation: |
| 325 // 1) The main thread allocates a compilation unit for each wasm function |
| 326 // and stores them in the vector {compilation_units}. |
| 327 // 2) The main thread spawns {CompilationTask} instances which run on |
| 328 // the background threads. |
| 329 // 3.a) The background threads and the main thread pick one compilation |
| 330 // unit at a time and execute the parallel phase of the compilation |
| 331 // unit. After finishing the execution of the parallel phase, the |
| 332 // result is enqueued in {executed_units}. |
| 333 // 3.b) If {executed_units} contains a compilation unit, the main thread |
| 334 // dequeues it and finishes the compilation. |
| 335 // 4) After the parallel phase of all compilation units has started, the |
| 336 // main thread waits for all {CompilationTask} instances to finish. |
| 337 // 5) The main thread finishes the compilation. |
| 338 |
| 339 // Turn on the {CanonicalHandleScope} so that the background threads can |
| 340 // use the node cache. |
| 341 CanonicalHandleScope canonical(isolate_); |
| 342 |
| 343 // 1) The main thread allocates a compilation unit for each wasm function |
| 344 // and stores them in the vector {compilation_units}. |
| 345 InitializeParallelCompilation(module->functions, *module_env, thrower); |
| 346 |
| 347 // Objects for the synchronization with the background threads. |
| 348 base::AtomicNumber<size_t> next_unit( |
| 349 static_cast<size_t>(FLAG_skip_compiling_wasm_funcs)); |
| 350 |
| 351 // 2) The main thread spawns {CompilationTask} instances which run on |
| 352 // the background threads. |
| 353 std::unique_ptr<uint32_t[]> task_ids(StartCompilationTasks()); |
| 354 |
| 355 // 3.a) The background threads and the main thread pick one compilation |
| 356 // unit at a time and execute the parallel phase of the compilation |
| 357 // unit. After finishing the execution of the parallel phase, the |
| 358 // result is enqueued in {executed_units}. |
| 359 while (FetchAndExecuteCompilationUnit()) { |
| 360 // 3.b) If {executed_units} contains a compilation unit, the main thread |
| 361 // dequeues it and finishes the compilation unit. Compilation units |
| 362 // are finished concurrently to the background threads to save |
| 363 // memory. |
| 364 FinishCompilationUnits(results); |
| 365 } |
| 366 // 4) After the parallel phase of all compilation units has started, the |
| 367 // main thread waits for all {CompilationTask} instances to finish. |
| 368 WaitForCompilationTasks(task_ids.get()); |
| 369 // Finish the compilation of the remaining compilation units. |
| 370 FinishCompilationUnits(results); |
| 371 } |
| 372 |
| 373 void CompileSequentially(ModuleBytesEnv* module_env, |
| 374 std::vector<Handle<Code>>& results, |
| 375 ErrorThrower* thrower) { |
| 376 DCHECK(!thrower->error()); |
| 377 |
| 378 const WasmModule* module = module_env->module_env.module; |
| 379 for (uint32_t i = FLAG_skip_compiling_wasm_funcs; |
| 380 i < module->functions.size(); ++i) { |
| 381 const WasmFunction& func = module->functions[i]; |
| 382 if (func.imported) |
| 383 continue; // Imports are compiled at instantiation time. |
| 384 |
| 385 Handle<Code> code = Handle<Code>::null(); |
| 386 // Compile the function. |
| 387 code = compiler::WasmCompilationUnit::CompileWasmFunction( |
| 388 thrower, isolate_, module_env, &func); |
| 389 if (code.is_null()) { |
| 390 WasmName str = module_env->wire_bytes.GetName(&func); |
| 391 thrower->CompileError("Compilation of #%d:%.*s failed.", i, |
| 392 str.length(), str.start()); |
252 break; | 393 break; |
253 } | 394 } |
254 unit = executed_units.front(); | 395 results[i] = code; |
255 executed_units.pop(); | 396 } |
256 } | 397 } |
257 int j = unit->index(); | 398 |
258 results[j] = unit->FinishCompilation(); | 399 MaybeHandle<WasmModuleObject> CompileToModuleObject( |
259 delete unit; | 400 ErrorThrower* thrower, const ModuleWireBytes& wire_bytes, |
260 } | 401 Handle<Script> asm_js_script, |
| 402 Vector<const byte> asm_js_offset_table_bytes) { |
| 403 Factory* factory = isolate_->factory(); |
| 404 // The {module_wrapper} will take ownership of the {WasmModule} object, |
| 405 // and it will be destroyed when the GC reclaims the wrapper object. |
| 406 Handle<WasmModuleWrapper> module_wrapper = |
| 407 WasmModuleWrapper::New(isolate_, module_); |
| 408 WasmInstance temp_instance(module_); |
| 409 temp_instance.context = isolate_->native_context(); |
| 410 temp_instance.mem_size = WasmModule::kPageSize * module_->min_mem_pages; |
| 411 temp_instance.mem_start = nullptr; |
| 412 temp_instance.globals_start = nullptr; |
| 413 |
| 414 // Initialize the indirect tables with placeholders. |
| 415 int function_table_count = |
| 416 static_cast<int>(module_->function_tables.size()); |
| 417 Handle<FixedArray> function_tables = |
| 418 factory->NewFixedArray(function_table_count, TENURED); |
| 419 Handle<FixedArray> signature_tables = |
| 420 factory->NewFixedArray(function_table_count, TENURED); |
| 421 for (int i = 0; i < function_table_count; ++i) { |
| 422 temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED); |
| 423 temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED); |
| 424 function_tables->set(i, *temp_instance.function_tables[i]); |
| 425 signature_tables->set(i, *temp_instance.signature_tables[i]); |
| 426 } |
| 427 |
| 428 HistogramTimerScope wasm_compile_module_time_scope( |
| 429 isolate_->counters()->wasm_compile_module_time()); |
| 430 |
| 431 ModuleBytesEnv module_env(module_, &temp_instance, wire_bytes); |
| 432 |
| 433 // The {code_table} array contains import wrappers and functions (which |
| 434 // are both included in {functions.size()}, and export wrappers. |
| 435 int code_table_size = static_cast<int>(module_->functions.size() + |
| 436 module_->num_exported_functions); |
| 437 Handle<FixedArray> code_table = |
| 438 factory->NewFixedArray(static_cast<int>(code_table_size), TENURED); |
| 439 |
| 440 // Initialize the code table with the illegal builtin. All call sites will |
| 441 // be |
| 442 // patched at instantiation. |
| 443 Handle<Code> illegal_builtin = isolate_->builtins()->Illegal(); |
| 444 for (uint32_t i = 0; i < module_->functions.size(); ++i) { |
| 445 code_table->set(static_cast<int>(i), *illegal_builtin); |
| 446 temp_instance.function_code[i] = illegal_builtin; |
| 447 } |
| 448 |
| 449 isolate_->counters()->wasm_functions_per_module()->AddSample( |
| 450 static_cast<int>(module_->functions.size())); |
| 451 CompilationHelper helper(isolate_, module_); |
| 452 if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0) { |
| 453 // Avoid a race condition by collecting results into a second vector. |
| 454 std::vector<Handle<Code>> results(temp_instance.function_code); |
| 455 helper.CompileInParallel(&module_env, results, thrower); |
| 456 temp_instance.function_code.swap(results); |
| 457 } else { |
| 458 helper.CompileSequentially(&module_env, temp_instance.function_code, |
| 459 thrower); |
| 460 } |
| 461 if (thrower->error()) return {}; |
| 462 |
| 463 // At this point, compilation has completed. Update the code table. |
| 464 for (size_t i = FLAG_skip_compiling_wasm_funcs; |
| 465 i < temp_instance.function_code.size(); ++i) { |
| 466 Code* code = *temp_instance.function_code[i]; |
| 467 code_table->set(static_cast<int>(i), code); |
| 468 RecordStats(isolate_, code); |
| 469 } |
| 470 |
| 471 // Create heap objects for script, module bytes and asm.js offset table to |
| 472 // be |
| 473 // stored in the shared module data. |
| 474 Handle<Script> script; |
| 475 Handle<ByteArray> asm_js_offset_table; |
| 476 if (asm_js_script.is_null()) { |
| 477 script = CreateWasmScript(isolate_, wire_bytes); |
| 478 } else { |
| 479 script = asm_js_script; |
| 480 asm_js_offset_table = |
| 481 isolate_->factory()->NewByteArray(asm_js_offset_table_bytes.length()); |
| 482 asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(), |
| 483 asm_js_offset_table_bytes.length()); |
| 484 } |
| 485 // TODO(wasm): only save the sections necessary to deserialize a |
| 486 // {WasmModule}. E.g. function bodies could be omitted. |
| 487 Handle<String> module_bytes = |
| 488 factory |
| 489 ->NewStringFromOneByte({wire_bytes.start(), wire_bytes.length()}, |
| 490 TENURED) |
| 491 .ToHandleChecked(); |
| 492 DCHECK(module_bytes->IsSeqOneByteString()); |
| 493 |
| 494 // Create the shared module data. |
| 495 // TODO(clemensh): For the same module (same bytes / same hash), we should |
| 496 // only have one WasmSharedModuleData. Otherwise, we might only set |
| 497 // breakpoints on a (potentially empty) subset of the instances. |
| 498 |
| 499 Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New( |
| 500 isolate_, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes), |
| 501 script, asm_js_offset_table); |
| 502 |
| 503 // Create the compiled module object, and populate with compiled functions |
| 504 // and information needed at instantiation time. This object needs to be |
| 505 // serializable. Instantiation may occur off a deserialized version of this |
| 506 // object. |
| 507 Handle<WasmCompiledModule> compiled_module = |
| 508 WasmCompiledModule::New(isolate_, shared); |
| 509 compiled_module->set_num_imported_functions( |
| 510 module_->num_imported_functions); |
| 511 compiled_module->set_code_table(code_table); |
| 512 compiled_module->set_min_mem_pages(module_->min_mem_pages); |
| 513 compiled_module->set_max_mem_pages(module_->max_mem_pages); |
| 514 if (function_table_count > 0) { |
| 515 compiled_module->set_function_tables(function_tables); |
| 516 compiled_module->set_signature_tables(signature_tables); |
| 517 compiled_module->set_empty_function_tables(function_tables); |
| 518 } |
| 519 |
| 520 // If we created a wasm script, finish it now and make it public to the |
| 521 // debugger. |
| 522 if (asm_js_script.is_null()) { |
| 523 script->set_wasm_compiled_module(*compiled_module); |
| 524 isolate_->debug()->OnAfterCompile(script); |
| 525 } |
| 526 |
| 527 // Compile JS->WASM wrappers for exported functions. |
| 528 JSToWasmWrapperCache js_to_wasm_cache; |
| 529 int func_index = 0; |
| 530 for (auto exp : module_->export_table) { |
| 531 if (exp.kind != kExternalFunction) continue; |
| 532 Handle<Code> wasm_code(Code::cast(code_table->get(exp.index)), isolate_); |
| 533 Handle<Code> wrapper_code = |
| 534 js_to_wasm_cache.CloneOrCompileJSToWasmWrapper(isolate_, module_, |
| 535 wasm_code, exp.index); |
| 536 int export_index = |
| 537 static_cast<int>(module_->functions.size() + func_index); |
| 538 code_table->set(export_index, *wrapper_code); |
| 539 RecordStats(isolate_, *wrapper_code); |
| 540 func_index++; |
| 541 } |
| 542 |
| 543 return WasmModuleObject::New(isolate_, compiled_module); |
261 } | 544 } |
262 | 545 }; |
263 void CompileInParallel(Isolate* isolate, ModuleBytesEnv* module_env, | |
264 std::vector<Handle<Code>>& functions, | |
265 ErrorThrower* thrower) { | |
266 const WasmModule* module = module_env->module_env.module; | |
267 // Data structures for the parallel compilation. | |
268 std::vector<compiler::WasmCompilationUnit*> compilation_units( | |
269 module->functions.size()); | |
270 std::queue<compiler::WasmCompilationUnit*> executed_units; | |
271 | |
272 //----------------------------------------------------------------------- | |
273 // For parallel compilation: | |
274 // 1) The main thread allocates a compilation unit for each wasm function | |
275 // and stores them in the vector {compilation_units}. | |
276 // 2) The main thread spawns {WasmCompilationTask} instances which run on | |
277 // the background threads. | |
278 // 3.a) The background threads and the main thread pick one compilation | |
279 // unit at a time and execute the parallel phase of the compilation | |
280 // unit. After finishing the execution of the parallel phase, the | |
281 // result is enqueued in {executed_units}. | |
282 // 3.b) If {executed_units} contains a compilation unit, the main thread | |
283 // dequeues it and finishes the compilation. | |
284 // 4) After the parallel phase of all compilation units has started, the | |
285 // main thread waits for all {WasmCompilationTask} instances to finish. | |
286 // 5) The main thread finishes the compilation. | |
287 | |
288 // Turn on the {CanonicalHandleScope} so that the background threads can | |
289 // use the node cache. | |
290 CanonicalHandleScope canonical(isolate); | |
291 | |
292 // 1) The main thread allocates a compilation unit for each wasm function | |
293 // and stores them in the vector {compilation_units}. | |
294 InitializeParallelCompilation(isolate, module->functions, compilation_units, | |
295 *module_env, thrower); | |
296 | |
297 // Objects for the synchronization with the background threads. | |
298 base::Mutex result_mutex; | |
299 base::AtomicNumber<size_t> next_unit( | |
300 static_cast<size_t>(FLAG_skip_compiling_wasm_funcs)); | |
301 | |
302 // 2) The main thread spawns {WasmCompilationTask} instances which run on | |
303 // the background threads. | |
304 std::unique_ptr<uint32_t[]> task_ids(StartCompilationTasks( | |
305 isolate, compilation_units, executed_units, module->pending_tasks.get(), | |
306 result_mutex, next_unit)); | |
307 | |
308 // 3.a) The background threads and the main thread pick one compilation | |
309 // unit at a time and execute the parallel phase of the compilation | |
310 // unit. After finishing the execution of the parallel phase, the | |
311 // result is enqueued in {executed_units}. | |
312 while (FetchAndExecuteCompilationUnit(isolate, &compilation_units, | |
313 &executed_units, &result_mutex, | |
314 &next_unit)) { | |
315 // 3.b) If {executed_units} contains a compilation unit, the main thread | |
316 // dequeues it and finishes the compilation unit. Compilation units | |
317 // are finished concurrently to the background threads to save | |
318 // memory. | |
319 FinishCompilationUnits(executed_units, functions, result_mutex); | |
320 } | |
321 // 4) After the parallel phase of all compilation units has started, the | |
322 // main thread waits for all {WasmCompilationTask} instances to finish. | |
323 WaitForCompilationTasks(isolate, task_ids.get(), module->pending_tasks.get()); | |
324 // Finish the compilation of the remaining compilation units. | |
325 FinishCompilationUnits(executed_units, functions, result_mutex); | |
326 } | |
327 | |
328 void CompileSequentially(Isolate* isolate, ModuleBytesEnv* module_env, | |
329 std::vector<Handle<Code>>& functions, | |
330 ErrorThrower* thrower) { | |
331 DCHECK(!thrower->error()); | |
332 | |
333 const WasmModule* module = module_env->module_env.module; | |
334 for (uint32_t i = FLAG_skip_compiling_wasm_funcs; | |
335 i < module->functions.size(); ++i) { | |
336 const WasmFunction& func = module->functions[i]; | |
337 if (func.imported) continue; // Imports are compiled at instantiation time. | |
338 | |
339 Handle<Code> code = Handle<Code>::null(); | |
340 // Compile the function. | |
341 code = compiler::WasmCompilationUnit::CompileWasmFunction( | |
342 thrower, isolate, module_env, &func); | |
343 if (code.is_null()) { | |
344 WasmName str = module_env->wire_bytes.GetName(&func); | |
345 thrower->CompileError("Compilation of #%d:%.*s failed.", i, str.length(), | |
346 str.start()); | |
347 break; | |
348 } | |
349 // Install the code into the linker table. | |
350 functions[i] = code; | |
351 } | |
352 } | |
353 | 546 |
354 static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner, | 547 static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner, |
355 WasmCompiledModule* compiled_module) { | 548 WasmCompiledModule* compiled_module) { |
356 TRACE("Resetting %d\n", compiled_module->instance_id()); | 549 TRACE("Resetting %d\n", compiled_module->instance_id()); |
357 Object* undefined = *isolate->factory()->undefined_value(); | 550 Object* undefined = *isolate->factory()->undefined_value(); |
358 Object* fct_obj = compiled_module->ptr_to_code_table(); | 551 Object* fct_obj = compiled_module->ptr_to_code_table(); |
359 if (fct_obj != nullptr && fct_obj != undefined) { | 552 if (fct_obj != nullptr && fct_obj != undefined) { |
360 uint32_t old_mem_size = compiled_module->mem_size(); | 553 uint32_t old_mem_size = compiled_module->mem_size(); |
361 uint32_t default_mem_size = compiled_module->default_mem_size(); | 554 uint32_t default_mem_size = compiled_module->default_mem_size(); |
362 Object* mem_start = compiled_module->maybe_ptr_to_memory(); | 555 Object* mem_start = compiled_module->maybe_ptr_to_memory(); |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
537 Handle<WasmCompiledModule> compiled_module, int func_index) { | 730 Handle<WasmCompiledModule> compiled_module, int func_index) { |
538 WasmModule* module = compiled_module->module(); | 731 WasmModule* module = compiled_module->module(); |
539 if (func_index < 0 || | 732 if (func_index < 0 || |
540 static_cast<size_t>(func_index) > module->functions.size()) { | 733 static_cast<size_t>(func_index) > module->functions.size()) { |
541 return {0, 0}; | 734 return {0, 0}; |
542 } | 735 } |
543 WasmFunction& func = module->functions[func_index]; | 736 WasmFunction& func = module->functions[func_index]; |
544 return {static_cast<int>(func.code_start_offset), | 737 return {static_cast<int>(func.code_start_offset), |
545 static_cast<int>(func.code_end_offset - func.code_start_offset)}; | 738 static_cast<int>(func.code_end_offset - func.code_start_offset)}; |
546 } | 739 } |
547 | |
548 Handle<Script> CreateWasmScript(Isolate* isolate, | |
549 const ModuleWireBytes& wire_bytes) { | |
550 Handle<Script> script = | |
551 isolate->factory()->NewScript(isolate->factory()->empty_string()); | |
552 FixedArray* array = isolate->native_context()->embedder_data(); | |
553 script->set_context_data(array->get(v8::Context::kDebugIdIndex)); | |
554 script->set_type(Script::TYPE_WASM); | |
555 | |
556 int hash = StringHasher::HashSequentialString( | |
557 reinterpret_cast<const char*>(wire_bytes.start()), wire_bytes.length(), | |
558 kZeroHashSeed); | |
559 | |
560 const int kBufferSize = 50; | |
561 char buffer[kBufferSize]; | |
562 int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash); | |
563 DCHECK(url_chars >= 0 && url_chars < kBufferSize); | |
564 MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte( | |
565 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars), | |
566 TENURED); | |
567 script->set_source_url(*url_str.ToHandleChecked()); | |
568 | |
569 int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash); | |
570 DCHECK(name_chars >= 0 && name_chars < kBufferSize); | |
571 MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte( | |
572 Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars), | |
573 TENURED); | |
574 script->set_name(*name_str.ToHandleChecked()); | |
575 | |
576 return script; | |
577 } | |
578 | |
579 class JSToWasmWrapperCache { | |
580 public: | |
581 Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate, | |
582 const wasm::WasmModule* module, | |
583 Handle<Code> wasm_code, | |
584 uint32_t index) { | |
585 const wasm::WasmFunction* func = &module->functions[index]; | |
586 int cached_idx = sig_map_.Find(func->sig); | |
587 if (cached_idx >= 0) { | |
588 Handle<Code> code = isolate->factory()->CopyCode(code_cache_[cached_idx]); | |
589 // Now patch the call to wasm code. | |
590 for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) { | |
591 DCHECK(!it.done()); | |
592 Code* target = | |
593 Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); | |
594 if (target->kind() == Code::WASM_FUNCTION || | |
595 target->kind() == Code::WASM_TO_JS_FUNCTION || | |
596 target->builtin_index() == Builtins::kIllegal) { | |
597 it.rinfo()->set_target_address(wasm_code->instruction_start()); | |
598 break; | |
599 } | |
600 } | |
601 return code; | |
602 } | |
603 | |
604 Handle<Code> code = | |
605 compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index); | |
606 uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig); | |
607 DCHECK_EQ(code_cache_.size(), new_cache_idx); | |
608 USE(new_cache_idx); | |
609 code_cache_.push_back(code); | |
610 return code; | |
611 } | |
612 | |
613 private: | |
614 // sig_map_ maps signatures to an index in code_cache_. | |
615 wasm::SignatureMap sig_map_; | |
616 std::vector<Handle<Code>> code_cache_; | |
617 }; | |
618 | |
619 } // namespace | 740 } // namespace |
620 | 741 |
621 Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* backing_store, | 742 Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* backing_store, |
622 size_t size, bool is_external, | 743 size_t size, bool is_external, |
623 bool enable_guard_regions) { | 744 bool enable_guard_regions) { |
624 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); | 745 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
625 JSArrayBuffer::Setup(buffer, isolate, is_external, backing_store, | 746 JSArrayBuffer::Setup(buffer, isolate, is_external, backing_store, |
626 static_cast<int>(size)); | 747 static_cast<int>(size)); |
627 buffer->set_is_neuterable(false); | 748 buffer->set_is_neuterable(false); |
628 buffer->set_has_guard_region(enable_guard_regions); | 749 buffer->set_has_guard_region(enable_guard_regions); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
716 } | 837 } |
717 | 838 |
718 int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module, | 839 int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module, |
719 int func_index) { | 840 int func_index) { |
720 return GetFunctionOffsetAndLength(compiled_module, func_index).first; | 841 return GetFunctionOffsetAndLength(compiled_module, func_index).first; |
721 } | 842 } |
722 | 843 |
723 WasmModule::WasmModule(Zone* owned) | 844 WasmModule::WasmModule(Zone* owned) |
724 : owned_zone(owned), pending_tasks(new base::Semaphore(0)) {} | 845 : owned_zone(owned), pending_tasks(new base::Semaphore(0)) {} |
725 | 846 |
726 MaybeHandle<WasmModuleObject> CompileToModuleObject( | |
727 Isolate* isolate, WasmModule* m, ErrorThrower* thrower, | |
728 const ModuleWireBytes& wire_bytes, Handle<Script> asm_js_script, | |
729 Vector<const byte> asm_js_offset_table_bytes) { | |
730 Factory* factory = isolate->factory(); | |
731 MaybeHandle<WasmModuleObject> nothing; | |
732 // The {module_wrapper} will take ownership of the {WasmModule} object, | |
733 // and it will be destroyed when the GC reclaims the wrapper object. | |
734 Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New(isolate, m); | |
735 WasmInstance temp_instance(m); | |
736 temp_instance.context = isolate->native_context(); | |
737 temp_instance.mem_size = WasmModule::kPageSize * m->min_mem_pages; | |
738 temp_instance.mem_start = nullptr; | |
739 temp_instance.globals_start = nullptr; | |
740 | |
741 // Initialize the indirect tables with placeholders. | |
742 int function_table_count = static_cast<int>(m->function_tables.size()); | |
743 Handle<FixedArray> function_tables = | |
744 factory->NewFixedArray(function_table_count, TENURED); | |
745 Handle<FixedArray> signature_tables = | |
746 factory->NewFixedArray(function_table_count, TENURED); | |
747 for (int i = 0; i < function_table_count; ++i) { | |
748 temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED); | |
749 temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED); | |
750 function_tables->set(i, *temp_instance.function_tables[i]); | |
751 signature_tables->set(i, *temp_instance.signature_tables[i]); | |
752 } | |
753 | |
754 HistogramTimerScope wasm_compile_module_time_scope( | |
755 isolate->counters()->wasm_compile_module_time()); | |
756 | |
757 ModuleBytesEnv module_env(m, &temp_instance, wire_bytes); | |
758 | |
759 // The {code_table} array contains import wrappers and functions (which | |
760 // are both included in {functions.size()}, and export wrappers. | |
761 int code_table_size = | |
762 static_cast<int>(m->functions.size() + m->num_exported_functions); | |
763 Handle<FixedArray> code_table = | |
764 factory->NewFixedArray(static_cast<int>(code_table_size), TENURED); | |
765 | |
766 // Initialize the code table with the illegal builtin. All call sites will be | |
767 // patched at instantiation. | |
768 Handle<Code> illegal_builtin = isolate->builtins()->Illegal(); | |
769 for (uint32_t i = 0; i < m->functions.size(); ++i) { | |
770 code_table->set(static_cast<int>(i), *illegal_builtin); | |
771 temp_instance.function_code[i] = illegal_builtin; | |
772 } | |
773 | |
774 isolate->counters()->wasm_functions_per_module()->AddSample( | |
775 static_cast<int>(m->functions.size())); | |
776 if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0) { | |
777 // Avoid a race condition by collecting results into a second vector. | |
778 std::vector<Handle<Code>> results(temp_instance.function_code); | |
779 CompileInParallel(isolate, &module_env, results, thrower); | |
780 temp_instance.function_code.swap(results); | |
781 } else { | |
782 CompileSequentially(isolate, &module_env, temp_instance.function_code, | |
783 thrower); | |
784 } | |
785 if (thrower->error()) return nothing; | |
786 | |
787 // At this point, compilation has completed. Update the code table. | |
788 for (size_t i = FLAG_skip_compiling_wasm_funcs; | |
789 i < temp_instance.function_code.size(); ++i) { | |
790 Code* code = *temp_instance.function_code[i]; | |
791 code_table->set(static_cast<int>(i), code); | |
792 RecordStats(isolate, code); | |
793 } | |
794 | |
795 // Create heap objects for script, module bytes and asm.js offset table to be | |
796 // stored in the shared module data. | |
797 Handle<Script> script; | |
798 Handle<ByteArray> asm_js_offset_table; | |
799 if (asm_js_script.is_null()) { | |
800 script = CreateWasmScript(isolate, wire_bytes); | |
801 } else { | |
802 script = asm_js_script; | |
803 asm_js_offset_table = | |
804 isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length()); | |
805 asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(), | |
806 asm_js_offset_table_bytes.length()); | |
807 } | |
808 // TODO(wasm): only save the sections necessary to deserialize a | |
809 // {WasmModule}. E.g. function bodies could be omitted. | |
810 Handle<String> module_bytes = | |
811 factory | |
812 ->NewStringFromOneByte({wire_bytes.start(), wire_bytes.length()}, | |
813 TENURED) | |
814 .ToHandleChecked(); | |
815 DCHECK(module_bytes->IsSeqOneByteString()); | |
816 | |
817 // Create the shared module data. | |
818 // TODO(clemensh): For the same module (same bytes / same hash), we should | |
819 // only have one WasmSharedModuleData. Otherwise, we might only set | |
820 // breakpoints on a (potentially empty) subset of the instances. | |
821 | |
822 Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New( | |
823 isolate, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes), | |
824 script, asm_js_offset_table); | |
825 | |
826 // Create the compiled module object, and populate with compiled functions | |
827 // and information needed at instantiation time. This object needs to be | |
828 // serializable. Instantiation may occur off a deserialized version of this | |
829 // object. | |
830 Handle<WasmCompiledModule> compiled_module = | |
831 WasmCompiledModule::New(isolate, shared); | |
832 compiled_module->set_num_imported_functions(m->num_imported_functions); | |
833 compiled_module->set_code_table(code_table); | |
834 compiled_module->set_min_mem_pages(m->min_mem_pages); | |
835 compiled_module->set_max_mem_pages(m->max_mem_pages); | |
836 if (function_table_count > 0) { | |
837 compiled_module->set_function_tables(function_tables); | |
838 compiled_module->set_signature_tables(signature_tables); | |
839 compiled_module->set_empty_function_tables(function_tables); | |
840 } | |
841 | |
842 // If we created a wasm script, finish it now and make it public to the | |
843 // debugger. | |
844 if (asm_js_script.is_null()) { | |
845 script->set_wasm_compiled_module(*compiled_module); | |
846 isolate->debug()->OnAfterCompile(script); | |
847 } | |
848 | |
849 // Compile JS->WASM wrappers for exported functions. | |
850 JSToWasmWrapperCache js_to_wasm_cache; | |
851 int func_index = 0; | |
852 for (auto exp : m->export_table) { | |
853 if (exp.kind != kExternalFunction) continue; | |
854 Handle<Code> wasm_code(Code::cast(code_table->get(exp.index)), isolate); | |
855 Handle<Code> wrapper_code = js_to_wasm_cache.CloneOrCompileJSToWasmWrapper( | |
856 isolate, m, wasm_code, exp.index); | |
857 int export_index = static_cast<int>(m->functions.size() + func_index); | |
858 code_table->set(export_index, *wrapper_code); | |
859 RecordStats(isolate, *wrapper_code); | |
860 func_index++; | |
861 } | |
862 | |
863 return WasmModuleObject::New(isolate, compiled_module); | |
864 } | |
865 | |
866 static WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate, | 847 static WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate, |
867 Handle<Object> target) { | 848 Handle<Object> target) { |
868 if (target->IsJSFunction()) { | 849 if (target->IsJSFunction()) { |
869 Handle<JSFunction> func = Handle<JSFunction>::cast(target); | 850 Handle<JSFunction> func = Handle<JSFunction>::cast(target); |
870 if (func->code()->kind() == Code::JS_TO_WASM_FUNCTION) { | 851 if (func->code()->kind() == Code::JS_TO_WASM_FUNCTION) { |
871 auto exported = Handle<WasmExportedFunction>::cast(func); | 852 auto exported = Handle<WasmExportedFunction>::cast(func); |
872 Handle<WasmInstanceObject> other_instance(exported->instance(), isolate); | 853 Handle<WasmInstanceObject> other_instance(exported->instance(), isolate); |
873 int func_index = exported->function_index(); | 854 int func_index = exported->function_index(); |
874 return &other_instance->module()->functions[func_index]; | 855 return &other_instance->module()->functions[func_index]; |
875 } | 856 } |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
975 module_(module_object->compiled_module()->module()), | 956 module_(module_object->compiled_module()->module()), |
976 thrower_(thrower), | 957 thrower_(thrower), |
977 module_object_(module_object), | 958 module_object_(module_object), |
978 ffi_(ffi.is_null() ? Handle<JSReceiver>::null() | 959 ffi_(ffi.is_null() ? Handle<JSReceiver>::null() |
979 : ffi.ToHandleChecked()), | 960 : ffi.ToHandleChecked()), |
980 memory_(memory.is_null() ? Handle<JSArrayBuffer>::null() | 961 memory_(memory.is_null() ? Handle<JSArrayBuffer>::null() |
981 : memory.ToHandleChecked()) {} | 962 : memory.ToHandleChecked()) {} |
982 | 963 |
983 // Build an instance, in all of its glory. | 964 // Build an instance, in all of its glory. |
984 MaybeHandle<WasmInstanceObject> Build() { | 965 MaybeHandle<WasmInstanceObject> Build() { |
985 MaybeHandle<WasmInstanceObject> nothing; | |
986 | |
987 // Check that an imports argument was provided, if the module requires it. | 966 // Check that an imports argument was provided, if the module requires it. |
988 // No point in continuing otherwise. | 967 // No point in continuing otherwise. |
989 if (!module_->import_table.empty() && ffi_.is_null()) { | 968 if (!module_->import_table.empty() && ffi_.is_null()) { |
990 thrower_->TypeError( | 969 thrower_->TypeError( |
991 "Imports argument must be present and must be an object"); | 970 "Imports argument must be present and must be an object"); |
992 return nothing; | 971 return {}; |
993 } | 972 } |
994 | 973 |
995 HistogramTimerScope wasm_instantiate_module_time_scope( | 974 HistogramTimerScope wasm_instantiate_module_time_scope( |
996 isolate_->counters()->wasm_instantiate_module_time()); | 975 isolate_->counters()->wasm_instantiate_module_time()); |
997 Factory* factory = isolate_->factory(); | 976 Factory* factory = isolate_->factory(); |
998 | 977 |
999 //-------------------------------------------------------------------------- | 978 //-------------------------------------------------------------------------- |
1000 // Reuse the compiled module (if no owner), otherwise clone. | 979 // Reuse the compiled module (if no owner), otherwise clone. |
1001 //-------------------------------------------------------------------------- | 980 //-------------------------------------------------------------------------- |
1002 Handle<FixedArray> code_table; | 981 Handle<FixedArray> code_table; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1079 //-------------------------------------------------------------------------- | 1058 //-------------------------------------------------------------------------- |
1080 MaybeHandle<JSArrayBuffer> old_globals; | 1059 MaybeHandle<JSArrayBuffer> old_globals; |
1081 uint32_t globals_size = module_->globals_size; | 1060 uint32_t globals_size = module_->globals_size; |
1082 if (globals_size > 0) { | 1061 if (globals_size > 0) { |
1083 const bool enable_guard_regions = false; | 1062 const bool enable_guard_regions = false; |
1084 Handle<JSArrayBuffer> global_buffer = | 1063 Handle<JSArrayBuffer> global_buffer = |
1085 NewArrayBuffer(isolate_, globals_size, enable_guard_regions); | 1064 NewArrayBuffer(isolate_, globals_size, enable_guard_regions); |
1086 globals_ = global_buffer; | 1065 globals_ = global_buffer; |
1087 if (globals_.is_null()) { | 1066 if (globals_.is_null()) { |
1088 thrower_->RangeError("Out of memory: wasm globals"); | 1067 thrower_->RangeError("Out of memory: wasm globals"); |
1089 return nothing; | 1068 return {}; |
1090 } | 1069 } |
1091 Address old_globals_start = nullptr; | 1070 Address old_globals_start = nullptr; |
1092 if (!owner.is_null()) { | 1071 if (!owner.is_null()) { |
1093 DCHECK(owner.ToHandleChecked()->has_globals_buffer()); | 1072 DCHECK(owner.ToHandleChecked()->has_globals_buffer()); |
1094 old_globals_start = static_cast<Address>( | 1073 old_globals_start = static_cast<Address>( |
1095 owner.ToHandleChecked()->globals_buffer()->backing_store()); | 1074 owner.ToHandleChecked()->globals_buffer()->backing_store()); |
1096 } | 1075 } |
1097 Address new_globals_start = | 1076 Address new_globals_start = |
1098 static_cast<Address>(global_buffer->backing_store()); | 1077 static_cast<Address>(global_buffer->backing_store()); |
1099 code_specialization.RelocateGlobals(old_globals_start, new_globals_start); | 1078 code_specialization.RelocateGlobals(old_globals_start, new_globals_start); |
1100 instance->set_globals_buffer(*global_buffer); | 1079 instance->set_globals_buffer(*global_buffer); |
1101 } | 1080 } |
1102 | 1081 |
1103 //-------------------------------------------------------------------------- | 1082 //-------------------------------------------------------------------------- |
1104 // Prepare for initialization of function tables. | 1083 // Prepare for initialization of function tables. |
1105 //-------------------------------------------------------------------------- | 1084 //-------------------------------------------------------------------------- |
1106 int function_table_count = | 1085 int function_table_count = |
1107 static_cast<int>(module_->function_tables.size()); | 1086 static_cast<int>(module_->function_tables.size()); |
1108 table_instances_.reserve(module_->function_tables.size()); | 1087 table_instances_.reserve(module_->function_tables.size()); |
1109 for (int index = 0; index < function_table_count; ++index) { | 1088 for (int index = 0; index < function_table_count; ++index) { |
1110 table_instances_.push_back( | 1089 table_instances_.push_back( |
1111 {Handle<WasmTableObject>::null(), Handle<FixedArray>::null(), | 1090 {Handle<WasmTableObject>::null(), Handle<FixedArray>::null(), |
1112 Handle<FixedArray>::null(), Handle<FixedArray>::null()}); | 1091 Handle<FixedArray>::null(), Handle<FixedArray>::null()}); |
1113 } | 1092 } |
1114 | 1093 |
1115 //-------------------------------------------------------------------------- | 1094 //-------------------------------------------------------------------------- |
1116 // Process the imports for the module. | 1095 // Process the imports for the module. |
1117 //-------------------------------------------------------------------------- | 1096 //-------------------------------------------------------------------------- |
1118 int num_imported_functions = ProcessImports(code_table, instance); | 1097 int num_imported_functions = ProcessImports(code_table, instance); |
1119 if (num_imported_functions < 0) return nothing; | 1098 if (num_imported_functions < 0) return {}; |
1120 | 1099 |
1121 //-------------------------------------------------------------------------- | 1100 //-------------------------------------------------------------------------- |
1122 // Process the initialization for the module's globals. | 1101 // Process the initialization for the module's globals. |
1123 //-------------------------------------------------------------------------- | 1102 //-------------------------------------------------------------------------- |
1124 InitGlobals(); | 1103 InitGlobals(); |
1125 | 1104 |
1126 //-------------------------------------------------------------------------- | 1105 //-------------------------------------------------------------------------- |
1127 // Set up the indirect function tables for the new instance. | 1106 // Set up the indirect function tables for the new instance. |
1128 //-------------------------------------------------------------------------- | 1107 //-------------------------------------------------------------------------- |
1129 if (function_table_count > 0) | 1108 if (function_table_count > 0) |
1130 InitializeTables(code_table, instance, &code_specialization); | 1109 InitializeTables(code_table, instance, &code_specialization); |
1131 | 1110 |
1132 //-------------------------------------------------------------------------- | 1111 //-------------------------------------------------------------------------- |
1133 // Set up the memory for the new instance. | 1112 // Set up the memory for the new instance. |
1134 //-------------------------------------------------------------------------- | 1113 //-------------------------------------------------------------------------- |
1135 MaybeHandle<JSArrayBuffer> old_memory; | 1114 MaybeHandle<JSArrayBuffer> old_memory; |
1136 | 1115 |
1137 uint32_t min_mem_pages = module_->min_mem_pages; | 1116 uint32_t min_mem_pages = module_->min_mem_pages; |
1138 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); | 1117 isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); |
1139 | 1118 |
1140 if (!memory_.is_null()) { | 1119 if (!memory_.is_null()) { |
1141 // Set externally passed ArrayBuffer non neuterable. | 1120 // Set externally passed ArrayBuffer non neuterable. |
1142 memory_->set_is_neuterable(false); | 1121 memory_->set_is_neuterable(false); |
1143 | 1122 |
1144 DCHECK_IMPLIES(EnableGuardRegions(), module_->origin == kAsmJsOrigin || | 1123 DCHECK_IMPLIES(EnableGuardRegions(), module_->origin == kAsmJsOrigin || |
1145 memory_->has_guard_region()); | 1124 memory_->has_guard_region()); |
1146 } else if (min_mem_pages > 0) { | 1125 } else if (min_mem_pages > 0) { |
1147 memory_ = AllocateMemory(min_mem_pages); | 1126 memory_ = AllocateMemory(min_mem_pages); |
1148 if (memory_.is_null()) return nothing; // failed to allocate memory | 1127 if (memory_.is_null()) return {}; // failed to allocate memory |
1149 } | 1128 } |
1150 | 1129 |
1151 //-------------------------------------------------------------------------- | 1130 //-------------------------------------------------------------------------- |
1152 // Check that indirect function table segments are within bounds. | 1131 // Check that indirect function table segments are within bounds. |
1153 //-------------------------------------------------------------------------- | 1132 //-------------------------------------------------------------------------- |
1154 for (WasmTableInit& table_init : module_->table_inits) { | 1133 for (WasmTableInit& table_init : module_->table_inits) { |
1155 DCHECK(table_init.table_index < table_instances_.size()); | 1134 DCHECK(table_init.table_index < table_instances_.size()); |
1156 uint32_t base = EvalUint32InitExpr(table_init.offset); | 1135 uint32_t base = EvalUint32InitExpr(table_init.offset); |
1157 uint32_t table_size = | 1136 uint32_t table_size = |
1158 table_instances_[table_init.table_index].function_table->length(); | 1137 table_instances_[table_init.table_index].function_table->length(); |
1159 if (!in_bounds(base, static_cast<uint32_t>(table_init.entries.size()), | 1138 if (!in_bounds(base, static_cast<uint32_t>(table_init.entries.size()), |
1160 table_size)) { | 1139 table_size)) { |
1161 thrower_->LinkError("table initializer is out of bounds"); | 1140 thrower_->LinkError("table initializer is out of bounds"); |
1162 return nothing; | 1141 return {}; |
1163 } | 1142 } |
1164 } | 1143 } |
1165 | 1144 |
1166 //-------------------------------------------------------------------------- | 1145 //-------------------------------------------------------------------------- |
1167 // Check that memory segments are within bounds. | 1146 // Check that memory segments are within bounds. |
1168 //-------------------------------------------------------------------------- | 1147 //-------------------------------------------------------------------------- |
1169 for (WasmDataSegment& seg : module_->data_segments) { | 1148 for (WasmDataSegment& seg : module_->data_segments) { |
1170 uint32_t base = EvalUint32InitExpr(seg.dest_addr); | 1149 uint32_t base = EvalUint32InitExpr(seg.dest_addr); |
1171 uint32_t mem_size = memory_.is_null() | 1150 uint32_t mem_size = memory_.is_null() |
1172 ? 0 : static_cast<uint32_t>(memory_->byte_length()->Number()); | 1151 ? 0 : static_cast<uint32_t>(memory_->byte_length()->Number()); |
1173 if (!in_bounds(base, seg.source_size, mem_size)) { | 1152 if (!in_bounds(base, seg.source_size, mem_size)) { |
1174 thrower_->LinkError("data segment is out of bounds"); | 1153 thrower_->LinkError("data segment is out of bounds"); |
1175 return nothing; | 1154 return {}; |
1176 } | 1155 } |
1177 } | 1156 } |
1178 | 1157 |
1179 //-------------------------------------------------------------------------- | 1158 //-------------------------------------------------------------------------- |
1180 // Initialize memory. | 1159 // Initialize memory. |
1181 //-------------------------------------------------------------------------- | 1160 //-------------------------------------------------------------------------- |
1182 if (!memory_.is_null()) { | 1161 if (!memory_.is_null()) { |
1183 instance->set_memory_buffer(*memory_); | 1162 instance->set_memory_buffer(*memory_); |
1184 Address mem_start = static_cast<Address>(memory_->backing_store()); | 1163 Address mem_start = static_cast<Address>(memory_->backing_store()); |
1185 uint32_t mem_size = | 1164 uint32_t mem_size = |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1333 MaybeHandle<Object> retval = | 1312 MaybeHandle<Object> retval = |
1334 Execution::Call(isolate_, startup_fct, undefined, 0, nullptr); | 1313 Execution::Call(isolate_, startup_fct, undefined, 0, nullptr); |
1335 | 1314 |
1336 if (retval.is_null()) { | 1315 if (retval.is_null()) { |
1337 DCHECK(isolate_->has_pending_exception()); | 1316 DCHECK(isolate_->has_pending_exception()); |
1338 isolate_->OptionalRescheduleException(false); | 1317 isolate_->OptionalRescheduleException(false); |
1339 // It's unfortunate that the new instance is already linked in the | 1318 // It's unfortunate that the new instance is already linked in the |
1340 // chain. However, we need to set up everything before executing the | 1319 // chain. However, we need to set up everything before executing the |
1341 // start function, such that stack trace information can be generated | 1320 // start function, such that stack trace information can be generated |
1342 // correctly already in the start function. | 1321 // correctly already in the start function. |
1343 return nothing; | 1322 return {}; |
1344 } | 1323 } |
1345 } | 1324 } |
1346 | 1325 |
1347 DCHECK(!isolate_->has_pending_exception()); | 1326 DCHECK(!isolate_->has_pending_exception()); |
1348 TRACE("Finishing instance %d\n", compiled_module_->instance_id()); | 1327 TRACE("Finishing instance %d\n", compiled_module_->instance_id()); |
1349 TRACE_CHAIN(module_object_->compiled_module()); | 1328 TRACE_CHAIN(module_object_->compiled_module()); |
1350 return instance; | 1329 return instance; |
1351 } | 1330 } |
1352 | 1331 |
1353 private: | 1332 private: |
(...skipping 1252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2606 ModuleResult result = | 2585 ModuleResult result = |
2607 DecodeWasmModule(isolate, bytes.start(), bytes.end(), true, kWasmOrigin); | 2586 DecodeWasmModule(isolate, bytes.start(), bytes.end(), true, kWasmOrigin); |
2608 if (result.val) delete result.val; | 2587 if (result.val) delete result.val; |
2609 return result.ok(); | 2588 return result.ok(); |
2610 } | 2589 } |
2611 | 2590 |
2612 MaybeHandle<WasmModuleObject> wasm::SyncCompileTranslatedAsmJs( | 2591 MaybeHandle<WasmModuleObject> wasm::SyncCompileTranslatedAsmJs( |
2613 Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes, | 2592 Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes, |
2614 Handle<Script> asm_js_script, | 2593 Handle<Script> asm_js_script, |
2615 Vector<const byte> asm_js_offset_table_bytes) { | 2594 Vector<const byte> asm_js_offset_table_bytes) { |
2616 MaybeHandle<WasmModuleObject> nothing; | |
2617 | 2595 |
2618 ModuleResult result = DecodeWasmModule(isolate, bytes.start(), bytes.end(), | 2596 ModuleResult result = DecodeWasmModule(isolate, bytes.start(), bytes.end(), |
2619 false, kAsmJsOrigin); | 2597 false, kAsmJsOrigin); |
2620 if (result.failed()) { | 2598 if (result.failed()) { |
2621 // TODO(titzer): use Result<std::unique_ptr<const WasmModule*>>? | 2599 // TODO(titzer): use Result<std::unique_ptr<const WasmModule*>>? |
2622 if (result.val) delete result.val; | 2600 if (result.val) delete result.val; |
2623 thrower->CompileFailed("Wasm decoding failed", result); | 2601 thrower->CompileFailed("Wasm decoding failed", result); |
2624 return nothing; | 2602 return {}; |
2625 } | 2603 } |
2626 | 2604 |
2627 return CompileToModuleObject(isolate, const_cast<WasmModule*>(result.val), | 2605 CompilationHelper helper(isolate, const_cast<WasmModule*>(result.val)); |
2628 thrower, bytes, asm_js_script, | 2606 return helper.CompileToModuleObject(thrower, bytes, asm_js_script, |
2629 asm_js_offset_table_bytes); | 2607 asm_js_offset_table_bytes); |
2630 } | 2608 } |
2631 | 2609 |
2632 MaybeHandle<WasmModuleObject> wasm::SyncCompile(Isolate* isolate, | 2610 MaybeHandle<WasmModuleObject> wasm::SyncCompile(Isolate* isolate, |
2633 ErrorThrower* thrower, | 2611 ErrorThrower* thrower, |
2634 const ModuleWireBytes& bytes) { | 2612 const ModuleWireBytes& bytes) { |
2635 MaybeHandle<WasmModuleObject> nothing; | |
2636 | |
2637 if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) { | 2613 if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) { |
2638 thrower->CompileError("Wasm code generation disallowed in this context"); | 2614 thrower->CompileError("Wasm code generation disallowed in this context"); |
2639 return nothing; | 2615 return {}; |
2640 } | 2616 } |
2641 | 2617 |
2642 ModuleResult result = | 2618 ModuleResult result = |
2643 DecodeWasmModule(isolate, bytes.start(), bytes.end(), false, kWasmOrigin); | 2619 DecodeWasmModule(isolate, bytes.start(), bytes.end(), false, kWasmOrigin); |
2644 if (result.failed()) { | 2620 if (result.failed()) { |
2645 if (result.val) delete result.val; | 2621 if (result.val) delete result.val; |
2646 thrower->CompileFailed("Wasm decoding failed", result); | 2622 thrower->CompileFailed("Wasm decoding failed", result); |
2647 return nothing; | 2623 return {}; |
2648 } | 2624 } |
2649 | 2625 |
2650 return CompileToModuleObject(isolate, const_cast<WasmModule*>(result.val), | 2626 CompilationHelper helper(isolate, const_cast<WasmModule*>(result.val)); |
2651 thrower, bytes, Handle<Script>(), | 2627 return helper.CompileToModuleObject(thrower, bytes, Handle<Script>(), |
2652 Vector<const byte>()); | 2628 Vector<const byte>()); |
2653 } | 2629 } |
2654 | 2630 |
2655 MaybeHandle<WasmInstanceObject> wasm::SyncInstantiate( | 2631 MaybeHandle<WasmInstanceObject> wasm::SyncInstantiate( |
2656 Isolate* isolate, ErrorThrower* thrower, | 2632 Isolate* isolate, ErrorThrower* thrower, |
2657 Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports, | 2633 Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports, |
2658 MaybeHandle<JSArrayBuffer> memory) { | 2634 MaybeHandle<JSArrayBuffer> memory) { |
2659 InstantiationHelper helper(isolate, thrower, module_object, imports, memory); | 2635 InstantiationHelper helper(isolate, thrower, module_object, imports, memory); |
2660 return helper.Build(); | 2636 return helper.Build(); |
2661 } | 2637 } |
2662 | 2638 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2732 Handle<String> module_property_name = | 2708 Handle<String> module_property_name = |
2733 isolate->factory()->InternalizeUtf8String("module"); | 2709 isolate->factory()->InternalizeUtf8String("module"); |
2734 Handle<String> instance_property_name = | 2710 Handle<String> instance_property_name = |
2735 isolate->factory()->InternalizeUtf8String("instance"); | 2711 isolate->factory()->InternalizeUtf8String("instance"); |
2736 JSObject::AddProperty(ret, module_property_name, module, NONE); | 2712 JSObject::AddProperty(ret, module_property_name, module, NONE); |
2737 JSObject::AddProperty(ret, instance_property_name, | 2713 JSObject::AddProperty(ret, instance_property_name, |
2738 instance_object.ToHandleChecked(), NONE); | 2714 instance_object.ToHandleChecked(), NONE); |
2739 | 2715 |
2740 ResolvePromise(isolate, promise, ret); | 2716 ResolvePromise(isolate, promise, ret); |
2741 } | 2717 } |
OLD | NEW |