Source code

Revision control

Other Tools

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
*
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 "mozilla/Maybe.h"
22
#include "mozilla/Unused.h"
23
24
#include <algorithm>
25
26
#include "jit/ProcessExecutableMemory.h"
27
#include "util/Text.h"
28
#include "wasm/WasmBaselineCompile.h"
29
#include "wasm/WasmCraneliftCompile.h"
30
#include "wasm/WasmGenerator.h"
31
#include "wasm/WasmIonCompile.h"
32
#include "wasm/WasmOpIter.h"
33
#include "wasm/WasmProcess.h"
34
#include "wasm/WasmSignalHandlers.h"
35
#include "wasm/WasmValidate.h"
36
37
using namespace js;
38
using namespace js::jit;
39
using namespace js::wasm;
40
41
uint32_t wasm::ObservedCPUFeatures() {
42
enum Arch {
43
X86 = 0x1,
44
X64 = 0x2,
45
ARM = 0x3,
46
MIPS = 0x4,
47
MIPS64 = 0x5,
48
ARM64 = 0x6,
49
ARCH_BITS = 3
50
};
51
52
#if defined(JS_CODEGEN_X86)
53
MOZ_ASSERT(uint32_t(jit::CPUInfo::GetSSEVersion()) <=
54
(UINT32_MAX >> ARCH_BITS));
55
return X86 | (uint32_t(jit::CPUInfo::GetSSEVersion()) << ARCH_BITS);
56
#elif defined(JS_CODEGEN_X64)
57
MOZ_ASSERT(uint32_t(jit::CPUInfo::GetSSEVersion()) <=
58
(UINT32_MAX >> ARCH_BITS));
59
return X64 | (uint32_t(jit::CPUInfo::GetSSEVersion()) << ARCH_BITS);
60
#elif defined(JS_CODEGEN_ARM)
61
MOZ_ASSERT(jit::GetARMFlags() <= (UINT32_MAX >> ARCH_BITS));
62
return ARM | (jit::GetARMFlags() << ARCH_BITS);
63
#elif defined(JS_CODEGEN_ARM64)
64
MOZ_ASSERT(jit::GetARM64Flags() <= (UINT32_MAX >> ARCH_BITS));
65
return ARM64 | (jit::GetARM64Flags() << ARCH_BITS);
66
#elif defined(JS_CODEGEN_MIPS32)
67
MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
68
return MIPS | (jit::GetMIPSFlags() << ARCH_BITS);
69
#elif defined(JS_CODEGEN_MIPS64)
70
MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
71
return MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS);
72
#elif defined(JS_CODEGEN_NONE)
73
return 0;
74
#else
75
# error "unknown architecture"
76
#endif
77
}
78
79
SharedCompileArgs CompileArgs::build(JSContext* cx,
80
ScriptedCaller&& scriptedCaller) {
81
bool baseline = BaselineCanCompile() && cx->options().wasmBaseline();
82
bool ion = IonCanCompile() && cx->options().wasmIon();
83
#ifdef ENABLE_WASM_CRANELIFT
84
bool cranelift = CraneliftCanCompile() && cx->options().wasmCranelift();
85
#else
86
bool cranelift = false;
87
#endif
88
89
#ifdef ENABLE_WASM_GC
90
bool gc = cx->options().wasmGc();
91
#else
92
bool gc = false;
93
#endif
94
95
#ifdef ENABLE_WASM_BIGINT
96
bool bigInt = cx->options().isWasmBigIntEnabled();
97
#else
98
bool bigInt = false;
99
#endif
100
101
// Debug information such as source view or debug traps will require
102
// additional memory and permanently stay in baseline code, so we try to
103
// only enable it when a developer actually cares: when the debugger tab
104
// is open.
105
bool debug = cx->realm()->debuggerObservesAsmJS();
106
107
bool sharedMemory =
108
cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
109
bool forceTiering =
110
cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2;
111
112
if (debug || gc) {
113
if (!baseline) {
114
JS_ReportErrorASCII(cx, "can't use wasm debug/gc without baseline");
115
return nullptr;
116
}
117
ion = false;
118
cranelift = false;
119
}
120
121
if (forceTiering && (!baseline || (!cranelift && !ion))) {
122
// This can happen only in testing, and in this case we don't have a
123
// proper way to signal the error, so just silently override the default,
124
// instead of adding a skip-if directive to every test using debug/gc.
125
forceTiering = false;
126
}
127
128
#ifdef ENABLE_WASM_CRANELIFT
129
if (!baseline && !ion && !cranelift) {
130
if (cx->options().wasmCranelift() && !CraneliftCanCompile()) {
131
// We're forcing to use Cranelift on a platform that doesn't support it.
132
JS_ReportErrorASCII(cx, "cranelift isn't supported on this platform");
133
return nullptr;
134
}
135
}
136
#endif
137
138
// HasCompilerSupport() should prevent failure here.
139
MOZ_RELEASE_ASSERT(baseline || ion || cranelift);
140
141
CompileArgs* target = cx->new_<CompileArgs>(std::move(scriptedCaller));
142
if (!target) {
143
return nullptr;
144
}
145
146
target->baselineEnabled = baseline;
147
target->ionEnabled = ion;
148
target->craneliftEnabled = cranelift;
149
target->debugEnabled = debug;
150
target->sharedMemoryEnabled = sharedMemory;
151
target->forceTiering = forceTiering;
152
target->gcEnabled = gc;
153
target->hugeMemory = wasm::IsHugeMemoryEnabled();
154
target->bigIntEnabled = bigInt;
155
156
return target;
157
}
158
159
// Classify the current system as one of a set of recognizable classes. This
160
// really needs to get our tier-1 systems right.
161
//
162
// TODO: We don't yet have a good measure of how fast a system is. We
163
// distinguish between mobile and desktop because these are very different kinds
164
// of systems, but we could further distinguish between low / medium / high end
165
// within those major classes. If we do so, then constants below would be
166
// provided for each (class, architecture, system-tier) combination, not just
167
// (class, architecture) as now.
168
//
169
// CPU clock speed is not by itself a good predictor of system performance, as
170
// there are high-performance systems with slow clocks (recent Intel) and
171
// low-performance systems with fast clocks (older AMD). We can also use
172
// physical memory, core configuration, OS details, CPU class and family, and
173
// CPU manufacturer to disambiguate.
174
175
enum class SystemClass {
176
DesktopX86,
177
DesktopX64,
178
DesktopUnknown32,
179
DesktopUnknown64,
180
MobileX86,
181
MobileArm32,
182
MobileArm64,
183
MobileUnknown32,
184
MobileUnknown64
185
};
186
187
static SystemClass ClassifySystem() {
188
bool isDesktop;
189
190
#if defined(ANDROID) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
191
isDesktop = false;
192
#else
193
isDesktop = true;
194
#endif
195
196
if (isDesktop) {
197
#if defined(JS_CODEGEN_X64)
198
return SystemClass::DesktopX64;
199
#elif defined(JS_CODEGEN_X86)
200
return SystemClass::DesktopX86;
201
#elif defined(JS_64BIT)
202
return SystemClass::DesktopUnknown64;
203
#else
204
return SystemClass::DesktopUnknown32;
205
#endif
206
} else {
207
#if defined(JS_CODEGEN_X86)
208
return SystemClass::MobileX86;
209
#elif defined(JS_CODEGEN_ARM)
210
return SystemClass::MobileArm32;
211
#elif defined(JS_CODEGEN_ARM64)
212
return SystemClass::MobileArm64;
213
#elif defined(JS_64BIT)
214
return SystemClass::MobileUnknown64;
215
#else
216
return SystemClass::MobileUnknown32;
217
#endif
218
}
219
}
220
221
// Code sizes in machine code bytes per bytecode byte, again empirical except
222
// where marked.
223
//
224
// The Ion estimate for ARM64 is the measured Baseline value scaled by a
225
// plausible factor for optimized code.
226
227
static const double x64Tox86Inflation = 1.25;
228
229
static const double x64IonBytesPerBytecode = 2.45;
230
static const double x86IonBytesPerBytecode =
231
x64IonBytesPerBytecode * x64Tox86Inflation;
232
static const double arm32IonBytesPerBytecode = 3.3;
233
static const double arm64IonBytesPerBytecode = 3.0 / 1.4; // Estimate
234
235
static const double x64BaselineBytesPerBytecode = x64IonBytesPerBytecode * 1.43;
236
static const double x86BaselineBytesPerBytecode =
237
x64BaselineBytesPerBytecode * x64Tox86Inflation;
238
static const double arm32BaselineBytesPerBytecode =
239
arm32IonBytesPerBytecode * 1.39;
240
static const double arm64BaselineBytesPerBytecode = 3.0;
241
242
static double OptimizedBytesPerBytecode(SystemClass cls) {
243
switch (cls) {
244
case SystemClass::DesktopX86:
245
case SystemClass::MobileX86:
246
case SystemClass::DesktopUnknown32:
247
return x86IonBytesPerBytecode;
248
case SystemClass::DesktopX64:
249
case SystemClass::DesktopUnknown64:
250
return x64IonBytesPerBytecode;
251
case SystemClass::MobileArm32:
252
case SystemClass::MobileUnknown32:
253
return arm32IonBytesPerBytecode;
254
case SystemClass::MobileArm64:
255
case SystemClass::MobileUnknown64:
256
return arm64IonBytesPerBytecode;
257
default:
258
MOZ_CRASH();
259
}
260
}
261
262
static double BaselineBytesPerBytecode(SystemClass cls) {
263
switch (cls) {
264
case SystemClass::DesktopX86:
265
case SystemClass::MobileX86:
266
case SystemClass::DesktopUnknown32:
267
return x86BaselineBytesPerBytecode;
268
case SystemClass::DesktopX64:
269
case SystemClass::DesktopUnknown64:
270
return x64BaselineBytesPerBytecode;
271
case SystemClass::MobileArm32:
272
case SystemClass::MobileUnknown32:
273
return arm32BaselineBytesPerBytecode;
274
case SystemClass::MobileArm64:
275
case SystemClass::MobileUnknown64:
276
return arm64BaselineBytesPerBytecode;
277
default:
278
MOZ_CRASH();
279
}
280
}
281
282
double wasm::EstimateCompiledCodeSize(Tier tier, size_t bytecodeSize) {
283
SystemClass cls = ClassifySystem();
284
switch (tier) {
285
case Tier::Baseline:
286
return double(bytecodeSize) * BaselineBytesPerBytecode(cls);
287
case Tier::Optimized:
288
return double(bytecodeSize) * OptimizedBytesPerBytecode(cls);
289
}
290
MOZ_CRASH("bad tier");
291
}
292
293
// If parallel Ion compilation is going to take longer than this, we should
294
// tier.
295
296
static const double tierCutoffMs = 10;
297
298
// Compilation rate values are empirical except when noted, the reference
299
// systems are:
300
//
301
// Late-2013 MacBook Pro (2.6GHz 4 x hyperthreaded Haswell, Mac OS X)
302
// Late-2015 Nexus 5X (1.4GHz 4 x Cortex-A53 + 1.8GHz 2 x Cortex-A57, Android)
303
// Ca-2016 SoftIron Overdrive 1000 (1.7GHz 4 x Cortex-A57, Fedora)
304
//
305
// The rates are always per core.
306
//
307
// The estimate for ARM64 is the Baseline compilation rate on the SoftIron
308
// (because we have no Ion yet), divided by 5 to estimate Ion compile rate and
309
// then divided by 2 to make it more reasonable for consumer ARM64 systems.
310
311
static const double x64IonBytecodesPerMs = 2100;
312
static const double x86IonBytecodesPerMs = 1500;
313
static const double arm32IonBytecodesPerMs = 450;
314
static const double arm64IonBytecodesPerMs = 750; // Estimate
315
316
// Tiering cutoff values: if code section sizes are below these values (when
317
// divided by the effective number of cores) we do not tier, because we guess
318
// that parallel Ion compilation will be fast enough.
319
320
static const double x64DesktopTierCutoff = x64IonBytecodesPerMs * tierCutoffMs;
321
static const double x86DesktopTierCutoff = x86IonBytecodesPerMs * tierCutoffMs;
322
static const double x86MobileTierCutoff = x86DesktopTierCutoff / 2; // Guess
323
static const double arm32MobileTierCutoff =
324
arm32IonBytecodesPerMs * tierCutoffMs;
325
static const double arm64MobileTierCutoff =
326
arm64IonBytecodesPerMs * tierCutoffMs;
327
328
static double CodesizeCutoff(SystemClass cls) {
329
switch (cls) {
330
case SystemClass::DesktopX86:
331
case SystemClass::DesktopUnknown32:
332
return x86DesktopTierCutoff;
333
case SystemClass::DesktopX64:
334
case SystemClass::DesktopUnknown64:
335
return x64DesktopTierCutoff;
336
case SystemClass::MobileX86:
337
return x86MobileTierCutoff;
338
case SystemClass::MobileArm32:
339
case SystemClass::MobileUnknown32:
340
return arm32MobileTierCutoff;
341
case SystemClass::MobileArm64:
342
case SystemClass::MobileUnknown64:
343
return arm64MobileTierCutoff;
344
default:
345
MOZ_CRASH();
346
}
347
}
348
349
// As the number of cores grows the effectiveness of each core dwindles (on the
350
// systems we care about for SpiderMonkey).
351
//
352
// The data are empirical, computed from the observed compilation time of the
353
// Tanks demo code on a variable number of cores.
354
//
355
// The heuristic may fail on NUMA systems where the core count is high but the
356
// performance increase is nil or negative once the program moves beyond one
357
// socket. However, few browser users have such systems.
358
359
static double EffectiveCores(uint32_t cores) {
360
if (cores <= 3) {
361
return pow(cores, 0.9);
362
}
363
return pow(cores, 0.75);
364
}
365
366
#ifndef JS_64BIT
367
// Don't tier if tiering will fill code memory to more to more than this
368
// fraction.
369
370
static const double spaceCutoffPct = 0.9;
371
#endif
372
373
// Figure out whether we should use tiered compilation or not.
374
static bool TieringBeneficial(uint32_t codeSize) {
375
uint32_t cpuCount = HelperThreadState().cpuCount;
376
MOZ_ASSERT(cpuCount > 0);
377
378
// It's mostly sensible not to background compile when there's only one
379
// hardware thread as we want foreground computation to have access to that.
380
// However, if wasm background compilation helper threads can be given lower
381
// priority then background compilation on single-core systems still makes
382
// some kind of sense. That said, this is a non-issue: as of September 2017
383
// 1-core was down to 3.5% of our population and falling.
384
385
if (cpuCount == 1) {
386
return false;
387
}
388
389
MOZ_ASSERT(HelperThreadState().threadCount >= cpuCount);
390
391
// Compute the max number of threads available to do actual background
392
// compilation work.
393
394
uint32_t workers = HelperThreadState().maxWasmCompilationThreads();
395
396
// The number of cores we will use is bounded both by the CPU count and the
397
// worker count.
398
399
uint32_t cores = std::min(cpuCount, workers);
400
401
SystemClass cls = ClassifySystem();
402
403
// Ion compilation on available cores must take long enough to be worth the
404
// bother.
405
406
double cutoffSize = CodesizeCutoff(cls);
407
double effectiveCores = EffectiveCores(cores);
408
409
if ((codeSize / effectiveCores) < cutoffSize) {
410
return false;
411
}
412
413
// Do not implement a size cutoff for 64-bit systems since the code size
414
// budget for 64 bit is so large that it will hardly ever be an issue.
415
// (Also the cutoff percentage might be different on 64-bit.)
416
417
#ifndef JS_64BIT
418
// If the amount of executable code for baseline compilation jeopardizes the
419
// availability of executable memory for ion code then do not tier, for now.
420
//
421
// TODO: For now we consider this module in isolation. We should really
422
// worry about what else is going on in this process and might be filling up
423
// the code memory. It's like we need some kind of code memory reservation
424
// system or JIT compilation for large modules.
425
426
double ionRatio = OptimizedBytesPerBytecode(cls);
427
double baselineRatio = BaselineBytesPerBytecode(cls);
428
double needMemory = codeSize * (ionRatio + baselineRatio);
429
double availMemory = LikelyAvailableExecutableMemory();
430
double cutoff = spaceCutoffPct * MaxCodeBytesPerProcess;
431
432
// If the sum of baseline and ion code makes us exceeds some set percentage
433
// of the executable memory then disable tiering.
434
435
if ((MaxCodeBytesPerProcess - availMemory) + needMemory > cutoff) {
436
return false;
437
}
438
#endif
439
440
return true;
441
}
442
443
CompilerEnvironment::CompilerEnvironment(const CompileArgs& args)
444
: state_(InitialWithArgs), args_(&args) {}
445
446
CompilerEnvironment::CompilerEnvironment(CompileMode mode, Tier tier,
447
OptimizedBackend optimizedBackend,
448
DebugEnabled debugEnabled,
449
bool multiValueConfigured,
450
bool refTypesConfigured,
451
bool gcTypesConfigured,
452
bool hugeMemory, bool bigIntConfigured)
453
: state_(InitialWithModeTierDebug),
454
mode_(mode),
455
tier_(tier),
456
optimizedBackend_(optimizedBackend),
457
debug_(debugEnabled),
458
refTypes_(refTypesConfigured),
459
gcTypes_(gcTypesConfigured),
460
multiValues_(multiValueConfigured),
461
hugeMemory_(hugeMemory),
462
bigInt_(bigIntConfigured) {}
463
464
void CompilerEnvironment::computeParameters(bool gcFeatureOptIn) {
465
MOZ_ASSERT(state_ == InitialWithModeTierDebug);
466
467
if (gcTypes_) {
468
gcTypes_ = gcFeatureOptIn;
469
}
470
state_ = Computed;
471
}
472
473
void CompilerEnvironment::computeParameters(Decoder& d, bool gcFeatureOptIn) {
474
MOZ_ASSERT(!isComputed());
475
476
if (state_ == InitialWithModeTierDebug) {
477
computeParameters(gcFeatureOptIn);
478
return;
479
}
480
481
bool gcEnabled = args_->gcEnabled && gcFeatureOptIn;
482
bool baselineEnabled = args_->baselineEnabled;
483
bool ionEnabled = args_->ionEnabled;
484
bool debugEnabled = args_->debugEnabled;
485
bool craneliftEnabled = args_->craneliftEnabled;
486
bool forceTiering = args_->forceTiering;
487
bool hugeMemory = args_->hugeMemory;
488
bool bigIntEnabled = args_->bigIntEnabled;
489
490
bool hasSecondTier = ionEnabled || craneliftEnabled;
491
MOZ_ASSERT_IF(gcEnabled || debugEnabled, baselineEnabled);
492
MOZ_ASSERT_IF(forceTiering, baselineEnabled && hasSecondTier);
493
494
// HasCompilerSupport() should prevent failure here
495
MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled || craneliftEnabled);
496
497
uint32_t codeSectionSize = 0;
498
499
SectionRange range;
500
if (StartsCodeSection(d.begin(), d.end(), &range)) {
501
codeSectionSize = range.size;
502
}
503
504
if (baselineEnabled && hasSecondTier && CanUseExtraThreads() &&
505
(TieringBeneficial(codeSectionSize) || forceTiering)) {
506
mode_ = CompileMode::Tier1;
507
tier_ = Tier::Baseline;
508
} else {
509
mode_ = CompileMode::Once;
510
tier_ = hasSecondTier ? Tier::Optimized : Tier::Baseline;
511
}
512
513
optimizedBackend_ =
514
craneliftEnabled ? OptimizedBackend::Cranelift : OptimizedBackend::Ion;
515
516
debug_ = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
517
gcTypes_ = gcEnabled;
518
#ifdef ENABLE_WASM_REFTYPES
519
refTypes_ = !craneliftEnabled;
520
#else
521
refTypes_ = false;
522
#endif
523
#ifdef ENABLE_WASM_MULTI_VALUE
524
multiValues_ = !craneliftEnabled;
525
#else
526
multiValues_ = false;
527
#endif
528
hugeMemory_ = hugeMemory;
529
bigInt_ = bigIntEnabled && !craneliftEnabled;
530
state_ = Computed;
531
}
532
533
template <class DecoderT>
534
static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg,
535
uint32_t funcIndex) {
536
uint32_t bodySize;
537
if (!d.readVarU32(&bodySize)) {
538
return d.fail("expected number of function body bytes");
539
}
540
541
if (bodySize > MaxFunctionBytes) {
542
return d.fail("function body too big");
543
}
544
545
const size_t offsetInModule = d.currentOffset();
546
547
// Skip over the function body; it will be validated by the compilation
548
// thread.
549
const uint8_t* bodyBegin;
550
if (!d.readBytes(bodySize, &bodyBegin)) {
551
return d.fail("function body length too big");
552
}
553
554
return mg.compileFuncDef(funcIndex, offsetInModule, bodyBegin,
555
bodyBegin + bodySize);
556
}
557
558
template <class DecoderT>
559
static bool DecodeCodeSection(const ModuleEnvironment& env, DecoderT& d,
560
ModuleGenerator& mg) {
561
if (!env.codeSection) {
562
if (env.numFuncDefs() != 0) {
563
return d.fail("expected code section");
564
}
565
566
return mg.finishFuncDefs();
567
}
568
569
uint32_t numFuncDefs;
570
if (!d.readVarU32(&numFuncDefs)) {
571
return d.fail("expected function body count");
572
}
573
574
if (numFuncDefs != env.numFuncDefs()) {
575
return d.fail(
576
"function body count does not match function signature count");
577
}
578
579
for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
580
if (!DecodeFunctionBody(d, mg, env.numFuncImports() + funcDefIndex)) {
581
return false;
582
}
583
}
584
585
if (!d.finishSection(*env.codeSection, "code")) {
586
return false;
587
}
588
589
return mg.finishFuncDefs();
590
}
591
592
SharedModule wasm::CompileBuffer(const CompileArgs& args,
593
const ShareableBytes& bytecode,
594
UniqueChars* error,
595
UniqueCharsVector* warnings,
596
JS::OptimizedEncodingListener* listener) {
597
Decoder d(bytecode.bytes, 0, error, warnings);
598
599
CompilerEnvironment compilerEnv(args);
600
ModuleEnvironment env(&compilerEnv, args.sharedMemoryEnabled
601
? Shareable::True
602
: Shareable::False);
603
if (!DecodeModuleEnvironment(d, &env)) {
604
return nullptr;
605
}
606
607
ModuleGenerator mg(args, &env, nullptr, error);
608
if (!mg.init()) {
609
return nullptr;
610
}
611
612
if (!DecodeCodeSection(env, d, mg)) {
613
return nullptr;
614
}
615
616
if (!DecodeModuleTail(d, &env)) {
617
return nullptr;
618
}
619
620
return mg.finishModule(bytecode, listener);
621
}
622
623
void wasm::CompileTier2(const CompileArgs& args, const Bytes& bytecode,
624
const Module& module, Atomic<bool>* cancelled) {
625
UniqueChars error;
626
Decoder d(bytecode, 0, &error);
627
628
bool gcTypesConfigured = false; // No optimized backend support yet
629
#ifdef ENABLE_WASM_REFTYPES
630
bool refTypesConfigured = !args.craneliftEnabled;
631
#else
632
bool refTypesConfigured = false;
633
#endif
634
#ifdef ENABLE_WASM_MULTI_VALUE
635
bool multiValueConfigured = !args.craneliftEnabled;
636
#else
637
bool multiValueConfigured = false;
638
#endif
639
bool bigIntConfigured = args.bigIntEnabled && !args.craneliftEnabled;
640
641
OptimizedBackend optimizedBackend = args.craneliftEnabled
642
? OptimizedBackend::Cranelift
643
: OptimizedBackend::Ion;
644
645
CompilerEnvironment compilerEnv(
646
CompileMode::Tier2, Tier::Optimized, optimizedBackend,
647
DebugEnabled::False, multiValueConfigured, refTypesConfigured,
648
gcTypesConfigured, args.hugeMemory, bigIntConfigured);
649
650
ModuleEnvironment env(&compilerEnv, args.sharedMemoryEnabled
651
? Shareable::True
652
: Shareable::False);
653
if (!DecodeModuleEnvironment(d, &env)) {
654
return;
655
}
656
657
ModuleGenerator mg(args, &env, cancelled, &error);
658
if (!mg.init()) {
659
return;
660
}
661
662
if (!DecodeCodeSection(env, d, mg)) {
663
return;
664
}
665
666
if (!DecodeModuleTail(d, &env)) {
667
return;
668
}
669
670
if (!mg.finishTier2(module)) {
671
return;
672
}
673
674
// The caller doesn't care about success or failure; only that compilation
675
// is inactive, so there is no success to return here.
676
}
677
678
class StreamingDecoder {
679
Decoder d_;
680
const ExclusiveBytesPtr& codeBytesEnd_;
681
const Atomic<bool>& cancelled_;
682
683
public:
684
StreamingDecoder(const ModuleEnvironment& env, const Bytes& begin,
685
const ExclusiveBytesPtr& codeBytesEnd,
686
const Atomic<bool>& cancelled, UniqueChars* error,
687
UniqueCharsVector* warnings)
688
: d_(begin, env.codeSection->start, error, warnings),
689
codeBytesEnd_(codeBytesEnd),
690
cancelled_(cancelled) {}
691
692
bool fail(const char* msg) { return d_.fail(msg); }
693
694
bool done() const { return d_.done(); }
695
696
size_t currentOffset() const { return d_.currentOffset(); }
697
698
bool waitForBytes(size_t numBytes) {
699
numBytes = std::min(numBytes, d_.bytesRemain());
700
const uint8_t* requiredEnd = d_.currentPosition() + numBytes;
701
auto codeBytesEnd = codeBytesEnd_.lock();
702
while (codeBytesEnd < requiredEnd) {
703
if (cancelled_) {
704
return false;
705
}
706
codeBytesEnd.wait();
707
}
708
return true;
709
}
710
711
bool readVarU32(uint32_t* u32) {
712
return waitForBytes(MaxVarU32DecodedBytes) && d_.readVarU32(u32);
713
}
714
715
bool readBytes(size_t size, const uint8_t** begin) {
716
return waitForBytes(size) && d_.readBytes(size, begin);
717
}
718
719
bool finishSection(const SectionRange& range, const char* name) {
720
return d_.finishSection(range, name);
721
}
722
};
723
724
static SharedBytes CreateBytecode(const Bytes& env, const Bytes& code,
725
const Bytes& tail, UniqueChars* error) {
726
size_t size = env.length() + code.length() + tail.length();
727
if (size > MaxModuleBytes) {
728
*error = DuplicateString("module too big");
729
return nullptr;
730
}
731
732
MutableBytes bytecode = js_new<ShareableBytes>();
733
if (!bytecode || !bytecode->bytes.resize(size)) {
734
return nullptr;
735
}
736
737
uint8_t* p = bytecode->bytes.begin();
738
739
memcpy(p, env.begin(), env.length());
740
p += env.length();
741
742
memcpy(p, code.begin(), code.length());
743
p += code.length();
744
745
memcpy(p, tail.begin(), tail.length());
746
p += tail.length();
747
748
MOZ_ASSERT(p == bytecode->end());
749
750
return bytecode;
751
}
752
753
SharedModule wasm::CompileStreaming(
754
const CompileArgs& args, const Bytes& envBytes, const Bytes& codeBytes,
755
const ExclusiveBytesPtr& codeBytesEnd,
756
const ExclusiveStreamEndData& exclusiveStreamEnd,
757
const Atomic<bool>& cancelled, UniqueChars* error,
758
UniqueCharsVector* warnings) {
759
CompilerEnvironment compilerEnv(args);
760
ModuleEnvironment env(&compilerEnv, args.sharedMemoryEnabled
761
? Shareable::True
762
: Shareable::False);
763
764
{
765
Decoder d(envBytes, 0, error, warnings);
766
767
if (!DecodeModuleEnvironment(d, &env)) {
768
return nullptr;
769
}
770
771
if (!env.codeSection) {
772
d.fail("unknown section before code section");
773
return nullptr;
774
}
775
776
MOZ_RELEASE_ASSERT(env.codeSection->size == codeBytes.length());
777
MOZ_RELEASE_ASSERT(d.done());
778
}
779
780
ModuleGenerator mg(args, &env, &cancelled, error);
781
if (!mg.init()) {
782
return nullptr;
783
}
784
785
{
786
StreamingDecoder d(env, codeBytes, codeBytesEnd, cancelled, error,
787
warnings);
788
789
if (!DecodeCodeSection(env, d, mg)) {
790
return nullptr;
791
}
792
793
MOZ_RELEASE_ASSERT(d.done());
794
}
795
796
{
797
auto streamEnd = exclusiveStreamEnd.lock();
798
while (!streamEnd->reached) {
799
if (cancelled) {
800
return nullptr;
801
}
802
streamEnd.wait();
803
}
804
}
805
806
const StreamEndData& streamEnd = exclusiveStreamEnd.lock();
807
const Bytes& tailBytes = *streamEnd.tailBytes;
808
809
{
810
Decoder d(tailBytes, env.codeSection->end(), error, warnings);
811
812
if (!DecodeModuleTail(d, &env)) {
813
return nullptr;
814
}
815
816
MOZ_RELEASE_ASSERT(d.done());
817
}
818
819
SharedBytes bytecode = CreateBytecode(envBytes, codeBytes, tailBytes, error);
820
if (!bytecode) {
821
return nullptr;
822
}
823
824
return mg.finishModule(*bytecode, streamEnd.tier2Listener);
825
}