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