Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp
Warning:line 3774, column 18
Value stored to 'exception' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_wasm3.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src -I /var/lib/jenkins/workspace/firefox-scan-build/js/src -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-07-27-022226-2793976-1 -x c++ Unified_cpp_js_src_wasm3.cpp
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/WasmIonCompile.h"
20
21#include "mozilla/DebugOnly.h"
22#include "mozilla/MathAlgorithms.h"
23
24#include <algorithm>
25
26#include "jit/ABIArgGenerator.h"
27#include "jit/CodeGenerator.h"
28#include "jit/CompileInfo.h"
29#include "jit/Ion.h"
30#include "jit/IonOptimizationLevels.h"
31#include "jit/MIR-wasm.h"
32#include "jit/MIR.h"
33#include "jit/ShuffleAnalysis.h"
34#include "js/ScalarType.h" // js::Scalar::Type
35#include "wasm/WasmBaselineCompile.h"
36#include "wasm/WasmBuiltinModule.h"
37#include "wasm/WasmBuiltins.h"
38#include "wasm/WasmCodegenTypes.h"
39#include "wasm/WasmGC.h"
40#include "wasm/WasmGcObject.h"
41#include "wasm/WasmGenerator.h"
42#include "wasm/WasmOpIter.h"
43#include "wasm/WasmSignalHandlers.h"
44#include "wasm/WasmStubs.h"
45#include "wasm/WasmValidate.h"
46
47using namespace js;
48using namespace js::jit;
49using namespace js::wasm;
50
51using mozilla::IsPowerOfTwo;
52using mozilla::Maybe;
53using mozilla::Nothing;
54using mozilla::Some;
55
56namespace {
57
58using UniqueCompileInfo = UniquePtr<CompileInfo>;
59using UniqueCompileInfoVector = Vector<UniqueCompileInfo, 1, SystemAllocPolicy>;
60
61using BlockVector = Vector<MBasicBlock*, 8, SystemAllocPolicy>;
62using DefVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
63
64// To compile try-catch blocks, we extend the IonCompilePolicy's ControlItem
65// from being just an MBasicBlock* to a Control structure collecting additional
66// information.
67using ControlInstructionVector =
68 Vector<MControlInstruction*, 8, SystemAllocPolicy>;
69
70struct TryControl {
71 // Branches to bind to the try's landing pad.
72 ControlInstructionVector landingPadPatches;
73 // For `try_table`, the list of tagged catches and labels to branch to.
74 TryTableCatchVector catches;
75 // Whether this try is in the body and should catch any thrown exception.
76 bool inBody;
77
78 TryControl() : inBody(false) {}
79
80 // Reset the try control for when it is cached in FunctionCompiler.
81 void reset() {
82 landingPadPatches.clearAndFree();
83 catches.clearAndFree();
84 inBody = false;
85 }
86};
87using UniqueTryControl = UniquePtr<TryControl>;
88using VectorUniqueTryControl = Vector<UniqueTryControl, 2, SystemAllocPolicy>;
89
90struct Control {
91 MBasicBlock* block;
92 UniqueTryControl tryControl;
93
94 Control() : block(nullptr), tryControl(nullptr) {}
95 Control(Control&&) = default;
96 Control(const Control&) = delete;
97};
98
99// [SMDOC] WebAssembly Exception Handling in Ion
100// =======================================================
101//
102// ## Throwing instructions
103//
104// Wasm exceptions can be thrown by either a throw instruction (local throw),
105// or by a wasm call.
106//
107// ## The "catching try control"
108//
109// We know we are in try-code if there is a surrounding ControlItem with
110// LabelKind::Try. The innermost such control is called the
111// "catching try control".
112//
113// ## Throws without a catching try control
114//
115// Such throws are implemented with an instance call that triggers the exception
116// unwinding runtime. The exception unwinding runtime will not return to the
117// function.
118//
119// ## "landing pad" and "pre-pad" blocks
120//
121// When an exception is thrown, the unwinder will search for the nearest
122// enclosing try block and redirect control flow to it. The code that executes
123// before any catch blocks is called the 'landing pad'. The 'landing pad' is
124// responsible to:
125// 1. Consume the pending exception state from
126// Instance::pendingException(Tag)
127// 2. Branch to the correct catch block, or else rethrow
128//
129// There is one landing pad for each try block. The immediate predecessors of
130// the landing pad are called 'pre-pad' blocks. There is one pre-pad block per
131// throwing instruction.
132//
133// ## Creating pre-pad blocks
134//
135// There are two possible sorts of pre-pad blocks, depending on whether we
136// are branching after a local throw instruction, or after a wasm call:
137//
138// - If we encounter a local throw, we create the exception and tag objects,
139// store them to Instance::pendingException(Tag), and then jump to the
140// landing pad.
141//
142// - If we encounter a wasm call, we construct a MWasmCallCatchable which is a
143// control instruction with either a branch to a fallthrough block or
144// to a pre-pad block.
145//
146// The pre-pad block for a wasm call is empty except for a jump to the
147// landing pad. It only exists to avoid critical edges which when split would
148// violate the invariants of MWasmCallCatchable. The pending exception state
149// is taken care of by the unwinder.
150//
151// Each pre-pad ends with a pending jump to the landing pad. The pending jumps
152// to the landing pad are tracked in `tryPadPatches`. These are called
153// "pad patches".
154//
155// ## Creating the landing pad
156//
157// When we exit try-code, we check if tryPadPatches has captured any control
158// instructions (pad patches). If not, we don't compile any catches and we mark
159// the rest as dead code.
160//
161// If there are pre-pad blocks, we join them to create a landing pad (or just
162// "pad"). The pad's last two slots are the caught exception, and the
163// exception's tag object.
164//
165// There are three different forms of try-catch/catch_all Wasm instructions,
166// which result in different form of landing pad.
167//
168// 1. A catchless try, so a Wasm instruction of the form "try ... end".
169// - In this case, we end the pad by rethrowing the caught exception.
170//
171// 2. A single catch_all after a try.
172// - If the first catch after a try is a catch_all, then there won't be
173// any more catches, but we need the exception and its tag object, in
174// case the code in a catch_all contains "rethrow" instructions.
175// - The Wasm instruction "rethrow", gets the exception and tag object to
176// rethrow from the last two slots of the landing pad which, due to
177// validation, is the l'th surrounding ControlItem.
178// - We immediately GoTo to a new block after the pad and pop both the
179// exception and tag object, as we don't need them anymore in this case.
180//
181// 3. Otherwise, there is one or more catch code blocks following.
182// - In this case, we construct the landing pad by creating a sequence
183// of compare and branch blocks that compare the pending exception tag
184// object to the tag object of the current tagged catch block. This is
185// done incrementally as we visit each tagged catch block in the bytecode
186// stream. At every step, we update the ControlItem's block to point to
187// the next block to be created in the landing pad sequence. The final
188// block will either be a rethrow, if there is no catch_all, or else a
189// jump to a catch_all block.
190
191struct IonCompilePolicy {
192 // We store SSA definitions in the value stack.
193 using Value = MDefinition*;
194 using ValueVector = DefVector;
195
196 // We store loop headers and then/else blocks in the control flow stack.
197 // In the case of try-catch control blocks, we collect additional information
198 // regarding the possible paths from throws and calls to a landing pad, as
199 // well as information on the landing pad's handlers (its catches).
200 using ControlItem = Control;
201};
202
203using IonOpIter = OpIter<IonCompilePolicy>;
204
205class FunctionCompiler;
206
207// CallCompileState describes a call that is being compiled.
208
209class CallCompileState {
210 // A generator object that is passed each argument as it is compiled.
211 WasmABIArgGenerator abi_;
212
213 // Accumulates the register arguments while compiling arguments.
214 MWasmCallBase::Args regArgs_;
215
216 // Reserved argument for passing Instance* to builtin instance method calls.
217 ABIArg instanceArg_;
218
219 // The stack area in which the callee will write stack return values, or
220 // nullptr if no stack results.
221 MWasmStackResultArea* stackResultArea_ = nullptr;
222
223 // Indicates that the call is a return/tail call.
224 bool returnCall = false;
225
226 // Only FunctionCompiler should be directly manipulating CallCompileState.
227 friend class FunctionCompiler;
228};
229
230// Encapsulates the compilation of a single function in an asm.js module. The
231// function compiler handles the creation and final backend compilation of the
232// MIR graph.
233class FunctionCompiler {
234 struct ControlFlowPatch {
235 MControlInstruction* ins;
236 uint32_t index;
237 ControlFlowPatch(MControlInstruction* ins, uint32_t index)
238 : ins(ins), index(index) {}
239 };
240
241 using ControlFlowPatchVector = Vector<ControlFlowPatch, 0, SystemAllocPolicy>;
242
243 struct PendingBlockTarget {
244 ControlFlowPatchVector patches;
245 BranchHint hint = BranchHint::Invalid;
246 };
247
248 using PendingBlockTargetVector =
249 Vector<PendingBlockTarget, 0, SystemAllocPolicy>;
250
251 // Inlined functions accumulate all returns to be bound to a caller function
252 // after compilation is finished.
253 struct PendingInlineReturn {
254 PendingInlineReturn(MGoto* jump, DefVector&& results)
255 : jump(jump), results(std::move(results)) {}
256
257 MGoto* jump;
258 DefVector results;
259 };
260 using PendingInlineReturnVector =
261 Vector<PendingInlineReturn, 1, SystemAllocPolicy>;
262
263 // The caller function compiler, if any, that we are being inlined into.
264 const FunctionCompiler* callerCompiler_;
265 const uint32_t inliningDepth_;
266 const CompilerEnvironment& compilerEnv_;
267 const CodeMetadata& codeMeta_;
268 IonOpIter iter_;
269 uint32_t functionBodyOffset_;
270 const FuncCompileInput& func_;
271 const ValTypeVector& locals_;
272 size_t lastReadCallSite_;
273
274 TempAllocator& alloc_;
275 MIRGraph& graph_;
276 const CompileInfo& info_;
277 MIRGenerator& mirGen_;
278
279 MBasicBlock* curBlock_;
280 uint32_t maxStackArgBytes_;
281
282 uint32_t loopDepth_;
283 uint32_t blockDepth_;
284 PendingBlockTargetVector pendingBlocks_;
285 // Control flow patches created by `delegate` instructions that target the
286 // outermost label of this function. These will be bound to a pad that will
287 // do a rethrow in `emitBodyDelegateThrowPad`.
288 ControlInstructionVector bodyDelegatePadPatches_;
289 // A vector of the returns in this function for use when we're being inlined
290 // into another function.
291 PendingInlineReturnVector pendingInlineReturns_;
292
293 // Instance pointer argument to the current function.
294 MWasmParameter* instancePointer_;
295 MWasmParameter* stackResultPointer_;
296
297 // Reference to masm.tryNotes_
298 wasm::TryNoteVector& tryNotes_;
299
300 // Reference to the top-level vector of CompileInfo to keep alive for this
301 // compilation.
302 UniqueCompileInfoVector& compileInfos_;
303
304 // Cache of TryControl to minimize heap allocations
305 VectorUniqueTryControl tryControlCache_;
306
307 public:
308 // Construct a FunctionCompiler for the top-level function of a compilation
309 FunctionCompiler(const CompilerEnvironment& compilerEnv,
310 const CodeMetadata& codeMeta, Decoder& decoder,
311 const FuncCompileInput& func, const ValTypeVector& locals,
312 MIRGenerator& mirGen, const CompileInfo& compileInfo,
313 TryNoteVector& tryNotes,
314 UniqueCompileInfoVector& compileInfos)
315 : callerCompiler_(nullptr),
316 inliningDepth_(0),
317 compilerEnv_(compilerEnv),
318 codeMeta_(codeMeta),
319 iter_(codeMeta, decoder),
320 functionBodyOffset_(decoder.beginOffset()),
321 func_(func),
322 locals_(locals),
323 lastReadCallSite_(0),
324 alloc_(mirGen.alloc()),
325 graph_(mirGen.graph()),
326 info_(compileInfo),
327 mirGen_(mirGen),
328 curBlock_(nullptr),
329 maxStackArgBytes_(0),
330 loopDepth_(0),
331 blockDepth_(0),
332 instancePointer_(nullptr),
333 stackResultPointer_(nullptr),
334 tryNotes_(tryNotes),
335 compileInfos_(compileInfos) {}
336
337 // Construct a FunctionCompiler for an inlined callee of a compilation
338 FunctionCompiler(const FunctionCompiler* callerCompiler, Decoder& decoder,
339 const FuncCompileInput& func, const ValTypeVector& locals,
340 const CompileInfo& compileInfo)
341 : callerCompiler_(callerCompiler),
342 inliningDepth_(callerCompiler_->inliningDepth() + 1),
343 compilerEnv_(callerCompiler_->compilerEnv_),
344 codeMeta_(callerCompiler_->codeMeta_),
345 iter_(codeMeta_, decoder),
346 functionBodyOffset_(decoder.beginOffset()),
347 func_(func),
348 locals_(locals),
349 lastReadCallSite_(0),
350 alloc_(callerCompiler_->alloc_),
351 graph_(callerCompiler_->graph_),
352 info_(compileInfo),
353 mirGen_(callerCompiler_->mirGen_),
354 curBlock_(nullptr),
355 maxStackArgBytes_(0),
356 loopDepth_(callerCompiler_->loopDepth_),
357 blockDepth_(0),
358 instancePointer_(callerCompiler_->instancePointer_),
359 stackResultPointer_(nullptr),
360 tryNotes_(callerCompiler_->tryNotes_),
361 compileInfos_(callerCompiler_->compileInfos_) {}
362
363 const CodeMetadata& codeMeta() const { return codeMeta_; }
364
365 IonOpIter& iter() { return iter_; }
366 uint32_t relativeBytecodeOffset() {
367 return readBytecodeOffset() - functionBodyOffset_;
368 }
369 TempAllocator& alloc() const { return alloc_; }
370 // FIXME(1401675): Replace with BlockType.
371 uint32_t funcIndex() const { return func_.index; }
372 const FuncType& funcType() const {
373 return codeMeta_.getFuncType(func_.index);
374 }
375
376 bool isInlined() const { return callerCompiler_ != nullptr; }
377 uint32_t inliningDepth() const { return inliningDepth_; }
378
379 MBasicBlock* getCurBlock() const { return curBlock_; }
380 BytecodeOffset bytecodeOffset() const { return iter_.bytecodeOffset(); }
381 BytecodeOffset bytecodeIfNotAsmJS() const {
382 return codeMeta_.isAsmJS() ? BytecodeOffset() : iter_.bytecodeOffset();
383 }
384 FeatureUsage featureUsage() const { return iter_.featureUsage(); }
385
386 // Try to get a free TryControl from the cache, or allocate a new one.
387 [[nodiscard]] UniqueTryControl newTryControl() {
388 if (tryControlCache_.empty()) {
389 return UniqueTryControl(js_new<TryControl>());
390 }
391 UniqueTryControl tryControl = std::move(tryControlCache_.back());
392 tryControlCache_.popBack();
393 return tryControl;
394 }
395
396 // Release the TryControl to the cache.
397 void freeTryControl(UniqueTryControl&& tryControl) {
398 // Ensure that it's in a consistent state
399 tryControl->reset();
400 // Ignore any OOM, as we'll fail later
401 (void)tryControlCache_.append(std::move(tryControl));
402 }
403
404 [[nodiscard]] bool initTopLevel() {
405 // Prepare the entry block for MIR generation:
406
407 const ArgTypeVector args(funcType());
408
409 if (!mirGen_.ensureBallast()) {
410 return false;
411 }
412 if (!newBlock(/* prev */ nullptr, &curBlock_)) {
413 return false;
414 }
415
416 for (WasmABIArgIter i(args); !i.done(); i++) {
417 MWasmParameter* ins = MWasmParameter::New(alloc(), *i, i.mirType());
418 curBlock_->add(ins);
419 if (args.isSyntheticStackResultPointerArg(i.index())) {
420 MOZ_ASSERT(stackResultPointer_ == nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stackResultPointer_ == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stackResultPointer_ == nullptr
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"stackResultPointer_ == nullptr", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 420); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultPointer_ == nullptr"
")"); do { *((volatile int*)__null) = 420; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
421 stackResultPointer_ = ins;
422 } else {
423 curBlock_->initSlot(info().localSlot(args.naturalIndex(i.index())),
424 ins);
425 }
426 if (!mirGen_.ensureBallast()) {
427 return false;
428 }
429 }
430
431 // Set up a parameter that receives the hidden instance pointer argument.
432 instancePointer_ =
433 MWasmParameter::New(alloc(), ABIArg(InstanceReg), MIRType::Pointer);
434 curBlock_->add(instancePointer_);
435 if (!mirGen_.ensureBallast()) {
436 return false;
437 }
438
439 for (size_t i = args.lengthWithoutStackResults(); i < locals_.length();
440 i++) {
441 ValType slotValType = locals_[i];
442#ifndef ENABLE_WASM_SIMD1
443 if (slotValType == ValType::V128) {
444 return iter().fail("Ion has no SIMD support yet");
445 }
446#endif
447 MDefinition* zero = constantZeroOfValType(slotValType);
448 curBlock_->initSlot(info().localSlot(i), zero);
449 if (!mirGen_.ensureBallast()) {
450 return false;
451 }
452 }
453
454 return true;
455 }
456
457 [[nodiscard]] bool initInline(const DefVector& argValues) {
458 // Prepare the entry block for MIR generation:
459 if (!mirGen_.ensureBallast()) {
460 return false;
461 }
462 if (!newBlock(nullptr, &curBlock_)) {
463 return false;
464 }
465
466 MBasicBlock* pred = callerCompiler_->curBlock_;
467 pred->end(MGoto::New(alloc(), curBlock_));
468 if (!curBlock_->addPredecessorWithoutPhis(pred)) {
469 return false;
470 }
471
472 // Set up args slots to point to passed argument values
473 const FuncType& type = funcType();
474 for (uint32_t argIndex = 0; argIndex < type.args().length(); argIndex++) {
475 curBlock_->initSlot(info().localSlot(argIndex), argValues[argIndex]);
476 }
477
478 // Set up a parameter that receives the hidden instance pointer argument.
479 instancePointer_ = callerCompiler_->instancePointer_;
480
481 // Initialize all local slots to zero value
482 for (size_t i = type.args().length(); i < locals_.length(); i++) {
483 ValType slotValType = locals_[i];
484#ifndef ENABLE_WASM_SIMD1
485 if (slotValType == ValType::V128) {
486 return iter().fail("Ion has no SIMD support yet");
487 }
488#endif
489 MDefinition* zero = constantZeroOfValType(slotValType);
490 curBlock_->initSlot(info().localSlot(i), zero);
491 if (!mirGen_.ensureBallast()) {
492 return false;
493 }
494 }
495
496 return true;
497 }
498
499 // Add a compile info for an inlined function. This keeps the inlined
500 // function's compile info alive for the outermost function's
501 // compilation.
502 [[nodiscard]] CompileInfo* addInlineCallInfo(uint32_t numLocals) {
503 UniqueCompileInfo compileInfo = MakeUnique<CompileInfo>(numLocals);
504 if (!compileInfo || !compileInfos_.append(std::move(compileInfo))) {
505 return nullptr;
506 }
507 return compileInfos_[compileInfos_.length() - 1].get();
508 }
509
510 void finish() {
511 mirGen().accumulateWasmMaxStackArgBytes(maxStackArgBytes_);
512
513 MOZ_ASSERT(callerCompiler_ ? (loopDepth_ == callerCompiler_->loopDepth_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callerCompiler_ ? (loopDepth_ == callerCompiler_->
loopDepth_) : (loopDepth_ == 0))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(callerCompiler_ ? (loopDepth_
== callerCompiler_->loopDepth_) : (loopDepth_ == 0)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("callerCompiler_ ? (loopDepth_ == callerCompiler_->loopDepth_) : (loopDepth_ == 0)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 514); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callerCompiler_ ? (loopDepth_ == callerCompiler_->loopDepth_) : (loopDepth_ == 0)"
")"); do { *((volatile int*)__null) = 514; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
514 : (loopDepth_ == 0))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callerCompiler_ ? (loopDepth_ == callerCompiler_->
loopDepth_) : (loopDepth_ == 0))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(callerCompiler_ ? (loopDepth_
== callerCompiler_->loopDepth_) : (loopDepth_ == 0)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("callerCompiler_ ? (loopDepth_ == callerCompiler_->loopDepth_) : (loopDepth_ == 0)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 514); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callerCompiler_ ? (loopDepth_ == callerCompiler_->loopDepth_) : (loopDepth_ == 0)"
")"); do { *((volatile int*)__null) = 514; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
515 MOZ_ASSERT(blockDepth_ == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(blockDepth_ == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(blockDepth_ == 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("blockDepth_ == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 515); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_ == 0"
")"); do { *((volatile int*)__null) = 515; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
516#ifdef DEBUG1
517 for (PendingBlockTarget& targets : pendingBlocks_) {
518 MOZ_ASSERT(targets.patches.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(targets.patches.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(targets.patches.empty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("targets.patches.empty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 518); AnnotateMozCrashReason("MOZ_ASSERT" "(" "targets.patches.empty()"
")"); do { *((volatile int*)__null) = 518; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
519 }
520#endif
521 MOZ_ASSERT(inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("inDeadCode()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 521); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 521; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
522 MOZ_ASSERT(done())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(done())>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(done()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("done()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 522); AnnotateMozCrashReason("MOZ_ASSERT" "(" "done()" ")")
; do { *((volatile int*)__null) = 522; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
523 MOZ_ASSERT(func_.callSiteLineNums.length() == lastReadCallSite_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(func_.callSiteLineNums.length() == lastReadCallSite_
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(func_.callSiteLineNums.length() == lastReadCallSite_
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"func_.callSiteLineNums.length() == lastReadCallSite_", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 523); AnnotateMozCrashReason("MOZ_ASSERT" "(" "func_.callSiteLineNums.length() == lastReadCallSite_"
")"); do { *((volatile int*)__null) = 523; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
524 }
525
526 /************************* Read-only interface (after local scope setup) */
527
528 MIRGenerator& mirGen() const { return mirGen_; }
529 MIRGraph& mirGraph() const { return graph_; }
530 const CompileInfo& info() const { return info_; }
531 const CompilerEnvironment& compilerEnv() const { return compilerEnv_; }
532
533 MDefinition* getLocalDef(unsigned slot) {
534 if (inDeadCode()) {
535 return nullptr;
536 }
537 return curBlock_->getSlot(info().localSlot(slot));
538 }
539
540 const ValTypeVector& locals() const { return locals_; }
541
542 /*********************************************************** Constants ***/
543
544 MDefinition* constantF32(float f) {
545 if (inDeadCode()) {
546 return nullptr;
547 }
548 auto* cst = MWasmFloatConstant::NewFloat32(alloc(), f);
549 curBlock_->add(cst);
550 return cst;
551 }
552 // Hide all other overloads, to guarantee no implicit argument conversion.
553 template <typename T>
554 MDefinition* constantF32(T) = delete;
555
556 MDefinition* constantF64(double d) {
557 if (inDeadCode()) {
558 return nullptr;
559 }
560 auto* cst = MWasmFloatConstant::NewDouble(alloc(), d);
561 curBlock_->add(cst);
562 return cst;
563 }
564 template <typename T>
565 MDefinition* constantF64(T) = delete;
566
567 MDefinition* constantI32(int32_t i) {
568 if (inDeadCode()) {
569 return nullptr;
570 }
571 MConstant* constant =
572 MConstant::New(alloc(), Int32Value(i), MIRType::Int32);
573 curBlock_->add(constant);
574 return constant;
575 }
576 template <typename T>
577 MDefinition* constantI32(T) = delete;
578
579 MDefinition* constantI64(int64_t i) {
580 if (inDeadCode()) {
581 return nullptr;
582 }
583 MConstant* constant = MConstant::NewInt64(alloc(), i);
584 curBlock_->add(constant);
585 return constant;
586 }
587 template <typename T>
588 MDefinition* constantI64(T) = delete;
589
590 // Produce an MConstant of the machine's target int type (Int32 or Int64).
591 MDefinition* constantTargetWord(intptr_t n) {
592 return targetIs64Bit() ? constantI64(int64_t(n)) : constantI32(int32_t(n));
593 }
594 template <typename T>
595 MDefinition* constantTargetWord(T) = delete;
596
597#ifdef ENABLE_WASM_SIMD1
598 MDefinition* constantV128(V128 v) {
599 if (inDeadCode()) {
600 return nullptr;
601 }
602 MWasmFloatConstant* constant = MWasmFloatConstant::NewSimd128(
603 alloc(), SimdConstant::CreateSimd128((int8_t*)v.bytes));
604 curBlock_->add(constant);
605 return constant;
606 }
607 template <typename T>
608 MDefinition* constantV128(T) = delete;
609#endif
610
611 MDefinition* constantNullRef() {
612 if (inDeadCode()) {
613 return nullptr;
614 }
615 // MConstant has a lot of baggage so we don't use that here.
616 MWasmNullConstant* constant = MWasmNullConstant::New(alloc());
617 curBlock_->add(constant);
618 return constant;
619 }
620
621 // Produce a zero constant for the specified ValType.
622 MDefinition* constantZeroOfValType(ValType valType) {
623 switch (valType.kind()) {
624 case ValType::I32:
625 return constantI32(0);
626 case ValType::I64:
627 return constantI64(int64_t(0));
628#ifdef ENABLE_WASM_SIMD1
629 case ValType::V128:
630 return constantV128(V128(0));
631#endif
632 case ValType::F32:
633 return constantF32(0.0f);
634 case ValType::F64:
635 return constantF64(0.0);
636 case ValType::Ref:
637 return constantNullRef();
638 default:
639 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 639); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 639; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
640 }
641 }
642
643 /***************************** Code generation (after local scope setup) */
644
645 void fence() {
646 if (inDeadCode()) {
647 return;
648 }
649 MWasmFence* ins = MWasmFence::New(alloc());
650 curBlock_->add(ins);
651 }
652
653 template <class T>
654 MDefinition* unary(MDefinition* op) {
655 if (inDeadCode()) {
656 return nullptr;
657 }
658 T* ins = T::New(alloc(), op);
659 curBlock_->add(ins);
660 return ins;
661 }
662
663 template <class T>
664 MDefinition* unary(MDefinition* op, MIRType type) {
665 if (inDeadCode()) {
666 return nullptr;
667 }
668 T* ins = T::New(alloc(), op, type);
669 curBlock_->add(ins);
670 return ins;
671 }
672
673 template <class T>
674 MDefinition* binary(MDefinition* lhs, MDefinition* rhs) {
675 if (inDeadCode()) {
676 return nullptr;
677 }
678 T* ins = T::New(alloc(), lhs, rhs);
679 curBlock_->add(ins);
680 return ins;
681 }
682
683 template <class T>
684 MDefinition* binary(MDefinition* lhs, MDefinition* rhs, MIRType type) {
685 if (inDeadCode()) {
686 return nullptr;
687 }
688 T* ins = T::New(alloc(), lhs, rhs, type);
689 curBlock_->add(ins);
690 return ins;
691 }
692
693 template <class T>
694 MDefinition* binary(MDefinition* lhs, MDefinition* rhs, MIRType type,
695 MWasmBinaryBitwise::SubOpcode subOpc) {
696 if (inDeadCode()) {
697 return nullptr;
698 }
699 T* ins = T::New(alloc(), lhs, rhs, type, subOpc);
700 curBlock_->add(ins);
701 return ins;
702 }
703
704 MDefinition* ursh(MDefinition* lhs, MDefinition* rhs, MIRType type) {
705 if (inDeadCode()) {
706 return nullptr;
707 }
708 auto* ins = MUrsh::NewWasm(alloc(), lhs, rhs, type);
709 curBlock_->add(ins);
710 return ins;
711 }
712
713 MDefinition* add(MDefinition* lhs, MDefinition* rhs, MIRType type) {
714 if (inDeadCode()) {
715 return nullptr;
716 }
717 auto* ins = MAdd::NewWasm(alloc(), lhs, rhs, type);
718 curBlock_->add(ins);
719 return ins;
720 }
721
722 bool mustPreserveNaN(MIRType type) {
723 return IsFloatingPointType(type) && !codeMeta().isAsmJS();
724 }
725
726 MDefinition* sub(MDefinition* lhs, MDefinition* rhs, MIRType type) {
727 if (inDeadCode()) {
728 return nullptr;
729 }
730
731 // wasm can't fold x - 0.0 because of NaN with custom payloads.
732 MSub* ins = MSub::NewWasm(alloc(), lhs, rhs, type, mustPreserveNaN(type));
733 curBlock_->add(ins);
734 return ins;
735 }
736
737 MDefinition* nearbyInt(MDefinition* input, RoundingMode roundingMode) {
738 if (inDeadCode()) {
739 return nullptr;
740 }
741
742 auto* ins = MNearbyInt::New(alloc(), input, input->type(), roundingMode);
743 curBlock_->add(ins);
744 return ins;
745 }
746
747 MDefinition* minMax(MDefinition* lhs, MDefinition* rhs, MIRType type,
748 bool isMax) {
749 if (inDeadCode()) {
750 return nullptr;
751 }
752
753 if (mustPreserveNaN(type)) {
754 // Convert signaling NaN to quiet NaNs.
755 MDefinition* zero = constantZeroOfValType(ValType::fromMIRType(type));
756 lhs = sub(lhs, zero, type);
757 rhs = sub(rhs, zero, type);
758 }
759
760 MMinMax* ins = MMinMax::NewWasm(alloc(), lhs, rhs, type, isMax);
761 curBlock_->add(ins);
762 return ins;
763 }
764
765 MDefinition* mul(MDefinition* lhs, MDefinition* rhs, MIRType type,
766 MMul::Mode mode) {
767 if (inDeadCode()) {
768 return nullptr;
769 }
770
771 // wasm can't fold x * 1.0 because of NaN with custom payloads.
772 auto* ins =
773 MMul::NewWasm(alloc(), lhs, rhs, type, mode, mustPreserveNaN(type));
774 curBlock_->add(ins);
775 return ins;
776 }
777
778 MDefinition* div(MDefinition* lhs, MDefinition* rhs, MIRType type,
779 bool unsignd) {
780 if (inDeadCode()) {
781 return nullptr;
782 }
783 bool trapOnError = !codeMeta().isAsmJS();
784 if (!unsignd && type == MIRType::Int32) {
785 // Enforce the signedness of the operation by coercing the operands
786 // to signed. Otherwise, operands that "look" unsigned to Ion but
787 // are not unsigned to Baldr (eg, unsigned right shifts) may lead to
788 // the operation being executed unsigned. Applies to mod() as well.
789 //
790 // Do this for Int32 only since Int64 is not subject to the same
791 // issues.
792 //
793 // Note the offsets passed to MWasmBuiltinTruncateToInt32 are wrong here,
794 // but it doesn't matter: they're not codegen'd to calls since inputs
795 // already are int32.
796 auto* lhs2 = createTruncateToInt32(lhs);
797 curBlock_->add(lhs2);
798 lhs = lhs2;
799 auto* rhs2 = createTruncateToInt32(rhs);
800 curBlock_->add(rhs2);
801 rhs = rhs2;
802 }
803
804 // For x86 and arm we implement i64 div via c++ builtin.
805 // A call to c++ builtin requires instance pointer.
806#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
807 if (type == MIRType::Int64) {
808 auto* ins =
809 MWasmBuiltinDivI64::New(alloc(), lhs, rhs, instancePointer_, unsignd,
810 trapOnError, bytecodeOffset());
811 curBlock_->add(ins);
812 return ins;
813 }
814#endif
815
816 auto* ins = MDiv::New(alloc(), lhs, rhs, type, unsignd, trapOnError,
817 bytecodeOffset(), mustPreserveNaN(type));
818 curBlock_->add(ins);
819 return ins;
820 }
821
822 MInstruction* createTruncateToInt32(MDefinition* op) {
823 if (op->type() == MIRType::Double || op->type() == MIRType::Float32) {
824 return MWasmBuiltinTruncateToInt32::New(alloc(), op, instancePointer_);
825 }
826
827 return MTruncateToInt32::New(alloc(), op);
828 }
829
830 MDefinition* mod(MDefinition* lhs, MDefinition* rhs, MIRType type,
831 bool unsignd) {
832 if (inDeadCode()) {
833 return nullptr;
834 }
835 bool trapOnError = !codeMeta().isAsmJS();
836 if (!unsignd && type == MIRType::Int32) {
837 // See block comment in div().
838 auto* lhs2 = createTruncateToInt32(lhs);
839 curBlock_->add(lhs2);
840 lhs = lhs2;
841 auto* rhs2 = createTruncateToInt32(rhs);
842 curBlock_->add(rhs2);
843 rhs = rhs2;
844 }
845
846 // For x86 and arm we implement i64 mod via c++ builtin.
847 // A call to c++ builtin requires instance pointer.
848#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
849 if (type == MIRType::Int64) {
850 auto* ins =
851 MWasmBuiltinModI64::New(alloc(), lhs, rhs, instancePointer_, unsignd,
852 trapOnError, bytecodeOffset());
853 curBlock_->add(ins);
854 return ins;
855 }
856#endif
857
858 // Should be handled separately because we call BuiltinThunk for this case
859 // and so, need to add the dependency from instancePointer.
860 if (type == MIRType::Double) {
861 auto* ins = MWasmBuiltinModD::New(alloc(), lhs, rhs, instancePointer_,
862 type, bytecodeOffset());
863 curBlock_->add(ins);
864 return ins;
865 }
866
867 auto* ins = MMod::New(alloc(), lhs, rhs, type, unsignd, trapOnError,
868 bytecodeOffset());
869 curBlock_->add(ins);
870 return ins;
871 }
872
873 MDefinition* bitnot(MDefinition* op) {
874 if (inDeadCode()) {
875 return nullptr;
876 }
877 auto* ins = MBitNot::New(alloc(), op);
878 curBlock_->add(ins);
879 return ins;
880 }
881
882 MDefinition* select(MDefinition* trueExpr, MDefinition* falseExpr,
883 MDefinition* condExpr) {
884 if (inDeadCode()) {
885 return nullptr;
886 }
887 auto* ins = MWasmSelect::New(alloc(), trueExpr, falseExpr, condExpr);
888 curBlock_->add(ins);
889 return ins;
890 }
891
892 MDefinition* extendI32(MDefinition* op, bool isUnsigned) {
893 if (inDeadCode()) {
894 return nullptr;
895 }
896 auto* ins = MExtendInt32ToInt64::New(alloc(), op, isUnsigned);
897 curBlock_->add(ins);
898 return ins;
899 }
900
901 MDefinition* signExtend(MDefinition* op, uint32_t srcSize,
902 uint32_t targetSize) {
903 if (inDeadCode()) {
904 return nullptr;
905 }
906 MInstruction* ins;
907 switch (targetSize) {
908 case 4: {
909 MSignExtendInt32::Mode mode;
910 switch (srcSize) {
911 case 1:
912 mode = MSignExtendInt32::Byte;
913 break;
914 case 2:
915 mode = MSignExtendInt32::Half;
916 break;
917 default:
918 MOZ_CRASH("Bad sign extension")do { do { } while (false); MOZ_ReportCrash("" "Bad sign extension"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 918); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 918; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
919 }
920 ins = MSignExtendInt32::New(alloc(), op, mode);
921 break;
922 }
923 case 8: {
924 MSignExtendInt64::Mode mode;
925 switch (srcSize) {
926 case 1:
927 mode = MSignExtendInt64::Byte;
928 break;
929 case 2:
930 mode = MSignExtendInt64::Half;
931 break;
932 case 4:
933 mode = MSignExtendInt64::Word;
934 break;
935 default:
936 MOZ_CRASH("Bad sign extension")do { do { } while (false); MOZ_ReportCrash("" "Bad sign extension"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 936); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 936; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
937 }
938 ins = MSignExtendInt64::New(alloc(), op, mode);
939 break;
940 }
941 default: {
942 MOZ_CRASH("Bad sign extension")do { do { } while (false); MOZ_ReportCrash("" "Bad sign extension"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 942); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 942; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
943 }
944 }
945 curBlock_->add(ins);
946 return ins;
947 }
948
949 MDefinition* convertI64ToFloatingPoint(MDefinition* op, MIRType type,
950 bool isUnsigned) {
951 if (inDeadCode()) {
952 return nullptr;
953 }
954#if defined(JS_CODEGEN_ARM)
955 auto* ins = MBuiltinInt64ToFloatingPoint::New(
956 alloc(), op, instancePointer_, type, bytecodeOffset(), isUnsigned);
957#else
958 auto* ins = MInt64ToFloatingPoint::New(alloc(), op, type, bytecodeOffset(),
959 isUnsigned);
960#endif
961 curBlock_->add(ins);
962 return ins;
963 }
964
965 MDefinition* rotate(MDefinition* input, MDefinition* count, MIRType type,
966 bool left) {
967 if (inDeadCode()) {
968 return nullptr;
969 }
970 auto* ins = MRotate::New(alloc(), input, count, type, left);
971 curBlock_->add(ins);
972 return ins;
973 }
974
975 template <class T>
976 MDefinition* truncate(MDefinition* op, TruncFlags flags) {
977 if (inDeadCode()) {
978 return nullptr;
979 }
980 auto* ins = T::New(alloc(), op, flags, bytecodeOffset());
981 curBlock_->add(ins);
982 return ins;
983 }
984
985#if defined(JS_CODEGEN_ARM)
986 MDefinition* truncateWithInstance(MDefinition* op, TruncFlags flags) {
987 if (inDeadCode()) {
988 return nullptr;
989 }
990 auto* ins = MWasmBuiltinTruncateToInt64::New(alloc(), op, instancePointer_,
991 flags, bytecodeOffset());
992 curBlock_->add(ins);
993 return ins;
994 }
995#endif
996
997 MDefinition* compare(MDefinition* lhs, MDefinition* rhs, JSOp op,
998 MCompare::CompareType type) {
999 if (inDeadCode()) {
1000 return nullptr;
1001 }
1002 auto* ins = MCompare::NewWasm(alloc(), lhs, rhs, op, type);
1003 curBlock_->add(ins);
1004 return ins;
1005 }
1006
1007 void assign(unsigned slot, MDefinition* def) {
1008 if (inDeadCode()) {
1009 return;
1010 }
1011 curBlock_->setSlot(info().localSlot(slot), def);
1012 }
1013
1014 MDefinition* compareIsNull(MDefinition* ref, JSOp compareOp) {
1015 MDefinition* nullVal = constantNullRef();
1016 if (!nullVal) {
1017 return nullptr;
1018 }
1019 return compare(ref, nullVal, compareOp, MCompare::Compare_WasmAnyRef);
1020 }
1021
1022 [[nodiscard]] bool refAsNonNull(MDefinition* ref) {
1023 if (inDeadCode()) {
1024 return true;
1025 }
1026
1027 auto* ins = MWasmTrapIfNull::New(
1028 alloc(), ref, wasm::Trap::NullPointerDereference, bytecodeOffset());
1029
1030 curBlock_->add(ins);
1031 return true;
1032 }
1033
1034#ifdef ENABLE_WASM_GC1
1035 [[nodiscard]] bool brOnNull(uint32_t relativeDepth, const DefVector& values,
1036 const ResultType& type, MDefinition* condition) {
1037 if (inDeadCode()) {
1038 return true;
1039 }
1040
1041 MBasicBlock* fallthroughBlock = nullptr;
1042 if (!newBlock(curBlock_, &fallthroughBlock)) {
1043 return false;
1044 }
1045
1046 MDefinition* check = compareIsNull(condition, JSOp::Eq);
1047 if (!check) {
1048 return false;
1049 }
1050 MTest* test = MTest::New(alloc(), check, nullptr, fallthroughBlock);
1051 if (!test ||
1052 !addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) {
1053 return false;
1054 }
1055
1056 if (!pushDefs(values)) {
1057 return false;
1058 }
1059
1060 curBlock_->end(test);
1061 curBlock_ = fallthroughBlock;
1062 return true;
1063 }
1064
1065 [[nodiscard]] bool brOnNonNull(uint32_t relativeDepth,
1066 const DefVector& values,
1067 const ResultType& type,
1068 MDefinition* condition) {
1069 if (inDeadCode()) {
1070 return true;
1071 }
1072
1073 MBasicBlock* fallthroughBlock = nullptr;
1074 if (!newBlock(curBlock_, &fallthroughBlock)) {
1075 return false;
1076 }
1077
1078 MDefinition* check = compareIsNull(condition, JSOp::Ne);
1079 if (!check) {
1080 return false;
1081 }
1082 MTest* test = MTest::New(alloc(), check, nullptr, fallthroughBlock);
1083 if (!test ||
1084 !addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) {
1085 return false;
1086 }
1087
1088 if (!pushDefs(values)) {
1089 return false;
1090 }
1091
1092 curBlock_->end(test);
1093 curBlock_ = fallthroughBlock;
1094 return true;
1095 }
1096
1097#endif // ENABLE_WASM_GC
1098
1099#ifdef ENABLE_WASM_GC1
1100 MDefinition* refI31(MDefinition* input) {
1101 auto* ins = MWasmNewI31Ref::New(alloc(), input);
1102 curBlock_->add(ins);
1103 return ins;
1104 }
1105
1106 MDefinition* i31Get(MDefinition* input, FieldWideningOp wideningOp) {
1107 auto* ins = MWasmI31RefGet::New(alloc(), input, wideningOp);
1108 curBlock_->add(ins);
1109 return ins;
1110 }
1111#endif // ENABLE_WASM_GC
1112
1113#ifdef ENABLE_WASM_SIMD1
1114 // About Wasm SIMD as supported by Ion:
1115 //
1116 // The expectation is that Ion will only ever support SIMD on x86 and x64,
1117 // since ARMv7 will cease to be a tier-1 platform soon, and MIPS64 will never
1118 // implement SIMD.
1119 //
1120 // The division of the operations into MIR nodes reflects that expectation,
1121 // and is a good fit for x86/x64. Should the expectation change we'll
1122 // possibly want to re-architect the SIMD support to be a little more general.
1123 //
1124 // Most SIMD operations map directly to a single MIR node that ultimately ends
1125 // up being expanded in the macroassembler.
1126 //
1127 // Some SIMD operations that do have a complete macroassembler expansion are
1128 // open-coded into multiple MIR nodes here; in some cases that's just
1129 // convenience, in other cases it may also allow them to benefit from Ion
1130 // optimizations. The reason for the expansions will be documented by a
1131 // comment.
1132
1133 // (v128,v128) -> v128 effect-free binary operations
1134 MDefinition* binarySimd128(MDefinition* lhs, MDefinition* rhs,
1135 bool commutative, SimdOp op) {
1136 if (inDeadCode()) {
1137 return nullptr;
1138 }
1139
1140 MOZ_ASSERT(lhs->type() == MIRType::Simd128 &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::Simd128 && rhs->
type() == MIRType::Simd128)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::Simd128
&& rhs->type() == MIRType::Simd128))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1141); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1141; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1141 rhs->type() == MIRType::Simd128)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::Simd128 && rhs->
type() == MIRType::Simd128)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::Simd128
&& rhs->type() == MIRType::Simd128))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1141); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1141; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1142
1143 auto* ins = MWasmBinarySimd128::New(alloc(), lhs, rhs, commutative, op);
1144 curBlock_->add(ins);
1145 return ins;
1146 }
1147
1148 // (v128,i32) -> v128 effect-free shift operations
1149 MDefinition* shiftSimd128(MDefinition* lhs, MDefinition* rhs, SimdOp op) {
1150 if (inDeadCode()) {
1151 return nullptr;
1152 }
1153
1154 MOZ_ASSERT(lhs->type() == MIRType::Simd128 &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::Simd128 && rhs->
type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::Simd128
&& rhs->type() == MIRType::Int32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1155); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1155; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1155 rhs->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::Simd128 && rhs->
type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::Simd128
&& rhs->type() == MIRType::Int32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1155); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1155; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1156
1157 int32_t maskBits;
1158 if (MacroAssembler::MustMaskShiftCountSimd128(op, &maskBits)) {
1159 MDefinition* mask = constantI32(maskBits);
1160 auto* rhs2 = MBitAnd::New(alloc(), rhs, mask, MIRType::Int32);
1161 curBlock_->add(rhs2);
1162 rhs = rhs2;
1163 }
1164
1165 auto* ins = MWasmShiftSimd128::New(alloc(), lhs, rhs, op);
1166 curBlock_->add(ins);
1167 return ins;
1168 }
1169
1170 // (v128,scalar,imm) -> v128
1171 MDefinition* replaceLaneSimd128(MDefinition* lhs, MDefinition* rhs,
1172 uint32_t laneIndex, SimdOp op) {
1173 if (inDeadCode()) {
1174 return nullptr;
1175 }
1176
1177 MOZ_ASSERT(lhs->type() == MIRType::Simd128)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::Simd128)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::Simd128
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lhs->type() == MIRType::Simd128", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1177); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1177; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1178
1179 auto* ins = MWasmReplaceLaneSimd128::New(alloc(), lhs, rhs, laneIndex, op);
1180 curBlock_->add(ins);
1181 return ins;
1182 }
1183
1184 // (scalar) -> v128 effect-free unary operations
1185 MDefinition* scalarToSimd128(MDefinition* src, SimdOp op) {
1186 if (inDeadCode()) {
1187 return nullptr;
1188 }
1189
1190 auto* ins = MWasmScalarToSimd128::New(alloc(), src, op);
1191 curBlock_->add(ins);
1192 return ins;
1193 }
1194
1195 // (v128) -> v128 effect-free unary operations
1196 MDefinition* unarySimd128(MDefinition* src, SimdOp op) {
1197 if (inDeadCode()) {
1198 return nullptr;
1199 }
1200
1201 MOZ_ASSERT(src->type() == MIRType::Simd128)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(src->type() == MIRType::Simd128)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(src->type() == MIRType::Simd128
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"src->type() == MIRType::Simd128", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1201); AnnotateMozCrashReason("MOZ_ASSERT" "(" "src->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1201; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1202 auto* ins = MWasmUnarySimd128::New(alloc(), src, op);
1203 curBlock_->add(ins);
1204 return ins;
1205 }
1206
1207 // (v128, imm) -> scalar effect-free unary operations
1208 MDefinition* reduceSimd128(MDefinition* src, SimdOp op, ValType outType,
1209 uint32_t imm = 0) {
1210 if (inDeadCode()) {
1211 return nullptr;
1212 }
1213
1214 MOZ_ASSERT(src->type() == MIRType::Simd128)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(src->type() == MIRType::Simd128)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(src->type() == MIRType::Simd128
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"src->type() == MIRType::Simd128", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1214); AnnotateMozCrashReason("MOZ_ASSERT" "(" "src->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1214; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1215 auto* ins =
1216 MWasmReduceSimd128::New(alloc(), src, op, outType.toMIRType(), imm);
1217 curBlock_->add(ins);
1218 return ins;
1219 }
1220
1221 // (v128, v128, v128) -> v128 effect-free operations
1222 MDefinition* ternarySimd128(MDefinition* v0, MDefinition* v1, MDefinition* v2,
1223 SimdOp op) {
1224 if (inDeadCode()) {
1225 return nullptr;
1226 }
1227
1228 MOZ_ASSERT(v0->type() == MIRType::Simd128 &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v0->type() == MIRType::Simd128 && v1->
type() == MIRType::Simd128 && v2->type() == MIRType
::Simd128)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(v0->type() == MIRType::Simd128 &&
v1->type() == MIRType::Simd128 && v2->type() ==
MIRType::Simd128))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1230); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1230; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1229 v1->type() == MIRType::Simd128 &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v0->type() == MIRType::Simd128 && v1->
type() == MIRType::Simd128 && v2->type() == MIRType
::Simd128)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(v0->type() == MIRType::Simd128 &&
v1->type() == MIRType::Simd128 && v2->type() ==
MIRType::Simd128))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1230); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1230; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1230 v2->type() == MIRType::Simd128)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v0->type() == MIRType::Simd128 && v1->
type() == MIRType::Simd128 && v2->type() == MIRType
::Simd128)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(v0->type() == MIRType::Simd128 &&
v1->type() == MIRType::Simd128 && v2->type() ==
MIRType::Simd128))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1230); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1230; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1231
1232 auto* ins = MWasmTernarySimd128::New(alloc(), v0, v1, v2, op);
1233 curBlock_->add(ins);
1234 return ins;
1235 }
1236
1237 // (v128, v128, imm_v128) -> v128 effect-free operations
1238 MDefinition* shuffleSimd128(MDefinition* v1, MDefinition* v2, V128 control) {
1239 if (inDeadCode()) {
1240 return nullptr;
1241 }
1242
1243 MOZ_ASSERT(v1->type() == MIRType::Simd128)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v1->type() == MIRType::Simd128)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(v1->type() == MIRType::Simd128
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"v1->type() == MIRType::Simd128", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1243); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v1->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1243; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1244 MOZ_ASSERT(v2->type() == MIRType::Simd128)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v2->type() == MIRType::Simd128)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(v2->type() == MIRType::Simd128
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"v2->type() == MIRType::Simd128", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1244; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1245 auto* ins = BuildWasmShuffleSimd128(
1246 alloc(), reinterpret_cast<int8_t*>(control.bytes), v1, v2);
1247 curBlock_->add(ins);
1248 return ins;
1249 }
1250
1251 // Also see below for SIMD memory references
1252
1253#endif // ENABLE_WASM_SIMD
1254
1255 /************************************************ Linear memory accesses */
1256
1257 // For detailed information about memory accesses, see "Linear memory
1258 // addresses and bounds checking" in WasmMemory.cpp.
1259
1260 private:
1261 // If the platform does not have a HeapReg, load the memory base from
1262 // instance.
1263 MDefinition* maybeLoadMemoryBase(uint32_t memoryIndex) {
1264#ifdef WASM_HAS_HEAPREG1
1265 if (memoryIndex == 0) {
1266 return nullptr;
1267 }
1268#endif
1269 return memoryBase(memoryIndex);
1270 }
1271
1272 public:
1273 // A value holding the memory base, whether that's HeapReg or some other
1274 // register.
1275 MDefinition* memoryBase(uint32_t memoryIndex) {
1276 AliasSet aliases = !codeMeta_.memories[memoryIndex].canMovingGrow()
1277 ? AliasSet::None()
1278 : AliasSet::Load(AliasSet::WasmHeapMeta);
1279#ifdef WASM_HAS_HEAPREG1
1280 if (memoryIndex == 0) {
1281 MWasmHeapReg* base = MWasmHeapReg::New(alloc(), aliases);
1282 curBlock_->add(base);
1283 return base;
1284 }
1285#endif
1286 uint32_t offset =
1287 memoryIndex == 0
1288 ? Instance::offsetOfMemory0Base()
1289 : (Instance::offsetInData(
1290 codeMeta_.offsetOfMemoryInstanceData(memoryIndex) +
1291 offsetof(MemoryInstanceData, base)__builtin_offsetof(MemoryInstanceData, base)));
1292 MWasmLoadInstance* base = MWasmLoadInstance::New(
1293 alloc(), instancePointer_, offset, MIRType::Pointer, aliases);
1294 curBlock_->add(base);
1295 return base;
1296 }
1297
1298 private:
1299 // If the bounds checking strategy requires it, load the bounds check limit
1300 // from the instance.
1301 MWasmLoadInstance* maybeLoadBoundsCheckLimit(uint32_t memoryIndex,
1302 MIRType type) {
1303 MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::Int32 || type == MIRType::Int64)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(type == MIRType::Int32 || type == MIRType::Int64))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Int32 || type == MIRType::Int64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1303); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Int32 || type == MIRType::Int64"
")"); do { *((volatile int*)__null) = 1303; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1304 if (codeMeta_.hugeMemoryEnabled(memoryIndex)) {
1305 return nullptr;
1306 }
1307 uint32_t offset =
1308 memoryIndex == 0
1309 ? Instance::offsetOfMemory0BoundsCheckLimit()
1310 : (Instance::offsetInData(
1311 codeMeta_.offsetOfMemoryInstanceData(memoryIndex) +
1312 offsetof(MemoryInstanceData, boundsCheckLimit)__builtin_offsetof(MemoryInstanceData, boundsCheckLimit)));
1313 AliasSet aliases = !codeMeta_.memories[memoryIndex].canMovingGrow()
1314 ? AliasSet::None()
1315 : AliasSet::Load(AliasSet::WasmHeapMeta);
1316 auto* load = MWasmLoadInstance::New(alloc(), instancePointer_, offset, type,
1317 aliases);
1318 curBlock_->add(load);
1319 return load;
1320 }
1321
1322 // Return true if the access requires an alignment check. If so, sets
1323 // *mustAdd to true if the offset must be added to the pointer before
1324 // checking.
1325 bool needAlignmentCheck(MemoryAccessDesc* access, MDefinition* base,
1326 bool* mustAdd) {
1327 MOZ_ASSERT(!*mustAdd)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!*mustAdd)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!*mustAdd))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!*mustAdd", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*mustAdd" ")"
); do { *((volatile int*)__null) = 1327; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1328
1329 // asm.js accesses are always aligned and need no checks.
1330 if (codeMeta_.isAsmJS() || !access->isAtomic()) {
1331 return false;
1332 }
1333
1334 // If the EA is known and aligned it will need no checks.
1335 if (base->isConstant()) {
1336 // We only care about the low bits, so overflow is OK, as is chopping off
1337 // the high bits of an i64 pointer.
1338 uint32_t ptr = 0;
1339 if (isMem64(access->memoryIndex())) {
1340 ptr = uint32_t(base->toConstant()->toInt64());
1341 } else {
1342 ptr = base->toConstant()->toInt32();
1343 }
1344 if (((ptr + access->offset64()) & (access->byteSize() - 1)) == 0) {
1345 return false;
1346 }
1347 }
1348
1349 // If the offset is aligned then the EA is just the pointer, for
1350 // the purposes of this check.
1351 *mustAdd = (access->offset64() & (access->byteSize() - 1)) != 0;
1352 return true;
1353 }
1354
1355 // Fold a constant base into the offset and make the base 0, provided the
1356 // offset stays below the guard limit. The reason for folding the base into
1357 // the offset rather than vice versa is that a small offset can be ignored
1358 // by both explicit bounds checking and bounds check elimination.
1359 void foldConstantPointer(MemoryAccessDesc* access, MDefinition** base) {
1360 uint32_t offsetGuardLimit = GetMaxOffsetGuardLimit(
1361 codeMeta_.hugeMemoryEnabled(access->memoryIndex()));
1362
1363 if ((*base)->isConstant()) {
1364 uint64_t basePtr = 0;
1365 if (isMem64(access->memoryIndex())) {
1366 basePtr = uint64_t((*base)->toConstant()->toInt64());
1367 } else {
1368 basePtr = uint64_t(int64_t((*base)->toConstant()->toInt32()));
1369 }
1370
1371 uint64_t offset = access->offset64();
1372
1373 if (offset < offsetGuardLimit && basePtr < offsetGuardLimit - offset) {
1374 offset += uint32_t(basePtr);
1375 access->setOffset32(uint32_t(offset));
1376 *base = isMem64(access->memoryIndex()) ? constantI64(int64_t(0))
1377 : constantI32(0);
1378 }
1379 }
1380 }
1381
1382 // If the offset must be added because it is large or because the true EA must
1383 // be checked, compute the effective address, trapping on overflow.
1384 void maybeComputeEffectiveAddress(MemoryAccessDesc* access,
1385 MDefinition** base, bool mustAddOffset) {
1386 uint32_t offsetGuardLimit = GetMaxOffsetGuardLimit(
1387 codeMeta_.hugeMemoryEnabled(access->memoryIndex()));
1388
1389 if (access->offset64() >= offsetGuardLimit ||
1390 access->offset64() > UINT32_MAX(4294967295U) || mustAddOffset ||
1391 !JitOptions.wasmFoldOffsets) {
1392 *base = computeEffectiveAddress(*base, access);
1393 }
1394 }
1395
1396 MWasmLoadInstance* needBoundsCheck(uint32_t memoryIndex) {
1397#ifdef JS_64BIT1
1398 // For 32-bit base pointers:
1399 //
1400 // If the bounds check uses the full 64 bits of the bounds check limit, then
1401 // the base pointer must be zero-extended to 64 bits before checking and
1402 // wrapped back to 32-bits after Spectre masking. (And it's important that
1403 // the value we end up with has flowed through the Spectre mask.)
1404 //
1405 // If the memory's max size is known to be smaller than 64K pages exactly,
1406 // we can use a 32-bit check and avoid extension and wrapping.
1407 static_assert(0x100000000 % PageSize == 0);
1408 bool mem32LimitIs64Bits =
1409 isMem32(memoryIndex) &&
1410 !codeMeta_.memories[memoryIndex].boundsCheckLimitIs32Bits() &&
1411 MaxMemoryPages(codeMeta_.memories[memoryIndex].indexType()) >=
1412 Pages(0x100000000 / PageSize);
1413#else
1414 // On 32-bit platforms we have no more than 2GB memory and the limit for a
1415 // 32-bit base pointer is never a 64-bit value.
1416 bool mem32LimitIs64Bits = false;
1417#endif
1418 return maybeLoadBoundsCheckLimit(memoryIndex,
1419 mem32LimitIs64Bits || isMem64(memoryIndex)
1420 ? MIRType::Int64
1421 : MIRType::Int32);
1422 }
1423
1424 void performBoundsCheck(uint32_t memoryIndex, MDefinition** base,
1425 MWasmLoadInstance* boundsCheckLimit) {
1426 // At the outset, actualBase could be the result of pretty much any integer
1427 // operation, or it could be the load of an integer constant. If its type
1428 // is i32, we may assume the value has a canonical representation for the
1429 // platform, see doc block in MacroAssembler.h.
1430 MDefinition* actualBase = *base;
1431
1432 // Extend an i32 index value to perform a 64-bit bounds check if the memory
1433 // can be 4GB or larger.
1434 bool extendAndWrapIndex =
1435 isMem32(memoryIndex) && boundsCheckLimit->type() == MIRType::Int64;
1436 if (extendAndWrapIndex) {
1437 auto* extended = MWasmExtendU32Index::New(alloc(), actualBase);
1438 curBlock_->add(extended);
1439 actualBase = extended;
1440 }
1441
1442 auto target = memoryIndex == 0 ? MWasmBoundsCheck::Memory0
1443 : MWasmBoundsCheck::Unknown;
1444 auto* ins = MWasmBoundsCheck::New(alloc(), actualBase, boundsCheckLimit,
1445 bytecodeOffset(), target);
1446 curBlock_->add(ins);
1447 actualBase = ins;
1448
1449 // If we're masking, then we update *base to create a dependency chain
1450 // through the masked index. But we will first need to wrap the index
1451 // value if it was extended above.
1452 if (JitOptions.spectreIndexMasking) {
1453 if (extendAndWrapIndex) {
1454 auto* wrapped = MWasmWrapU32Index::New(alloc(), actualBase);
1455 curBlock_->add(wrapped);
1456 actualBase = wrapped;
1457 }
1458 *base = actualBase;
1459 }
1460 }
1461
1462 // Perform all necessary checking before a wasm heap access, based on the
1463 // attributes of the access and base pointer.
1464 //
1465 // For 64-bit indices on platforms that are limited to indices that fit into
1466 // 32 bits (all 32-bit platforms and mips64), this returns a bounds-checked
1467 // `base` that has type Int32. Lowering code depends on this and will assert
1468 // that the base has this type. See the end of this function.
1469
1470 void checkOffsetAndAlignmentAndBounds(MemoryAccessDesc* access,
1471 MDefinition** base) {
1472 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1472); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 1472; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1473 MOZ_ASSERT(!codeMeta_.isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!codeMeta_.isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!codeMeta_.isAsmJS()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!codeMeta_.isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1473; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1474
1475 // Attempt to fold an offset into a constant base pointer so as to simplify
1476 // the addressing expression. This may update *base.
1477 foldConstantPointer(access, base);
1478
1479 // Determine whether an alignment check is needed and whether the offset
1480 // must be checked too.
1481 bool mustAddOffsetForAlignmentCheck = false;
1482 bool alignmentCheck =
1483 needAlignmentCheck(access, *base, &mustAddOffsetForAlignmentCheck);
1484
1485 // If bounds checking or alignment checking requires it, compute the
1486 // effective address: add the offset into the pointer and trap on overflow.
1487 // This may update *base.
1488 maybeComputeEffectiveAddress(access, base, mustAddOffsetForAlignmentCheck);
1489
1490 // Emit the alignment check if necessary; it traps if it fails.
1491 if (alignmentCheck) {
1492 curBlock_->add(MWasmAlignmentCheck::New(
1493 alloc(), *base, access->byteSize(), bytecodeOffset()));
1494 }
1495
1496 // Emit the bounds check if necessary; it traps if it fails. This may
1497 // update *base.
1498 MWasmLoadInstance* boundsCheckLimit =
1499 needBoundsCheck(access->memoryIndex());
1500 if (boundsCheckLimit) {
1501 performBoundsCheck(access->memoryIndex(), base, boundsCheckLimit);
1502 }
1503
1504#ifndef JS_64BIT1
1505 if (isMem64(access->memoryIndex())) {
1506 // We must have had an explicit bounds check (or one was elided if it was
1507 // proved redundant), and on 32-bit systems the index will for sure fit in
1508 // 32 bits: the max memory is 2GB. So chop the index down to 32-bit to
1509 // simplify the back-end.
1510 MOZ_ASSERT((*base)->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((*base)->type() == MIRType::Int64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((*base)->type() == MIRType
::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(*base)->type() == MIRType::Int64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1510); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(*base)->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 1510; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1511 MOZ_ASSERT(!codeMeta_.hugeMemoryEnabled(access->memoryIndex()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!codeMeta_.hugeMemoryEnabled(access->memoryIndex(
)))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!codeMeta_.hugeMemoryEnabled(access->memoryIndex(
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!codeMeta_.hugeMemoryEnabled(access->memoryIndex())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.hugeMemoryEnabled(access->memoryIndex())"
")"); do { *((volatile int*)__null) = 1511; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1512 auto* chopped = MWasmWrapU32Index::New(alloc(), *base);
1513 MOZ_ASSERT(chopped->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(chopped->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(chopped->type() == MIRType
::Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("chopped->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1513); AnnotateMozCrashReason("MOZ_ASSERT" "(" "chopped->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1513; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1514 curBlock_->add(chopped);
1515 *base = chopped;
1516 }
1517#endif
1518 }
1519
1520 bool isSmallerAccessForI64(ValType result, const MemoryAccessDesc* access) {
1521 if (result == ValType::I64 && access->byteSize() <= 4) {
1522 // These smaller accesses should all be zero-extending.
1523 MOZ_ASSERT(!isSignedIntType(access->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isSignedIntType(access->type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isSignedIntType(access->
type())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!isSignedIntType(access->type())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1523); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isSignedIntType(access->type())"
")"); do { *((volatile int*)__null) = 1523; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1524 return true;
1525 }
1526 return false;
1527 }
1528
1529 public:
1530 bool isMem32(uint32_t memoryIndex) {
1531 return codeMeta_.memories[memoryIndex].indexType() == IndexType::I32;
1532 }
1533 bool isMem64(uint32_t memoryIndex) {
1534 return codeMeta_.memories[memoryIndex].indexType() == IndexType::I64;
1535 }
1536 bool hugeMemoryEnabled(uint32_t memoryIndex) {
1537 return codeMeta_.hugeMemoryEnabled(memoryIndex);
1538 }
1539
1540 // Add the offset into the pointer to yield the EA; trap on overflow.
1541 MDefinition* computeEffectiveAddress(MDefinition* base,
1542 MemoryAccessDesc* access) {
1543 if (inDeadCode()) {
1544 return nullptr;
1545 }
1546 uint64_t offset = access->offset64();
1547 if (offset == 0) {
1548 return base;
1549 }
1550 auto* ins = MWasmAddOffset::New(alloc(), base, offset, bytecodeOffset());
1551 curBlock_->add(ins);
1552 access->clearOffset();
1553 return ins;
1554 }
1555
1556 MDefinition* load(MDefinition* base, MemoryAccessDesc* access,
1557 ValType result) {
1558 if (inDeadCode()) {
1559 return nullptr;
1560 }
1561
1562 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1563 MInstruction* load = nullptr;
1564 if (codeMeta_.isAsmJS()) {
1565 MOZ_ASSERT(access->offset64() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(access->offset64() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(access->offset64() == 0))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("access->offset64() == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1565); AnnotateMozCrashReason("MOZ_ASSERT" "(" "access->offset64() == 0"
")"); do { *((volatile int*)__null) = 1565; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1566 MWasmLoadInstance* boundsCheckLimit =
1567 maybeLoadBoundsCheckLimit(access->memoryIndex(), MIRType::Int32);
1568 load = MAsmJSLoadHeap::New(alloc(), memoryBase, base, boundsCheckLimit,
1569 access->type());
1570 } else {
1571 checkOffsetAndAlignmentAndBounds(access, &base);
1572#ifndef JS_64BIT1
1573 MOZ_ASSERT(base->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1573); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1573; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1574#endif
1575 load = MWasmLoad::New(alloc(), memoryBase, base, *access,
1576 result.toMIRType());
1577 }
1578 if (!load) {
1579 return nullptr;
1580 }
1581 curBlock_->add(load);
1582 return load;
1583 }
1584
1585 void store(MDefinition* base, MemoryAccessDesc* access, MDefinition* v) {
1586 if (inDeadCode()) {
1587 return;
1588 }
1589
1590 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1591 MInstruction* store = nullptr;
1592 if (codeMeta_.isAsmJS()) {
1593 MOZ_ASSERT(access->offset64() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(access->offset64() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(access->offset64() == 0))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("access->offset64() == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1593); AnnotateMozCrashReason("MOZ_ASSERT" "(" "access->offset64() == 0"
")"); do { *((volatile int*)__null) = 1593; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1594 MWasmLoadInstance* boundsCheckLimit =
1595 maybeLoadBoundsCheckLimit(access->memoryIndex(), MIRType::Int32);
1596 store = MAsmJSStoreHeap::New(alloc(), memoryBase, base, boundsCheckLimit,
1597 access->type(), v);
1598 } else {
1599 checkOffsetAndAlignmentAndBounds(access, &base);
1600#ifndef JS_64BIT1
1601 MOZ_ASSERT(base->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1601); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1601; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1602#endif
1603 store = MWasmStore::New(alloc(), memoryBase, base, *access, v);
1604 }
1605 if (!store) {
1606 return;
1607 }
1608 curBlock_->add(store);
1609 }
1610
1611 MDefinition* atomicCompareExchangeHeap(MDefinition* base,
1612 MemoryAccessDesc* access,
1613 ValType result, MDefinition* oldv,
1614 MDefinition* newv) {
1615 if (inDeadCode()) {
1616 return nullptr;
1617 }
1618
1619 checkOffsetAndAlignmentAndBounds(access, &base);
1620#ifndef JS_64BIT1
1621 MOZ_ASSERT(base->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1621); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1621; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1622#endif
1623
1624 if (isSmallerAccessForI64(result, access)) {
1625 auto* cvtOldv =
1626 MWrapInt64ToInt32::New(alloc(), oldv, /*bottomHalf=*/true);
1627 curBlock_->add(cvtOldv);
1628 oldv = cvtOldv;
1629
1630 auto* cvtNewv =
1631 MWrapInt64ToInt32::New(alloc(), newv, /*bottomHalf=*/true);
1632 curBlock_->add(cvtNewv);
1633 newv = cvtNewv;
1634 }
1635
1636 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1637 MInstruction* cas = MWasmCompareExchangeHeap::New(
1638 alloc(), bytecodeOffset(), memoryBase, base, *access, oldv, newv,
1639 instancePointer_);
1640 if (!cas) {
1641 return nullptr;
1642 }
1643 curBlock_->add(cas);
1644
1645 if (isSmallerAccessForI64(result, access)) {
1646 cas = MExtendInt32ToInt64::New(alloc(), cas, true);
1647 curBlock_->add(cas);
1648 }
1649
1650 return cas;
1651 }
1652
1653 MDefinition* atomicExchangeHeap(MDefinition* base, MemoryAccessDesc* access,
1654 ValType result, MDefinition* value) {
1655 if (inDeadCode()) {
1656 return nullptr;
1657 }
1658
1659 checkOffsetAndAlignmentAndBounds(access, &base);
1660#ifndef JS_64BIT1
1661 MOZ_ASSERT(base->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1661); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1661; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1662#endif
1663
1664 if (isSmallerAccessForI64(result, access)) {
1665 auto* cvtValue =
1666 MWrapInt64ToInt32::New(alloc(), value, /*bottomHalf=*/true);
1667 curBlock_->add(cvtValue);
1668 value = cvtValue;
1669 }
1670
1671 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1672 MInstruction* xchg =
1673 MWasmAtomicExchangeHeap::New(alloc(), bytecodeOffset(), memoryBase,
1674 base, *access, value, instancePointer_);
1675 if (!xchg) {
1676 return nullptr;
1677 }
1678 curBlock_->add(xchg);
1679
1680 if (isSmallerAccessForI64(result, access)) {
1681 xchg = MExtendInt32ToInt64::New(alloc(), xchg, true);
1682 curBlock_->add(xchg);
1683 }
1684
1685 return xchg;
1686 }
1687
1688 MDefinition* atomicBinopHeap(AtomicOp op, MDefinition* base,
1689 MemoryAccessDesc* access, ValType result,
1690 MDefinition* value) {
1691 if (inDeadCode()) {
1692 return nullptr;
1693 }
1694
1695 checkOffsetAndAlignmentAndBounds(access, &base);
1696#ifndef JS_64BIT1
1697 MOZ_ASSERT(base->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1697); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1697; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1698#endif
1699
1700 if (isSmallerAccessForI64(result, access)) {
1701 auto* cvtValue =
1702 MWrapInt64ToInt32::New(alloc(), value, /*bottomHalf=*/true);
1703 curBlock_->add(cvtValue);
1704 value = cvtValue;
1705 }
1706
1707 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1708 MInstruction* binop =
1709 MWasmAtomicBinopHeap::New(alloc(), bytecodeOffset(), op, memoryBase,
1710 base, *access, value, instancePointer_);
1711 if (!binop) {
1712 return nullptr;
1713 }
1714 curBlock_->add(binop);
1715
1716 if (isSmallerAccessForI64(result, access)) {
1717 binop = MExtendInt32ToInt64::New(alloc(), binop, true);
1718 curBlock_->add(binop);
1719 }
1720
1721 return binop;
1722 }
1723
1724#ifdef ENABLE_WASM_SIMD1
1725 MDefinition* loadSplatSimd128(Scalar::Type viewType,
1726 const LinearMemoryAddress<MDefinition*>& addr,
1727 wasm::SimdOp splatOp) {
1728 if (inDeadCode()) {
1729 return nullptr;
1730 }
1731
1732 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
1733 bytecodeIfNotAsmJS(),
1734 hugeMemoryEnabled(addr.memoryIndex));
1735
1736 // Generate better code (on x86)
1737 // If AVX2 is enabled, more broadcast operators are available.
1738 if (viewType == Scalar::Float64
1739# if defined(JS_CODEGEN_X641) || defined(JS_CODEGEN_X86)
1740 || (js::jit::CPUInfo::IsAVX2Present() &&
1741 (viewType == Scalar::Uint8 || viewType == Scalar::Uint16 ||
1742 viewType == Scalar::Float32))
1743# endif
1744 ) {
1745 access.setSplatSimd128Load();
1746 return load(addr.base, &access, ValType::V128);
1747 }
1748
1749 ValType resultType = ValType::I32;
1750 if (viewType == Scalar::Float32) {
1751 resultType = ValType::F32;
1752 splatOp = wasm::SimdOp::F32x4Splat;
1753 }
1754 auto* scalar = load(addr.base, &access, resultType);
1755 if (!inDeadCode() && !scalar) {
1756 return nullptr;
1757 }
1758 return scalarToSimd128(scalar, splatOp);
1759 }
1760
1761 MDefinition* loadExtendSimd128(const LinearMemoryAddress<MDefinition*>& addr,
1762 wasm::SimdOp op) {
1763 if (inDeadCode()) {
1764 return nullptr;
1765 }
1766
1767 // Generate better code (on x86) by loading as a double with an
1768 // operation that sign extends directly.
1769 MemoryAccessDesc access(addr.memoryIndex, Scalar::Float64, addr.align,
1770 addr.offset, bytecodeIfNotAsmJS(),
1771 hugeMemoryEnabled(addr.memoryIndex));
1772 access.setWidenSimd128Load(op);
1773 return load(addr.base, &access, ValType::V128);
1774 }
1775
1776 MDefinition* loadZeroSimd128(Scalar::Type viewType, size_t numBytes,
1777 const LinearMemoryAddress<MDefinition*>& addr) {
1778 if (inDeadCode()) {
1779 return nullptr;
1780 }
1781
1782 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
1783 bytecodeIfNotAsmJS(),
1784 hugeMemoryEnabled(addr.memoryIndex));
1785 access.setZeroExtendSimd128Load();
1786 return load(addr.base, &access, ValType::V128);
1787 }
1788
1789 MDefinition* loadLaneSimd128(uint32_t laneSize,
1790 const LinearMemoryAddress<MDefinition*>& addr,
1791 uint32_t laneIndex, MDefinition* src) {
1792 if (inDeadCode()) {
1793 return nullptr;
1794 }
1795
1796 MemoryAccessDesc access(addr.memoryIndex, Scalar::Simd128, addr.align,
1797 addr.offset, bytecodeIfNotAsmJS(),
1798 hugeMemoryEnabled(addr.memoryIndex));
1799 MDefinition* memoryBase = maybeLoadMemoryBase(access.memoryIndex());
1800 MDefinition* base = addr.base;
1801 MOZ_ASSERT(!codeMeta_.isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!codeMeta_.isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!codeMeta_.isAsmJS()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!codeMeta_.isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1801); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1801; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1802 checkOffsetAndAlignmentAndBounds(&access, &base);
1803# ifndef JS_64BIT1
1804 MOZ_ASSERT(base->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1804); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1804; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1805# endif
1806 MInstruction* load = MWasmLoadLaneSimd128::New(
1807 alloc(), memoryBase, base, access, laneSize, laneIndex, src);
1808 if (!load) {
1809 return nullptr;
1810 }
1811 curBlock_->add(load);
1812 return load;
1813 }
1814
1815 void storeLaneSimd128(uint32_t laneSize,
1816 const LinearMemoryAddress<MDefinition*>& addr,
1817 uint32_t laneIndex, MDefinition* src) {
1818 if (inDeadCode()) {
1819 return;
1820 }
1821 MemoryAccessDesc access(addr.memoryIndex, Scalar::Simd128, addr.align,
1822 addr.offset, bytecodeIfNotAsmJS(),
1823 hugeMemoryEnabled(addr.memoryIndex));
1824 MDefinition* memoryBase = maybeLoadMemoryBase(access.memoryIndex());
1825 MDefinition* base = addr.base;
1826 MOZ_ASSERT(!codeMeta_.isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!codeMeta_.isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!codeMeta_.isAsmJS()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!codeMeta_.isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1826); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1826; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1827 checkOffsetAndAlignmentAndBounds(&access, &base);
1828# ifndef JS_64BIT1
1829 MOZ_ASSERT(base->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1829); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1829; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1830# endif
1831 MInstruction* store = MWasmStoreLaneSimd128::New(
1832 alloc(), memoryBase, base, access, laneSize, laneIndex, src);
1833 if (!store) {
1834 return;
1835 }
1836 curBlock_->add(store);
1837 }
1838#endif // ENABLE_WASM_SIMD
1839
1840 /************************************************ Global variable accesses */
1841
1842 MDefinition* loadGlobalVar(unsigned instanceDataOffset, bool isConst,
1843 bool isIndirect, MIRType type) {
1844 if (inDeadCode()) {
1845 return nullptr;
1846 }
1847
1848 MInstruction* load;
1849 if (isIndirect) {
1850 // Pull a pointer to the value out of Instance::globalArea, then
1851 // load from that pointer. Note that the pointer is immutable
1852 // even though the value it points at may change, hence the use of
1853 // |true| for the first node's |isConst| value, irrespective of
1854 // the |isConst| formal parameter to this method. The latter
1855 // applies to the denoted value as a whole.
1856 auto* cellPtr = MWasmLoadInstanceDataField::New(
1857 alloc(), MIRType::Pointer, instanceDataOffset,
1858 /*isConst=*/true, instancePointer_);
1859 curBlock_->add(cellPtr);
1860 load = MWasmLoadGlobalCell::New(alloc(), type, cellPtr);
1861 } else {
1862 // Pull the value directly out of Instance::globalArea.
1863 load = MWasmLoadInstanceDataField::New(alloc(), type, instanceDataOffset,
1864 isConst, instancePointer_);
1865 }
1866 curBlock_->add(load);
1867 return load;
1868 }
1869
1870 [[nodiscard]] bool storeGlobalVar(uint32_t lineOrBytecode,
1871 uint32_t instanceDataOffset,
1872 bool isIndirect, MDefinition* v) {
1873 if (inDeadCode()) {
1874 return true;
1875 }
1876
1877 if (isIndirect) {
1878 // Pull a pointer to the value out of Instance::globalArea, then
1879 // store through that pointer.
1880 auto* valueAddr = MWasmLoadInstanceDataField::New(
1881 alloc(), MIRType::Pointer, instanceDataOffset,
1882 /*isConst=*/true, instancePointer_);
1883 curBlock_->add(valueAddr);
1884
1885 // Handle a store to a ref-typed field specially
1886 if (v->type() == MIRType::WasmAnyRef) {
1887 // Load the previous value for the post-write barrier
1888 auto* prevValue =
1889 MWasmLoadGlobalCell::New(alloc(), MIRType::WasmAnyRef, valueAddr);
1890 curBlock_->add(prevValue);
1891
1892 // Store the new value
1893 auto* store =
1894 MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
1895 /*valueOffset=*/0, v, AliasSet::WasmGlobalCell,
1896 WasmPreBarrierKind::Normal);
1897 curBlock_->add(store);
1898
1899 // Call the post-write barrier
1900 return postBarrierPrecise(lineOrBytecode, valueAddr, prevValue);
1901 }
1902
1903 auto* store = MWasmStoreGlobalCell::New(alloc(), v, valueAddr);
1904 curBlock_->add(store);
1905 return true;
1906 }
1907 // Or else store the value directly in Instance::globalArea.
1908
1909 // Handle a store to a ref-typed field specially
1910 if (v->type() == MIRType::WasmAnyRef) {
1911 // Compute the address of the ref-typed global
1912 auto* valueAddr = MWasmDerivedPointer::New(
1913 alloc(), instancePointer_,
1914 wasm::Instance::offsetInData(instanceDataOffset));
1915 curBlock_->add(valueAddr);
1916
1917 // Load the previous value for the post-write barrier
1918 auto* prevValue =
1919 MWasmLoadGlobalCell::New(alloc(), MIRType::WasmAnyRef, valueAddr);
1920 curBlock_->add(prevValue);
1921
1922 // Store the new value
1923 auto* store =
1924 MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
1925 /*valueOffset=*/0, v, AliasSet::WasmInstanceData,
1926 WasmPreBarrierKind::Normal);
1927 curBlock_->add(store);
1928
1929 // Call the post-write barrier
1930 return postBarrierPrecise(lineOrBytecode, valueAddr, prevValue);
1931 }
1932
1933 auto* store = MWasmStoreInstanceDataField::New(alloc(), instanceDataOffset,
1934 v, instancePointer_);
1935 curBlock_->add(store);
1936 return true;
1937 }
1938
1939 MDefinition* loadTableField(uint32_t tableIndex, unsigned fieldOffset,
1940 MIRType type) {
1941 uint32_t instanceDataOffset = wasm::Instance::offsetInData(
1942 codeMeta_.offsetOfTableInstanceData(tableIndex) + fieldOffset);
1943 auto* load =
1944 MWasmLoadInstance::New(alloc(), instancePointer_, instanceDataOffset,
1945 type, AliasSet::Load(AliasSet::WasmTableMeta));
1946 curBlock_->add(load);
1947 return load;
1948 }
1949
1950 MDefinition* loadTableLength(uint32_t tableIndex) {
1951 return loadTableField(tableIndex, offsetof(TableInstanceData, length)__builtin_offsetof(TableInstanceData, length),
1952 MIRType::Int32);
1953 }
1954
1955 MDefinition* loadTableElements(uint32_t tableIndex) {
1956 return loadTableField(tableIndex, offsetof(TableInstanceData, elements)__builtin_offsetof(TableInstanceData, elements),
1957 MIRType::Pointer);
1958 }
1959
1960 MDefinition* tableIndexToI32(IndexType indexType, MDefinition* index) {
1961 switch (indexType) {
1962 case IndexType::I32:
1963 return index;
1964 case IndexType::I64:
1965 auto* clamp = MWasmClampTable64Index::New(alloc(), index);
1966 if (!clamp) {
1967 return nullptr;
1968 }
1969 curBlock_->add(clamp);
1970 return clamp;
1971 }
1972 MOZ_CRASH("unknown index type")do { do { } while (false); MOZ_ReportCrash("" "unknown index type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1972); AnnotateMozCrashReason("MOZ_CRASH(" "unknown index type"
")"); do { *((volatile int*)__null) = 1972; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1973 }
1974
1975 MDefinition* tableGetAnyRef(uint32_t tableIndex, MDefinition* index) {
1976 // Load the table length and perform a bounds check with spectre index
1977 // masking
1978 auto* length = loadTableLength(tableIndex);
1979 auto* check = MWasmBoundsCheck::New(
1980 alloc(), index, length, bytecodeOffset(), MWasmBoundsCheck::Unknown);
1981 curBlock_->add(check);
1982 if (JitOptions.spectreIndexMasking) {
1983 index = check;
1984 }
1985
1986 // Load the table elements and load the element
1987 auto* elements = loadTableElements(tableIndex);
1988 auto* element = MWasmLoadTableElement::New(alloc(), elements, index);
1989 curBlock_->add(element);
1990 return element;
1991 }
1992
1993 [[nodiscard]] bool tableSetAnyRef(uint32_t tableIndex, MDefinition* index,
1994 MDefinition* value,
1995 uint32_t lineOrBytecode) {
1996 // Load the table length and perform a bounds check with spectre index
1997 // masking
1998 auto* length = loadTableLength(tableIndex);
1999 auto* check = MWasmBoundsCheck::New(
2000 alloc(), index, length, bytecodeOffset(), MWasmBoundsCheck::Unknown);
2001 curBlock_->add(check);
2002 if (JitOptions.spectreIndexMasking) {
2003 index = check;
2004 }
2005
2006 // Load the table elements
2007 auto* elements = loadTableElements(tableIndex);
2008
2009 // Load the previous value
2010 auto* prevValue = MWasmLoadTableElement::New(alloc(), elements, index);
2011 curBlock_->add(prevValue);
2012
2013 // Compute the value's location for the post barrier
2014 auto* loc =
2015 MWasmDerivedIndexPointer::New(alloc(), elements, index, ScalePointer);
2016 curBlock_->add(loc);
2017
2018 // Store the new value
2019 auto* store = MWasmStoreRef::New(
2020 alloc(), instancePointer_, loc, /*valueOffset=*/0, value,
2021 AliasSet::WasmTableElement, WasmPreBarrierKind::Normal);
2022 curBlock_->add(store);
2023
2024 // Perform the post barrier
2025 return postBarrierPrecise(lineOrBytecode, loc, prevValue);
2026 }
2027
2028 void addInterruptCheck() {
2029 if (inDeadCode()) {
2030 return;
2031 }
2032 curBlock_->add(
2033 MWasmInterruptCheck::New(alloc(), instancePointer_, bytecodeOffset()));
2034 }
2035
2036 // Perform a post-write barrier to update the generational store buffer. This
2037 // version will remove a previous store buffer entry if it is no longer
2038 // needed.
2039 [[nodiscard]] bool postBarrierPrecise(uint32_t lineOrBytecode,
2040 MDefinition* valueAddr,
2041 MDefinition* value) {
2042 return emitInstanceCall2(lineOrBytecode, SASigPostBarrierPrecise, valueAddr,
2043 value);
2044 }
2045
2046 // Perform a post-write barrier to update the generational store buffer. This
2047 // version will remove a previous store buffer entry if it is no longer
2048 // needed.
2049 [[nodiscard]] bool postBarrierPreciseWithOffset(uint32_t lineOrBytecode,
2050 MDefinition* valueBase,
2051 uint32_t valueOffset,
2052 MDefinition* value) {
2053 MDefinition* valueOffsetDef = constantI32(int32_t(valueOffset));
2054 if (!valueOffsetDef) {
2055 return false;
2056 }
2057 return emitInstanceCall3(lineOrBytecode, SASigPostBarrierPreciseWithOffset,
2058 valueBase, valueOffsetDef, value);
2059 }
2060
2061 // Perform a post-write barrier to update the generational store buffer. This
2062 // version is the most efficient and only requires the address to store the
2063 // value and the new value. It does not remove a previous store buffer entry
2064 // if it is no longer needed, you must use a precise post-write barrier for
2065 // that.
2066 [[nodiscard]] bool postBarrierImmediate(uint32_t lineOrBytecode,
2067 MDefinition* object,
2068 MDefinition* valueBase,
2069 uint32_t valueOffset,
2070 MDefinition* newValue) {
2071 auto* barrier = MWasmPostWriteBarrierImmediate::New(
2072 alloc(), instancePointer_, object, valueBase, valueOffset, newValue);
2073 if (!barrier) {
2074 return false;
2075 }
2076 curBlock_->add(barrier);
2077 return true;
2078 }
2079
2080 [[nodiscard]] bool postBarrierIndex(uint32_t lineOrBytecode,
2081 MDefinition* object,
2082 MDefinition* valueBase,
2083 MDefinition* index, uint32_t scale,
2084 MDefinition* newValue) {
2085 auto* barrier = MWasmPostWriteBarrierIndex::New(
2086 alloc(), instancePointer_, object, valueBase, index, scale, newValue);
2087 if (!barrier) {
2088 return false;
2089 }
2090 curBlock_->add(barrier);
2091 return true;
2092 }
2093
2094 /***************************************************************** Calls */
2095
2096 // The IonMonkey backend maintains a single stack offset (from the stack
2097 // pointer to the base of the frame) by adding the total amount of spill
2098 // space required plus the maximum stack required for argument passing.
2099 // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must
2100 // manually accumulate, for the entire function, the maximum required stack
2101 // space for argument passing. (This is passed to the CodeGenerator via
2102 // MIRGenerator::maxWasmStackArgBytes.) This is just be the maximum of the
2103 // stack space required for each individual call (as determined by the call
2104 // ABI).
2105
2106 // Operations that modify a CallCompileState.
2107
2108 [[nodiscard]] bool passInstance(MIRType instanceType,
2109 CallCompileState* args) {
2110 if (inDeadCode()) {
2111 return true;
2112 }
2113
2114 // Should only pass an instance once. And it must be a non-GC pointer.
2115 MOZ_ASSERT(args->instanceArg_ == ABIArg())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(args->instanceArg_ == ABIArg())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(args->instanceArg_ == ABIArg
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("args->instanceArg_ == ABIArg()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2115); AnnotateMozCrashReason("MOZ_ASSERT" "(" "args->instanceArg_ == ABIArg()"
")"); do { *((volatile int*)__null) = 2115; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2116 MOZ_ASSERT(instanceType == MIRType::Pointer)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(instanceType == MIRType::Pointer)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(instanceType == MIRType::Pointer
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"instanceType == MIRType::Pointer", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2116); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instanceType == MIRType::Pointer"
")"); do { *((volatile int*)__null) = 2116; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2117 args->instanceArg_ = args->abi_.next(MIRType::Pointer);
2118 return true;
2119 }
2120
2121 // Do not call this directly. Call one of the passArg() variants instead.
2122 [[nodiscard]] bool passArgWorker(MDefinition* argDef, MIRType type,
2123 CallCompileState* call) {
2124 MOZ_ASSERT(argDef->type() == type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argDef->type() == type)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(argDef->type() == type)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("argDef->type() == type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2124); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argDef->type() == type"
")"); do { *((volatile int*)__null) = 2124; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2125
2126 ABIArg arg = call->abi_.next(type);
2127 switch (arg.kind()) {
2128#ifdef JS_CODEGEN_REGISTER_PAIR
2129 case ABIArg::GPR_PAIR: {
2130 auto mirLow =
2131 MWrapInt64ToInt32::New(alloc(), argDef, /* bottomHalf = */ true);
2132 curBlock_->add(mirLow);
2133 auto mirHigh =
2134 MWrapInt64ToInt32::New(alloc(), argDef, /* bottomHalf = */ false);
2135 curBlock_->add(mirHigh);
2136 return call->regArgs_.append(
2137 MWasmCallBase::Arg(AnyRegister(arg.gpr64().low), mirLow)) &&
2138 call->regArgs_.append(
2139 MWasmCallBase::Arg(AnyRegister(arg.gpr64().high), mirHigh));
2140 }
2141#endif
2142 case ABIArg::GPR:
2143 case ABIArg::FPU:
2144 return call->regArgs_.append(MWasmCallBase::Arg(arg.reg(), argDef));
2145 case ABIArg::Stack: {
2146 auto* mir =
2147 MWasmStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
2148 curBlock_->add(mir);
2149 return true;
2150 }
2151 case ABIArg::Uninitialized:
2152 MOZ_ASSERT_UNREACHABLE("Uninitialized ABIArg kind")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Uninitialized ABIArg kind" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Uninitialized ABIArg kind" ")");
do { *((volatile int*)__null) = 2152; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2153 }
2154 MOZ_CRASH("Unknown ABIArg kind.")do { do { } while (false); MOZ_ReportCrash("" "Unknown ABIArg kind."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2154); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown ABIArg kind."
")"); do { *((volatile int*)__null) = 2154; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2155 }
2156
2157 template <typename VecT>
2158 [[nodiscard]] bool passArgs(const DefVector& argDefs, const VecT& types,
2159 CallCompileState* call) {
2160 MOZ_ASSERT(argDefs.length() == types.length())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argDefs.length() == types.length())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(argDefs.length() == types.length
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("argDefs.length() == types.length()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2160); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argDefs.length() == types.length()"
")"); do { *((volatile int*)__null) = 2160; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2161 for (uint32_t i = 0; i < argDefs.length(); i++) {
2162 MDefinition* def = argDefs[i];
2163 ValType type = types[i];
2164 if (!passArg(def, type, call)) {
2165 return false;
2166 }
2167 }
2168 return true;
2169 }
2170
2171 [[nodiscard]] bool passArg(MDefinition* argDef, MIRType type,
2172 CallCompileState* call) {
2173 if (inDeadCode()) {
2174 return true;
2175 }
2176 return passArgWorker(argDef, type, call);
2177 }
2178
2179 [[nodiscard]] bool passArg(MDefinition* argDef, ValType type,
2180 CallCompileState* call) {
2181 if (inDeadCode()) {
2182 return true;
2183 }
2184 return passArgWorker(argDef, type.toMIRType(), call);
2185 }
2186
2187 void markReturnCall(CallCompileState* call) { call->returnCall = true; }
2188
2189 // If the call returns results on the stack, prepare a stack area to receive
2190 // them, and pass the address of the stack area to the callee as an additional
2191 // argument.
2192 [[nodiscard]] bool passStackResultAreaCallArg(const ResultType& resultType,
2193 CallCompileState* call) {
2194 if (inDeadCode()) {
2195 return true;
2196 }
2197 ABIResultIter iter(resultType);
2198 while (!iter.done() && iter.cur().inRegister()) {
2199 iter.next();
2200 }
2201 if (iter.done()) {
2202 // No stack results.
2203 return true;
2204 }
2205
2206 auto* stackResultArea = MWasmStackResultArea::New(alloc());
2207 if (!stackResultArea) {
2208 return false;
2209 }
2210 if (!stackResultArea->init(alloc(), iter.remaining())) {
2211 return false;
2212 }
2213 for (uint32_t base = iter.index(); !iter.done(); iter.next()) {
2214 MWasmStackResultArea::StackResult loc(iter.cur().stackOffset(),
2215 iter.cur().type().toMIRType());
2216 stackResultArea->initResult(iter.index() - base, loc);
2217 }
2218 curBlock_->add(stackResultArea);
2219 MDefinition* def = call->returnCall ? (MDefinition*)stackResultPointer_
2220 : (MDefinition*)stackResultArea;
2221 if (!passArg(def, MIRType::StackResults, call)) {
2222 return false;
2223 }
2224 call->stackResultArea_ = stackResultArea;
2225 return true;
2226 }
2227
2228 [[nodiscard]] bool finishCall(CallCompileState* call) {
2229 if (inDeadCode()) {
2230 return true;
2231 }
2232
2233 if (!call->regArgs_.append(
2234 MWasmCallBase::Arg(AnyRegister(InstanceReg), instancePointer_))) {
2235 return false;
2236 }
2237
2238 uint32_t stackBytes = call->abi_.stackBytesConsumedSoFar();
2239
2240 maxStackArgBytes_ = std::max(maxStackArgBytes_, stackBytes);
2241 return true;
2242 }
2243
2244 // Wrappers for creating various kinds of calls.
2245
2246 [[nodiscard]] bool collectUnaryCallResult(MIRType type,
2247 MDefinition** result) {
2248 MInstruction* def;
2249 switch (type) {
2250 case MIRType::Int32:
2251 def = MWasmRegisterResult::New(alloc(), MIRType::Int32, ReturnReg);
2252 break;
2253 case MIRType::Int64:
2254 def = MWasmRegister64Result::New(alloc(), ReturnReg64);
2255 break;
2256 case MIRType::Float32:
2257 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnFloat32Reg);
2258 break;
2259 case MIRType::Double:
2260 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnDoubleReg);
2261 break;
2262#ifdef ENABLE_WASM_SIMD1
2263 case MIRType::Simd128:
2264 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnSimd128Reg);
2265 break;
2266#endif
2267 case MIRType::WasmAnyRef:
2268 def = MWasmRegisterResult::New(alloc(), MIRType::WasmAnyRef, ReturnReg);
2269 break;
2270 default:
2271 MOZ_CRASH("unexpected MIRType result for builtin call")do { do { } while (false); MOZ_ReportCrash("" "unexpected MIRType result for builtin call"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2271); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected MIRType result for builtin call"
")"); do { *((volatile int*)__null) = 2271; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2272 }
2273
2274 if (!def) {
2275 return false;
2276 }
2277
2278 curBlock_->add(def);
2279 *result = def;
2280
2281 return true;
2282 }
2283
2284 [[nodiscard]] bool collectCallResults(const ResultType& type,
2285 MWasmStackResultArea* stackResultArea,
2286 DefVector* results) {
2287 if (!results->reserve(type.length())) {
2288 return false;
2289 }
2290
2291 // The result iterator goes in the order in which results would be popped
2292 // off; we want the order in which they would be pushed.
2293 ABIResultIter iter(type);
2294 uint32_t stackResultCount = 0;
2295 while (!iter.done()) {
2296 if (iter.cur().onStack()) {
2297 stackResultCount++;
2298 }
2299 iter.next();
2300 }
2301
2302 for (iter.switchToPrev(); !iter.done(); iter.prev()) {
2303 if (!mirGen().ensureBallast()) {
2304 return false;
2305 }
2306 const ABIResult& result = iter.cur();
2307 MInstruction* def;
2308 if (result.inRegister()) {
2309 switch (result.type().kind()) {
2310 case wasm::ValType::I32:
2311 def =
2312 MWasmRegisterResult::New(alloc(), MIRType::Int32, result.gpr());
2313 break;
2314 case wasm::ValType::I64:
2315 def = MWasmRegister64Result::New(alloc(), result.gpr64());
2316 break;
2317 case wasm::ValType::F32:
2318 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Float32,
2319 result.fpr());
2320 break;
2321 case wasm::ValType::F64:
2322 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Double,
2323 result.fpr());
2324 break;
2325 case wasm::ValType::Ref:
2326 def = MWasmRegisterResult::New(alloc(), MIRType::WasmAnyRef,
2327 result.gpr());
2328 break;
2329 case wasm::ValType::V128:
2330#ifdef ENABLE_WASM_SIMD1
2331 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Simd128,
2332 result.fpr());
2333#else
2334 return this->iter().fail("Ion has no SIMD support yet");
2335#endif
2336 }
2337 } else {
2338 MOZ_ASSERT(stackResultArea)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stackResultArea)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stackResultArea))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("stackResultArea"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2338); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultArea"
")"); do { *((volatile int*)__null) = 2338; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2339 MOZ_ASSERT(stackResultCount)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stackResultCount)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stackResultCount))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("stackResultCount"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2339); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultCount"
")"); do { *((volatile int*)__null) = 2339; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2340 uint32_t idx = --stackResultCount;
2341 def = MWasmStackResult::New(alloc(), stackResultArea, idx);
2342 }
2343
2344 if (!def) {
2345 return false;
2346 }
2347 curBlock_->add(def);
2348 results->infallibleAppend(def);
2349 }
2350
2351 MOZ_ASSERT(results->length() == type.length())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(results->length() == type.length())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(results->length() == type.length()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("results->length() == type.length()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2351); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results->length() == type.length()"
")"); do { *((volatile int*)__null) = 2351; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2352
2353 return true;
2354 }
2355
2356 [[nodiscard]] bool catchableCall(const CallSiteDesc& desc,
2357 const CalleeDesc& callee,
2358 const MWasmCallBase::Args& args,
2359 const ArgTypeVector& argTypes,
2360 MDefinition* indexOrRef = nullptr) {
2361 MWasmCallTryDesc tryDesc;
2362 if (!beginTryCall(&tryDesc)) {
2363 return false;
2364 }
2365
2366 MInstruction* ins;
2367 if (tryDesc.inTry) {
2368 ins = MWasmCallCatchable::New(alloc(), desc, callee, args,
2369 StackArgAreaSizeUnaligned(argTypes),
2370 tryDesc, indexOrRef);
2371 } else {
2372 ins = MWasmCallUncatchable::New(alloc(), desc, callee, args,
2373 StackArgAreaSizeUnaligned(argTypes),
2374 indexOrRef);
2375 }
2376 if (!ins) {
2377 return false;
2378 }
2379 curBlock_->add(ins);
2380
2381 return finishTryCall(&tryDesc);
2382 }
2383
2384 [[nodiscard]]
2385 bool shouldInlineCallDirect(uint32_t funcIndex) {
2386 // We only support this mode when lazy tiering. This is currently a
2387 // requirement because we need the full module bytecode and function
2388 // definition ranges, which are not available in other modes.
2389 if (compilerEnv().mode() != CompileMode::LazyTiering) {
2390 return false;
2391 }
2392
2393 // Limit the inlining depth.
2394 if (inliningDepth() > JS::Prefs::wasm_experimental_inline_depth_limit()) {
2395 return false;
2396 }
2397
2398 // We temporarily do not support inlining when there's a try handler
2399 // active.
2400 if (inTryCode()) {
2401 return false;
2402 }
2403
2404 // We do not support inlining a callee which uses tail calls
2405 FeatureUsage funcFeatureUsage = codeMeta().funcDefFeatureUsage(funcIndex);
2406 if (funcFeatureUsage & FeatureUsage::ReturnCall) {
2407 return false;
2408 }
2409
2410 // Limit the callee function to under a specific size.
2411 const FuncDefRange& funcRange = codeMeta().funcDefRange(funcIndex);
2412 return funcRange.bodyLength <=
2413 JS::Prefs::wasm_experimental_inline_size_limit();
2414 }
2415
2416 [[nodiscard]]
2417 bool finishInlinedCallDirect(FunctionCompiler& calleeCompiler,
2418 DefVector* results) {
2419 const PendingInlineReturnVector& calleeReturns =
2420 calleeCompiler.pendingInlineReturns_;
2421 const FuncType& calleeFuncType = calleeCompiler.funcType();
2422
2423 // Add the observed features from the inlined function to this function
2424 iter_.addFeatureUsage(calleeCompiler.featureUsage());
2425
2426 // If there were no returns, then we are now in dead code
2427 if (calleeReturns.empty()) {
2428 curBlock_ = nullptr;
2429 return true;
2430 }
2431
2432 // Create a block to join all of the returns from the inlined function
2433 MBasicBlock* lastBlockBeforeCall = curBlock_;
2434 MBasicBlock* joinAfterCall = nullptr;
2435 if (!newBlock(nullptr, &joinAfterCall)) {
2436 return false;
2437 }
2438
2439 // The join block inherits all of the locals state from immediately before
2440 // the inlined call
2441 joinAfterCall->inheritSlots(lastBlockBeforeCall);
2442
2443 // The join block has a phi node for every result of the inlined function
2444 // type. Each phi node has an operand for each of the returns of the
2445 // inlined function.
2446 for (uint32_t i = 0; i < calleeFuncType.results().length(); i++) {
2447 MPhi* phi = MPhi::New(alloc(), calleeFuncType.results()[i].toMIRType());
2448 if (!phi || !phi->reserveLength(calleeReturns.length())) {
2449 return false;
2450 }
2451 joinAfterCall->addPhi(phi);
2452 if (!results->append(phi)) {
2453 return false;
2454 }
2455 }
2456
2457 // Bind every return from the inlined function to go to the join block, and
2458 // add the results for the return to the phi nodes.
2459 for (size_t i = 0; i < calleeReturns.length(); i++) {
2460 const PendingInlineReturn& calleeReturn = calleeReturns[i];
2461
2462 // Setup the predecessor and successor relationship
2463 MBasicBlock* pred = calleeReturn.jump->block();
2464 if (!joinAfterCall->addPredecessorWithoutPhis(pred)) {
2465 return false;
2466 }
2467 calleeReturn.jump->replaceSuccessor(MGoto::TargetIndex, joinAfterCall);
2468
2469 // For each result in this return, add it to the corresponding phi node
2470 for (uint32_t resultIndex = 0;
2471 resultIndex < calleeFuncType.results().length(); resultIndex++) {
2472 MDefinition* result = (*results)[resultIndex];
2473 ((MPhi*)(result))->addInput(calleeReturn.results[resultIndex]);
2474 }
2475 }
2476
2477 // Continue MIR generation starting in the join block
2478 curBlock_ = joinAfterCall;
2479
2480 return true;
2481 }
2482
2483 [[nodiscard]] bool callDirect(const FuncType& funcType, uint32_t funcIndex,
2484 uint32_t lineOrBytecode,
2485 const CallCompileState& call,
2486 DefVector* results) {
2487 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2487); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2487; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2488
2489 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Func);
2490 ResultType resultType = ResultType::Vector(funcType.results());
2491 auto callee = CalleeDesc::function(funcIndex);
2492 ArgTypeVector args(funcType);
2493
2494 if (!catchableCall(desc, callee, call.regArgs_, args)) {
2495 return false;
2496 }
2497 return collectCallResults(resultType, call.stackResultArea_, results);
2498 }
2499
2500 [[nodiscard]] bool returnCallDirect(const FuncType& funcType,
2501 uint32_t funcIndex,
2502 uint32_t lineOrBytecode,
2503 const CallCompileState& call,
2504 DefVector* results) {
2505 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2505; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2506
2507 // We do not support tail calls in inlined functions.
2508 MOZ_RELEASE_ASSERT(!isInlined())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isInlined())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isInlined()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isInlined()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2508); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!isInlined()"
")"); do { *((volatile int*)__null) = 2508; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2509
2510 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::ReturnFunc);
2511 auto callee = CalleeDesc::function(funcIndex);
2512 ArgTypeVector args(funcType);
2513
2514 auto ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2515 StackArgAreaSizeUnaligned(args), nullptr);
2516 if (!ins) {
2517 return false;
2518 }
2519 curBlock_->end(ins);
2520 curBlock_ = nullptr;
2521 return true;
2522 }
2523
2524 [[nodiscard]] bool returnCallImport(unsigned globalDataOffset,
2525 uint32_t lineOrBytecode,
2526 const CallCompileState& call,
2527 const FuncType& funcType,
2528 DefVector* results) {
2529 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2529); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2529; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2530
2531 // We do not support tail calls in inlined functions.
2532 MOZ_RELEASE_ASSERT(!isInlined())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isInlined())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isInlined()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isInlined()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2532); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!isInlined()"
")"); do { *((volatile int*)__null) = 2532; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2533
2534 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Import);
2535 auto callee = CalleeDesc::import(globalDataOffset);
2536 ArgTypeVector args(funcType);
2537
2538 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2539 StackArgAreaSizeUnaligned(args), nullptr);
2540 if (!ins) {
2541 return false;
2542 }
2543 curBlock_->end(ins);
2544 curBlock_ = nullptr;
2545 return true;
2546 }
2547
2548 [[nodiscard]] bool returnCallIndirect(uint32_t funcTypeIndex,
2549 uint32_t tableIndex, MDefinition* index,
2550 uint32_t lineOrBytecode,
2551 const CallCompileState& call,
2552 DefVector* results) {
2553 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2553); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2553; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2554
2555 // We do not support tail calls in inlined functions.
2556 MOZ_RELEASE_ASSERT(!isInlined())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isInlined())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isInlined()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isInlined()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2556); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!isInlined()"
")"); do { *((volatile int*)__null) = 2556; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2557
2558 const FuncType& funcType = (*codeMeta_.types)[funcTypeIndex].funcType();
2559 CallIndirectId callIndirectId =
2560 CallIndirectId::forFuncType(codeMeta_, funcTypeIndex);
2561
2562 CalleeDesc callee;
2563 MOZ_ASSERT(callIndirectId.kind() != CallIndirectIdKind::AsmJS)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callIndirectId.kind() != CallIndirectIdKind::AsmJS)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(callIndirectId.kind() != CallIndirectIdKind::AsmJS))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("callIndirectId.kind() != CallIndirectIdKind::AsmJS"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2563); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() != CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2563; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2564 const TableDesc& table = codeMeta_.tables[tableIndex];
2565 callee =
2566 CalleeDesc::wasmTable(codeMeta_, table, tableIndex, callIndirectId);
2567
2568 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Indirect);
2569 ArgTypeVector args(funcType);
2570
2571 MDefinition* index32 = tableIndexToI32(table.indexType(), index);
2572 if (!index32) {
2573 return false;
2574 }
2575
2576 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2577 StackArgAreaSizeUnaligned(args), index32);
2578 if (!ins) {
2579 return false;
2580 }
2581 curBlock_->end(ins);
2582 curBlock_ = nullptr;
2583 return true;
2584 }
2585
2586 [[nodiscard]] bool callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex,
2587 MDefinition* index, uint32_t lineOrBytecode,
2588 const CallCompileState& call,
2589 DefVector* results) {
2590 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2590); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2590; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2591
2592 const FuncType& funcType = (*codeMeta_.types)[funcTypeIndex].funcType();
2593 CallIndirectId callIndirectId =
2594 CallIndirectId::forFuncType(codeMeta_, funcTypeIndex);
2595
2596 CalleeDesc callee;
2597 if (codeMeta_.isAsmJS()) {
2598 MOZ_ASSERT(tableIndex == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(tableIndex == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(tableIndex == 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("tableIndex == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2598); AnnotateMozCrashReason("MOZ_ASSERT" "(" "tableIndex == 0"
")"); do { *((volatile int*)__null) = 2598; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2599 MOZ_ASSERT(callIndirectId.kind() == CallIndirectIdKind::AsmJS)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callIndirectId.kind() == CallIndirectIdKind::AsmJS)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(callIndirectId.kind() == CallIndirectIdKind::AsmJS))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("callIndirectId.kind() == CallIndirectIdKind::AsmJS"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2599); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() == CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2599; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2600 uint32_t tableIndex = codeMeta_.asmJSSigToTableIndex[funcTypeIndex];
2601 const TableDesc& table = codeMeta_.tables[tableIndex];
2602 MOZ_ASSERT(IsPowerOfTwo(table.initialLength()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsPowerOfTwo(table.initialLength()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsPowerOfTwo(table.initialLength
())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("IsPowerOfTwo(table.initialLength())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2602); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsPowerOfTwo(table.initialLength())"
")"); do { *((volatile int*)__null) = 2602; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2603
2604 MDefinition* mask = constantI32(int32_t(table.initialLength() - 1));
2605 MBitAnd* maskedIndex = MBitAnd::New(alloc(), index, mask, MIRType::Int32);
2606 curBlock_->add(maskedIndex);
2607
2608 index = maskedIndex;
2609 callee = CalleeDesc::asmJSTable(codeMeta_, tableIndex);
2610 } else {
2611 MOZ_ASSERT(callIndirectId.kind() != CallIndirectIdKind::AsmJS)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callIndirectId.kind() != CallIndirectIdKind::AsmJS)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(callIndirectId.kind() != CallIndirectIdKind::AsmJS))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("callIndirectId.kind() != CallIndirectIdKind::AsmJS"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2611); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() != CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2611; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2612 const TableDesc& table = codeMeta_.tables[tableIndex];
2613 callee =
2614 CalleeDesc::wasmTable(codeMeta_, table, tableIndex, callIndirectId);
2615 index = tableIndexToI32(table.indexType(), index);
2616 if (!index) {
2617 return false;
2618 }
2619 }
2620
2621 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Indirect);
2622 ArgTypeVector args(funcType);
2623 ResultType resultType = ResultType::Vector(funcType.results());
2624
2625 if (!catchableCall(desc, callee, call.regArgs_, args, index)) {
2626 return false;
2627 }
2628 return collectCallResults(resultType, call.stackResultArea_, results);
2629 }
2630
2631 [[nodiscard]] bool callImport(unsigned instanceDataOffset,
2632 uint32_t lineOrBytecode,
2633 const CallCompileState& call,
2634 const FuncType& funcType, DefVector* results) {
2635 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2635); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2635; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2636
2637 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Import);
2638 auto callee = CalleeDesc::import(instanceDataOffset);
2639 ArgTypeVector args(funcType);
2640 ResultType resultType = ResultType::Vector(funcType.results());
2641
2642 if (!catchableCall(desc, callee, call.regArgs_, args)) {
2643 return false;
2644 }
2645 return collectCallResults(resultType, call.stackResultArea_, results);
2646 }
2647
2648 [[nodiscard]] bool builtinCall(const SymbolicAddressSignature& builtin,
2649 uint32_t lineOrBytecode,
2650 const CallCompileState& call,
2651 MDefinition** def) {
2652 if (inDeadCode()) {
2653 *def = nullptr;
2654 return true;
2655 }
2656
2657 MOZ_ASSERT(builtin.failureMode == FailureMode::Infallible)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(builtin.failureMode == FailureMode::Infallible)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(builtin.failureMode == FailureMode::Infallible))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("builtin.failureMode == FailureMode::Infallible"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2657); AnnotateMozCrashReason("MOZ_ASSERT" "(" "builtin.failureMode == FailureMode::Infallible"
")"); do { *((volatile int*)__null) = 2657; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2658
2659 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
2660 auto callee = CalleeDesc::builtin(builtin.identity);
2661 auto* ins = MWasmCallUncatchable::New(alloc(), desc, callee, call.regArgs_,
2662 StackArgAreaSizeUnaligned(builtin));
2663 if (!ins) {
2664 return false;
2665 }
2666
2667 curBlock_->add(ins);
2668
2669 return collectUnaryCallResult(builtin.retType, def);
2670 }
2671
2672 [[nodiscard]] bool builtinInstanceMethodCall(
2673 const SymbolicAddressSignature& builtin, uint32_t lineOrBytecode,
2674 const CallCompileState& call, MDefinition** def = nullptr) {
2675 MOZ_ASSERT_IF(!def, builtin.retType == MIRType::None)do { if (!def) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(builtin.retType == MIRType::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(builtin.retType == MIRType::
None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("builtin.retType == MIRType::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2675); AnnotateMozCrashReason("MOZ_ASSERT" "(" "builtin.retType == MIRType::None"
")"); do { *((volatile int*)__null) = 2675; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2676 if (inDeadCode()) {
2677 if (def) {
2678 *def = nullptr;
2679 }
2680 return true;
2681 }
2682
2683 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
2684 MWasmCallTryDesc tryDesc;
2685 if (!beginTryCall(&tryDesc)) {
2686 return false;
2687 }
2688
2689 MInstruction* ins;
2690 if (tryDesc.inTry) {
2691 ins = MWasmCallCatchable::NewBuiltinInstanceMethodCall(
2692 alloc(), desc, builtin.identity, builtin.failureMode,
2693 call.instanceArg_, call.regArgs_, StackArgAreaSizeUnaligned(builtin),
2694 tryDesc);
2695 } else {
2696 ins = MWasmCallUncatchable::NewBuiltinInstanceMethodCall(
2697 alloc(), desc, builtin.identity, builtin.failureMode,
2698 call.instanceArg_, call.regArgs_, StackArgAreaSizeUnaligned(builtin));
2699 }
2700 if (!ins) {
2701 return false;
2702 }
2703 curBlock_->add(ins);
2704
2705 if (!finishTryCall(&tryDesc)) {
2706 return false;
2707 }
2708
2709 if (!def) {
2710 return true;
2711 }
2712 return collectUnaryCallResult(builtin.retType, def);
2713 }
2714
2715 [[nodiscard]] bool stackSwitch(MDefinition* suspender, MDefinition* fn,
2716 MDefinition* data, StackSwitchKind kind) {
2717 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2717); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2717; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2718
2719 MInstruction* ins;
2720 switch (kind) {
2721 case StackSwitchKind::SwitchToMain:
2722 ins = MWasmStackSwitchToMain::New(alloc(), suspender, fn, data);
2723 break;
2724 case StackSwitchKind::SwitchToSuspendable:
2725 ins = MWasmStackSwitchToSuspendable::New(alloc(), suspender, fn, data);
2726 break;
2727 case StackSwitchKind::ContinueOnSuspendable:
2728 ins = MWasmStackContinueOnSuspendable::New(alloc(), suspender);
2729 break;
2730 }
2731 if (!ins) {
2732 return false;
2733 }
2734
2735 curBlock_->add(ins);
2736
2737 return true;
2738 }
2739
2740#ifdef ENABLE_WASM_GC1
2741 [[nodiscard]] bool callRef(const FuncType& funcType, MDefinition* ref,
2742 uint32_t lineOrBytecode,
2743 const CallCompileState& call, DefVector* results) {
2744 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2744); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2744; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2745
2746 CalleeDesc callee = CalleeDesc::wasmFuncRef();
2747
2748 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::FuncRef);
2749 ArgTypeVector args(funcType);
2750 ResultType resultType = ResultType::Vector(funcType.results());
2751
2752 if (!catchableCall(desc, callee, call.regArgs_, args, ref)) {
2753 return false;
2754 }
2755 return collectCallResults(resultType, call.stackResultArea_, results);
2756 }
2757
2758# ifdef ENABLE_WASM_TAIL_CALLS1
2759 [[nodiscard]] bool returnCallRef(const FuncType& funcType, MDefinition* ref,
2760 uint32_t lineOrBytecode,
2761 const CallCompileState& call,
2762 DefVector* results) {
2763 MOZ_ASSERT(!inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!inDeadCode()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2763); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2763; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2764
2765 CalleeDesc callee = CalleeDesc::wasmFuncRef();
2766
2767 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::FuncRef);
2768 ArgTypeVector args(funcType);
2769
2770 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2771 StackArgAreaSizeUnaligned(args), ref);
2772 if (!ins) {
2773 return false;
2774 }
2775 curBlock_->end(ins);
2776 curBlock_ = nullptr;
2777 return true;
2778 }
2779
2780# endif // ENABLE_WASM_TAIL_CALLS
2781
2782#endif // ENABLE_WASM_GC
2783
2784 /*********************************************** Control flow generation */
2785
2786 inline bool inDeadCode() const { return curBlock_ == nullptr; }
2787
2788 [[nodiscard]] bool returnValues(DefVector&& values) {
2789 if (inDeadCode()) {
2790 return true;
2791 }
2792
2793 // If we're inlined into another function, we must accumulate the returns
2794 // so that they can be patched into the caller function.
2795 if (isInlined()) {
2796 MGoto* jump = MGoto::New(alloc());
2797 if (!jump) {
2798 return false;
2799 }
2800 curBlock_->end(jump);
2801 curBlock_ = nullptr;
2802 return pendingInlineReturns_.emplaceBack(
2803 PendingInlineReturn(jump, std::move(values)));
2804 }
2805
2806 if (values.empty()) {
2807 curBlock_->end(MWasmReturnVoid::New(alloc(), instancePointer_));
2808 } else {
2809 ResultType resultType = ResultType::Vector(funcType().results());
2810 ABIResultIter iter(resultType);
2811 // Switch to iterate in FIFO order instead of the default LIFO.
2812 while (!iter.done()) {
2813 iter.next();
2814 }
2815 iter.switchToPrev();
2816 for (uint32_t i = 0; !iter.done(); iter.prev(), i++) {
2817 if (!mirGen().ensureBallast()) {
2818 return false;
2819 }
2820 const ABIResult& result = iter.cur();
2821 if (result.onStack()) {
2822 MOZ_ASSERT(iter.remaining() > 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter.remaining() > 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter.remaining() > 1))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("iter.remaining() > 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.remaining() > 1"
")"); do { *((volatile int*)__null) = 2822; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2823 if (result.type().isRefRepr()) {
2824 auto* store = MWasmStoreRef::New(
2825 alloc(), instancePointer_, stackResultPointer_,
2826 result.stackOffset(), values[i], AliasSet::WasmStackResult,
2827 WasmPreBarrierKind::None);
2828 curBlock_->add(store);
2829 } else {
2830 auto* store = MWasmStoreStackResult::New(
2831 alloc(), stackResultPointer_, result.stackOffset(), values[i]);
2832 curBlock_->add(store);
2833 }
2834 } else {
2835 MOZ_ASSERT(iter.remaining() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter.remaining() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter.remaining() == 1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("iter.remaining() == 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2835); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.remaining() == 1"
")"); do { *((volatile int*)__null) = 2835; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2836 MOZ_ASSERT(i + 1 == values.length())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(i + 1 == values.length())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(i + 1 == values.length()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("i + 1 == values.length()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2836); AnnotateMozCrashReason("MOZ_ASSERT" "(" "i + 1 == values.length()"
")"); do { *((volatile int*)__null) = 2836; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2837 curBlock_->end(
2838 MWasmReturn::New(alloc(), values[i], instancePointer_));
2839 }
2840 }
2841 }
2842 curBlock_ = nullptr;
2843 return true;
2844 }
2845
2846 void unreachableTrap() {
2847 if (inDeadCode()) {
2848 return;
2849 }
2850
2851 auto* ins =
2852 MWasmTrap::New(alloc(), wasm::Trap::Unreachable, bytecodeOffset());
2853 curBlock_->end(ins);
2854 curBlock_ = nullptr;
2855 }
2856
2857 private:
2858 static uint32_t numPushed(MBasicBlock* block) {
2859 return block->stackDepth() - block->info().firstStackSlot();
2860 }
2861
2862 public:
2863 [[nodiscard]] bool pushDefs(const DefVector& defs) {
2864 if (inDeadCode()) {
2865 return true;
2866 }
2867 MOZ_ASSERT(numPushed(curBlock_) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numPushed(curBlock_) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numPushed(curBlock_) == 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("numPushed(curBlock_) == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2867); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 2867; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2868 if (!curBlock_->ensureHasSlots(defs.length())) {
2869 return false;
2870 }
2871 for (MDefinition* def : defs) {
2872 MOZ_ASSERT(def->type() != MIRType::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(def->type() != MIRType::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(def->type() != MIRType::None
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"def->type() != MIRType::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2872); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() != MIRType::None"
")"); do { *((volatile int*)__null) = 2872; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2873 curBlock_->push(def);
2874 }
2875 return true;
2876 }
2877
2878 [[nodiscard]] bool popPushedDefs(DefVector* defs) {
2879 size_t n = numPushed(curBlock_);
2880 if (!defs->resizeUninitialized(n)) {
2881 return false;
2882 }
2883 for (; n > 0; n--) {
2884 MDefinition* def = curBlock_->pop();
2885 MOZ_ASSERT(def->type() != MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(def->type() != MIRType::Value)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(def->type() != MIRType::Value
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"def->type() != MIRType::Value", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2885); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() != MIRType::Value"
")"); do { *((volatile int*)__null) = 2885; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2886 (*defs)[n - 1] = def;
2887 }
2888 return true;
2889 }
2890
2891 private:
2892 [[nodiscard]] bool addJoinPredecessor(const DefVector& defs,
2893 MBasicBlock** joinPred) {
2894 *joinPred = curBlock_;
2895 if (inDeadCode()) {
2896 return true;
2897 }
2898 return pushDefs(defs);
2899 }
2900
2901 public:
2902 [[nodiscard]] bool branchAndStartThen(MDefinition* cond,
2903 MBasicBlock** elseBlock) {
2904 if (inDeadCode()) {
2905 *elseBlock = nullptr;
2906 } else {
2907 MBasicBlock* thenBlock;
2908 if (!newBlock(curBlock_, &thenBlock)) {
2909 return false;
2910 }
2911 if (!newBlock(curBlock_, elseBlock)) {
2912 return false;
2913 }
2914
2915 curBlock_->end(MTest::New(alloc(), cond, thenBlock, *elseBlock));
2916
2917 curBlock_ = thenBlock;
2918 mirGraph().moveBlockToEnd(curBlock_);
2919 }
2920
2921 return startBlock();
2922 }
2923
2924 [[nodiscard]] bool switchToElse(MBasicBlock* elseBlock,
2925 MBasicBlock** thenJoinPred) {
2926 DefVector values;
2927 if (!finishBlock(&values)) {
2928 return false;
2929 }
2930
2931 if (!elseBlock) {
2932 *thenJoinPred = nullptr;
2933 } else {
2934 if (!addJoinPredecessor(values, thenJoinPred)) {
2935 return false;
2936 }
2937
2938 curBlock_ = elseBlock;
2939 mirGraph().moveBlockToEnd(curBlock_);
2940 }
2941
2942 return startBlock();
2943 }
2944
2945 [[nodiscard]] bool joinIfElse(MBasicBlock* thenJoinPred, DefVector* defs) {
2946 DefVector values;
2947 if (!finishBlock(&values)) {
2948 return false;
2949 }
2950
2951 if (!thenJoinPred && inDeadCode()) {
2952 return true;
2953 }
2954
2955 MBasicBlock* elseJoinPred;
2956 if (!addJoinPredecessor(values, &elseJoinPred)) {
2957 return false;
2958 }
2959
2960 mozilla::Array<MBasicBlock*, 2> blocks;
2961 size_t numJoinPreds = 0;
2962 if (thenJoinPred) {
2963 blocks[numJoinPreds++] = thenJoinPred;
2964 }
2965 if (elseJoinPred) {
2966 blocks[numJoinPreds++] = elseJoinPred;
2967 }
2968
2969 if (numJoinPreds == 0) {
2970 return true;
2971 }
2972
2973 MBasicBlock* join;
2974 if (!goToNewBlock(blocks[0], &join)) {
2975 return false;
2976 }
2977 for (size_t i = 1; i < numJoinPreds; ++i) {
2978 if (!goToExistingBlock(blocks[i], join)) {
2979 return false;
2980 }
2981 }
2982
2983 curBlock_ = join;
2984 return popPushedDefs(defs);
2985 }
2986
2987 [[nodiscard]] bool startBlock() {
2988 MOZ_ASSERT_IF(blockDepth_ < pendingBlocks_.length(),do { if (blockDepth_ < pendingBlocks_.length()) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pendingBlocks_
[blockDepth_].patches.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pendingBlocks_[blockDepth_].
patches.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("pendingBlocks_[blockDepth_].patches.empty()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2989); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pendingBlocks_[blockDepth_].patches.empty()"
")"); do { *((volatile int*)__null) = 2989; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
2989 pendingBlocks_[blockDepth_].patches.empty())do { if (blockDepth_ < pendingBlocks_.length()) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pendingBlocks_
[blockDepth_].patches.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pendingBlocks_[blockDepth_].
patches.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("pendingBlocks_[blockDepth_].patches.empty()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2989); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pendingBlocks_[blockDepth_].patches.empty()"
")"); do { *((volatile int*)__null) = 2989; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2990 blockDepth_++;
2991 return true;
2992 }
2993
2994 [[nodiscard]] bool finishBlock(DefVector* defs) {
2995 MOZ_ASSERT(blockDepth_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(blockDepth_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(blockDepth_))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("blockDepth_", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2995); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_"
")"); do { *((volatile int*)__null) = 2995; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2996 uint32_t topLabel = --blockDepth_;
2997 return bindBranches(topLabel, defs);
2998 }
2999
3000 [[nodiscard]] bool startLoop(MBasicBlock** loopHeader, size_t paramCount) {
3001 *loopHeader = nullptr;
3002
3003 blockDepth_++;
3004 loopDepth_++;
3005
3006 if (inDeadCode()) {
3007 return true;
3008 }
3009
3010 // Create the loop header.
3011 MOZ_ASSERT(curBlock_->loopDepth() == loopDepth_ - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(curBlock_->loopDepth() == loopDepth_ - 1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(curBlock_->loopDepth() == loopDepth_ - 1))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("curBlock_->loopDepth() == loopDepth_ - 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3011); AnnotateMozCrashReason("MOZ_ASSERT" "(" "curBlock_->loopDepth() == loopDepth_ - 1"
")"); do { *((volatile int*)__null) = 3011; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3012 *loopHeader = MBasicBlock::New(mirGraph(), info(), curBlock_,
3013 MBasicBlock::PENDING_LOOP_HEADER);
3014 if (!*loopHeader) {
3015 return false;
3016 }
3017
3018 (*loopHeader)->setLoopDepth(loopDepth_);
3019 mirGraph().addBlock(*loopHeader);
3020 curBlock_->end(MGoto::New(alloc(), *loopHeader));
3021
3022 DefVector loopParams;
3023 if (!iter().getResults(paramCount, &loopParams)) {
3024 return false;
3025 }
3026 for (size_t i = 0; i < paramCount; i++) {
3027 MPhi* phi = MPhi::New(alloc(), loopParams[i]->type());
3028 if (!phi) {
3029 return false;
3030 }
3031 if (!phi->reserveLength(2)) {
3032 return false;
3033 }
3034 (*loopHeader)->addPhi(phi);
3035 phi->addInput(loopParams[i]);
3036 loopParams[i] = phi;
3037 }
3038 iter().setResults(paramCount, loopParams);
3039
3040 MBasicBlock* body;
3041 if (!goToNewBlock(*loopHeader, &body)) {
3042 return false;
3043 }
3044 curBlock_ = body;
3045 return true;
3046 }
3047
3048 private:
3049 void fixupRedundantPhis(MBasicBlock* b) {
3050 for (size_t i = 0, depth = b->stackDepth(); i < depth; i++) {
3051 MDefinition* def = b->getSlot(i);
3052 if (def->isUnused()) {
3053 b->setSlot(i, def->toPhi()->getOperand(0));
3054 }
3055 }
3056 }
3057
3058 [[nodiscard]] bool setLoopBackedge(MBasicBlock* loopEntry,
3059 MBasicBlock* loopBody,
3060 MBasicBlock* backedge, size_t paramCount) {
3061 if (!loopEntry->setBackedgeWasm(backedge, paramCount)) {
3062 return false;
3063 }
3064
3065 // Flag all redundant phis as unused.
3066 for (MPhiIterator phi = loopEntry->phisBegin(); phi != loopEntry->phisEnd();
3067 phi++) {
3068 MOZ_ASSERT(phi->numOperands() == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(phi->numOperands() == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(phi->numOperands() == 2))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("phi->numOperands() == 2"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3068); AnnotateMozCrashReason("MOZ_ASSERT" "(" "phi->numOperands() == 2"
")"); do { *((volatile int*)__null) = 3068; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3069 if (phi->getOperand(0) == phi->getOperand(1)) {
3070 phi->setUnused();
3071 }
3072 }
3073
3074 // Fix up phis stored in the slots Vector of pending blocks.
3075 for (PendingBlockTarget& pendingBlockTarget : pendingBlocks_) {
3076 for (ControlFlowPatch& p : pendingBlockTarget.patches) {
3077 MBasicBlock* block = p.ins->block();
3078 if (block->loopDepth() >= loopEntry->loopDepth()) {
3079 fixupRedundantPhis(block);
3080 }
3081 }
3082 }
3083
3084 // The loop body, if any, might be referencing recycled phis too.
3085 if (loopBody) {
3086 fixupRedundantPhis(loopBody);
3087 }
3088
3089 // Pending jumps to an enclosing try-catch may reference the recycled phis.
3090 // We have to search above all enclosing try blocks, as a delegate may move
3091 // patches around.
3092 for (uint32_t depth = 0; depth < iter().controlStackDepth(); depth++) {
3093 LabelKind kind = iter().controlKind(depth);
3094 if (kind != LabelKind::Try && kind != LabelKind::TryTable &&
3095 kind != LabelKind::Body) {
3096 continue;
3097 }
3098 Control& control = iter().controlItem(depth);
3099 if (!control.tryControl) {
3100 continue;
3101 }
3102 for (MControlInstruction* patch : control.tryControl->landingPadPatches) {
3103 MBasicBlock* block = patch->block();
3104 if (block->loopDepth() >= loopEntry->loopDepth()) {
3105 fixupRedundantPhis(block);
3106 }
3107 }
3108 }
3109 for (MControlInstruction* patch : bodyDelegatePadPatches_) {
3110 MBasicBlock* block = patch->block();
3111 if (block->loopDepth() >= loopEntry->loopDepth()) {
3112 fixupRedundantPhis(block);
3113 }
3114 }
3115
3116 // Discard redundant phis and add to the free list.
3117 for (MPhiIterator phi = loopEntry->phisBegin();
3118 phi != loopEntry->phisEnd();) {
3119 MPhi* entryDef = *phi++;
3120 if (!entryDef->isUnused()) {
3121 continue;
3122 }
3123
3124 entryDef->justReplaceAllUsesWith(entryDef->getOperand(0));
3125 loopEntry->discardPhi(entryDef);
3126 mirGraph().addPhiToFreeList(entryDef);
3127 }
3128
3129 return true;
3130 }
3131
3132 public:
3133 [[nodiscard]] bool closeLoop(MBasicBlock* loopHeader,
3134 DefVector* loopResults) {
3135 MOZ_ASSERT(blockDepth_ >= 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(blockDepth_ >= 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(blockDepth_ >= 1))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("blockDepth_ >= 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3135); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_ >= 1"
")"); do { *((volatile int*)__null) = 3135; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3136 MOZ_ASSERT(loopDepth_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(loopDepth_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(loopDepth_))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("loopDepth_", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loopDepth_"
")"); do { *((volatile int*)__null) = 3136; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3137
3138 uint32_t headerLabel = blockDepth_ - 1;
3139
3140 if (!loopHeader) {
3141 MOZ_ASSERT(inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("inDeadCode()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3141); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 3141; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3142 MOZ_ASSERT(headerLabel >= pendingBlocks_.length() ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(headerLabel >= pendingBlocks_.length() || pendingBlocks_
[headerLabel].patches.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(headerLabel >= pendingBlocks_
.length() || pendingBlocks_[headerLabel].patches.empty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3143); AnnotateMozCrashReason("MOZ_ASSERT" "(" "headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
")"); do { *((volatile int*)__null) = 3143; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3143 pendingBlocks_[headerLabel].patches.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(headerLabel >= pendingBlocks_.length() || pendingBlocks_
[headerLabel].patches.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(headerLabel >= pendingBlocks_
.length() || pendingBlocks_[headerLabel].patches.empty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3143); AnnotateMozCrashReason("MOZ_ASSERT" "(" "headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
")"); do { *((volatile int*)__null) = 3143; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3144 blockDepth_--;
3145 loopDepth_--;
3146 return true;
3147 }
3148
3149 // Op::Loop doesn't have an implicit backedge so temporarily set
3150 // aside the end of the loop body to bind backedges.
3151 MBasicBlock* loopBody = curBlock_;
3152 curBlock_ = nullptr;
3153
3154 // As explained in bug 1253544, Ion apparently has an invariant that
3155 // there is only one backedge to loop headers. To handle wasm's ability
3156 // to have multiple backedges to the same loop header, we bind all those
3157 // branches as forward jumps to a single backward jump. This is
3158 // unfortunate but the optimizer is able to fold these into single jumps
3159 // to backedges.
3160 DefVector backedgeValues;
3161 if (!bindBranches(headerLabel, &backedgeValues)) {
3162 return false;
3163 }
3164
3165 MOZ_ASSERT(loopHeader->loopDepth() == loopDepth_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(loopHeader->loopDepth() == loopDepth_)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(loopHeader->loopDepth() == loopDepth_))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("loopHeader->loopDepth() == loopDepth_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3165); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loopHeader->loopDepth() == loopDepth_"
")"); do { *((volatile int*)__null) = 3165; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3166
3167 if (curBlock_) {
3168 // We're on the loop backedge block, created by bindBranches.
3169 for (size_t i = 0, n = numPushed(curBlock_); i != n; i++) {
3170 curBlock_->pop();
3171 }
3172
3173 if (!pushDefs(backedgeValues)) {
3174 return false;
3175 }
3176
3177 MOZ_ASSERT(curBlock_->loopDepth() == loopDepth_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(curBlock_->loopDepth() == loopDepth_)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(curBlock_->loopDepth() == loopDepth_))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("curBlock_->loopDepth() == loopDepth_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3177); AnnotateMozCrashReason("MOZ_ASSERT" "(" "curBlock_->loopDepth() == loopDepth_"
")"); do { *((volatile int*)__null) = 3177; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3178 curBlock_->end(MGoto::New(alloc(), loopHeader));
3179 if (!setLoopBackedge(loopHeader, loopBody, curBlock_,
3180 backedgeValues.length())) {
3181 return false;
3182 }
3183 }
3184
3185 curBlock_ = loopBody;
3186
3187 loopDepth_--;
3188
3189 // If the loop depth still at the inner loop body, correct it.
3190 if (curBlock_ && curBlock_->loopDepth() != loopDepth_) {
3191 MBasicBlock* out;
3192 if (!goToNewBlock(curBlock_, &out)) {
3193 return false;
3194 }
3195 curBlock_ = out;
3196 }
3197
3198 blockDepth_ -= 1;
3199 return inDeadCode() || popPushedDefs(loopResults);
3200 }
3201
3202 [[nodiscard]] bool addControlFlowPatch(
3203 MControlInstruction* ins, uint32_t relative, uint32_t index,
3204 BranchHint branchHint = BranchHint::Invalid) {
3205 MOZ_ASSERT(relative < blockDepth_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(relative < blockDepth_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(relative < blockDepth_)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("relative < blockDepth_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3205); AnnotateMozCrashReason("MOZ_ASSERT" "(" "relative < blockDepth_"
")"); do { *((volatile int*)__null) = 3205; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3206 uint32_t absolute = blockDepth_ - 1 - relative;
3207
3208 if (absolute >= pendingBlocks_.length() &&
3209 !pendingBlocks_.resize(absolute + 1)) {
3210 return false;
3211 }
3212
3213 pendingBlocks_[absolute].hint = branchHint;
3214 return pendingBlocks_[absolute].patches.append(
3215 ControlFlowPatch(ins, index));
3216 }
3217
3218 [[nodiscard]] bool br(uint32_t relativeDepth, const DefVector& values) {
3219 if (inDeadCode()) {
3220 return true;
3221 }
3222
3223 MGoto* jump = MGoto::New(alloc());
3224 if (!addControlFlowPatch(jump, relativeDepth, MGoto::TargetIndex)) {
3225 return false;
3226 }
3227
3228 if (!pushDefs(values)) {
3229 return false;
3230 }
3231
3232 curBlock_->end(jump);
3233 curBlock_ = nullptr;
3234 return true;
3235 }
3236
3237 [[nodiscard]] bool brIf(uint32_t relativeDepth, const DefVector& values,
3238 MDefinition* condition, BranchHint branchHint) {
3239 if (inDeadCode()) {
3240 return true;
3241 }
3242
3243 MBasicBlock* joinBlock = nullptr;
3244 if (!newBlock(curBlock_, &joinBlock)) {
3245 return false;
3246 }
3247
3248 MTest* test = MTest::New(alloc(), condition, nullptr, joinBlock);
3249 if (!addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex,
3250 branchHint)) {
3251 return false;
3252 }
3253
3254 if (!pushDefs(values)) {
3255 return false;
3256 }
3257
3258 curBlock_->end(test);
3259 curBlock_ = joinBlock;
3260
3261 return true;
3262 }
3263
3264 [[nodiscard]] bool brTable(MDefinition* operand, uint32_t defaultDepth,
3265 const Uint32Vector& depths,
3266 const DefVector& values) {
3267 if (inDeadCode()) {
3268 return true;
3269 }
3270
3271 size_t numCases = depths.length();
3272 MOZ_ASSERT(numCases <= INT32_MAX)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numCases <= (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numCases <= (2147483647))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("numCases <= (2147483647)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numCases <= (2147483647)"
")"); do { *((volatile int*)__null) = 3272; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3273 MOZ_ASSERT(numCases)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numCases)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numCases))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("numCases", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3273); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numCases" ")"
); do { *((volatile int*)__null) = 3273; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3274
3275 MTableSwitch* table =
3276 MTableSwitch::New(alloc(), operand, 0, int32_t(numCases - 1));
3277
3278 size_t defaultIndex;
3279 if (!table->addDefault(nullptr, &defaultIndex)) {
3280 return false;
3281 }
3282 if (!addControlFlowPatch(table, defaultDepth, defaultIndex)) {
3283 return false;
3284 }
3285
3286 using IndexToCaseMap =
3287 HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>;
3288
3289 IndexToCaseMap indexToCase;
3290 if (!indexToCase.put(defaultDepth, defaultIndex)) {
3291 return false;
3292 }
3293
3294 for (size_t i = 0; i < numCases; i++) {
3295 if (!mirGen_.ensureBallast()) {
3296 return false;
3297 }
3298
3299 uint32_t depth = depths[i];
3300
3301 size_t caseIndex;
3302 IndexToCaseMap::AddPtr p = indexToCase.lookupForAdd(depth);
3303 if (!p) {
3304 if (!table->addSuccessor(nullptr, &caseIndex)) {
3305 return false;
3306 }
3307 if (!addControlFlowPatch(table, depth, caseIndex)) {
3308 return false;
3309 }
3310 if (!indexToCase.add(p, depth, caseIndex)) {
3311 return false;
3312 }
3313 } else {
3314 caseIndex = p->value();
3315 }
3316
3317 if (!table->addCase(caseIndex)) {
3318 return false;
3319 }
3320 }
3321
3322 if (!pushDefs(values)) {
3323 return false;
3324 }
3325
3326 curBlock_->end(table);
3327 curBlock_ = nullptr;
3328
3329 return true;
3330 }
3331
3332 /********************************************************** Exceptions ***/
3333
3334 bool inTryBlockFrom(uint32_t fromRelativeDepth, uint32_t* relativeDepth) {
3335 return iter().controlFindInnermostFrom(
3336 [](LabelKind kind, const Control& control) {
3337 return control.tryControl != nullptr && control.tryControl->inBody;
3338 },
3339 fromRelativeDepth, relativeDepth);
3340 }
3341
3342 bool inTryBlock(uint32_t* relativeDepth) {
3343 return inTryBlockFrom(0, relativeDepth);
3344 }
3345
3346 bool inTryCode() {
3347 uint32_t relativeDepth;
3348 return inTryBlock(&relativeDepth);
3349 }
3350
3351 MDefinition* loadTag(uint32_t tagIndex) {
3352 MWasmLoadInstanceDataField* tag = MWasmLoadInstanceDataField::New(
3353 alloc(), MIRType::WasmAnyRef,
3354 codeMeta_.offsetOfTagInstanceData(tagIndex), true, instancePointer_);
3355 curBlock_->add(tag);
3356 return tag;
3357 }
3358
3359 void loadPendingExceptionState(MInstruction** exception, MInstruction** tag) {
3360 *exception = MWasmLoadInstance::New(
3361 alloc(), instancePointer_, wasm::Instance::offsetOfPendingException(),
3362 MIRType::WasmAnyRef, AliasSet::Load(AliasSet::WasmPendingException));
3363 curBlock_->add(*exception);
3364
3365 *tag = MWasmLoadInstance::New(
3366 alloc(), instancePointer_,
3367 wasm::Instance::offsetOfPendingExceptionTag(), MIRType::WasmAnyRef,
3368 AliasSet::Load(AliasSet::WasmPendingException));
3369 curBlock_->add(*tag);
3370 }
3371
3372 [[nodiscard]] bool setPendingExceptionState(MDefinition* exception,
3373 MDefinition* tag) {
3374 // Set the pending exception object
3375 auto* exceptionAddr = MWasmDerivedPointer::New(
3376 alloc(), instancePointer_, Instance::offsetOfPendingException());
3377 curBlock_->add(exceptionAddr);
3378 auto* setException = MWasmStoreRef::New(
3379 alloc(), instancePointer_, exceptionAddr, /*valueOffset=*/0, exception,
3380 AliasSet::WasmPendingException, WasmPreBarrierKind::Normal);
3381 curBlock_->add(setException);
3382 if (!postBarrierPrecise(/*lineOrBytecode=*/0, exceptionAddr, exception)) {
3383 return false;
3384 }
3385
3386 // Set the pending exception tag object
3387 auto* exceptionTagAddr = MWasmDerivedPointer::New(
3388 alloc(), instancePointer_, Instance::offsetOfPendingExceptionTag());
3389 curBlock_->add(exceptionTagAddr);
3390 auto* setExceptionTag = MWasmStoreRef::New(
3391 alloc(), instancePointer_, exceptionTagAddr, /*valueOffset=*/0, tag,
3392 AliasSet::WasmPendingException, WasmPreBarrierKind::Normal);
3393 curBlock_->add(setExceptionTag);
3394 return postBarrierPrecise(/*lineOrBytecode=*/0, exceptionTagAddr, tag);
3395 }
3396
3397 [[nodiscard]] bool addPadPatch(MControlInstruction* ins,
3398 size_t relativeTryDepth) {
3399 Control& control = iter().controlItem(relativeTryDepth);
3400 return control.tryControl->landingPadPatches.emplaceBack(ins);
3401 }
3402
3403 [[nodiscard]] bool endWithPadPatch(uint32_t relativeTryDepth) {
3404 MGoto* jumpToLandingPad = MGoto::New(alloc());
3405 curBlock_->end(jumpToLandingPad);
3406 return addPadPatch(jumpToLandingPad, relativeTryDepth);
3407 }
3408
3409 [[nodiscard]] bool delegatePadPatches(const ControlInstructionVector& patches,
3410 uint32_t relativeDepth) {
3411 if (patches.empty()) {
3412 return true;
3413 }
3414
3415 // Find where we are delegating the pad patches to.
3416 ControlInstructionVector* targetPatches;
3417 uint32_t targetRelativeDepth;
3418 if (inTryBlockFrom(relativeDepth, &targetRelativeDepth)) {
3419 targetPatches = &iter()
3420 .controlItem(targetRelativeDepth)
3421 .tryControl->landingPadPatches;
3422 } else {
3423 MOZ_ASSERT(relativeDepth <= blockDepth_ - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(relativeDepth <= blockDepth_ - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(relativeDepth <= blockDepth_
- 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("relativeDepth <= blockDepth_ - 1", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "relativeDepth <= blockDepth_ - 1"
")"); do { *((volatile int*)__null) = 3423; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3424 targetPatches = &bodyDelegatePadPatches_;
3425 }
3426
3427 // Append the delegate's pad patches to the target's.
3428 for (MControlInstruction* ins : patches) {
3429 if (!targetPatches->emplaceBack(ins)) {
3430 return false;
3431 }
3432 }
3433 return true;
3434 }
3435
3436 [[nodiscard]] bool beginTryCall(MWasmCallTryDesc* call) {
3437 call->inTry = inTryBlock(&call->relativeTryDepth);
3438 if (!call->inTry) {
3439 return true;
3440 }
3441 // Allocate a try note
3442 if (!tryNotes_.append(wasm::TryNote())) {
3443 return false;
3444 }
3445 call->tryNoteIndex = tryNotes_.length() - 1;
3446 // Allocate blocks for fallthrough and exceptions
3447 return newBlock(curBlock_, &call->fallthroughBlock) &&
3448 newBlock(curBlock_, &call->prePadBlock);
3449 }
3450
3451 [[nodiscard]] bool finishTryCall(MWasmCallTryDesc* call) {
3452 if (!call->inTry) {
3453 return true;
3454 }
3455
3456 // Switch to the prePadBlock
3457 MBasicBlock* callBlock = curBlock_;
3458 curBlock_ = call->prePadBlock;
3459
3460 // Mark this as the landing pad for the call
3461 curBlock_->add(
3462 MWasmCallLandingPrePad::New(alloc(), callBlock, call->tryNoteIndex));
3463
3464 // End with a pending jump to the landing pad
3465 if (!endWithPadPatch(call->relativeTryDepth)) {
3466 return false;
3467 }
3468
3469 // Compilation continues in the fallthroughBlock.
3470 curBlock_ = call->fallthroughBlock;
3471 return true;
3472 }
3473
3474 // Create a landing pad for a try block if there are any throwing
3475 // instructions. This is also used for the implicit rethrow landing pad used
3476 // for delegate instructions that target the outermost label.
3477 [[nodiscard]] bool createTryLandingPadIfNeeded(
3478 ControlInstructionVector& landingPadPatches, MBasicBlock** landingPad) {
3479 // If there are no pad-patches for this try control, it means there are no
3480 // instructions in the try code that could throw an exception. In this
3481 // case, all the catches are dead code, and the try code ends up equivalent
3482 // to a plain wasm block.
3483 if (landingPadPatches.empty()) {
3484 *landingPad = nullptr;
3485 return true;
3486 }
3487
3488 // Otherwise, if there are (pad-) branches from places in the try code that
3489 // may throw an exception, bind these branches to a new landing pad
3490 // block. This is done similarly to what is done in bindBranches.
3491 MControlInstruction* ins = landingPadPatches[0];
3492 MBasicBlock* pred = ins->block();
3493 if (!newBlock(pred, landingPad)) {
3494 return false;
3495 }
3496 ins->replaceSuccessor(0, *landingPad);
3497 for (size_t i = 1; i < landingPadPatches.length(); i++) {
3498 ins = landingPadPatches[i];
3499 pred = ins->block();
3500 if (!(*landingPad)->addPredecessor(alloc(), pred)) {
3501 return false;
3502 }
3503 ins->replaceSuccessor(0, *landingPad);
3504 }
3505
3506 // Set up the slots in the landing pad block.
3507 if (!setupLandingPadSlots(landingPad)) {
3508 return false;
3509 }
3510
3511 // Clear the now bound pad patches.
3512 landingPadPatches.clear();
3513 return true;
3514 }
3515
3516 [[nodiscard]] bool createTryTableLandingPad(TryControl* tryControl) {
3517 MBasicBlock* landingPad;
3518 if (!createTryLandingPadIfNeeded(tryControl->landingPadPatches,
3519 &landingPad)) {
3520 return false;
3521 }
3522
3523 // If there is no landing pad created, no exceptions were possibly thrown
3524 // and we don't need to do anything here.
3525 if (!landingPad) {
3526 return true;
3527 }
3528
3529 MBasicBlock* originalBlock = curBlock_;
3530 curBlock_ = landingPad;
3531
3532 bool hadCatchAll = false;
3533 for (const TryTableCatch& tryTableCatch : tryControl->catches) {
3534 MOZ_ASSERT(numPushed(curBlock_) == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numPushed(curBlock_) == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numPushed(curBlock_) == 2)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("numPushed(curBlock_) == 2"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 2"
")"); do { *((volatile int*)__null) = 3534; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3535
3536 // Handle a catch_all by jumping to the target block
3537 if (tryTableCatch.tagIndex == CatchAllIndex) {
3538 // Get the exception from the slots we pushed when adding
3539 // control flow patches.
3540 curBlock_->pop();
3541 MDefinition* exception = curBlock_->pop();
3542
3543 // Capture the exnref value if we need to
3544 DefVector values;
3545 if (tryTableCatch.captureExnRef && !values.append(exception)) {
3546 return false;
3547 }
3548
3549 // Branch to the catch_all code
3550 if (!br(tryTableCatch.labelRelativeDepth, values)) {
3551 return false;
3552 }
3553
3554 // Break from the loop and skip the implicit rethrow that's needed
3555 // if we didn't have a catch_all
3556 hadCatchAll = true;
3557 break;
3558 }
3559
3560 // Handle a tagged catch by doing a compare and branch on the tag index,
3561 // jumping to a catch block if they match, or else to a fallthrough block
3562 // to continue the landing pad.
3563 MBasicBlock* catchBlock = nullptr;
3564 MBasicBlock* fallthroughBlock = nullptr;
3565 if (!newBlock(curBlock_, &catchBlock) ||
3566 !newBlock(curBlock_, &fallthroughBlock)) {
3567 return false;
3568 }
3569
3570 // Get the exception and its tag from the slots we pushed when adding
3571 // control flow patches.
3572 MDefinition* exceptionTag = curBlock_->pop();
3573 curBlock_->pop();
3574
3575 // Branch to the catch block if the exception's tag matches this catch
3576 // block's tag.
3577 MDefinition* catchTag = loadTag(tryTableCatch.tagIndex);
3578 MDefinition* matchesCatchTag = compare(exceptionTag, catchTag, JSOp::Eq,
3579 MCompare::Compare_WasmAnyRef);
3580 curBlock_->end(
3581 MTest::New(alloc(), matchesCatchTag, catchBlock, fallthroughBlock));
3582
3583 // Set up the catch block by extracting the values from the exception
3584 // object.
3585 curBlock_ = catchBlock;
3586
3587 // Remove the tag and exception slots from the block, they are no
3588 // longer necessary.
3589 curBlock_->pop();
3590 MDefinition* exception = curBlock_->pop();
3591 MOZ_ASSERT(numPushed(curBlock_) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numPushed(curBlock_) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numPushed(curBlock_) == 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("numPushed(curBlock_) == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 3591; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3592
3593 // Extract the exception values for the catch block
3594 DefVector values;
3595 if (!loadExceptionValues(exception, tryTableCatch.tagIndex, &values)) {
3596 return false;
3597 }
3598 if (tryTableCatch.captureExnRef && !values.append(exception)) {
3599 return false;
3600 }
3601
3602 if (!br(tryTableCatch.labelRelativeDepth, values)) {
3603 return false;
3604 }
3605
3606 curBlock_ = fallthroughBlock;
3607 }
3608
3609 // If there was no catch_all, we must rethrow this exception.
3610 if (!hadCatchAll) {
3611 MOZ_ASSERT(numPushed(curBlock_) == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numPushed(curBlock_) == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numPushed(curBlock_) == 2)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("numPushed(curBlock_) == 2"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3611); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 2"
")"); do { *((volatile int*)__null) = 3611; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3612 MDefinition* tag = curBlock_->pop();
3613 MDefinition* exception = curBlock_->pop();
3614 MOZ_ASSERT(numPushed(curBlock_) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numPushed(curBlock_) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numPushed(curBlock_) == 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("numPushed(curBlock_) == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3614); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 3614; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3615
3616 if (!throwFrom(exception, tag)) {
3617 return false;
3618 }
3619 }
3620
3621 curBlock_ = originalBlock;
3622 return true;
3623 }
3624
3625 // Consume the pending exception state from instance, and set up the slots
3626 // of the landing pad with the exception state.
3627 [[nodiscard]] bool setupLandingPadSlots(MBasicBlock** landingPad) {
3628 MBasicBlock* prevBlock = curBlock_;
3629 curBlock_ = *landingPad;
3630
3631 // Load the pending exception and tag
3632 MInstruction* exception;
3633 MInstruction* tag;
3634 loadPendingExceptionState(&exception, &tag);
3635
3636 // Clear the pending exception and tag
3637 auto* null = constantNullRef();
3638 if (!setPendingExceptionState(null, null)) {
3639 return false;
3640 }
3641
3642 // Push the exception and its tag on the stack to make them available
3643 // to the landing pad blocks.
3644 if (!curBlock_->ensureHasSlots(2)) {
3645 return false;
3646 }
3647 curBlock_->push(exception);
3648 curBlock_->push(tag);
3649 *landingPad = curBlock_;
3650
3651 curBlock_ = prevBlock;
3652 return true;
3653 }
3654
3655 [[nodiscard]] bool startTry() {
3656 Control& control = iter().controlItem();
3657 control.block = curBlock_;
3658 control.tryControl = newTryControl();
3659 if (!control.tryControl) {
3660 return false;
3661 }
3662 control.tryControl->inBody = true;
3663 return startBlock();
3664 }
3665
3666 [[nodiscard]] bool startTryTable(TryTableCatchVector&& catches) {
3667 Control& control = iter().controlItem();
3668 control.block = curBlock_;
3669 control.tryControl = newTryControl();
3670 if (!control.tryControl) {
3671 return false;
3672 }
3673 control.tryControl->inBody = true;
3674 control.tryControl->catches = std::move(catches);
3675 return startBlock();
3676 }
3677
3678 [[nodiscard]] bool joinTryOrCatchBlock(Control& control) {
3679 // If the try or catch block ended with dead code, there is no need to
3680 // do any control flow join.
3681 if (inDeadCode()) {
3682 return true;
3683 }
3684
3685 // This is a split path which we'll need to join later, using a control
3686 // flow patch.
3687 MOZ_ASSERT(!curBlock_->hasLastIns())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!curBlock_->hasLastIns())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!curBlock_->hasLastIns())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!curBlock_->hasLastIns()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3687); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!curBlock_->hasLastIns()"
")"); do { *((volatile int*)__null) = 3687; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3688 MGoto* jump = MGoto::New(alloc());
3689 if (!addControlFlowPatch(jump, 0, MGoto::TargetIndex)) {
3690 return false;
3691 }
3692
3693 // Finish the current block with the control flow patch instruction.
3694 curBlock_->end(jump);
3695 return true;
3696 }
3697
3698 // Finish the previous block (either a try or catch block) and then setup a
3699 // new catch block.
3700 [[nodiscard]] bool switchToCatch(Control& control, LabelKind fromKind,
3701 uint32_t tagIndex) {
3702 // Mark this control node as being no longer in the body of the try
3703 control.tryControl->inBody = false;
3704
3705 // If there is no control block, then either:
3706 // - the entry of the try block is dead code, or
3707 // - there is no landing pad for the try-catch.
3708 // In either case, any catch will be dead code.
3709 if (!control.block) {
3710 MOZ_ASSERT(inDeadCode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inDeadCode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(inDeadCode()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("inDeadCode()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 3710; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3711 return true;
3712 }
3713
3714 // Join the previous try or catch block with a patch to the future join of
3715 // the whole try-catch block.
3716 if (!joinTryOrCatchBlock(control)) {
3717 return false;
3718 }
3719
3720 // If we are switching from the try block, create the landing pad. This is
3721 // guaranteed to happen once and only once before processing catch blocks.
3722 if (fromKind == LabelKind::Try) {
3723 MBasicBlock* padBlock = nullptr;
3724 if (!createTryLandingPadIfNeeded(control.tryControl->landingPadPatches,
3725 &padBlock)) {
3726 return false;
3727 }
3728 // Set the control block for this try-catch to the landing pad.
3729 control.block = padBlock;
3730 }
3731
3732 // If there is no landing pad, then this and following catches are dead
3733 // code.
3734 if (!control.block) {
3735 curBlock_ = nullptr;
3736 return true;
3737 }
3738
3739 // Switch to the landing pad.
3740 curBlock_ = control.block;
3741
3742 // Handle a catch_all by immediately jumping to a new block. We require a
3743 // new block (as opposed to just emitting the catch_all code in the current
3744 // block) because rethrow requires the exception/tag to be present in the
3745 // landing pad's slots, while the catch_all block must not have the
3746 // exception/tag in slots.
3747 if (tagIndex == CatchAllIndex) {
3748 MBasicBlock* catchAllBlock = nullptr;
3749 if (!goToNewBlock(curBlock_, &catchAllBlock)) {
3750 return false;
3751 }
3752 // Compilation will continue in the catch_all block.
3753 curBlock_ = catchAllBlock;
3754 // Remove the tag and exception slots from the block, they are no
3755 // longer necessary.
3756 curBlock_->pop();
3757 curBlock_->pop();
3758 return true;
3759 }
3760
3761 // Handle a tagged catch by doing a compare and branch on the tag index,
3762 // jumping to a catch block if they match, or else to a fallthrough block
3763 // to continue the landing pad.
3764 MBasicBlock* catchBlock = nullptr;
3765 MBasicBlock* fallthroughBlock = nullptr;
3766 if (!newBlock(curBlock_, &catchBlock) ||
3767 !newBlock(curBlock_, &fallthroughBlock)) {
3768 return false;
3769 }
3770
3771 // Get the exception and its tag from the slots we pushed when adding
3772 // control flow patches.
3773 MDefinition* exceptionTag = curBlock_->pop();
3774 MDefinition* exception = curBlock_->pop();
Value stored to 'exception' during its initialization is never read
3775
3776 // Branch to the catch block if the exception's tag matches this catch
3777 // block's tag.
3778 MDefinition* catchTag = loadTag(tagIndex);
3779 MDefinition* matchesCatchTag =
3780 compare(exceptionTag, catchTag, JSOp::Eq, MCompare::Compare_WasmAnyRef);
3781 curBlock_->end(
3782 MTest::New(alloc(), matchesCatchTag, catchBlock, fallthroughBlock));
3783
3784 // The landing pad will continue in the fallthrough block
3785 control.block = fallthroughBlock;
3786
3787 // Set up the catch block by extracting the values from the exception
3788 // object.
3789 curBlock_ = catchBlock;
3790
3791 // Remove the tag and exception slots from the block, they are no
3792 // longer necessary.
3793 curBlock_->pop();
3794 exception = curBlock_->pop();
3795
3796 // Extract the exception values for the catch block
3797 DefVector values;
3798 if (!loadExceptionValues(exception, tagIndex, &values)) {
3799 return false;
3800 }
3801 iter().setResults(values.length(), values);
3802 return true;
3803 }
3804
3805 [[nodiscard]] bool loadExceptionValues(MDefinition* exception,
3806 uint32_t tagIndex, DefVector* values) {
3807 SharedTagType tagType = codeMeta().tags[tagIndex].type;
3808 const ValTypeVector& params = tagType->argTypes();
3809 const TagOffsetVector& offsets = tagType->argOffsets();
3810
3811 // Get the data pointer from the exception object
3812 auto* data = MWasmLoadField::New(
3813 alloc(), exception, WasmExceptionObject::offsetOfData(),
3814 MIRType::Pointer, MWideningOp::None, AliasSet::Load(AliasSet::Any));
3815 if (!data) {
3816 return false;
3817 }
3818 curBlock_->add(data);
3819
3820 // Presize the values vector to the number of params
3821 if (!values->reserve(params.length())) {
3822 return false;
3823 }
3824
3825 // Load each value from the data pointer
3826 for (size_t i = 0; i < params.length(); i++) {
3827 if (!mirGen_.ensureBallast()) {
3828 return false;
3829 }
3830 auto* load = MWasmLoadFieldKA::New(
3831 alloc(), exception, data, offsets[i], params[i].toMIRType(),
3832 MWideningOp::None, AliasSet::Load(AliasSet::Any));
3833 if (!load || !values->append(load)) {
3834 return false;
3835 }
3836 curBlock_->add(load);
3837 }
3838 return true;
3839 }
3840
3841 [[nodiscard]] bool finishTryCatch(LabelKind kind, Control& control,
3842 DefVector* defs) {
3843 switch (kind) {
3844 case LabelKind::Try: {
3845 // This is a catchless try, we must delegate all throwing instructions
3846 // to the nearest enclosing try block if one exists, or else to the
3847 // body block which will handle it in emitBodyDelegateThrowPad. We
3848 // specify a relativeDepth of '1' to delegate outside of the still
3849 // active try block.
3850 uint32_t relativeDepth = 1;
3851 if (!delegatePadPatches(control.tryControl->landingPadPatches,
3852 relativeDepth)) {
3853 return false;
3854 }
3855 break;
3856 }
3857 case LabelKind::Catch: {
3858 MOZ_ASSERT(!control.tryControl->inBody)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!control.tryControl->inBody)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!control.tryControl->inBody
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!control.tryControl->inBody", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3858); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl->inBody"
")"); do { *((volatile int*)__null) = 3858; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3859 // This is a try without a catch_all, we must have a rethrow at the end
3860 // of the landing pad (if any).
3861 MBasicBlock* padBlock = control.block;
3862 if (padBlock) {
3863 MBasicBlock* prevBlock = curBlock_;
3864 curBlock_ = padBlock;
3865 MDefinition* tag = curBlock_->pop();
3866 MDefinition* exception = curBlock_->pop();
3867 if (!throwFrom(exception, tag)) {
3868 return false;
3869 }
3870 curBlock_ = prevBlock;
3871 }
3872 break;
3873 }
3874 case LabelKind::CatchAll: {
3875 MOZ_ASSERT(!control.tryControl->inBody)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!control.tryControl->inBody)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!control.tryControl->inBody
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!control.tryControl->inBody", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3875); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl->inBody"
")"); do { *((volatile int*)__null) = 3875; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3876 // This is a try with a catch_all, and requires no special handling.
3877 break;
3878 }
3879 default:
3880 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3880); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 3880; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
3881 }
3882
3883 // Finish the block, joining the try and catch blocks
3884 return finishBlock(defs);
3885 }
3886
3887 [[nodiscard]] bool finishTryTable(Control& control, DefVector* defs) {
3888 // Mark this control as no longer in the body of the try
3889 control.tryControl->inBody = false;
3890 // Create a landing pad for all of the catches
3891 if (!createTryTableLandingPad(control.tryControl.get())) {
3892 return false;
3893 }
3894 // Finish the block, joining the try and catch blocks
3895 return finishBlock(defs);
3896 }
3897
3898 [[nodiscard]] bool emitBodyDelegateThrowPad(Control& control) {
3899 // Create a landing pad for any throwing instructions
3900 MBasicBlock* padBlock;
3901 if (!createTryLandingPadIfNeeded(bodyDelegatePadPatches_, &padBlock)) {
3902 return false;
3903 }
3904
3905 // If no landing pad was necessary, then we don't need to do anything here
3906 if (!padBlock) {
3907 return true;
3908 }
3909
3910 // Switch to the landing pad and rethrow the exception
3911 MBasicBlock* prevBlock = curBlock_;
3912 curBlock_ = padBlock;
3913 MDefinition* tag = curBlock_->pop();
3914 MDefinition* exception = curBlock_->pop();
3915 if (!throwFrom(exception, tag)) {
3916 return false;
3917 }
3918 curBlock_ = prevBlock;
3919 return true;
3920 }
3921
3922 [[nodiscard]] bool emitNewException(MDefinition* tag,
3923 MDefinition** exception) {
3924 return emitInstanceCall1(readBytecodeOffset(), SASigExceptionNew, tag,
3925 exception);
3926 }
3927
3928 [[nodiscard]] bool emitThrow(uint32_t tagIndex, const DefVector& argValues) {
3929 if (inDeadCode()) {
3930 return true;
3931 }
3932 uint32_t bytecodeOffset = readBytecodeOffset();
3933
3934 // Load the tag
3935 MDefinition* tag = loadTag(tagIndex);
3936 if (!tag) {
3937 return false;
3938 }
3939
3940 // Allocate an exception object
3941 MDefinition* exception;
3942 if (!emitNewException(tag, &exception)) {
3943 return false;
3944 }
3945
3946 // Load the data pointer from the object
3947 auto* data = MWasmLoadField::New(
3948 alloc(), exception, WasmExceptionObject::offsetOfData(),
3949 MIRType::Pointer, MWideningOp::None, AliasSet::Load(AliasSet::Any));
3950 if (!data) {
3951 return false;
3952 }
3953 curBlock_->add(data);
3954
3955 // Store the params into the data pointer
3956 SharedTagType tagType = codeMeta_.tags[tagIndex].type;
3957 for (size_t i = 0; i < tagType->argOffsets().length(); i++) {
3958 if (!mirGen_.ensureBallast()) {
3959 return false;
3960 }
3961 ValType type = tagType->argTypes()[i];
3962 uint32_t offset = tagType->argOffsets()[i];
3963
3964 if (!type.isRefRepr()) {
3965 auto* store = MWasmStoreFieldKA::New(alloc(), exception, data, offset,
3966 argValues[i], MNarrowingOp::None,
3967 AliasSet::Store(AliasSet::Any));
3968 if (!store) {
3969 return false;
3970 }
3971 curBlock_->add(store);
3972 continue;
3973 }
3974
3975 // Store the new value
3976 auto* store = MWasmStoreFieldRefKA::New(
3977 alloc(), instancePointer_, exception, data, offset, argValues[i],
3978 AliasSet::Store(AliasSet::Any), Nothing(), WasmPreBarrierKind::None);
3979 if (!store) {
3980 return false;
3981 }
3982 curBlock_->add(store);
3983
3984 // Call the post-write barrier
3985 if (!postBarrierImmediate(bytecodeOffset, exception, data, offset,
3986 argValues[i])) {
3987 return false;
3988 }
3989 }
3990
3991 // Throw the exception
3992 return throwFrom(exception, tag);
3993 }
3994
3995 [[nodiscard]] bool emitThrowRef(MDefinition* exnRef) {
3996 if (inDeadCode()) {
3997 return true;
3998 }
3999
4000 // The exception must be non-null
4001 if (!refAsNonNull(exnRef)) {
4002 return false;
4003 }
4004
4005 // Call Instance::throwException to perform tag unpacking and throw the
4006 // exception
4007 if (!emitInstanceCall1(readBytecodeOffset(), SASigThrowException, exnRef)) {
4008 return false;
4009 }
4010 unreachableTrap();
4011
4012 curBlock_ = nullptr;
4013 return true;
4014 }
4015
4016 [[nodiscard]] bool throwFrom(MDefinition* exn, MDefinition* tag) {
4017 if (inDeadCode()) {
4018 return true;
4019 }
4020
4021 // Check if there is a local catching try control, and if so, then add a
4022 // pad-patch to its tryPadPatches.
4023 uint32_t relativeTryDepth;
4024 if (inTryBlock(&relativeTryDepth)) {
4025 // Set the pending exception state, the landing pad will read from this
4026 if (!setPendingExceptionState(exn, tag)) {
4027 return false;
4028 }
4029
4030 // End with a pending jump to the landing pad
4031 if (!endWithPadPatch(relativeTryDepth)) {
4032 return false;
4033 }
4034 curBlock_ = nullptr;
4035 return true;
4036 }
4037
4038 // If there is no surrounding catching block, call an instance method to
4039 // throw the exception.
4040 if (!emitInstanceCall1(readBytecodeOffset(), SASigThrowException, exn)) {
4041 return false;
4042 }
4043 unreachableTrap();
4044
4045 curBlock_ = nullptr;
4046 return true;
4047 }
4048
4049 [[nodiscard]] bool emitRethrow(uint32_t relativeDepth) {
4050 if (inDeadCode()) {
4051 return true;
4052 }
4053
4054 Control& control = iter().controlItem(relativeDepth);
4055 MBasicBlock* pad = control.block;
4056 MOZ_ASSERT(pad)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pad)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(pad))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("pad", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pad" ")"); do
{ *((volatile int*)__null) = 4056; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4057 MOZ_ASSERT(pad->nslots() > 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pad->nslots() > 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pad->nslots() > 1))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("pad->nslots() > 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4057); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pad->nslots() > 1"
")"); do { *((volatile int*)__null) = 4057; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4058 MOZ_ASSERT(iter().controlKind(relativeDepth) == LabelKind::Catch ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter().controlKind(relativeDepth) == LabelKind::Catch
|| iter().controlKind(relativeDepth) == LabelKind::CatchAll)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(iter().controlKind(relativeDepth) == LabelKind::Catch
|| iter().controlKind(relativeDepth) == LabelKind::CatchAll)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
")"); do { *((volatile int*)__null) = 4059; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4059 iter().controlKind(relativeDepth) == LabelKind::CatchAll)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter().controlKind(relativeDepth) == LabelKind::Catch
|| iter().controlKind(relativeDepth) == LabelKind::CatchAll)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(iter().controlKind(relativeDepth) == LabelKind::Catch
|| iter().controlKind(relativeDepth) == LabelKind::CatchAll)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
")"); do { *((volatile int*)__null) = 4059; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4060
4061 // The exception will always be the last slot in the landing pad.
4062 size_t exnSlotPosition = pad->nslots() - 2;
4063 MDefinition* tag = pad->getSlot(exnSlotPosition + 1);
4064 MDefinition* exception = pad->getSlot(exnSlotPosition);
4065 MOZ_ASSERT(exception->type() == MIRType::WasmAnyRef &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(exception->type() == MIRType::WasmAnyRef &&
tag->type() == MIRType::WasmAnyRef)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(exception->type() == MIRType
::WasmAnyRef && tag->type() == MIRType::WasmAnyRef
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4066); AnnotateMozCrashReason("MOZ_ASSERT" "(" "exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4066; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4066 tag->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(exception->type() == MIRType::WasmAnyRef &&
tag->type() == MIRType::WasmAnyRef)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(exception->type() == MIRType
::WasmAnyRef && tag->type() == MIRType::WasmAnyRef
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4066); AnnotateMozCrashReason("MOZ_ASSERT" "(" "exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4066; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4067 return throwFrom(exception, tag);
4068 }
4069
4070 /*********************************************** Instance call helpers ***/
4071
4072 // Do not call this function directly -- it offers no protection against
4073 // mis-counting of arguments. Instead call one of
4074 // ::emitInstanceCall{0,1,2,3,4,5,6}.
4075 //
4076 // Emits a call to the Instance function indicated by `callee`. This is
4077 // assumed to take an Instance pointer as its first argument. The remaining
4078 // args are taken from `args`, which is assumed to hold `numArgs` entries.
4079 // If `result` is non-null, the MDefinition* holding the return value is
4080 // written to `*result`.
4081 [[nodiscard]] bool emitInstanceCallN(uint32_t lineOrBytecode,
4082 const SymbolicAddressSignature& callee,
4083 MDefinition** args, size_t numArgs,
4084 MDefinition** result = nullptr) {
4085 // Check that the first formal parameter is plausibly an Instance pointer.
4086 MOZ_ASSERT(callee.numArgs > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callee.numArgs > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(callee.numArgs > 0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("callee.numArgs > 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4086); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.numArgs > 0"
")"); do { *((volatile int*)__null) = 4086; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4087 MOZ_ASSERT(callee.argTypes[0] == MIRType::Pointer)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callee.argTypes[0] == MIRType::Pointer)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(callee.argTypes[0] == MIRType::Pointer))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("callee.argTypes[0] == MIRType::Pointer"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4087); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.argTypes[0] == MIRType::Pointer"
")"); do { *((volatile int*)__null) = 4087; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4088 // Check we agree on the number of args.
4089 MOZ_ASSERT(numArgs + 1 /* the instance pointer */ == callee.numArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numArgs + 1 == callee.numArgs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numArgs + 1 == callee.numArgs
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"numArgs + 1 == callee.numArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4089); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numArgs + 1 == callee.numArgs"
")"); do { *((volatile int*)__null) = 4089; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4090 // Check we agree on whether a value is returned.
4091 MOZ_ASSERT((result == nullptr) == (callee.retType == MIRType::None))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((result == nullptr) == (callee.retType == MIRType::None
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((result == nullptr) == (callee.retType == MIRType::None
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(result == nullptr) == (callee.retType == MIRType::None)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(result == nullptr) == (callee.retType == MIRType::None)"
")"); do { *((volatile int*)__null) = 4091; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4092
4093 // If we are in dead code, it can happen that some of the `args` entries
4094 // are nullptr, which will look like an OOM to the logic below. So exit
4095 // at this point. `passInstance`, `passArg`, `finishCall` and
4096 // `builtinInstanceMethodCall` all do nothing in dead code, so it's valid
4097 // to exit here.
4098 if (inDeadCode()) {
4099 if (result) {
4100 *result = nullptr;
4101 }
4102 return true;
4103 }
4104
4105 // Check all args for signs of OOMness before attempting to allocating any
4106 // more memory.
4107 for (size_t i = 0; i < numArgs; i++) {
4108 if (!args[i]) {
4109 if (result) {
4110 *result = nullptr;
4111 }
4112 return false;
4113 }
4114 }
4115
4116 // Finally, construct the call.
4117 CallCompileState ccsArgs;
4118 if (!passInstance(callee.argTypes[0], &ccsArgs)) {
4119 return false;
4120 }
4121 for (size_t i = 0; i < numArgs; i++) {
4122 if (!passArg(args[i], callee.argTypes[i + 1], &ccsArgs)) {
4123 return false;
4124 }
4125 }
4126 if (!finishCall(&ccsArgs)) {
4127 return false;
4128 }
4129 return builtinInstanceMethodCall(callee, lineOrBytecode, ccsArgs, result);
4130 }
4131
4132 [[nodiscard]] bool emitInstanceCall0(uint32_t lineOrBytecode,
4133 const SymbolicAddressSignature& callee,
4134 MDefinition** result = nullptr) {
4135 MDefinition* args[0] = {};
4136 return emitInstanceCallN(lineOrBytecode, callee, args, 0, result);
4137 }
4138 [[nodiscard]] bool emitInstanceCall1(uint32_t lineOrBytecode,
4139 const SymbolicAddressSignature& callee,
4140 MDefinition* arg1,
4141 MDefinition** result = nullptr) {
4142 MDefinition* args[1] = {arg1};
4143 return emitInstanceCallN(lineOrBytecode, callee, args, 1, result);
4144 }
4145 [[nodiscard]] bool emitInstanceCall2(uint32_t lineOrBytecode,
4146 const SymbolicAddressSignature& callee,
4147 MDefinition* arg1, MDefinition* arg2,
4148 MDefinition** result = nullptr) {
4149 MDefinition* args[2] = {arg1, arg2};
4150 return emitInstanceCallN(lineOrBytecode, callee, args, 2, result);
4151 }
4152 [[nodiscard]] bool emitInstanceCall3(uint32_t lineOrBytecode,
4153 const SymbolicAddressSignature& callee,
4154 MDefinition* arg1, MDefinition* arg2,
4155 MDefinition* arg3,
4156 MDefinition** result = nullptr) {
4157 MDefinition* args[3] = {arg1, arg2, arg3};
4158 return emitInstanceCallN(lineOrBytecode, callee, args, 3, result);
4159 }
4160 [[nodiscard]] bool emitInstanceCall4(uint32_t lineOrBytecode,
4161 const SymbolicAddressSignature& callee,
4162 MDefinition* arg1, MDefinition* arg2,
4163 MDefinition* arg3, MDefinition* arg4,
4164 MDefinition** result = nullptr) {
4165 MDefinition* args[4] = {arg1, arg2, arg3, arg4};
4166 return emitInstanceCallN(lineOrBytecode, callee, args, 4, result);
4167 }
4168 [[nodiscard]] bool emitInstanceCall5(uint32_t lineOrBytecode,
4169 const SymbolicAddressSignature& callee,
4170 MDefinition* arg1, MDefinition* arg2,
4171 MDefinition* arg3, MDefinition* arg4,
4172 MDefinition* arg5,
4173 MDefinition** result = nullptr) {
4174 MDefinition* args[5] = {arg1, arg2, arg3, arg4, arg5};
4175 return emitInstanceCallN(lineOrBytecode, callee, args, 5, result);
4176 }
4177 [[nodiscard]] bool emitInstanceCall6(uint32_t lineOrBytecode,
4178 const SymbolicAddressSignature& callee,
4179 MDefinition* arg1, MDefinition* arg2,
4180 MDefinition* arg3, MDefinition* arg4,
4181 MDefinition* arg5, MDefinition* arg6,
4182 MDefinition** result = nullptr) {
4183 MDefinition* args[6] = {arg1, arg2, arg3, arg4, arg5, arg6};
4184 return emitInstanceCallN(lineOrBytecode, callee, args, 6, result);
4185 }
4186
4187 /******************************** WasmGC: low level load/store helpers ***/
4188
4189 // Given a (StorageType, FieldExtension) pair, produce the (MIRType,
4190 // MWideningOp) pair that will give the correct operation for reading the
4191 // value from memory.
4192 static void fieldLoadInfoToMIR(StorageType type, FieldWideningOp wideningOp,
4193 MIRType* mirType, MWideningOp* mirWideningOp) {
4194 switch (type.kind()) {
4195 case StorageType::I8: {
4196 switch (wideningOp) {
4197 case FieldWideningOp::Signed:
4198 *mirType = MIRType::Int32;
4199 *mirWideningOp = MWideningOp::FromS8;
4200 return;
4201 case FieldWideningOp::Unsigned:
4202 *mirType = MIRType::Int32;
4203 *mirWideningOp = MWideningOp::FromU8;
4204 return;
4205 default:
4206 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4206); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 4206; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
4207 }
4208 }
4209 case StorageType::I16: {
4210 switch (wideningOp) {
4211 case FieldWideningOp::Signed:
4212 *mirType = MIRType::Int32;
4213 *mirWideningOp = MWideningOp::FromS16;
4214 return;
4215 case FieldWideningOp::Unsigned:
4216 *mirType = MIRType::Int32;
4217 *mirWideningOp = MWideningOp::FromU16;
4218 return;
4219 default:
4220 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4220); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 4220; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
4221 }
4222 }
4223 default: {
4224 switch (wideningOp) {
4225 case FieldWideningOp::None:
4226 *mirType = type.toMIRType();
4227 *mirWideningOp = MWideningOp::None;
4228 return;
4229 default:
4230 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4230); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 4230; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
4231 }
4232 }
4233 }
4234 }
4235
4236 // Given a StorageType, return the Scale required when accessing array
4237 // elements of this type.
4238 static Scale scaleFromFieldType(StorageType type) {
4239 if (type.kind() == StorageType::V128) {
4240 // V128 is accessed differently, so this scale will not be used.
4241 return Scale::Invalid;
4242 }
4243 return ShiftToScale(type.indexingShift());
4244 }
4245
4246 // Given a StorageType, produce the MNarrowingOp required for writing the
4247 // value to memory.
4248 static MNarrowingOp fieldStoreInfoToMIR(StorageType type) {
4249 switch (type.kind()) {
4250 case StorageType::I8:
4251 return MNarrowingOp::To8;
4252 case StorageType::I16:
4253 return MNarrowingOp::To16;
4254 default:
4255 return MNarrowingOp::None;
4256 }
4257 }
4258
4259 // Generate a write of `value` at address `base + offset`, where `offset` is
4260 // known at JIT time. If the written value is a reftype, the previous value
4261 // at `base + offset` will be retrieved and handed off to the post-write
4262 // barrier. `keepAlive` will be referenced by the instruction so as to hold
4263 // it live (from the GC's point of view).
4264 [[nodiscard]] bool writeGcValueAtBasePlusOffset(
4265 uint32_t lineOrBytecode, StorageType type, MDefinition* keepAlive,
4266 AliasSet::Flag aliasBitset, MDefinition* value, MDefinition* base,
4267 uint32_t offset, bool needsTrapInfo, WasmPreBarrierKind preBarrierKind) {
4268 MOZ_ASSERT(aliasBitset != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aliasBitset != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aliasBitset != 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aliasBitset != 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4268); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4268; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4269 MOZ_ASSERT(keepAlive->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(keepAlive->type() == MIRType::WasmAnyRef)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(keepAlive->type() == MIRType::WasmAnyRef))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("keepAlive->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4269); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4269; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4270 MOZ_ASSERT(type.widenToValType().toMIRType() == value->type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type.widenToValType().toMIRType() == value->type(
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(type.widenToValType().toMIRType() == value->type(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("type.widenToValType().toMIRType() == value->type()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4270); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType().toMIRType() == value->type()"
")"); do { *((volatile int*)__null) = 4270; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4271 MNarrowingOp narrowingOp = fieldStoreInfoToMIR(type);
4272
4273 if (!type.isRefRepr()) {
4274 MaybeTrapSiteInfo maybeTrap;
4275 if (needsTrapInfo) {
4276 maybeTrap.emplace(getTrapSiteInfo());
4277 }
4278 auto* store = MWasmStoreFieldKA::New(
4279 alloc(), keepAlive, base, offset, value, narrowingOp,
4280 AliasSet::Store(aliasBitset), maybeTrap);
4281 if (!store) {
4282 return false;
4283 }
4284 curBlock_->add(store);
4285 return true;
4286 }
4287
4288 // Otherwise it's a ref store. Load the previous value so we can show it
4289 // to the post-write barrier.
4290 //
4291 // Optimisation opportunity: for the case where this field write results
4292 // from struct.new, the old value is always zero. So we should synthesise
4293 // a suitable zero constant rather than reading it from the object. See
4294 // also bug 1799999.
4295 MOZ_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp::
None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 4295; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4296 MOZ_ASSERT(type.widenToValType() == type.valType())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type.widenToValType() == type.valType())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(type.widenToValType() == type.valType()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("type.widenToValType() == type.valType()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType() == type.valType()"
")"); do { *((volatile int*)__null) = 4296; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4297
4298 // Store the new value
4299 auto* store = MWasmStoreFieldRefKA::New(
4300 alloc(), instancePointer_, keepAlive, base, offset, value,
4301 AliasSet::Store(aliasBitset), mozilla::Some(getTrapSiteInfo()),
4302 preBarrierKind);
4303 if (!store) {
4304 return false;
4305 }
4306 curBlock_->add(store);
4307
4308 // Call the post-write barrier
4309 return postBarrierImmediate(lineOrBytecode, keepAlive, base, offset, value);
4310 }
4311
4312 // Generate a write of `value` at address `base + index * scale`, where
4313 // `scale` is known at JIT-time. If the written value is a reftype, the
4314 // previous value at `base + index * scale` will be retrieved and handed off
4315 // to the post-write barrier. `keepAlive` will be referenced by the
4316 // instruction so as to hold it live (from the GC's point of view).
4317 [[nodiscard]] bool writeGcValueAtBasePlusScaledIndex(
4318 uint32_t lineOrBytecode, StorageType type, MDefinition* keepAlive,
4319 AliasSet::Flag aliasBitset, MDefinition* value, MDefinition* base,
4320 uint32_t scale, MDefinition* index, WasmPreBarrierKind preBarrierKind) {
4321 MOZ_ASSERT(aliasBitset != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aliasBitset != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aliasBitset != 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aliasBitset != 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4321); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4321; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4322 MOZ_ASSERT(keepAlive->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(keepAlive->type() == MIRType::WasmAnyRef)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(keepAlive->type() == MIRType::WasmAnyRef))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("keepAlive->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4322; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4323 MOZ_ASSERT(type.widenToValType().toMIRType() == value->type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type.widenToValType().toMIRType() == value->type(
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(type.widenToValType().toMIRType() == value->type(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("type.widenToValType().toMIRType() == value->type()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType().toMIRType() == value->type()"
")"); do { *((volatile int*)__null) = 4323; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4324 MOZ_ASSERT(scale == 1 || scale == 2 || scale == 4 || scale == 8 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scale == 1 || scale == 2 || scale == 4 || scale == 8
|| scale == 16)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(scale == 1 || scale == 2 || scale
== 4 || scale == 8 || scale == 16))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4325); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
")"); do { *((volatile int*)__null) = 4325; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4325 scale == 16)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scale == 1 || scale == 2 || scale == 4 || scale == 8
|| scale == 16)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(scale == 1 || scale == 2 || scale
== 4 || scale == 8 || scale == 16))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4325); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
")"); do { *((volatile int*)__null) = 4325; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4326
4327 MNarrowingOp narrowingOp = fieldStoreInfoToMIR(type);
4328
4329 if (!type.isRefRepr()) {
4330 MaybeTrapSiteInfo maybeTrap;
4331 Scale scale = scaleFromFieldType(type);
4332 auto* store = MWasmStoreElementKA::New(
4333 alloc(), keepAlive, base, index, value, narrowingOp, scale,
4334 AliasSet::Store(aliasBitset), maybeTrap);
4335 if (!store) {
4336 return false;
4337 }
4338 curBlock_->add(store);
4339 return true;
4340 }
4341
4342 // Otherwise it's a ref store.
4343 MOZ_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp::
None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4343); AnnotateMozCrashReason("MOZ_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 4343; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4344 MOZ_ASSERT(type.widenToValType() == type.valType())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type.widenToValType() == type.valType())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(type.widenToValType() == type.valType()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("type.widenToValType() == type.valType()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4344); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType() == type.valType()"
")"); do { *((volatile int*)__null) = 4344; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4345
4346 // Store the new value
4347 auto* store = MWasmStoreElementRefKA::New(
4348 alloc(), instancePointer_, keepAlive, base, index, value,
4349 AliasSet::Store(aliasBitset), mozilla::Some(getTrapSiteInfo()),
4350 preBarrierKind);
4351 if (!store) {
4352 return false;
4353 }
4354 curBlock_->add(store);
4355
4356 return postBarrierIndex(lineOrBytecode, keepAlive, base, index,
4357 sizeof(void*), value);
4358 }
4359
4360 // Generate a read from address `base + offset`, where `offset` is known at
4361 // JIT time. The loaded value will be widened as described by `type` and
4362 // `fieldWideningOp`. `keepAlive` will be referenced by the instruction so as
4363 // to hold it live (from the GC's point of view).
4364 [[nodiscard]] MDefinition* readGcValueAtBasePlusOffset(
4365 StorageType type, FieldWideningOp fieldWideningOp, MDefinition* keepAlive,
4366 AliasSet::Flag aliasBitset, MDefinition* base, uint32_t offset,
4367 bool needsTrapInfo) {
4368 MOZ_ASSERT(aliasBitset != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aliasBitset != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aliasBitset != 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aliasBitset != 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4368); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4368; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4369 MOZ_ASSERT(keepAlive->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(keepAlive->type() == MIRType::WasmAnyRef)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(keepAlive->type() == MIRType::WasmAnyRef))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("keepAlive->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4369); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4369; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4370 MIRType mirType;
4371 MWideningOp mirWideningOp;
4372 fieldLoadInfoToMIR(type, fieldWideningOp, &mirType, &mirWideningOp);
4373 MaybeTrapSiteInfo maybeTrap;
4374 if (needsTrapInfo) {
4375 maybeTrap.emplace(getTrapSiteInfo());
4376 }
4377 auto* load = MWasmLoadFieldKA::New(alloc(), keepAlive, base, offset,
4378 mirType, mirWideningOp,
4379 AliasSet::Load(aliasBitset), maybeTrap);
4380 if (!load) {
4381 return nullptr;
4382 }
4383 curBlock_->add(load);
4384 return load;
4385 }
4386
4387 // Generate a read from address `base + index * scale`, where `scale` is
4388 // known at JIT-time. The loaded value will be widened as described by
4389 // `type` and `fieldWideningOp`. `keepAlive` will be referenced by the
4390 // instruction so as to hold it live (from the GC's point of view).
4391 [[nodiscard]] MDefinition* readGcArrayValueAtIndex(
4392 StorageType type, FieldWideningOp fieldWideningOp, MDefinition* keepAlive,
4393 AliasSet::Flag aliasBitset, MDefinition* base, MDefinition* index) {
4394 MOZ_ASSERT(aliasBitset != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aliasBitset != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aliasBitset != 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("aliasBitset != 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4394; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4395 MOZ_ASSERT(keepAlive->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(keepAlive->type() == MIRType::WasmAnyRef)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(keepAlive->type() == MIRType::WasmAnyRef))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("keepAlive->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4395); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4395; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4396
4397 MIRType mirType;
4398 MWideningOp mirWideningOp;
4399 fieldLoadInfoToMIR(type, fieldWideningOp, &mirType, &mirWideningOp);
4400 Scale scale = scaleFromFieldType(type);
4401 auto* load = MWasmLoadElementKA::New(
4402 alloc(), keepAlive, base, index, mirType, mirWideningOp, scale,
4403 AliasSet::Load(aliasBitset), mozilla::Some(getTrapSiteInfo()));
4404 if (!load) {
4405 return nullptr;
4406 }
4407 curBlock_->add(load);
4408 return load;
4409 }
4410
4411 /************************************************ WasmGC: type helpers ***/
4412
4413 // Returns an MDefinition holding the supertype vector for `typeIndex`.
4414 [[nodiscard]] MDefinition* loadSuperTypeVector(uint32_t typeIndex) {
4415 uint32_t stvOffset = codeMeta().offsetOfSuperTypeVector(typeIndex);
4416
4417 auto* load =
4418 MWasmLoadInstanceDataField::New(alloc(), MIRType::Pointer, stvOffset,
4419 /*isConst=*/true, instancePointer_);
4420 if (!load) {
4421 return nullptr;
4422 }
4423 curBlock_->add(load);
4424 return load;
4425 }
4426
4427 [[nodiscard]] MDefinition* loadTypeDefInstanceData(uint32_t typeIndex) {
4428 size_t offset = Instance::offsetInData(
4429 codeMeta_.offsetOfTypeDefInstanceData(typeIndex));
4430 auto* result = MWasmDerivedPointer::New(alloc(), instancePointer_, offset);
4431 if (!result) {
4432 return nullptr;
4433 }
4434 curBlock_->add(result);
4435 return result;
4436 }
4437
4438 /********************************************** WasmGC: struct helpers ***/
4439
4440 [[nodiscard]] MDefinition* createStructObject(uint32_t typeIndex,
4441 bool zeroFields) {
4442 const TypeDef& typeDef = (*codeMeta().types)[typeIndex];
4443 gc::AllocKind allocKind = WasmStructObject::allocKindForTypeDef(&typeDef);
4444 bool isOutline =
4445 WasmStructObject::requiresOutlineBytes(typeDef.structType().size_);
4446
4447 // Allocate an uninitialized struct. This requires the type definition
4448 // for the struct.
4449 MDefinition* typeDefData = loadTypeDefInstanceData(typeIndex);
4450 if (!typeDefData) {
4451 return nullptr;
4452 }
4453
4454 auto* structObject =
4455 MWasmNewStructObject::New(alloc(), instancePointer_, typeDefData,
4456 isOutline, zeroFields, allocKind);
4457 if (!structObject) {
4458 return nullptr;
4459 }
4460 curBlock_->add(structObject);
4461
4462 return structObject;
4463 }
4464
4465 // Helper function for EmitStruct{New,Set}: given a MIR pointer to a
4466 // WasmStructObject, a MIR pointer to a value, and a field descriptor,
4467 // generate MIR to write the value to the relevant field in the object.
4468 [[nodiscard]] bool writeValueToStructField(
4469 uint32_t lineOrBytecode, const StructField& field,
4470 MDefinition* structObject, MDefinition* value,
4471 WasmPreBarrierKind preBarrierKind) {
4472 StorageType fieldType = field.type;
4473 uint32_t fieldOffset = field.offset;
4474
4475 bool areaIsOutline;
4476 uint32_t areaOffset;
4477 WasmStructObject::fieldOffsetToAreaAndOffset(fieldType, fieldOffset,
4478 &areaIsOutline, &areaOffset);
4479
4480 // Make `base` point at the first byte of either the struct object as a
4481 // whole or of the out-of-line data area. And adjust `areaOffset`
4482 // accordingly.
4483 MDefinition* base;
4484 bool needsTrapInfo;
4485 if (areaIsOutline) {
4486 auto* load = MWasmLoadField::New(
4487 alloc(), structObject, WasmStructObject::offsetOfOutlineData(),
4488 MIRType::Pointer, MWideningOp::None,
4489 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer),
4490 mozilla::Some(getTrapSiteInfo()));
4491 if (!load) {
4492 return false;
4493 }
4494 curBlock_->add(load);
4495 base = load;
4496 needsTrapInfo = false;
4497 } else {
4498 base = structObject;
4499 needsTrapInfo = true;
4500 areaOffset += WasmStructObject::offsetOfInlineData();
4501 }
4502 // The transaction is to happen at `base + areaOffset`, so to speak.
4503 // After this point we must ignore `fieldOffset`.
4504
4505 // The alias set denoting the field's location, although lacking a
4506 // Load-vs-Store indication at this point.
4507 AliasSet::Flag fieldAliasSet = areaIsOutline
4508 ? AliasSet::WasmStructOutlineDataArea
4509 : AliasSet::WasmStructInlineDataArea;
4510
4511 return writeGcValueAtBasePlusOffset(lineOrBytecode, fieldType, structObject,
4512 fieldAliasSet, value, base, areaOffset,
4513 needsTrapInfo, preBarrierKind);
4514 }
4515
4516 // Helper function for EmitStructGet: given a MIR pointer to a
4517 // WasmStructObject, a field descriptor and a field widening operation,
4518 // generate MIR to read the value from the relevant field in the object.
4519 [[nodiscard]] MDefinition* readValueFromStructField(
4520 const StructField& field, FieldWideningOp wideningOp,
4521 MDefinition* structObject) {
4522 StorageType fieldType = field.type;
4523 uint32_t fieldOffset = field.offset;
4524
4525 bool areaIsOutline;
4526 uint32_t areaOffset;
4527 WasmStructObject::fieldOffsetToAreaAndOffset(fieldType, fieldOffset,
4528 &areaIsOutline, &areaOffset);
4529
4530 // Make `base` point at the first byte of either the struct object as a
4531 // whole or of the out-of-line data area. And adjust `areaOffset`
4532 // accordingly.
4533 MDefinition* base;
4534 bool needsTrapInfo;
4535 if (areaIsOutline) {
4536 auto* loadOOLptr = MWasmLoadField::New(
4537 alloc(), structObject, WasmStructObject::offsetOfOutlineData(),
4538 MIRType::Pointer, MWideningOp::None,
4539 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer),
4540 mozilla::Some(getTrapSiteInfo()));
4541 if (!loadOOLptr) {
4542 return nullptr;
4543 }
4544 curBlock_->add(loadOOLptr);
4545 base = loadOOLptr;
4546 needsTrapInfo = false;
4547 } else {
4548 base = structObject;
4549 needsTrapInfo = true;
4550 areaOffset += WasmStructObject::offsetOfInlineData();
4551 }
4552 // The transaction is to happen at `base + areaOffset`, so to speak.
4553 // After this point we must ignore `fieldOffset`.
4554
4555 // The alias set denoting the field's location, although lacking a
4556 // Load-vs-Store indication at this point.
4557 AliasSet::Flag fieldAliasSet = areaIsOutline
4558 ? AliasSet::WasmStructOutlineDataArea
4559 : AliasSet::WasmStructInlineDataArea;
4560
4561 return readGcValueAtBasePlusOffset(fieldType, wideningOp, structObject,
4562 fieldAliasSet, base, areaOffset,
4563 needsTrapInfo);
4564 }
4565
4566 /********************************* WasmGC: address-arithmetic helpers ***/
4567
4568 inline bool targetIs64Bit() const {
4569#ifdef JS_64BIT1
4570 return true;
4571#else
4572 return false;
4573#endif
4574 }
4575
4576 // Generate MIR to unsigned widen `val` out to the target word size. If
4577 // `val` is already at the target word size, this is a no-op. The only
4578 // other allowed case is where `val` is Int32 and we're compiling for a
4579 // 64-bit target, in which case a widen is generated.
4580 [[nodiscard]] MDefinition* unsignedWidenToTargetWord(MDefinition* val) {
4581 if (targetIs64Bit()) {
4582 if (val->type() == MIRType::Int32) {
4583 auto* ext = MExtendInt32ToInt64::New(alloc(), val, /*isUnsigned=*/true);
4584 if (!ext) {
4585 return nullptr;
4586 }
4587 curBlock_->add(ext);
4588 return ext;
4589 }
4590 MOZ_ASSERT(val->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(val->type() == MIRType::Int64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(val->type() == MIRType::Int64
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"val->type() == MIRType::Int64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4590); AnnotateMozCrashReason("MOZ_ASSERT" "(" "val->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 4590; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4591 return val;
4592 }
4593 MOZ_ASSERT(val->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(val->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(val->type() == MIRType::Int32
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"val->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4593); AnnotateMozCrashReason("MOZ_ASSERT" "(" "val->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4593; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4594 return val;
4595 }
4596
4597 /********************************************** WasmGC: array helpers ***/
4598
4599 // Given `arrayObject`, the address of a WasmArrayObject, generate MIR to
4600 // return the contents of the WasmArrayObject::numElements_ field.
4601 // Adds trap site info for the null check.
4602 [[nodiscard]] MDefinition* getWasmArrayObjectNumElements(
4603 MDefinition* arrayObject) {
4604 MOZ_ASSERT(arrayObject->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arrayObject->type() == MIRType::WasmAnyRef)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(arrayObject->type() == MIRType::WasmAnyRef))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("arrayObject->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4604); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4604; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4605
4606 auto* numElements = MWasmLoadField::New(
4607 alloc(), arrayObject, WasmArrayObject::offsetOfNumElements(),
4608 MIRType::Int32, MWideningOp::None,
4609 AliasSet::Load(AliasSet::WasmArrayNumElements),
4610 mozilla::Some(getTrapSiteInfo()));
4611 if (!numElements) {
4612 return nullptr;
4613 }
4614 curBlock_->add(numElements);
4615
4616 return numElements;
4617 }
4618
4619 // Given `arrayObject`, the address of a WasmArrayObject, generate MIR to
4620 // return the contents of the WasmArrayObject::data_ field.
4621 [[nodiscard]] MDefinition* getWasmArrayObjectData(MDefinition* arrayObject) {
4622 MOZ_ASSERT(arrayObject->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arrayObject->type() == MIRType::WasmAnyRef)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(arrayObject->type() == MIRType::WasmAnyRef))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("arrayObject->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4622); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4622; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4623
4624 auto* data = MWasmLoadField::New(
4625 alloc(), arrayObject, WasmArrayObject::offsetOfData(),
4626 MIRType::WasmArrayData, MWideningOp::None,
4627 AliasSet::Load(AliasSet::WasmArrayDataPointer),
4628 mozilla::Some(getTrapSiteInfo()));
4629 if (!data) {
4630 return nullptr;
4631 }
4632 curBlock_->add(data);
4633
4634 return data;
4635 }
4636
4637 // Given a JIT-time-known type index `typeIndex` and a run-time known number
4638 // of elements `numElements`, create MIR to allocate a new wasm array,
4639 // possibly initialized with `typeIndex`s default value.
4640 [[nodiscard]] MDefinition* createArrayObject(uint32_t lineOrBytecode,
4641 uint32_t typeIndex,
4642 MDefinition* numElements,
4643 uint32_t elemSize,
4644 bool zeroFields) {
4645 // Get the type definition for the array as a whole.
4646 MDefinition* typeDefData = loadTypeDefInstanceData(typeIndex);
4647 if (!typeDefData) {
4648 return nullptr;
4649 }
4650
4651 auto* arrayObject = MWasmNewArrayObject::New(
4652 alloc(), instancePointer_, numElements, typeDefData, elemSize,
4653 zeroFields, bytecodeOffset());
4654 if (!arrayObject) {
4655 return nullptr;
4656 }
4657 curBlock_->add(arrayObject);
4658
4659 return arrayObject;
4660 }
4661
4662 // This emits MIR to perform several actions common to array loads and
4663 // stores. Given `arrayObject`, that points to a WasmArrayObject, and an
4664 // index value `index`, it:
4665 //
4666 // * Generates a trap if the array pointer is null
4667 // * Gets the size of the array
4668 // * Emits a bounds check of `index` against the array size
4669 // * Retrieves the OOL object pointer from the array
4670 // * Includes check for null via signal handler.
4671 //
4672 // The returned value is for the OOL object pointer.
4673 [[nodiscard]] MDefinition* setupForArrayAccess(MDefinition* arrayObject,
4674 MDefinition* index) {
4675 MOZ_ASSERT(arrayObject->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arrayObject->type() == MIRType::WasmAnyRef)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(arrayObject->type() == MIRType::WasmAnyRef))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("arrayObject->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4675); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4675; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4676 MOZ_ASSERT(index->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(index->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(index->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("index->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4676); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4676; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4677
4678 // Check for null is done in getWasmArrayObjectNumElements.
4679
4680 // Get the size value for the array.
4681 MDefinition* numElements = getWasmArrayObjectNumElements(arrayObject);
4682 if (!numElements) {
4683 return nullptr;
4684 }
4685
4686 // Create a bounds check.
4687 auto* boundsCheck =
4688 MWasmBoundsCheck::New(alloc(), index, numElements, bytecodeOffset(),
4689 MWasmBoundsCheck::Target::Unknown);
4690 if (!boundsCheck) {
4691 return nullptr;
4692 }
4693 curBlock_->add(boundsCheck);
4694
4695 // Get the address of the first byte of the (OOL) data area.
4696 return getWasmArrayObjectData(arrayObject);
4697 }
4698
4699 [[nodiscard]] bool fillArray(uint32_t lineOrBytecode,
4700 const ArrayType& arrayType,
4701 MDefinition* arrayObject, MDefinition* index,
4702 MDefinition* numElements, MDefinition* val,
4703 WasmPreBarrierKind preBarrierKind) {
4704 mozilla::DebugOnly<MIRType> valMIRType = val->type();
4705 StorageType elemType = arrayType.elementType_;
4706 MOZ_ASSERT(elemType.widenToValType().toMIRType() == valMIRType)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(elemType.widenToValType().toMIRType() == valMIRType)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(elemType.widenToValType().toMIRType() == valMIRType)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("elemType.widenToValType().toMIRType() == valMIRType"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4706); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemType.widenToValType().toMIRType() == valMIRType"
")"); do { *((volatile int*)__null) = 4706; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4707
4708 uint32_t elemSize = elemType.size();
4709 MOZ_ASSERT(elemSize >= 1 && elemSize <= 16)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(elemSize >= 1 && elemSize <= 16)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(elemSize >= 1 && elemSize <= 16))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("elemSize >= 1 && elemSize <= 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4709); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize >= 1 && elemSize <= 16"
")"); do { *((volatile int*)__null) = 4709; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4710
4711 // Make `arrayBase` point at the first byte of the (OOL) data area.
4712 MDefinition* arrayBase = getWasmArrayObjectData(arrayObject);
4713 if (!arrayBase) {
4714 return false;
4715 }
4716
4717 // We have:
4718 // arrayBase : TargetWord
4719 // index : Int32
4720 // numElements : Int32
4721 // val : <any StorageType>
4722 // $elemSize = arrayType.elementType_.size(); 1, 2, 4, 8 or 16
4723 //
4724 // Generate MIR:
4725 // <in current block>
4726 // limit : Int32 = index + numElements
4727 // if (limit == index) goto after; // skip loop if trip count == 0
4728 // loop:
4729 // indexPhi = phi(index, indexNext)
4730 // arrayBase[index * $elemSize] = val
4731 // indexNext = indexPhi + 1
4732 // if (indexNext <u limit) goto loop;
4733 // after:
4734 //
4735 // We construct the loop "manually" rather than using
4736 // FunctionCompiler::{startLoop,closeLoop} as the latter have awareness of
4737 // the wasm view of loops, whereas the loop we're building here is not a
4738 // wasm-level loop.
4739 // ==== Create the "loop" and "after" blocks ====
4740 MBasicBlock* loopBlock;
4741 if (!newBlock(curBlock_, &loopBlock, MBasicBlock::LOOP_HEADER)) {
4742 return false;
4743 }
4744 MBasicBlock* afterBlock;
4745 if (!newBlock(loopBlock, &afterBlock)) {
4746 return false;
4747 }
4748
4749 // ==== Fill in the remainder of the block preceding the loop ====
4750 MAdd* limit = MAdd::NewWasm(alloc(), index, numElements, MIRType::Int32);
4751 if (!limit) {
4752 return false;
4753 }
4754 curBlock_->add(limit);
4755
4756 // Use JSOp::StrictEq, not ::Eq, so that the comparison (and eventually
4757 // the entire initialisation loop) will be folded out in the case where
4758 // the number of elements is zero. See MCompare::tryFoldEqualOperands.
4759 MDefinition* limitEqualsBase =
4760 compare(limit, index, JSOp::StrictEq, MCompare::Compare_UInt32);
4761 if (!limitEqualsBase) {
4762 return false;
4763 }
4764 MTest* skipIfLimitEqualsBase =
4765 MTest::New(alloc(), limitEqualsBase, afterBlock, loopBlock);
4766 if (!skipIfLimitEqualsBase) {
4767 return false;
4768 }
4769 curBlock_->end(skipIfLimitEqualsBase);
4770 if (!afterBlock->addPredecessor(alloc(), curBlock_)) {
4771 return false;
4772 }
4773
4774 // ==== Fill in the loop block as best we can ====
4775 curBlock_ = loopBlock;
4776 MPhi* indexPhi = MPhi::New(alloc(), MIRType::Int32);
4777 if (!indexPhi) {
4778 return false;
4779 }
4780 if (!indexPhi->reserveLength(2)) {
4781 return false;
4782 }
4783 indexPhi->addInput(index);
4784 curBlock_->addPhi(indexPhi);
4785 curBlock_->setLoopDepth(loopDepth_ + 1);
4786
4787 if (!writeGcValueAtBasePlusScaledIndex(
4788 lineOrBytecode, elemType, arrayObject, AliasSet::WasmArrayDataArea,
4789 val, arrayBase, elemSize, indexPhi, preBarrierKind)) {
4790 return false;
4791 }
4792
4793 auto* indexNext =
4794 MAdd::NewWasm(alloc(), indexPhi, constantI32(1), MIRType::Int32);
4795 if (!indexNext) {
4796 return false;
4797 }
4798 curBlock_->add(indexNext);
4799 indexPhi->addInput(indexNext);
4800
4801 MDefinition* indexNextLtuLimit =
4802 compare(indexNext, limit, JSOp::Lt, MCompare::Compare_UInt32);
4803 if (!indexNextLtuLimit) {
4804 return false;
4805 }
4806 auto* continueIfIndexNextLtuLimit =
4807 MTest::New(alloc(), indexNextLtuLimit, loopBlock, afterBlock);
4808 if (!continueIfIndexNextLtuLimit) {
4809 return false;
4810 }
4811 curBlock_->end(continueIfIndexNextLtuLimit);
4812 if (!loopBlock->addPredecessor(alloc(), loopBlock)) {
4813 return false;
4814 }
4815 // ==== Loop block completed ====
4816
4817 curBlock_ = afterBlock;
4818 return true;
4819 }
4820
4821 // This routine generates all MIR required for `array.new`. The returned
4822 // value is for the newly created array.
4823 [[nodiscard]] MDefinition* createArrayNewCallAndLoop(uint32_t lineOrBytecode,
4824 uint32_t typeIndex,
4825 MDefinition* numElements,
4826 MDefinition* fillValue) {
4827 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType();
4828
4829 // Create the array object, uninitialized.
4830 MDefinition* arrayObject =
4831 createArrayObject(lineOrBytecode, typeIndex, numElements,
4832 arrayType.elementType_.size(), /*zeroFields=*/false);
4833 if (!arrayObject) {
4834 return nullptr;
4835 }
4836
4837 // Optimisation opportunity: if the fill value is zero, maybe we should
4838 // likewise skip over the initialisation loop entirely (and, if the zero
4839 // value is visible at JIT time, the loop will be removed). For the
4840 // reftyped case, that would be a big win since each iteration requires a
4841 // call to the post-write barrier routine.
4842
4843 if (!fillArray(lineOrBytecode, arrayType, arrayObject, constantI32(0),
4844 numElements, fillValue, WasmPreBarrierKind::None)) {
4845 return nullptr;
4846 }
4847
4848 return arrayObject;
4849 }
4850
4851 [[nodiscard]] bool createArrayFill(uint32_t lineOrBytecode,
4852 uint32_t typeIndex,
4853 MDefinition* arrayObject,
4854 MDefinition* index, MDefinition* val,
4855 MDefinition* numElements) {
4856 MOZ_ASSERT(arrayObject->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arrayObject->type() == MIRType::WasmAnyRef)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(arrayObject->type() == MIRType::WasmAnyRef))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("arrayObject->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4856); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4856; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4857 MOZ_ASSERT(index->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(index->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(index->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("index->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4857); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4857; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4858 MOZ_ASSERT(numElements->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numElements->type() == MIRType::Int32)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(numElements->type() == MIRType::Int32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("numElements->type() == MIRType::Int32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4858); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numElements->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4858; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4859
4860 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType();
4861
4862 // Check for null is done in getWasmArrayObjectNumElements.
4863
4864 // Get the array's actual size.
4865 MDefinition* actualNumElements = getWasmArrayObjectNumElements(arrayObject);
4866 if (!actualNumElements) {
4867 return false;
4868 }
4869
4870 // Create a bounds check.
4871 auto* boundsCheck = MWasmBoundsCheckRange32::New(
4872 alloc(), index, numElements, actualNumElements, bytecodeOffset());
4873 if (!boundsCheck) {
4874 return false;
4875 }
4876 curBlock_->add(boundsCheck);
4877
4878 return fillArray(lineOrBytecode, arrayType, arrayObject, index, numElements,
4879 val, WasmPreBarrierKind::Normal);
4880 }
4881
4882 /*********************************************** WasmGC: other helpers ***/
4883
4884 // Generate MIR that causes a trap of kind `trapKind` if `arg` is zero.
4885 // Currently `arg` may only be a MIRType::Int32, but that requirement could
4886 // be relaxed if needed in future.
4887 [[nodiscard]] bool trapIfZero(wasm::Trap trapKind, MDefinition* arg) {
4888 MOZ_ASSERT(arg->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arg->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arg->type() == MIRType::Int32
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"arg->type() == MIRType::Int32", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4888); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4888; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4889
4890 MBasicBlock* trapBlock = nullptr;
4891 if (!newBlock(curBlock_, &trapBlock)) {
4892 return false;
4893 }
4894
4895 auto* trap = MWasmTrap::New(alloc(), trapKind, bytecodeOffset());
4896 if (!trap) {
4897 return false;
4898 }
4899 trapBlock->end(trap);
4900
4901 MBasicBlock* joinBlock = nullptr;
4902 if (!newBlock(curBlock_, &joinBlock)) {
4903 return false;
4904 }
4905
4906 auto* test = MTest::New(alloc(), arg, joinBlock, trapBlock);
4907 if (!test) {
4908 return false;
4909 }
4910 curBlock_->end(test);
4911 curBlock_ = joinBlock;
4912 return true;
4913 }
4914
4915 [[nodiscard]] MDefinition* isRefSubtypeOf(MDefinition* ref,
4916 RefType sourceType,
4917 RefType destType) {
4918 MInstruction* isSubTypeOf = nullptr;
4919 if (destType.isTypeRef()) {
4920 uint32_t typeIndex = codeMeta_.types->indexOf(*destType.typeDef());
4921 MDefinition* superSTV = loadSuperTypeVector(typeIndex);
4922 isSubTypeOf = MWasmRefIsSubtypeOfConcrete::New(alloc(), ref, superSTV,
4923 sourceType, destType);
4924 } else {
4925 isSubTypeOf =
4926 MWasmRefIsSubtypeOfAbstract::New(alloc(), ref, sourceType, destType);
4927 }
4928 MOZ_ASSERT(isSubTypeOf)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSubTypeOf)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isSubTypeOf))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("isSubTypeOf", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4928); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSubTypeOf"
")"); do { *((volatile int*)__null) = 4928; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4929
4930 curBlock_->add(isSubTypeOf);
4931 return isSubTypeOf;
4932 }
4933
4934 // Generate MIR that attempts to downcast `ref` to `castToTypeDef`. If the
4935 // downcast fails, we trap. If it succeeds, then `ref` can be assumed to
4936 // have a type that is a subtype of (or the same as) `castToTypeDef` after
4937 // this point.
4938 [[nodiscard]] bool refCast(MDefinition* ref, RefType sourceType,
4939 RefType destType) {
4940 MDefinition* success = isRefSubtypeOf(ref, sourceType, destType);
4941 if (!success) {
4942 return false;
4943 }
4944
4945 // Trap if `success` is zero. If it's nonzero, we have established that
4946 // `ref <: castToTypeDef`.
4947 return trapIfZero(wasm::Trap::BadCast, success);
4948 }
4949
4950 // Generate MIR that computes a boolean value indicating whether or not it
4951 // is possible to downcast `ref` to `destType`.
4952 [[nodiscard]] MDefinition* refTest(MDefinition* ref, RefType sourceType,
4953 RefType destType) {
4954 return isRefSubtypeOf(ref, sourceType, destType);
4955 }
4956
4957 // Generates MIR for br_on_cast and br_on_cast_fail.
4958 [[nodiscard]] bool brOnCastCommon(bool onSuccess, uint32_t labelRelativeDepth,
4959 RefType sourceType, RefType destType,
4960 const ResultType& labelType,
4961 const DefVector& values) {
4962 if (inDeadCode()) {
4963 return true;
4964 }
4965
4966 MBasicBlock* fallthroughBlock = nullptr;
4967 if (!newBlock(curBlock_, &fallthroughBlock)) {
4968 return false;
4969 }
4970
4971 // `values` are the values in the top block-value on the stack. Since the
4972 // argument to `br_on_cast{_fail}` is at the top of the stack, it is the
4973 // last element in `values`.
4974 //
4975 // For both br_on_cast and br_on_cast_fail, the OpIter validation routines
4976 // ensure that `values` is non-empty (by rejecting the case
4977 // `labelType->length() < 1`) and that the last value in `values` is
4978 // reftyped.
4979 MOZ_RELEASE_ASSERT(values.length() > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(values.length() > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(values.length() > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("values.length() > 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4979); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "values.length() > 0"
")"); do { *((volatile int*)__null) = 4979; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4980 MDefinition* ref = values.back();
4981 MOZ_ASSERT(ref->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ref->type() == MIRType::WasmAnyRef)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(ref->type() == MIRType::WasmAnyRef))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("ref->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4981); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4981; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4982
4983 MDefinition* success = isRefSubtypeOf(ref, sourceType, destType);
4984 if (!success) {
4985 return false;
4986 }
4987
4988 MTest* test;
4989 if (onSuccess) {
4990 test = MTest::New(alloc(), success, nullptr, fallthroughBlock);
4991 if (!test || !addControlFlowPatch(test, labelRelativeDepth,
4992 MTest::TrueBranchIndex)) {
4993 return false;
4994 }
4995 } else {
4996 test = MTest::New(alloc(), success, fallthroughBlock, nullptr);
4997 if (!test || !addControlFlowPatch(test, labelRelativeDepth,
4998 MTest::FalseBranchIndex)) {
4999 return false;
5000 }
5001 }
5002
5003 if (!pushDefs(values)) {
5004 return false;
5005 }
5006
5007 curBlock_->end(test);
5008 curBlock_ = fallthroughBlock;
5009 return true;
5010 }
5011
5012 [[nodiscard]] bool brOnNonStruct(const DefVector& values) {
5013 if (inDeadCode()) {
5014 return true;
5015 }
5016
5017 MBasicBlock* fallthroughBlock = nullptr;
5018 if (!newBlock(curBlock_, &fallthroughBlock)) {
5019 return false;
5020 }
5021
5022 MOZ_ASSERT(values.length() > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(values.length() > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(values.length() > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("values.length() > 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5022); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.length() > 0"
")"); do { *((volatile int*)__null) = 5022; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5023 MOZ_ASSERT(values.back()->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(values.back()->type() == MIRType::WasmAnyRef)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(values.back()->type() == MIRType::WasmAnyRef))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("values.back()->type() == MIRType::WasmAnyRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5023); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.back()->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 5023; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5024
5025 MGoto* jump = MGoto::New(alloc(), fallthroughBlock);
5026 if (!jump) {
5027 return false;
5028 }
5029 if (!pushDefs(values)) {
5030 return false;
5031 }
5032
5033 curBlock_->end(jump);
5034 curBlock_ = fallthroughBlock;
5035 return true;
5036 }
5037
5038 /************************************************************ DECODING ***/
5039
5040 // AsmJS adds a line number to `callSiteLineNums` for certain operations that
5041 // are represented by a JS call, such as math builtins. We use these line
5042 // numbers when calling builtins. This method will read from
5043 // `callSiteLineNums` when we are using AsmJS, or else return the current
5044 // bytecode offset.
5045 //
5046 // This method MUST be called from opcodes that AsmJS will emit a call site
5047 // line number for, or else the arrays will get out of sync. Other opcodes
5048 // must use `readBytecodeOffset` below.
5049 uint32_t readCallSiteLineOrBytecode() {
5050 if (!func_.callSiteLineNums.empty()) {
5051 return func_.callSiteLineNums[lastReadCallSite_++];
5052 }
5053 return iter_.lastOpcodeOffset();
5054 }
5055
5056 // Return the current bytecode offset.
5057 uint32_t readBytecodeOffset() { return iter_.lastOpcodeOffset(); }
5058
5059 TrapSiteInfo getTrapSiteInfo() {
5060 return TrapSiteInfo(wasm::BytecodeOffset(readBytecodeOffset()));
5061 }
5062
5063#if DEBUG1
5064 bool done() const { return iter_.done(); }
5065#endif
5066
5067 /*************************************************************************/
5068 private:
5069 [[nodiscard]] bool newBlock(MBasicBlock* pred, MBasicBlock** block,
5070 MBasicBlock::Kind kind = MBasicBlock::NORMAL) {
5071 *block = MBasicBlock::New(mirGraph(), info(), pred, kind);
5072 if (!*block) {
5073 return false;
5074 }
5075 mirGraph().addBlock(*block);
5076 (*block)->setLoopDepth(loopDepth_);
5077 return true;
5078 }
5079
5080 [[nodiscard]] bool goToNewBlock(MBasicBlock* pred, MBasicBlock** block) {
5081 if (!newBlock(pred, block)) {
5082 return false;
5083 }
5084 pred->end(MGoto::New(alloc(), *block));
5085 return true;
5086 }
5087
5088 [[nodiscard]] bool goToExistingBlock(MBasicBlock* prev, MBasicBlock* next) {
5089 MOZ_ASSERT(prev)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(prev)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(prev))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("prev", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5089); AnnotateMozCrashReason("MOZ_ASSERT" "(" "prev" ")");
do { *((volatile int*)__null) = 5089; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5090 MOZ_ASSERT(next)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(next)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(next))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("next", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5090); AnnotateMozCrashReason("MOZ_ASSERT" "(" "next" ")");
do { *((volatile int*)__null) = 5090; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5091 prev->end(MGoto::New(alloc(), next));
5092 return next->addPredecessor(alloc(), prev);
5093 }
5094
5095 [[nodiscard]] bool bindBranches(uint32_t absolute, DefVector* defs) {
5096 if (absolute >= pendingBlocks_.length() ||
5097 pendingBlocks_[absolute].patches.empty()) {
5098 return inDeadCode() || popPushedDefs(defs);
5099 }
5100
5101 ControlFlowPatchVector& patches = pendingBlocks_[absolute].patches;
5102 MControlInstruction* ins = patches[0].ins;
5103 MBasicBlock* pred = ins->block();
5104
5105 MBasicBlock* join = nullptr;
5106 if (!newBlock(pred, &join)) {
5107 return false;
5108 }
5109
5110 // Use branch hinting information if any.
5111 if (pendingBlocks_[absolute].hint != BranchHint::Invalid) {
5112 join->setBranchHinting(pendingBlocks_[absolute].hint);
5113 }
5114
5115 pred->mark();
5116 ins->replaceSuccessor(patches[0].index, join);
5117
5118 for (size_t i = 1; i < patches.length(); i++) {
5119 ins = patches[i].ins;
5120
5121 pred = ins->block();
5122 if (!pred->isMarked()) {
5123 if (!join->addPredecessor(alloc(), pred)) {
5124 return false;
5125 }
5126 pred->mark();
5127 }
5128
5129 ins->replaceSuccessor(patches[i].index, join);
5130 }
5131
5132 MOZ_ASSERT_IF(curBlock_, !curBlock_->isMarked())do { if (curBlock_) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(!curBlock_->isMarked())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!curBlock_->isMarked())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!curBlock_->isMarked()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5132); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!curBlock_->isMarked()"
")"); do { *((volatile int*)__null) = 5132; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5133 for (uint32_t i = 0; i < join->numPredecessors(); i++) {
5134 join->getPredecessor(i)->unmark();
5135 }
5136
5137 if (curBlock_ && !goToExistingBlock(curBlock_, join)) {
5138 return false;
5139 }
5140
5141 curBlock_ = join;
5142
5143 if (!popPushedDefs(defs)) {
5144 return false;
5145 }
5146
5147 patches.clear();
5148 return true;
5149 }
5150};
5151
5152template <>
5153MDefinition* FunctionCompiler::unary<MToFloat32>(MDefinition* op) {
5154 if (inDeadCode()) {
5155 return nullptr;
5156 }
5157 auto* ins = MToFloat32::New(alloc(), op, mustPreserveNaN(op->type()));
5158 curBlock_->add(ins);
5159 return ins;
5160}
5161
5162template <>
5163MDefinition* FunctionCompiler::unary<MWasmBuiltinTruncateToInt32>(
5164 MDefinition* op) {
5165 if (inDeadCode()) {
5166 return nullptr;
5167 }
5168 auto* ins = MWasmBuiltinTruncateToInt32::New(
5169 alloc(), op, instancePointer_,
5170 BytecodeOffset(readCallSiteLineOrBytecode()));
5171 curBlock_->add(ins);
5172 return ins;
5173}
5174
5175template <>
5176MDefinition* FunctionCompiler::unary<MNot>(MDefinition* op) {
5177 if (inDeadCode()) {
5178 return nullptr;
5179 }
5180 auto* ins = MNot::NewInt32(alloc(), op);
5181 curBlock_->add(ins);
5182 return ins;
5183}
5184
5185template <>
5186MDefinition* FunctionCompiler::unary<MAbs>(MDefinition* op, MIRType type) {
5187 if (inDeadCode()) {
5188 return nullptr;
5189 }
5190 auto* ins = MAbs::NewWasm(alloc(), op, type);
5191 curBlock_->add(ins);
5192 return ins;
5193}
5194
5195} // end anonymous namespace
5196
5197bool EmitBodyExprs(FunctionCompiler& f);
5198
5199static bool EmitI32Const(FunctionCompiler& f) {
5200 int32_t i32;
5201 if (!f.iter().readI32Const(&i32)) {
5202 return false;
5203 }
5204
5205 f.iter().setResult(f.constantI32(i32));
5206 return true;
5207}
5208
5209static bool EmitI64Const(FunctionCompiler& f) {
5210 int64_t i64;
5211 if (!f.iter().readI64Const(&i64)) {
5212 return false;
5213 }
5214
5215 f.iter().setResult(f.constantI64(i64));
5216 return true;
5217}
5218
5219static bool EmitF32Const(FunctionCompiler& f) {
5220 float f32;
5221 if (!f.iter().readF32Const(&f32)) {
5222 return false;
5223 }
5224
5225 f.iter().setResult(f.constantF32(f32));
5226 return true;
5227}
5228
5229static bool EmitF64Const(FunctionCompiler& f) {
5230 double f64;
5231 if (!f.iter().readF64Const(&f64)) {
5232 return false;
5233 }
5234
5235 f.iter().setResult(f.constantF64(f64));
5236 return true;
5237}
5238
5239static bool EmitBlock(FunctionCompiler& f) {
5240 ResultType params;
5241 return f.iter().readBlock(&params) && f.startBlock();
5242}
5243
5244static bool EmitLoop(FunctionCompiler& f) {
5245 ResultType params;
5246 if (!f.iter().readLoop(&params)) {
5247 return false;
5248 }
5249
5250 MBasicBlock* loopHeader;
5251 if (!f.startLoop(&loopHeader, params.length())) {
5252 return false;
5253 }
5254
5255 f.addInterruptCheck();
5256
5257 f.iter().controlItem().block = loopHeader;
5258 return true;
5259}
5260
5261static bool EmitIf(FunctionCompiler& f) {
5262 BranchHint branchHint =
5263 f.iter().getBranchHint(f.funcIndex(), f.relativeBytecodeOffset());
5264
5265 ResultType params;
5266 MDefinition* condition = nullptr;
5267 if (!f.iter().readIf(&params, &condition)) {
5268 return false;
5269 }
5270
5271 MBasicBlock* elseBlock;
5272 if (!f.branchAndStartThen(condition, &elseBlock)) {
5273 return false;
5274 }
5275
5276 // Store the branch hint in the basic block.
5277 if (!f.inDeadCode() && branchHint != BranchHint::Invalid) {
5278 f.getCurBlock()->setBranchHinting(branchHint);
5279 }
5280
5281 f.iter().controlItem().block = elseBlock;
5282 return true;
5283}
5284
5285static bool EmitElse(FunctionCompiler& f) {
5286 ResultType paramType;
5287 ResultType resultType;
5288 DefVector thenValues;
5289 if (!f.iter().readElse(&paramType, &resultType, &thenValues)) {
5290 return false;
5291 }
5292
5293 if (!f.pushDefs(thenValues)) {
5294 return false;
5295 }
5296
5297 Control& control = f.iter().controlItem();
5298 return f.switchToElse(control.block, &control.block);
5299}
5300
5301static bool EmitEnd(FunctionCompiler& f) {
5302 LabelKind kind;
5303 ResultType type;
5304 DefVector preJoinDefs;
5305 DefVector resultsForEmptyElse;
5306 if (!f.iter().readEnd(&kind, &type, &preJoinDefs, &resultsForEmptyElse)) {
5307 return false;
5308 }
5309
5310 Control& control = f.iter().controlItem();
5311 MBasicBlock* block = control.block;
5312
5313 if (!f.pushDefs(preJoinDefs)) {
5314 return false;
5315 }
5316
5317 // Every label case is responsible to pop the control item at the appropriate
5318 // time for the label case
5319 DefVector postJoinDefs;
5320 switch (kind) {
5321 case LabelKind::Body: {
5322 MOZ_ASSERT(!control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!control.tryControl))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("!control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5322; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5323 if (!f.emitBodyDelegateThrowPad(control)) {
5324 return false;
5325 }
5326 if (!f.finishBlock(&postJoinDefs)) {
5327 return false;
5328 }
5329 if (!f.returnValues(std::move(postJoinDefs))) {
5330 return false;
5331 }
5332 f.iter().popEnd();
5333 MOZ_ASSERT(f.iter().controlStackEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(f.iter().controlStackEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(f.iter().controlStackEmpty()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"f.iter().controlStackEmpty()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5333); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f.iter().controlStackEmpty()"
")"); do { *((volatile int*)__null) = 5333; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5334 return f.iter().endFunction(f.iter().end());
5335 }
5336 case LabelKind::Block:
5337 MOZ_ASSERT(!control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!control.tryControl))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("!control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5337); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5337; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5338 if (!f.finishBlock(&postJoinDefs)) {
5339 return false;
5340 }
5341 f.iter().popEnd();
5342 break;
5343 case LabelKind::Loop:
5344 MOZ_ASSERT(!control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!control.tryControl))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("!control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5344); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5344; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5345 if (!f.closeLoop(block, &postJoinDefs)) {
5346 return false;
5347 }
5348 f.iter().popEnd();
5349 break;
5350 case LabelKind::Then: {
5351 MOZ_ASSERT(!control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!control.tryControl))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("!control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5351); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5351; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5352 // If we didn't see an Else, create a trivial else block so that we create
5353 // a diamond anyway, to preserve Ion invariants.
5354 if (!f.switchToElse(block, &block)) {
5355 return false;
5356 }
5357
5358 if (!f.pushDefs(resultsForEmptyElse)) {
5359 return false;
5360 }
5361
5362 if (!f.joinIfElse(block, &postJoinDefs)) {
5363 return false;
5364 }
5365 f.iter().popEnd();
5366 break;
5367 }
5368 case LabelKind::Else:
5369 MOZ_ASSERT(!control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!control.tryControl))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("!control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5369); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5369; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5370 if (!f.joinIfElse(block, &postJoinDefs)) {
5371 return false;
5372 }
5373 f.iter().popEnd();
5374 break;
5375 case LabelKind::Try:
5376 case LabelKind::Catch:
5377 case LabelKind::CatchAll:
5378 MOZ_ASSERT(control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(control.tryControl))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5378); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5378; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5379 if (!f.finishTryCatch(kind, control, &postJoinDefs)) {
5380 return false;
5381 }
5382 f.freeTryControl(std::move(control.tryControl));
5383 f.iter().popEnd();
5384 break;
5385 case LabelKind::TryTable:
5386 MOZ_ASSERT(control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(control.tryControl))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5386; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5387 if (!f.finishTryTable(control, &postJoinDefs)) {
5388 return false;
5389 }
5390 f.freeTryControl(std::move(control.tryControl));
5391 f.iter().popEnd();
5392 break;
5393 }
5394
5395 MOZ_ASSERT_IF(!f.inDeadCode(), postJoinDefs.length() == type.length())do { if (!f.inDeadCode()) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(postJoinDefs.length() ==
type.length())>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(postJoinDefs.length() == type.
length()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("postJoinDefs.length() == type.length()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5395); AnnotateMozCrashReason("MOZ_ASSERT" "(" "postJoinDefs.length() == type.length()"
")"); do { *((volatile int*)__null) = 5395; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5396 f.iter().setResults(postJoinDefs.length(), postJoinDefs);
5397
5398 return true;
5399}
5400
5401static bool EmitBr(FunctionCompiler& f) {
5402 uint32_t relativeDepth;
5403 ResultType type;
5404 DefVector values;
5405 if (!f.iter().readBr(&relativeDepth, &type, &values)) {
5406 return false;
5407 }
5408
5409 return f.br(relativeDepth, values);
5410}
5411
5412static bool EmitBrIf(FunctionCompiler& f) {
5413 uint32_t relativeDepth;
5414 ResultType type;
5415 DefVector values;
5416 MDefinition* condition;
5417
5418 BranchHint branchHint =
5419 f.iter().getBranchHint(f.funcIndex(), f.relativeBytecodeOffset());
5420
5421 if (!f.iter().readBrIf(&relativeDepth, &type, &values, &condition)) {
5422 return false;
5423 }
5424
5425 return f.brIf(relativeDepth, values, condition, branchHint);
5426}
5427
5428static bool EmitBrTable(FunctionCompiler& f) {
5429 Uint32Vector depths;
5430 uint32_t defaultDepth;
5431 ResultType branchValueType;
5432 DefVector branchValues;
5433 MDefinition* index;
5434 if (!f.iter().readBrTable(&depths, &defaultDepth, &branchValueType,
5435 &branchValues, &index)) {
5436 return false;
5437 }
5438
5439 // If all the targets are the same, or there are no targets, we can just
5440 // use a goto. This is not just an optimization: MaybeFoldConditionBlock
5441 // assumes that tables have more than one successor.
5442 bool allSameDepth = true;
5443 for (uint32_t depth : depths) {
5444 if (depth != defaultDepth) {
5445 allSameDepth = false;
5446 break;
5447 }
5448 }
5449
5450 if (allSameDepth) {
5451 return f.br(defaultDepth, branchValues);
5452 }
5453
5454 return f.brTable(index, defaultDepth, depths, branchValues);
5455}
5456
5457static bool EmitReturn(FunctionCompiler& f) {
5458 DefVector values;
5459 if (!f.iter().readReturn(&values)) {
5460 return false;
5461 }
5462
5463 return f.returnValues(std::move(values));
5464}
5465
5466static bool EmitUnreachable(FunctionCompiler& f) {
5467 if (!f.iter().readUnreachable()) {
5468 return false;
5469 }
5470
5471 f.unreachableTrap();
5472 return true;
5473}
5474
5475static bool EmitTry(FunctionCompiler& f) {
5476 ResultType params;
5477 if (!f.iter().readTry(&params)) {
5478 return false;
5479 }
5480
5481 return f.startTry();
5482}
5483
5484static bool EmitCatch(FunctionCompiler& f) {
5485 LabelKind kind;
5486 uint32_t tagIndex;
5487 ResultType paramType, resultType;
5488 DefVector tryValues;
5489 if (!f.iter().readCatch(&kind, &tagIndex, &paramType, &resultType,
5490 &tryValues)) {
5491 return false;
5492 }
5493
5494 // Pushing the results of the previous block, to properly join control flow
5495 // after the try and after each handler, as well as potential control flow
5496 // patches from other instrunctions. This is similar to what is done for
5497 // if-then-else control flow and for most other control control flow joins.
5498 if (!f.pushDefs(tryValues)) {
5499 return false;
5500 }
5501
5502 return f.switchToCatch(f.iter().controlItem(), kind, tagIndex);
5503}
5504
5505static bool EmitCatchAll(FunctionCompiler& f) {
5506 LabelKind kind;
5507 ResultType paramType, resultType;
5508 DefVector tryValues;
5509 if (!f.iter().readCatchAll(&kind, &paramType, &resultType, &tryValues)) {
5510 return false;
5511 }
5512
5513 // Pushing the results of the previous block, to properly join control flow
5514 // after the try and after each handler, as well as potential control flow
5515 // patches from other instrunctions.
5516 if (!f.pushDefs(tryValues)) {
5517 return false;
5518 }
5519
5520 return f.switchToCatch(f.iter().controlItem(), kind, CatchAllIndex);
5521}
5522
5523static bool EmitTryTable(FunctionCompiler& f) {
5524 ResultType params;
5525 TryTableCatchVector catches;
5526 if (!f.iter().readTryTable(&params, &catches)) {
5527 return false;
5528 }
5529
5530 return f.startTryTable(std::move(catches));
5531}
5532
5533static bool EmitDelegate(FunctionCompiler& f) {
5534 uint32_t relativeDepth;
5535 ResultType resultType;
5536 DefVector tryValues;
5537 if (!f.iter().readDelegate(&relativeDepth, &resultType, &tryValues)) {
5538 return false;
5539 }
5540
5541 Control& control = f.iter().controlItem();
5542 MBasicBlock* block = control.block;
5543 MOZ_ASSERT(control.tryControl)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(control.tryControl)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(control.tryControl))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("control.tryControl"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5543); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5543; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5544
5545 // Unless the entire try-delegate is dead code, delegate any pad-patches from
5546 // this try to the next try-block above relativeDepth.
5547 if (block) {
5548 ControlInstructionVector& delegatePadPatches =
5549 control.tryControl->landingPadPatches;
5550 if (!f.delegatePadPatches(delegatePadPatches, relativeDepth)) {
5551 return false;
5552 }
5553 }
5554 f.freeTryControl(std::move(control.tryControl));
5555 f.iter().popDelegate();
5556
5557 // Push the results of the previous block, and join control flow with
5558 // potential control flow patches from other instrunctions in the try code.
5559 // This is similar to what is done for EmitEnd.
5560 if (!f.pushDefs(tryValues)) {
5561 return false;
5562 }
5563 DefVector postJoinDefs;
5564 if (!f.finishBlock(&postJoinDefs)) {
5565 return false;
5566 }
5567 MOZ_ASSERT_IF(!f.inDeadCode(), postJoinDefs.length() == resultType.length())do { if (!f.inDeadCode()) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(postJoinDefs.length() ==
resultType.length())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(postJoinDefs.length() == resultType
.length()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("postJoinDefs.length() == resultType.length()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "postJoinDefs.length() == resultType.length()"
")"); do { *((volatile int*)__null) = 5567; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5568 f.iter().setResults(postJoinDefs.length(), postJoinDefs);
5569
5570 return true;
5571}
5572
5573static bool EmitThrow(FunctionCompiler& f) {
5574 uint32_t tagIndex;
5575 DefVector argValues;
5576 if (!f.iter().readThrow(&tagIndex, &argValues)) {
5577 return false;
5578 }
5579
5580 return f.emitThrow(tagIndex, argValues);
5581}
5582
5583static bool EmitThrowRef(FunctionCompiler& f) {
5584 MDefinition* exnRef;
5585 if (!f.iter().readThrowRef(&exnRef)) {
5586 return false;
5587 }
5588
5589 return f.emitThrowRef(exnRef);
5590}
5591
5592static bool EmitRethrow(FunctionCompiler& f) {
5593 uint32_t relativeDepth;
5594 if (!f.iter().readRethrow(&relativeDepth)) {
5595 return false;
5596 }
5597
5598 return f.emitRethrow(relativeDepth);
5599}
5600
5601static bool EmitInlineCall(FunctionCompiler& callerCompiler,
5602 const FuncType& funcType, uint32_t funcIndex,
5603 const DefVector& args, DefVector* results) {
5604 UniqueChars error;
5605 const Bytes& bytecode = callerCompiler.codeMeta().bytecode->bytes;
5606 const FuncDefRange& funcRange =
5607 callerCompiler.codeMeta().funcDefRange(funcIndex);
5608 const uint8_t* bodyBegin = bytecode.begin() + funcRange.bytecodeOffset;
5609 const uint8_t* bodyEnd = bodyBegin + funcRange.bodyLength;
5610 FuncCompileInput func(funcIndex, funcRange.bytecodeOffset, bodyBegin, bodyEnd,
5611 Uint32Vector());
5612 Decoder d(func.begin, func.end, func.lineOrBytecode, &error);
5613
5614 ValTypeVector locals;
5615 if (!DecodeLocalEntriesWithParams(d, callerCompiler.codeMeta(), funcIndex,
5616 &locals)) {
5617 return false;
5618 }
5619
5620 CompileInfo* compileInfo = callerCompiler.addInlineCallInfo(locals.length());
5621 if (!compileInfo) {
5622 return false;
5623 }
5624
5625 FunctionCompiler calleeCompiler(&callerCompiler, d, func, locals,
5626 *compileInfo);
5627 if (!calleeCompiler.initInline(args)) {
5628 MOZ_ASSERT(!error)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!error)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(!error))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!error", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5628); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!error" ")"
); do { *((volatile int*)__null) = 5628; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5629 return false;
5630 }
5631
5632 if (!calleeCompiler.startBlock()) {
5633 MOZ_ASSERT(!error)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!error)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(!error))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!error", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5633); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!error" ")"
); do { *((volatile int*)__null) = 5633; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5634 return false;
5635 }
5636
5637 if (!EmitBodyExprs(calleeCompiler)) {
5638 MOZ_ASSERT(!error)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!error)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(!error))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!error", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5638); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!error" ")"
); do { *((volatile int*)__null) = 5638; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5639 return false;
5640 }
5641
5642 calleeCompiler.finish();
5643
5644 return callerCompiler.finishInlinedCallDirect(calleeCompiler, results);
5645}
5646
5647static bool EmitCallArgs(FunctionCompiler& f, const FuncType& funcType,
5648 const DefVector& args, CallCompileState* call) {
5649 for (size_t i = 0, n = funcType.args().length(); i < n; ++i) {
5650 if (!f.mirGen().ensureBallast()) {
5651 return false;
5652 }
5653 if (!f.passArg(args[i], funcType.args()[i], call)) {
5654 return false;
5655 }
5656 }
5657
5658 ResultType resultType = ResultType::Vector(funcType.results());
5659 if (!f.passStackResultAreaCallArg(resultType, call)) {
5660 return false;
5661 }
5662
5663 return f.finishCall(call);
5664}
5665
5666static bool EmitCall(FunctionCompiler& f, bool asmJSFuncDef) {
5667 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5668
5669 uint32_t funcIndex;
5670 DefVector args;
5671 if (asmJSFuncDef) {
5672 if (!f.iter().readOldCallDirect(f.codeMeta().numFuncImports, &funcIndex,
5673 &args)) {
5674 return false;
5675 }
5676 } else {
5677 if (!f.iter().readCall(&funcIndex, &args)) {
5678 return false;
5679 }
5680 }
5681
5682 if (f.inDeadCode()) {
5683 return true;
5684 }
5685
5686 const FuncType& funcType = f.codeMeta().getFuncType(funcIndex);
5687
5688 DefVector results;
5689 if (f.codeMeta().funcIsImport(funcIndex)) {
5690 CallCompileState call;
5691 if (!EmitCallArgs(f, funcType, args, &call)) {
5692 return false;
5693 }
5694
5695 uint32_t instanceDataOffset =
5696 f.codeMeta().offsetOfFuncImportInstanceData(funcIndex);
5697 if (!f.callImport(instanceDataOffset, lineOrBytecode, call, funcType,
5698 &results)) {
5699 return false;
5700 }
5701 } else {
5702 if (f.shouldInlineCallDirect(funcIndex)) {
5703 if (!EmitInlineCall(f, funcType, funcIndex, args, &results)) {
5704 return false;
5705 }
5706 } else {
5707 CallCompileState call;
5708 if (!EmitCallArgs(f, funcType, args, &call)) {
5709 return false;
5710 }
5711 if (!f.callDirect(funcType, funcIndex, lineOrBytecode, call, &results)) {
5712 return false;
5713 }
5714 }
5715 }
5716
5717 f.iter().setResults(results.length(), results);
5718 return true;
5719}
5720
5721static bool EmitCallIndirect(FunctionCompiler& f, bool oldStyle) {
5722 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5723
5724 uint32_t funcTypeIndex;
5725 uint32_t tableIndex;
5726 MDefinition* callee;
5727 DefVector args;
5728 if (oldStyle) {
5729 tableIndex = 0;
5730 if (!f.iter().readOldCallIndirect(&funcTypeIndex, &callee, &args)) {
5731 return false;
5732 }
5733 } else {
5734 if (!f.iter().readCallIndirect(&funcTypeIndex, &tableIndex, &callee,
5735 &args)) {
5736 return false;
5737 }
5738 }
5739
5740 if (f.inDeadCode()) {
5741 return true;
5742 }
5743
5744 const FuncType& funcType = (*f.codeMeta().types)[funcTypeIndex].funcType();
5745
5746 CallCompileState call;
5747 if (!EmitCallArgs(f, funcType, args, &call)) {
5748 return false;
5749 }
5750
5751 DefVector results;
5752 if (!f.callIndirect(funcTypeIndex, tableIndex, callee, lineOrBytecode, call,
5753 &results)) {
5754 return false;
5755 }
5756
5757 f.iter().setResults(results.length(), results);
5758 return true;
5759}
5760
5761#ifdef ENABLE_WASM_JSPI1
5762static bool EmitStackSwitch(FunctionCompiler& f) {
5763 StackSwitchKind kind;
5764 MDefinition* suspender;
5765 MDefinition* fn;
5766 MDefinition* data;
5767 if (!f.iter().readStackSwitch(&kind, &suspender, &fn, &data)) {
5768 return false;
5769 }
5770 if (!f.stackSwitch(suspender, fn, data, kind)) {
5771 return false;
5772 }
5773 return true;
5774}
5775#endif
5776
5777#ifdef ENABLE_WASM_TAIL_CALLS1
5778static bool EmitReturnCall(FunctionCompiler& f) {
5779 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5780
5781 uint32_t funcIndex;
5782 DefVector args;
5783 if (!f.iter().readReturnCall(&funcIndex, &args)) {
5784 return false;
5785 }
5786
5787 if (f.inDeadCode()) {
5788 return true;
5789 }
5790
5791 const FuncType& funcType = f.codeMeta().getFuncType(funcIndex);
5792
5793 CallCompileState call;
5794 f.markReturnCall(&call);
5795 if (!EmitCallArgs(f, funcType, args, &call)) {
5796 return false;
5797 }
5798
5799 DefVector results;
5800 if (f.codeMeta().funcIsImport(funcIndex)) {
5801 uint32_t globalDataOffset =
5802 f.codeMeta().offsetOfFuncImportInstanceData(funcIndex);
5803 if (!f.returnCallImport(globalDataOffset, lineOrBytecode, call, funcType,
5804 &results)) {
5805 return false;
5806 }
5807 } else {
5808 if (!f.returnCallDirect(funcType, funcIndex, lineOrBytecode, call,
5809 &results)) {
5810 return false;
5811 }
5812 }
5813 return true;
5814}
5815
5816static bool EmitReturnCallIndirect(FunctionCompiler& f) {
5817 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5818
5819 uint32_t funcTypeIndex;
5820 uint32_t tableIndex;
5821 MDefinition* callee;
5822 DefVector args;
5823 if (!f.iter().readReturnCallIndirect(&funcTypeIndex, &tableIndex, &callee,
5824 &args)) {
5825 return false;
5826 }
5827
5828 if (f.inDeadCode()) {
5829 return true;
5830 }
5831
5832 const FuncType& funcType = (*f.codeMeta().types)[funcTypeIndex].funcType();
5833
5834 CallCompileState call;
5835 f.markReturnCall(&call);
5836 if (!EmitCallArgs(f, funcType, args, &call)) {
5837 return false;
5838 }
5839
5840 DefVector results;
5841 return f.returnCallIndirect(funcTypeIndex, tableIndex, callee, lineOrBytecode,
5842 call, &results);
5843}
5844#endif
5845
5846#if defined(ENABLE_WASM_TAIL_CALLS1) && defined(ENABLE_WASM_GC1)
5847static bool EmitReturnCallRef(FunctionCompiler& f) {
5848 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5849
5850 const FuncType* funcType;
5851 MDefinition* callee;
5852 DefVector args;
5853
5854 if (!f.iter().readReturnCallRef(&funcType, &callee, &args)) {
5855 return false;
5856 }
5857
5858 if (f.inDeadCode()) {
5859 return true;
5860 }
5861
5862 CallCompileState call;
5863 f.markReturnCall(&call);
5864 if (!EmitCallArgs(f, *funcType, args, &call)) {
5865 return false;
5866 }
5867
5868 DefVector results;
5869 return f.returnCallRef(*funcType, callee, lineOrBytecode, call, &results);
5870}
5871#endif
5872
5873static bool EmitGetLocal(FunctionCompiler& f) {
5874 uint32_t id;
5875 if (!f.iter().readGetLocal(f.locals(), &id)) {
5876 return false;
5877 }
5878
5879 f.iter().setResult(f.getLocalDef(id));
5880 return true;
5881}
5882
5883static bool EmitSetLocal(FunctionCompiler& f) {
5884 uint32_t id;
5885 MDefinition* value;
5886 if (!f.iter().readSetLocal(f.locals(), &id, &value)) {
5887 return false;
5888 }
5889
5890 f.assign(id, value);
5891 return true;
5892}
5893
5894static bool EmitTeeLocal(FunctionCompiler& f) {
5895 uint32_t id;
5896 MDefinition* value;
5897 if (!f.iter().readTeeLocal(f.locals(), &id, &value)) {
5898 return false;
5899 }
5900
5901 f.assign(id, value);
5902 return true;
5903}
5904
5905static bool EmitGetGlobal(FunctionCompiler& f) {
5906 uint32_t id;
5907 if (!f.iter().readGetGlobal(&id)) {
5908 return false;
5909 }
5910
5911 const GlobalDesc& global = f.codeMeta().globals[id];
5912 if (!global.isConstant()) {
5913 f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(),
5914 global.isIndirect(),
5915 global.type().toMIRType()));
5916 return true;
5917 }
5918
5919 LitVal value = global.constantValue();
5920
5921 MDefinition* result;
5922 switch (value.type().kind()) {
5923 case ValType::I32:
5924 result = f.constantI32(int32_t(value.i32()));
5925 break;
5926 case ValType::I64:
5927 result = f.constantI64(int64_t(value.i64()));
5928 break;
5929 case ValType::F32:
5930 result = f.constantF32(value.f32());
5931 break;
5932 case ValType::F64:
5933 result = f.constantF64(value.f64());
5934 break;
5935 case ValType::V128:
5936#ifdef ENABLE_WASM_SIMD1
5937 result = f.constantV128(value.v128());
5938 break;
5939#else
5940 return f.iter().fail("Ion has no SIMD support yet");
5941#endif
5942 case ValType::Ref:
5943 MOZ_ASSERT(value.ref().isNull())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(value.ref().isNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(value.ref().isNull()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("value.ref().isNull()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5943); AnnotateMozCrashReason("MOZ_ASSERT" "(" "value.ref().isNull()"
")"); do { *((volatile int*)__null) = 5943; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5944 result = f.constantNullRef();
5945 break;
5946 default:
5947 MOZ_CRASH("unexpected type in EmitGetGlobal")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in EmitGetGlobal"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5947); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in EmitGetGlobal"
")"); do { *((volatile int*)__null) = 5947; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
5948 }
5949
5950 f.iter().setResult(result);
5951 return true;
5952}
5953
5954static bool EmitSetGlobal(FunctionCompiler& f) {
5955 uint32_t bytecodeOffset = f.readBytecodeOffset();
5956
5957 uint32_t id;
5958 MDefinition* value;
5959 if (!f.iter().readSetGlobal(&id, &value)) {
5960 return false;
5961 }
5962
5963 const GlobalDesc& global = f.codeMeta().globals[id];
5964 MOZ_ASSERT(global.isMutable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(global.isMutable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(global.isMutable()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("global.isMutable()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5964); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global.isMutable()"
")"); do { *((volatile int*)__null) = 5964; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5965 return f.storeGlobalVar(bytecodeOffset, global.offset(), global.isIndirect(),
5966 value);
5967}
5968
5969static bool EmitTeeGlobal(FunctionCompiler& f) {
5970 uint32_t bytecodeOffset = f.readBytecodeOffset();
5971
5972 uint32_t id;
5973 MDefinition* value;
5974 if (!f.iter().readTeeGlobal(&id, &value)) {
5975 return false;
5976 }
5977
5978 const GlobalDesc& global = f.codeMeta().globals[id];
5979 MOZ_ASSERT(global.isMutable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(global.isMutable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(global.isMutable()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("global.isMutable()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5979); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global.isMutable()"
")"); do { *((volatile int*)__null) = 5979; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5980
5981 return f.storeGlobalVar(bytecodeOffset, global.offset(), global.isIndirect(),
5982 value);
5983}
5984
5985template <typename MIRClass>
5986static bool EmitUnary(FunctionCompiler& f, ValType operandType) {
5987 MDefinition* input;
5988 if (!f.iter().readUnary(operandType, &input)) {
5989 return false;
5990 }
5991
5992 f.iter().setResult(f.unary<MIRClass>(input));
5993 return true;
5994}
5995
5996template <typename MIRClass>
5997static bool EmitConversion(FunctionCompiler& f, ValType operandType,
5998 ValType resultType) {
5999 MDefinition* input;
6000 if (!f.iter().readConversion(operandType, resultType, &input)) {
6001 return false;
6002 }
6003
6004 f.iter().setResult(f.unary<MIRClass>(input));
6005 return true;
6006}
6007
6008template <typename MIRClass>
6009static bool EmitUnaryWithType(FunctionCompiler& f, ValType operandType,
6010 MIRType mirType) {
6011 MDefinition* input;
6012 if (!f.iter().readUnary(operandType, &input)) {
6013 return false;
6014 }
6015
6016 f.iter().setResult(f.unary<MIRClass>(input, mirType));
6017 return true;
6018}
6019
6020template <typename MIRClass>
6021static bool EmitConversionWithType(FunctionCompiler& f, ValType operandType,
6022 ValType resultType, MIRType mirType) {
6023 MDefinition* input;
6024 if (!f.iter().readConversion(operandType, resultType, &input)) {
6025 return false;
6026 }
6027
6028 f.iter().setResult(f.unary<MIRClass>(input, mirType));
6029 return true;
6030}
6031
6032static bool EmitTruncate(FunctionCompiler& f, ValType operandType,
6033 ValType resultType, bool isUnsigned,
6034 bool isSaturating) {
6035 MDefinition* input = nullptr;
6036 if (!f.iter().readConversion(operandType, resultType, &input)) {
6037 return false;
6038 }
6039
6040 TruncFlags flags = 0;
6041 if (isUnsigned) {
6042 flags |= TRUNC_UNSIGNED;
6043 }
6044 if (isSaturating) {
6045 flags |= TRUNC_SATURATING;
6046 }
6047 if (resultType == ValType::I32) {
6048 if (f.codeMeta().isAsmJS()) {
6049 if (f.inDeadCode()) {
6050 // The read callsite line, produced by prepareCall, has to be
6051 // consumed -- the MWasmBuiltinTruncateToInt32 and MTruncateToInt32
6052 // will not create MIR node.
6053 (void)f.readCallSiteLineOrBytecode();
6054 f.iter().setResult(nullptr);
6055 } else if (input && (input->type() == MIRType::Double ||
6056 input->type() == MIRType::Float32)) {
6057 f.iter().setResult(f.unary<MWasmBuiltinTruncateToInt32>(input));
6058 } else {
6059 f.iter().setResult(f.unary<MTruncateToInt32>(input));
6060 }
6061 } else {
6062 f.iter().setResult(f.truncate<MWasmTruncateToInt32>(input, flags));
6063 }
6064 } else {
6065 MOZ_ASSERT(resultType == ValType::I64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(resultType == ValType::I64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(resultType == ValType::I64))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("resultType == ValType::I64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6065); AnnotateMozCrashReason("MOZ_ASSERT" "(" "resultType == ValType::I64"
")"); do { *((volatile int*)__null) = 6065; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6066 MOZ_ASSERT(!f.codeMeta().isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!f.codeMeta().isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!f.codeMeta().isAsmJS()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!f.codeMeta().isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6066); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!f.codeMeta().isAsmJS()"
")"); do { *((volatile int*)__null) = 6066; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6067#if defined(JS_CODEGEN_ARM)
6068 f.iter().setResult(f.truncateWithInstance(input, flags));
6069#else
6070 f.iter().setResult(f.truncate<MWasmTruncateToInt64>(input, flags));
6071#endif
6072 }
6073 return true;
6074}
6075
6076static bool EmitSignExtend(FunctionCompiler& f, uint32_t srcSize,
6077 uint32_t targetSize) {
6078 MDefinition* input;
6079 ValType type = targetSize == 4 ? ValType::I32 : ValType::I64;
6080 if (!f.iter().readConversion(type, type, &input)) {
6081 return false;
6082 }
6083
6084 f.iter().setResult(f.signExtend(input, srcSize, targetSize));
6085 return true;
6086}
6087
6088static bool EmitExtendI32(FunctionCompiler& f, bool isUnsigned) {
6089 MDefinition* input;
6090 if (!f.iter().readConversion(ValType::I32, ValType::I64, &input)) {
6091 return false;
6092 }
6093
6094 f.iter().setResult(f.extendI32(input, isUnsigned));
6095 return true;
6096}
6097
6098static bool EmitConvertI64ToFloatingPoint(FunctionCompiler& f,
6099 ValType resultType, MIRType mirType,
6100 bool isUnsigned) {
6101 MDefinition* input;
6102 if (!f.iter().readConversion(ValType::I64, resultType, &input)) {
6103 return false;
6104 }
6105
6106 f.iter().setResult(f.convertI64ToFloatingPoint(input, mirType, isUnsigned));
6107 return true;
6108}
6109
6110static bool EmitReinterpret(FunctionCompiler& f, ValType resultType,
6111 ValType operandType, MIRType mirType) {
6112 MDefinition* input;
6113 if (!f.iter().readConversion(operandType, resultType, &input)) {
6114 return false;
6115 }
6116
6117 f.iter().setResult(f.unary<MWasmReinterpret>(input, mirType));
6118 return true;
6119}
6120
6121static bool EmitAdd(FunctionCompiler& f, ValType type, MIRType mirType) {
6122 MDefinition* lhs;
6123 MDefinition* rhs;
6124 if (!f.iter().readBinary(type, &lhs, &rhs)) {
6125 return false;
6126 }
6127
6128 f.iter().setResult(f.add(lhs, rhs, mirType));
6129 return true;
6130}
6131
6132static bool EmitSub(FunctionCompiler& f, ValType type, MIRType mirType) {
6133 MDefinition* lhs;
6134 MDefinition* rhs;
6135 if (!f.iter().readBinary(type, &lhs, &rhs)) {
6136 return false;
6137 }
6138
6139 f.iter().setResult(f.sub(lhs, rhs, mirType));
6140 return true;
6141}
6142
6143static bool EmitRotate(FunctionCompiler& f, ValType type, bool isLeftRotation) {
6144 MDefinition* lhs;
6145 MDefinition* rhs;
6146 if (!f.iter().readBinary(type, &lhs, &rhs)) {
6147 return false;
6148 }
6149
6150 MDefinition* result = f.rotate(lhs, rhs, type.toMIRType(), isLeftRotation);
6151 f.iter().setResult(result);
6152 return true;
6153}
6154
6155static bool EmitBitNot(FunctionCompiler& f, ValType operandType) {
6156 MDefinition* input;
6157 if (!f.iter().readUnary(operandType, &input)) {
6158 return false;
6159 }
6160
6161 f.iter().setResult(f.bitnot(input));
6162 return true;
6163}
6164
6165static bool EmitBitwiseAndOrXor(FunctionCompiler& f, ValType operandType,
6166 MIRType mirType,
6167 MWasmBinaryBitwise::SubOpcode subOpc) {
6168 MDefinition* lhs;
6169 MDefinition* rhs;
6170 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6171 return false;
6172 }
6173
6174 f.iter().setResult(f.binary<MWasmBinaryBitwise>(lhs, rhs, mirType, subOpc));
6175 return true;
6176}
6177
6178template <typename MIRClass>
6179static bool EmitShift(FunctionCompiler& f, ValType operandType,
6180 MIRType mirType) {
6181 MDefinition* lhs;
6182 MDefinition* rhs;
6183 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6184 return false;
6185 }
6186
6187 f.iter().setResult(f.binary<MIRClass>(lhs, rhs, mirType));
6188 return true;
6189}
6190
6191static bool EmitUrsh(FunctionCompiler& f, ValType operandType,
6192 MIRType mirType) {
6193 MDefinition* lhs;
6194 MDefinition* rhs;
6195 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6196 return false;
6197 }
6198
6199 f.iter().setResult(f.ursh(lhs, rhs, mirType));
6200 return true;
6201}
6202
6203static bool EmitMul(FunctionCompiler& f, ValType operandType, MIRType mirType) {
6204 MDefinition* lhs;
6205 MDefinition* rhs;
6206 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6207 return false;
6208 }
6209
6210 f.iter().setResult(
6211 f.mul(lhs, rhs, mirType,
6212 mirType == MIRType::Int32 ? MMul::Integer : MMul::Normal));
6213 return true;
6214}
6215
6216static bool EmitDiv(FunctionCompiler& f, ValType operandType, MIRType mirType,
6217 bool isUnsigned) {
6218 MDefinition* lhs;
6219 MDefinition* rhs;
6220 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6221 return false;
6222 }
6223
6224 f.iter().setResult(f.div(lhs, rhs, mirType, isUnsigned));
6225 return true;
6226}
6227
6228static bool EmitRem(FunctionCompiler& f, ValType operandType, MIRType mirType,
6229 bool isUnsigned) {
6230 MDefinition* lhs;
6231 MDefinition* rhs;
6232 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6233 return false;
6234 }
6235
6236 f.iter().setResult(f.mod(lhs, rhs, mirType, isUnsigned));
6237 return true;
6238}
6239
6240static bool EmitMinMax(FunctionCompiler& f, ValType operandType,
6241 MIRType mirType, bool isMax) {
6242 MDefinition* lhs;
6243 MDefinition* rhs;
6244 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6245 return false;
6246 }
6247
6248 f.iter().setResult(f.minMax(lhs, rhs, mirType, isMax));
6249 return true;
6250}
6251
6252static bool EmitCopySign(FunctionCompiler& f, ValType operandType) {
6253 MDefinition* lhs;
6254 MDefinition* rhs;
6255 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6256 return false;
6257 }
6258
6259 f.iter().setResult(f.binary<MCopySign>(lhs, rhs, operandType.toMIRType()));
6260 return true;
6261}
6262
6263static bool EmitComparison(FunctionCompiler& f, ValType operandType,
6264 JSOp compareOp, MCompare::CompareType compareType) {
6265 MDefinition* lhs;
6266 MDefinition* rhs;
6267 if (!f.iter().readComparison(operandType, &lhs, &rhs)) {
6268 return false;
6269 }
6270
6271 f.iter().setResult(f.compare(lhs, rhs, compareOp, compareType));
6272 return true;
6273}
6274
6275static bool EmitSelect(FunctionCompiler& f, bool typed) {
6276 StackType type;
6277 MDefinition* trueValue;
6278 MDefinition* falseValue;
6279 MDefinition* condition;
6280 if (!f.iter().readSelect(typed, &type, &trueValue, &falseValue, &condition)) {
6281 return false;
6282 }
6283
6284 f.iter().setResult(f.select(trueValue, falseValue, condition));
6285 return true;
6286}
6287
6288static bool EmitLoad(FunctionCompiler& f, ValType type, Scalar::Type viewType) {
6289 LinearMemoryAddress<MDefinition*> addr;
6290 if (!f.iter().readLoad(type, Scalar::byteSize(viewType), &addr)) {
6291 return false;
6292 }
6293
6294 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6295 f.bytecodeIfNotAsmJS(),
6296 f.hugeMemoryEnabled(addr.memoryIndex));
6297 auto* ins = f.load(addr.base, &access, type);
6298 if (!f.inDeadCode() && !ins) {
6299 return false;
6300 }
6301
6302 f.iter().setResult(ins);
6303 return true;
6304}
6305
6306static bool EmitStore(FunctionCompiler& f, ValType resultType,
6307 Scalar::Type viewType) {
6308 LinearMemoryAddress<MDefinition*> addr;
6309 MDefinition* value;
6310 if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr,
6311 &value)) {
6312 return false;
6313 }
6314
6315 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6316 f.bytecodeIfNotAsmJS(),
6317 f.hugeMemoryEnabled(addr.memoryIndex));
6318
6319 f.store(addr.base, &access, value);
6320 return true;
6321}
6322
6323static bool EmitTeeStore(FunctionCompiler& f, ValType resultType,
6324 Scalar::Type viewType) {
6325 LinearMemoryAddress<MDefinition*> addr;
6326 MDefinition* value;
6327 if (!f.iter().readTeeStore(resultType, Scalar::byteSize(viewType), &addr,
6328 &value)) {
6329 return false;
6330 }
6331
6332 MOZ_ASSERT(f.isMem32(addr.memoryIndex))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(f.isMem32(addr.memoryIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(f.isMem32(addr.memoryIndex))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("f.isMem32(addr.memoryIndex)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6332); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f.isMem32(addr.memoryIndex)"
")"); do { *((volatile int*)__null) = 6332; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
; // asm.js opcode
6333 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6334 f.bytecodeIfNotAsmJS(),
6335 f.hugeMemoryEnabled(addr.memoryIndex));
6336
6337 f.store(addr.base, &access, value);
6338 return true;
6339}
6340
6341static bool EmitTeeStoreWithCoercion(FunctionCompiler& f, ValType resultType,
6342 Scalar::Type viewType) {
6343 LinearMemoryAddress<MDefinition*> addr;
6344 MDefinition* value;
6345 if (!f.iter().readTeeStore(resultType, Scalar::byteSize(viewType), &addr,
6346 &value)) {
6347 return false;
6348 }
6349
6350 if (resultType == ValType::F32 && viewType == Scalar::Float64) {
6351 value = f.unary<MToDouble>(value);
6352 } else if (resultType == ValType::F64 && viewType == Scalar::Float32) {
6353 value = f.unary<MToFloat32>(value);
6354 } else {
6355 MOZ_CRASH("unexpected coerced store")do { do { } while (false); MOZ_ReportCrash("" "unexpected coerced store"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6355); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected coerced store"
")"); do { *((volatile int*)__null) = 6355; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
6356 }
6357
6358 MOZ_ASSERT(f.isMem32(addr.memoryIndex))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(f.isMem32(addr.memoryIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(f.isMem32(addr.memoryIndex))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("f.isMem32(addr.memoryIndex)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6358); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f.isMem32(addr.memoryIndex)"
")"); do { *((volatile int*)__null) = 6358; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
; // asm.js opcode
6359 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6360 f.bytecodeIfNotAsmJS(),
6361 f.hugeMemoryEnabled(addr.memoryIndex));
6362
6363 f.store(addr.base, &access, value);
6364 return true;
6365}
6366
6367static bool TryInlineUnaryBuiltin(FunctionCompiler& f, SymbolicAddress callee,
6368 MDefinition* input) {
6369 if (!input) {
6370 return false;
6371 }
6372
6373 MOZ_ASSERT(IsFloatingPointType(input->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsFloatingPointType(input->type()))>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(IsFloatingPointType(input->type())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("IsFloatingPointType(input->type())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6373); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(input->type())"
")"); do { *((volatile int*)__null) = 6373; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6374
6375 RoundingMode mode;
6376 if (!IsRoundingFunction(callee, &mode)) {
6377 return false;
6378 }
6379
6380 if (!MNearbyInt::HasAssemblerSupport(mode)) {
6381 return false;
6382 }
6383
6384 f.iter().setResult(f.nearbyInt(input, mode));
6385 return true;
6386}
6387
6388static bool EmitUnaryMathBuiltinCall(FunctionCompiler& f,
6389 const SymbolicAddressSignature& callee) {
6390 MOZ_ASSERT(callee.numArgs == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callee.numArgs == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(callee.numArgs == 1))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("callee.numArgs == 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6390); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.numArgs == 1"
")"); do { *((volatile int*)__null) = 6390; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6391
6392 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
6393
6394 MDefinition* input;
6395 if (!f.iter().readUnary(ValType::fromMIRType(callee.argTypes[0]), &input)) {
6396 return false;
6397 }
6398
6399 if (TryInlineUnaryBuiltin(f, callee.identity, input)) {
6400 return true;
6401 }
6402
6403 CallCompileState call;
6404 if (!f.passArg(input, callee.argTypes[0], &call)) {
6405 return false;
6406 }
6407
6408 if (!f.finishCall(&call)) {
6409 return false;
6410 }
6411
6412 MDefinition* def;
6413 if (!f.builtinCall(callee, lineOrBytecode, call, &def)) {
6414 return false;
6415 }
6416
6417 f.iter().setResult(def);
6418 return true;
6419}
6420
6421static bool EmitBinaryMathBuiltinCall(FunctionCompiler& f,
6422 const SymbolicAddressSignature& callee) {
6423 MOZ_ASSERT(callee.numArgs == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callee.numArgs == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(callee.numArgs == 2))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("callee.numArgs == 2"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.numArgs == 2"
")"); do { *((volatile int*)__null) = 6423; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6424 MOZ_ASSERT(callee.argTypes[0] == callee.argTypes[1])do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callee.argTypes[0] == callee.argTypes[1])>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(callee.argTypes[0] == callee.argTypes[1]))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("callee.argTypes[0] == callee.argTypes[1]"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.argTypes[0] == callee.argTypes[1]"
")"); do { *((volatile int*)__null) = 6424; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6425
6426 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
6427
6428 CallCompileState call;
6429 MDefinition* lhs;
6430 MDefinition* rhs;
6431 // This call to readBinary assumes both operands have the same type.
6432 if (!f.iter().readBinary(ValType::fromMIRType(callee.argTypes[0]), &lhs,
6433 &rhs)) {
6434 return false;
6435 }
6436
6437 if (!f.passArg(lhs, callee.argTypes[0], &call)) {
6438 return false;
6439 }
6440
6441 if (!f.passArg(rhs, callee.argTypes[1], &call)) {
6442 return false;
6443 }
6444
6445 if (!f.finishCall(&call)) {
6446 return false;
6447 }
6448
6449 MDefinition* def;
6450 if (!f.builtinCall(callee, lineOrBytecode, call, &def)) {
6451 return false;
6452 }
6453
6454 f.iter().setResult(def);
6455 return true;
6456}
6457
6458static bool EmitMemoryGrow(FunctionCompiler& f) {
6459 uint32_t bytecodeOffset = f.readBytecodeOffset();
6460
6461 MDefinition* delta;
6462 uint32_t memoryIndex;
6463 if (!f.iter().readMemoryGrow(&memoryIndex, &delta)) {
6464 return false;
6465 }
6466
6467 if (f.inDeadCode()) {
6468 return true;
6469 }
6470
6471 MDefinition* memoryIndexValue = f.constantI32(int32_t(memoryIndex));
6472 if (!memoryIndexValue) {
6473 return false;
6474 }
6475
6476 const SymbolicAddressSignature& callee =
6477 f.isMem32(memoryIndex) ? SASigMemoryGrowM32 : SASigMemoryGrowM64;
6478
6479 MDefinition* ret;
6480 if (!f.emitInstanceCall2(bytecodeOffset, callee, delta, memoryIndexValue,
6481 &ret)) {
6482 return false;
6483 }
6484
6485 f.iter().setResult(ret);
6486 return true;
6487}
6488
6489static bool EmitMemorySize(FunctionCompiler& f) {
6490 uint32_t bytecodeOffset = f.readBytecodeOffset();
6491
6492 uint32_t memoryIndex;
6493 if (!f.iter().readMemorySize(&memoryIndex)) {
6494 return false;
6495 }
6496
6497 if (f.inDeadCode()) {
6498 return true;
6499 }
6500
6501 MDefinition* memoryIndexValue = f.constantI32(int32_t(memoryIndex));
6502 if (!memoryIndexValue) {
6503 return false;
6504 }
6505
6506 const SymbolicAddressSignature& callee =
6507 f.isMem32(memoryIndex) ? SASigMemorySizeM32 : SASigMemorySizeM64;
6508
6509 MDefinition* ret;
6510 if (!f.emitInstanceCall1(bytecodeOffset, callee, memoryIndexValue, &ret)) {
6511 return false;
6512 }
6513
6514 f.iter().setResult(ret);
6515 return true;
6516}
6517
6518static bool EmitAtomicCmpXchg(FunctionCompiler& f, ValType type,
6519 Scalar::Type viewType) {
6520 LinearMemoryAddress<MDefinition*> addr;
6521 MDefinition* oldValue;
6522 MDefinition* newValue;
6523 if (!f.iter().readAtomicCmpXchg(&addr, type, byteSize(viewType), &oldValue,
6524 &newValue)) {
6525 return false;
6526 }
6527
6528 MemoryAccessDesc access(
6529 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6530 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Full());
6531 auto* ins =
6532 f.atomicCompareExchangeHeap(addr.base, &access, type, oldValue, newValue);
6533 if (!f.inDeadCode() && !ins) {
6534 return false;
6535 }
6536
6537 f.iter().setResult(ins);
6538 return true;
6539}
6540
6541static bool EmitAtomicLoad(FunctionCompiler& f, ValType type,
6542 Scalar::Type viewType) {
6543 LinearMemoryAddress<MDefinition*> addr;
6544 if (!f.iter().readAtomicLoad(&addr, type, byteSize(viewType))) {
6545 return false;
6546 }
6547
6548 MemoryAccessDesc access(
6549 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6550 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Load());
6551 auto* ins = f.load(addr.base, &access, type);
6552 if (!f.inDeadCode() && !ins) {
6553 return false;
6554 }
6555
6556 f.iter().setResult(ins);
6557 return true;
6558}
6559
6560static bool EmitAtomicRMW(FunctionCompiler& f, ValType type,
6561 Scalar::Type viewType, jit::AtomicOp op) {
6562 LinearMemoryAddress<MDefinition*> addr;
6563 MDefinition* value;
6564 if (!f.iter().readAtomicRMW(&addr, type, byteSize(viewType), &value)) {
6565 return false;
6566 }
6567
6568 MemoryAccessDesc access(
6569 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6570 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Full());
6571 auto* ins = f.atomicBinopHeap(op, addr.base, &access, type, value);
6572 if (!f.inDeadCode() && !ins) {
6573 return false;
6574 }
6575
6576 f.iter().setResult(ins);
6577 return true;
6578}
6579
6580static bool EmitAtomicStore(FunctionCompiler& f, ValType type,
6581 Scalar::Type viewType) {
6582 LinearMemoryAddress<MDefinition*> addr;
6583 MDefinition* value;
6584 if (!f.iter().readAtomicStore(&addr, type, byteSize(viewType), &value)) {
6585 return false;
6586 }
6587
6588 MemoryAccessDesc access(
6589 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6590 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Store());
6591 f.store(addr.base, &access, value);
6592 return true;
6593}
6594
6595static bool EmitWait(FunctionCompiler& f, ValType type, uint32_t byteSize) {
6596 MOZ_ASSERT(type == ValType::I32 || type == ValType::I64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == ValType::I32 || type == ValType::I64)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(type == ValType::I32 || type == ValType::I64))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("type == ValType::I32 || type == ValType::I64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6596); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == ValType::I32 || type == ValType::I64"
")"); do { *((volatile int*)__null) = 6596; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6597 MOZ_ASSERT(type.size() == byteSize)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type.size() == byteSize)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type.size() == byteSize))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("type.size() == byteSize"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6597); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.size() == byteSize"
")"); do { *((volatile int*)__null) = 6597; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6598
6599 uint32_t bytecodeOffset = f.readBytecodeOffset();
6600
6601 LinearMemoryAddress<MDefinition*> addr;
6602 MDefinition* expected;
6603 MDefinition* timeout;
6604 if (!f.iter().readWait(&addr, type, byteSize, &expected, &timeout)) {
6605 return false;
6606 }
6607
6608 if (f.inDeadCode()) {
6609 return true;
6610 }
6611
6612 MemoryAccessDesc access(addr.memoryIndex,
6613 type == ValType::I32 ? Scalar::Int32 : Scalar::Int64,
6614 addr.align, addr.offset, f.bytecodeOffset(),
6615 f.hugeMemoryEnabled(addr.memoryIndex));
6616 MDefinition* ptr = f.computeEffectiveAddress(addr.base, &access);
6617 if (!ptr) {
6618 return false;
6619 }
6620
6621 MDefinition* memoryIndex = f.constantI32(int32_t(addr.memoryIndex));
6622 if (!memoryIndex) {
6623 return false;
6624 }
6625
6626 const SymbolicAddressSignature& callee =
6627 f.isMem32(addr.memoryIndex)
6628 ? (type == ValType::I32 ? SASigWaitI32M32 : SASigWaitI64M32)
6629 : (type == ValType::I32 ? SASigWaitI32M64 : SASigWaitI64M64);
6630
6631 MDefinition* ret;
6632 if (!f.emitInstanceCall4(bytecodeOffset, callee, ptr, expected, timeout,
6633 memoryIndex, &ret)) {
6634 return false;
6635 }
6636
6637 f.iter().setResult(ret);
6638 return true;
6639}
6640
6641static bool EmitFence(FunctionCompiler& f) {
6642 if (!f.iter().readFence()) {
6643 return false;
6644 }
6645
6646 f.fence();
6647 return true;
6648}
6649
6650static bool EmitWake(FunctionCompiler& f) {
6651 uint32_t bytecodeOffset = f.readBytecodeOffset();
6652
6653 LinearMemoryAddress<MDefinition*> addr;
6654 MDefinition* count;
6655 if (!f.iter().readWake(&addr, &count)) {
6656 return false;
6657 }
6658
6659 if (f.inDeadCode()) {
6660 return true;
6661 }
6662
6663 MemoryAccessDesc access(addr.memoryIndex, Scalar::Int32, addr.align,
6664 addr.offset, f.bytecodeOffset(),
6665 f.hugeMemoryEnabled(addr.memoryIndex));
6666 MDefinition* ptr = f.computeEffectiveAddress(addr.base, &access);
6667 if (!ptr) {
6668 return false;
6669 }
6670
6671 MDefinition* memoryIndex = f.constantI32(int32_t(addr.memoryIndex));
6672 if (!memoryIndex) {
6673 return false;
6674 }
6675
6676 const SymbolicAddressSignature& callee =
6677 f.isMem32(addr.memoryIndex) ? SASigWakeM32 : SASigWakeM64;
6678
6679 MDefinition* ret;
6680 if (!f.emitInstanceCall3(bytecodeOffset, callee, ptr, count, memoryIndex,
6681 &ret)) {
6682 return false;
6683 }
6684
6685 f.iter().setResult(ret);
6686 return true;
6687}
6688
6689static bool EmitAtomicXchg(FunctionCompiler& f, ValType type,
6690 Scalar::Type viewType) {
6691 LinearMemoryAddress<MDefinition*> addr;
6692 MDefinition* value;
6693 if (!f.iter().readAtomicRMW(&addr, type, byteSize(viewType), &value)) {
6694 return false;
6695 }
6696
6697 MemoryAccessDesc access(
6698 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6699 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Full());
6700 MDefinition* ins = f.atomicExchangeHeap(addr.base, &access, type, value);
6701 if (!f.inDeadCode() && !ins) {
6702 return false;
6703 }
6704
6705 f.iter().setResult(ins);
6706 return true;
6707}
6708
6709static bool EmitMemCopyCall(FunctionCompiler& f, uint32_t dstMemIndex,
6710 uint32_t srcMemIndex, MDefinition* dst,
6711 MDefinition* src, MDefinition* len) {
6712 uint32_t bytecodeOffset = f.readBytecodeOffset();
6713
6714 if (dstMemIndex == srcMemIndex) {
6715 const SymbolicAddressSignature& callee =
6716 (f.codeMeta().usesSharedMemory(dstMemIndex)
6717 ? (f.isMem32(dstMemIndex) ? SASigMemCopySharedM32
6718 : SASigMemCopySharedM64)
6719 : (f.isMem32(dstMemIndex) ? SASigMemCopyM32 : SASigMemCopyM64));
6720 MDefinition* memoryBase = f.memoryBase(dstMemIndex);
6721 if (!memoryBase) {
6722 return false;
6723 }
6724 return f.emitInstanceCall4(bytecodeOffset, callee, dst, src, len,
6725 memoryBase);
6726 }
6727
6728 IndexType dstIndexType = f.codeMeta().memories[dstMemIndex].indexType();
6729 IndexType srcIndexType = f.codeMeta().memories[srcMemIndex].indexType();
6730
6731 if (dstIndexType == IndexType::I32) {
6732 dst = f.extendI32(dst, /*isUnsigned=*/true);
6733 if (!dst) {
6734 return false;
6735 }
6736 }
6737 if (srcIndexType == IndexType::I32) {
6738 src = f.extendI32(src, /*isUnsigned=*/true);
6739 if (!src) {
6740 return false;
6741 }
6742 }
6743 if (dstIndexType == IndexType::I32 || srcIndexType == IndexType::I32) {
6744 len = f.extendI32(len, /*isUnsigned=*/true);
6745 if (!len) {
6746 return false;
6747 }
6748 }
6749
6750 MDefinition* dstMemIndexValue = f.constantI32(int32_t(dstMemIndex));
6751 if (!dstMemIndexValue) {
6752 return false;
6753 }
6754
6755 MDefinition* srcMemIndexValue = f.constantI32(int32_t(srcMemIndex));
6756 if (!srcMemIndexValue) {
6757 return false;
6758 }
6759
6760 return f.emitInstanceCall5(bytecodeOffset, SASigMemCopyAny, dst, src, len,
6761 dstMemIndexValue, srcMemIndexValue);
6762}
6763
6764static bool EmitMemCopyInline(FunctionCompiler& f, uint32_t memoryIndex,
6765 MDefinition* dst, MDefinition* src,
6766 uint32_t length) {
6767 MOZ_ASSERT(length != 0 && length <= MaxInlineMemoryCopyLength)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length != 0 && length <= MaxInlineMemoryCopyLength
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(length != 0 && length <= MaxInlineMemoryCopyLength
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"length != 0 && length <= MaxInlineMemoryCopyLength"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 6767); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length != 0 && length <= MaxInlineMemoryCopyLength"
")"); do { *((volatile int*)__null) = 6767; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6768
6769 // Compute the number of copies of each width we will need to do
6770 size_t remainder = length;
6771#ifdef ENABLE_WASM_SIMD1
6772 size_t numCopies16 = 0;
6773 if (MacroAssembler::SupportsFastUnalignedFPAccesses()) {
6774 numCopies16 = remainder / sizeof(V128);
6775 remainder %= sizeof(V128);
6776 }
6777#endif
6778#ifdef JS_64BIT1
6779 size_t numCopies8 = remainder / sizeof(uint64_t);
6780 remainder %= sizeof(uint64_t);
6781#endif
6782 size_t numCopies4 = remainder / sizeof(uint32_t);
6783 remainder %= sizeof(uint32_t);
6784 size_t numCopies2 = remainder / sizeof(uint16_t);
6785 remainder %= sizeof(uint16_t);
6786 size_t numCopies1 = remainder;
6787
6788 // Load all source bytes from low to high using the widest transfer width we
6789 // can for the system. We will trap without writing anything if any source
6790 // byte is out-of-bounds.
6791 size_t offset = 0;
6792 DefVector loadedValues;
6793
6794#ifdef ENABLE_WASM_SIMD1
6795 for (uint32_t i = 0; i < numCopies16; i++) {
6796 MemoryAccessDesc access(memoryIndex, Scalar::Simd128, 1, offset,
6797 f.bytecodeOffset(),
6798 f.hugeMemoryEnabled(memoryIndex));
6799 auto* load = f.load(src, &access, ValType::V128);
6800 if (!load || !loadedValues.append(load)) {
6801 return false;
6802 }
6803
6804 offset += sizeof(V128);
6805 }
6806#endif
6807
6808#ifdef JS_64BIT1
6809 for (uint32_t i = 0; i < numCopies8; i++) {
6810 MemoryAccessDesc access(memoryIndex, Scalar::Int64, 1, offset,
6811 f.bytecodeOffset(),
6812 f.hugeMemoryEnabled(memoryIndex));
6813 auto* load = f.load(src, &access, ValType::I64);
6814 if (!load || !loadedValues.append(load)) {
6815 return false;
6816 }
6817
6818 offset += sizeof(uint64_t);
6819 }
6820#endif
6821
6822 for (uint32_t i = 0; i < numCopies4; i++) {
6823 MemoryAccessDesc access(memoryIndex, Scalar::Uint32, 1, offset,
6824 f.bytecodeOffset(),
6825 f.hugeMemoryEnabled(memoryIndex));
6826 auto* load = f.load(src, &access, ValType::I32);
6827 if (!load || !loadedValues.append(load)) {
6828 return false;
6829 }
6830
6831 offset += sizeof(uint32_t);
6832 }
6833
6834 if (numCopies2) {
6835 MemoryAccessDesc access(memoryIndex, Scalar::Uint16, 1, offset,
6836 f.bytecodeOffset(),
6837 f.hugeMemoryEnabled(memoryIndex));
6838 auto* load = f.load(src, &access, ValType::I32);
6839 if (!load || !loadedValues.append(load)) {
6840 return false;
6841 }
6842
6843 offset += sizeof(uint16_t);
6844 }
6845
6846 if (numCopies1) {
6847 MemoryAccessDesc access(memoryIndex, Scalar::Uint8, 1, offset,
6848 f.bytecodeOffset(),
6849 f.hugeMemoryEnabled(memoryIndex));
6850 auto* load = f.load(src, &access, ValType::I32);
6851 if (!load || !loadedValues.append(load)) {
6852 return false;
6853 }
6854 }
6855
6856 // Store all source bytes to the destination from high to low. We will trap
6857 // without writing anything on the first store if any dest byte is
6858 // out-of-bounds.
6859 offset = length;
6860
6861 if (numCopies1) {
6862 offset -= sizeof(uint8_t);
6863
6864 MemoryAccessDesc access(memoryIndex, Scalar::Uint8, 1, offset,
6865 f.bytecodeOffset(),
6866 f.hugeMemoryEnabled(memoryIndex));
6867 auto* value = loadedValues.popCopy();
6868 f.store(dst, &access, value);
6869 }
6870
6871 if (numCopies2) {
6872 offset -= sizeof(uint16_t);
6873
6874 MemoryAccessDesc access(memoryIndex, Scalar::Uint16, 1, offset,
6875 f.bytecodeOffset(),
6876 f.hugeMemoryEnabled(memoryIndex));
6877 auto* value = loadedValues.popCopy();
6878 f.store(dst, &access, value);
6879 }
6880
6881 for (uint32_t i = 0; i < numCopies4; i++) {
6882 offset -= sizeof(uint32_t);
6883
6884 MemoryAccessDesc access(memoryIndex, Scalar::Uint32, 1, offset,
6885 f.bytecodeOffset(),
6886 f.hugeMemoryEnabled(memoryIndex));
6887 auto* value = loadedValues.popCopy();
6888 f.store(dst, &access, value);
6889 }
6890
6891#ifdef JS_64BIT1
6892 for (uint32_t i = 0; i < numCopies8; i++) {
6893 offset -= sizeof(uint64_t);
6894
6895 MemoryAccessDesc access(memoryIndex, Scalar::Int64, 1, offset,
6896 f.bytecodeOffset(),
6897 f.hugeMemoryEnabled(memoryIndex));
6898 auto* value = loadedValues.popCopy();
6899 f.store(dst, &access, value);
6900 }
6901#endif
6902
6903#ifdef ENABLE_WASM_SIMD1
6904 for (uint32_t i = 0; i < numCopies16; i++) {
6905 offset -= sizeof(V128);
6906
6907 MemoryAccessDesc access(memoryIndex, Scalar::Simd128, 1, offset,
6908 f.bytecodeOffset(),
6909 f.hugeMemoryEnabled(memoryIndex));
6910 auto* value = loadedValues.popCopy();
6911 f.store(dst, &access, value);
6912 }
6913#endif
6914
6915 return true;
6916}
6917
6918static bool EmitMemCopy(FunctionCompiler& f) {
6919 MDefinition *dst, *src, *len;
6920 uint32_t dstMemIndex;
6921 uint32_t srcMemIndex;
6922 if (!f.iter().readMemOrTableCopy(true, &dstMemIndex, &dst, &srcMemIndex, &src,
6923 &len)) {
6924 return false;
6925 }
6926
6927 if (f.inDeadCode()) {
6928 return true;
6929 }
6930
6931 if (dstMemIndex == srcMemIndex && len->isConstant()) {
6932 uint64_t length = f.isMem32(dstMemIndex) ? len->toConstant()->toInt32()
6933 : len->toConstant()->toInt64();
6934 static_assert(MaxInlineMemoryCopyLength <= UINT32_MAX(4294967295U));
6935 if (length != 0 && length <= MaxInlineMemoryCopyLength) {
6936 return EmitMemCopyInline(f, dstMemIndex, dst, src, uint32_t(length));
6937 }
6938 }
6939
6940 return EmitMemCopyCall(f, dstMemIndex, srcMemIndex, dst, src, len);
6941}
6942
6943static bool EmitTableCopy(FunctionCompiler& f) {
6944 MDefinition *dst, *src, *len;
6945 uint32_t dstTableIndex;
6946 uint32_t srcTableIndex;
6947 if (!f.iter().readMemOrTableCopy(false, &dstTableIndex, &dst, &srcTableIndex,
6948 &src, &len)) {
6949 return false;
6950 }
6951
6952 if (f.inDeadCode()) {
6953 return true;
6954 }
6955
6956 uint32_t bytecodeOffset = f.readBytecodeOffset();
6957 const TableDesc& dstTable = f.codeMeta().tables[dstTableIndex];
6958 const TableDesc& srcTable = f.codeMeta().tables[srcTableIndex];
6959
6960 IndexType dstIndexType = dstTable.indexType();
6961 IndexType srcIndexType = srcTable.indexType();
6962 IndexType lenIndexType =
6963 dstIndexType == IndexType::I64 && srcIndexType == IndexType::I64
6964 ? IndexType::I64
6965 : IndexType::I32;
6966
6967 MDefinition* dst32 = f.tableIndexToI32(dstIndexType, dst);
6968 if (!dst32) {
6969 return false;
6970 }
6971
6972 MDefinition* src32 = f.tableIndexToI32(srcIndexType, src);
6973 if (!src32) {
6974 return false;
6975 }
6976
6977 MDefinition* len32 = f.tableIndexToI32(lenIndexType, len);
6978 if (!len32) {
6979 return false;
6980 }
6981
6982 MDefinition* dti = f.constantI32(int32_t(dstTableIndex));
6983 MDefinition* sti = f.constantI32(int32_t(srcTableIndex));
6984
6985 return f.emitInstanceCall5(bytecodeOffset, SASigTableCopy, dst32, src32,
6986 len32, dti, sti);
6987}
6988
6989static bool EmitDataOrElemDrop(FunctionCompiler& f, bool isData) {
6990 uint32_t segIndexVal = 0;
6991 if (!f.iter().readDataOrElemDrop(isData, &segIndexVal)) {
6992 return false;
6993 }
6994
6995 if (f.inDeadCode()) {
6996 return true;
6997 }
6998
6999 uint32_t bytecodeOffset = f.readBytecodeOffset();
7000
7001 MDefinition* segIndex = f.constantI32(int32_t(segIndexVal));
7002
7003 const SymbolicAddressSignature& callee =
7004 isData ? SASigDataDrop : SASigElemDrop;
7005 return f.emitInstanceCall1(bytecodeOffset, callee, segIndex);
7006}
7007
7008static bool EmitMemFillCall(FunctionCompiler& f, uint32_t memoryIndex,
7009 MDefinition* start, MDefinition* val,
7010 MDefinition* len) {
7011 MDefinition* memoryBase = f.memoryBase(memoryIndex);
7012
7013 uint32_t bytecodeOffset = f.readBytecodeOffset();
7014 const SymbolicAddressSignature& callee =
7015 (f.codeMeta().usesSharedMemory(memoryIndex)
7016 ? (f.isMem32(memoryIndex) ? SASigMemFillSharedM32
7017 : SASigMemFillSharedM64)
7018 : (f.isMem32(memoryIndex) ? SASigMemFillM32 : SASigMemFillM64));
7019 return f.emitInstanceCall4(bytecodeOffset, callee, start, val, len,
7020 memoryBase);
7021}
7022
7023static bool EmitMemFillInline(FunctionCompiler& f, uint32_t memoryIndex,
7024 MDefinition* start, MDefinition* val,
7025 uint32_t length) {
7026 MOZ_ASSERT(length != 0 && length <= MaxInlineMemoryFillLength)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length != 0 && length <= MaxInlineMemoryFillLength
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(length != 0 && length <= MaxInlineMemoryFillLength
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"length != 0 && length <= MaxInlineMemoryFillLength"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 7026); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length != 0 && length <= MaxInlineMemoryFillLength"
")"); do { *((volatile int*)__null) = 7026; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7027 uint32_t value = val->toConstant()->toInt32();
7028
7029 // Compute the number of copies of each width we will need to do
7030 size_t remainder = length;
7031#ifdef ENABLE_WASM_SIMD1
7032 size_t numCopies16 = 0;
7033 if (MacroAssembler::SupportsFastUnalignedFPAccesses()) {
7034 numCopies16 = remainder / sizeof(V128);
7035 remainder %= sizeof(V128);
7036 }
7037#endif
7038#ifdef JS_64BIT1
7039 size_t numCopies8 = remainder / sizeof(uint64_t);
7040 remainder %= sizeof(uint64_t);
7041#endif
7042 size_t numCopies4 = remainder / sizeof(uint32_t);
7043 remainder %= sizeof(uint32_t);
7044 size_t numCopies2 = remainder / sizeof(uint16_t);
7045 remainder %= sizeof(uint16_t);
7046 size_t numCopies1 = remainder;
7047
7048 // Generate splatted definitions for wider fills as needed
7049#ifdef ENABLE_WASM_SIMD1
7050 MDefinition* val16 = numCopies16 ? f.constantV128(V128(value)) : nullptr;
7051#endif
7052#ifdef JS_64BIT1
7053 MDefinition* val8 =
7054 numCopies8 ? f.constantI64(int64_t(SplatByteToUInt<uint64_t>(value, 8)))
7055 : nullptr;
7056#endif
7057 MDefinition* val4 =
7058 numCopies4 ? f.constantI32(int32_t(SplatByteToUInt<uint32_t>(value, 4)))
7059 : nullptr;
7060 MDefinition* val2 =
7061 numCopies2 ? f.constantI32(int32_t(SplatByteToUInt<uint32_t>(value, 2)))
7062 : nullptr;
7063
7064 // Store the fill value to the destination from high to low. We will trap
7065 // without writing anything on the first store if any dest byte is
7066 // out-of-bounds.
7067 size_t offset = length;
7068
7069 if (numCopies1) {
7070 offset -= sizeof(uint8_t);
7071
7072 MemoryAccessDesc access(memoryIndex, Scalar::Uint8, 1, offset,
7073 f.bytecodeOffset(),
7074 f.hugeMemoryEnabled(memoryIndex));
7075 f.store(start, &access, val);
7076 }
7077
7078 if (numCopies2) {
7079 offset -= sizeof(uint16_t);
7080
7081 MemoryAccessDesc access(memoryIndex, Scalar::Uint16, 1, offset,
7082 f.bytecodeOffset(),
7083 f.hugeMemoryEnabled(memoryIndex));
7084 f.store(start, &access, val2);
7085 }
7086
7087 for (uint32_t i = 0; i < numCopies4; i++) {
7088 offset -= sizeof(uint32_t);
7089
7090 MemoryAccessDesc access(memoryIndex, Scalar::Uint32, 1, offset,
7091 f.bytecodeOffset(),
7092 f.hugeMemoryEnabled(memoryIndex));
7093 f.store(start, &access, val4);
7094 }
7095
7096#ifdef JS_64BIT1
7097 for (uint32_t i = 0; i < numCopies8; i++) {
7098 offset -= sizeof(uint64_t);
7099
7100 MemoryAccessDesc access(memoryIndex, Scalar::Int64, 1, offset,
7101 f.bytecodeOffset(),
7102 f.hugeMemoryEnabled(memoryIndex));
7103 f.store(start, &access, val8);
7104 }
7105#endif
7106
7107#ifdef ENABLE_WASM_SIMD1
7108 for (uint32_t i = 0; i < numCopies16; i++) {
7109 offset -= sizeof(V128);
7110
7111 MemoryAccessDesc access(memoryIndex, Scalar::Simd128, 1, offset,
7112 f.bytecodeOffset(),
7113 f.hugeMemoryEnabled(memoryIndex));
7114 f.store(start, &access, val16);
7115 }
7116#endif
7117
7118 return true;
7119}
7120
7121static bool EmitMemFill(FunctionCompiler& f) {
7122 uint32_t memoryIndex;
7123 MDefinition *start, *val, *len;
7124 if (!f.iter().readMemFill(&memoryIndex, &start, &val, &len)) {
7125 return false;
7126 }
7127
7128 if (f.inDeadCode()) {
7129 return true;
7130 }
7131
7132 if (len->isConstant() && val->isConstant()) {
7133 uint64_t length = f.isMem32(memoryIndex) ? len->toConstant()->toInt32()
7134 : len->toConstant()->toInt64();
7135 static_assert(MaxInlineMemoryFillLength <= UINT32_MAX(4294967295U));
7136 if (length != 0 && length <= MaxInlineMemoryFillLength) {
7137 return EmitMemFillInline(f, memoryIndex, start, val, uint32_t(length));
7138 }
7139 }
7140
7141 return EmitMemFillCall(f, memoryIndex, start, val, len);
7142}
7143
7144static bool EmitMemInit(FunctionCompiler& f) {
7145 uint32_t segIndexVal = 0, dstMemIndex = 0;
7146 MDefinition *dstOff, *srcOff, *len;
7147 if (!f.iter().readMemOrTableInit(true, &segIndexVal, &dstMemIndex, &dstOff,
7148 &srcOff, &len)) {
7149 return false;
7150 }
7151
7152 if (f.inDeadCode()) {
7153 return true;
7154 }
7155
7156 uint32_t bytecodeOffset = f.readBytecodeOffset();
7157 const SymbolicAddressSignature& callee =
7158 (f.isMem32(dstMemIndex) ? SASigMemInitM32 : SASigMemInitM64);
7159
7160 MDefinition* segIndex = f.constantI32(int32_t(segIndexVal));
7161 if (!segIndex) {
7162 return false;
7163 }
7164
7165 MDefinition* dti = f.constantI32(int32_t(dstMemIndex));
7166 if (!dti) {
7167 return false;
7168 }
7169
7170 return f.emitInstanceCall5(bytecodeOffset, callee, dstOff, srcOff, len,
7171 segIndex, dti);
7172}
7173
7174static bool EmitTableInit(FunctionCompiler& f) {
7175 uint32_t segIndexVal = 0, dstTableIndex = 0;
7176 MDefinition *dstOff, *srcOff, *len;
7177 if (!f.iter().readMemOrTableInit(false, &segIndexVal, &dstTableIndex, &dstOff,
7178 &srcOff, &len)) {
7179 return false;
7180 }
7181
7182 if (f.inDeadCode()) {
7183 return true;
7184 }
7185
7186 uint32_t bytecodeOffset = f.readBytecodeOffset();
7187 const TableDesc& table = f.codeMeta().tables[dstTableIndex];
7188
7189 MDefinition* dstOff32 = f.tableIndexToI32(table.indexType(), dstOff);
7190 if (!dstOff32) {
7191 return false;
7192 }
7193
7194 MDefinition* segIndex = f.constantI32(int32_t(segIndexVal));
7195 if (!segIndex) {
7196 return false;
7197 }
7198
7199 MDefinition* dti = f.constantI32(int32_t(dstTableIndex));
7200 if (!dti) {
7201 return false;
7202 }
7203
7204 return f.emitInstanceCall5(bytecodeOffset, SASigTableInit, dstOff32, srcOff,
7205 len, segIndex, dti);
7206}
7207
7208static bool EmitTableFill(FunctionCompiler& f) {
7209 uint32_t tableIndex;
7210 MDefinition *start, *val, *len;
7211 if (!f.iter().readTableFill(&tableIndex, &start, &val, &len)) {
7212 return false;
7213 }
7214
7215 if (f.inDeadCode()) {
7216 return true;
7217 }
7218
7219 uint32_t bytecodeOffset = f.readBytecodeOffset();
7220 const TableDesc& table = f.codeMeta().tables[tableIndex];
7221
7222 MDefinition* start32 = f.tableIndexToI32(table.indexType(), start);
7223 if (!start32) {
7224 return false;
7225 }
7226
7227 MDefinition* len32 = f.tableIndexToI32(table.indexType(), len);
7228 if (!len32) {
7229 return false;
7230 }
7231
7232 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7233 if (!tableIndexArg) {
7234 return false;
7235 }
7236
7237 return f.emitInstanceCall4(bytecodeOffset, SASigTableFill, start32, val,
7238 len32, tableIndexArg);
7239}
7240
7241#if ENABLE_WASM_MEMORY_CONTROL1
7242static bool EmitMemDiscard(FunctionCompiler& f) {
7243 uint32_t memoryIndex;
7244 MDefinition *start, *len;
7245 if (!f.iter().readMemDiscard(&memoryIndex, &start, &len)) {
7246 return false;
7247 }
7248
7249 if (f.inDeadCode()) {
7250 return true;
7251 }
7252
7253 uint32_t bytecodeOffset = f.readBytecodeOffset();
7254
7255 MDefinition* memoryBase = f.memoryBase(memoryIndex);
7256 bool isMem32 = f.isMem32(memoryIndex);
7257
7258 const SymbolicAddressSignature& callee =
7259 (f.codeMeta().usesSharedMemory(memoryIndex)
7260 ? (isMem32 ? SASigMemDiscardSharedM32 : SASigMemDiscardSharedM64)
7261 : (isMem32 ? SASigMemDiscardM32 : SASigMemDiscardM64));
7262 return f.emitInstanceCall3(bytecodeOffset, callee, start, len, memoryBase);
7263}
7264#endif
7265
7266static bool EmitTableGet(FunctionCompiler& f) {
7267 uint32_t tableIndex;
7268 MDefinition* index;
7269 if (!f.iter().readTableGet(&tableIndex, &index)) {
7270 return false;
7271 }
7272
7273 if (f.inDeadCode()) {
7274 return true;
7275 }
7276
7277 const TableDesc& table = f.codeMeta().tables[tableIndex];
7278
7279 MDefinition* index32 = f.tableIndexToI32(table.indexType(), index);
7280 if (!index32) {
7281 return false;
7282 }
7283
7284 if (table.elemType.tableRepr() == TableRepr::Ref) {
7285 MDefinition* ret = f.tableGetAnyRef(tableIndex, index32);
7286 if (!ret) {
7287 return false;
7288 }
7289 f.iter().setResult(ret);
7290 return true;
7291 }
7292
7293 uint32_t bytecodeOffset = f.readBytecodeOffset();
7294
7295 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7296 if (!tableIndexArg) {
7297 return false;
7298 }
7299
7300 // The return value here is either null, denoting an error, or a short-lived
7301 // pointer to a location containing a possibly-null ref.
7302 MDefinition* ret;
7303 if (!f.emitInstanceCall2(bytecodeOffset, SASigTableGet, index32,
7304 tableIndexArg, &ret)) {
7305 return false;
7306 }
7307
7308 f.iter().setResult(ret);
7309 return true;
7310}
7311
7312static bool EmitTableGrow(FunctionCompiler& f) {
7313 uint32_t tableIndex;
7314 MDefinition* initValue;
7315 MDefinition* delta;
7316 if (!f.iter().readTableGrow(&tableIndex, &initValue, &delta)) {
7317 return false;
7318 }
7319
7320 if (f.inDeadCode()) {
7321 return true;
7322 }
7323
7324 uint32_t bytecodeOffset = f.readBytecodeOffset();
7325 const TableDesc& table = f.codeMeta().tables[tableIndex];
7326
7327 MDefinition* delta32 = f.tableIndexToI32(table.indexType(), delta);
7328 if (!delta32) {
7329 return false;
7330 }
7331
7332 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7333 if (!tableIndexArg) {
7334 return false;
7335 }
7336
7337 MDefinition* ret;
7338 if (!f.emitInstanceCall3(bytecodeOffset, SASigTableGrow, initValue, delta32,
7339 tableIndexArg, &ret)) {
7340 return false;
7341 }
7342
7343 if (table.indexType() == IndexType::I64) {
7344 ret = f.extendI32(ret, false);
7345 if (!ret) {
7346 return false;
7347 }
7348 }
7349
7350 f.iter().setResult(ret);
7351 return true;
7352}
7353
7354static bool EmitTableSet(FunctionCompiler& f) {
7355 uint32_t tableIndex;
7356 MDefinition* index;
7357 MDefinition* value;
7358 if (!f.iter().readTableSet(&tableIndex, &index, &value)) {
7359 return false;
7360 }
7361
7362 if (f.inDeadCode()) {
7363 return true;
7364 }
7365
7366 uint32_t bytecodeOffset = f.readBytecodeOffset();
7367
7368 const TableDesc& table = f.codeMeta().tables[tableIndex];
7369
7370 MDefinition* index32 = f.tableIndexToI32(table.indexType(), index);
7371 if (!index32) {
7372 return false;
7373 }
7374
7375 if (table.elemType.tableRepr() == TableRepr::Ref) {
7376 return f.tableSetAnyRef(tableIndex, index32, value, bytecodeOffset);
7377 }
7378
7379 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7380 if (!tableIndexArg) {
7381 return false;
7382 }
7383
7384 return f.emitInstanceCall3(bytecodeOffset, SASigTableSet, index32, value,
7385 tableIndexArg);
7386}
7387
7388static bool EmitTableSize(FunctionCompiler& f) {
7389 uint32_t tableIndex;
7390 if (!f.iter().readTableSize(&tableIndex)) {
7391 return false;
7392 }
7393
7394 if (f.inDeadCode()) {
7395 return true;
7396 }
7397
7398 MDefinition* length = f.loadTableLength(tableIndex);
7399 if (!length) {
7400 return false;
7401 }
7402
7403 if (f.codeMeta().tables[tableIndex].indexType() == IndexType::I64) {
7404 length = f.extendI32(length, true);
7405 if (!length) {
7406 return false;
7407 }
7408 }
7409
7410 f.iter().setResult(length);
7411 return true;
7412}
7413
7414static bool EmitRefFunc(FunctionCompiler& f) {
7415 uint32_t funcIndex;
7416 if (!f.iter().readRefFunc(&funcIndex)) {
7417 return false;
7418 }
7419
7420 if (f.inDeadCode()) {
7421 return true;
7422 }
7423
7424 uint32_t bytecodeOffset = f.readBytecodeOffset();
7425
7426 MDefinition* funcIndexArg = f.constantI32(int32_t(funcIndex));
7427 if (!funcIndexArg) {
7428 return false;
7429 }
7430
7431 // The return value here is either null, denoting an error, or a short-lived
7432 // pointer to a location containing a possibly-null ref.
7433 MDefinition* ret;
7434 if (!f.emitInstanceCall1(bytecodeOffset, SASigRefFunc, funcIndexArg, &ret)) {
7435 return false;
7436 }
7437
7438 f.iter().setResult(ret);
7439 return true;
7440}
7441
7442static bool EmitRefNull(FunctionCompiler& f) {
7443 RefType type;
7444 if (!f.iter().readRefNull(&type)) {
7445 return false;
7446 }
7447
7448 if (f.inDeadCode()) {
7449 return true;
7450 }
7451
7452 MDefinition* nullVal = f.constantNullRef();
7453 if (!nullVal) {
7454 return false;
7455 }
7456 f.iter().setResult(nullVal);
7457 return true;
7458}
7459
7460static bool EmitRefIsNull(FunctionCompiler& f) {
7461 MDefinition* input;
7462 if (!f.iter().readRefIsNull(&input)) {
7463 return false;
7464 }
7465
7466 if (f.inDeadCode()) {
7467 return true;
7468 }
7469
7470 MDefinition* nullVal = f.constantNullRef();
7471 if (!nullVal) {
7472 return false;
7473 }
7474 f.iter().setResult(
7475 f.compare(input, nullVal, JSOp::Eq, MCompare::Compare_WasmAnyRef));
7476 return true;
7477}
7478
7479#ifdef ENABLE_WASM_SIMD1
7480static bool EmitConstSimd128(FunctionCompiler& f) {
7481 V128 v128;
7482 if (!f.iter().readV128Const(&v128)) {
7483 return false;
7484 }
7485
7486 f.iter().setResult(f.constantV128(v128));
7487 return true;
7488}
7489
7490static bool EmitBinarySimd128(FunctionCompiler& f, bool commutative,
7491 SimdOp op) {
7492 MDefinition* lhs;
7493 MDefinition* rhs;
7494 if (!f.iter().readBinary(ValType::V128, &lhs, &rhs)) {
7495 return false;
7496 }
7497
7498 f.iter().setResult(f.binarySimd128(lhs, rhs, commutative, op));
7499 return true;
7500}
7501
7502static bool EmitTernarySimd128(FunctionCompiler& f, wasm::SimdOp op) {
7503 MDefinition* v0;
7504 MDefinition* v1;
7505 MDefinition* v2;
7506 if (!f.iter().readTernary(ValType::V128, &v0, &v1, &v2)) {
7507 return false;
7508 }
7509
7510 f.iter().setResult(f.ternarySimd128(v0, v1, v2, op));
7511 return true;
7512}
7513
7514static bool EmitShiftSimd128(FunctionCompiler& f, SimdOp op) {
7515 MDefinition* lhs;
7516 MDefinition* rhs;
7517 if (!f.iter().readVectorShift(&lhs, &rhs)) {
7518 return false;
7519 }
7520
7521 f.iter().setResult(f.shiftSimd128(lhs, rhs, op));
7522 return true;
7523}
7524
7525static bool EmitSplatSimd128(FunctionCompiler& f, ValType inType, SimdOp op) {
7526 MDefinition* src;
7527 if (!f.iter().readConversion(inType, ValType::V128, &src)) {
7528 return false;
7529 }
7530
7531 f.iter().setResult(f.scalarToSimd128(src, op));
7532 return true;
7533}
7534
7535static bool EmitUnarySimd128(FunctionCompiler& f, SimdOp op) {
7536 MDefinition* src;
7537 if (!f.iter().readUnary(ValType::V128, &src)) {
7538 return false;
7539 }
7540
7541 f.iter().setResult(f.unarySimd128(src, op));
7542 return true;
7543}
7544
7545static bool EmitReduceSimd128(FunctionCompiler& f, SimdOp op) {
7546 MDefinition* src;
7547 if (!f.iter().readConversion(ValType::V128, ValType::I32, &src)) {
7548 return false;
7549 }
7550
7551 f.iter().setResult(f.reduceSimd128(src, op, ValType::I32));
7552 return true;
7553}
7554
7555static bool EmitExtractLaneSimd128(FunctionCompiler& f, ValType outType,
7556 uint32_t laneLimit, SimdOp op) {
7557 uint32_t laneIndex;
7558 MDefinition* src;
7559 if (!f.iter().readExtractLane(outType, laneLimit, &laneIndex, &src)) {
7560 return false;
7561 }
7562
7563 f.iter().setResult(f.reduceSimd128(src, op, outType, laneIndex));
7564 return true;
7565}
7566
7567static bool EmitReplaceLaneSimd128(FunctionCompiler& f, ValType laneType,
7568 uint32_t laneLimit, SimdOp op) {
7569 uint32_t laneIndex;
7570 MDefinition* lhs;
7571 MDefinition* rhs;
7572 if (!f.iter().readReplaceLane(laneType, laneLimit, &laneIndex, &lhs, &rhs)) {
7573 return false;
7574 }
7575
7576 f.iter().setResult(f.replaceLaneSimd128(lhs, rhs, laneIndex, op));
7577 return true;
7578}
7579
7580static bool EmitShuffleSimd128(FunctionCompiler& f) {
7581 MDefinition* v1;
7582 MDefinition* v2;
7583 V128 control;
7584 if (!f.iter().readVectorShuffle(&v1, &v2, &control)) {
7585 return false;
7586 }
7587
7588 f.iter().setResult(f.shuffleSimd128(v1, v2, control));
7589 return true;
7590}
7591
7592static bool EmitLoadSplatSimd128(FunctionCompiler& f, Scalar::Type viewType,
7593 wasm::SimdOp splatOp) {
7594 LinearMemoryAddress<MDefinition*> addr;
7595 if (!f.iter().readLoadSplat(Scalar::byteSize(viewType), &addr)) {
7596 return false;
7597 }
7598
7599 auto* ins = f.loadSplatSimd128(viewType, addr, splatOp);
7600 if (!f.inDeadCode() && !ins) {
7601 return false;
7602 }
7603 f.iter().setResult(ins);
7604 return true;
7605}
7606
7607static bool EmitLoadExtendSimd128(FunctionCompiler& f, wasm::SimdOp op) {
7608 LinearMemoryAddress<MDefinition*> addr;
7609 if (!f.iter().readLoadExtend(&addr)) {
7610 return false;
7611 }
7612
7613 auto* ins = f.loadExtendSimd128(addr, op);
7614 if (!f.inDeadCode() && !ins) {
7615 return false;
7616 }
7617 f.iter().setResult(ins);
7618 return true;
7619}
7620
7621static bool EmitLoadZeroSimd128(FunctionCompiler& f, Scalar::Type viewType,
7622 size_t numBytes) {
7623 LinearMemoryAddress<MDefinition*> addr;
7624 if (!f.iter().readLoadSplat(numBytes, &addr)) {
7625 return false;
7626 }
7627
7628 auto* ins = f.loadZeroSimd128(viewType, numBytes, addr);
7629 if (!f.inDeadCode() && !ins) {
7630 return false;
7631 }
7632 f.iter().setResult(ins);
7633 return true;
7634}
7635
7636static bool EmitLoadLaneSimd128(FunctionCompiler& f, uint32_t laneSize) {
7637 uint32_t laneIndex;
7638 MDefinition* src;
7639 LinearMemoryAddress<MDefinition*> addr;
7640 if (!f.iter().readLoadLane(laneSize, &addr, &laneIndex, &src)) {
7641 return false;
7642 }
7643
7644 auto* ins = f.loadLaneSimd128(laneSize, addr, laneIndex, src);
7645 if (!f.inDeadCode() && !ins) {
7646 return false;
7647 }
7648 f.iter().setResult(ins);
7649 return true;
7650}
7651
7652static bool EmitStoreLaneSimd128(FunctionCompiler& f, uint32_t laneSize) {
7653 uint32_t laneIndex;
7654 MDefinition* src;
7655 LinearMemoryAddress<MDefinition*> addr;
7656 if (!f.iter().readStoreLane(laneSize, &addr, &laneIndex, &src)) {
7657 return false;
7658 }
7659
7660 f.storeLaneSimd128(laneSize, addr, laneIndex, src);
7661 return true;
7662}
7663
7664#endif // ENABLE_WASM_SIMD
7665
7666#ifdef ENABLE_WASM_GC1
7667static bool EmitRefAsNonNull(FunctionCompiler& f) {
7668 MDefinition* ref;
7669 if (!f.iter().readRefAsNonNull(&ref)) {
7670 return false;
7671 }
7672
7673 return f.refAsNonNull(ref);
7674}
7675
7676static bool EmitBrOnNull(FunctionCompiler& f) {
7677 uint32_t relativeDepth;
7678 ResultType type;
7679 DefVector values;
7680 MDefinition* condition;
7681 if (!f.iter().readBrOnNull(&relativeDepth, &type, &values, &condition)) {
7682 return false;
7683 }
7684
7685 return f.brOnNull(relativeDepth, values, type, condition);
7686}
7687
7688static bool EmitBrOnNonNull(FunctionCompiler& f) {
7689 uint32_t relativeDepth;
7690 ResultType type;
7691 DefVector values;
7692 MDefinition* condition;
7693 if (!f.iter().readBrOnNonNull(&relativeDepth, &type, &values, &condition)) {
7694 return false;
7695 }
7696
7697 return f.brOnNonNull(relativeDepth, values, type, condition);
7698}
7699
7700static bool EmitCallRef(FunctionCompiler& f) {
7701 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7702
7703 const FuncType* funcType;
7704 MDefinition* callee;
7705 DefVector args;
7706
7707 if (!f.iter().readCallRef(&funcType, &callee, &args)) {
7708 return false;
7709 }
7710
7711 if (f.inDeadCode()) {
7712 return true;
7713 }
7714
7715 CallCompileState call;
7716 if (!EmitCallArgs(f, *funcType, args, &call)) {
7717 return false;
7718 }
7719
7720 DefVector results;
7721 if (!f.callRef(*funcType, callee, lineOrBytecode, call, &results)) {
7722 return false;
7723 }
7724
7725 f.iter().setResults(results.length(), results);
7726 return true;
7727}
7728
7729#endif // ENABLE_WASM_GC
7730
7731#ifdef ENABLE_WASM_GC1
7732
7733static bool EmitStructNew(FunctionCompiler& f) {
7734 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7735
7736 uint32_t typeIndex;
7737 DefVector args;
7738 if (!f.iter().readStructNew(&typeIndex, &args)) {
7739 return false;
7740 }
7741
7742 if (f.inDeadCode()) {
7743 return true;
7744 }
7745
7746 const TypeDef& typeDef = (*f.codeMeta().types)[typeIndex];
7747 const StructType& structType = typeDef.structType();
7748 MOZ_ASSERT(args.length() == structType.fields_.length())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(args.length() == structType.fields_.length())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(args.length() == structType.fields_.length()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("args.length() == structType.fields_.length()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 7748); AnnotateMozCrashReason("MOZ_ASSERT" "(" "args.length() == structType.fields_.length()"
")"); do { *((volatile int*)__null) = 7748; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7749
7750 MDefinition* structObject = f.createStructObject(typeIndex, false);
7751 if (!structObject) {
7752 return false;
7753 }
7754
7755 // And fill in the fields.
7756 for (uint32_t fieldIndex = 0; fieldIndex < structType.fields_.length();
7757 fieldIndex++) {
7758 if (!f.mirGen().ensureBallast()) {
7759 return false;
7760 }
7761 const StructField& field = structType.fields_[fieldIndex];
7762 if (!f.writeValueToStructField(lineOrBytecode, field, structObject,
7763 args[fieldIndex],
7764 WasmPreBarrierKind::None)) {
7765 return false;
7766 }
7767 }
7768
7769 f.iter().setResult(structObject);
7770 return true;
7771}
7772
7773static bool EmitStructNewDefault(FunctionCompiler& f) {
7774 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7775
7776 uint32_t typeIndex;
7777 if (!f.iter().readStructNewDefault(&typeIndex)) {
7778 return false;
7779 }
7780
7781 if (f.inDeadCode()) {
7782 return true;
7783 }
7784
7785 const StructType& structType = (*f.codeMeta().types)[typeIndex].structType();
7786
7787 // Allocate a default initialized struct. This requires the type definition
7788 // for the struct.
7789 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
7790 if (!typeDefData) {
7791 return false;
7792 }
7793
7794 // Figure out whether we need an OOL storage area, and hence which routine
7795 // to call.
7796 SymbolicAddressSignature calleeSASig =
7797 WasmStructObject::requiresOutlineBytes(structType.size_)
7798 ? SASigStructNewOOL_true
7799 : SASigStructNewIL_true;
7800
7801 // Create call: structObject = Instance::structNew{IL,OOL}<true>(typeDefData)
7802 MDefinition* structObject;
7803 if (!f.emitInstanceCall1(lineOrBytecode, calleeSASig, typeDefData,
7804 &structObject)) {
7805 return false;
7806 }
7807
7808 f.iter().setResult(structObject);
7809 return true;
7810}
7811
7812static bool EmitStructSet(FunctionCompiler& f) {
7813 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7814
7815 uint32_t typeIndex;
7816 uint32_t fieldIndex;
7817 MDefinition* structObject;
7818 MDefinition* value;
7819 if (!f.iter().readStructSet(&typeIndex, &fieldIndex, &structObject, &value)) {
7820 return false;
7821 }
7822
7823 if (f.inDeadCode()) {
7824 return true;
7825 }
7826
7827 // Check for null is done at writeValueToStructField.
7828
7829 // And fill in the field.
7830 const StructType& structType = (*f.codeMeta().types)[typeIndex].structType();
7831 const StructField& field = structType.fields_[fieldIndex];
7832 return f.writeValueToStructField(lineOrBytecode, field, structObject, value,
7833 WasmPreBarrierKind::Normal);
7834}
7835
7836static bool EmitStructGet(FunctionCompiler& f, FieldWideningOp wideningOp) {
7837 uint32_t typeIndex;
7838 uint32_t fieldIndex;
7839 MDefinition* structObject;
7840 if (!f.iter().readStructGet(&typeIndex, &fieldIndex, wideningOp,
7841 &structObject)) {
7842 return false;
7843 }
7844
7845 if (f.inDeadCode()) {
7846 return true;
7847 }
7848
7849 // Check for null is done at readValueFromStructField.
7850
7851 // And fetch the data.
7852 const StructType& structType = (*f.codeMeta().types)[typeIndex].structType();
7853 const StructField& field = structType.fields_[fieldIndex];
7854 MDefinition* load =
7855 f.readValueFromStructField(field, wideningOp, structObject);
7856 if (!load) {
7857 return false;
7858 }
7859
7860 f.iter().setResult(load);
7861 return true;
7862}
7863
7864static bool EmitArrayNew(FunctionCompiler& f) {
7865 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7866
7867 uint32_t typeIndex;
7868 MDefinition* numElements;
7869 MDefinition* fillValue;
7870 if (!f.iter().readArrayNew(&typeIndex, &numElements, &fillValue)) {
7871 return false;
7872 }
7873
7874 if (f.inDeadCode()) {
7875 return true;
7876 }
7877
7878 // If the requested size exceeds MaxArrayPayloadBytes, the MIR generated by
7879 // this helper will trap.
7880 MDefinition* arrayObject = f.createArrayNewCallAndLoop(
7881 lineOrBytecode, typeIndex, numElements, fillValue);
7882 if (!arrayObject) {
7883 return false;
7884 }
7885
7886 f.iter().setResult(arrayObject);
7887 return true;
7888}
7889
7890static bool EmitArrayNewDefault(FunctionCompiler& f) {
7891 // This is almost identical to EmitArrayNew, except we skip the
7892 // initialisation loop.
7893 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7894
7895 uint32_t typeIndex;
7896 MDefinition* numElements;
7897 if (!f.iter().readArrayNewDefault(&typeIndex, &numElements)) {
7898 return false;
7899 }
7900
7901 if (f.inDeadCode()) {
7902 return true;
7903 }
7904
7905 // Create the array object, default-initialized.
7906 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
7907 MDefinition* arrayObject =
7908 f.createArrayObject(lineOrBytecode, typeIndex, numElements,
7909 arrayType.elementType_.size(), /*zeroFields=*/true);
7910 if (!arrayObject) {
7911 return false;
7912 }
7913
7914 f.iter().setResult(arrayObject);
7915 return true;
7916}
7917
7918static bool EmitArrayNewFixed(FunctionCompiler& f) {
7919 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7920
7921 uint32_t typeIndex, numElements;
7922 DefVector values;
7923
7924 if (!f.iter().readArrayNewFixed(&typeIndex, &numElements, &values)) {
7925 return false;
7926 }
7927 MOZ_ASSERT(values.length() == numElements)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(values.length() == numElements)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(values.length() == numElements
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"values.length() == numElements", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 7927); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.length() == numElements"
")"); do { *((volatile int*)__null) = 7927; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7928
7929 if (f.inDeadCode()) {
7930 return true;
7931 }
7932
7933 MDefinition* numElementsDef = f.constantI32(int32_t(numElements));
7934 if (!numElementsDef) {
7935 return false;
7936 }
7937
7938 // Create the array object, uninitialized.
7939 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
7940 StorageType elemType = arrayType.elementType_;
7941 uint32_t elemSize = elemType.size();
7942 MDefinition* arrayObject =
7943 f.createArrayObject(lineOrBytecode, typeIndex, numElementsDef, elemSize,
7944 /*zeroFields=*/false);
7945 if (!arrayObject) {
7946 return false;
7947 }
7948
7949 // Make `base` point at the first byte of the (OOL) data area.
7950 MDefinition* base = f.getWasmArrayObjectData(arrayObject);
7951 if (!base) {
7952 return false;
7953 }
7954
7955 // Write each element in turn.
7956
7957 // How do we know that the offset expression `i * elemSize` below remains
7958 // within 2^31 (signed-i32) range? In the worst case we will have 16-byte
7959 // values, and there can be at most MaxFunctionBytes expressions, if it were
7960 // theoretically possible to generate one expression per instruction byte.
7961 // Hence the max offset we can be expected to generate is
7962 // `16 * MaxFunctionBytes`.
7963 static_assert(16 /* sizeof v128 */ * MaxFunctionBytes <=
7964 MaxArrayPayloadBytes);
7965 MOZ_RELEASE_ASSERT(numElements <= MaxFunctionBytes)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numElements <= MaxFunctionBytes)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(numElements <= MaxFunctionBytes
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"numElements <= MaxFunctionBytes", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 7965); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "numElements <= MaxFunctionBytes"
")"); do { *((volatile int*)__null) = 7965; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7966
7967 for (uint32_t i = 0; i < numElements; i++) {
7968 if (!f.mirGen().ensureBallast()) {
7969 return false;
7970 }
7971 // `i * elemSize` is made safe by the assertions above.
7972 if (!f.writeGcValueAtBasePlusOffset(
7973 lineOrBytecode, elemType, arrayObject, AliasSet::WasmArrayDataArea,
7974 values[numElements - 1 - i], base, i * elemSize, false,
7975 WasmPreBarrierKind::None)) {
7976 return false;
7977 }
7978 }
7979
7980 f.iter().setResult(arrayObject);
7981 return true;
7982}
7983
7984static bool EmitArrayNewData(FunctionCompiler& f) {
7985 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
7986
7987 uint32_t typeIndex, segIndex;
7988 MDefinition* segByteOffset;
7989 MDefinition* numElements;
7990 if (!f.iter().readArrayNewData(&typeIndex, &segIndex, &segByteOffset,
7991 &numElements)) {
7992 return false;
7993 }
7994
7995 if (f.inDeadCode()) {
7996 return true;
7997 }
7998
7999 // Get the type definition data for the array as a whole.
8000 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8001 if (!typeDefData) {
8002 return false;
8003 }
8004
8005 // Other values we need to pass to the instance call:
8006 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8007 if (!segIndexM) {
8008 return false;
8009 }
8010
8011 // Create call:
8012 // arrayObject = Instance::arrayNewData(segByteOffset:u32, numElements:u32,
8013 // typeDefData:word, segIndex:u32)
8014 // If the requested size exceeds MaxArrayPayloadBytes, the MIR generated by
8015 // this call will trap.
8016 MDefinition* arrayObject;
8017 if (!f.emitInstanceCall4(lineOrBytecode, SASigArrayNewData, segByteOffset,
8018 numElements, typeDefData, segIndexM, &arrayObject)) {
8019 return false;
8020 }
8021
8022 f.iter().setResult(arrayObject);
8023 return true;
8024}
8025
8026static bool EmitArrayNewElem(FunctionCompiler& f) {
8027 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8028
8029 uint32_t typeIndex, segIndex;
8030 MDefinition* segElemIndex;
8031 MDefinition* numElements;
8032 if (!f.iter().readArrayNewElem(&typeIndex, &segIndex, &segElemIndex,
8033 &numElements)) {
8034 return false;
8035 }
8036
8037 if (f.inDeadCode()) {
8038 return true;
8039 }
8040
8041 // Get the type definition for the array as a whole.
8042 // Get the type definition data for the array as a whole.
8043 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8044 if (!typeDefData) {
8045 return false;
8046 }
8047
8048 // Other values we need to pass to the instance call:
8049 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8050 if (!segIndexM) {
8051 return false;
8052 }
8053
8054 // Create call:
8055 // arrayObject = Instance::arrayNewElem(segElemIndex:u32, numElements:u32,
8056 // typeDefData:word, segIndex:u32)
8057 // If the requested size exceeds MaxArrayPayloadBytes, the MIR generated by
8058 // this call will trap.
8059 MDefinition* arrayObject;
8060 if (!f.emitInstanceCall4(lineOrBytecode, SASigArrayNewElem, segElemIndex,
8061 numElements, typeDefData, segIndexM, &arrayObject)) {
8062 return false;
8063 }
8064
8065 f.iter().setResult(arrayObject);
8066 return true;
8067}
8068
8069static bool EmitArrayInitData(FunctionCompiler& f) {
8070 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8071
8072 uint32_t typeIndex, segIndex;
8073 MDefinition* array;
8074 MDefinition* arrayIndex;
8075 MDefinition* segOffset;
8076 MDefinition* length;
8077 if (!f.iter().readArrayInitData(&typeIndex, &segIndex, &array, &arrayIndex,
8078 &segOffset, &length)) {
8079 return false;
8080 }
8081
8082 if (f.inDeadCode()) {
8083 return true;
8084 }
8085
8086 // Get the type definition data for the array as a whole.
8087 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8088 if (!typeDefData) {
8089 return false;
8090 }
8091
8092 // Other values we need to pass to the instance call:
8093 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8094 if (!segIndexM) {
8095 return false;
8096 }
8097
8098 // Create call:
8099 // Instance::arrayInitData(array:word, index:u32, segByteOffset:u32,
8100 // numElements:u32, typeDefData:word, segIndex:u32) If the requested size
8101 // exceeds MaxArrayPayloadBytes, the MIR generated by this call will trap.
8102 return f.emitInstanceCall6(lineOrBytecode, SASigArrayInitData, array,
8103 arrayIndex, segOffset, length, typeDefData,
8104 segIndexM);
8105}
8106
8107static bool EmitArrayInitElem(FunctionCompiler& f) {
8108 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8109
8110 uint32_t typeIndex, segIndex;
8111 MDefinition* array;
8112 MDefinition* arrayIndex;
8113 MDefinition* segOffset;
8114 MDefinition* length;
8115 if (!f.iter().readArrayInitElem(&typeIndex, &segIndex, &array, &arrayIndex,
8116 &segOffset, &length)) {
8117 return false;
8118 }
8119
8120 if (f.inDeadCode()) {
8121 return true;
8122 }
8123
8124 // Get the type definition data for the array as a whole.
8125 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8126 if (!typeDefData) {
8127 return false;
8128 }
8129
8130 // Other values we need to pass to the instance call:
8131 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8132 if (!segIndexM) {
8133 return false;
8134 }
8135
8136 // Create call:
8137 // Instance::arrayInitElem(array:word, index:u32, segByteOffset:u32,
8138 // numElements:u32, typeDefData:word, segIndex:u32) If the requested size
8139 // exceeds MaxArrayPayloadBytes, the MIR generated by this call will trap.
8140 return f.emitInstanceCall6(lineOrBytecode, SASigArrayInitElem, array,
8141 arrayIndex, segOffset, length, typeDefData,
8142 segIndexM);
8143}
8144
8145static bool EmitArraySet(FunctionCompiler& f) {
8146 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8147
8148 uint32_t typeIndex;
8149 MDefinition* value;
8150 MDefinition* index;
8151 MDefinition* arrayObject;
8152 if (!f.iter().readArraySet(&typeIndex, &value, &index, &arrayObject)) {
8153 return false;
8154 }
8155
8156 if (f.inDeadCode()) {
8157 return true;
8158 }
8159
8160 // Check for null is done at setupForArrayAccess.
8161
8162 // Create the object null check and the array bounds check and get the OOL
8163 // data pointer.
8164 MDefinition* base = f.setupForArrayAccess(arrayObject, index);
8165 if (!base) {
8166 return false;
8167 }
8168
8169 // And do the store.
8170 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
8171 StorageType elemType = arrayType.elementType_;
8172 uint32_t elemSize = elemType.size();
8173 MOZ_ASSERT(elemSize >= 1 && elemSize <= 16)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(elemSize >= 1 && elemSize <= 16)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(elemSize >= 1 && elemSize <= 16))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("elemSize >= 1 && elemSize <= 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 8173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize >= 1 && elemSize <= 16"
")"); do { *((volatile int*)__null) = 8173; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8174
8175 return f.writeGcValueAtBasePlusScaledIndex(
8176 lineOrBytecode, elemType, arrayObject, AliasSet::WasmArrayDataArea, value,
8177 base, elemSize, index, WasmPreBarrierKind::Normal);
8178}
8179
8180static bool EmitArrayGet(FunctionCompiler& f, FieldWideningOp wideningOp) {
8181 uint32_t typeIndex;
8182 MDefinition* index;
8183 MDefinition* arrayObject;
8184 if (!f.iter().readArrayGet(&typeIndex, wideningOp, &index, &arrayObject)) {
8185 return false;
8186 }
8187
8188 if (f.inDeadCode()) {
8189 return true;
8190 }
8191
8192 // Check for null is done at setupForArrayAccess.
8193
8194 // Create the object null check and the array bounds check and get the data
8195 // pointer.
8196 MDefinition* base = f.setupForArrayAccess(arrayObject, index);
8197 if (!base) {
8198 return false;
8199 }
8200
8201 // And do the load.
8202 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
8203 StorageType elemType = arrayType.elementType_;
8204
8205 MDefinition* load =
8206 f.readGcArrayValueAtIndex(elemType, wideningOp, arrayObject,
8207 AliasSet::WasmArrayDataArea, base, index);
8208 if (!load) {
8209 return false;
8210 }
8211
8212 f.iter().setResult(load);
8213 return true;
8214}
8215
8216static bool EmitArrayLen(FunctionCompiler& f) {
8217 MDefinition* arrayObject;
8218 if (!f.iter().readArrayLen(&arrayObject)) {
8219 return false;
8220 }
8221
8222 if (f.inDeadCode()) {
8223 return true;
8224 }
8225
8226 // Check for null is done at getWasmArrayObjectNumElements.
8227
8228 // Get the size value for the array
8229 MDefinition* numElements = f.getWasmArrayObjectNumElements(arrayObject);
8230 if (!numElements) {
8231 return false;
8232 }
8233
8234 f.iter().setResult(numElements);
8235 return true;
8236}
8237
8238static bool EmitArrayCopy(FunctionCompiler& f) {
8239 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8240
8241 int32_t elemSize;
8242 bool elemsAreRefTyped;
8243 MDefinition* dstArrayObject;
8244 MDefinition* dstArrayIndex;
8245 MDefinition* srcArrayObject;
8246 MDefinition* srcArrayIndex;
8247 MDefinition* numElements;
8248 if (!f.iter().readArrayCopy(&elemSize, &elemsAreRefTyped, &dstArrayObject,
8249 &dstArrayIndex, &srcArrayObject, &srcArrayIndex,
8250 &numElements)) {
8251 return false;
8252 }
8253
8254 if (f.inDeadCode()) {
8255 return true;
8256 }
8257
8258 MOZ_ASSERT_IF(elemsAreRefTyped,do { if (elemsAreRefTyped) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(size_t(elemSize) == MIRTypeToSize
(TargetWordMIRType()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(size_t(elemSize) == MIRTypeToSize
(TargetWordMIRType())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("size_t(elemSize) == MIRTypeToSize(TargetWordMIRType())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 8259); AnnotateMozCrashReason("MOZ_ASSERT" "(" "size_t(elemSize) == MIRTypeToSize(TargetWordMIRType())"
")"); do { *((volatile int*)__null) = 8259; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
8259 size_t(elemSize) == MIRTypeToSize(TargetWordMIRType()))do { if (elemsAreRefTyped) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(size_t(elemSize) == MIRTypeToSize
(TargetWordMIRType()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(size_t(elemSize) == MIRTypeToSize
(TargetWordMIRType())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("size_t(elemSize) == MIRTypeToSize(TargetWordMIRType())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 8259); AnnotateMozCrashReason("MOZ_ASSERT" "(" "size_t(elemSize) == MIRTypeToSize(TargetWordMIRType())"
")"); do { *((volatile int*)__null) = 8259; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
8260 MOZ_ASSERT_IF(!elemsAreRefTyped, elemSize == 1 || elemSize == 2 ||do { if (!elemsAreRefTyped) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(elemSize == 1 || elemSize
== 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize
== 8 || elemSize == 16))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 8262); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 8262; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
8261 elemSize == 4 || elemSize == 8 ||do { if (!elemsAreRefTyped) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(elemSize == 1 || elemSize
== 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize
== 8 || elemSize == 16))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 8262); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 8262; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
8262 elemSize == 16)do { if (!elemsAreRefTyped) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(elemSize == 1 || elemSize
== 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize
== 8 || elemSize == 16))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 8262); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 8262; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
8263
8264 // A negative element size is used to inform Instance::arrayCopy that the
8265 // values are reftyped. This avoids having to pass it an extra boolean
8266 // argument.
8267 MDefinition* elemSizeDef =
8268 f.constantI32(elemsAreRefTyped ? -elemSize : elemSize);
8269 if (!elemSizeDef) {
8270 return false;
8271 }
8272
8273 // Create call:
8274 // Instance::arrayCopy(dstArrayObject:word, dstArrayIndex:u32,
8275 // srcArrayObject:word, srcArrayIndex:u32,
8276 // numElements:u32,
8277 // (elemsAreRefTyped ? -elemSize : elemSize):u32))
8278 return f.emitInstanceCall6(lineOrBytecode, SASigArrayCopy, dstArrayObject,
8279 dstArrayIndex, srcArrayObject, srcArrayIndex,
8280 numElements, elemSizeDef);
8281}
8282
8283static bool EmitArrayFill(FunctionCompiler& f) {
8284 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8285
8286 uint32_t typeIndex;
8287 MDefinition* array;
8288 MDefinition* index;
8289 MDefinition* val;
8290 MDefinition* numElements;
8291 if (!f.iter().readArrayFill(&typeIndex, &array, &index, &val, &numElements)) {
8292 return false;
8293 }
8294
8295 if (f.inDeadCode()) {
8296 return true;
8297 }
8298
8299 return f.createArrayFill(lineOrBytecode, typeIndex, array, index, val,
8300 numElements);
8301}
8302
8303static bool EmitRefI31(FunctionCompiler& f) {
8304 MDefinition* input;
8305 if (!f.iter().readConversion(
8306 ValType::I32, ValType(RefType::i31().asNonNullable()), &input)) {
8307 return false;
8308 }
8309
8310 if (f.inDeadCode()) {
8311 return true;
8312 }
8313
8314 MDefinition* output = f.refI31(input);
8315 if (!output) {
8316 return false;
8317 }
8318 f.iter().setResult(output);
8319 return true;
8320}
8321
8322static bool EmitI31Get(FunctionCompiler& f, FieldWideningOp wideningOp) {
8323 MOZ_ASSERT(wideningOp != FieldWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wideningOp != FieldWideningOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wideningOp != FieldWideningOp
::None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("wideningOp != FieldWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 8323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp != FieldWideningOp::None"
")"); do { *((volatile int*)__null) = 8323; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8324
8325 MDefinition* input;
8326 if (!f.iter().readConversion(ValType(RefType::i31()), ValType::I32, &input)) {
8327 return false;
8328 }
8329
8330 if (f.inDeadCode()) {
8331 return true;
8332 }
8333
8334 if (!f.refAsNonNull(input)) {
8335 return false;
8336 }
8337 MDefinition* output = f.i31Get(input, wideningOp);
8338 if (!output) {
8339 return false;
8340 }
8341 f.iter().setResult(output);
8342 return true;
8343}
8344
8345static bool EmitRefTest(FunctionCompiler& f, bool nullable) {
8346 MDefinition* ref;
8347 RefType sourceType;
8348 RefType destType;
8349 if (!f.iter().readRefTest(nullable, &sourceType, &destType, &ref)) {
8350 return false;
8351 }
8352
8353 if (f.inDeadCode()) {
8354 return true;
8355 }
8356
8357 MDefinition* success = f.refTest(ref, sourceType, destType);
8358 if (!success) {
8359 return false;
8360 }
8361
8362 f.iter().setResult(success);
8363 return true;
8364}
8365
8366static bool EmitRefCast(FunctionCompiler& f, bool nullable) {
8367 MDefinition* ref;
8368 RefType sourceType;
8369 RefType destType;
8370 if (!f.iter().readRefCast(nullable, &sourceType, &destType, &ref)) {
8371 return false;
8372 }
8373
8374 if (f.inDeadCode()) {
8375 return true;
8376 }
8377
8378 if (!f.refCast(ref, sourceType, destType)) {
8379 return false;
8380 }
8381
8382 f.iter().setResult(ref);
8383 return true;
8384}
8385
8386static bool EmitBrOnCast(FunctionCompiler& f, bool onSuccess) {
8387 uint32_t labelRelativeDepth;
8388 RefType sourceType;
8389 RefType destType;
8390 ResultType labelType;
8391 DefVector values;
8392 if (!f.iter().readBrOnCast(onSuccess, &labelRelativeDepth, &sourceType,
8393 &destType, &labelType, &values)) {
8394 return false;
8395 }
8396
8397 return f.brOnCastCommon(onSuccess, labelRelativeDepth, sourceType, destType,
8398 labelType, values);
8399}
8400
8401static bool EmitAnyConvertExtern(FunctionCompiler& f) {
8402 // any.convert_extern is a no-op because anyref and extern share the same
8403 // representation
8404 MDefinition* ref;
8405 if (!f.iter().readRefConversion(RefType::extern_(), RefType::any(), &ref)) {
8406 return false;
8407 }
8408
8409 f.iter().setResult(ref);
8410 return true;
8411}
8412
8413static bool EmitExternConvertAny(FunctionCompiler& f) {
8414 // extern.convert_any is a no-op because anyref and extern share the same
8415 // representation
8416 MDefinition* ref;
8417 if (!f.iter().readRefConversion(RefType::any(), RefType::extern_(), &ref)) {
8418 return false;
8419 }
8420
8421 f.iter().setResult(ref);
8422 return true;
8423}
8424
8425#endif // ENABLE_WASM_GC
8426
8427static bool EmitCallBuiltinModuleFunc(FunctionCompiler& f) {
8428 // It's almost possible to use FunctionCompiler::emitInstanceCallN here.
8429 // Unfortunately not currently possible though, since ::emitInstanceCallN
8430 // expects an array of arguments along with a size, and that's not what is
8431 // available here. It would be possible if we were prepared to copy
8432 // `builtinModuleFunc->params` into a fixed-sized (16 element?) array, add
8433 // `memoryBase`, and make the call.
8434 const BuiltinModuleFunc* builtinModuleFunc;
8435
8436 DefVector params;
8437 if (!f.iter().readCallBuiltinModuleFunc(&builtinModuleFunc, &params)) {
8438 return false;
8439 }
8440
8441 uint32_t bytecodeOffset = f.readBytecodeOffset();
8442 const SymbolicAddressSignature& callee = *builtinModuleFunc->sig();
8443
8444 CallCompileState args;
8445 if (!f.passInstance(callee.argTypes[0], &args)) {
8446 return false;
8447 }
8448
8449 if (!f.passArgs(params, builtinModuleFunc->funcType()->args(), &args)) {
8450 return false;
8451 }
8452
8453 if (builtinModuleFunc->usesMemory()) {
8454 MDefinition* memoryBase = f.memoryBase(0);
8455 if (!f.passArg(memoryBase, MIRType::Pointer, &args)) {
8456 return false;
8457 }
8458 }
8459
8460 if (!f.finishCall(&args)) {
8461 return false;
8462 }
8463
8464 bool hasResult = !builtinModuleFunc->funcType()->results().empty();
8465 MDefinition* result = nullptr;
8466 MDefinition** resultOutParam = hasResult ? &result : nullptr;
8467 if (!f.builtinInstanceMethodCall(callee, bytecodeOffset, args,
8468 resultOutParam)) {
8469 return false;
8470 }
8471
8472 if (hasResult) {
8473 f.iter().setResult(result);
8474 }
8475 return true;
8476}
8477
8478bool EmitBodyExprs(FunctionCompiler& f) {
8479 if (!f.iter().startFunction(f.funcIndex(), f.locals())) {
8480 return false;
8481 }
8482
8483#define CHECK(c) \
8484 if (!(c)) return false; \
8485 break
8486
8487 while (true) {
8488 if (!f.mirGen().ensureBallast()) {
8489 return false;
8490 }
8491
8492 OpBytes op;
8493 if (!f.iter().readOp(&op)) {
8494 return false;
8495 }
8496
8497 switch (op.b0) {
8498 case uint16_t(Op::End):
8499 if (!EmitEnd(f)) {
8500 return false;
8501 }
8502 if (f.iter().controlStackEmpty()) {
8503 return true;
8504 }
8505 break;
8506
8507 // Control opcodes
8508 case uint16_t(Op::Unreachable):
8509 CHECK(EmitUnreachable(f));
8510 case uint16_t(Op::Nop):
8511 CHECK(f.iter().readNop());
8512 case uint16_t(Op::Block):
8513 CHECK(EmitBlock(f));
8514 case uint16_t(Op::Loop):
8515 CHECK(EmitLoop(f));
8516 case uint16_t(Op::If):
8517 CHECK(EmitIf(f));
8518 case uint16_t(Op::Else):
8519 CHECK(EmitElse(f));
8520 case uint16_t(Op::Try):
8521 CHECK(EmitTry(f));
8522 case uint16_t(Op::Catch):
8523 CHECK(EmitCatch(f));
8524 case uint16_t(Op::CatchAll):
8525 CHECK(EmitCatchAll(f));
8526 case uint16_t(Op::Delegate):
8527 if (!EmitDelegate(f)) {
8528 return false;
8529 }
8530 break;
8531 case uint16_t(Op::Throw):
8532 CHECK(EmitThrow(f));
8533 case uint16_t(Op::Rethrow):
8534 CHECK(EmitRethrow(f));
8535 case uint16_t(Op::ThrowRef):
8536 if (!f.codeMeta().exnrefEnabled()) {
8537 return f.iter().unrecognizedOpcode(&op);
8538 }
8539 CHECK(EmitThrowRef(f));
8540 case uint16_t(Op::TryTable):
8541 if (!f.codeMeta().exnrefEnabled()) {
8542 return f.iter().unrecognizedOpcode(&op);
8543 }
8544 CHECK(EmitTryTable(f));
8545 case uint16_t(Op::Br):
8546 CHECK(EmitBr(f));
8547 case uint16_t(Op::BrIf):
8548 CHECK(EmitBrIf(f));
8549 case uint16_t(Op::BrTable):
8550 CHECK(EmitBrTable(f));
8551 case uint16_t(Op::Return):
8552 CHECK(EmitReturn(f));
8553
8554 // Calls
8555 case uint16_t(Op::Call):
8556 CHECK(EmitCall(f, /* asmJSFuncDef = */ false));
8557 case uint16_t(Op::CallIndirect):
8558 CHECK(EmitCallIndirect(f, /* oldStyle = */ false));
8559
8560 // Parametric operators
8561 case uint16_t(Op::Drop):
8562 CHECK(f.iter().readDrop());
8563 case uint16_t(Op::SelectNumeric):
8564 CHECK(EmitSelect(f, /*typed*/ false));
8565 case uint16_t(Op::SelectTyped):
8566 CHECK(EmitSelect(f, /*typed*/ true));
8567
8568 // Locals and globals
8569 case uint16_t(Op::LocalGet):
8570 CHECK(EmitGetLocal(f));
8571 case uint16_t(Op::LocalSet):
8572 CHECK(EmitSetLocal(f));
8573 case uint16_t(Op::LocalTee):
8574 CHECK(EmitTeeLocal(f));
8575 case uint16_t(Op::GlobalGet):
8576 CHECK(EmitGetGlobal(f));
8577 case uint16_t(Op::GlobalSet):
8578 CHECK(EmitSetGlobal(f));
8579 case uint16_t(Op::TableGet):
8580 CHECK(EmitTableGet(f));
8581 case uint16_t(Op::TableSet):
8582 CHECK(EmitTableSet(f));
8583
8584 // Memory-related operators
8585 case uint16_t(Op::I32Load):
8586 CHECK(EmitLoad(f, ValType::I32, Scalar::Int32));
8587 case uint16_t(Op::I64Load):
8588 CHECK(EmitLoad(f, ValType::I64, Scalar::Int64));
8589 case uint16_t(Op::F32Load):
8590 CHECK(EmitLoad(f, ValType::F32, Scalar::Float32));
8591 case uint16_t(Op::F64Load):
8592 CHECK(EmitLoad(f, ValType::F64, Scalar::Float64));
8593 case uint16_t(Op::I32Load8S):
8594 CHECK(EmitLoad(f, ValType::I32, Scalar::Int8));
8595 case uint16_t(Op::I32Load8U):
8596 CHECK(EmitLoad(f, ValType::I32, Scalar::Uint8));
8597 case uint16_t(Op::I32Load16S):
8598 CHECK(EmitLoad(f, ValType::I32, Scalar::Int16));
8599 case uint16_t(Op::I32Load16U):
8600 CHECK(EmitLoad(f, ValType::I32, Scalar::Uint16));
8601 case uint16_t(Op::I64Load8S):
8602 CHECK(EmitLoad(f, ValType::I64, Scalar::Int8));
8603 case uint16_t(Op::I64Load8U):
8604 CHECK(EmitLoad(f, ValType::I64, Scalar::Uint8));
8605 case uint16_t(Op::I64Load16S):
8606 CHECK(EmitLoad(f, ValType::I64, Scalar::Int16));
8607 case uint16_t(Op::I64Load16U):
8608 CHECK(EmitLoad(f, ValType::I64, Scalar::Uint16));
8609 case uint16_t(Op::I64Load32S):
8610 CHECK(EmitLoad(f, ValType::I64, Scalar::Int32));
8611 case uint16_t(Op::I64Load32U):
8612 CHECK(EmitLoad(f, ValType::I64, Scalar::Uint32));
8613 case uint16_t(Op::I32Store):
8614 CHECK(EmitStore(f, ValType::I32, Scalar::Int32));
8615 case uint16_t(Op::I64Store):
8616 CHECK(EmitStore(f, ValType::I64, Scalar::Int64));
8617 case uint16_t(Op::F32Store):
8618 CHECK(EmitStore(f, ValType::F32, Scalar::Float32));
8619 case uint16_t(Op::F64Store):
8620 CHECK(EmitStore(f, ValType::F64, Scalar::Float64));
8621 case uint16_t(Op::I32Store8):
8622 CHECK(EmitStore(f, ValType::I32, Scalar::Int8));
8623 case uint16_t(Op::I32Store16):
8624 CHECK(EmitStore(f, ValType::I32, Scalar::Int16));
8625 case uint16_t(Op::I64Store8):
8626 CHECK(EmitStore(f, ValType::I64, Scalar::Int8));
8627 case uint16_t(Op::I64Store16):
8628 CHECK(EmitStore(f, ValType::I64, Scalar::Int16));
8629 case uint16_t(Op::I64Store32):
8630 CHECK(EmitStore(f, ValType::I64, Scalar::Int32));
8631 case uint16_t(Op::MemorySize):
8632 CHECK(EmitMemorySize(f));
8633 case uint16_t(Op::MemoryGrow):
8634 CHECK(EmitMemoryGrow(f));
8635
8636 // Constants
8637 case uint16_t(Op::I32Const):
8638 CHECK(EmitI32Const(f));
8639 case uint16_t(Op::I64Const):
8640 CHECK(EmitI64Const(f));
8641 case uint16_t(Op::F32Const):
8642 CHECK(EmitF32Const(f));
8643 case uint16_t(Op::F64Const):
8644 CHECK(EmitF64Const(f));
8645
8646 // Comparison operators
8647 case uint16_t(Op::I32Eqz):
8648 CHECK(EmitConversion<MNot>(f, ValType::I32, ValType::I32));
8649 case uint16_t(Op::I32Eq):
8650 CHECK(
8651 EmitComparison(f, ValType::I32, JSOp::Eq, MCompare::Compare_Int32));
8652 case uint16_t(Op::I32Ne):
8653 CHECK(
8654 EmitComparison(f, ValType::I32, JSOp::Ne, MCompare::Compare_Int32));
8655 case uint16_t(Op::I32LtS):
8656 CHECK(
8657 EmitComparison(f, ValType::I32, JSOp::Lt, MCompare::Compare_Int32));
8658 case uint16_t(Op::I32LtU):
8659 CHECK(EmitComparison(f, ValType::I32, JSOp::Lt,
8660 MCompare::Compare_UInt32));
8661 case uint16_t(Op::I32GtS):
8662 CHECK(
8663 EmitComparison(f, ValType::I32, JSOp::Gt, MCompare::Compare_Int32));
8664 case uint16_t(Op::I32GtU):
8665 CHECK(EmitComparison(f, ValType::I32, JSOp::Gt,
8666 MCompare::Compare_UInt32));
8667 case uint16_t(Op::I32LeS):
8668 CHECK(
8669 EmitComparison(f, ValType::I32, JSOp::Le, MCompare::Compare_Int32));
8670 case uint16_t(Op::I32LeU):
8671 CHECK(EmitComparison(f, ValType::I32, JSOp::Le,
8672 MCompare::Compare_UInt32));
8673 case uint16_t(Op::I32GeS):
8674 CHECK(
8675 EmitComparison(f, ValType::I32, JSOp::Ge, MCompare::Compare_Int32));
8676 case uint16_t(Op::I32GeU):
8677 CHECK(EmitComparison(f, ValType::I32, JSOp::Ge,
8678 MCompare::Compare_UInt32));
8679 case uint16_t(Op::I64Eqz):
8680 CHECK(EmitConversion<MNot>(f, ValType::I64, ValType::I32));
8681 case uint16_t(Op::I64Eq):
8682 CHECK(
8683 EmitComparison(f, ValType::I64, JSOp::Eq, MCompare::Compare_Int64));
8684 case uint16_t(Op::I64Ne):
8685 CHECK(
8686 EmitComparison(f, ValType::I64, JSOp::Ne, MCompare::Compare_Int64));
8687 case uint16_t(Op::I64LtS):
8688 CHECK(
8689 EmitComparison(f, ValType::I64, JSOp::Lt, MCompare::Compare_Int64));
8690 case uint16_t(Op::I64LtU):
8691 CHECK(EmitComparison(f, ValType::I64, JSOp::Lt,
8692 MCompare::Compare_UInt64));
8693 case uint16_t(Op::I64GtS):
8694 CHECK(
8695 EmitComparison(f, ValType::I64, JSOp::Gt, MCompare::Compare_Int64));
8696 case uint16_t(Op::I64GtU):
8697 CHECK(EmitComparison(f, ValType::I64, JSOp::Gt,
8698 MCompare::Compare_UInt64));
8699 case uint16_t(Op::I64LeS):
8700 CHECK(
8701 EmitComparison(f, ValType::I64, JSOp::Le, MCompare::Compare_Int64));
8702 case uint16_t(Op::I64LeU):
8703 CHECK(EmitComparison(f, ValType::I64, JSOp::Le,
8704 MCompare::Compare_UInt64));
8705 case uint16_t(Op::I64GeS):
8706 CHECK(
8707 EmitComparison(f, ValType::I64, JSOp::Ge, MCompare::Compare_Int64));
8708 case uint16_t(Op::I64GeU):
8709 CHECK(EmitComparison(f, ValType::I64, JSOp::Ge,
8710 MCompare::Compare_UInt64));
8711 case uint16_t(Op::F32Eq):
8712 CHECK(EmitComparison(f, ValType::F32, JSOp::Eq,
8713 MCompare::Compare_Float32));
8714 case uint16_t(Op::F32Ne):
8715 CHECK(EmitComparison(f, ValType::F32, JSOp::Ne,
8716 MCompare::Compare_Float32));
8717 case uint16_t(Op::F32Lt):
8718 CHECK(EmitComparison(f, ValType::F32, JSOp::Lt,
8719 MCompare::Compare_Float32));
8720 case uint16_t(Op::F32Gt):
8721 CHECK(EmitComparison(f, ValType::F32, JSOp::Gt,
8722 MCompare::Compare_Float32));
8723 case uint16_t(Op::F32Le):
8724 CHECK(EmitComparison(f, ValType::F32, JSOp::Le,
8725 MCompare::Compare_Float32));
8726 case uint16_t(Op::F32Ge):
8727 CHECK(EmitComparison(f, ValType::F32, JSOp::Ge,
8728 MCompare::Compare_Float32));
8729 case uint16_t(Op::F64Eq):
8730 CHECK(EmitComparison(f, ValType::F64, JSOp::Eq,
8731 MCompare::Compare_Double));
8732 case uint16_t(Op::F64Ne):
8733 CHECK(EmitComparison(f, ValType::F64, JSOp::Ne,
8734 MCompare::Compare_Double));
8735 case uint16_t(Op::F64Lt):
8736 CHECK(EmitComparison(f, ValType::F64, JSOp::Lt,
8737 MCompare::Compare_Double));
8738 case uint16_t(Op::F64Gt):
8739 CHECK(EmitComparison(f, ValType::F64, JSOp::Gt,
8740 MCompare::Compare_Double));
8741 case uint16_t(Op::F64Le):
8742 CHECK(EmitComparison(f, ValType::F64, JSOp::Le,
8743 MCompare::Compare_Double));
8744 case uint16_t(Op::F64Ge):
8745 CHECK(EmitComparison(f, ValType::F64, JSOp::Ge,
8746 MCompare::Compare_Double));
8747
8748 // Numeric operators
8749 case uint16_t(Op::I32Clz):
8750 CHECK(EmitUnaryWithType<MClz>(f, ValType::I32, MIRType::Int32));
8751 case uint16_t(Op::I32Ctz):
8752 CHECK(EmitUnaryWithType<MCtz>(f, ValType::I32, MIRType::Int32));
8753 case uint16_t(Op::I32Popcnt):
8754 CHECK(EmitUnaryWithType<MPopcnt>(f, ValType::I32, MIRType::Int32));
8755 case uint16_t(Op::I32Add):
8756 CHECK(EmitAdd(f, ValType::I32, MIRType::Int32));
8757 case uint16_t(Op::I32Sub):
8758 CHECK(EmitSub(f, ValType::I32, MIRType::Int32));
8759 case uint16_t(Op::I32Mul):
8760 CHECK(EmitMul(f, ValType::I32, MIRType::Int32));
8761 case uint16_t(Op::I32DivS):
8762 case uint16_t(Op::I32DivU):
8763 CHECK(
8764 EmitDiv(f, ValType::I32, MIRType::Int32, Op(op.b0) == Op::I32DivU));
8765 case uint16_t(Op::I32RemS):
8766 case uint16_t(Op::I32RemU):
8767 CHECK(
8768 EmitRem(f, ValType::I32, MIRType::Int32, Op(op.b0) == Op::I32RemU));
8769 case uint16_t(Op::I32And):
8770 CHECK(EmitBitwiseAndOrXor(f, ValType::I32, MIRType::Int32,
8771 MWasmBinaryBitwise::SubOpcode::And));
8772 case uint16_t(Op::I32Or):
8773 CHECK(EmitBitwiseAndOrXor(f, ValType::I32, MIRType::Int32,
8774 MWasmBinaryBitwise::SubOpcode::Or));
8775 case uint16_t(Op::I32Xor):
8776 CHECK(EmitBitwiseAndOrXor(f, ValType::I32, MIRType::Int32,
8777 MWasmBinaryBitwise::SubOpcode::Xor));
8778 case uint16_t(Op::I32Shl):
8779 CHECK(EmitShift<MLsh>(f, ValType::I32, MIRType::Int32));
8780 case uint16_t(Op::I32ShrS):
8781 CHECK(EmitShift<MRsh>(f, ValType::I32, MIRType::Int32));
8782 case uint16_t(Op::I32ShrU):
8783 CHECK(EmitUrsh(f, ValType::I32, MIRType::Int32));
8784 case uint16_t(Op::I32Rotl):
8785 case uint16_t(Op::I32Rotr):
8786 CHECK(EmitRotate(f, ValType::I32, Op(op.b0) == Op::I32Rotl));
8787 case uint16_t(Op::I64Clz):
8788 CHECK(EmitUnaryWithType<MClz>(f, ValType::I64, MIRType::Int64));
8789 case uint16_t(Op::I64Ctz):
8790 CHECK(EmitUnaryWithType<MCtz>(f, ValType::I64, MIRType::Int64));
8791 case uint16_t(Op::I64Popcnt):
8792 CHECK(EmitUnaryWithType<MPopcnt>(f, ValType::I64, MIRType::Int64));
8793 case uint16_t(Op::I64Add):
8794 CHECK(EmitAdd(f, ValType::I64, MIRType::Int64));
8795 case uint16_t(Op::I64Sub):
8796 CHECK(EmitSub(f, ValType::I64, MIRType::Int64));
8797 case uint16_t(Op::I64Mul):
8798 CHECK(EmitMul(f, ValType::I64, MIRType::Int64));
8799 case uint16_t(Op::I64DivS):
8800 case uint16_t(Op::I64DivU):
8801 CHECK(
8802 EmitDiv(f, ValType::I64, MIRType::Int64, Op(op.b0) == Op::I64DivU));
8803 case uint16_t(Op::I64RemS):
8804 case uint16_t(Op::I64RemU):
8805 CHECK(
8806 EmitRem(f, ValType::I64, MIRType::Int64, Op(op.b0) == Op::I64RemU));
8807 case uint16_t(Op::I64And):
8808 CHECK(EmitBitwiseAndOrXor(f, ValType::I64, MIRType::Int64,
8809 MWasmBinaryBitwise::SubOpcode::And));
8810 case uint16_t(Op::I64Or):
8811 CHECK(EmitBitwiseAndOrXor(f, ValType::I64, MIRType::Int64,
8812 MWasmBinaryBitwise::SubOpcode::Or));
8813 case uint16_t(Op::I64Xor):
8814 CHECK(EmitBitwiseAndOrXor(f, ValType::I64, MIRType::Int64,
8815 MWasmBinaryBitwise::SubOpcode::Xor));
8816 case uint16_t(Op::I64Shl):
8817 CHECK(EmitShift<MLsh>(f, ValType::I64, MIRType::Int64));
8818 case uint16_t(Op::I64ShrS):
8819 CHECK(EmitShift<MRsh>(f, ValType::I64, MIRType::Int64));
8820 case uint16_t(Op::I64ShrU):
8821 CHECK(EmitUrsh(f, ValType::I64, MIRType::Int64));
8822 case uint16_t(Op::I64Rotl):
8823 case uint16_t(Op::I64Rotr):
8824 CHECK(EmitRotate(f, ValType::I64, Op(op.b0) == Op::I64Rotl));
8825 case uint16_t(Op::F32Abs):
8826 CHECK(EmitUnaryWithType<MAbs>(f, ValType::F32, MIRType::Float32));
8827 case uint16_t(Op::F32Neg):
8828 CHECK(EmitUnaryWithType<MWasmNeg>(f, ValType::F32, MIRType::Float32));
8829 case uint16_t(Op::F32Ceil):
8830 CHECK(EmitUnaryMathBuiltinCall(f, SASigCeilF));
8831 case uint16_t(Op::F32Floor):
8832 CHECK(EmitUnaryMathBuiltinCall(f, SASigFloorF));
8833 case uint16_t(Op::F32Trunc):
8834 CHECK(EmitUnaryMathBuiltinCall(f, SASigTruncF));
8835 case uint16_t(Op::F32Nearest):
8836 CHECK(EmitUnaryMathBuiltinCall(f, SASigNearbyIntF));
8837 case uint16_t(Op::F32Sqrt):
8838 CHECK(EmitUnaryWithType<MSqrt>(f, ValType::F32, MIRType::Float32));
8839 case uint16_t(Op::F32Add):
8840 CHECK(EmitAdd(f, ValType::F32, MIRType::Float32));
8841 case uint16_t(Op::F32Sub):
8842 CHECK(EmitSub(f, ValType::F32, MIRType::Float32));
8843 case uint16_t(Op::F32Mul):
8844 CHECK(EmitMul(f, ValType::F32, MIRType::Float32));
8845 case uint16_t(Op::F32Div):
8846 CHECK(EmitDiv(f, ValType::F32, MIRType::Float32,
8847 /* isUnsigned = */ false));
8848 case uint16_t(Op::F32Min):
8849 case uint16_t(Op::F32Max):
8850 CHECK(EmitMinMax(f, ValType::F32, MIRType::Float32,
8851 Op(op.b0) == Op::F32Max));
8852 case uint16_t(Op::F32CopySign):
8853 CHECK(EmitCopySign(f, ValType::F32));
8854 case uint16_t(Op::F64Abs):
8855 CHECK(EmitUnaryWithType<MAbs>(f, ValType::F64, MIRType::Double));
8856 case uint16_t(Op::F64Neg):
8857 CHECK(EmitUnaryWithType<MWasmNeg>(f, ValType::F64, MIRType::Double));
8858 case uint16_t(Op::F64Ceil):
8859 CHECK(EmitUnaryMathBuiltinCall(f, SASigCeilD));
8860 case uint16_t(Op::F64Floor):
8861 CHECK(EmitUnaryMathBuiltinCall(f, SASigFloorD));
8862 case uint16_t(Op::F64Trunc):
8863 CHECK(EmitUnaryMathBuiltinCall(f, SASigTruncD));
8864 case uint16_t(Op::F64Nearest):
8865 CHECK(EmitUnaryMathBuiltinCall(f, SASigNearbyIntD));
8866 case uint16_t(Op::F64Sqrt):
8867 CHECK(EmitUnaryWithType<MSqrt>(f, ValType::F64, MIRType::Double));
8868 case uint16_t(Op::F64Add):
8869 CHECK(EmitAdd(f, ValType::F64, MIRType::Double));
8870 case uint16_t(Op::F64Sub):
8871 CHECK(EmitSub(f, ValType::F64, MIRType::Double));
8872 case uint16_t(Op::F64Mul):
8873 CHECK(EmitMul(f, ValType::F64, MIRType::Double));
8874 case uint16_t(Op::F64Div):
8875 CHECK(EmitDiv(f, ValType::F64, MIRType::Double,
8876 /* isUnsigned = */ false));
8877 case uint16_t(Op::F64Min):
8878 case uint16_t(Op::F64Max):
8879 CHECK(EmitMinMax(f, ValType::F64, MIRType::Double,
8880 Op(op.b0) == Op::F64Max));
8881 case uint16_t(Op::F64CopySign):
8882 CHECK(EmitCopySign(f, ValType::F64));
8883
8884 // Conversions
8885 case uint16_t(Op::I32WrapI64):
8886 CHECK(EmitConversion<MWrapInt64ToInt32>(f, ValType::I64, ValType::I32));
8887 case uint16_t(Op::I32TruncF32S):
8888 case uint16_t(Op::I32TruncF32U):
8889 CHECK(EmitTruncate(f, ValType::F32, ValType::I32,
8890 Op(op.b0) == Op::I32TruncF32U, false));
8891 case uint16_t(Op::I32TruncF64S):
8892 case uint16_t(Op::I32TruncF64U):
8893 CHECK(EmitTruncate(f, ValType::F64, ValType::I32,
8894 Op(op.b0) == Op::I32TruncF64U, false));
8895 case uint16_t(Op::I64ExtendI32S):
8896 case uint16_t(Op::I64ExtendI32U):
8897 CHECK(EmitExtendI32(f, Op(op.b0) == Op::I64ExtendI32U));
8898 case uint16_t(Op::I64TruncF32S):
8899 case uint16_t(Op::I64TruncF32U):
8900 CHECK(EmitTruncate(f, ValType::F32, ValType::I64,
8901 Op(op.b0) == Op::I64TruncF32U, false));
8902 case uint16_t(Op::I64TruncF64S):
8903 case uint16_t(Op::I64TruncF64U):
8904 CHECK(EmitTruncate(f, ValType::F64, ValType::I64,
8905 Op(op.b0) == Op::I64TruncF64U, false));
8906 case uint16_t(Op::F32ConvertI32S):
8907 CHECK(EmitConversion<MToFloat32>(f, ValType::I32, ValType::F32));
8908 case uint16_t(Op::F32ConvertI32U):
8909 CHECK(EmitConversion<MWasmUnsignedToFloat32>(f, ValType::I32,
8910 ValType::F32));
8911 case uint16_t(Op::F32ConvertI64S):
8912 case uint16_t(Op::F32ConvertI64U):
8913 CHECK(EmitConvertI64ToFloatingPoint(f, ValType::F32, MIRType::Float32,
8914 Op(op.b0) == Op::F32ConvertI64U));
8915 case uint16_t(Op::F32DemoteF64):
8916 CHECK(EmitConversion<MToFloat32>(f, ValType::F64, ValType::F32));
8917 case uint16_t(Op::F64ConvertI32S):
8918 CHECK(EmitConversion<MToDouble>(f, ValType::I32, ValType::F64));
8919 case uint16_t(Op::F64ConvertI32U):
8920 CHECK(EmitConversion<MWasmUnsignedToDouble>(f, ValType::I32,
8921 ValType::F64));
8922 case uint16_t(Op::F64ConvertI64S):
8923 case uint16_t(Op::F64ConvertI64U):
8924 CHECK(EmitConvertI64ToFloatingPoint(f, ValType::F64, MIRType::Double,
8925 Op(op.b0) == Op::F64ConvertI64U));
8926 case uint16_t(Op::F64PromoteF32):
8927 CHECK(EmitConversion<MToDouble>(f, ValType::F32, ValType::F64));
8928
8929 // Reinterpretations
8930 case uint16_t(Op::I32ReinterpretF32):
8931 CHECK(EmitReinterpret(f, ValType::I32, ValType::F32, MIRType::Int32));
8932 case uint16_t(Op::I64ReinterpretF64):
8933 CHECK(EmitReinterpret(f, ValType::I64, ValType::F64, MIRType::Int64));
8934 case uint16_t(Op::F32ReinterpretI32):
8935 CHECK(EmitReinterpret(f, ValType::F32, ValType::I32, MIRType::Float32));
8936 case uint16_t(Op::F64ReinterpretI64):
8937 CHECK(EmitReinterpret(f, ValType::F64, ValType::I64, MIRType::Double));
8938
8939#ifdef ENABLE_WASM_GC1
8940 case uint16_t(Op::RefEq):
8941 if (!f.codeMeta().gcEnabled()) {
8942 return f.iter().unrecognizedOpcode(&op);
8943 }
8944 CHECK(EmitComparison(f, RefType::eq(), JSOp::Eq,
8945 MCompare::Compare_WasmAnyRef));
8946#endif
8947 case uint16_t(Op::RefFunc):
8948 CHECK(EmitRefFunc(f));
8949 case uint16_t(Op::RefNull):
8950 CHECK(EmitRefNull(f));
8951 case uint16_t(Op::RefIsNull):
8952 CHECK(EmitRefIsNull(f));
8953
8954 // Sign extensions
8955 case uint16_t(Op::I32Extend8S):
8956 CHECK(EmitSignExtend(f, 1, 4));
8957 case uint16_t(Op::I32Extend16S):
8958 CHECK(EmitSignExtend(f, 2, 4));
8959 case uint16_t(Op::I64Extend8S):
8960 CHECK(EmitSignExtend(f, 1, 8));
8961 case uint16_t(Op::I64Extend16S):
8962 CHECK(EmitSignExtend(f, 2, 8));
8963 case uint16_t(Op::I64Extend32S):
8964 CHECK(EmitSignExtend(f, 4, 8));
8965
8966#ifdef ENABLE_WASM_TAIL_CALLS1
8967 case uint16_t(Op::ReturnCall): {
8968 if (!f.codeMeta().tailCallsEnabled()) {
8969 return f.iter().unrecognizedOpcode(&op);
8970 }
8971 CHECK(EmitReturnCall(f));
8972 }
8973 case uint16_t(Op::ReturnCallIndirect): {
8974 if (!f.codeMeta().tailCallsEnabled()) {
8975 return f.iter().unrecognizedOpcode(&op);
8976 }
8977 CHECK(EmitReturnCallIndirect(f));
8978 }
8979#endif
8980
8981#ifdef ENABLE_WASM_GC1
8982 case uint16_t(Op::RefAsNonNull):
8983 if (!f.codeMeta().gcEnabled()) {
8984 return f.iter().unrecognizedOpcode(&op);
8985 }
8986 CHECK(EmitRefAsNonNull(f));
8987 case uint16_t(Op::BrOnNull): {
8988 if (!f.codeMeta().gcEnabled()) {
8989 return f.iter().unrecognizedOpcode(&op);
8990 }
8991 CHECK(EmitBrOnNull(f));
8992 }
8993 case uint16_t(Op::BrOnNonNull): {
8994 if (!f.codeMeta().gcEnabled()) {
8995 return f.iter().unrecognizedOpcode(&op);
8996 }
8997 CHECK(EmitBrOnNonNull(f));
8998 }
8999 case uint16_t(Op::CallRef): {
9000 if (!f.codeMeta().gcEnabled()) {
9001 return f.iter().unrecognizedOpcode(&op);
9002 }
9003 CHECK(EmitCallRef(f));
9004 }
9005#endif
9006
9007#if defined(ENABLE_WASM_TAIL_CALLS1) && defined(ENABLE_WASM_GC1)
9008 case uint16_t(Op::ReturnCallRef): {
9009 if (!f.codeMeta().gcEnabled() || !f.codeMeta().tailCallsEnabled()) {
9010 return f.iter().unrecognizedOpcode(&op);
9011 }
9012 CHECK(EmitReturnCallRef(f));
9013 }
9014#endif
9015
9016 // Gc operations
9017#ifdef ENABLE_WASM_GC1
9018 case uint16_t(Op::GcPrefix): {
9019 if (!f.codeMeta().gcEnabled()) {
9020 return f.iter().unrecognizedOpcode(&op);
9021 }
9022 switch (op.b1) {
9023 case uint32_t(GcOp::StructNew):
9024 CHECK(EmitStructNew(f));
9025 case uint32_t(GcOp::StructNewDefault):
9026 CHECK(EmitStructNewDefault(f));
9027 case uint32_t(GcOp::StructSet):
9028 CHECK(EmitStructSet(f));
9029 case uint32_t(GcOp::StructGet):
9030 CHECK(EmitStructGet(f, FieldWideningOp::None));
9031 case uint32_t(GcOp::StructGetS):
9032 CHECK(EmitStructGet(f, FieldWideningOp::Signed));
9033 case uint32_t(GcOp::StructGetU):
9034 CHECK(EmitStructGet(f, FieldWideningOp::Unsigned));
9035 case uint32_t(GcOp::ArrayNew):
9036 CHECK(EmitArrayNew(f));
9037 case uint32_t(GcOp::ArrayNewDefault):
9038 CHECK(EmitArrayNewDefault(f));
9039 case uint32_t(GcOp::ArrayNewFixed):
9040 CHECK(EmitArrayNewFixed(f));
9041 case uint32_t(GcOp::ArrayNewData):
9042 CHECK(EmitArrayNewData(f));
9043 case uint32_t(GcOp::ArrayNewElem):
9044 CHECK(EmitArrayNewElem(f));
9045 case uint32_t(GcOp::ArrayInitData):
9046 CHECK(EmitArrayInitData(f));
9047 case uint32_t(GcOp::ArrayInitElem):
9048 CHECK(EmitArrayInitElem(f));
9049 case uint32_t(GcOp::ArraySet):
9050 CHECK(EmitArraySet(f));
9051 case uint32_t(GcOp::ArrayGet):
9052 CHECK(EmitArrayGet(f, FieldWideningOp::None));
9053 case uint32_t(GcOp::ArrayGetS):
9054 CHECK(EmitArrayGet(f, FieldWideningOp::Signed));
9055 case uint32_t(GcOp::ArrayGetU):
9056 CHECK(EmitArrayGet(f, FieldWideningOp::Unsigned));
9057 case uint32_t(GcOp::ArrayLen):
9058 CHECK(EmitArrayLen(f));
9059 case uint32_t(GcOp::ArrayCopy):
9060 CHECK(EmitArrayCopy(f));
9061 case uint32_t(GcOp::ArrayFill):
9062 CHECK(EmitArrayFill(f));
9063 case uint32_t(GcOp::RefI31):
9064 CHECK(EmitRefI31(f));
9065 case uint32_t(GcOp::I31GetS):
9066 CHECK(EmitI31Get(f, FieldWideningOp::Signed));
9067 case uint32_t(GcOp::I31GetU):
9068 CHECK(EmitI31Get(f, FieldWideningOp::Unsigned));
9069 case uint32_t(GcOp::BrOnCast):
9070 CHECK(EmitBrOnCast(f, /*onSuccess=*/true));
9071 case uint32_t(GcOp::BrOnCastFail):
9072 CHECK(EmitBrOnCast(f, /*onSuccess=*/false));
9073 case uint32_t(GcOp::RefTest):
9074 CHECK(EmitRefTest(f, /*nullable=*/false));
9075 case uint32_t(GcOp::RefTestNull):
9076 CHECK(EmitRefTest(f, /*nullable=*/true));
9077 case uint32_t(GcOp::RefCast):
9078 CHECK(EmitRefCast(f, /*nullable=*/false));
9079 case uint32_t(GcOp::RefCastNull):
9080 CHECK(EmitRefCast(f, /*nullable=*/true));
9081 case uint16_t(GcOp::AnyConvertExtern):
9082 CHECK(EmitAnyConvertExtern(f));
9083 case uint16_t(GcOp::ExternConvertAny):
9084 CHECK(EmitExternConvertAny(f));
9085 default:
9086 return f.iter().unrecognizedOpcode(&op);
9087 } // switch (op.b1)
9088 break;
9089 }
9090#endif
9091
9092 // SIMD operations
9093#ifdef ENABLE_WASM_SIMD1
9094 case uint16_t(Op::SimdPrefix): {
9095 if (!f.codeMeta().simdAvailable()) {
9096 return f.iter().unrecognizedOpcode(&op);
9097 }
9098 switch (op.b1) {
9099 case uint32_t(SimdOp::V128Const):
9100 CHECK(EmitConstSimd128(f));
9101 case uint32_t(SimdOp::V128Load):
9102 CHECK(EmitLoad(f, ValType::V128, Scalar::Simd128));
9103 case uint32_t(SimdOp::V128Store):
9104 CHECK(EmitStore(f, ValType::V128, Scalar::Simd128));
9105 case uint32_t(SimdOp::V128And):
9106 case uint32_t(SimdOp::V128Or):
9107 case uint32_t(SimdOp::V128Xor):
9108 case uint32_t(SimdOp::I8x16AvgrU):
9109 case uint32_t(SimdOp::I16x8AvgrU):
9110 case uint32_t(SimdOp::I8x16Add):
9111 case uint32_t(SimdOp::I8x16AddSatS):
9112 case uint32_t(SimdOp::I8x16AddSatU):
9113 case uint32_t(SimdOp::I8x16MinS):
9114 case uint32_t(SimdOp::I8x16MinU):
9115 case uint32_t(SimdOp::I8x16MaxS):
9116 case uint32_t(SimdOp::I8x16MaxU):
9117 case uint32_t(SimdOp::I16x8Add):
9118 case uint32_t(SimdOp::I16x8AddSatS):
9119 case uint32_t(SimdOp::I16x8AddSatU):
9120 case uint32_t(SimdOp::I16x8Mul):
9121 case uint32_t(SimdOp::I16x8MinS):
9122 case uint32_t(SimdOp::I16x8MinU):
9123 case uint32_t(SimdOp::I16x8MaxS):
9124 case uint32_t(SimdOp::I16x8MaxU):
9125 case uint32_t(SimdOp::I32x4Add):
9126 case uint32_t(SimdOp::I32x4Mul):
9127 case uint32_t(SimdOp::I32x4MinS):
9128 case uint32_t(SimdOp::I32x4MinU):
9129 case uint32_t(SimdOp::I32x4MaxS):
9130 case uint32_t(SimdOp::I32x4MaxU):
9131 case uint32_t(SimdOp::I64x2Add):
9132 case uint32_t(SimdOp::I64x2Mul):
9133 case uint32_t(SimdOp::F32x4Add):
9134 case uint32_t(SimdOp::F32x4Mul):
9135 case uint32_t(SimdOp::F32x4Min):
9136 case uint32_t(SimdOp::F32x4Max):
9137 case uint32_t(SimdOp::F64x2Add):
9138 case uint32_t(SimdOp::F64x2Mul):
9139 case uint32_t(SimdOp::F64x2Min):
9140 case uint32_t(SimdOp::F64x2Max):
9141 case uint32_t(SimdOp::I8x16Eq):
9142 case uint32_t(SimdOp::I8x16Ne):
9143 case uint32_t(SimdOp::I16x8Eq):
9144 case uint32_t(SimdOp::I16x8Ne):
9145 case uint32_t(SimdOp::I32x4Eq):
9146 case uint32_t(SimdOp::I32x4Ne):
9147 case uint32_t(SimdOp::I64x2Eq):
9148 case uint32_t(SimdOp::I64x2Ne):
9149 case uint32_t(SimdOp::F32x4Eq):
9150 case uint32_t(SimdOp::F32x4Ne):
9151 case uint32_t(SimdOp::F64x2Eq):
9152 case uint32_t(SimdOp::F64x2Ne):
9153 case uint32_t(SimdOp::I32x4DotI16x8S):
9154 case uint32_t(SimdOp::I16x8ExtmulLowI8x16S):
9155 case uint32_t(SimdOp::I16x8ExtmulHighI8x16S):
9156 case uint32_t(SimdOp::I16x8ExtmulLowI8x16U):
9157 case uint32_t(SimdOp::I16x8ExtmulHighI8x16U):
9158 case uint32_t(SimdOp::I32x4ExtmulLowI16x8S):
9159 case uint32_t(SimdOp::I32x4ExtmulHighI16x8S):
9160 case uint32_t(SimdOp::I32x4ExtmulLowI16x8U):
9161 case uint32_t(SimdOp::I32x4ExtmulHighI16x8U):
9162 case uint32_t(SimdOp::I64x2ExtmulLowI32x4S):
9163 case uint32_t(SimdOp::I64x2ExtmulHighI32x4S):
9164 case uint32_t(SimdOp::I64x2ExtmulLowI32x4U):
9165 case uint32_t(SimdOp::I64x2ExtmulHighI32x4U):
9166 case uint32_t(SimdOp::I16x8Q15MulrSatS):
9167 CHECK(EmitBinarySimd128(f, /* commutative= */ true, SimdOp(op.b1)));
9168 case uint32_t(SimdOp::V128AndNot):
9169 case uint32_t(SimdOp::I8x16Sub):
9170 case uint32_t(SimdOp::I8x16SubSatS):
9171 case uint32_t(SimdOp::I8x16SubSatU):
9172 case uint32_t(SimdOp::I16x8Sub):
9173 case uint32_t(SimdOp::I16x8SubSatS):
9174 case uint32_t(SimdOp::I16x8SubSatU):
9175 case uint32_t(SimdOp::I32x4Sub):
9176 case uint32_t(SimdOp::I64x2Sub):
9177 case uint32_t(SimdOp::F32x4Sub):
9178 case uint32_t(SimdOp::F32x4Div):
9179 case uint32_t(SimdOp::F64x2Sub):
9180 case uint32_t(SimdOp::F64x2Div):
9181 case uint32_t(SimdOp::I8x16NarrowI16x8S):
9182 case uint32_t(SimdOp::I8x16NarrowI16x8U):
9183 case uint32_t(SimdOp::I16x8NarrowI32x4S):
9184 case uint32_t(SimdOp::I16x8NarrowI32x4U):
9185 case uint32_t(SimdOp::I8x16LtS):
9186 case uint32_t(SimdOp::I8x16LtU):
9187 case uint32_t(SimdOp::I8x16GtS):
9188 case uint32_t(SimdOp::I8x16GtU):
9189 case uint32_t(SimdOp::I8x16LeS):
9190 case uint32_t(SimdOp::I8x16LeU):
9191 case uint32_t(SimdOp::I8x16GeS):
9192 case uint32_t(SimdOp::I8x16GeU):
9193 case uint32_t(SimdOp::I16x8LtS):
9194 case uint32_t(SimdOp::I16x8LtU):
9195 case uint32_t(SimdOp::I16x8GtS):
9196 case uint32_t(SimdOp::I16x8GtU):
9197 case uint32_t(SimdOp::I16x8LeS):
9198 case uint32_t(SimdOp::I16x8LeU):
9199 case uint32_t(SimdOp::I16x8GeS):
9200 case uint32_t(SimdOp::I16x8GeU):
9201 case uint32_t(SimdOp::I32x4LtS):
9202 case uint32_t(SimdOp::I32x4LtU):
9203 case uint32_t(SimdOp::I32x4GtS):
9204 case uint32_t(SimdOp::I32x4GtU):
9205 case uint32_t(SimdOp::I32x4LeS):
9206 case uint32_t(SimdOp::I32x4LeU):
9207 case uint32_t(SimdOp::I32x4GeS):
9208 case uint32_t(SimdOp::I32x4GeU):
9209 case uint32_t(SimdOp::I64x2LtS):
9210 case uint32_t(SimdOp::I64x2GtS):
9211 case uint32_t(SimdOp::I64x2LeS):
9212 case uint32_t(SimdOp::I64x2GeS):
9213 case uint32_t(SimdOp::F32x4Lt):
9214 case uint32_t(SimdOp::F32x4Gt):
9215 case uint32_t(SimdOp::F32x4Le):
9216 case uint32_t(SimdOp::F32x4Ge):
9217 case uint32_t(SimdOp::F64x2Lt):
9218 case uint32_t(SimdOp::F64x2Gt):
9219 case uint32_t(SimdOp::F64x2Le):
9220 case uint32_t(SimdOp::F64x2Ge):
9221 case uint32_t(SimdOp::I8x16Swizzle):
9222 case uint32_t(SimdOp::F32x4PMax):
9223 case uint32_t(SimdOp::F32x4PMin):
9224 case uint32_t(SimdOp::F64x2PMax):
9225 case uint32_t(SimdOp::F64x2PMin):
9226 CHECK(
9227 EmitBinarySimd128(f, /* commutative= */ false, SimdOp(op.b1)));
9228 case uint32_t(SimdOp::I8x16Splat):
9229 case uint32_t(SimdOp::I16x8Splat):
9230 case uint32_t(SimdOp::I32x4Splat):
9231 CHECK(EmitSplatSimd128(f, ValType::I32, SimdOp(op.b1)));
9232 case uint32_t(SimdOp::I64x2Splat):
9233 CHECK(EmitSplatSimd128(f, ValType::I64, SimdOp(op.b1)));
9234 case uint32_t(SimdOp::F32x4Splat):
9235 CHECK(EmitSplatSimd128(f, ValType::F32, SimdOp(op.b1)));
9236 case uint32_t(SimdOp::F64x2Splat):
9237 CHECK(EmitSplatSimd128(f, ValType::F64, SimdOp(op.b1)));
9238 case uint32_t(SimdOp::I8x16Neg):
9239 case uint32_t(SimdOp::I16x8Neg):
9240 case uint32_t(SimdOp::I16x8ExtendLowI8x16S):
9241 case uint32_t(SimdOp::I16x8ExtendHighI8x16S):
9242 case uint32_t(SimdOp::I16x8ExtendLowI8x16U):
9243 case uint32_t(SimdOp::I16x8ExtendHighI8x16U):
9244 case uint32_t(SimdOp::I32x4Neg):
9245 case uint32_t(SimdOp::I32x4ExtendLowI16x8S):
9246 case uint32_t(SimdOp::I32x4ExtendHighI16x8S):
9247 case uint32_t(SimdOp::I32x4ExtendLowI16x8U):
9248 case uint32_t(SimdOp::I32x4ExtendHighI16x8U):
9249 case uint32_t(SimdOp::I32x4TruncSatF32x4S):
9250 case uint32_t(SimdOp::I32x4TruncSatF32x4U):
9251 case uint32_t(SimdOp::I64x2Neg):
9252 case uint32_t(SimdOp::I64x2ExtendLowI32x4S):
9253 case uint32_t(SimdOp::I64x2ExtendHighI32x4S):
9254 case uint32_t(SimdOp::I64x2ExtendLowI32x4U):
9255 case uint32_t(SimdOp::I64x2ExtendHighI32x4U):
9256 case uint32_t(SimdOp::F32x4Abs):
9257 case uint32_t(SimdOp::F32x4Neg):
9258 case uint32_t(SimdOp::F32x4Sqrt):
9259 case uint32_t(SimdOp::F32x4ConvertI32x4S):
9260 case uint32_t(SimdOp::F32x4ConvertI32x4U):
9261 case uint32_t(SimdOp::F64x2Abs):
9262 case uint32_t(SimdOp::F64x2Neg):
9263 case uint32_t(SimdOp::F64x2Sqrt):
9264 case uint32_t(SimdOp::V128Not):
9265 case uint32_t(SimdOp::I8x16Popcnt):
9266 case uint32_t(SimdOp::I8x16Abs):
9267 case uint32_t(SimdOp::I16x8Abs):
9268 case uint32_t(SimdOp::I32x4Abs):
9269 case uint32_t(SimdOp::I64x2Abs):
9270 case uint32_t(SimdOp::F32x4Ceil):
9271 case uint32_t(SimdOp::F32x4Floor):
9272 case uint32_t(SimdOp::F32x4Trunc):
9273 case uint32_t(SimdOp::F32x4Nearest):
9274 case uint32_t(SimdOp::F64x2Ceil):
9275 case uint32_t(SimdOp::F64x2Floor):
9276 case uint32_t(SimdOp::F64x2Trunc):
9277 case uint32_t(SimdOp::F64x2Nearest):
9278 case uint32_t(SimdOp::F32x4DemoteF64x2Zero):
9279 case uint32_t(SimdOp::F64x2PromoteLowF32x4):
9280 case uint32_t(SimdOp::F64x2ConvertLowI32x4S):
9281 case uint32_t(SimdOp::F64x2ConvertLowI32x4U):
9282 case uint32_t(SimdOp::I32x4TruncSatF64x2SZero):
9283 case uint32_t(SimdOp::I32x4TruncSatF64x2UZero):
9284 case uint32_t(SimdOp::I16x8ExtaddPairwiseI8x16S):
9285 case uint32_t(SimdOp::I16x8ExtaddPairwiseI8x16U):
9286 case uint32_t(SimdOp::I32x4ExtaddPairwiseI16x8S):
9287 case uint32_t(SimdOp::I32x4ExtaddPairwiseI16x8U):
9288 CHECK(EmitUnarySimd128(f, SimdOp(op.b1)));
9289 case uint32_t(SimdOp::V128AnyTrue):
9290 case uint32_t(SimdOp::I8x16AllTrue):
9291 case uint32_t(SimdOp::I16x8AllTrue):
9292 case uint32_t(SimdOp::I32x4AllTrue):
9293 case uint32_t(SimdOp::I64x2AllTrue):
9294 case uint32_t(SimdOp::I8x16Bitmask):
9295 case uint32_t(SimdOp::I16x8Bitmask):
9296 case uint32_t(SimdOp::I32x4Bitmask):
9297 case uint32_t(SimdOp::I64x2Bitmask):
9298 CHECK(EmitReduceSimd128(f, SimdOp(op.b1)));
9299 case uint32_t(SimdOp::I8x16Shl):
9300 case uint32_t(SimdOp::I8x16ShrS):
9301 case uint32_t(SimdOp::I8x16ShrU):
9302 case uint32_t(SimdOp::I16x8Shl):
9303 case uint32_t(SimdOp::I16x8ShrS):
9304 case uint32_t(SimdOp::I16x8ShrU):
9305 case uint32_t(SimdOp::I32x4Shl):
9306 case uint32_t(SimdOp::I32x4ShrS):
9307 case uint32_t(SimdOp::I32x4ShrU):
9308 case uint32_t(SimdOp::I64x2Shl):
9309 case uint32_t(SimdOp::I64x2ShrS):
9310 case uint32_t(SimdOp::I64x2ShrU):
9311 CHECK(EmitShiftSimd128(f, SimdOp(op.b1)));
9312 case uint32_t(SimdOp::I8x16ExtractLaneS):
9313 case uint32_t(SimdOp::I8x16ExtractLaneU):
9314 CHECK(EmitExtractLaneSimd128(f, ValType::I32, 16, SimdOp(op.b1)));
9315 case uint32_t(SimdOp::I16x8ExtractLaneS):
9316 case uint32_t(SimdOp::I16x8ExtractLaneU):
9317 CHECK(EmitExtractLaneSimd128(f, ValType::I32, 8, SimdOp(op.b1)));
9318 case uint32_t(SimdOp::I32x4ExtractLane):
9319 CHECK(EmitExtractLaneSimd128(f, ValType::I32, 4, SimdOp(op.b1)));
9320 case uint32_t(SimdOp::I64x2ExtractLane):
9321 CHECK(EmitExtractLaneSimd128(f, ValType::I64, 2, SimdOp(op.b1)));
9322 case uint32_t(SimdOp::F32x4ExtractLane):
9323 CHECK(EmitExtractLaneSimd128(f, ValType::F32, 4, SimdOp(op.b1)));
9324 case uint32_t(SimdOp::F64x2ExtractLane):
9325 CHECK(EmitExtractLaneSimd128(f, ValType::F64, 2, SimdOp(op.b1)));
9326 case uint32_t(SimdOp::I8x16ReplaceLane):
9327 CHECK(EmitReplaceLaneSimd128(f, ValType::I32, 16, SimdOp(op.b1)));
9328 case uint32_t(SimdOp::I16x8ReplaceLane):
9329 CHECK(EmitReplaceLaneSimd128(f, ValType::I32, 8, SimdOp(op.b1)));
9330 case uint32_t(SimdOp::I32x4ReplaceLane):
9331 CHECK(EmitReplaceLaneSimd128(f, ValType::I32, 4, SimdOp(op.b1)));
9332 case uint32_t(SimdOp::I64x2ReplaceLane):
9333 CHECK(EmitReplaceLaneSimd128(f, ValType::I64, 2, SimdOp(op.b1)));
9334 case uint32_t(SimdOp::F32x4ReplaceLane):
9335 CHECK(EmitReplaceLaneSimd128(f, ValType::F32, 4, SimdOp(op.b1)));
9336 case uint32_t(SimdOp::F64x2ReplaceLane):
9337 CHECK(EmitReplaceLaneSimd128(f, ValType::F64, 2, SimdOp(op.b1)));
9338 case uint32_t(SimdOp::V128Bitselect):
9339 CHECK(EmitTernarySimd128(f, SimdOp(op.b1)));
9340 case uint32_t(SimdOp::I8x16Shuffle):
9341 CHECK(EmitShuffleSimd128(f));
9342 case uint32_t(SimdOp::V128Load8Splat):
9343 CHECK(EmitLoadSplatSimd128(f, Scalar::Uint8, SimdOp::I8x16Splat));
9344 case uint32_t(SimdOp::V128Load16Splat):
9345 CHECK(EmitLoadSplatSimd128(f, Scalar::Uint16, SimdOp::I16x8Splat));
9346 case uint32_t(SimdOp::V128Load32Splat):
9347 CHECK(EmitLoadSplatSimd128(f, Scalar::Float32, SimdOp::I32x4Splat));
9348 case uint32_t(SimdOp::V128Load64Splat):
9349 CHECK(EmitLoadSplatSimd128(f, Scalar::Float64, SimdOp::I64x2Splat));
9350 case uint32_t(SimdOp::V128Load8x8S):
9351 case uint32_t(SimdOp::V128Load8x8U):
9352 case uint32_t(SimdOp::V128Load16x4S):
9353 case uint32_t(SimdOp::V128Load16x4U):
9354 case uint32_t(SimdOp::V128Load32x2S):
9355 case uint32_t(SimdOp::V128Load32x2U):
9356 CHECK(EmitLoadExtendSimd128(f, SimdOp(op.b1)));
9357 case uint32_t(SimdOp::V128Load32Zero):
9358 CHECK(EmitLoadZeroSimd128(f, Scalar::Float32, 4));
9359 case uint32_t(SimdOp::V128Load64Zero):
9360 CHECK(EmitLoadZeroSimd128(f, Scalar::Float64, 8));
9361 case uint32_t(SimdOp::V128Load8Lane):
9362 CHECK(EmitLoadLaneSimd128(f, 1));
9363 case uint32_t(SimdOp::V128Load16Lane):
9364 CHECK(EmitLoadLaneSimd128(f, 2));
9365 case uint32_t(SimdOp::V128Load32Lane):
9366 CHECK(EmitLoadLaneSimd128(f, 4));
9367 case uint32_t(SimdOp::V128Load64Lane):
9368 CHECK(EmitLoadLaneSimd128(f, 8));
9369 case uint32_t(SimdOp::V128Store8Lane):
9370 CHECK(EmitStoreLaneSimd128(f, 1));
9371 case uint32_t(SimdOp::V128Store16Lane):
9372 CHECK(EmitStoreLaneSimd128(f, 2));
9373 case uint32_t(SimdOp::V128Store32Lane):
9374 CHECK(EmitStoreLaneSimd128(f, 4));
9375 case uint32_t(SimdOp::V128Store64Lane):
9376 CHECK(EmitStoreLaneSimd128(f, 8));
9377# ifdef ENABLE_WASM_RELAXED_SIMD1
9378 case uint32_t(SimdOp::F32x4RelaxedMadd):
9379 case uint32_t(SimdOp::F32x4RelaxedNmadd):
9380 case uint32_t(SimdOp::F64x2RelaxedMadd):
9381 case uint32_t(SimdOp::F64x2RelaxedNmadd):
9382 case uint32_t(SimdOp::I8x16RelaxedLaneSelect):
9383 case uint32_t(SimdOp::I16x8RelaxedLaneSelect):
9384 case uint32_t(SimdOp::I32x4RelaxedLaneSelect):
9385 case uint32_t(SimdOp::I64x2RelaxedLaneSelect):
9386 case uint32_t(SimdOp::I32x4DotI8x16I7x16AddS): {
9387 if (!f.codeMeta().v128RelaxedEnabled()) {
9388 return f.iter().unrecognizedOpcode(&op);
9389 }
9390 CHECK(EmitTernarySimd128(f, SimdOp(op.b1)));
9391 }
9392 case uint32_t(SimdOp::F32x4RelaxedMin):
9393 case uint32_t(SimdOp::F32x4RelaxedMax):
9394 case uint32_t(SimdOp::F64x2RelaxedMin):
9395 case uint32_t(SimdOp::F64x2RelaxedMax):
9396 case uint32_t(SimdOp::I16x8RelaxedQ15MulrS): {
9397 if (!f.codeMeta().v128RelaxedEnabled()) {
9398 return f.iter().unrecognizedOpcode(&op);
9399 }
9400 CHECK(EmitBinarySimd128(f, /* commutative= */ true, SimdOp(op.b1)));
9401 }
9402 case uint32_t(SimdOp::I32x4RelaxedTruncF32x4S):
9403 case uint32_t(SimdOp::I32x4RelaxedTruncF32x4U):
9404 case uint32_t(SimdOp::I32x4RelaxedTruncF64x2SZero):
9405 case uint32_t(SimdOp::I32x4RelaxedTruncF64x2UZero): {
9406 if (!f.codeMeta().v128RelaxedEnabled()) {
9407 return f.iter().unrecognizedOpcode(&op);
9408 }
9409 CHECK(EmitUnarySimd128(f, SimdOp(op.b1)));
9410 }
9411 case uint32_t(SimdOp::I8x16RelaxedSwizzle):
9412 case uint32_t(SimdOp::I16x8DotI8x16I7x16S): {
9413 if (!f.codeMeta().v128RelaxedEnabled()) {
9414 return f.iter().unrecognizedOpcode(&op);
9415 }
9416 CHECK(
9417 EmitBinarySimd128(f, /* commutative= */ false, SimdOp(op.b1)));
9418 }
9419# endif
9420
9421 default:
9422 return f.iter().unrecognizedOpcode(&op);
9423 } // switch (op.b1)
9424 break;
9425 }
9426#endif
9427
9428 // Miscellaneous operations
9429 case uint16_t(Op::MiscPrefix): {
9430 switch (op.b1) {
9431 case uint32_t(MiscOp::I32TruncSatF32S):
9432 case uint32_t(MiscOp::I32TruncSatF32U):
9433 CHECK(EmitTruncate(f, ValType::F32, ValType::I32,
9434 MiscOp(op.b1) == MiscOp::I32TruncSatF32U, true));
9435 case uint32_t(MiscOp::I32TruncSatF64S):
9436 case uint32_t(MiscOp::I32TruncSatF64U):
9437 CHECK(EmitTruncate(f, ValType::F64, ValType::I32,
9438 MiscOp(op.b1) == MiscOp::I32TruncSatF64U, true));
9439 case uint32_t(MiscOp::I64TruncSatF32S):
9440 case uint32_t(MiscOp::I64TruncSatF32U):
9441 CHECK(EmitTruncate(f, ValType::F32, ValType::I64,
9442 MiscOp(op.b1) == MiscOp::I64TruncSatF32U, true));
9443 case uint32_t(MiscOp::I64TruncSatF64S):
9444 case uint32_t(MiscOp::I64TruncSatF64U):
9445 CHECK(EmitTruncate(f, ValType::F64, ValType::I64,
9446 MiscOp(op.b1) == MiscOp::I64TruncSatF64U, true));
9447 case uint32_t(MiscOp::MemoryCopy):
9448 CHECK(EmitMemCopy(f));
9449 case uint32_t(MiscOp::DataDrop):
9450 CHECK(EmitDataOrElemDrop(f, /*isData=*/true));
9451 case uint32_t(MiscOp::MemoryFill):
9452 CHECK(EmitMemFill(f));
9453 case uint32_t(MiscOp::MemoryInit):
9454 CHECK(EmitMemInit(f));
9455 case uint32_t(MiscOp::TableCopy):
9456 CHECK(EmitTableCopy(f));
9457 case uint32_t(MiscOp::ElemDrop):
9458 CHECK(EmitDataOrElemDrop(f, /*isData=*/false));
9459 case uint32_t(MiscOp::TableInit):
9460 CHECK(EmitTableInit(f));
9461 case uint32_t(MiscOp::TableFill):
9462 CHECK(EmitTableFill(f));
9463#if ENABLE_WASM_MEMORY_CONTROL1
9464 case uint32_t(MiscOp::MemoryDiscard): {
9465 if (!f.codeMeta().memoryControlEnabled()) {
9466 return f.iter().unrecognizedOpcode(&op);
9467 }
9468 CHECK(EmitMemDiscard(f));
9469 }
9470#endif
9471 case uint32_t(MiscOp::TableGrow):
9472 CHECK(EmitTableGrow(f));
9473 case uint32_t(MiscOp::TableSize):
9474 CHECK(EmitTableSize(f));
9475 default:
9476 return f.iter().unrecognizedOpcode(&op);
9477 }
9478 break;
9479 }
9480
9481 // Thread operations
9482 case uint16_t(Op::ThreadPrefix): {
9483 // Though thread ops can be used on nonshared memories, we make them
9484 // unavailable if shared memory has been disabled in the prefs, for
9485 // maximum predictability and safety and consistency with JS.
9486 if (f.codeMeta().sharedMemoryEnabled() == Shareable::False) {
9487 return f.iter().unrecognizedOpcode(&op);
9488 }
9489 switch (op.b1) {
9490 case uint32_t(ThreadOp::Wake):
9491 CHECK(EmitWake(f));
9492
9493 case uint32_t(ThreadOp::I32Wait):
9494 CHECK(EmitWait(f, ValType::I32, 4));
9495 case uint32_t(ThreadOp::I64Wait):
9496 CHECK(EmitWait(f, ValType::I64, 8));
9497 case uint32_t(ThreadOp::Fence):
9498 CHECK(EmitFence(f));
9499
9500 case uint32_t(ThreadOp::I32AtomicLoad):
9501 CHECK(EmitAtomicLoad(f, ValType::I32, Scalar::Int32));
9502 case uint32_t(ThreadOp::I64AtomicLoad):
9503 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Int64));
9504 case uint32_t(ThreadOp::I32AtomicLoad8U):
9505 CHECK(EmitAtomicLoad(f, ValType::I32, Scalar::Uint8));
9506 case uint32_t(ThreadOp::I32AtomicLoad16U):
9507 CHECK(EmitAtomicLoad(f, ValType::I32, Scalar::Uint16));
9508 case uint32_t(ThreadOp::I64AtomicLoad8U):
9509 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Uint8));
9510 case uint32_t(ThreadOp::I64AtomicLoad16U):
9511 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Uint16));
9512 case uint32_t(ThreadOp::I64AtomicLoad32U):
9513 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Uint32));
9514
9515 case uint32_t(ThreadOp::I32AtomicStore):
9516 CHECK(EmitAtomicStore(f, ValType::I32, Scalar::Int32));
9517 case uint32_t(ThreadOp::I64AtomicStore):
9518 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Int64));
9519 case uint32_t(ThreadOp::I32AtomicStore8U):
9520 CHECK(EmitAtomicStore(f, ValType::I32, Scalar::Uint8));
9521 case uint32_t(ThreadOp::I32AtomicStore16U):
9522 CHECK(EmitAtomicStore(f, ValType::I32, Scalar::Uint16));
9523 case uint32_t(ThreadOp::I64AtomicStore8U):
9524 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint8));
9525 case uint32_t(ThreadOp::I64AtomicStore16U):
9526 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint16));
9527 case uint32_t(ThreadOp::I64AtomicStore32U):
9528 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint32));
9529
9530 case uint32_t(ThreadOp::I32AtomicAdd):
9531 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Add));
9532 case uint32_t(ThreadOp::I64AtomicAdd):
9533 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Add));
9534 case uint32_t(ThreadOp::I32AtomicAdd8U):
9535 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Add));
9536 case uint32_t(ThreadOp::I32AtomicAdd16U):
9537 CHECK(
9538 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Add));
9539 case uint32_t(ThreadOp::I64AtomicAdd8U):
9540 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Add));
9541 case uint32_t(ThreadOp::I64AtomicAdd16U):
9542 CHECK(
9543 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Add));
9544 case uint32_t(ThreadOp::I64AtomicAdd32U):
9545 CHECK(
9546 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Add));
9547
9548 case uint32_t(ThreadOp::I32AtomicSub):
9549 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Sub));
9550 case uint32_t(ThreadOp::I64AtomicSub):
9551 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Sub));
9552 case uint32_t(ThreadOp::I32AtomicSub8U):
9553 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Sub));
9554 case uint32_t(ThreadOp::I32AtomicSub16U):
9555 CHECK(
9556 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Sub));
9557 case uint32_t(ThreadOp::I64AtomicSub8U):
9558 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Sub));
9559 case uint32_t(ThreadOp::I64AtomicSub16U):
9560 CHECK(
9561 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Sub));
9562 case uint32_t(ThreadOp::I64AtomicSub32U):
9563 CHECK(
9564 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Sub));
9565
9566 case uint32_t(ThreadOp::I32AtomicAnd):
9567 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::And));
9568 case uint32_t(ThreadOp::I64AtomicAnd):
9569 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::And));
9570 case uint32_t(ThreadOp::I32AtomicAnd8U):
9571 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::And));
9572 case uint32_t(ThreadOp::I32AtomicAnd16U):
9573 CHECK(
9574 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::And));
9575 case uint32_t(ThreadOp::I64AtomicAnd8U):
9576 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::And));
9577 case uint32_t(ThreadOp::I64AtomicAnd16U):
9578 CHECK(
9579 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::And));
9580 case uint32_t(ThreadOp::I64AtomicAnd32U):
9581 CHECK(
9582 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::And));
9583
9584 case uint32_t(ThreadOp::I32AtomicOr):
9585 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Or));
9586 case uint32_t(ThreadOp::I64AtomicOr):
9587 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Or));
9588 case uint32_t(ThreadOp::I32AtomicOr8U):
9589 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Or));
9590 case uint32_t(ThreadOp::I32AtomicOr16U):
9591 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Or));
9592 case uint32_t(ThreadOp::I64AtomicOr8U):
9593 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Or));
9594 case uint32_t(ThreadOp::I64AtomicOr16U):
9595 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Or));
9596 case uint32_t(ThreadOp::I64AtomicOr32U):
9597 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Or));
9598
9599 case uint32_t(ThreadOp::I32AtomicXor):
9600 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Xor));
9601 case uint32_t(ThreadOp::I64AtomicXor):
9602 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Xor));
9603 case uint32_t(ThreadOp::I32AtomicXor8U):
9604 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Xor));
9605 case uint32_t(ThreadOp::I32AtomicXor16U):
9606 CHECK(
9607 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Xor));
9608 case uint32_t(ThreadOp::I64AtomicXor8U):
9609 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Xor));
9610 case uint32_t(ThreadOp::I64AtomicXor16U):
9611 CHECK(
9612 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Xor));
9613 case uint32_t(ThreadOp::I64AtomicXor32U):
9614 CHECK(
9615 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Xor));
9616
9617 case uint32_t(ThreadOp::I32AtomicXchg):
9618 CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Int32));
9619 case uint32_t(ThreadOp::I64AtomicXchg):
9620 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Int64));
9621 case uint32_t(ThreadOp::I32AtomicXchg8U):
9622 CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Uint8));
9623 case uint32_t(ThreadOp::I32AtomicXchg16U):
9624 CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Uint16));
9625 case uint32_t(ThreadOp::I64AtomicXchg8U):
9626 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Uint8));
9627 case uint32_t(ThreadOp::I64AtomicXchg16U):
9628 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Uint16));
9629 case uint32_t(ThreadOp::I64AtomicXchg32U):
9630 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Uint32));
9631
9632 case uint32_t(ThreadOp::I32AtomicCmpXchg):
9633 CHECK(EmitAtomicCmpXchg(f, ValType::I32, Scalar::Int32));
9634 case uint32_t(ThreadOp::I64AtomicCmpXchg):
9635 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Int64));
9636 case uint32_t(ThreadOp::I32AtomicCmpXchg8U):
9637 CHECK(EmitAtomicCmpXchg(f, ValType::I32, Scalar::Uint8));
9638 case uint32_t(ThreadOp::I32AtomicCmpXchg16U):
9639 CHECK(EmitAtomicCmpXchg(f, ValType::I32, Scalar::Uint16));
9640 case uint32_t(ThreadOp::I64AtomicCmpXchg8U):
9641 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Uint8));
9642 case uint32_t(ThreadOp::I64AtomicCmpXchg16U):
9643 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Uint16));
9644 case uint32_t(ThreadOp::I64AtomicCmpXchg32U):
9645 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Uint32));
9646
9647 default:
9648 return f.iter().unrecognizedOpcode(&op);
9649 }
9650 break;
9651 }
9652
9653 // asm.js-specific operators
9654 case uint16_t(Op::MozPrefix): {
9655 if (op.b1 == uint32_t(MozOp::CallBuiltinModuleFunc)) {
9656 if (!f.codeMeta().isBuiltinModule()) {
9657 return f.iter().unrecognizedOpcode(&op);
9658 }
9659 CHECK(EmitCallBuiltinModuleFunc(f));
9660 }
9661#ifdef ENABLE_WASM_JSPI1
9662 if (op.b1 == uint32_t(MozOp::StackSwitch)) {
9663 if (!f.codeMeta().isBuiltinModule() ||
9664 !f.codeMeta().jsPromiseIntegrationEnabled()) {
9665 return f.iter().unrecognizedOpcode(&op);
9666 }
9667 CHECK(EmitStackSwitch(f));
9668 }
9669#endif
9670
9671 if (!f.codeMeta().isAsmJS()) {
9672 return f.iter().unrecognizedOpcode(&op);
9673 }
9674 switch (op.b1) {
9675 case uint32_t(MozOp::TeeGlobal):
9676 CHECK(EmitTeeGlobal(f));
9677 case uint32_t(MozOp::I32Min):
9678 case uint32_t(MozOp::I32Max):
9679 CHECK(EmitMinMax(f, ValType::I32, MIRType::Int32,
9680 MozOp(op.b1) == MozOp::I32Max));
9681 case uint32_t(MozOp::I32Neg):
9682 CHECK(EmitUnaryWithType<MWasmNeg>(f, ValType::I32, MIRType::Int32));
9683 case uint32_t(MozOp::I32BitNot):
9684 CHECK(EmitBitNot(f, ValType::I32));
9685 case uint32_t(MozOp::I32Abs):
9686 CHECK(EmitUnaryWithType<MAbs>(f, ValType::I32, MIRType::Int32));
9687 case uint32_t(MozOp::F32TeeStoreF64):
9688 CHECK(EmitTeeStoreWithCoercion(f, ValType::F32, Scalar::Float64));
9689 case uint32_t(MozOp::F64TeeStoreF32):
9690 CHECK(EmitTeeStoreWithCoercion(f, ValType::F64, Scalar::Float32));
9691 case uint32_t(MozOp::I32TeeStore8):
9692 CHECK(EmitTeeStore(f, ValType::I32, Scalar::Int8));
9693 case uint32_t(MozOp::I32TeeStore16):
9694 CHECK(EmitTeeStore(f, ValType::I32, Scalar::Int16));
9695 case uint32_t(MozOp::I64TeeStore8):
9696 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int8));
9697 case uint32_t(MozOp::I64TeeStore16):
9698 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int16));
9699 case uint32_t(MozOp::I64TeeStore32):
9700 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int32));
9701 case uint32_t(MozOp::I32TeeStore):
9702 CHECK(EmitTeeStore(f, ValType::I32, Scalar::Int32));
9703 case uint32_t(MozOp::I64TeeStore):
9704 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int64));
9705 case uint32_t(MozOp::F32TeeStore):
9706 CHECK(EmitTeeStore(f, ValType::F32, Scalar::Float32));
9707 case uint32_t(MozOp::F64TeeStore):
9708 CHECK(EmitTeeStore(f, ValType::F64, Scalar::Float64));
9709 case uint32_t(MozOp::F64Mod):
9710 CHECK(EmitRem(f, ValType::F64, MIRType::Double,
9711 /* isUnsigned = */ false));
9712 case uint32_t(MozOp::F64SinNative):
9713 CHECK(EmitUnaryMathBuiltinCall(f, SASigSinNativeD));
9714 case uint32_t(MozOp::F64SinFdlibm):
9715 CHECK(EmitUnaryMathBuiltinCall(f, SASigSinFdlibmD));
9716 case uint32_t(MozOp::F64CosNative):
9717 CHECK(EmitUnaryMathBuiltinCall(f, SASigCosNativeD));
9718 case uint32_t(MozOp::F64CosFdlibm):
9719 CHECK(EmitUnaryMathBuiltinCall(f, SASigCosFdlibmD));
9720 case uint32_t(MozOp::F64TanNative):
9721 CHECK(EmitUnaryMathBuiltinCall(f, SASigTanNativeD));
9722 case uint32_t(MozOp::F64TanFdlibm):
9723 CHECK(EmitUnaryMathBuiltinCall(f, SASigTanFdlibmD));
9724 case uint32_t(MozOp::F64Asin):
9725 CHECK(EmitUnaryMathBuiltinCall(f, SASigASinD));
9726 case uint32_t(MozOp::F64Acos):
9727 CHECK(EmitUnaryMathBuiltinCall(f, SASigACosD));
9728 case uint32_t(MozOp::F64Atan):
9729 CHECK(EmitUnaryMathBuiltinCall(f, SASigATanD));
9730 case uint32_t(MozOp::F64Exp):
9731 CHECK(EmitUnaryMathBuiltinCall(f, SASigExpD));
9732 case uint32_t(MozOp::F64Log):
9733 CHECK(EmitUnaryMathBuiltinCall(f, SASigLogD));
9734 case uint32_t(MozOp::F64Pow):
9735 CHECK(EmitBinaryMathBuiltinCall(f, SASigPowD));
9736 case uint32_t(MozOp::F64Atan2):
9737 CHECK(EmitBinaryMathBuiltinCall(f, SASigATan2D));
9738 case uint32_t(MozOp::OldCallDirect):
9739 CHECK(EmitCall(f, /* asmJSFuncDef = */ true));
9740 case uint32_t(MozOp::OldCallIndirect):
9741 CHECK(EmitCallIndirect(f, /* oldStyle = */ true));
9742
9743 default:
9744 return f.iter().unrecognizedOpcode(&op);
9745 }
9746 break;
9747 }
9748
9749 default:
9750 return f.iter().unrecognizedOpcode(&op);
9751 }
9752 }
9753
9754 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 9754); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { *((volatile int*)__null) = 9754; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
9755
9756#undef CHECK
9757}
9758
9759static bool IonBuildMIR(Decoder& d, const CompilerEnvironment& compilerEnv,
9760 const CodeMetadata& codeMeta,
9761 const FuncCompileInput& func,
9762 const ValTypeVector& locals, MIRGenerator& mir,
9763 TryNoteVector& tryNotes,
9764 UniqueCompileInfoVector& compileInfos,
9765 FeatureUsage* observedFeatures, UniqueChars* error) {
9766 // Initialize MIR global information used for optimization
9767 if (codeMeta.numMemories() > 0) {
9768 if (codeMeta.memories[0].indexType() == IndexType::I32) {
9769 mir.initMinWasmMemory0Length(codeMeta.memories[0].initialLength32());
9770 } else {
9771 mir.initMinWasmMemory0Length(codeMeta.memories[0].initialLength64());
9772 }
9773 }
9774
9775 // Build MIR graph
9776 FunctionCompiler f(compilerEnv, codeMeta, d, func, locals, mir,
9777 mir.outerInfo(), tryNotes, compileInfos);
9778 if (!f.initTopLevel()) {
9779 return false;
9780 }
9781
9782 if (!f.startBlock()) {
9783 return false;
9784 }
9785
9786 if (!EmitBodyExprs(f)) {
9787 return false;
9788 }
9789
9790 f.finish();
9791
9792 *observedFeatures = f.featureUsage();
9793
9794 return true;
9795}
9796
9797bool wasm::IonCompileFunctions(const CodeMetadata& codeMeta,
9798 const CompilerEnvironment& compilerEnv,
9799 LifoAlloc& lifo,
9800 const FuncCompileInputVector& inputs,
9801 CompiledCode* code, UniqueChars* error) {
9802 MOZ_ASSERT(compilerEnv.tier() == Tier::Optimized)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compilerEnv.tier() == Tier::Optimized)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(compilerEnv.tier() == Tier::Optimized))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("compilerEnv.tier() == Tier::Optimized"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 9802); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compilerEnv.tier() == Tier::Optimized"
")"); do { *((volatile int*)__null) = 9802; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9803 MOZ_ASSERT(compilerEnv.debug() == DebugEnabled::False)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compilerEnv.debug() == DebugEnabled::False)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(compilerEnv.debug() == DebugEnabled::False))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("compilerEnv.debug() == DebugEnabled::False"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 9803); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compilerEnv.debug() == DebugEnabled::False"
")"); do { *((volatile int*)__null) = 9803; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9804
9805 TempAllocator alloc(&lifo);
9806 JitContext jitContext;
9807 MOZ_ASSERT(IsCompilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsCompilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsCompilingWasm()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("IsCompilingWasm()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 9807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsCompilingWasm()"
")"); do { *((volatile int*)__null) = 9807; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9808 WasmMacroAssembler masm(alloc);
9809#if defined(JS_CODEGEN_ARM64)
9810 masm.SetStackPointer64(PseudoStackPointer64);
9811#endif
9812
9813 // Swap in already-allocated empty vectors to avoid malloc/free.
9814 MOZ_ASSERT(code->empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(code->empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(code->empty()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("code->empty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 9814); AnnotateMozCrashReason("MOZ_ASSERT" "(" "code->empty()"
")"); do { *((volatile int*)__null) = 9814; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9815 if (!code->swap(masm)) {
9816 return false;
9817 }
9818
9819 // Create a description of the stack layout created by GenerateTrapExit().
9820 RegisterOffsets trapExitLayout;
9821 size_t trapExitLayoutNumWords;
9822 GenerateTrapExitRegisterOffsets(&trapExitLayout, &trapExitLayoutNumWords);
9823
9824 for (const FuncCompileInput& func : inputs) {
9825 JitSpewCont(JitSpew_Codegen, "\n");
9826 JitSpew(JitSpew_Codegen,
9827 "# ================================"
9828 "==================================");
9829 JitSpew(JitSpew_Codegen, "# ==");
9830 JitSpew(JitSpew_Codegen,
9831 "# wasm::IonCompileFunctions: starting on function index %d",
9832 (int)func.index);
9833
9834 Decoder d(func.begin, func.end, func.lineOrBytecode, error);
9835
9836 // Build the local types vector.
9837 ValTypeVector locals;
9838 if (!DecodeLocalEntriesWithParams(d, codeMeta, func.index, &locals)) {
9839 return false;
9840 }
9841
9842 // Set up for Ion compilation.
9843 const JitCompileOptions options;
9844 MIRGraph graph(&alloc);
9845 CompileInfo compileInfo(locals.length());
9846 // Only activate branch hinting if the option is enabled and some hints were
9847 // parsed.
9848 if (codeMeta.branchHintingEnabled() && !codeMeta.branchHints.isEmpty()) {
9849 compileInfo.setBranchHinting(true);
9850 }
9851
9852 MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo,
9853 IonOptimizations.get(OptimizationLevel::Wasm));
9854 UniqueCompileInfoVector compileInfos;
9855
9856 // Build MIR graph
9857 FeatureUsage observedFeatures;
9858 if (!IonBuildMIR(d, compilerEnv, codeMeta, func, locals, mir,
9859 masm.tryNotes(), compileInfos, &observedFeatures, error)) {
9860 return false;
9861 }
9862
9863 // Record observed feature usage
9864 code->featureUsage |= observedFeatures;
9865
9866 // Compile MIR graph
9867 {
9868 jit::SpewBeginWasmFunction(&mir, func.index);
9869 jit::AutoSpewEndFunction spewEndFunction(&mir);
9870
9871 if (!OptimizeMIR(&mir)) {
9872 return false;
9873 }
9874
9875 LIRGraph* lir = GenerateLIR(&mir);
9876 if (!lir) {
9877 return false;
9878 }
9879
9880 size_t unwindInfoBefore = masm.codeRangeUnwindInfos().length();
9881
9882 CodeGenerator codegen(&mir, lir, &masm);
9883
9884 BytecodeOffset prologueTrapOffset(func.lineOrBytecode);
9885 FuncOffsets offsets;
9886 ArgTypeVector args(codeMeta.getFuncType(func.index));
9887 if (!codegen.generateWasm(CallIndirectId::forFunc(codeMeta, func.index),
9888 prologueTrapOffset, args, trapExitLayout,
9889 trapExitLayoutNumWords, &offsets,
9890 &code->stackMaps, &d)) {
9891 return false;
9892 }
9893
9894 bool hasUnwindInfo =
9895 unwindInfoBefore != masm.codeRangeUnwindInfos().length();
9896
9897 // Record this function's code range
9898 if (!code->codeRanges.emplaceBack(func.index, offsets, hasUnwindInfo)) {
9899 return false;
9900 }
9901 }
9902
9903 // Record this function's specific feature usage
9904 if (!code->funcs.emplaceBack(func.index, observedFeatures)) {
9905 return false;
9906 }
9907
9908 JitSpew(JitSpew_Codegen,
9909 "# wasm::IonCompileFunctions: completed function index %d",
9910 (int)func.index);
9911 JitSpew(JitSpew_Codegen, "# ==");
9912 JitSpew(JitSpew_Codegen,
9913 "# ================================"
9914 "==================================");
9915 JitSpewCont(JitSpew_Codegen, "\n");
9916 }
9917
9918 masm.finish();
9919 if (masm.oom()) {
9920 return false;
9921 }
9922
9923 return code->swap(masm);
9924}
9925
9926bool wasm::IonDumpFunction(const CompilerEnvironment& compilerEnv,
9927 const CodeMetadata& codeMeta,
9928 const FuncCompileInput& func,
9929 IonDumpContents contents, GenericPrinter& out,
9930 UniqueChars* error) {
9931 LifoAlloc lifo(TempAllocator::PreferredLifoChunkSize);
9932 TempAllocator alloc(&lifo);
9933 JitContext jitContext;
9934 Decoder d(func.begin, func.end, func.lineOrBytecode, error);
9935
9936 // Decode the locals.
9937 ValTypeVector locals;
9938 if (!DecodeLocalEntriesWithParams(d, codeMeta, func.index, &locals)) {
9939 return false;
9940 }
9941
9942 // Set up for Ion compilation.
9943 const JitCompileOptions options;
9944 MIRGraph graph(&alloc);
9945 CompileInfo compileInfo(locals.length());
9946 MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo,
9947 IonOptimizations.get(OptimizationLevel::Wasm));
9948
9949 // Build MIR graph
9950 TryNoteVector tryNotes;
9951 UniqueCompileInfoVector compileInfos;
9952 FeatureUsage observedFeatures;
9953 if (!IonBuildMIR(d, compilerEnv, codeMeta, func, locals, mir, tryNotes,
9954 compileInfos, &observedFeatures, error)) {
9955 return false;
9956 }
9957
9958 if (contents == IonDumpContents::UnoptimizedMIR) {
9959 graph.dump(out);
9960 return true;
9961 }
9962
9963 // Optimize the MIR graph
9964 if (!OptimizeMIR(&mir)) {
9965 return false;
9966 }
9967
9968 if (contents == IonDumpContents::OptimizedMIR) {
9969 graph.dump(out);
9970 return true;
9971 }
9972
9973#ifdef JS_JITSPEW1
9974 // Generate the LIR graph
9975 LIRGraph* lir = GenerateLIR(&mir);
9976 if (!lir) {
9977 return false;
9978 }
9979
9980 MOZ_ASSERT(contents == IonDumpContents::LIR)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(contents == IonDumpContents::LIR)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(contents == IonDumpContents::
LIR))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("contents == IonDumpContents::LIR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 9980); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contents == IonDumpContents::LIR"
")"); do { *((volatile int*)__null) = 9980; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9981 lir->dump(out);
9982#else
9983 out.printf("cannot dump LIR without --enable-jitspew");
9984#endif
9985 return true;
9986}
9987
9988bool js::wasm::IonPlatformSupport() {
9989#if defined(JS_CODEGEN_X641) || defined(JS_CODEGEN_X86) || \
9990 defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \
9991 defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONG64) || \
9992 defined(JS_CODEGEN_RISCV64)
9993 return true;
9994#else
9995 return false;
9996#endif
9997}