WasmCompile.cpp (44161B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * 4 * Copyright 2015 Mozilla Foundation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #include "wasm/WasmCompile.h" 20 21 #include <algorithm> 22 #include <cstdint> 23 24 #include "js/Conversions.h" 25 #include "js/Equality.h" 26 #include "js/ForOfIterator.h" 27 #include "js/PropertyAndElement.h" 28 29 #ifndef __wasi__ 30 # include "jit/ProcessExecutableMemory.h" 31 #endif 32 33 #include "jit/FlushICache.h" 34 #include "jit/JitOptions.h" 35 #include "util/Text.h" 36 #include "vm/HelperThreads.h" 37 #include "vm/JSAtomState.h" 38 #include "vm/Realm.h" 39 #include "wasm/WasmBaselineCompile.h" 40 #include "wasm/WasmFeatures.h" 41 #include "wasm/WasmGenerator.h" 42 #include "wasm/WasmIonCompile.h" 43 #include "wasm/WasmOpIter.h" 44 #include "wasm/WasmProcess.h" 45 #include "wasm/WasmSignalHandlers.h" 46 #include "wasm/WasmValidate.h" 47 48 using namespace js; 49 using namespace js::jit; 50 using namespace js::wasm; 51 52 using mozilla::Atomic; 53 54 uint32_t wasm::ObservedCPUFeatures() { 55 enum Arch : uint32_t { 56 X86 = 0x1, 57 X64 = 0x2, 58 ARM = 0x3, 59 MIPS = 0x4, 60 MIPS64 = 0x5, 61 ARM64 = 0x6, 62 LOONG64 = 0x7, 63 RISCV64 = 0x8, 64 65 LAST = RISCV64, 66 ARCH_BITS = 4 67 }; 68 69 static_assert(LAST < (1 << ARCH_BITS)); 70 71 #if defined(JS_CODEGEN_X86) 72 MOZ_ASSERT(uint32_t(jit::CPUInfo::GetFingerprint()) <= 73 (UINT32_MAX >> ARCH_BITS)); 74 return X86 | (uint32_t(jit::CPUInfo::GetFingerprint()) << ARCH_BITS); 75 #elif defined(JS_CODEGEN_X64) 76 MOZ_ASSERT(uint32_t(jit::CPUInfo::GetFingerprint()) <= 77 (UINT32_MAX >> ARCH_BITS)); 78 return X64 | (uint32_t(jit::CPUInfo::GetFingerprint()) << ARCH_BITS); 79 #elif defined(JS_CODEGEN_ARM) 80 MOZ_ASSERT(jit::GetARMFlags() <= (UINT32_MAX >> ARCH_BITS)); 81 return ARM | (jit::GetARMFlags() << ARCH_BITS); 82 #elif defined(JS_CODEGEN_ARM64) 83 MOZ_ASSERT(jit::GetARM64Flags() <= (UINT32_MAX >> ARCH_BITS)); 84 return ARM64 | (jit::GetARM64Flags() << ARCH_BITS); 85 #elif defined(JS_CODEGEN_MIPS64) 86 MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS)); 87 return MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS); 88 #elif defined(JS_CODEGEN_LOONG64) 89 MOZ_ASSERT(jit::GetLOONG64Flags() <= (UINT32_MAX >> ARCH_BITS)); 90 return LOONG64 | (jit::GetLOONG64Flags() << ARCH_BITS); 91 #elif defined(JS_CODEGEN_RISCV64) 92 MOZ_ASSERT(jit::GetRISCV64Flags() <= (UINT32_MAX >> ARCH_BITS)); 93 return RISCV64 | (jit::GetRISCV64Flags() << ARCH_BITS); 94 #elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32) 95 return 0; 96 #else 97 # error "unknown architecture" 98 #endif 99 } 100 101 bool FeatureOptions::init(JSContext* cx, HandleValue val) { 102 if (val.isNullOrUndefined()) { 103 return true; 104 } 105 106 if (!val.isObject()) { 107 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 108 JSMSG_WASM_BAD_COMPILE_OPTIONS); 109 return false; 110 } 111 RootedObject obj(cx, &val.toObject()); 112 113 if (IsPrivilegedContext(cx)) { 114 RootedValue disableOptimizingCompiler(cx); 115 if (!JS_GetProperty(cx, obj, "disableOptimizingCompiler", 116 &disableOptimizingCompiler)) { 117 return false; 118 } 119 120 this->disableOptimizingCompiler = JS::ToBoolean(disableOptimizingCompiler); 121 122 RootedValue mozIntGemm(cx); 123 if (!JS_GetProperty(cx, obj, "mozIntGemm", &mozIntGemm)) { 124 return false; 125 } 126 127 this->mozIntGemm = JS::ToBoolean(mozIntGemm); 128 } else { 129 MOZ_ASSERT(!this->disableOptimizingCompiler); 130 } 131 132 // Check the 'importedStringConstants' option 133 RootedValue importedStringConstants(cx); 134 if (!JS_GetProperty(cx, obj, "importedStringConstants", 135 &importedStringConstants)) { 136 return false; 137 } 138 139 if (importedStringConstants.isNullOrUndefined()) { 140 this->jsStringConstants = false; 141 } else { 142 this->jsStringConstants = true; 143 144 RootedString importedStringConstantsString( 145 cx, JS::ToString(cx, importedStringConstants)); 146 if (!importedStringConstantsString) { 147 return false; 148 } 149 150 UniqueChars jsStringConstantsNamespace = 151 StringToNewUTF8CharsZ(cx, *importedStringConstantsString); 152 if (!jsStringConstantsNamespace) { 153 return false; 154 } 155 156 this->jsStringConstantsNamespace = 157 cx->new_<ShareableChars>(std::move(jsStringConstantsNamespace)); 158 if (!this->jsStringConstantsNamespace) { 159 return false; 160 } 161 } 162 163 // Get the `builtins` iterable 164 RootedValue builtins(cx); 165 if (!JS_GetProperty(cx, obj, "builtins", &builtins)) { 166 return false; 167 } 168 169 if (!builtins.isUndefined()) { 170 JS::ForOfIterator iterator(cx); 171 172 if (!iterator.init(builtins, JS::ForOfIterator::ThrowOnNonIterable)) { 173 return false; 174 } 175 176 RootedValue jsStringModule(cx, StringValue(cx->names().jsStringModule)); 177 RootedValue nextBuiltin(cx); 178 while (true) { 179 bool done; 180 if (!iterator.next(&nextBuiltin, &done)) { 181 return false; 182 } 183 if (done) { 184 break; 185 } 186 187 bool jsStringBuiltins; 188 if (!JS::LooselyEqual(cx, nextBuiltin, jsStringModule, 189 &jsStringBuiltins)) { 190 return false; 191 } 192 193 // We ignore unknown builtins 194 if (!jsStringBuiltins) { 195 continue; 196 } 197 198 // You cannot request the same builtin twice 199 if (this->jsStringBuiltins && jsStringBuiltins) { 200 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 201 JSMSG_WASM_DUPLICATE_BUILTIN); 202 return false; 203 } 204 this->jsStringBuiltins = jsStringBuiltins; 205 } 206 } 207 208 return true; 209 } 210 211 FeatureArgs FeatureArgs::build(JSContext* cx, const FeatureOptions& options) { 212 FeatureArgs features; 213 214 #define WASM_FEATURE(NAME, LOWER_NAME, ...) \ 215 features.LOWER_NAME = wasm::NAME##Available(cx); 216 JS_FOR_WASM_FEATURES(WASM_FEATURE); 217 #undef WASM_FEATURE 218 219 features.sharedMemory = 220 wasm::ThreadsAvailable(cx) ? Shareable::True : Shareable::False; 221 222 features.simd = jit::JitSupportsWasmSimd(); 223 features.isBuiltinModule = options.isBuiltinModule; 224 features.builtinModules.jsString = options.jsStringBuiltins; 225 features.builtinModules.jsStringConstants = options.jsStringConstants; 226 features.builtinModules.jsStringConstantsNamespace = 227 options.jsStringConstantsNamespace; 228 features.builtinModules.intGemm = 229 MozIntGemmAvailable(cx) && options.mozIntGemm; 230 231 return features; 232 } 233 234 SharedCompileArgs CompileArgs::build(JSContext* cx, 235 ScriptedCaller&& scriptedCaller, 236 const FeatureOptions& options, 237 CompileArgsError* error) { 238 bool baseline = BaselineAvailable(cx); 239 bool ion = IonAvailable(cx); 240 241 // If the user requested to disable ion and we're able to, fallback to 242 // baseline. 243 if (baseline && options.disableOptimizingCompiler) { 244 ion = false; 245 } 246 247 // Debug information such as source view or debug traps will require 248 // additional memory and permanently stay in baseline code, so we try to 249 // only enable it when a developer actually cares: when the debugger tab 250 // is open. 251 bool debug = cx->realm() && cx->realm()->debuggerObservesWasm(); 252 253 bool forceTiering = 254 cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2; 255 256 // The <Compiler>Available() predicates should ensure no failure here, but 257 // when we're fuzzing we allow inconsistent switches and the check may thus 258 // fail. Let it go to a run-time error instead of crashing. 259 if (debug && ion) { 260 *error = CompileArgsError::NoCompiler; 261 return nullptr; 262 } 263 264 if (forceTiering && !(baseline && ion)) { 265 // This can happen only in testing, and in this case we don't have a 266 // proper way to signal the error, so just silently override the default, 267 // instead of adding a skip-if directive to every test using debug/gc. 268 forceTiering = false; 269 } 270 271 if (!(baseline || ion)) { 272 *error = CompileArgsError::NoCompiler; 273 return nullptr; 274 } 275 276 CompileArgs* target = cx->new_<CompileArgs>(); 277 if (!target) { 278 *error = CompileArgsError::OutOfMemory; 279 return nullptr; 280 } 281 282 target->scriptedCaller = std::move(scriptedCaller); 283 target->baselineEnabled = baseline; 284 target->ionEnabled = ion; 285 target->debugEnabled = debug; 286 target->forceTiering = forceTiering; 287 target->features = FeatureArgs::build(cx, options); 288 289 return target; 290 } 291 292 void wasm::SetUseCountersForFeatureUsage(JSContext* cx, JSObject* object, 293 FeatureUsage usage) { 294 if (usage & FeatureUsage::LegacyExceptions) { 295 cx->runtime()->setUseCounter(object, JSUseCounter::WASM_LEGACY_EXCEPTIONS); 296 } 297 } 298 299 SharedCompileArgs CompileArgs::buildForAsmJS(ScriptedCaller&& scriptedCaller) { 300 CompileArgs* target = js_new<CompileArgs>(); 301 if (!target) { 302 return nullptr; 303 } 304 305 target->scriptedCaller = std::move(scriptedCaller); 306 // AsmJS is deprecated and doesn't have mechanisms for experimental features, 307 // so we don't need to initialize the FeatureArgs. It also only targets the 308 // Ion backend and does not need WASM debug support since it is de-optimized 309 // to JS in that case. 310 target->ionEnabled = true; 311 target->debugEnabled = false; 312 313 return target; 314 } 315 316 SharedCompileArgs CompileArgs::buildForValidation(const FeatureArgs& args) { 317 CompileArgs* target = js_new<CompileArgs>(); 318 if (!target) { 319 return nullptr; 320 } 321 322 // Validation will not need compilers, just mark them disabled 323 target->baselineEnabled = false; 324 target->ionEnabled = false; 325 target->debugEnabled = false; 326 target->forceTiering = false; 327 328 // Set the features 329 target->features = args; 330 331 return target; 332 } 333 334 SharedCompileArgs CompileArgs::buildAndReport(JSContext* cx, 335 ScriptedCaller&& scriptedCaller, 336 const FeatureOptions& options, 337 bool reportOOM) { 338 CompileArgsError error; 339 SharedCompileArgs args = 340 CompileArgs::build(cx, std::move(scriptedCaller), options, &error); 341 if (args) { 342 Log(cx, "available wasm compilers: tier1=%s tier2=%s", 343 args->baselineEnabled ? "baseline" : "none", 344 args->ionEnabled ? "ion" : "none"); 345 return args; 346 } 347 348 switch (error) { 349 case CompileArgsError::NoCompiler: { 350 JS_ReportErrorASCII(cx, "no WebAssembly compiler available"); 351 break; 352 } 353 case CompileArgsError::OutOfMemory: { 354 // Most callers are required to return 'false' without reporting an OOM, 355 // so we make reporting it optional here. 356 if (reportOOM) { 357 ReportOutOfMemory(cx); 358 } 359 break; 360 } 361 } 362 return nullptr; 363 } 364 365 BytecodeSource::BytecodeSource(const uint8_t* begin, size_t length) { 366 BytecodeRange envRange; 367 BytecodeRange codeRange; 368 BytecodeRange tailRange; 369 if (StartsCodeSection(begin, begin + length, &codeRange)) { 370 if (codeRange.end <= length) { 371 envRange = BytecodeRange(0, codeRange.start); 372 tailRange = BytecodeRange(codeRange.end, length - codeRange.end); 373 } else { 374 MOZ_RELEASE_ASSERT(codeRange.start <= length); 375 // If the specified code range is larger than the buffer, clamp it to the 376 // the buffer size. This buffer will be rejected later. 377 envRange = BytecodeRange(0, codeRange.start); 378 codeRange = BytecodeRange(codeRange.start, length - codeRange.start); 379 MOZ_RELEASE_ASSERT(codeRange.end == length); 380 tailRange = BytecodeRange(length, 0); 381 } 382 } else { 383 envRange = BytecodeRange(0, length); 384 codeRange = BytecodeRange(length, 0); 385 tailRange = BytecodeRange(length, 0); 386 } 387 388 BytecodeSpan module(begin, length); 389 env_ = envRange.toSpan(module); 390 code_ = codeRange.toSpan(module); 391 tail_ = tailRange.toSpan(module); 392 } 393 394 BytecodeBuffer::BytecodeBuffer(const ShareableBytes* env, 395 const ShareableBytes* code, 396 const ShareableBytes* tail) 397 : env_(env), 398 code_(code), 399 tail_(tail), 400 source_(env_ ? env_->span() : BytecodeSpan(), 401 code_ ? code_->span() : BytecodeSpan(), 402 tail_ ? tail_->span() : BytecodeSpan()) {} 403 404 bool BytecodeBuffer::fromSource(const BytecodeSource& bytecodeSource, 405 BytecodeBuffer* bytecodeBuffer) { 406 SharedBytes env; 407 if (!bytecodeSource.envRange().isEmpty()) { 408 env = ShareableBytes::fromSpan(bytecodeSource.envSpan()); 409 if (!env) { 410 return false; 411 } 412 } 413 414 SharedBytes code; 415 if (bytecodeSource.hasCodeSection() && 416 !bytecodeSource.codeRange().isEmpty()) { 417 code = ShareableBytes::fromSpan(bytecodeSource.codeSpan()); 418 if (!code) { 419 return false; 420 } 421 } 422 423 SharedBytes tail; 424 if (bytecodeSource.hasCodeSection() && 425 !bytecodeSource.tailRange().isEmpty()) { 426 tail = ShareableBytes::fromSpan(bytecodeSource.tailSpan()); 427 if (!tail) { 428 return false; 429 } 430 } 431 432 *bytecodeBuffer = BytecodeBuffer(env, code, tail); 433 return true; 434 } 435 436 /* 437 * [SMDOC] Tiered wasm compilation. 438 * 439 * "Tiered compilation" refers to the mechanism where we first compile the code 440 * with a fast non-optimizing compiler so that we can start running the code 441 * quickly, while in the background recompiling the code with the slower 442 * optimizing compiler. Code created by baseline is called "tier-1"; code 443 * created by the optimizing compiler is called "tier-2". When the tier-2 code 444 * is ready, we "tier up" the code by creating paths from tier-1 code into their 445 * tier-2 counterparts; this patching is performed as the program is running. 446 * 447 * ## Selecting the compilation mode 448 * 449 * When wasm bytecode arrives, we choose the compilation strategy based on 450 * switches and on aspects of the code and the hardware. If switches allow 451 * tiered compilation to happen (the normal case), the following logic applies. 452 * 453 * If the code is sufficiently large that tiered compilation would be beneficial 454 * but not so large that it might blow our compiled code budget and make 455 * compilation fail, we choose tiered compilation. Otherwise we go straight to 456 * optimized code. 457 * 458 * The expected benefit of tiering is computed by TieringBeneficial(), below, 459 * based on various estimated parameters of the hardware: ratios of object code 460 * to byte code, speed of the system, number of cores. 461 * 462 * ## Mechanics of tiering up; patching 463 * 464 * Every time control enters a tier-1 function, the function prologue loads its 465 * tiering pointer from the tiering jump table (see JumpTable in WasmCode.h) and 466 * jumps to it. 467 * 468 * Initially, an entry in the tiering table points to the instruction inside the 469 * tier-1 function that follows the jump instruction (hence the jump is an 470 * expensive nop). When the tier-2 compiler is finished, the table is patched 471 * racily to point into the tier-2 function at the correct prologue location 472 * (see loop near the end of Module::finishTier2()). As tier-2 compilation is 473 * performed at most once per Module, there is at most one such racy overwrite 474 * per table element during the lifetime of the Module. 475 * 476 * The effect of the patching is to cause the tier-1 function to jump to its 477 * tier-2 counterpart whenever the tier-1 function is called subsequently. That 478 * is, tier-1 code performs standard frame setup on behalf of whatever code it 479 * jumps to, and the target code (tier-1 or tier-2) allocates its own frame in 480 * whatever way it wants. 481 * 482 * The racy writing means that it is often nondeterministic whether tier-1 or 483 * tier-2 code is reached by any call during the tiering-up process; if F calls 484 * A and B in that order, it may reach tier-2 code for A and tier-1 code for B. 485 * If F is running concurrently on threads T1 and T2, T1 and T2 may see code 486 * from different tiers for either function. 487 * 488 * Note, tiering up also requires upgrading the jit-entry stubs so that they 489 * reference tier-2 code. The mechanics of this upgrading are described at 490 * WasmInstanceObject::getExportedFunction(). 491 * 492 * ## Current limitations of tiering 493 * 494 * Tiering is not always seamless. Partly, it is possible for a program to get 495 * stuck in tier-1 code. Partly, a function that has tiered up continues to 496 * force execution to go via tier-1 code to reach tier-2 code, paying for an 497 * additional jump and a slightly less optimized prologue than tier-2 code could 498 * have had on its own. 499 * 500 * Known tiering limitiations: 501 * 502 * - We can tier up only at function boundaries. If a tier-1 function has a 503 * long-running loop it will not tier up until it returns to its caller. If 504 * this loop never exits (a runloop in a worker, for example) then the 505 * function will never tier up. 506 * 507 * To do better, we need OSR. 508 * 509 * - Wasm Table entries are never patched during tier-up. A Table of funcref 510 * holds not a JSFunction pointer, but a (code*,instance*) pair of pointers. 511 * When a table.set operation is performed, the JSFunction value is decomposed 512 * and its code and instance pointers are stored in the table; subsequently, 513 * when a table.get operation is performed, the JSFunction value is 514 * reconstituted from its code pointer using fairly elaborate machinery. (The 515 * mechanics are the same also for the reflected JS operations on a 516 * WebAssembly.Table. For everything, see WasmTable.{cpp,h}.) The code pointer 517 * in the Table will always be the code pointer belonging to the best tier that 518 * was active at the time when that function was stored in that Table slot; in 519 * many cases, it will be tier-1 code. As a consequence, a call through a table 520 * will first enter tier-1 code and then jump to tier-2 code. 521 * 522 * To do better, we must update all the tables in the system when an instance 523 * tiers up. This is expected to be very hard. 524 * 525 * - Imported Wasm functions are never patched during tier-up. Imports are held 526 * in FuncImportInstanceData values in the instance, and for a wasm 527 * callee, what's stored is the raw code pointer into the best tier of the 528 * callee that was active at the time the import was resolved. That could be 529 * baseline code, and if it is, the situation is as for Table entries: a call 530 * to an import will always go via that import's tier-1 code, which will tier 531 * up with an indirect jump. 532 * 533 * To do better, we must update all the import tables in the system that 534 * import functions from instances whose modules have tiered up. This is 535 * expected to be hard. 536 */ 537 538 // Classify the current system as one of a set of recognizable classes. This 539 // really needs to get our tier-1 systems right. 540 // 541 // TODO: We don't yet have a good measure of how fast a system is. We 542 // distinguish between mobile and desktop because these are very different kinds 543 // of systems, but we could further distinguish between low / medium / high end 544 // within those major classes. If we do so, then constants below would be 545 // provided for each (class, architecture, system-tier) combination, not just 546 // (class, architecture) as now. 547 // 548 // CPU clock speed is not by itself a good predictor of system performance, as 549 // there are high-performance systems with slow clocks (recent Intel) and 550 // low-performance systems with fast clocks (older AMD). We can also use 551 // physical memory, core configuration, OS details, CPU class and family, and 552 // CPU manufacturer to disambiguate. 553 554 enum class SystemClass { 555 DesktopX86, 556 DesktopX64, 557 DesktopUnknown32, 558 DesktopUnknown64, 559 MobileX86, 560 MobileArm32, 561 MobileArm64, 562 MobileUnknown32, 563 MobileUnknown64 564 }; 565 566 static SystemClass ClassifySystem() { 567 bool isDesktop; 568 569 #if defined(ANDROID) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) 570 isDesktop = false; 571 #else 572 isDesktop = true; 573 #endif 574 575 if (isDesktop) { 576 #if defined(JS_CODEGEN_X64) 577 return SystemClass::DesktopX64; 578 #elif defined(JS_CODEGEN_X86) 579 return SystemClass::DesktopX86; 580 #elif defined(JS_64BIT) 581 return SystemClass::DesktopUnknown64; 582 #else 583 return SystemClass::DesktopUnknown32; 584 #endif 585 } else { 586 #if defined(JS_CODEGEN_X86) 587 return SystemClass::MobileX86; 588 #elif defined(JS_CODEGEN_ARM) 589 return SystemClass::MobileArm32; 590 #elif defined(JS_CODEGEN_ARM64) 591 return SystemClass::MobileArm64; 592 #elif defined(JS_64BIT) 593 return SystemClass::MobileUnknown64; 594 #else 595 return SystemClass::MobileUnknown32; 596 #endif 597 } 598 } 599 600 // Code sizes in machine code bytes per bytecode byte, again empirical except 601 // where marked. 602 // 603 // The Ion estimate for ARM64 is the measured Baseline value scaled by a 604 // plausible factor for optimized code. 605 606 static const double x64Tox86Inflation = 1.25; 607 608 static const double x64IonBytesPerBytecode = 2.45; 609 static const double x86IonBytesPerBytecode = 610 x64IonBytesPerBytecode * x64Tox86Inflation; 611 static const double arm32IonBytesPerBytecode = 3.3; 612 static const double arm64IonBytesPerBytecode = 3.0 / 1.4; // Estimate 613 614 static const double x64BaselineBytesPerBytecode = x64IonBytesPerBytecode * 1.43; 615 static const double x86BaselineBytesPerBytecode = 616 x64BaselineBytesPerBytecode * x64Tox86Inflation; 617 static const double arm32BaselineBytesPerBytecode = 618 arm32IonBytesPerBytecode * 1.39; 619 static const double arm64BaselineBytesPerBytecode = 3.0; 620 621 static double OptimizedBytesPerBytecode(SystemClass cls) { 622 switch (cls) { 623 case SystemClass::DesktopX86: 624 case SystemClass::MobileX86: 625 case SystemClass::DesktopUnknown32: 626 return x86IonBytesPerBytecode; 627 case SystemClass::DesktopX64: 628 case SystemClass::DesktopUnknown64: 629 return x64IonBytesPerBytecode; 630 case SystemClass::MobileArm32: 631 case SystemClass::MobileUnknown32: 632 return arm32IonBytesPerBytecode; 633 case SystemClass::MobileArm64: 634 case SystemClass::MobileUnknown64: 635 return arm64IonBytesPerBytecode; 636 default: 637 MOZ_CRASH(); 638 } 639 } 640 641 static double BaselineBytesPerBytecode(SystemClass cls) { 642 switch (cls) { 643 case SystemClass::DesktopX86: 644 case SystemClass::MobileX86: 645 case SystemClass::DesktopUnknown32: 646 return x86BaselineBytesPerBytecode; 647 case SystemClass::DesktopX64: 648 case SystemClass::DesktopUnknown64: 649 return x64BaselineBytesPerBytecode; 650 case SystemClass::MobileArm32: 651 case SystemClass::MobileUnknown32: 652 return arm32BaselineBytesPerBytecode; 653 case SystemClass::MobileArm64: 654 case SystemClass::MobileUnknown64: 655 return arm64BaselineBytesPerBytecode; 656 default: 657 MOZ_CRASH(); 658 } 659 } 660 661 double wasm::EstimateCompiledCodeSize(Tier tier, size_t bytecodeSize) { 662 SystemClass cls = ClassifySystem(); 663 switch (tier) { 664 case Tier::Baseline: 665 return double(bytecodeSize) * BaselineBytesPerBytecode(cls); 666 case Tier::Optimized: 667 return double(bytecodeSize) * OptimizedBytesPerBytecode(cls); 668 } 669 MOZ_CRASH("bad tier"); 670 } 671 672 // If parallel Ion compilation is going to take longer than this, we should 673 // tier. 674 675 static const double tierCutoffMs = 10; 676 677 // Compilation rate values are empirical except when noted, the reference 678 // systems are: 679 // 680 // Late-2013 MacBook Pro (2.6GHz 4 x hyperthreaded Haswell, Mac OS X) 681 // Late-2015 Nexus 5X (1.4GHz 4 x Cortex-A53 + 1.8GHz 2 x Cortex-A57, Android) 682 // Ca-2016 SoftIron Overdrive 1000 (1.7GHz 4 x Cortex-A57, Fedora) 683 // 684 // The rates are always per core. 685 // 686 // The estimate for ARM64 is the Baseline compilation rate on the SoftIron 687 // (because we have no Ion yet), divided by 5 to estimate Ion compile rate and 688 // then divided by 2 to make it more reasonable for consumer ARM64 systems. 689 690 static const double x64IonBytecodesPerMs = 2100; 691 static const double x86IonBytecodesPerMs = 1500; 692 static const double arm32IonBytecodesPerMs = 450; 693 static const double arm64IonBytecodesPerMs = 750; // Estimate 694 695 // Tiering cutoff values: if code section sizes are below these values (when 696 // divided by the effective number of cores) we do not tier, because we guess 697 // that parallel Ion compilation will be fast enough. 698 699 static const double x64DesktopTierCutoff = x64IonBytecodesPerMs * tierCutoffMs; 700 static const double x86DesktopTierCutoff = x86IonBytecodesPerMs * tierCutoffMs; 701 static const double x86MobileTierCutoff = x86DesktopTierCutoff / 2; // Guess 702 static const double arm32MobileTierCutoff = 703 arm32IonBytecodesPerMs * tierCutoffMs; 704 static const double arm64MobileTierCutoff = 705 arm64IonBytecodesPerMs * tierCutoffMs; 706 707 static double CodesizeCutoff(SystemClass cls) { 708 switch (cls) { 709 case SystemClass::DesktopX86: 710 case SystemClass::DesktopUnknown32: 711 return x86DesktopTierCutoff; 712 case SystemClass::DesktopX64: 713 case SystemClass::DesktopUnknown64: 714 return x64DesktopTierCutoff; 715 case SystemClass::MobileX86: 716 return x86MobileTierCutoff; 717 case SystemClass::MobileArm32: 718 case SystemClass::MobileUnknown32: 719 return arm32MobileTierCutoff; 720 case SystemClass::MobileArm64: 721 case SystemClass::MobileUnknown64: 722 return arm64MobileTierCutoff; 723 default: 724 MOZ_CRASH(); 725 } 726 } 727 728 // As the number of cores grows the effectiveness of each core dwindles (on the 729 // systems we care about for SpiderMonkey). 730 // 731 // The data are empirical, computed from the observed compilation time of the 732 // Tanks demo code on a variable number of cores. 733 // 734 // The heuristic may fail on NUMA systems where the core count is high but the 735 // performance increase is nil or negative once the program moves beyond one 736 // socket. However, few browser users have such systems. 737 738 static double EffectiveCores(uint32_t cores) { 739 if (cores <= 3) { 740 return pow(cores, 0.9); 741 } 742 return pow(cores, 0.75); 743 } 744 745 #ifndef JS_64BIT 746 // Don't tier if tiering will fill code memory to more to more than this 747 // fraction. 748 749 static const double spaceCutoffPct = 0.9; 750 #endif 751 752 // Figure out whether we should use tiered compilation or not. 753 static bool TieringBeneficial(bool lazyTiering, uint32_t codeSize) { 754 // Lazy tiering is assumed to always be beneficial when it is enabled. 755 if (lazyTiering) { 756 return true; 757 } 758 759 uint32_t cpuCount = GetHelperThreadCPUCount(); 760 MOZ_ASSERT(cpuCount > 0); 761 762 // It's mostly sensible not to background compile when there's only one 763 // hardware thread as we want foreground computation to have access to that. 764 // However, if wasm background compilation helper threads can be given lower 765 // priority then background compilation on single-core systems still makes 766 // some kind of sense. That said, this is a non-issue: as of September 2017 767 // 1-core was down to 3.5% of our population and falling. 768 769 if (cpuCount == 1) { 770 return false; 771 } 772 773 // Compute the max number of threads available to do actual background 774 // compilation work. 775 776 uint32_t workers = GetMaxWasmCompilationThreads(); 777 778 // The number of cores we will use is bounded both by the CPU count and the 779 // worker count, since the worker count already takes this into account. 780 781 uint32_t cores = workers; 782 783 SystemClass cls = ClassifySystem(); 784 785 // Ion compilation on available cores must take long enough to be worth the 786 // bother. 787 788 double cutoffSize = CodesizeCutoff(cls); 789 double effectiveCores = EffectiveCores(cores); 790 791 if ((codeSize / effectiveCores) < cutoffSize) { 792 return false; 793 } 794 795 // Do not implement a size cutoff for 64-bit systems since the code size 796 // budget for 64 bit is so large that it will hardly ever be an issue. 797 // (Also the cutoff percentage might be different on 64-bit.) 798 799 #ifndef JS_64BIT 800 // If the amount of executable code for baseline compilation jeopardizes the 801 // availability of executable memory for ion code then do not tier, for now. 802 // 803 // TODO: For now we consider this module in isolation. We should really 804 // worry about what else is going on in this process and might be filling up 805 // the code memory. It's like we need some kind of code memory reservation 806 // system or JIT compilation for large modules. 807 808 double ionRatio = OptimizedBytesPerBytecode(cls); 809 double baselineRatio = BaselineBytesPerBytecode(cls); 810 double needMemory = codeSize * (ionRatio + baselineRatio); 811 double availMemory = LikelyAvailableExecutableMemory(); 812 double cutoff = spaceCutoffPct * MaxCodeBytesPerProcess; 813 814 // If the sum of baseline and ion code makes us exceeds some set percentage 815 // of the executable memory then disable tiering. 816 817 if ((MaxCodeBytesPerProcess - availMemory) + needMemory > cutoff) { 818 return false; 819 } 820 #endif 821 822 return true; 823 } 824 825 // Ensure that we have the non-compiler requirements to tier safely. 826 static bool PlatformCanTier(bool lazyTiering) { 827 // Note: ensure this function stays in sync with `WasmLazyTieringEnabled()`. 828 // Tiering needs background threads if we're using eager tiering or we're 829 // using lazy tiering without the synchronous flag. 830 bool synchronousTiering = 831 lazyTiering && JS::Prefs::wasm_lazy_tiering_synchronous(); 832 833 return (synchronousTiering || CanUseExtraThreads()) && 834 jit::CanFlushExecutionContextForAllThreads(); 835 } 836 837 CompilerEnvironment::CompilerEnvironment(const CompileArgs& args) 838 : state_(InitialWithArgs), args_(&args) {} 839 840 CompilerEnvironment::CompilerEnvironment(CompileMode mode, Tier tier, 841 DebugEnabled debugEnabled) 842 : state_(InitialWithModeTierDebug), 843 mode_(mode), 844 tier_(tier), 845 debug_(debugEnabled) {} 846 847 void CompilerEnvironment::computeParameters() { 848 MOZ_ASSERT(state_ == InitialWithModeTierDebug); 849 850 state_ = Computed; 851 } 852 853 void CompilerEnvironment::computeParameters(const ModuleMetadata& moduleMeta) { 854 MOZ_ASSERT(!isComputed()); 855 856 if (state_ == InitialWithModeTierDebug) { 857 computeParameters(); 858 return; 859 } 860 861 bool baselineEnabled = args_->baselineEnabled; 862 bool ionEnabled = args_->ionEnabled; 863 bool debugEnabled = args_->debugEnabled; 864 bool forceTiering = args_->forceTiering; 865 866 bool hasSecondTier = ionEnabled; 867 MOZ_ASSERT_IF(debugEnabled, baselineEnabled); 868 MOZ_ASSERT_IF(forceTiering, baselineEnabled && hasSecondTier); 869 870 // Various constraints in various places should prevent failure here. 871 MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled); 872 873 bool isGcModule = moduleMeta.codeMeta->types->hasGcType(); 874 uint32_t codeSectionSize = moduleMeta.codeMeta->codeSectionSize(); 875 876 // We use lazy tiering if the 'for-all' pref is enabled, or the 'gc-only' 877 // pref is enabled and we're compiling a GC module. However, forcing 878 // serialization-testing disables lazy tiering. 879 bool testSerialization = args_->features.testSerialization; 880 bool lazyTiering = (JS::Prefs::wasm_lazy_tiering() || 881 (JS::Prefs::wasm_lazy_tiering_for_gc() && isGcModule)) && 882 !testSerialization; 883 884 if (baselineEnabled && hasSecondTier && 885 (TieringBeneficial(lazyTiering, codeSectionSize) || forceTiering) && 886 PlatformCanTier(lazyTiering)) { 887 mode_ = lazyTiering ? CompileMode::LazyTiering : CompileMode::EagerTiering; 888 tier_ = Tier::Baseline; 889 } else { 890 mode_ = CompileMode::Once; 891 tier_ = hasSecondTier ? Tier::Optimized : Tier::Baseline; 892 } 893 894 debug_ = debugEnabled ? DebugEnabled::True : DebugEnabled::False; 895 896 state_ = Computed; 897 } 898 899 template <class DecoderT, class ModuleGeneratorT> 900 static bool DecodeFunctionBody(DecoderT& d, ModuleGeneratorT& mg, 901 uint32_t funcIndex) { 902 uint32_t bodySize; 903 if (!d.readVarU32(&bodySize)) { 904 return d.fail("expected number of function body bytes"); 905 } 906 907 if (bodySize > MaxFunctionBytes) { 908 return d.fail("function body too big"); 909 } 910 911 const size_t offsetInModule = d.currentOffset(); 912 913 // Skip over the function body; it will be validated by the compilation 914 // thread. 915 const uint8_t* bodyBegin; 916 if (!d.readBytes(bodySize, &bodyBegin)) { 917 return d.fail("function body length too big"); 918 } 919 920 return mg.compileFuncDef(funcIndex, offsetInModule, bodyBegin, 921 bodyBegin + bodySize); 922 } 923 924 template <class DecoderT, class ModuleGeneratorT> 925 static bool DecodeCodeSection(const CodeMetadata& codeMeta, DecoderT& d, 926 ModuleGeneratorT& mg) { 927 if (!codeMeta.codeSectionRange) { 928 if (codeMeta.numFuncDefs() != 0) { 929 return d.fail("expected code section"); 930 } 931 932 return mg.finishFuncDefs(); 933 } 934 935 uint32_t numFuncDefs; 936 if (!d.readVarU32(&numFuncDefs)) { 937 return d.fail("expected function body count"); 938 } 939 940 if (numFuncDefs != codeMeta.numFuncDefs()) { 941 return d.fail( 942 "function body count does not match function signature count"); 943 } 944 945 for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) { 946 if (!DecodeFunctionBody(d, mg, codeMeta.numFuncImports + funcDefIndex)) { 947 return false; 948 } 949 } 950 951 if (!d.finishSection(*codeMeta.codeSectionRange, "code")) { 952 return false; 953 } 954 955 return mg.finishFuncDefs(); 956 } 957 958 SharedModule wasm::CompileBuffer(const CompileArgs& args, 959 const BytecodeBufferOrSource& bytecode, 960 UniqueChars* error, 961 UniqueCharsVector* warnings, 962 JS::OptimizedEncodingListener* listener) { 963 MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>(); 964 if (!moduleMeta || !moduleMeta->init(args)) { 965 return nullptr; 966 } 967 968 const BytecodeSource& bytecodeSource = bytecode.source(); 969 Decoder envDecoder(bytecodeSource.envSpan(), bytecodeSource.envRange().start, 970 error, warnings); 971 if (!DecodeModuleEnvironment(envDecoder, moduleMeta->codeMeta.get(), 972 moduleMeta)) { 973 return nullptr; 974 } 975 976 CompilerEnvironment compilerEnv(args); 977 compilerEnv.computeParameters(*moduleMeta); 978 if (!moduleMeta->prepareForCompile(compilerEnv.mode())) { 979 return nullptr; 980 } 981 982 ModuleGenerator mg(*moduleMeta->codeMeta, compilerEnv, 983 compilerEnv.initialState(), nullptr, error, warnings); 984 if (!mg.initializeCompleteTier()) { 985 return nullptr; 986 } 987 988 // If our bytecode has a code section, then we must switch decoders for 989 // these section. 990 if (bytecodeSource.hasCodeSection()) { 991 // DecodeModuleEnvironment will stop and return true if there is an unknown 992 // section before the code section. We must check this and return an error. 993 if (!moduleMeta->codeMeta->codeSectionRange) { 994 envDecoder.fail("unknown section before code section"); 995 return nullptr; 996 } 997 998 // Our pre-parse that split the module should ensure that after we've 999 // parsed the environment there are no bytes left. 1000 MOZ_RELEASE_ASSERT(envDecoder.done()); 1001 1002 Decoder codeDecoder(bytecodeSource.codeSpan(), 1003 bytecodeSource.codeRange().start, error, warnings); 1004 if (!DecodeCodeSection(*moduleMeta->codeMeta, codeDecoder, mg)) { 1005 return nullptr; 1006 } 1007 // Our pre-parse that split the module should ensure that after we've 1008 // parsed the code section there are no bytes left. 1009 MOZ_RELEASE_ASSERT(codeDecoder.done()); 1010 1011 Decoder tailDecoder(bytecodeSource.tailSpan(), 1012 bytecodeSource.tailRange().start, error, warnings); 1013 if (!DecodeModuleTail(tailDecoder, moduleMeta->codeMeta, moduleMeta)) { 1014 return nullptr; 1015 } 1016 // Decoding the module tail should consume all remaining bytes. 1017 MOZ_RELEASE_ASSERT(tailDecoder.done()); 1018 } else { 1019 // We still must call this method even without a code section because it 1020 // does validation that ensure we aren't missing function definitions. 1021 if (!DecodeCodeSection(*moduleMeta->codeMeta, envDecoder, mg)) { 1022 return nullptr; 1023 } 1024 1025 if (!DecodeModuleTail(envDecoder, moduleMeta->codeMeta, moduleMeta)) { 1026 return nullptr; 1027 } 1028 1029 // Decoding the module tail should consume all remaining bytes. 1030 MOZ_RELEASE_ASSERT(envDecoder.done()); 1031 } 1032 1033 return mg.finishModule(bytecode, *moduleMeta, listener); 1034 } 1035 1036 bool wasm::CompileCompleteTier2(const ShareableBytes* codeSection, 1037 const Module& module, UniqueChars* error, 1038 UniqueCharsVector* warnings, 1039 Atomic<bool>* cancelled) { 1040 CompilerEnvironment compilerEnv(CompileMode::EagerTiering, Tier::Optimized, 1041 DebugEnabled::False); 1042 compilerEnv.computeParameters(); 1043 1044 const CodeMetadata& codeMeta = module.codeMeta(); 1045 ModuleGenerator mg(codeMeta, compilerEnv, CompileState::EagerTier2, cancelled, 1046 error, warnings); 1047 if (!mg.initializeCompleteTier()) { 1048 return false; 1049 } 1050 1051 if (codeMeta.codeSectionRange) { 1052 BytecodeSpan codeSpan(codeSection->begin(), codeSection->end()); 1053 Decoder d(codeSpan, codeMeta.codeSectionRange->start, error); 1054 if (!DecodeCodeSection(module.codeMeta(), d, mg)) { 1055 return false; 1056 } 1057 } else { 1058 MOZ_ASSERT(!codeSection); 1059 MOZ_ASSERT(codeMeta.numFuncDefs() == 0); 1060 if (!mg.finishFuncDefs()) { 1061 return false; 1062 } 1063 } 1064 1065 return mg.finishTier2(module); 1066 } 1067 1068 bool wasm::CompilePartialTier2(const Code& code, uint32_t funcIndex, 1069 UniqueChars* error, UniqueCharsVector* warnings, 1070 mozilla::Atomic<bool>* cancelled) { 1071 CompilerEnvironment compilerEnv(CompileMode::LazyTiering, Tier::Optimized, 1072 DebugEnabled::False); 1073 compilerEnv.computeParameters(); 1074 1075 const CodeMetadata& codeMeta = code.codeMeta(); 1076 ModuleGenerator mg(codeMeta, compilerEnv, CompileState::LazyTier2, cancelled, 1077 error, warnings); 1078 if (!mg.initializePartialTier(code, funcIndex)) { 1079 // The module is already validated, so this can only be an OOM. 1080 MOZ_ASSERT(!*error); 1081 return false; 1082 } 1083 1084 const BytecodeRange& funcRange = code.codeTailMeta().funcDefRange(funcIndex); 1085 BytecodeSpan funcBytecode = code.codeTailMeta().funcDefBody(funcIndex); 1086 1087 // The following sequence will compile/finish this function, on this thread. 1088 // `error` (as stashed in `mg`) may get set to, for example, "stack frame too 1089 // large", or to "", denoting OOM. 1090 return mg.compileFuncDef(funcIndex, funcRange.start, funcBytecode.data(), 1091 funcBytecode.data() + funcBytecode.size()) && 1092 mg.finishFuncDefs() && mg.finishPartialTier2(); 1093 } 1094 1095 class StreamingDecoder { 1096 Decoder d_; 1097 const ExclusiveBytesPtr& codeBytesEnd_; 1098 const Atomic<bool>& cancelled_; 1099 1100 public: 1101 StreamingDecoder(const CodeMetadata& codeMeta, const Bytes& begin, 1102 const ExclusiveBytesPtr& codeBytesEnd, 1103 const Atomic<bool>& cancelled, UniqueChars* error, 1104 UniqueCharsVector* warnings) 1105 : d_(begin, codeMeta.codeSectionRange->start, error, warnings), 1106 codeBytesEnd_(codeBytesEnd), 1107 cancelled_(cancelled) {} 1108 1109 bool fail(const char* msg) { return d_.fail(msg); } 1110 1111 bool done() const { return d_.done(); } 1112 1113 size_t currentOffset() const { return d_.currentOffset(); } 1114 1115 bool waitForBytes(size_t numBytes) { 1116 numBytes = std::min(numBytes, d_.bytesRemain()); 1117 const uint8_t* requiredEnd = d_.currentPosition() + numBytes; 1118 auto codeBytesEnd = codeBytesEnd_.lock(); 1119 while (codeBytesEnd < requiredEnd) { 1120 if (cancelled_) { 1121 return false; 1122 } 1123 codeBytesEnd.wait(); 1124 } 1125 return true; 1126 } 1127 1128 bool readVarU32(uint32_t* u32) { 1129 return waitForBytes(MaxVarU32DecodedBytes) && d_.readVarU32(u32); 1130 } 1131 1132 bool readBytes(size_t size, const uint8_t** begin) { 1133 return waitForBytes(size) && d_.readBytes(size, begin); 1134 } 1135 1136 bool finishSection(const BytecodeRange& range, const char* name) { 1137 return d_.finishSection(range, name); 1138 } 1139 }; 1140 1141 SharedModule wasm::CompileStreaming( 1142 const CompileArgs& args, const ShareableBytes& envBytes, 1143 const ShareableBytes& codeBytes, const ExclusiveBytesPtr& codeBytesEnd, 1144 const ExclusiveStreamEndData& exclusiveStreamEnd, 1145 const Atomic<bool>& cancelled, UniqueChars* error, 1146 UniqueCharsVector* warnings) { 1147 CompilerEnvironment compilerEnv(args); 1148 MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>(); 1149 if (!moduleMeta || !moduleMeta->init(args)) { 1150 return nullptr; 1151 } 1152 CodeMetadata& codeMeta = *moduleMeta->codeMeta; 1153 1154 { 1155 Decoder d(envBytes.vector, 0, error, warnings); 1156 1157 if (!DecodeModuleEnvironment(d, &codeMeta, moduleMeta)) { 1158 return nullptr; 1159 } 1160 compilerEnv.computeParameters(*moduleMeta); 1161 1162 if (!codeMeta.codeSectionRange) { 1163 d.fail("unknown section before code section"); 1164 return nullptr; 1165 } 1166 1167 MOZ_RELEASE_ASSERT(codeMeta.codeSectionRange->size() == codeBytes.length()); 1168 MOZ_RELEASE_ASSERT(d.done()); 1169 } 1170 1171 if (!moduleMeta->prepareForCompile(compilerEnv.mode())) { 1172 return nullptr; 1173 } 1174 1175 ModuleGenerator mg(codeMeta, compilerEnv, compilerEnv.initialState(), 1176 &cancelled, error, warnings); 1177 if (!mg.initializeCompleteTier()) { 1178 return nullptr; 1179 } 1180 1181 { 1182 StreamingDecoder d(codeMeta, codeBytes.vector, codeBytesEnd, cancelled, 1183 error, warnings); 1184 1185 if (!DecodeCodeSection(codeMeta, d, mg)) { 1186 return nullptr; 1187 } 1188 1189 MOZ_RELEASE_ASSERT(d.done()); 1190 } 1191 1192 { 1193 auto streamEnd = exclusiveStreamEnd.lock(); 1194 while (!streamEnd->reached) { 1195 if (cancelled) { 1196 return nullptr; 1197 } 1198 streamEnd.wait(); 1199 } 1200 } 1201 1202 const StreamEndData streamEnd = exclusiveStreamEnd.lock(); 1203 const ShareableBytes& tailBytes = *streamEnd.tailBytes; 1204 1205 { 1206 Decoder d(tailBytes.vector, codeMeta.codeSectionRange->end, error, 1207 warnings); 1208 1209 if (!DecodeModuleTail(d, &codeMeta, moduleMeta)) { 1210 return nullptr; 1211 } 1212 1213 MOZ_RELEASE_ASSERT(d.done()); 1214 } 1215 1216 BytecodeBuffer bytecodeBuffer(&envBytes, &codeBytes, &tailBytes); 1217 return mg.finishModule(BytecodeBufferOrSource(bytecodeBuffer), *moduleMeta, 1218 streamEnd.completeTier2Listener); 1219 } 1220 1221 class DumpIonModuleGenerator { 1222 private: 1223 const CompilerEnvironment& compilerEnv_; 1224 CodeMetadata& codeMeta_; 1225 uint32_t targetFuncIndex_; 1226 GenericPrinter& out_; 1227 UniqueChars* error_; 1228 1229 public: 1230 DumpIonModuleGenerator(const CompilerEnvironment& compilerEnv, 1231 CodeMetadata& codeMeta, uint32_t targetFuncIndex, 1232 GenericPrinter& out, UniqueChars* error) 1233 : compilerEnv_(compilerEnv), 1234 codeMeta_(codeMeta), 1235 targetFuncIndex_(targetFuncIndex), 1236 out_(out), 1237 error_(error) {} 1238 1239 bool finishFuncDefs() { return true; } 1240 bool compileFuncDef(uint32_t funcIndex, uint32_t lineOrBytecode, 1241 const uint8_t* begin, const uint8_t* end) { 1242 if (funcIndex != targetFuncIndex_) { 1243 return true; 1244 } 1245 1246 FuncCompileInput input(funcIndex, lineOrBytecode, begin, end, 1247 Uint32Vector()); 1248 return IonDumpFunction(compilerEnv_, codeMeta_, input, out_, error_); 1249 } 1250 }; 1251 1252 bool wasm::DumpIonFunctionInModule(const ShareableBytes& bytecode, 1253 uint32_t targetFuncIndex, 1254 GenericPrinter& out, UniqueChars* error) { 1255 SharedCompileArgs compileArgs = 1256 CompileArgs::buildForValidation(FeatureArgs::allEnabled()); 1257 if (!compileArgs) { 1258 return false; 1259 } 1260 CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized, 1261 DebugEnabled::False); 1262 compilerEnv.computeParameters(); 1263 1264 UniqueCharsVector warnings; 1265 Decoder d(bytecode.span(), 0, error, &warnings); 1266 MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>(); 1267 if (!moduleMeta || !moduleMeta->init(*compileArgs)) { 1268 return false; 1269 } 1270 1271 if (!DecodeModuleEnvironment(d, moduleMeta->codeMeta, moduleMeta)) { 1272 return false; 1273 } 1274 1275 DumpIonModuleGenerator mg(compilerEnv, *moduleMeta->codeMeta, targetFuncIndex, 1276 out, error); 1277 return moduleMeta->prepareForCompile(CompileMode::Once) && 1278 DecodeCodeSection(*moduleMeta->codeMeta, d, mg); 1279 }