Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp
Warning:line 3993, 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-19/lib/clang/19 -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-19/lib/clang/19/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 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -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-09-22-115206-3586786-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::Nothing;
53
54namespace {
55
56using UniqueCompileInfo = UniquePtr<CompileInfo>;
57using UniqueCompileInfoVector = Vector<UniqueCompileInfo, 1, SystemAllocPolicy>;
58
59using BlockVector = Vector<MBasicBlock*, 8, SystemAllocPolicy>;
60using DefVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
61
62// To compile try-catch blocks, we extend the IonCompilePolicy's ControlItem
63// from being just an MBasicBlock* to a Control structure collecting additional
64// information.
65using ControlInstructionVector =
66 Vector<MControlInstruction*, 8, SystemAllocPolicy>;
67
68struct TryControl {
69 // Branches to bind to the try's landing pad.
70 ControlInstructionVector landingPadPatches;
71 // For `try_table`, the list of tagged catches and labels to branch to.
72 TryTableCatchVector catches;
73 // Whether this try is in the body and should catch any thrown exception.
74 bool inBody;
75
76 TryControl() : inBody(false) {}
77
78 // Reset the try control for when it is cached in FunctionCompiler.
79 void reset() {
80 landingPadPatches.clearAndFree();
81 catches.clearAndFree();
82 inBody = false;
83 }
84};
85using UniqueTryControl = UniquePtr<TryControl>;
86using VectorUniqueTryControl = Vector<UniqueTryControl, 2, SystemAllocPolicy>;
87
88struct Control {
89 MBasicBlock* block;
90 UniqueTryControl tryControl;
91
92 Control() : block(nullptr), tryControl(nullptr) {}
93 Control(Control&&) = default;
94 Control(const Control&) = delete;
95};
96
97// [SMDOC] WebAssembly Exception Handling in Ion
98// =======================================================
99//
100// ## Throwing instructions
101//
102// Wasm exceptions can be thrown by either a throw instruction (local throw),
103// or by a wasm call.
104//
105// ## The "catching try control"
106//
107// We know we are in try-code if there is a surrounding ControlItem with
108// LabelKind::Try. The innermost such control is called the
109// "catching try control".
110//
111// ## Throws without a catching try control
112//
113// Such throws are implemented with an instance call that triggers the exception
114// unwinding runtime. The exception unwinding runtime will not return to the
115// function.
116//
117// ## "landing pad" and "pre-pad" blocks
118//
119// When an exception is thrown, the unwinder will search for the nearest
120// enclosing try block and redirect control flow to it. The code that executes
121// before any catch blocks is called the 'landing pad'. The 'landing pad' is
122// responsible to:
123// 1. Consume the pending exception state from
124// Instance::pendingException(Tag)
125// 2. Branch to the correct catch block, or else rethrow
126//
127// There is one landing pad for each try block. The immediate predecessors of
128// the landing pad are called 'pre-pad' blocks. There is one pre-pad block per
129// throwing instruction.
130//
131// ## Creating pre-pad blocks
132//
133// There are two possible sorts of pre-pad blocks, depending on whether we
134// are branching after a local throw instruction, or after a wasm call:
135//
136// - If we encounter a local throw, we create the exception and tag objects,
137// store them to Instance::pendingException(Tag), and then jump to the
138// landing pad.
139//
140// - If we encounter a wasm call, we construct a MWasmCallCatchable which is a
141// control instruction with either a branch to a fallthrough block or
142// to a pre-pad block.
143//
144// The pre-pad block for a wasm call is empty except for a jump to the
145// landing pad. It only exists to avoid critical edges which when split would
146// violate the invariants of MWasmCallCatchable. The pending exception state
147// is taken care of by the unwinder.
148//
149// Each pre-pad ends with a pending jump to the landing pad. The pending jumps
150// to the landing pad are tracked in `tryPadPatches`. These are called
151// "pad patches".
152//
153// ## Creating the landing pad
154//
155// When we exit try-code, we check if tryPadPatches has captured any control
156// instructions (pad patches). If not, we don't compile any catches and we mark
157// the rest as dead code.
158//
159// If there are pre-pad blocks, we join them to create a landing pad (or just
160// "pad"). The pad's last two slots are the caught exception, and the
161// exception's tag object.
162//
163// There are three different forms of try-catch/catch_all Wasm instructions,
164// which result in different form of landing pad.
165//
166// 1. A catchless try, so a Wasm instruction of the form "try ... end".
167// - In this case, we end the pad by rethrowing the caught exception.
168//
169// 2. A single catch_all after a try.
170// - If the first catch after a try is a catch_all, then there won't be
171// any more catches, but we need the exception and its tag object, in
172// case the code in a catch_all contains "rethrow" instructions.
173// - The Wasm instruction "rethrow", gets the exception and tag object to
174// rethrow from the last two slots of the landing pad which, due to
175// validation, is the l'th surrounding ControlItem.
176// - We immediately GoTo to a new block after the pad and pop both the
177// exception and tag object, as we don't need them anymore in this case.
178//
179// 3. Otherwise, there is one or more catch code blocks following.
180// - In this case, we construct the landing pad by creating a sequence
181// of compare and branch blocks that compare the pending exception tag
182// object to the tag object of the current tagged catch block. This is
183// done incrementally as we visit each tagged catch block in the bytecode
184// stream. At every step, we update the ControlItem's block to point to
185// the next block to be created in the landing pad sequence. The final
186// block will either be a rethrow, if there is no catch_all, or else a
187// jump to a catch_all block.
188
189struct IonCompilePolicy {
190 // We store SSA definitions in the value stack.
191 using Value = MDefinition*;
192 using ValueVector = DefVector;
193
194 // We store loop headers and then/else blocks in the control flow stack.
195 // In the case of try-catch control blocks, we collect additional information
196 // regarding the possible paths from throws and calls to a landing pad, as
197 // well as information on the landing pad's handlers (its catches).
198 using ControlItem = Control;
199};
200
201using IonOpIter = OpIter<IonCompilePolicy>;
202
203class FunctionCompiler;
204
205// CallCompileState describes a call that is being compiled.
206
207class CallCompileState {
208 // A generator object that is passed each argument as it is compiled.
209 WasmABIArgGenerator abi_;
210
211 // Accumulates the register arguments while compiling arguments.
212 MWasmCallBase::Args regArgs_;
213
214 // Reserved argument for passing Instance* to builtin instance method calls.
215 ABIArg instanceArg_;
216
217 // The stack area in which the callee will write stack return values, or
218 // nullptr if no stack results.
219 MWasmStackResultArea* stackResultArea_ = nullptr;
220
221 // Indicates that the call is a return/tail call.
222 bool returnCall = false;
223
224 // Only FunctionCompiler should be directly manipulating CallCompileState.
225 friend class FunctionCompiler;
226};
227
228// Encapsulates the compilation of a single function in an asm.js module. The
229// function compiler handles the creation and final backend compilation of the
230// MIR graph.
231class FunctionCompiler {
232 struct ControlFlowPatch {
233 MControlInstruction* ins;
234 uint32_t index;
235 ControlFlowPatch(MControlInstruction* ins, uint32_t index)
236 : ins(ins), index(index) {}
237 };
238
239 using ControlFlowPatchVector = Vector<ControlFlowPatch, 0, SystemAllocPolicy>;
240
241 struct PendingBlockTarget {
242 ControlFlowPatchVector patches;
243 BranchHint hint = BranchHint::Invalid;
244 };
245
246 using PendingBlockTargetVector =
247 Vector<PendingBlockTarget, 0, SystemAllocPolicy>;
248
249 // Inlined functions accumulate all returns to be bound to a caller function
250 // after compilation is finished.
251 struct PendingInlineReturn {
252 PendingInlineReturn(MGoto* jump, DefVector&& results)
253 : jump(jump), results(std::move(results)) {}
254
255 MGoto* jump;
256 DefVector results;
257 };
258 using PendingInlineReturnVector =
259 Vector<PendingInlineReturn, 1, SystemAllocPolicy>;
260
261 // While compiling a wasm function to Ion, we maintain a stack of
262 // `FunctionCompiler`s. There is always at least one element in the stack,
263 // the "top level" entry. This is for the top level function that we are
264 // compiling.
265 //
266 // When we inline a function call, a new FunctionCompiler is pushed on the
267 // stack. It holds state relating to the inlinee and is popped once the
268 // inlinee has been completed. Hence, if we are currently in N levels of
269 // inlined functions, the stack will contain N + 1 entries.
270 //
271 // The stack is not represented as a vector but rather by each
272 // FunctionCompiler having a pointer to the previous entry in the stack:
273 // `callerCompiler_`. If this is null then we are at the top level. In
274 // order to be be able to access the top level FunctionCompiler in constant
275 // time, each FunctionCompiler also contains a pointer `topLevelCompiler_`
276 // directly to it. Again, if this is null then we are at the top level.
277 // `callerCompiler_` and `callerCompiler_` are either both null or both
278 // non-null.
279 //
280 // Example: this is how the stack would look when we are 3 levels deep in
281 // inlined functions:
282 //
283 // FC1 = TopLevel cc=null tlc=null
284 //
285 // FC2 = Inlinee@depth0 cc=FC1 tlc=FC1
286 //
287 // FC3 = Inlinee@depth1 cc=FC2 tlc=FC1
288 //
289 // FC4 = Inlinee@depth2 cc=FC3 tlc=FC1
290 //
291 // (FC is `FunctionCompiler`,
292 // cc is `callerCompiler_`, tlc is `toplevelCompiler_`)
293
294 // The top-level function compiler. nullptr if this *is* the top level.
295 FunctionCompiler* toplevelCompiler_;
296
297 // The caller function compiler, if any, that we are being inlined into.
298 // Note that `inliningDepth_` is zero for the first inlinee, one for the
299 // second inlinee, etc.
300 const FunctionCompiler* callerCompiler_;
301 const uint32_t inliningDepth_;
302
303 // Statistics for inlining (at all depths) into the top-level function.
304 // These are only valid for the top-level function. For inlined callees
305 // these have no meaning and must be zero.
306 struct InliningStats {
307 size_t topLevelBytecodeSize = 0; // size of top level function
308 size_t inlinedDirectBytecodeSize = 0; // sum of sizes of inlinees
309 size_t inlinedDirectFunctions = 0; // number of inlinees
310 size_t inlinedCallRefBytecodeSize = 0; // sum of sizes of inlinees
311 size_t inlinedCallRefFunctions = 0; // number of inlinees
312 bool allZero() const {
313 return (topLevelBytecodeSize | inlinedDirectBytecodeSize |
314 inlinedDirectFunctions | inlinedCallRefBytecodeSize |
315 inlinedCallRefFunctions) == 0;
316 }
317 };
318 InliningStats stats;
319
320 const CompilerEnvironment& compilerEnv_;
321 const CodeMetadata& codeMeta_;
322 IonOpIter iter_;
323 uint32_t functionBodyOffset_;
324 const FuncCompileInput& func_;
325 const ValTypeVector& locals_;
326 size_t lastReadCallSite_;
327 size_t numCallRefs_;
328
329 TempAllocator& alloc_;
330 MIRGraph& graph_;
331 const CompileInfo& info_;
332 MIRGenerator& mirGen_;
333
334 MBasicBlock* curBlock_;
335 uint32_t maxStackArgBytes_;
336
337 uint32_t loopDepth_;
338 uint32_t blockDepth_;
339 PendingBlockTargetVector pendingBlocks_;
340 // Control flow patches created by `delegate` instructions that target the
341 // outermost label of this function. These will be bound to a pad that will
342 // do a rethrow in `emitBodyDelegateThrowPad`.
343 ControlInstructionVector bodyDelegatePadPatches_;
344 // A vector of the returns in this function for use when we're being inlined
345 // into another function.
346 PendingInlineReturnVector pendingInlineReturns_;
347
348 // Instance pointer argument to the current function.
349 MWasmParameter* instancePointer_;
350 MWasmParameter* stackResultPointer_;
351
352 // Reference to masm.tryNotes_
353 wasm::TryNoteVector& tryNotes_;
354
355 // Reference to the top-level vector of CompileInfo to keep alive for this
356 // compilation.
357 UniqueCompileInfoVector& compileInfos_;
358
359 // Cache of TryControl to minimize heap allocations
360 VectorUniqueTryControl tryControlCache_;
361
362 public:
363 // Construct a FunctionCompiler for the top-level function of a compilation
364 FunctionCompiler(const CompilerEnvironment& compilerEnv,
365 const CodeMetadata& codeMeta, Decoder& decoder,
366 const FuncCompileInput& func, const ValTypeVector& locals,
367 MIRGenerator& mirGen, const CompileInfo& compileInfo,
368 TryNoteVector& tryNotes,
369 UniqueCompileInfoVector& compileInfos)
370 : toplevelCompiler_(nullptr),
371 callerCompiler_(nullptr),
372 inliningDepth_(0),
373 stats(),
374 compilerEnv_(compilerEnv),
375 codeMeta_(codeMeta),
376 iter_(codeMeta, decoder),
377 functionBodyOffset_(decoder.beginOffset()),
378 func_(func),
379 locals_(locals),
380 lastReadCallSite_(0),
381 numCallRefs_(0),
382 alloc_(mirGen.alloc()),
383 graph_(mirGen.graph()),
384 info_(compileInfo),
385 mirGen_(mirGen),
386 curBlock_(nullptr),
387 maxStackArgBytes_(0),
388 loopDepth_(0),
389 blockDepth_(0),
390 instancePointer_(nullptr),
391 stackResultPointer_(nullptr),
392 tryNotes_(tryNotes),
393 compileInfos_(compileInfos) {
394 stats.topLevelBytecodeSize = func.end - func.begin;
395 }
396
397 // Construct a FunctionCompiler for an inlined callee of a compilation
398 FunctionCompiler(FunctionCompiler* toplevelCompiler,
399 const FunctionCompiler* callerCompiler, Decoder& decoder,
400 const FuncCompileInput& func, const ValTypeVector& locals,
401 const CompileInfo& compileInfo)
402 : toplevelCompiler_(toplevelCompiler),
403 callerCompiler_(callerCompiler),
404 inliningDepth_(callerCompiler_->inliningDepth() + 1),
405 compilerEnv_(callerCompiler_->compilerEnv_),
406 codeMeta_(callerCompiler_->codeMeta_),
407 iter_(codeMeta_, decoder),
408 functionBodyOffset_(decoder.beginOffset()),
409 func_(func),
410 locals_(locals),
411 lastReadCallSite_(0),
412 numCallRefs_(0),
413 alloc_(callerCompiler_->alloc_),
414 graph_(callerCompiler_->graph_),
415 info_(compileInfo),
416 mirGen_(callerCompiler_->mirGen_),
417 curBlock_(nullptr),
418 maxStackArgBytes_(0),
419 loopDepth_(callerCompiler_->loopDepth_),
420 blockDepth_(0),
421 instancePointer_(callerCompiler_->instancePointer_),
422 stackResultPointer_(nullptr),
423 tryNotes_(callerCompiler_->tryNotes_),
424 compileInfos_(callerCompiler_->compileInfos_) {
425 MOZ_ASSERT(toplevelCompiler && callerCompiler)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(toplevelCompiler && callerCompiler)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(toplevelCompiler && callerCompiler))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("toplevelCompiler && callerCompiler"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 425); AnnotateMozCrashReason("MOZ_ASSERT" "(" "toplevelCompiler && callerCompiler"
")"); do { *((volatile int*)__null) = 425; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
426 }
427
428 ~FunctionCompiler() {
429 MOZ_ASSERT((toplevelCompiler_ == nullptr) == (callerCompiler_ == nullptr))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((toplevelCompiler_ == nullptr) == (callerCompiler_ ==
nullptr))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!((toplevelCompiler_ == nullptr) == (
callerCompiler_ == nullptr)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(toplevelCompiler_ == nullptr) == (callerCompiler_ == nullptr)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 429); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(toplevelCompiler_ == nullptr) == (callerCompiler_ == nullptr)"
")"); do { *((volatile int*)__null) = 429; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
430 if (toplevelCompiler_) {
431 // This FunctionCompiler was for an inlined callee.
432 MOZ_ASSERT(stats.allZero())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stats.allZero())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stats.allZero()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("stats.allZero()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stats.allZero()"
")"); do { *((volatile int*)__null) = 432; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
433 } else {
434 // This FunctionCompiler was for the top level function. Transfer the
435 // accumulated stats into the CodeMeta object (bypassing the
436 // WasmGenerator).
437 MOZ_ASSERT(stats.topLevelBytecodeSize > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stats.topLevelBytecodeSize > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stats.topLevelBytecodeSize >
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("stats.topLevelBytecodeSize > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 437); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stats.topLevelBytecodeSize > 0"
")"); do { *((volatile int*)__null) = 437; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
438 auto guard = codeMeta().stats.writeLock();
439 guard->partialNumFuncs += 1;
440 guard->partialBCSize += stats.topLevelBytecodeSize;
441 guard->partialNumFuncsInlinedDirect += stats.inlinedDirectFunctions;
442 guard->partialBCInlinedSizeDirect += stats.inlinedDirectBytecodeSize;
443 guard->partialNumFuncsInlinedCallRef += stats.inlinedCallRefFunctions;
444 guard->partialBCInlinedSizeCallRef += stats.inlinedCallRefBytecodeSize;
445 }
446 }
447
448 const CodeMetadata& codeMeta() const { return codeMeta_; }
449
450 IonOpIter& iter() { return iter_; }
451 uint32_t relativeBytecodeOffset() {
452 return readBytecodeOffset() - functionBodyOffset_;
453 }
454 TempAllocator& alloc() const { return alloc_; }
455 // FIXME(1401675): Replace with BlockType.
456 uint32_t funcIndex() const { return func_.index; }
457 const FuncType& funcType() const {
458 return codeMeta_.getFuncType(func_.index);
459 }
460
461 bool isInlined() const { return callerCompiler_ != nullptr; }
462 uint32_t inliningDepth() const { return inliningDepth_; }
463
464 // Update statistics once we have completed an inlined function.
465 void updateInliningStats(size_t inlineeBytecodeSize,
466 InliningHeuristics::CallKind callKind) {
467 MOZ_ASSERT(!toplevelCompiler_ && !callerCompiler_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!toplevelCompiler_ && !callerCompiler_)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!toplevelCompiler_ && !callerCompiler_))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!toplevelCompiler_ && !callerCompiler_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 467); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!toplevelCompiler_ && !callerCompiler_"
")"); do { *((volatile int*)__null) = 467; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
468 MOZ_ASSERT(stats.topLevelBytecodeSize > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stats.topLevelBytecodeSize > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stats.topLevelBytecodeSize >
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("stats.topLevelBytecodeSize > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 468); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stats.topLevelBytecodeSize > 0"
")"); do { *((volatile int*)__null) = 468; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
469 if (callKind == InliningHeuristics::CallKind::Direct) {
470 stats.inlinedDirectBytecodeSize += inlineeBytecodeSize;
471 stats.inlinedDirectFunctions += 1;
472 } else {
473 MOZ_ASSERT(callKind == InliningHeuristics::CallKind::CallRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(callKind == InliningHeuristics::CallKind::CallRef)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(callKind == InliningHeuristics::CallKind::CallRef)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("callKind == InliningHeuristics::CallKind::CallRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callKind == InliningHeuristics::CallKind::CallRef"
")"); do { *((volatile int*)__null) = 473; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
474 stats.inlinedCallRefBytecodeSize += inlineeBytecodeSize;
475 stats.inlinedCallRefFunctions += 1;
476 }
477 }
478 FunctionCompiler* toplevelCompiler() { return toplevelCompiler_; }
479
480 MBasicBlock* getCurBlock() const { return curBlock_; }
481 BytecodeOffset bytecodeOffset() const { return iter_.bytecodeOffset(); }
482 BytecodeOffset bytecodeIfNotAsmJS() const {
483 return codeMeta_.isAsmJS() ? BytecodeOffset() : iter_.bytecodeOffset();
484 }
485 FeatureUsage featureUsage() const { return iter_.featureUsage(); }
486
487 // Try to get a free TryControl from the cache, or allocate a new one.
488 [[nodiscard]] UniqueTryControl newTryControl() {
489 if (tryControlCache_.empty()) {
490 return UniqueTryControl(js_new<TryControl>());
491 }
492 UniqueTryControl tryControl = std::move(tryControlCache_.back());
493 tryControlCache_.popBack();
494 return tryControl;
495 }
496
497 // Release the TryControl to the cache.
498 void freeTryControl(UniqueTryControl&& tryControl) {
499 // Ensure that it's in a consistent state
500 tryControl->reset();
501 // Ignore any OOM, as we'll fail later
502 (void)tryControlCache_.append(std::move(tryControl));
503 }
504
505 [[nodiscard]] bool initTopLevel() {
506 // Prepare the entry block for MIR generation:
507
508 const ArgTypeVector args(funcType());
509
510 if (!mirGen_.ensureBallast()) {
511 return false;
512 }
513 if (!newBlock(/* prev */ nullptr, &curBlock_)) {
514 return false;
515 }
516
517 for (WasmABIArgIter i(args); !i.done(); i++) {
518 MWasmParameter* ins = MWasmParameter::New(alloc(), *i, i.mirType());
519 curBlock_->add(ins);
520 if (args.isSyntheticStackResultPointerArg(i.index())) {
521 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"
, 521); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultPointer_ == nullptr"
")"); do { *((volatile int*)__null) = 521; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
522 stackResultPointer_ = ins;
523 } else {
524 curBlock_->initSlot(info().localSlot(args.naturalIndex(i.index())),
525 ins);
526 }
527 if (!mirGen_.ensureBallast()) {
528 return false;
529 }
530 }
531
532 // Set up a parameter that receives the hidden instance pointer argument.
533 instancePointer_ =
534 MWasmParameter::New(alloc(), ABIArg(InstanceReg), MIRType::Pointer);
535 curBlock_->add(instancePointer_);
536 if (!mirGen_.ensureBallast()) {
537 return false;
538 }
539
540 for (size_t i = args.lengthWithoutStackResults(); i < locals_.length();
541 i++) {
542 ValType slotValType = locals_[i];
543#ifndef ENABLE_WASM_SIMD1
544 if (slotValType == ValType::V128) {
545 return iter().fail("Ion has no SIMD support yet");
546 }
547#endif
548 MDefinition* zero = constantZeroOfValType(slotValType);
549 curBlock_->initSlot(info().localSlot(i), zero);
550 if (!mirGen_.ensureBallast()) {
551 return false;
552 }
553 }
554
555 return true;
556 }
557
558 [[nodiscard]] bool initInline(const DefVector& argValues) {
559 // Prepare the entry block for MIR generation:
560 if (!mirGen_.ensureBallast()) {
561 return false;
562 }
563 if (!newBlock(nullptr, &curBlock_)) {
564 return false;
565 }
566
567 MBasicBlock* pred = callerCompiler_->curBlock_;
568 pred->end(MGoto::New(alloc(), curBlock_));
569 if (!curBlock_->addPredecessorWithoutPhis(pred)) {
570 return false;
571 }
572
573 // Set up args slots to point to passed argument values
574 const FuncType& type = funcType();
575 for (uint32_t argIndex = 0; argIndex < type.args().length(); argIndex++) {
576 curBlock_->initSlot(info().localSlot(argIndex), argValues[argIndex]);
577 }
578
579 // Set up a parameter that receives the hidden instance pointer argument.
580 instancePointer_ = callerCompiler_->instancePointer_;
581
582 // Initialize all local slots to zero value
583 for (size_t i = type.args().length(); i < locals_.length(); i++) {
584 ValType slotValType = locals_[i];
585#ifndef ENABLE_WASM_SIMD1
586 if (slotValType == ValType::V128) {
587 return iter().fail("Ion has no SIMD support yet");
588 }
589#endif
590 MDefinition* zero = constantZeroOfValType(slotValType);
591 curBlock_->initSlot(info().localSlot(i), zero);
592 if (!mirGen_.ensureBallast()) {
593 return false;
594 }
595 }
596
597 return true;
598 }
599
600 // Add a compile info for an inlined function. This keeps the inlined
601 // function's compile info alive for the outermost function's
602 // compilation.
603 [[nodiscard]] CompileInfo* addInlineCallInfo(uint32_t numLocals) {
604 UniqueCompileInfo compileInfo = MakeUnique<CompileInfo>(numLocals);
605 if (!compileInfo || !compileInfos_.append(std::move(compileInfo))) {
606 return nullptr;
607 }
608 return compileInfos_[compileInfos_.length() - 1].get();
609 }
610
611 void finish() {
612 mirGen().accumulateWasmMaxStackArgBytes(maxStackArgBytes_);
613
614 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"
, 615); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callerCompiler_ ? (loopDepth_ == callerCompiler_->loopDepth_) : (loopDepth_ == 0)"
")"); do { *((volatile int*)__null) = 615; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
615 : (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"
, 615); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callerCompiler_ ? (loopDepth_ == callerCompiler_->loopDepth_) : (loopDepth_ == 0)"
")"); do { *((volatile int*)__null) = 615; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
616 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"
, 616); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_ == 0"
")"); do { *((volatile int*)__null) = 616; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
617#ifdef DEBUG1
618 for (PendingBlockTarget& targets : pendingBlocks_) {
619 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"
, 619); AnnotateMozCrashReason("MOZ_ASSERT" "(" "targets.patches.empty()"
")"); do { *((volatile int*)__null) = 619; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
620 }
621#endif
622 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"
, 622); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 622; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
623 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"
, 623); AnnotateMozCrashReason("MOZ_ASSERT" "(" "done()" ")")
; do { *((volatile int*)__null) = 623; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
624 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"
, 624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "func_.callSiteLineNums.length() == lastReadCallSite_"
")"); do { *((volatile int*)__null) = 624; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
625 MOZ_ASSERT_IF(do { if (compilerEnv().mode() == CompileMode::LazyTiering) { do
{ static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_"
")"); do { *((volatile int*)__null) = 627; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
626 compilerEnv().mode() == CompileMode::LazyTiering,do { if (compilerEnv().mode() == CompileMode::LazyTiering) { do
{ static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_"
")"); do { *((volatile int*)__null) = 627; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
627 codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_)do { if (compilerEnv().mode() == CompileMode::LazyTiering) { do
{ static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta_.getFuncDefCallRefs(funcIndex()).length == numCallRefs_"
")"); do { *((volatile int*)__null) = 627; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
628 }
629
630 /************************* Read-only interface (after local scope setup) */
631
632 MIRGenerator& mirGen() const { return mirGen_; }
633 MIRGraph& mirGraph() const { return graph_; }
634 const CompileInfo& info() const { return info_; }
635 const CompilerEnvironment& compilerEnv() const { return compilerEnv_; }
636
637 MDefinition* getLocalDef(unsigned slot) {
638 if (inDeadCode()) {
639 return nullptr;
640 }
641 return curBlock_->getSlot(info().localSlot(slot));
642 }
643
644 const ValTypeVector& locals() const { return locals_; }
645
646 /*********************************************************** Constants ***/
647
648 MDefinition* constantF32(float f) {
649 if (inDeadCode()) {
650 return nullptr;
651 }
652 auto* cst = MWasmFloatConstant::NewFloat32(alloc(), f);
653 curBlock_->add(cst);
654 return cst;
655 }
656 // Hide all other overloads, to guarantee no implicit argument conversion.
657 template <typename T>
658 MDefinition* constantF32(T) = delete;
659
660 MDefinition* constantF64(double d) {
661 if (inDeadCode()) {
662 return nullptr;
663 }
664 auto* cst = MWasmFloatConstant::NewDouble(alloc(), d);
665 curBlock_->add(cst);
666 return cst;
667 }
668 template <typename T>
669 MDefinition* constantF64(T) = delete;
670
671 MDefinition* constantI32(int32_t i) {
672 if (inDeadCode()) {
673 return nullptr;
674 }
675 MConstant* constant =
676 MConstant::New(alloc(), Int32Value(i), MIRType::Int32);
677 curBlock_->add(constant);
678 return constant;
679 }
680 template <typename T>
681 MDefinition* constantI32(T) = delete;
682
683 MDefinition* constantI64(int64_t i) {
684 if (inDeadCode()) {
685 return nullptr;
686 }
687 MConstant* constant = MConstant::NewInt64(alloc(), i);
688 curBlock_->add(constant);
689 return constant;
690 }
691 template <typename T>
692 MDefinition* constantI64(T) = delete;
693
694 // Produce an MConstant of the machine's target int type (Int32 or Int64).
695 MDefinition* constantTargetWord(intptr_t n) {
696 return targetIs64Bit() ? constantI64(int64_t(n)) : constantI32(int32_t(n));
697 }
698 template <typename T>
699 MDefinition* constantTargetWord(T) = delete;
700
701#ifdef ENABLE_WASM_SIMD1
702 MDefinition* constantV128(V128 v) {
703 if (inDeadCode()) {
704 return nullptr;
705 }
706 MWasmFloatConstant* constant = MWasmFloatConstant::NewSimd128(
707 alloc(), SimdConstant::CreateSimd128((int8_t*)v.bytes));
708 curBlock_->add(constant);
709 return constant;
710 }
711 template <typename T>
712 MDefinition* constantV128(T) = delete;
713#endif
714
715 MDefinition* constantNullRef() {
716 if (inDeadCode()) {
717 return nullptr;
718 }
719 // MConstant has a lot of baggage so we don't use that here.
720 MWasmNullConstant* constant = MWasmNullConstant::New(alloc());
721 curBlock_->add(constant);
722 return constant;
723 }
724
725 // Produce a zero constant for the specified ValType.
726 MDefinition* constantZeroOfValType(ValType valType) {
727 switch (valType.kind()) {
728 case ValType::I32:
729 return constantI32(0);
730 case ValType::I64:
731 return constantI64(int64_t(0));
732#ifdef ENABLE_WASM_SIMD1
733 case ValType::V128:
734 return constantV128(V128(0));
735#endif
736 case ValType::F32:
737 return constantF32(0.0f);
738 case ValType::F64:
739 return constantF64(0.0);
740 case ValType::Ref:
741 return constantNullRef();
742 default:
743 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 743); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 743; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
744 }
745 }
746
747 /***************************** Code generation (after local scope setup) */
748
749 void fence() {
750 if (inDeadCode()) {
751 return;
752 }
753 MWasmFence* ins = MWasmFence::New(alloc());
754 curBlock_->add(ins);
755 }
756
757 template <class T>
758 MDefinition* unary(MDefinition* op) {
759 if (inDeadCode()) {
760 return nullptr;
761 }
762 T* ins = T::New(alloc(), op);
763 curBlock_->add(ins);
764 return ins;
765 }
766
767 template <class T>
768 MDefinition* unary(MDefinition* op, MIRType type) {
769 if (inDeadCode()) {
770 return nullptr;
771 }
772 T* ins = T::New(alloc(), op, type);
773 curBlock_->add(ins);
774 return ins;
775 }
776
777 template <class T>
778 MDefinition* binary(MDefinition* lhs, MDefinition* rhs) {
779 if (inDeadCode()) {
780 return nullptr;
781 }
782 T* ins = T::New(alloc(), lhs, rhs);
783 curBlock_->add(ins);
784 return ins;
785 }
786
787 template <class T>
788 MDefinition* binary(MDefinition* lhs, MDefinition* rhs, MIRType type) {
789 if (inDeadCode()) {
790 return nullptr;
791 }
792 T* ins = T::New(alloc(), lhs, rhs, type);
793 curBlock_->add(ins);
794 return ins;
795 }
796
797 template <class T>
798 MDefinition* binary(MDefinition* lhs, MDefinition* rhs, MIRType type,
799 MWasmBinaryBitwise::SubOpcode subOpc) {
800 if (inDeadCode()) {
801 return nullptr;
802 }
803 T* ins = T::New(alloc(), lhs, rhs, type, subOpc);
804 curBlock_->add(ins);
805 return ins;
806 }
807
808 MDefinition* ursh(MDefinition* lhs, MDefinition* rhs, MIRType type) {
809 if (inDeadCode()) {
810 return nullptr;
811 }
812 auto* ins = MUrsh::NewWasm(alloc(), lhs, rhs, type);
813 curBlock_->add(ins);
814 return ins;
815 }
816
817 MDefinition* add(MDefinition* lhs, MDefinition* rhs, MIRType type) {
818 if (inDeadCode()) {
819 return nullptr;
820 }
821 auto* ins = MAdd::NewWasm(alloc(), lhs, rhs, type);
822 curBlock_->add(ins);
823 return ins;
824 }
825
826 bool mustPreserveNaN(MIRType type) {
827 return IsFloatingPointType(type) && !codeMeta().isAsmJS();
828 }
829
830 MDefinition* sub(MDefinition* lhs, MDefinition* rhs, MIRType type) {
831 if (inDeadCode()) {
832 return nullptr;
833 }
834
835 // wasm can't fold x - 0.0 because of NaN with custom payloads.
836 MSub* ins = MSub::NewWasm(alloc(), lhs, rhs, type, mustPreserveNaN(type));
837 curBlock_->add(ins);
838 return ins;
839 }
840
841 MDefinition* nearbyInt(MDefinition* input, RoundingMode roundingMode) {
842 if (inDeadCode()) {
843 return nullptr;
844 }
845
846 auto* ins = MNearbyInt::New(alloc(), input, input->type(), roundingMode);
847 curBlock_->add(ins);
848 return ins;
849 }
850
851 MDefinition* minMax(MDefinition* lhs, MDefinition* rhs, MIRType type,
852 bool isMax) {
853 if (inDeadCode()) {
854 return nullptr;
855 }
856
857 if (mustPreserveNaN(type)) {
858 // Convert signaling NaN to quiet NaNs.
859 MDefinition* zero = constantZeroOfValType(ValType::fromMIRType(type));
860 lhs = sub(lhs, zero, type);
861 rhs = sub(rhs, zero, type);
862 }
863
864 MMinMax* ins = MMinMax::NewWasm(alloc(), lhs, rhs, type, isMax);
865 curBlock_->add(ins);
866 return ins;
867 }
868
869 MDefinition* mul(MDefinition* lhs, MDefinition* rhs, MIRType type,
870 MMul::Mode mode) {
871 if (inDeadCode()) {
872 return nullptr;
873 }
874
875 // wasm can't fold x * 1.0 because of NaN with custom payloads.
876 auto* ins =
877 MMul::NewWasm(alloc(), lhs, rhs, type, mode, mustPreserveNaN(type));
878 curBlock_->add(ins);
879 return ins;
880 }
881
882 MDefinition* div(MDefinition* lhs, MDefinition* rhs, MIRType type,
883 bool unsignd) {
884 if (inDeadCode()) {
885 return nullptr;
886 }
887 bool trapOnError = !codeMeta().isAsmJS();
888 if (!unsignd && type == MIRType::Int32) {
889 // Enforce the signedness of the operation by coercing the operands
890 // to signed. Otherwise, operands that "look" unsigned to Ion but
891 // are not unsigned to Baldr (eg, unsigned right shifts) may lead to
892 // the operation being executed unsigned. Applies to mod() as well.
893 //
894 // Do this for Int32 only since Int64 is not subject to the same
895 // issues.
896 //
897 // Note the offsets passed to MWasmBuiltinTruncateToInt32 are wrong here,
898 // but it doesn't matter: they're not codegen'd to calls since inputs
899 // already are int32.
900 auto* lhs2 = createTruncateToInt32(lhs);
901 curBlock_->add(lhs2);
902 lhs = lhs2;
903 auto* rhs2 = createTruncateToInt32(rhs);
904 curBlock_->add(rhs2);
905 rhs = rhs2;
906 }
907
908 // For x86 and arm we implement i64 div via c++ builtin.
909 // A call to c++ builtin requires instance pointer.
910#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
911 if (type == MIRType::Int64) {
912 auto* ins =
913 MWasmBuiltinDivI64::New(alloc(), lhs, rhs, instancePointer_, unsignd,
914 trapOnError, bytecodeOffset());
915 curBlock_->add(ins);
916 return ins;
917 }
918#endif
919
920 auto* ins = MDiv::New(alloc(), lhs, rhs, type, unsignd, trapOnError,
921 bytecodeOffset(), mustPreserveNaN(type));
922 curBlock_->add(ins);
923 return ins;
924 }
925
926 MInstruction* createTruncateToInt32(MDefinition* op) {
927 if (op->type() == MIRType::Double || op->type() == MIRType::Float32) {
928 return MWasmBuiltinTruncateToInt32::New(alloc(), op, instancePointer_);
929 }
930
931 return MTruncateToInt32::New(alloc(), op);
932 }
933
934 MDefinition* mod(MDefinition* lhs, MDefinition* rhs, MIRType type,
935 bool unsignd) {
936 if (inDeadCode()) {
937 return nullptr;
938 }
939 bool trapOnError = !codeMeta().isAsmJS();
940 if (!unsignd && type == MIRType::Int32) {
941 // See block comment in div().
942 auto* lhs2 = createTruncateToInt32(lhs);
943 curBlock_->add(lhs2);
944 lhs = lhs2;
945 auto* rhs2 = createTruncateToInt32(rhs);
946 curBlock_->add(rhs2);
947 rhs = rhs2;
948 }
949
950 // For x86 and arm we implement i64 mod via c++ builtin.
951 // A call to c++ builtin requires instance pointer.
952#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
953 if (type == MIRType::Int64) {
954 auto* ins =
955 MWasmBuiltinModI64::New(alloc(), lhs, rhs, instancePointer_, unsignd,
956 trapOnError, bytecodeOffset());
957 curBlock_->add(ins);
958 return ins;
959 }
960#endif
961
962 // Should be handled separately because we call BuiltinThunk for this case
963 // and so, need to add the dependency from instancePointer.
964 if (type == MIRType::Double) {
965 auto* ins = MWasmBuiltinModD::New(alloc(), lhs, rhs, instancePointer_,
966 type, bytecodeOffset());
967 curBlock_->add(ins);
968 return ins;
969 }
970
971 auto* ins = MMod::New(alloc(), lhs, rhs, type, unsignd, trapOnError,
972 bytecodeOffset());
973 curBlock_->add(ins);
974 return ins;
975 }
976
977 MDefinition* bitnot(MDefinition* op) {
978 if (inDeadCode()) {
979 return nullptr;
980 }
981 auto* ins = MBitNot::New(alloc(), op);
982 curBlock_->add(ins);
983 return ins;
984 }
985
986 MDefinition* select(MDefinition* trueExpr, MDefinition* falseExpr,
987 MDefinition* condExpr) {
988 if (inDeadCode()) {
989 return nullptr;
990 }
991 auto* ins = MWasmSelect::New(alloc(), trueExpr, falseExpr, condExpr);
992 curBlock_->add(ins);
993 return ins;
994 }
995
996 MDefinition* extendI32(MDefinition* op, bool isUnsigned) {
997 if (inDeadCode()) {
998 return nullptr;
999 }
1000 auto* ins = MExtendInt32ToInt64::New(alloc(), op, isUnsigned);
1001 curBlock_->add(ins);
1002 return ins;
1003 }
1004
1005 MDefinition* signExtend(MDefinition* op, uint32_t srcSize,
1006 uint32_t targetSize) {
1007 if (inDeadCode()) {
1008 return nullptr;
1009 }
1010 MInstruction* ins;
1011 switch (targetSize) {
1012 case 4: {
1013 MSignExtendInt32::Mode mode;
1014 switch (srcSize) {
1015 case 1:
1016 mode = MSignExtendInt32::Byte;
1017 break;
1018 case 2:
1019 mode = MSignExtendInt32::Half;
1020 break;
1021 default:
1022 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"
, 1022); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 1022; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1023 }
1024 ins = MSignExtendInt32::New(alloc(), op, mode);
1025 break;
1026 }
1027 case 8: {
1028 MSignExtendInt64::Mode mode;
1029 switch (srcSize) {
1030 case 1:
1031 mode = MSignExtendInt64::Byte;
1032 break;
1033 case 2:
1034 mode = MSignExtendInt64::Half;
1035 break;
1036 case 4:
1037 mode = MSignExtendInt64::Word;
1038 break;
1039 default:
1040 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"
, 1040); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 1040; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1041 }
1042 ins = MSignExtendInt64::New(alloc(), op, mode);
1043 break;
1044 }
1045 default: {
1046 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"
, 1046); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 1046; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1047 }
1048 }
1049 curBlock_->add(ins);
1050 return ins;
1051 }
1052
1053 MDefinition* convertI64ToFloatingPoint(MDefinition* op, MIRType type,
1054 bool isUnsigned) {
1055 if (inDeadCode()) {
1056 return nullptr;
1057 }
1058#if defined(JS_CODEGEN_ARM)
1059 auto* ins = MBuiltinInt64ToFloatingPoint::New(
1060 alloc(), op, instancePointer_, type, bytecodeOffset(), isUnsigned);
1061#else
1062 auto* ins = MInt64ToFloatingPoint::New(alloc(), op, type, bytecodeOffset(),
1063 isUnsigned);
1064#endif
1065 curBlock_->add(ins);
1066 return ins;
1067 }
1068
1069 MDefinition* rotate(MDefinition* input, MDefinition* count, MIRType type,
1070 bool left) {
1071 if (inDeadCode()) {
1072 return nullptr;
1073 }
1074 auto* ins = MRotate::New(alloc(), input, count, type, left);
1075 curBlock_->add(ins);
1076 return ins;
1077 }
1078
1079 template <class T>
1080 MDefinition* truncate(MDefinition* op, TruncFlags flags) {
1081 if (inDeadCode()) {
1082 return nullptr;
1083 }
1084 auto* ins = T::New(alloc(), op, flags, bytecodeOffset());
1085 curBlock_->add(ins);
1086 return ins;
1087 }
1088
1089#if defined(JS_CODEGEN_ARM)
1090 MDefinition* truncateWithInstance(MDefinition* op, TruncFlags flags) {
1091 if (inDeadCode()) {
1092 return nullptr;
1093 }
1094 auto* ins = MWasmBuiltinTruncateToInt64::New(alloc(), op, instancePointer_,
1095 flags, bytecodeOffset());
1096 curBlock_->add(ins);
1097 return ins;
1098 }
1099#endif
1100
1101 MDefinition* compare(MDefinition* lhs, MDefinition* rhs, JSOp op,
1102 MCompare::CompareType type) {
1103 if (inDeadCode()) {
1104 return nullptr;
1105 }
1106 auto* ins = MCompare::NewWasm(alloc(), lhs, rhs, op, type);
1107 curBlock_->add(ins);
1108 return ins;
1109 }
1110
1111 void assign(unsigned slot, MDefinition* def) {
1112 if (inDeadCode()) {
1113 return;
1114 }
1115 curBlock_->setSlot(info().localSlot(slot), def);
1116 }
1117
1118 MDefinition* compareIsNull(MDefinition* ref, JSOp compareOp) {
1119 MDefinition* nullVal = constantNullRef();
1120 if (!nullVal) {
1121 return nullptr;
1122 }
1123 return compare(ref, nullVal, compareOp, MCompare::Compare_WasmAnyRef);
1124 }
1125
1126 [[nodiscard]] bool refAsNonNull(MDefinition* ref) {
1127 if (inDeadCode()) {
1128 return true;
1129 }
1130
1131 auto* ins = MWasmTrapIfNull::New(
1132 alloc(), ref, wasm::Trap::NullPointerDereference, bytecodeOffset());
1133
1134 curBlock_->add(ins);
1135 return true;
1136 }
1137
1138#ifdef ENABLE_WASM_GC1
1139 [[nodiscard]] bool brOnNull(uint32_t relativeDepth, const DefVector& values,
1140 const ResultType& type, MDefinition* condition) {
1141 if (inDeadCode()) {
1142 return true;
1143 }
1144
1145 MBasicBlock* fallthroughBlock = nullptr;
1146 if (!newBlock(curBlock_, &fallthroughBlock)) {
1147 return false;
1148 }
1149
1150 MDefinition* check = compareIsNull(condition, JSOp::Eq);
1151 if (!check) {
1152 return false;
1153 }
1154 MTest* test = MTest::New(alloc(), check, nullptr, fallthroughBlock);
1155 if (!test ||
1156 !addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) {
1157 return false;
1158 }
1159
1160 if (!pushDefs(values)) {
1161 return false;
1162 }
1163
1164 curBlock_->end(test);
1165 curBlock_ = fallthroughBlock;
1166 return true;
1167 }
1168
1169 [[nodiscard]] bool brOnNonNull(uint32_t relativeDepth,
1170 const DefVector& values,
1171 const ResultType& type,
1172 MDefinition* condition) {
1173 if (inDeadCode()) {
1174 return true;
1175 }
1176
1177 MBasicBlock* fallthroughBlock = nullptr;
1178 if (!newBlock(curBlock_, &fallthroughBlock)) {
1179 return false;
1180 }
1181
1182 MDefinition* check = compareIsNull(condition, JSOp::Ne);
1183 if (!check) {
1184 return false;
1185 }
1186 MTest* test = MTest::New(alloc(), check, nullptr, fallthroughBlock);
1187 if (!test ||
1188 !addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) {
1189 return false;
1190 }
1191
1192 if (!pushDefs(values)) {
1193 return false;
1194 }
1195
1196 curBlock_->end(test);
1197 curBlock_ = fallthroughBlock;
1198 return true;
1199 }
1200
1201#endif // ENABLE_WASM_GC
1202
1203#ifdef ENABLE_WASM_GC1
1204 MDefinition* refI31(MDefinition* input) {
1205 auto* ins = MWasmNewI31Ref::New(alloc(), input);
1206 curBlock_->add(ins);
1207 return ins;
1208 }
1209
1210 MDefinition* i31Get(MDefinition* input, FieldWideningOp wideningOp) {
1211 auto* ins = MWasmI31RefGet::New(alloc(), input, wideningOp);
1212 curBlock_->add(ins);
1213 return ins;
1214 }
1215#endif // ENABLE_WASM_GC
1216
1217#ifdef ENABLE_WASM_SIMD1
1218 // About Wasm SIMD as supported by Ion:
1219 //
1220 // The expectation is that Ion will only ever support SIMD on x86 and x64,
1221 // since ARMv7 will cease to be a tier-1 platform soon, and MIPS64 will never
1222 // implement SIMD.
1223 //
1224 // The division of the operations into MIR nodes reflects that expectation,
1225 // and is a good fit for x86/x64. Should the expectation change we'll
1226 // possibly want to re-architect the SIMD support to be a little more general.
1227 //
1228 // Most SIMD operations map directly to a single MIR node that ultimately ends
1229 // up being expanded in the macroassembler.
1230 //
1231 // Some SIMD operations that do have a complete macroassembler expansion are
1232 // open-coded into multiple MIR nodes here; in some cases that's just
1233 // convenience, in other cases it may also allow them to benefit from Ion
1234 // optimizations. The reason for the expansions will be documented by a
1235 // comment.
1236
1237 // (v128,v128) -> v128 effect-free binary operations
1238 MDefinition* binarySimd128(MDefinition* lhs, MDefinition* rhs,
1239 bool commutative, SimdOp op) {
1240 if (inDeadCode()) {
1241 return nullptr;
1242 }
1243
1244 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"
, 1245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1245; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1245 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"
, 1245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1245; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1246
1247 auto* ins = MWasmBinarySimd128::New(alloc(), lhs, rhs, commutative, op);
1248 curBlock_->add(ins);
1249 return ins;
1250 }
1251
1252 // (v128,i32) -> v128 effect-free shift operations
1253 MDefinition* shiftSimd128(MDefinition* lhs, MDefinition* rhs, SimdOp op) {
1254 if (inDeadCode()) {
1255 return nullptr;
1256 }
1257
1258 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"
, 1259); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1259; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1259 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"
, 1259); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1259; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1260
1261 int32_t maskBits;
1262 if (MacroAssembler::MustMaskShiftCountSimd128(op, &maskBits)) {
1263 MDefinition* mask = constantI32(maskBits);
1264 auto* rhs2 = MBitAnd::New(alloc(), rhs, mask, MIRType::Int32);
1265 curBlock_->add(rhs2);
1266 rhs = rhs2;
1267 }
1268
1269 auto* ins = MWasmShiftSimd128::New(alloc(), lhs, rhs, op);
1270 curBlock_->add(ins);
1271 return ins;
1272 }
1273
1274 // (v128,scalar,imm) -> v128
1275 MDefinition* replaceLaneSimd128(MDefinition* lhs, MDefinition* rhs,
1276 uint32_t laneIndex, SimdOp op) {
1277 if (inDeadCode()) {
1278 return nullptr;
1279 }
1280
1281 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"
, 1281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1281; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1282
1283 auto* ins = MWasmReplaceLaneSimd128::New(alloc(), lhs, rhs, laneIndex, op);
1284 curBlock_->add(ins);
1285 return ins;
1286 }
1287
1288 // (scalar) -> v128 effect-free unary operations
1289 MDefinition* scalarToSimd128(MDefinition* src, SimdOp op) {
1290 if (inDeadCode()) {
1291 return nullptr;
1292 }
1293
1294 auto* ins = MWasmScalarToSimd128::New(alloc(), src, op);
1295 curBlock_->add(ins);
1296 return ins;
1297 }
1298
1299 // (v128) -> v128 effect-free unary operations
1300 MDefinition* unarySimd128(MDefinition* src, SimdOp op) {
1301 if (inDeadCode()) {
1302 return nullptr;
1303 }
1304
1305 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"
, 1305); AnnotateMozCrashReason("MOZ_ASSERT" "(" "src->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1305; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1306 auto* ins = MWasmUnarySimd128::New(alloc(), src, op);
1307 curBlock_->add(ins);
1308 return ins;
1309 }
1310
1311 // (v128, imm) -> scalar effect-free unary operations
1312 MDefinition* reduceSimd128(MDefinition* src, SimdOp op, ValType outType,
1313 uint32_t imm = 0) {
1314 if (inDeadCode()) {
1315 return nullptr;
1316 }
1317
1318 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"
, 1318); AnnotateMozCrashReason("MOZ_ASSERT" "(" "src->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1318; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1319 auto* ins =
1320 MWasmReduceSimd128::New(alloc(), src, op, outType.toMIRType(), imm);
1321 curBlock_->add(ins);
1322 return ins;
1323 }
1324
1325 // (v128, v128, v128) -> v128 effect-free operations
1326 MDefinition* ternarySimd128(MDefinition* v0, MDefinition* v1, MDefinition* v2,
1327 SimdOp op) {
1328 if (inDeadCode()) {
1329 return nullptr;
1330 }
1331
1332 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"
, 1334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1334; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1333 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"
, 1334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1334; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1334 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"
, 1334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1334; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1335
1336 auto* ins = MWasmTernarySimd128::New(alloc(), v0, v1, v2, op);
1337 curBlock_->add(ins);
1338 return ins;
1339 }
1340
1341 // (v128, v128, imm_v128) -> v128 effect-free operations
1342 MDefinition* shuffleSimd128(MDefinition* v1, MDefinition* v2, V128 control) {
1343 if (inDeadCode()) {
1344 return nullptr;
1345 }
1346
1347 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"
, 1347); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v1->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1347; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1348 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"
, 1348); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1348; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1349 auto* ins = BuildWasmShuffleSimd128(
1350 alloc(), reinterpret_cast<int8_t*>(control.bytes), v1, v2);
1351 curBlock_->add(ins);
1352 return ins;
1353 }
1354
1355 // Also see below for SIMD memory references
1356
1357#endif // ENABLE_WASM_SIMD
1358
1359 /************************************************ Linear memory accesses */
1360
1361 // For detailed information about memory accesses, see "Linear memory
1362 // addresses and bounds checking" in WasmMemory.cpp.
1363
1364 private:
1365 // If the platform does not have a HeapReg, load the memory base from
1366 // instance.
1367 MDefinition* maybeLoadMemoryBase(uint32_t memoryIndex) {
1368#ifdef WASM_HAS_HEAPREG1
1369 if (memoryIndex == 0) {
1370 return nullptr;
1371 }
1372#endif
1373 return memoryBase(memoryIndex);
1374 }
1375
1376 public:
1377 // A value holding the memory base, whether that's HeapReg or some other
1378 // register.
1379 MDefinition* memoryBase(uint32_t memoryIndex) {
1380 AliasSet aliases = !codeMeta_.memories[memoryIndex].canMovingGrow()
1381 ? AliasSet::None()
1382 : AliasSet::Load(AliasSet::WasmHeapMeta);
1383#ifdef WASM_HAS_HEAPREG1
1384 if (memoryIndex == 0) {
1385 MWasmHeapReg* base = MWasmHeapReg::New(alloc(), aliases);
1386 curBlock_->add(base);
1387 return base;
1388 }
1389#endif
1390 uint32_t offset =
1391 memoryIndex == 0
1392 ? Instance::offsetOfMemory0Base()
1393 : (Instance::offsetInData(
1394 codeMeta_.offsetOfMemoryInstanceData(memoryIndex) +
1395 offsetof(MemoryInstanceData, base)__builtin_offsetof(MemoryInstanceData, base)));
1396 MWasmLoadInstance* base = MWasmLoadInstance::New(
1397 alloc(), instancePointer_, offset, MIRType::Pointer, aliases);
1398 curBlock_->add(base);
1399 return base;
1400 }
1401
1402 private:
1403 // If the bounds checking strategy requires it, load the bounds check limit
1404 // from the instance.
1405 MWasmLoadInstance* maybeLoadBoundsCheckLimit(uint32_t memoryIndex,
1406 MIRType type) {
1407 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"
, 1407); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Int32 || type == MIRType::Int64"
")"); do { *((volatile int*)__null) = 1407; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1408 if (codeMeta_.hugeMemoryEnabled(memoryIndex)) {
1409 return nullptr;
1410 }
1411 uint32_t offset =
1412 memoryIndex == 0
1413 ? Instance::offsetOfMemory0BoundsCheckLimit()
1414 : (Instance::offsetInData(
1415 codeMeta_.offsetOfMemoryInstanceData(memoryIndex) +
1416 offsetof(MemoryInstanceData, boundsCheckLimit)__builtin_offsetof(MemoryInstanceData, boundsCheckLimit)));
1417 AliasSet aliases = !codeMeta_.memories[memoryIndex].canMovingGrow()
1418 ? AliasSet::None()
1419 : AliasSet::Load(AliasSet::WasmHeapMeta);
1420 auto* load = MWasmLoadInstance::New(alloc(), instancePointer_, offset, type,
1421 aliases);
1422 curBlock_->add(load);
1423 return load;
1424 }
1425
1426 // Return true if the access requires an alignment check. If so, sets
1427 // *mustAdd to true if the offset must be added to the pointer before
1428 // checking.
1429 bool needAlignmentCheck(MemoryAccessDesc* access, MDefinition* base,
1430 bool* mustAdd) {
1431 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"
, 1431); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*mustAdd" ")"
); do { *((volatile int*)__null) = 1431; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1432
1433 // asm.js accesses are always aligned and need no checks.
1434 if (codeMeta_.isAsmJS() || !access->isAtomic()) {
1435 return false;
1436 }
1437
1438 // If the EA is known and aligned it will need no checks.
1439 if (base->isConstant()) {
1440 // We only care about the low bits, so overflow is OK, as is chopping off
1441 // the high bits of an i64 pointer.
1442 uint32_t ptr = 0;
1443 if (isMem64(access->memoryIndex())) {
1444 ptr = uint32_t(base->toConstant()->toInt64());
1445 } else {
1446 ptr = base->toConstant()->toInt32();
1447 }
1448 if (((ptr + access->offset64()) & (access->byteSize() - 1)) == 0) {
1449 return false;
1450 }
1451 }
1452
1453 // If the offset is aligned then the EA is just the pointer, for
1454 // the purposes of this check.
1455 *mustAdd = (access->offset64() & (access->byteSize() - 1)) != 0;
1456 return true;
1457 }
1458
1459 // Fold a constant base into the offset and make the base 0, provided the
1460 // offset stays below the guard limit. The reason for folding the base into
1461 // the offset rather than vice versa is that a small offset can be ignored
1462 // by both explicit bounds checking and bounds check elimination.
1463 void foldConstantPointer(MemoryAccessDesc* access, MDefinition** base) {
1464 uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit(
1465 codeMeta_.hugeMemoryEnabled(access->memoryIndex()));
1466
1467 if ((*base)->isConstant()) {
1468 uint64_t basePtr = 0;
1469 if (isMem64(access->memoryIndex())) {
1470 basePtr = uint64_t((*base)->toConstant()->toInt64());
1471 } else {
1472 basePtr = uint64_t(int64_t((*base)->toConstant()->toInt32()));
1473 }
1474
1475 uint64_t offset = access->offset64();
1476 if (offset < offsetGuardLimit && basePtr < offsetGuardLimit - offset) {
1477 offset += basePtr;
1478 access->setOffset32(uint32_t(offset));
1479 *base = isMem64(access->memoryIndex()) ? constantI64(int64_t(0))
1480 : constantI32(0);
1481 }
1482 }
1483 }
1484
1485 // If the offset must be added because it is large or because the true EA must
1486 // be checked, compute the effective address, trapping on overflow.
1487 void maybeComputeEffectiveAddress(MemoryAccessDesc* access,
1488 MDefinition** base, bool mustAddOffset) {
1489 uint64_t offsetGuardLimit = GetMaxOffsetGuardLimit(
1490 codeMeta_.hugeMemoryEnabled(access->memoryIndex()));
1491
1492 if (access->offset64() >= offsetGuardLimit ||
1493 access->offset64() > UINT32_MAX(4294967295U) || mustAddOffset ||
1494 !JitOptions.wasmFoldOffsets) {
1495 *base = computeEffectiveAddress(*base, access);
1496 }
1497 }
1498
1499 MWasmLoadInstance* needBoundsCheck(uint32_t memoryIndex) {
1500#ifdef JS_64BIT1
1501 // For 32-bit base pointers:
1502 //
1503 // If the bounds check uses the full 64 bits of the bounds check limit, then
1504 // the base pointer must be zero-extended to 64 bits before checking and
1505 // wrapped back to 32-bits after Spectre masking. (And it's important that
1506 // the value we end up with has flowed through the Spectre mask.)
1507 //
1508 // If the memory's max size is known to be smaller than 64K pages exactly,
1509 // we can use a 32-bit check and avoid extension and wrapping.
1510 static_assert(0x100000000 % PageSize == 0);
1511 bool mem32LimitIs64Bits =
1512 isMem32(memoryIndex) &&
1513 !codeMeta_.memories[memoryIndex].boundsCheckLimitIs32Bits() &&
1514 MaxMemoryPages(codeMeta_.memories[memoryIndex].indexType()) >=
1515 Pages(0x100000000 / PageSize);
1516#else
1517 // On 32-bit platforms we have no more than 2GB memory and the limit for a
1518 // 32-bit base pointer is never a 64-bit value.
1519 bool mem32LimitIs64Bits = false;
1520#endif
1521 return maybeLoadBoundsCheckLimit(memoryIndex,
1522 mem32LimitIs64Bits || isMem64(memoryIndex)
1523 ? MIRType::Int64
1524 : MIRType::Int32);
1525 }
1526
1527 void performBoundsCheck(uint32_t memoryIndex, MDefinition** base,
1528 MWasmLoadInstance* boundsCheckLimit) {
1529 // At the outset, actualBase could be the result of pretty much any integer
1530 // operation, or it could be the load of an integer constant. If its type
1531 // is i32, we may assume the value has a canonical representation for the
1532 // platform, see doc block in MacroAssembler.h.
1533 MDefinition* actualBase = *base;
1534
1535 // Extend an i32 index value to perform a 64-bit bounds check if the memory
1536 // can be 4GB or larger.
1537 bool extendAndWrapIndex =
1538 isMem32(memoryIndex) && boundsCheckLimit->type() == MIRType::Int64;
1539 if (extendAndWrapIndex) {
1540 auto* extended = MWasmExtendU32Index::New(alloc(), actualBase);
1541 curBlock_->add(extended);
1542 actualBase = extended;
1543 }
1544
1545 auto target = memoryIndex == 0 ? MWasmBoundsCheck::Memory0
1546 : MWasmBoundsCheck::Unknown;
1547 auto* ins = MWasmBoundsCheck::New(alloc(), actualBase, boundsCheckLimit,
1548 bytecodeOffset(), target);
1549 curBlock_->add(ins);
1550 actualBase = ins;
1551
1552 // If we're masking, then we update *base to create a dependency chain
1553 // through the masked index. But we will first need to wrap the index
1554 // value if it was extended above.
1555 if (JitOptions.spectreIndexMasking) {
1556 if (extendAndWrapIndex) {
1557 auto* wrapped = MWasmWrapU32Index::New(alloc(), actualBase);
1558 curBlock_->add(wrapped);
1559 actualBase = wrapped;
1560 }
1561 *base = actualBase;
1562 }
1563 }
1564
1565 // Perform all necessary checking before a wasm heap access, based on the
1566 // attributes of the access and base pointer.
1567 //
1568 // For 64-bit indices on platforms that are limited to indices that fit into
1569 // 32 bits (all 32-bit platforms and mips64), this returns a bounds-checked
1570 // `base` that has type Int32. Lowering code depends on this and will assert
1571 // that the base has this type. See the end of this function.
1572
1573 void checkOffsetAndAlignmentAndBounds(MemoryAccessDesc* access,
1574 MDefinition** base) {
1575 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"
, 1575); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 1575; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1576 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"
, 1576); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1576; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1577
1578 // Attempt to fold a constant base pointer into the offset so as to simplify
1579 // the addressing expression. This may update *base.
1580 foldConstantPointer(access, base);
1581
1582 // Determine whether an alignment check is needed and whether the offset
1583 // must be checked too.
1584 bool mustAddOffsetForAlignmentCheck = false;
1585 bool alignmentCheck =
1586 needAlignmentCheck(access, *base, &mustAddOffsetForAlignmentCheck);
1587
1588 // If bounds checking or alignment checking requires it, compute the
1589 // effective address: add the offset into the pointer and trap on overflow.
1590 // This may update *base.
1591 maybeComputeEffectiveAddress(access, base, mustAddOffsetForAlignmentCheck);
1592
1593 // Emit the alignment check if necessary; it traps if it fails.
1594 if (alignmentCheck) {
1595 curBlock_->add(MWasmAlignmentCheck::New(
1596 alloc(), *base, access->byteSize(), bytecodeOffset()));
1597 }
1598
1599 // Emit the bounds check if necessary; it traps if it fails. This may
1600 // update *base.
1601 MWasmLoadInstance* boundsCheckLimit =
1602 needBoundsCheck(access->memoryIndex());
1603 if (boundsCheckLimit) {
1604 performBoundsCheck(access->memoryIndex(), base, boundsCheckLimit);
1605 }
1606
1607#ifndef JS_64BIT1
1608 if (isMem64(access->memoryIndex())) {
1609 // We must have had an explicit bounds check (or one was elided if it was
1610 // proved redundant), and on 32-bit systems the index will for sure fit in
1611 // 32 bits: the max memory is 2GB. So chop the index down to 32-bit to
1612 // simplify the back-end.
1613 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"
, 1613); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(*base)->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 1613; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1614 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"
, 1614); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.hugeMemoryEnabled(access->memoryIndex())"
")"); do { *((volatile int*)__null) = 1614; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1615 auto* chopped = MWasmWrapU32Index::New(alloc(), *base);
1616 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"
, 1616); AnnotateMozCrashReason("MOZ_ASSERT" "(" "chopped->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1616; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1617 curBlock_->add(chopped);
1618 *base = chopped;
1619 }
1620#endif
1621 }
1622
1623 bool isSmallerAccessForI64(ValType result, const MemoryAccessDesc* access) {
1624 if (result == ValType::I64 && access->byteSize() <= 4) {
1625 // These smaller accesses should all be zero-extending.
1626 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"
, 1626); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isSignedIntType(access->type())"
")"); do { *((volatile int*)__null) = 1626; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1627 return true;
1628 }
1629 return false;
1630 }
1631
1632 public:
1633 bool isMem32(uint32_t memoryIndex) {
1634 return codeMeta_.memories[memoryIndex].indexType() == IndexType::I32;
1635 }
1636 bool isMem64(uint32_t memoryIndex) {
1637 return codeMeta_.memories[memoryIndex].indexType() == IndexType::I64;
1638 }
1639 bool hugeMemoryEnabled(uint32_t memoryIndex) {
1640 return codeMeta_.hugeMemoryEnabled(memoryIndex);
1641 }
1642
1643 // Add the offset into the pointer to yield the EA; trap on overflow. Clears
1644 // the offset on the memory access as a result.
1645 MDefinition* computeEffectiveAddress(MDefinition* base,
1646 MemoryAccessDesc* access) {
1647 if (inDeadCode()) {
1648 return nullptr;
1649 }
1650 uint64_t offset = access->offset64();
1651 if (offset == 0) {
1652 return base;
1653 }
1654 auto* ins = MWasmAddOffset::New(alloc(), base, offset, bytecodeOffset());
1655 curBlock_->add(ins);
1656 access->clearOffset();
1657 return ins;
1658 }
1659
1660 MDefinition* load(MDefinition* base, MemoryAccessDesc* access,
1661 ValType result) {
1662 if (inDeadCode()) {
1663 return nullptr;
1664 }
1665
1666 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1667 MInstruction* load = nullptr;
1668 if (codeMeta_.isAsmJS()) {
1669 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"
, 1669); AnnotateMozCrashReason("MOZ_ASSERT" "(" "access->offset64() == 0"
")"); do { *((volatile int*)__null) = 1669; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1670 MWasmLoadInstance* boundsCheckLimit =
1671 maybeLoadBoundsCheckLimit(access->memoryIndex(), MIRType::Int32);
1672 load = MAsmJSLoadHeap::New(alloc(), memoryBase, base, boundsCheckLimit,
1673 access->type());
1674 } else {
1675 checkOffsetAndAlignmentAndBounds(access, &base);
1676#ifndef JS_64BIT1
1677 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"
, 1677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1677; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1678#endif
1679 load = MWasmLoad::New(alloc(), memoryBase, base, *access,
1680 result.toMIRType());
1681 }
1682 if (!load) {
1683 return nullptr;
1684 }
1685 curBlock_->add(load);
1686 return load;
1687 }
1688
1689 void store(MDefinition* base, MemoryAccessDesc* access, MDefinition* v) {
1690 if (inDeadCode()) {
1691 return;
1692 }
1693
1694 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1695 MInstruction* store = nullptr;
1696 if (codeMeta_.isAsmJS()) {
1697 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"
, 1697); AnnotateMozCrashReason("MOZ_ASSERT" "(" "access->offset64() == 0"
")"); do { *((volatile int*)__null) = 1697; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1698 MWasmLoadInstance* boundsCheckLimit =
1699 maybeLoadBoundsCheckLimit(access->memoryIndex(), MIRType::Int32);
1700 store = MAsmJSStoreHeap::New(alloc(), memoryBase, base, boundsCheckLimit,
1701 access->type(), v);
1702 } else {
1703 checkOffsetAndAlignmentAndBounds(access, &base);
1704#ifndef JS_64BIT1
1705 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"
, 1705); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1705; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1706#endif
1707 store = MWasmStore::New(alloc(), memoryBase, base, *access, v);
1708 }
1709 if (!store) {
1710 return;
1711 }
1712 curBlock_->add(store);
1713 }
1714
1715 MDefinition* atomicCompareExchangeHeap(MDefinition* base,
1716 MemoryAccessDesc* access,
1717 ValType result, MDefinition* oldv,
1718 MDefinition* newv) {
1719 if (inDeadCode()) {
1720 return nullptr;
1721 }
1722
1723 checkOffsetAndAlignmentAndBounds(access, &base);
1724#ifndef JS_64BIT1
1725 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"
, 1725); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1725; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1726#endif
1727
1728 if (isSmallerAccessForI64(result, access)) {
1729 auto* cvtOldv =
1730 MWrapInt64ToInt32::New(alloc(), oldv, /*bottomHalf=*/true);
1731 curBlock_->add(cvtOldv);
1732 oldv = cvtOldv;
1733
1734 auto* cvtNewv =
1735 MWrapInt64ToInt32::New(alloc(), newv, /*bottomHalf=*/true);
1736 curBlock_->add(cvtNewv);
1737 newv = cvtNewv;
1738 }
1739
1740 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1741 MInstruction* cas = MWasmCompareExchangeHeap::New(
1742 alloc(), bytecodeOffset(), memoryBase, base, *access, oldv, newv,
1743 instancePointer_);
1744 if (!cas) {
1745 return nullptr;
1746 }
1747 curBlock_->add(cas);
1748
1749 if (isSmallerAccessForI64(result, access)) {
1750 cas = MExtendInt32ToInt64::New(alloc(), cas, true);
1751 curBlock_->add(cas);
1752 }
1753
1754 return cas;
1755 }
1756
1757 MDefinition* atomicExchangeHeap(MDefinition* base, MemoryAccessDesc* access,
1758 ValType result, MDefinition* value) {
1759 if (inDeadCode()) {
1760 return nullptr;
1761 }
1762
1763 checkOffsetAndAlignmentAndBounds(access, &base);
1764#ifndef JS_64BIT1
1765 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"
, 1765); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1765; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1766#endif
1767
1768 if (isSmallerAccessForI64(result, access)) {
1769 auto* cvtValue =
1770 MWrapInt64ToInt32::New(alloc(), value, /*bottomHalf=*/true);
1771 curBlock_->add(cvtValue);
1772 value = cvtValue;
1773 }
1774
1775 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1776 MInstruction* xchg =
1777 MWasmAtomicExchangeHeap::New(alloc(), bytecodeOffset(), memoryBase,
1778 base, *access, value, instancePointer_);
1779 if (!xchg) {
1780 return nullptr;
1781 }
1782 curBlock_->add(xchg);
1783
1784 if (isSmallerAccessForI64(result, access)) {
1785 xchg = MExtendInt32ToInt64::New(alloc(), xchg, true);
1786 curBlock_->add(xchg);
1787 }
1788
1789 return xchg;
1790 }
1791
1792 MDefinition* atomicBinopHeap(AtomicOp op, MDefinition* base,
1793 MemoryAccessDesc* access, ValType result,
1794 MDefinition* value) {
1795 if (inDeadCode()) {
1796 return nullptr;
1797 }
1798
1799 checkOffsetAndAlignmentAndBounds(access, &base);
1800#ifndef JS_64BIT1
1801 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"
, 1801); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1801; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1802#endif
1803
1804 if (isSmallerAccessForI64(result, access)) {
1805 auto* cvtValue =
1806 MWrapInt64ToInt32::New(alloc(), value, /*bottomHalf=*/true);
1807 curBlock_->add(cvtValue);
1808 value = cvtValue;
1809 }
1810
1811 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1812 MInstruction* binop =
1813 MWasmAtomicBinopHeap::New(alloc(), bytecodeOffset(), op, memoryBase,
1814 base, *access, value, instancePointer_);
1815 if (!binop) {
1816 return nullptr;
1817 }
1818 curBlock_->add(binop);
1819
1820 if (isSmallerAccessForI64(result, access)) {
1821 binop = MExtendInt32ToInt64::New(alloc(), binop, true);
1822 curBlock_->add(binop);
1823 }
1824
1825 return binop;
1826 }
1827
1828#ifdef ENABLE_WASM_SIMD1
1829 MDefinition* loadSplatSimd128(Scalar::Type viewType,
1830 const LinearMemoryAddress<MDefinition*>& addr,
1831 wasm::SimdOp splatOp) {
1832 if (inDeadCode()) {
1833 return nullptr;
1834 }
1835
1836 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
1837 bytecodeIfNotAsmJS(),
1838 hugeMemoryEnabled(addr.memoryIndex));
1839
1840 // Generate better code (on x86)
1841 // If AVX2 is enabled, more broadcast operators are available.
1842 if (viewType == Scalar::Float64
1843# if defined(JS_CODEGEN_X641) || defined(JS_CODEGEN_X86)
1844 || (js::jit::CPUInfo::IsAVX2Present() &&
1845 (viewType == Scalar::Uint8 || viewType == Scalar::Uint16 ||
1846 viewType == Scalar::Float32))
1847# endif
1848 ) {
1849 access.setSplatSimd128Load();
1850 return load(addr.base, &access, ValType::V128);
1851 }
1852
1853 ValType resultType = ValType::I32;
1854 if (viewType == Scalar::Float32) {
1855 resultType = ValType::F32;
1856 splatOp = wasm::SimdOp::F32x4Splat;
1857 }
1858 auto* scalar = load(addr.base, &access, resultType);
1859 if (!inDeadCode() && !scalar) {
1860 return nullptr;
1861 }
1862 return scalarToSimd128(scalar, splatOp);
1863 }
1864
1865 MDefinition* loadExtendSimd128(const LinearMemoryAddress<MDefinition*>& addr,
1866 wasm::SimdOp op) {
1867 if (inDeadCode()) {
1868 return nullptr;
1869 }
1870
1871 // Generate better code (on x86) by loading as a double with an
1872 // operation that sign extends directly.
1873 MemoryAccessDesc access(addr.memoryIndex, Scalar::Float64, addr.align,
1874 addr.offset, bytecodeIfNotAsmJS(),
1875 hugeMemoryEnabled(addr.memoryIndex));
1876 access.setWidenSimd128Load(op);
1877 return load(addr.base, &access, ValType::V128);
1878 }
1879
1880 MDefinition* loadZeroSimd128(Scalar::Type viewType, size_t numBytes,
1881 const LinearMemoryAddress<MDefinition*>& addr) {
1882 if (inDeadCode()) {
1883 return nullptr;
1884 }
1885
1886 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
1887 bytecodeIfNotAsmJS(),
1888 hugeMemoryEnabled(addr.memoryIndex));
1889 access.setZeroExtendSimd128Load();
1890 return load(addr.base, &access, ValType::V128);
1891 }
1892
1893 MDefinition* loadLaneSimd128(uint32_t laneSize,
1894 const LinearMemoryAddress<MDefinition*>& addr,
1895 uint32_t laneIndex, MDefinition* src) {
1896 if (inDeadCode()) {
1897 return nullptr;
1898 }
1899
1900 MemoryAccessDesc access(addr.memoryIndex, Scalar::Simd128, addr.align,
1901 addr.offset, bytecodeIfNotAsmJS(),
1902 hugeMemoryEnabled(addr.memoryIndex));
1903 MDefinition* memoryBase = maybeLoadMemoryBase(access.memoryIndex());
1904 MDefinition* base = addr.base;
1905 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"
, 1905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1905; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1906 checkOffsetAndAlignmentAndBounds(&access, &base);
1907# ifndef JS_64BIT1
1908 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"
, 1908); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1908; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1909# endif
1910 MInstruction* load = MWasmLoadLaneSimd128::New(
1911 alloc(), memoryBase, base, access, laneSize, laneIndex, src);
1912 if (!load) {
1913 return nullptr;
1914 }
1915 curBlock_->add(load);
1916 return load;
1917 }
1918
1919 void storeLaneSimd128(uint32_t laneSize,
1920 const LinearMemoryAddress<MDefinition*>& addr,
1921 uint32_t laneIndex, MDefinition* src) {
1922 if (inDeadCode()) {
1923 return;
1924 }
1925 MemoryAccessDesc access(addr.memoryIndex, Scalar::Simd128, addr.align,
1926 addr.offset, bytecodeIfNotAsmJS(),
1927 hugeMemoryEnabled(addr.memoryIndex));
1928 MDefinition* memoryBase = maybeLoadMemoryBase(access.memoryIndex());
1929 MDefinition* base = addr.base;
1930 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"
, 1930); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!codeMeta_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1930; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1931 checkOffsetAndAlignmentAndBounds(&access, &base);
1932# ifndef JS_64BIT1
1933 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"
, 1933); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1933; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1934# endif
1935 MInstruction* store = MWasmStoreLaneSimd128::New(
1936 alloc(), memoryBase, base, access, laneSize, laneIndex, src);
1937 if (!store) {
1938 return;
1939 }
1940 curBlock_->add(store);
1941 }
1942#endif // ENABLE_WASM_SIMD
1943
1944 /************************************************ Global variable accesses */
1945
1946 MDefinition* loadGlobalVar(unsigned instanceDataOffset, bool isConst,
1947 bool isIndirect, MIRType type) {
1948 if (inDeadCode()) {
1949 return nullptr;
1950 }
1951
1952 MInstruction* load;
1953 if (isIndirect) {
1954 // Pull a pointer to the value out of Instance::globalArea, then
1955 // load from that pointer. Note that the pointer is immutable
1956 // even though the value it points at may change, hence the use of
1957 // |true| for the first node's |isConst| value, irrespective of
1958 // the |isConst| formal parameter to this method. The latter
1959 // applies to the denoted value as a whole.
1960 auto* cellPtr = MWasmLoadInstanceDataField::New(
1961 alloc(), MIRType::Pointer, instanceDataOffset,
1962 /*isConst=*/true, instancePointer_);
1963 curBlock_->add(cellPtr);
1964 load = MWasmLoadGlobalCell::New(alloc(), type, cellPtr);
1965 } else {
1966 // Pull the value directly out of Instance::globalArea.
1967 load = MWasmLoadInstanceDataField::New(alloc(), type, instanceDataOffset,
1968 isConst, instancePointer_);
1969 }
1970 curBlock_->add(load);
1971 return load;
1972 }
1973
1974 [[nodiscard]] bool storeGlobalVar(uint32_t lineOrBytecode,
1975 uint32_t instanceDataOffset,
1976 bool isIndirect, MDefinition* v) {
1977 if (inDeadCode()) {
1978 return true;
1979 }
1980
1981 if (isIndirect) {
1982 // Pull a pointer to the value out of Instance::globalArea, then
1983 // store through that pointer.
1984 auto* valueAddr = MWasmLoadInstanceDataField::New(
1985 alloc(), MIRType::Pointer, instanceDataOffset,
1986 /*isConst=*/true, instancePointer_);
1987 curBlock_->add(valueAddr);
1988
1989 // Handle a store to a ref-typed field specially
1990 if (v->type() == MIRType::WasmAnyRef) {
1991 // Load the previous value for the post-write barrier
1992 auto* prevValue =
1993 MWasmLoadGlobalCell::New(alloc(), MIRType::WasmAnyRef, valueAddr);
1994 curBlock_->add(prevValue);
1995
1996 // Store the new value
1997 auto* store =
1998 MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
1999 /*valueOffset=*/0, v, AliasSet::WasmGlobalCell,
2000 WasmPreBarrierKind::Normal);
2001 curBlock_->add(store);
2002
2003 // Call the post-write barrier
2004 return postBarrierPrecise(lineOrBytecode, valueAddr, prevValue);
2005 }
2006
2007 auto* store = MWasmStoreGlobalCell::New(alloc(), v, valueAddr);
2008 curBlock_->add(store);
2009 return true;
2010 }
2011 // Or else store the value directly in Instance::globalArea.
2012
2013 // Handle a store to a ref-typed field specially
2014 if (v->type() == MIRType::WasmAnyRef) {
2015 // Compute the address of the ref-typed global
2016 auto* valueAddr = MWasmDerivedPointer::New(
2017 alloc(), instancePointer_,
2018 wasm::Instance::offsetInData(instanceDataOffset));
2019 curBlock_->add(valueAddr);
2020
2021 // Load the previous value for the post-write barrier
2022 auto* prevValue =
2023 MWasmLoadGlobalCell::New(alloc(), MIRType::WasmAnyRef, valueAddr);
2024 curBlock_->add(prevValue);
2025
2026 // Store the new value
2027 auto* store =
2028 MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
2029 /*valueOffset=*/0, v, AliasSet::WasmInstanceData,
2030 WasmPreBarrierKind::Normal);
2031 curBlock_->add(store);
2032
2033 // Call the post-write barrier
2034 return postBarrierPrecise(lineOrBytecode, valueAddr, prevValue);
2035 }
2036
2037 auto* store = MWasmStoreInstanceDataField::New(alloc(), instanceDataOffset,
2038 v, instancePointer_);
2039 curBlock_->add(store);
2040 return true;
2041 }
2042
2043 // Load the slot on the instance where the result of `ref.func` is cached.
2044 // This may be null if a function reference for this function has not been
2045 // asked for yet.
2046 MDefinition* loadCachedRefFunc(uint32_t funcIndex) {
2047 uint32_t exportedFuncIndex = codeMeta().findFuncExportIndex(funcIndex);
2048 MWasmLoadInstanceDataField* refFunc = MWasmLoadInstanceDataField::New(
2049 alloc(), MIRType::WasmAnyRef,
2050 codeMeta_.offsetOfFuncExportInstanceData(exportedFuncIndex) +
2051 offsetof(FuncExportInstanceData, func)__builtin_offsetof(FuncExportInstanceData, func),
2052 true, instancePointer_);
2053 curBlock_->add(refFunc);
2054 return refFunc;
2055 }
2056
2057 MDefinition* loadTableField(uint32_t tableIndex, unsigned fieldOffset,
2058 MIRType type) {
2059 uint32_t instanceDataOffset = wasm::Instance::offsetInData(
2060 codeMeta_.offsetOfTableInstanceData(tableIndex) + fieldOffset);
2061 auto* load =
2062 MWasmLoadInstance::New(alloc(), instancePointer_, instanceDataOffset,
2063 type, AliasSet::Load(AliasSet::WasmTableMeta));
2064 curBlock_->add(load);
2065 return load;
2066 }
2067
2068 MDefinition* loadTableLength(uint32_t tableIndex) {
2069 return loadTableField(tableIndex, offsetof(TableInstanceData, length)__builtin_offsetof(TableInstanceData, length),
2070 MIRType::Int32);
2071 }
2072
2073 MDefinition* loadTableElements(uint32_t tableIndex) {
2074 return loadTableField(tableIndex, offsetof(TableInstanceData, elements)__builtin_offsetof(TableInstanceData, elements),
2075 MIRType::Pointer);
2076 }
2077
2078 MDefinition* tableIndexToI32(IndexType indexType, MDefinition* index) {
2079 switch (indexType) {
2080 case IndexType::I32:
2081 return index;
2082 case IndexType::I64:
2083 auto* clamp = MWasmClampTable64Index::New(alloc(), index);
2084 if (!clamp) {
2085 return nullptr;
2086 }
2087 curBlock_->add(clamp);
2088 return clamp;
2089 }
2090 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"
, 2090); AnnotateMozCrashReason("MOZ_CRASH(" "unknown index type"
")"); do { *((volatile int*)__null) = 2090; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2091 }
2092
2093 MDefinition* tableGetAnyRef(uint32_t tableIndex, MDefinition* index) {
2094 // Load the table length and perform a bounds check with spectre index
2095 // masking
2096 auto* length = loadTableLength(tableIndex);
2097 auto* check = MWasmBoundsCheck::New(
2098 alloc(), index, length, bytecodeOffset(), MWasmBoundsCheck::Unknown);
2099 curBlock_->add(check);
2100 if (JitOptions.spectreIndexMasking) {
2101 index = check;
2102 }
2103
2104 // Load the table elements and load the element
2105 auto* elements = loadTableElements(tableIndex);
2106 auto* element = MWasmLoadTableElement::New(alloc(), elements, index);
2107 curBlock_->add(element);
2108 return element;
2109 }
2110
2111 [[nodiscard]] bool tableSetAnyRef(uint32_t tableIndex, MDefinition* index,
2112 MDefinition* value,
2113 uint32_t lineOrBytecode) {
2114 // Load the table length and perform a bounds check with spectre index
2115 // masking
2116 auto* length = loadTableLength(tableIndex);
2117 auto* check = MWasmBoundsCheck::New(
2118 alloc(), index, length, bytecodeOffset(), MWasmBoundsCheck::Unknown);
2119 curBlock_->add(check);
2120 if (JitOptions.spectreIndexMasking) {
2121 index = check;
2122 }
2123
2124 // Load the table elements
2125 auto* elements = loadTableElements(tableIndex);
2126
2127 // Load the previous value
2128 auto* prevValue = MWasmLoadTableElement::New(alloc(), elements, index);
2129 curBlock_->add(prevValue);
2130
2131 // Compute the value's location for the post barrier
2132 auto* loc =
2133 MWasmDerivedIndexPointer::New(alloc(), elements, index, ScalePointer);
2134 curBlock_->add(loc);
2135
2136 // Store the new value
2137 auto* store = MWasmStoreRef::New(
2138 alloc(), instancePointer_, loc, /*valueOffset=*/0, value,
2139 AliasSet::WasmTableElement, WasmPreBarrierKind::Normal);
2140 curBlock_->add(store);
2141
2142 // Perform the post barrier
2143 return postBarrierPrecise(lineOrBytecode, loc, prevValue);
2144 }
2145
2146 void addInterruptCheck() {
2147 if (inDeadCode()) {
2148 return;
2149 }
2150 curBlock_->add(
2151 MWasmInterruptCheck::New(alloc(), instancePointer_, bytecodeOffset()));
2152 }
2153
2154 // Perform a post-write barrier to update the generational store buffer. This
2155 // version will remove a previous store buffer entry if it is no longer
2156 // needed.
2157 [[nodiscard]] bool postBarrierPrecise(uint32_t lineOrBytecode,
2158 MDefinition* valueAddr,
2159 MDefinition* value) {
2160 return emitInstanceCall2(lineOrBytecode, SASigPostBarrierPrecise, valueAddr,
2161 value);
2162 }
2163
2164 // Perform a post-write barrier to update the generational store buffer. This
2165 // version will remove a previous store buffer entry if it is no longer
2166 // needed.
2167 [[nodiscard]] bool postBarrierPreciseWithOffset(uint32_t lineOrBytecode,
2168 MDefinition* valueBase,
2169 uint32_t valueOffset,
2170 MDefinition* value) {
2171 MDefinition* valueOffsetDef = constantI32(int32_t(valueOffset));
2172 if (!valueOffsetDef) {
2173 return false;
2174 }
2175 return emitInstanceCall3(lineOrBytecode, SASigPostBarrierPreciseWithOffset,
2176 valueBase, valueOffsetDef, value);
2177 }
2178
2179 // Perform a post-write barrier to update the generational store buffer. This
2180 // version is the most efficient and only requires the address to store the
2181 // value and the new value. It does not remove a previous store buffer entry
2182 // if it is no longer needed, you must use a precise post-write barrier for
2183 // that.
2184 [[nodiscard]] bool postBarrierImmediate(uint32_t lineOrBytecode,
2185 MDefinition* object,
2186 MDefinition* valueBase,
2187 uint32_t valueOffset,
2188 MDefinition* newValue) {
2189 auto* barrier = MWasmPostWriteBarrierImmediate::New(
2190 alloc(), instancePointer_, object, valueBase, valueOffset, newValue);
2191 if (!barrier) {
2192 return false;
2193 }
2194 curBlock_->add(barrier);
2195 return true;
2196 }
2197
2198 [[nodiscard]] bool postBarrierIndex(uint32_t lineOrBytecode,
2199 MDefinition* object,
2200 MDefinition* valueBase,
2201 MDefinition* index, uint32_t scale,
2202 MDefinition* newValue) {
2203 auto* barrier = MWasmPostWriteBarrierIndex::New(
2204 alloc(), instancePointer_, object, valueBase, index, scale, newValue);
2205 if (!barrier) {
2206 return false;
2207 }
2208 curBlock_->add(barrier);
2209 return true;
2210 }
2211
2212 /***************************************************************** Calls */
2213
2214 // The IonMonkey backend maintains a single stack offset (from the stack
2215 // pointer to the base of the frame) by adding the total amount of spill
2216 // space required plus the maximum stack required for argument passing.
2217 // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must
2218 // manually accumulate, for the entire function, the maximum required stack
2219 // space for argument passing. (This is passed to the CodeGenerator via
2220 // MIRGenerator::maxWasmStackArgBytes.) This is just be the maximum of the
2221 // stack space required for each individual call (as determined by the call
2222 // ABI).
2223
2224 // Operations that modify a CallCompileState.
2225
2226 [[nodiscard]] bool passInstance(MIRType instanceType,
2227 CallCompileState* args) {
2228 if (inDeadCode()) {
2229 return true;
2230 }
2231
2232 // Should only pass an instance once. And it must be a non-GC pointer.
2233 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"
, 2233); AnnotateMozCrashReason("MOZ_ASSERT" "(" "args->instanceArg_ == ABIArg()"
")"); do { *((volatile int*)__null) = 2233; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2234 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"
, 2234); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instanceType == MIRType::Pointer"
")"); do { *((volatile int*)__null) = 2234; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2235 args->instanceArg_ = args->abi_.next(MIRType::Pointer);
2236 return true;
2237 }
2238
2239 // Do not call this directly. Call one of the passArg() variants instead.
2240 [[nodiscard]] bool passArgWorker(MDefinition* argDef, MIRType type,
2241 CallCompileState* call) {
2242 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"
, 2242); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argDef->type() == type"
")"); do { *((volatile int*)__null) = 2242; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2243
2244 ABIArg arg = call->abi_.next(type);
2245 switch (arg.kind()) {
2246#ifdef JS_CODEGEN_REGISTER_PAIR
2247 case ABIArg::GPR_PAIR: {
2248 auto mirLow =
2249 MWrapInt64ToInt32::New(alloc(), argDef, /* bottomHalf = */ true);
2250 curBlock_->add(mirLow);
2251 auto mirHigh =
2252 MWrapInt64ToInt32::New(alloc(), argDef, /* bottomHalf = */ false);
2253 curBlock_->add(mirHigh);
2254 return call->regArgs_.append(
2255 MWasmCallBase::Arg(AnyRegister(arg.gpr64().low), mirLow)) &&
2256 call->regArgs_.append(
2257 MWasmCallBase::Arg(AnyRegister(arg.gpr64().high), mirHigh));
2258 }
2259#endif
2260 case ABIArg::GPR:
2261 case ABIArg::FPU:
2262 return call->regArgs_.append(MWasmCallBase::Arg(arg.reg(), argDef));
2263 case ABIArg::Stack: {
2264 auto* mir =
2265 MWasmStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
2266 curBlock_->add(mir);
2267 return true;
2268 }
2269 case ABIArg::Uninitialized:
2270 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"
, 2270); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Uninitialized ABIArg kind" ")");
do { *((volatile int*)__null) = 2270; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2271 }
2272 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"
, 2272); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown ABIArg kind."
")"); do { *((volatile int*)__null) = 2272; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2273 }
2274
2275 template <typename VecT>
2276 [[nodiscard]] bool passArgs(const DefVector& argDefs, const VecT& types,
2277 CallCompileState* call) {
2278 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"
, 2278); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argDefs.length() == types.length()"
")"); do { *((volatile int*)__null) = 2278; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2279 for (uint32_t i = 0; i < argDefs.length(); i++) {
2280 MDefinition* def = argDefs[i];
2281 ValType type = types[i];
2282 if (!passArg(def, type, call)) {
2283 return false;
2284 }
2285 }
2286 return true;
2287 }
2288
2289 [[nodiscard]] bool passArg(MDefinition* argDef, MIRType type,
2290 CallCompileState* call) {
2291 if (inDeadCode()) {
2292 return true;
2293 }
2294 return passArgWorker(argDef, type, call);
2295 }
2296
2297 [[nodiscard]] bool passArg(MDefinition* argDef, ValType type,
2298 CallCompileState* call) {
2299 if (inDeadCode()) {
2300 return true;
2301 }
2302 return passArgWorker(argDef, type.toMIRType(), call);
2303 }
2304
2305 void markReturnCall(CallCompileState* call) { call->returnCall = true; }
2306
2307 // If the call returns results on the stack, prepare a stack area to receive
2308 // them, and pass the address of the stack area to the callee as an additional
2309 // argument.
2310 [[nodiscard]] bool passStackResultAreaCallArg(const ResultType& resultType,
2311 CallCompileState* call) {
2312 if (inDeadCode()) {
2313 return true;
2314 }
2315 ABIResultIter iter(resultType);
2316 while (!iter.done() && iter.cur().inRegister()) {
2317 iter.next();
2318 }
2319 if (iter.done()) {
2320 // No stack results.
2321 return true;
2322 }
2323
2324 auto* stackResultArea = MWasmStackResultArea::New(alloc());
2325 if (!stackResultArea) {
2326 return false;
2327 }
2328 if (!stackResultArea->init(alloc(), iter.remaining())) {
2329 return false;
2330 }
2331 for (uint32_t base = iter.index(); !iter.done(); iter.next()) {
2332 MWasmStackResultArea::StackResult loc(iter.cur().stackOffset(),
2333 iter.cur().type().toMIRType());
2334 stackResultArea->initResult(iter.index() - base, loc);
2335 }
2336 curBlock_->add(stackResultArea);
2337 MDefinition* def = call->returnCall ? (MDefinition*)stackResultPointer_
2338 : (MDefinition*)stackResultArea;
2339 if (!passArg(def, MIRType::StackResults, call)) {
2340 return false;
2341 }
2342 call->stackResultArea_ = stackResultArea;
2343 return true;
2344 }
2345
2346 [[nodiscard]] bool finishCall(CallCompileState* call) {
2347 if (inDeadCode()) {
2348 return true;
2349 }
2350
2351 if (!call->regArgs_.append(
2352 MWasmCallBase::Arg(AnyRegister(InstanceReg), instancePointer_))) {
2353 return false;
2354 }
2355
2356 uint32_t stackBytes = call->abi_.stackBytesConsumedSoFar();
2357
2358 maxStackArgBytes_ = std::max(maxStackArgBytes_, stackBytes);
2359 return true;
2360 }
2361
2362 // Wrappers for creating various kinds of calls.
2363
2364 [[nodiscard]] bool collectUnaryCallResult(MIRType type,
2365 MDefinition** result) {
2366 MInstruction* def;
2367 switch (type) {
2368 case MIRType::Int32:
2369 def = MWasmRegisterResult::New(alloc(), MIRType::Int32, ReturnReg);
2370 break;
2371 case MIRType::Int64:
2372 def = MWasmRegister64Result::New(alloc(), ReturnReg64);
2373 break;
2374 case MIRType::Float32:
2375 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnFloat32Reg);
2376 break;
2377 case MIRType::Double:
2378 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnDoubleReg);
2379 break;
2380#ifdef ENABLE_WASM_SIMD1
2381 case MIRType::Simd128:
2382 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnSimd128Reg);
2383 break;
2384#endif
2385 case MIRType::WasmAnyRef:
2386 def = MWasmRegisterResult::New(alloc(), MIRType::WasmAnyRef, ReturnReg);
2387 break;
2388 default:
2389 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"
, 2389); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected MIRType result for builtin call"
")"); do { *((volatile int*)__null) = 2389; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2390 }
2391
2392 if (!def) {
2393 return false;
2394 }
2395
2396 curBlock_->add(def);
2397 *result = def;
2398
2399 return true;
2400 }
2401
2402 [[nodiscard]] bool collectCallResults(const ResultType& type,
2403 MWasmStackResultArea* stackResultArea,
2404 DefVector* results) {
2405 if (!results->reserve(type.length())) {
2406 return false;
2407 }
2408
2409 // The result iterator goes in the order in which results would be popped
2410 // off; we want the order in which they would be pushed.
2411 ABIResultIter iter(type);
2412 uint32_t stackResultCount = 0;
2413 while (!iter.done()) {
2414 if (iter.cur().onStack()) {
2415 stackResultCount++;
2416 }
2417 iter.next();
2418 }
2419
2420 for (iter.switchToPrev(); !iter.done(); iter.prev()) {
2421 if (!mirGen().ensureBallast()) {
2422 return false;
2423 }
2424 const ABIResult& result = iter.cur();
2425 MInstruction* def;
2426 if (result.inRegister()) {
2427 switch (result.type().kind()) {
2428 case wasm::ValType::I32:
2429 def =
2430 MWasmRegisterResult::New(alloc(), MIRType::Int32, result.gpr());
2431 break;
2432 case wasm::ValType::I64:
2433 def = MWasmRegister64Result::New(alloc(), result.gpr64());
2434 break;
2435 case wasm::ValType::F32:
2436 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Float32,
2437 result.fpr());
2438 break;
2439 case wasm::ValType::F64:
2440 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Double,
2441 result.fpr());
2442 break;
2443 case wasm::ValType::Ref:
2444 def = MWasmRegisterResult::New(alloc(), MIRType::WasmAnyRef,
2445 result.gpr());
2446 break;
2447 case wasm::ValType::V128:
2448#ifdef ENABLE_WASM_SIMD1
2449 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Simd128,
2450 result.fpr());
2451#else
2452 return this->iter().fail("Ion has no SIMD support yet");
2453#endif
2454 }
2455 } else {
2456 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"
, 2456); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultArea"
")"); do { *((volatile int*)__null) = 2456; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2457 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"
, 2457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultCount"
")"); do { *((volatile int*)__null) = 2457; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2458 uint32_t idx = --stackResultCount;
2459 def = MWasmStackResult::New(alloc(), stackResultArea, idx);
2460 }
2461
2462 if (!def) {
2463 return false;
2464 }
2465 curBlock_->add(def);
2466 results->infallibleAppend(def);
2467 }
2468
2469 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"
, 2469); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results->length() == type.length()"
")"); do { *((volatile int*)__null) = 2469; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2470
2471 return true;
2472 }
2473
2474 [[nodiscard]] bool catchableCall(const CallSiteDesc& desc,
2475 const CalleeDesc& callee,
2476 const MWasmCallBase::Args& args,
2477 const ArgTypeVector& argTypes,
2478 MDefinition* indexOrRef = nullptr) {
2479 MWasmCallTryDesc tryDesc;
2480 if (!beginTryCall(&tryDesc)) {
2481 return false;
2482 }
2483
2484 MInstruction* ins;
2485 if (tryDesc.inTry) {
2486 ins = MWasmCallCatchable::New(alloc(), desc, callee, args,
2487 StackArgAreaSizeUnaligned(argTypes),
2488 tryDesc, indexOrRef);
2489 } else {
2490 ins = MWasmCallUncatchable::New(alloc(), desc, callee, args,
2491 StackArgAreaSizeUnaligned(argTypes),
2492 indexOrRef);
2493 }
2494 if (!ins) {
2495 return false;
2496 }
2497 curBlock_->add(ins);
2498
2499 return finishTryCall(&tryDesc);
2500 }
2501
2502 [[nodiscard]]
2503 bool shouldInlineCall(InliningHeuristics::CallKind kind, uint32_t funcIndex) {
2504 // We only support this mode when lazy tiering. This is currently a
2505 // requirement because we need the full module bytecode and function
2506 // definition ranges, which are not available in other modes.
2507 if (compilerEnv().mode() != CompileMode::LazyTiering) {
2508 return false;
2509 }
2510
2511 // We can't inline an imported function.
2512 if (codeMeta().funcIsImport(funcIndex)) {
2513 return false;
2514 }
2515
2516 // We temporarily do not support inlining when there's a try handler
2517 // active.
2518 if (inTryCode()) {
2519 return false;
2520 }
2521
2522 // We do not support inlining a callee which uses tail calls
2523 FeatureUsage funcFeatureUsage = codeMeta().funcDefFeatureUsage(funcIndex);
2524 if (funcFeatureUsage & FeatureUsage::ReturnCall) {
2525 return false;
2526 }
2527
2528 // Ask the heuristics system if we're allowed to inline a function of this
2529 // size and kind at the current inlining depth.
2530 uint32_t inlineeBodySize = codeMeta().funcDefRange(funcIndex).bodyLength;
2531 return codeMeta_.inliningHeuristics.isSmallEnoughToInline(
2532 kind, inliningDepth(), inlineeBodySize);
2533 }
2534
2535 [[nodiscard]]
2536 bool finishInlinedCallDirect(FunctionCompiler& calleeCompiler,
2537 DefVector* results) {
2538 const PendingInlineReturnVector& calleeReturns =
2539 calleeCompiler.pendingInlineReturns_;
2540 const FuncType& calleeFuncType = calleeCompiler.funcType();
2541
2542 // Add the observed features from the inlined function to this function
2543 iter_.addFeatureUsage(calleeCompiler.featureUsage());
2544
2545 // If there were no returns, then we are now in dead code
2546 if (calleeReturns.empty()) {
2547 curBlock_ = nullptr;
2548 return true;
2549 }
2550
2551 // Create a block to join all of the returns from the inlined function
2552 MBasicBlock* lastBlockBeforeCall = curBlock_;
2553 MBasicBlock* joinAfterCall = nullptr;
2554 if (!newBlock(nullptr, &joinAfterCall)) {
2555 return false;
2556 }
2557
2558 // The join block inherits all of the locals state from immediately before
2559 // the inlined call
2560 joinAfterCall->inheritSlots(lastBlockBeforeCall);
2561
2562 // The join block has a phi node for every result of the inlined function
2563 // type. Each phi node has an operand for each of the returns of the
2564 // inlined function.
2565 for (uint32_t i = 0; i < calleeFuncType.results().length(); i++) {
2566 MPhi* phi = MPhi::New(alloc(), calleeFuncType.results()[i].toMIRType());
2567 if (!phi || !phi->reserveLength(calleeReturns.length())) {
2568 return false;
2569 }
2570 joinAfterCall->addPhi(phi);
2571 if (!results->append(phi)) {
2572 return false;
2573 }
2574 }
2575
2576 // Bind every return from the inlined function to go to the join block, and
2577 // add the results for the return to the phi nodes.
2578 for (size_t i = 0; i < calleeReturns.length(); i++) {
2579 const PendingInlineReturn& calleeReturn = calleeReturns[i];
2580
2581 // Setup the predecessor and successor relationship
2582 MBasicBlock* pred = calleeReturn.jump->block();
2583 if (!joinAfterCall->addPredecessorWithoutPhis(pred)) {
2584 return false;
2585 }
2586 calleeReturn.jump->replaceSuccessor(MGoto::TargetIndex, joinAfterCall);
2587
2588 // For each result in this return, add it to the corresponding phi node
2589 for (uint32_t resultIndex = 0;
2590 resultIndex < calleeFuncType.results().length(); resultIndex++) {
2591 MDefinition* result = (*results)[resultIndex];
2592 ((MPhi*)(result))->addInput(calleeReturn.results[resultIndex]);
2593 }
2594 }
2595
2596 // Continue MIR generation starting in the join block
2597 curBlock_ = joinAfterCall;
2598
2599 return true;
2600 }
2601
2602 [[nodiscard]] bool callDirect(const FuncType& funcType, uint32_t funcIndex,
2603 uint32_t lineOrBytecode,
2604 const CallCompileState& call,
2605 DefVector* results) {
2606 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"
, 2606); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2606; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2607
2608 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Func);
2609 ResultType resultType = ResultType::Vector(funcType.results());
2610 auto callee = CalleeDesc::function(funcIndex);
2611 ArgTypeVector args(funcType);
2612
2613 if (!catchableCall(desc, callee, call.regArgs_, args)) {
2614 return false;
2615 }
2616 return collectCallResults(resultType, call.stackResultArea_, results);
2617 }
2618
2619 [[nodiscard]] bool returnCallDirect(const FuncType& funcType,
2620 uint32_t funcIndex,
2621 uint32_t lineOrBytecode,
2622 const CallCompileState& call,
2623 DefVector* results) {
2624 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"
, 2624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2624; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2625
2626 // We do not support tail calls in inlined functions.
2627 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"
, 2627); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!isInlined()"
")"); do { *((volatile int*)__null) = 2627; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2628
2629 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::ReturnFunc);
2630 auto callee = CalleeDesc::function(funcIndex);
2631 ArgTypeVector args(funcType);
2632
2633 auto ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2634 StackArgAreaSizeUnaligned(args), nullptr);
2635 if (!ins) {
2636 return false;
2637 }
2638 curBlock_->end(ins);
2639 curBlock_ = nullptr;
2640 return true;
2641 }
2642
2643 [[nodiscard]] bool returnCallImport(unsigned globalDataOffset,
2644 uint32_t lineOrBytecode,
2645 const CallCompileState& call,
2646 const FuncType& funcType,
2647 DefVector* results) {
2648 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"
, 2648); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2648; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2649
2650 // We do not support tail calls in inlined functions.
2651 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"
, 2651); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!isInlined()"
")"); do { *((volatile int*)__null) = 2651; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2652
2653 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Import);
2654 auto callee = CalleeDesc::import(globalDataOffset);
2655 ArgTypeVector args(funcType);
2656
2657 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2658 StackArgAreaSizeUnaligned(args), nullptr);
2659 if (!ins) {
2660 return false;
2661 }
2662 curBlock_->end(ins);
2663 curBlock_ = nullptr;
2664 return true;
2665 }
2666
2667 [[nodiscard]] bool returnCallIndirect(uint32_t funcTypeIndex,
2668 uint32_t tableIndex, MDefinition* index,
2669 uint32_t lineOrBytecode,
2670 const CallCompileState& call,
2671 DefVector* results) {
2672 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"
, 2672); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2672; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2673
2674 // We do not support tail calls in inlined functions.
2675 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"
, 2675); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!isInlined()"
")"); do { *((volatile int*)__null) = 2675; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2676
2677 const FuncType& funcType = (*codeMeta_.types)[funcTypeIndex].funcType();
2678 CallIndirectId callIndirectId =
2679 CallIndirectId::forFuncType(codeMeta_, funcTypeIndex);
2680
2681 CalleeDesc callee;
2682 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"
, 2682); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() != CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2682; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2683 const TableDesc& table = codeMeta_.tables[tableIndex];
2684 callee =
2685 CalleeDesc::wasmTable(codeMeta_, table, tableIndex, callIndirectId);
2686
2687 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Indirect);
2688 ArgTypeVector args(funcType);
2689
2690 MDefinition* index32 = tableIndexToI32(table.indexType(), index);
2691 if (!index32) {
2692 return false;
2693 }
2694
2695 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2696 StackArgAreaSizeUnaligned(args), index32);
2697 if (!ins) {
2698 return false;
2699 }
2700 curBlock_->end(ins);
2701 curBlock_ = nullptr;
2702 return true;
2703 }
2704
2705 [[nodiscard]] bool callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex,
2706 MDefinition* index, uint32_t lineOrBytecode,
2707 const CallCompileState& call,
2708 DefVector* results) {
2709 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"
, 2709); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2709; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2710
2711 const FuncType& funcType = (*codeMeta_.types)[funcTypeIndex].funcType();
2712 CallIndirectId callIndirectId =
2713 CallIndirectId::forFuncType(codeMeta_, funcTypeIndex);
2714
2715 CalleeDesc callee;
2716 if (codeMeta_.isAsmJS()) {
2717 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"
, 2717); AnnotateMozCrashReason("MOZ_ASSERT" "(" "tableIndex == 0"
")"); do { *((volatile int*)__null) = 2717; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2718 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"
, 2718); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() == CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2718; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2719 uint32_t tableIndex = codeMeta_.asmJSSigToTableIndex[funcTypeIndex];
2720 const TableDesc& table = codeMeta_.tables[tableIndex];
2721 // ensured by asm.js validation
2722 MOZ_ASSERT(table.initialLength() <= UINT32_MAX)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(table.initialLength() <= (4294967295U))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(table.initialLength() <= (4294967295U)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("table.initialLength() <= (4294967295U)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2722); AnnotateMozCrashReason("MOZ_ASSERT" "(" "table.initialLength() <= (4294967295U)"
")"); do { *((volatile int*)__null) = 2722; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2723 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"
, 2723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsPowerOfTwo(table.initialLength())"
")"); do { *((volatile int*)__null) = 2723; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2724
2725 MDefinition* mask = constantI32(int32_t(table.initialLength() - 1));
2726 MBitAnd* maskedIndex = MBitAnd::New(alloc(), index, mask, MIRType::Int32);
2727 curBlock_->add(maskedIndex);
2728
2729 index = maskedIndex;
2730 callee = CalleeDesc::asmJSTable(codeMeta_, tableIndex);
2731 } else {
2732 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"
, 2732); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() != CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2732; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2733 const TableDesc& table = codeMeta_.tables[tableIndex];
2734 callee =
2735 CalleeDesc::wasmTable(codeMeta_, table, tableIndex, callIndirectId);
2736 index = tableIndexToI32(table.indexType(), index);
2737 if (!index) {
2738 return false;
2739 }
2740 }
2741
2742 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Indirect);
2743 ArgTypeVector args(funcType);
2744 ResultType resultType = ResultType::Vector(funcType.results());
2745
2746 if (!catchableCall(desc, callee, call.regArgs_, args, index)) {
2747 return false;
2748 }
2749 return collectCallResults(resultType, call.stackResultArea_, results);
2750 }
2751
2752 [[nodiscard]] bool callImport(unsigned instanceDataOffset,
2753 uint32_t lineOrBytecode,
2754 const CallCompileState& call,
2755 const FuncType& funcType, DefVector* results) {
2756 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"
, 2756); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2756; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2757
2758 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Import);
2759 auto callee = CalleeDesc::import(instanceDataOffset);
2760 ArgTypeVector args(funcType);
2761 ResultType resultType = ResultType::Vector(funcType.results());
2762
2763 if (!catchableCall(desc, callee, call.regArgs_, args)) {
2764 return false;
2765 }
2766 return collectCallResults(resultType, call.stackResultArea_, results);
2767 }
2768
2769 [[nodiscard]] bool builtinCall(const SymbolicAddressSignature& builtin,
2770 uint32_t lineOrBytecode,
2771 const CallCompileState& call,
2772 MDefinition** def) {
2773 if (inDeadCode()) {
2774 *def = nullptr;
2775 return true;
2776 }
2777
2778 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"
, 2778); AnnotateMozCrashReason("MOZ_ASSERT" "(" "builtin.failureMode == FailureMode::Infallible"
")"); do { *((volatile int*)__null) = 2778; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2779
2780 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
2781 auto callee = CalleeDesc::builtin(builtin.identity);
2782 auto* ins = MWasmCallUncatchable::New(alloc(), desc, callee, call.regArgs_,
2783 StackArgAreaSizeUnaligned(builtin));
2784 if (!ins) {
2785 return false;
2786 }
2787
2788 curBlock_->add(ins);
2789
2790 return collectUnaryCallResult(builtin.retType, def);
2791 }
2792
2793 [[nodiscard]] bool builtinInstanceMethodCall(
2794 const SymbolicAddressSignature& builtin, uint32_t lineOrBytecode,
2795 const CallCompileState& call, MDefinition** def = nullptr) {
2796 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"
, 2796); AnnotateMozCrashReason("MOZ_ASSERT" "(" "builtin.retType == MIRType::None"
")"); do { *((volatile int*)__null) = 2796; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2797 if (inDeadCode()) {
2798 if (def) {
2799 *def = nullptr;
2800 }
2801 return true;
2802 }
2803
2804 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
2805 MWasmCallTryDesc tryDesc;
2806 if (!beginTryCall(&tryDesc)) {
2807 return false;
2808 }
2809
2810 MInstruction* ins;
2811 if (tryDesc.inTry) {
2812 ins = MWasmCallCatchable::NewBuiltinInstanceMethodCall(
2813 alloc(), desc, builtin.identity, builtin.failureMode,
2814 call.instanceArg_, call.regArgs_, StackArgAreaSizeUnaligned(builtin),
2815 tryDesc);
2816 } else {
2817 ins = MWasmCallUncatchable::NewBuiltinInstanceMethodCall(
2818 alloc(), desc, builtin.identity, builtin.failureMode,
2819 call.instanceArg_, call.regArgs_, StackArgAreaSizeUnaligned(builtin));
2820 }
2821 if (!ins) {
2822 return false;
2823 }
2824 curBlock_->add(ins);
2825
2826 if (!finishTryCall(&tryDesc)) {
2827 return false;
2828 }
2829
2830 if (!def) {
2831 return true;
2832 }
2833 return collectUnaryCallResult(builtin.retType, def);
2834 }
2835
2836 [[nodiscard]] bool stackSwitch(MDefinition* suspender, MDefinition* fn,
2837 MDefinition* data, StackSwitchKind kind) {
2838 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"
, 2838); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2838; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2839
2840 MInstruction* ins;
2841 switch (kind) {
2842 case StackSwitchKind::SwitchToMain:
2843 ins = MWasmStackSwitchToMain::New(alloc(), suspender, fn, data);
2844 break;
2845 case StackSwitchKind::SwitchToSuspendable:
2846 ins = MWasmStackSwitchToSuspendable::New(alloc(), suspender, fn, data);
2847 break;
2848 case StackSwitchKind::ContinueOnSuspendable:
2849 ins = MWasmStackContinueOnSuspendable::New(alloc(), suspender);
2850 break;
2851 }
2852 if (!ins) {
2853 return false;
2854 }
2855
2856 curBlock_->add(ins);
2857
2858 return true;
2859 }
2860
2861#ifdef ENABLE_WASM_GC1
2862 [[nodiscard]] bool callRef(const FuncType& funcType, MDefinition* ref,
2863 uint32_t lineOrBytecode,
2864 const CallCompileState& call, DefVector* results) {
2865 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"
, 2865); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2865; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2866
2867 CalleeDesc callee = CalleeDesc::wasmFuncRef();
2868
2869 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::FuncRef);
2870 ArgTypeVector args(funcType);
2871 ResultType resultType = ResultType::Vector(funcType.results());
2872
2873 if (!catchableCall(desc, callee, call.regArgs_, args, ref)) {
2874 return false;
2875 }
2876 return collectCallResults(resultType, call.stackResultArea_, results);
2877 }
2878
2879# ifdef ENABLE_WASM_TAIL_CALLS1
2880 [[nodiscard]] bool returnCallRef(const FuncType& funcType, MDefinition* ref,
2881 uint32_t lineOrBytecode,
2882 const CallCompileState& call,
2883 DefVector* results) {
2884 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"
, 2884); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2884; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2885
2886 CalleeDesc callee = CalleeDesc::wasmFuncRef();
2887
2888 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::FuncRef);
2889 ArgTypeVector args(funcType);
2890
2891 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2892 StackArgAreaSizeUnaligned(args), ref);
2893 if (!ins) {
2894 return false;
2895 }
2896 curBlock_->end(ins);
2897 curBlock_ = nullptr;
2898 return true;
2899 }
2900
2901# endif // ENABLE_WASM_TAIL_CALLS
2902
2903#endif // ENABLE_WASM_GC
2904
2905 [[nodiscard]] MDefinition* stringCast(MDefinition* string) {
2906 auto* ins = MWasmTrapIfAnyRefIsNotJSString::New(
2907 alloc(), string, wasm::Trap::BadCast, bytecodeOffset());
2908 if (!ins) {
2909 return ins;
2910 }
2911 curBlock_->add(ins);
2912 return ins;
2913 }
2914
2915 [[nodiscard]] MDefinition* stringTest(MDefinition* string) {
2916 auto* ins = MWasmAnyRefIsJSString::New(alloc(), string);
2917 if (!ins) {
2918 return nullptr;
2919 }
2920 curBlock_->add(ins);
2921 return ins;
2922 }
2923
2924 [[nodiscard]] bool dispatchInlineBuiltinModuleFunc(
2925 const BuiltinModuleFunc& builtinModuleFunc, const DefVector& params) {
2926 BuiltinInlineOp inlineOp = builtinModuleFunc.inlineOp();
2927 MOZ_ASSERT(inlineOp != BuiltinInlineOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inlineOp != BuiltinInlineOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(inlineOp != BuiltinInlineOp::
None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("inlineOp != BuiltinInlineOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2927); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inlineOp != BuiltinInlineOp::None"
")"); do { *((volatile int*)__null) = 2927; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2928 switch (inlineOp) {
2929 case BuiltinInlineOp::StringCast: {
2930 MOZ_ASSERT(params.length() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(params.length() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(params.length() == 1))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("params.length() == 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2930); AnnotateMozCrashReason("MOZ_ASSERT" "(" "params.length() == 1"
")"); do { *((volatile int*)__null) = 2930; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2931 MDefinition* string = params[0];
2932 MDefinition* cast = stringCast(string);
2933 if (!cast) {
2934 return false;
2935 }
2936 iter().setResult(string);
2937 return true;
2938 }
2939 case BuiltinInlineOp::StringTest: {
2940 MOZ_ASSERT(params.length() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(params.length() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(params.length() == 1))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("params.length() == 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2940); AnnotateMozCrashReason("MOZ_ASSERT" "(" "params.length() == 1"
")"); do { *((volatile int*)__null) = 2940; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2941 MDefinition* string = params[0];
2942 MDefinition* test = stringTest(string);
2943 if (!test) {
2944 return false;
2945 }
2946 iter().setResult(test);
2947 return true;
2948 }
2949 case BuiltinInlineOp::None:
2950 case BuiltinInlineOp::Limit:
2951 break;
2952 }
2953 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 2953); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 2953; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
2954 }
2955
2956 [[nodiscard]] bool callBuiltinModuleFunc(
2957 const BuiltinModuleFunc& builtinModuleFunc, const DefVector& params) {
2958 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"
, 2958); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2958; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2959
2960 BuiltinInlineOp inlineOp = builtinModuleFunc.inlineOp();
2961 if (inlineOp != BuiltinInlineOp::None) {
2962 return dispatchInlineBuiltinModuleFunc(builtinModuleFunc, params);
2963 }
2964
2965 // It's almost possible to use FunctionCompiler::emitInstanceCallN here.
2966 // Unfortunately not currently possible though, since ::emitInstanceCallN
2967 // expects an array of arguments along with a size, and that's not what is
2968 // available here. It would be possible if we were prepared to copy
2969 // `builtinModuleFunc->params` into a fixed-sized (16 element?) array, add
2970 // `memoryBase`, and make the call.
2971 const SymbolicAddressSignature& callee = *builtinModuleFunc.sig();
2972
2973 CallCompileState args;
2974 if (!passInstance(callee.argTypes[0], &args) ||
2975 !passArgs(params, builtinModuleFunc.funcType()->args(), &args)) {
2976 return false;
2977 }
2978
2979 if (builtinModuleFunc.usesMemory()) {
2980 if (!passArg(memoryBase(0), MIRType::Pointer, &args)) {
2981 return false;
2982 }
2983 }
2984
2985 if (!finishCall(&args)) {
2986 return false;
2987 }
2988
2989 bool hasResult = !builtinModuleFunc.funcType()->results().empty();
2990 MDefinition* result = nullptr;
2991 MDefinition** resultOutParam = hasResult ? &result : nullptr;
2992 if (!builtinInstanceMethodCall(callee, readBytecodeOffset(), args,
2993 resultOutParam)) {
2994 return false;
2995 }
2996
2997 if (hasResult) {
2998 iter().setResult(result);
2999 }
3000 return true;
3001 }
3002
3003 /*********************************************** Control flow generation */
3004
3005 inline bool inDeadCode() const { return curBlock_ == nullptr; }
3006
3007 [[nodiscard]] bool returnValues(DefVector&& values) {
3008 if (inDeadCode()) {
3009 return true;
3010 }
3011
3012 // If we're inlined into another function, we must accumulate the returns
3013 // so that they can be patched into the caller function.
3014 if (isInlined()) {
3015 MGoto* jump = MGoto::New(alloc());
3016 if (!jump) {
3017 return false;
3018 }
3019 curBlock_->end(jump);
3020 curBlock_ = nullptr;
3021 return pendingInlineReturns_.emplaceBack(
3022 PendingInlineReturn(jump, std::move(values)));
3023 }
3024
3025 if (values.empty()) {
3026 curBlock_->end(MWasmReturnVoid::New(alloc(), instancePointer_));
3027 } else {
3028 ResultType resultType = ResultType::Vector(funcType().results());
3029 ABIResultIter iter(resultType);
3030 // Switch to iterate in FIFO order instead of the default LIFO.
3031 while (!iter.done()) {
3032 iter.next();
3033 }
3034 iter.switchToPrev();
3035 for (uint32_t i = 0; !iter.done(); iter.prev(), i++) {
3036 if (!mirGen().ensureBallast()) {
3037 return false;
3038 }
3039 const ABIResult& result = iter.cur();
3040 if (result.onStack()) {
3041 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"
, 3041); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.remaining() > 1"
")"); do { *((volatile int*)__null) = 3041; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3042 if (result.type().isRefRepr()) {
3043 auto* store = MWasmStoreRef::New(
3044 alloc(), instancePointer_, stackResultPointer_,
3045 result.stackOffset(), values[i], AliasSet::WasmStackResult,
3046 WasmPreBarrierKind::None);
3047 curBlock_->add(store);
3048 } else {
3049 auto* store = MWasmStoreStackResult::New(
3050 alloc(), stackResultPointer_, result.stackOffset(), values[i]);
3051 curBlock_->add(store);
3052 }
3053 } else {
3054 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"
, 3054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.remaining() == 1"
")"); do { *((volatile int*)__null) = 3054; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3055 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"
, 3055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "i + 1 == values.length()"
")"); do { *((volatile int*)__null) = 3055; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3056 curBlock_->end(
3057 MWasmReturn::New(alloc(), values[i], instancePointer_));
3058 }
3059 }
3060 }
3061 curBlock_ = nullptr;
3062 return true;
3063 }
3064
3065 void unreachableTrap() {
3066 if (inDeadCode()) {
3067 return;
3068 }
3069
3070 auto* ins =
3071 MWasmTrap::New(alloc(), wasm::Trap::Unreachable, bytecodeOffset());
3072 curBlock_->end(ins);
3073 curBlock_ = nullptr;
3074 }
3075
3076 private:
3077 static uint32_t numPushed(MBasicBlock* block) {
3078 return block->stackDepth() - block->info().firstStackSlot();
3079 }
3080
3081 public:
3082 [[nodiscard]] bool pushDefs(const DefVector& defs) {
3083 if (inDeadCode()) {
3084 return true;
3085 }
3086 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"
, 3086); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 3086; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3087 if (!curBlock_->ensureHasSlots(defs.length())) {
3088 return false;
3089 }
3090 for (MDefinition* def : defs) {
3091 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"
, 3091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() != MIRType::None"
")"); do { *((volatile int*)__null) = 3091; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3092 curBlock_->push(def);
3093 }
3094 return true;
3095 }
3096
3097 [[nodiscard]] bool popPushedDefs(DefVector* defs) {
3098 size_t n = numPushed(curBlock_);
3099 if (!defs->resizeUninitialized(n)) {
3100 return false;
3101 }
3102 for (; n > 0; n--) {
3103 MDefinition* def = curBlock_->pop();
3104 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"
, 3104); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() != MIRType::Value"
")"); do { *((volatile int*)__null) = 3104; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3105 (*defs)[n - 1] = def;
3106 }
3107 return true;
3108 }
3109
3110 private:
3111 [[nodiscard]] bool addJoinPredecessor(const DefVector& defs,
3112 MBasicBlock** joinPred) {
3113 *joinPred = curBlock_;
3114 if (inDeadCode()) {
3115 return true;
3116 }
3117 return pushDefs(defs);
3118 }
3119
3120 public:
3121 [[nodiscard]] bool branchAndStartThen(MDefinition* cond,
3122 MBasicBlock** elseBlock) {
3123 if (inDeadCode()) {
3124 *elseBlock = nullptr;
3125 } else {
3126 MBasicBlock* thenBlock;
3127 if (!newBlock(curBlock_, &thenBlock)) {
3128 return false;
3129 }
3130 if (!newBlock(curBlock_, elseBlock)) {
3131 return false;
3132 }
3133
3134 curBlock_->end(MTest::New(alloc(), cond, thenBlock, *elseBlock));
3135
3136 curBlock_ = thenBlock;
3137 mirGraph().moveBlockToEnd(curBlock_);
3138 }
3139
3140 return startBlock();
3141 }
3142
3143 [[nodiscard]] bool switchToElse(MBasicBlock* elseBlock,
3144 MBasicBlock** thenJoinPred) {
3145 DefVector values;
3146 if (!finishBlock(&values)) {
3147 return false;
3148 }
3149
3150 if (!elseBlock) {
3151 *thenJoinPred = nullptr;
3152 } else {
3153 if (!addJoinPredecessor(values, thenJoinPred)) {
3154 return false;
3155 }
3156
3157 curBlock_ = elseBlock;
3158 mirGraph().moveBlockToEnd(curBlock_);
3159 }
3160
3161 return startBlock();
3162 }
3163
3164 [[nodiscard]] bool joinIfElse(MBasicBlock* thenJoinPred, DefVector* defs) {
3165 DefVector values;
3166 if (!finishBlock(&values)) {
3167 return false;
3168 }
3169
3170 if (!thenJoinPred && inDeadCode()) {
3171 return true;
3172 }
3173
3174 MBasicBlock* elseJoinPred;
3175 if (!addJoinPredecessor(values, &elseJoinPred)) {
3176 return false;
3177 }
3178
3179 mozilla::Array<MBasicBlock*, 2> blocks;
3180 size_t numJoinPreds = 0;
3181 if (thenJoinPred) {
3182 blocks[numJoinPreds++] = thenJoinPred;
3183 }
3184 if (elseJoinPred) {
3185 blocks[numJoinPreds++] = elseJoinPred;
3186 }
3187
3188 if (numJoinPreds == 0) {
3189 return true;
3190 }
3191
3192 MBasicBlock* join;
3193 if (!goToNewBlock(blocks[0], &join)) {
3194 return false;
3195 }
3196 for (size_t i = 1; i < numJoinPreds; ++i) {
3197 if (!goToExistingBlock(blocks[i], join)) {
3198 return false;
3199 }
3200 }
3201
3202 curBlock_ = join;
3203 return popPushedDefs(defs);
3204 }
3205
3206 [[nodiscard]] bool startBlock() {
3207 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"
, 3208); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pendingBlocks_[blockDepth_].patches.empty()"
")"); do { *((volatile int*)__null) = 3208; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
3208 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"
, 3208); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pendingBlocks_[blockDepth_].patches.empty()"
")"); do { *((volatile int*)__null) = 3208; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
3209 blockDepth_++;
3210 return true;
3211 }
3212
3213 [[nodiscard]] bool finishBlock(DefVector* defs) {
3214 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"
, 3214); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_"
")"); do { *((volatile int*)__null) = 3214; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3215 uint32_t topLabel = --blockDepth_;
3216 return bindBranches(topLabel, defs);
3217 }
3218
3219 [[nodiscard]] bool startLoop(MBasicBlock** loopHeader, size_t paramCount) {
3220 *loopHeader = nullptr;
3221
3222 blockDepth_++;
3223 loopDepth_++;
3224
3225 if (inDeadCode()) {
3226 return true;
3227 }
3228
3229 // Create the loop header.
3230 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"
, 3230); AnnotateMozCrashReason("MOZ_ASSERT" "(" "curBlock_->loopDepth() == loopDepth_ - 1"
")"); do { *((volatile int*)__null) = 3230; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3231 *loopHeader = MBasicBlock::New(mirGraph(), info(), curBlock_,
3232 MBasicBlock::PENDING_LOOP_HEADER);
3233 if (!*loopHeader) {
3234 return false;
3235 }
3236
3237 (*loopHeader)->setLoopDepth(loopDepth_);
3238 mirGraph().addBlock(*loopHeader);
3239 curBlock_->end(MGoto::New(alloc(), *loopHeader));
3240
3241 DefVector loopParams;
3242 if (!iter().getResults(paramCount, &loopParams)) {
3243 return false;
3244 }
3245 for (size_t i = 0; i < paramCount; i++) {
3246 MPhi* phi = MPhi::New(alloc(), loopParams[i]->type());
3247 if (!phi) {
3248 return false;
3249 }
3250 if (!phi->reserveLength(2)) {
3251 return false;
3252 }
3253 (*loopHeader)->addPhi(phi);
3254 phi->addInput(loopParams[i]);
3255 loopParams[i] = phi;
3256 }
3257 iter().setResults(paramCount, loopParams);
3258
3259 MBasicBlock* body;
3260 if (!goToNewBlock(*loopHeader, &body)) {
3261 return false;
3262 }
3263 curBlock_ = body;
3264 return true;
3265 }
3266
3267 private:
3268 void fixupRedundantPhis(MBasicBlock* b) {
3269 for (size_t i = 0, depth = b->stackDepth(); i < depth; i++) {
3270 MDefinition* def = b->getSlot(i);
3271 if (def->isUnused()) {
3272 b->setSlot(i, def->toPhi()->getOperand(0));
3273 }
3274 }
3275 }
3276
3277 [[nodiscard]] bool setLoopBackedge(MBasicBlock* loopEntry,
3278 MBasicBlock* loopBody,
3279 MBasicBlock* backedge, size_t paramCount) {
3280 if (!loopEntry->setBackedgeWasm(backedge, paramCount)) {
3281 return false;
3282 }
3283
3284 // Flag all redundant phis as unused.
3285 for (MPhiIterator phi = loopEntry->phisBegin(); phi != loopEntry->phisEnd();
3286 phi++) {
3287 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"
, 3287); AnnotateMozCrashReason("MOZ_ASSERT" "(" "phi->numOperands() == 2"
")"); do { *((volatile int*)__null) = 3287; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3288 if (phi->getOperand(0) == phi->getOperand(1)) {
3289 phi->setUnused();
3290 }
3291 }
3292
3293 // Fix up phis stored in the slots Vector of pending blocks.
3294 for (PendingBlockTarget& pendingBlockTarget : pendingBlocks_) {
3295 for (ControlFlowPatch& p : pendingBlockTarget.patches) {
3296 MBasicBlock* block = p.ins->block();
3297 if (block->loopDepth() >= loopEntry->loopDepth()) {
3298 fixupRedundantPhis(block);
3299 }
3300 }
3301 }
3302
3303 // The loop body, if any, might be referencing recycled phis too.
3304 if (loopBody) {
3305 fixupRedundantPhis(loopBody);
3306 }
3307
3308 // Pending jumps to an enclosing try-catch may reference the recycled phis.
3309 // We have to search above all enclosing try blocks, as a delegate may move
3310 // patches around.
3311 for (uint32_t depth = 0; depth < iter().controlStackDepth(); depth++) {
3312 LabelKind kind = iter().controlKind(depth);
3313 if (kind != LabelKind::Try && kind != LabelKind::TryTable &&
3314 kind != LabelKind::Body) {
3315 continue;
3316 }
3317 Control& control = iter().controlItem(depth);
3318 if (!control.tryControl) {
3319 continue;
3320 }
3321 for (MControlInstruction* patch : control.tryControl->landingPadPatches) {
3322 MBasicBlock* block = patch->block();
3323 if (block->loopDepth() >= loopEntry->loopDepth()) {
3324 fixupRedundantPhis(block);
3325 }
3326 }
3327 }
3328 for (MControlInstruction* patch : bodyDelegatePadPatches_) {
3329 MBasicBlock* block = patch->block();
3330 if (block->loopDepth() >= loopEntry->loopDepth()) {
3331 fixupRedundantPhis(block);
3332 }
3333 }
3334
3335 // Discard redundant phis and add to the free list.
3336 for (MPhiIterator phi = loopEntry->phisBegin();
3337 phi != loopEntry->phisEnd();) {
3338 MPhi* entryDef = *phi++;
3339 if (!entryDef->isUnused()) {
3340 continue;
3341 }
3342
3343 entryDef->justReplaceAllUsesWith(entryDef->getOperand(0));
3344 loopEntry->discardPhi(entryDef);
3345 mirGraph().addPhiToFreeList(entryDef);
3346 }
3347
3348 return true;
3349 }
3350
3351 public:
3352 [[nodiscard]] bool closeLoop(MBasicBlock* loopHeader,
3353 DefVector* loopResults) {
3354 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"
, 3354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_ >= 1"
")"); do { *((volatile int*)__null) = 3354; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3355 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"
, 3355); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loopDepth_"
")"); do { *((volatile int*)__null) = 3355; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3356
3357 uint32_t headerLabel = blockDepth_ - 1;
3358
3359 if (!loopHeader) {
3360 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"
, 3360); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 3360; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3361 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"
, 3362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
")"); do { *((volatile int*)__null) = 3362; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3362 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"
, 3362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
")"); do { *((volatile int*)__null) = 3362; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3363 blockDepth_--;
3364 loopDepth_--;
3365 return true;
3366 }
3367
3368 // Op::Loop doesn't have an implicit backedge so temporarily set
3369 // aside the end of the loop body to bind backedges.
3370 MBasicBlock* loopBody = curBlock_;
3371 curBlock_ = nullptr;
3372
3373 // As explained in bug 1253544, Ion apparently has an invariant that
3374 // there is only one backedge to loop headers. To handle wasm's ability
3375 // to have multiple backedges to the same loop header, we bind all those
3376 // branches as forward jumps to a single backward jump. This is
3377 // unfortunate but the optimizer is able to fold these into single jumps
3378 // to backedges.
3379 DefVector backedgeValues;
3380 if (!bindBranches(headerLabel, &backedgeValues)) {
3381 return false;
3382 }
3383
3384 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"
, 3384); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loopHeader->loopDepth() == loopDepth_"
")"); do { *((volatile int*)__null) = 3384; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3385
3386 if (curBlock_) {
3387 // We're on the loop backedge block, created by bindBranches.
3388 for (size_t i = 0, n = numPushed(curBlock_); i != n; i++) {
3389 curBlock_->pop();
3390 }
3391
3392 if (!pushDefs(backedgeValues)) {
3393 return false;
3394 }
3395
3396 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"
, 3396); AnnotateMozCrashReason("MOZ_ASSERT" "(" "curBlock_->loopDepth() == loopDepth_"
")"); do { *((volatile int*)__null) = 3396; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3397 curBlock_->end(MGoto::New(alloc(), loopHeader));
3398 if (!setLoopBackedge(loopHeader, loopBody, curBlock_,
3399 backedgeValues.length())) {
3400 return false;
3401 }
3402 }
3403
3404 curBlock_ = loopBody;
3405
3406 loopDepth_--;
3407
3408 // If the loop depth still at the inner loop body, correct it.
3409 if (curBlock_ && curBlock_->loopDepth() != loopDepth_) {
3410 MBasicBlock* out;
3411 if (!goToNewBlock(curBlock_, &out)) {
3412 return false;
3413 }
3414 curBlock_ = out;
3415 }
3416
3417 blockDepth_ -= 1;
3418 return inDeadCode() || popPushedDefs(loopResults);
3419 }
3420
3421 [[nodiscard]] bool addControlFlowPatch(
3422 MControlInstruction* ins, uint32_t relative, uint32_t index,
3423 BranchHint branchHint = BranchHint::Invalid) {
3424 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"
, 3424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "relative < blockDepth_"
")"); do { *((volatile int*)__null) = 3424; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3425 uint32_t absolute = blockDepth_ - 1 - relative;
3426
3427 if (absolute >= pendingBlocks_.length() &&
3428 !pendingBlocks_.resize(absolute + 1)) {
3429 return false;
3430 }
3431
3432 pendingBlocks_[absolute].hint = branchHint;
3433 return pendingBlocks_[absolute].patches.append(
3434 ControlFlowPatch(ins, index));
3435 }
3436
3437 [[nodiscard]] bool br(uint32_t relativeDepth, const DefVector& values) {
3438 if (inDeadCode()) {
3439 return true;
3440 }
3441
3442 MGoto* jump = MGoto::New(alloc());
3443 if (!addControlFlowPatch(jump, relativeDepth, MGoto::TargetIndex)) {
3444 return false;
3445 }
3446
3447 if (!pushDefs(values)) {
3448 return false;
3449 }
3450
3451 curBlock_->end(jump);
3452 curBlock_ = nullptr;
3453 return true;
3454 }
3455
3456 [[nodiscard]] bool brIf(uint32_t relativeDepth, const DefVector& values,
3457 MDefinition* condition, BranchHint branchHint) {
3458 if (inDeadCode()) {
3459 return true;
3460 }
3461
3462 MBasicBlock* joinBlock = nullptr;
3463 if (!newBlock(curBlock_, &joinBlock)) {
3464 return false;
3465 }
3466
3467 MTest* test = MTest::New(alloc(), condition, nullptr, joinBlock);
3468 if (!addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex,
3469 branchHint)) {
3470 return false;
3471 }
3472
3473 if (!pushDefs(values)) {
3474 return false;
3475 }
3476
3477 curBlock_->end(test);
3478 curBlock_ = joinBlock;
3479
3480 return true;
3481 }
3482
3483 [[nodiscard]] bool brTable(MDefinition* operand, uint32_t defaultDepth,
3484 const Uint32Vector& depths,
3485 const DefVector& values) {
3486 if (inDeadCode()) {
3487 return true;
3488 }
3489
3490 size_t numCases = depths.length();
3491 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"
, 3491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numCases <= (2147483647)"
")"); do { *((volatile int*)__null) = 3491; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3492 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"
, 3492); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numCases" ")"
); do { *((volatile int*)__null) = 3492; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3493
3494 MTableSwitch* table =
3495 MTableSwitch::New(alloc(), operand, 0, int32_t(numCases - 1));
3496
3497 size_t defaultIndex;
3498 if (!table->addDefault(nullptr, &defaultIndex)) {
3499 return false;
3500 }
3501 if (!addControlFlowPatch(table, defaultDepth, defaultIndex)) {
3502 return false;
3503 }
3504
3505 using IndexToCaseMap =
3506 HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>;
3507
3508 IndexToCaseMap indexToCase;
3509 if (!indexToCase.put(defaultDepth, defaultIndex)) {
3510 return false;
3511 }
3512
3513 for (size_t i = 0; i < numCases; i++) {
3514 if (!mirGen_.ensureBallast()) {
3515 return false;
3516 }
3517
3518 uint32_t depth = depths[i];
3519
3520 size_t caseIndex;
3521 IndexToCaseMap::AddPtr p = indexToCase.lookupForAdd(depth);
3522 if (!p) {
3523 if (!table->addSuccessor(nullptr, &caseIndex)) {
3524 return false;
3525 }
3526 if (!addControlFlowPatch(table, depth, caseIndex)) {
3527 return false;
3528 }
3529 if (!indexToCase.add(p, depth, caseIndex)) {
3530 return false;
3531 }
3532 } else {
3533 caseIndex = p->value();
3534 }
3535
3536 if (!table->addCase(caseIndex)) {
3537 return false;
3538 }
3539 }
3540
3541 if (!pushDefs(values)) {
3542 return false;
3543 }
3544
3545 curBlock_->end(table);
3546 curBlock_ = nullptr;
3547
3548 return true;
3549 }
3550
3551 /********************************************************** Exceptions ***/
3552
3553 bool inTryBlockFrom(uint32_t fromRelativeDepth, uint32_t* relativeDepth) {
3554 return iter().controlFindInnermostFrom(
3555 [](LabelKind kind, const Control& control) {
3556 return control.tryControl != nullptr && control.tryControl->inBody;
3557 },
3558 fromRelativeDepth, relativeDepth);
3559 }
3560
3561 bool inTryBlock(uint32_t* relativeDepth) {
3562 return inTryBlockFrom(0, relativeDepth);
3563 }
3564
3565 bool inTryCode() {
3566 uint32_t relativeDepth;
3567 return inTryBlock(&relativeDepth);
3568 }
3569
3570 MDefinition* loadTag(uint32_t tagIndex) {
3571 MWasmLoadInstanceDataField* tag = MWasmLoadInstanceDataField::New(
3572 alloc(), MIRType::WasmAnyRef,
3573 codeMeta_.offsetOfTagInstanceData(tagIndex), true, instancePointer_);
3574 curBlock_->add(tag);
3575 return tag;
3576 }
3577
3578 void loadPendingExceptionState(MInstruction** exception, MInstruction** tag) {
3579 *exception = MWasmLoadInstance::New(
3580 alloc(), instancePointer_, wasm::Instance::offsetOfPendingException(),
3581 MIRType::WasmAnyRef, AliasSet::Load(AliasSet::WasmPendingException));
3582 curBlock_->add(*exception);
3583
3584 *tag = MWasmLoadInstance::New(
3585 alloc(), instancePointer_,
3586 wasm::Instance::offsetOfPendingExceptionTag(), MIRType::WasmAnyRef,
3587 AliasSet::Load(AliasSet::WasmPendingException));
3588 curBlock_->add(*tag);
3589 }
3590
3591 [[nodiscard]] bool setPendingExceptionState(MDefinition* exception,
3592 MDefinition* tag) {
3593 // Set the pending exception object
3594 auto* exceptionAddr = MWasmDerivedPointer::New(
3595 alloc(), instancePointer_, Instance::offsetOfPendingException());
3596 curBlock_->add(exceptionAddr);
3597 auto* setException = MWasmStoreRef::New(
3598 alloc(), instancePointer_, exceptionAddr, /*valueOffset=*/0, exception,
3599 AliasSet::WasmPendingException, WasmPreBarrierKind::Normal);
3600 curBlock_->add(setException);
3601 if (!postBarrierPrecise(/*lineOrBytecode=*/0, exceptionAddr, exception)) {
3602 return false;
3603 }
3604
3605 // Set the pending exception tag object
3606 auto* exceptionTagAddr = MWasmDerivedPointer::New(
3607 alloc(), instancePointer_, Instance::offsetOfPendingExceptionTag());
3608 curBlock_->add(exceptionTagAddr);
3609 auto* setExceptionTag = MWasmStoreRef::New(
3610 alloc(), instancePointer_, exceptionTagAddr, /*valueOffset=*/0, tag,
3611 AliasSet::WasmPendingException, WasmPreBarrierKind::Normal);
3612 curBlock_->add(setExceptionTag);
3613 return postBarrierPrecise(/*lineOrBytecode=*/0, exceptionTagAddr, tag);
3614 }
3615
3616 [[nodiscard]] bool addPadPatch(MControlInstruction* ins,
3617 size_t relativeTryDepth) {
3618 Control& control = iter().controlItem(relativeTryDepth);
3619 return control.tryControl->landingPadPatches.emplaceBack(ins);
3620 }
3621
3622 [[nodiscard]] bool endWithPadPatch(uint32_t relativeTryDepth) {
3623 MGoto* jumpToLandingPad = MGoto::New(alloc());
3624 curBlock_->end(jumpToLandingPad);
3625 return addPadPatch(jumpToLandingPad, relativeTryDepth);
3626 }
3627
3628 [[nodiscard]] bool delegatePadPatches(const ControlInstructionVector& patches,
3629 uint32_t relativeDepth) {
3630 if (patches.empty()) {
3631 return true;
3632 }
3633
3634 // Find where we are delegating the pad patches to.
3635 ControlInstructionVector* targetPatches;
3636 uint32_t targetRelativeDepth;
3637 if (inTryBlockFrom(relativeDepth, &targetRelativeDepth)) {
3638 targetPatches = &iter()
3639 .controlItem(targetRelativeDepth)
3640 .tryControl->landingPadPatches;
3641 } else {
3642 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"
, 3642); AnnotateMozCrashReason("MOZ_ASSERT" "(" "relativeDepth <= blockDepth_ - 1"
")"); do { *((volatile int*)__null) = 3642; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3643 targetPatches = &bodyDelegatePadPatches_;
3644 }
3645
3646 // Append the delegate's pad patches to the target's.
3647 for (MControlInstruction* ins : patches) {
3648 if (!targetPatches->emplaceBack(ins)) {
3649 return false;
3650 }
3651 }
3652 return true;
3653 }
3654
3655 [[nodiscard]] bool beginTryCall(MWasmCallTryDesc* call) {
3656 call->inTry = inTryBlock(&call->relativeTryDepth);
3657 if (!call->inTry) {
3658 return true;
3659 }
3660 // Allocate a try note
3661 if (!tryNotes_.append(wasm::TryNote())) {
3662 return false;
3663 }
3664 call->tryNoteIndex = tryNotes_.length() - 1;
3665 // Allocate blocks for fallthrough and exceptions
3666 return newBlock(curBlock_, &call->fallthroughBlock) &&
3667 newBlock(curBlock_, &call->prePadBlock);
3668 }
3669
3670 [[nodiscard]] bool finishTryCall(MWasmCallTryDesc* call) {
3671 if (!call->inTry) {
3672 return true;
3673 }
3674
3675 // Switch to the prePadBlock
3676 MBasicBlock* callBlock = curBlock_;
3677 curBlock_ = call->prePadBlock;
3678
3679 // Mark this as the landing pad for the call
3680 curBlock_->add(
3681 MWasmCallLandingPrePad::New(alloc(), callBlock, call->tryNoteIndex));
3682
3683 // End with a pending jump to the landing pad
3684 if (!endWithPadPatch(call->relativeTryDepth)) {
3685 return false;
3686 }
3687
3688 // Compilation continues in the fallthroughBlock.
3689 curBlock_ = call->fallthroughBlock;
3690 return true;
3691 }
3692
3693 // Create a landing pad for a try block if there are any throwing
3694 // instructions. This is also used for the implicit rethrow landing pad used
3695 // for delegate instructions that target the outermost label.
3696 [[nodiscard]] bool createTryLandingPadIfNeeded(
3697 ControlInstructionVector& landingPadPatches, MBasicBlock** landingPad) {
3698 // If there are no pad-patches for this try control, it means there are no
3699 // instructions in the try code that could throw an exception. In this
3700 // case, all the catches are dead code, and the try code ends up equivalent
3701 // to a plain wasm block.
3702 if (landingPadPatches.empty()) {
3703 *landingPad = nullptr;
3704 return true;
3705 }
3706
3707 // Otherwise, if there are (pad-) branches from places in the try code that
3708 // may throw an exception, bind these branches to a new landing pad
3709 // block. This is done similarly to what is done in bindBranches.
3710 MControlInstruction* ins = landingPadPatches[0];
3711 MBasicBlock* pred = ins->block();
3712 if (!newBlock(pred, landingPad)) {
3713 return false;
3714 }
3715 ins->replaceSuccessor(0, *landingPad);
3716 for (size_t i = 1; i < landingPadPatches.length(); i++) {
3717 ins = landingPadPatches[i];
3718 pred = ins->block();
3719 if (!(*landingPad)->addPredecessor(alloc(), pred)) {
3720 return false;
3721 }
3722 ins->replaceSuccessor(0, *landingPad);
3723 }
3724
3725 // Set up the slots in the landing pad block.
3726 if (!setupLandingPadSlots(landingPad)) {
3727 return false;
3728 }
3729
3730 // Clear the now bound pad patches.
3731 landingPadPatches.clear();
3732 return true;
3733 }
3734
3735 [[nodiscard]] bool createTryTableLandingPad(TryControl* tryControl) {
3736 MBasicBlock* landingPad;
3737 if (!createTryLandingPadIfNeeded(tryControl->landingPadPatches,
3738 &landingPad)) {
3739 return false;
3740 }
3741
3742 // If there is no landing pad created, no exceptions were possibly thrown
3743 // and we don't need to do anything here.
3744 if (!landingPad) {
3745 return true;
3746 }
3747
3748 MBasicBlock* originalBlock = curBlock_;
3749 curBlock_ = landingPad;
3750
3751 bool hadCatchAll = false;
3752 for (const TryTableCatch& tryTableCatch : tryControl->catches) {
3753 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"
, 3753); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 2"
")"); do { *((volatile int*)__null) = 3753; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3754
3755 // Handle a catch_all by jumping to the target block
3756 if (tryTableCatch.tagIndex == CatchAllIndex) {
3757 // Get the exception from the slots we pushed when adding
3758 // control flow patches.
3759 curBlock_->pop();
3760 MDefinition* exception = curBlock_->pop();
3761
3762 // Capture the exnref value if we need to
3763 DefVector values;
3764 if (tryTableCatch.captureExnRef && !values.append(exception)) {
3765 return false;
3766 }
3767
3768 // Branch to the catch_all code
3769 if (!br(tryTableCatch.labelRelativeDepth, values)) {
3770 return false;
3771 }
3772
3773 // Break from the loop and skip the implicit rethrow that's needed
3774 // if we didn't have a catch_all
3775 hadCatchAll = true;
3776 break;
3777 }
3778
3779 // Handle a tagged catch by doing a compare and branch on the tag index,
3780 // jumping to a catch block if they match, or else to a fallthrough block
3781 // to continue the landing pad.
3782 MBasicBlock* catchBlock = nullptr;
3783 MBasicBlock* fallthroughBlock = nullptr;
3784 if (!newBlock(curBlock_, &catchBlock) ||
3785 !newBlock(curBlock_, &fallthroughBlock)) {
3786 return false;
3787 }
3788
3789 // Get the exception and its tag from the slots we pushed when adding
3790 // control flow patches.
3791 MDefinition* exceptionTag = curBlock_->pop();
3792 curBlock_->pop();
3793
3794 // Branch to the catch block if the exception's tag matches this catch
3795 // block's tag.
3796 MDefinition* catchTag = loadTag(tryTableCatch.tagIndex);
3797 MDefinition* matchesCatchTag = compare(exceptionTag, catchTag, JSOp::Eq,
3798 MCompare::Compare_WasmAnyRef);
3799 curBlock_->end(
3800 MTest::New(alloc(), matchesCatchTag, catchBlock, fallthroughBlock));
3801
3802 // Set up the catch block by extracting the values from the exception
3803 // object.
3804 curBlock_ = catchBlock;
3805
3806 // Remove the tag and exception slots from the block, they are no
3807 // longer necessary.
3808 curBlock_->pop();
3809 MDefinition* exception = curBlock_->pop();
3810 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"
, 3810); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 3810; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3811
3812 // Extract the exception values for the catch block
3813 DefVector values;
3814 if (!loadExceptionValues(exception, tryTableCatch.tagIndex, &values)) {
3815 return false;
3816 }
3817 if (tryTableCatch.captureExnRef && !values.append(exception)) {
3818 return false;
3819 }
3820
3821 if (!br(tryTableCatch.labelRelativeDepth, values)) {
3822 return false;
3823 }
3824
3825 curBlock_ = fallthroughBlock;
3826 }
3827
3828 // If there was no catch_all, we must rethrow this exception.
3829 if (!hadCatchAll) {
3830 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"
, 3830); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 2"
")"); do { *((volatile int*)__null) = 3830; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3831 MDefinition* tag = curBlock_->pop();
3832 MDefinition* exception = curBlock_->pop();
3833 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"
, 3833); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 3833; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3834
3835 if (!throwFrom(exception, tag)) {
3836 return false;
3837 }
3838 }
3839
3840 curBlock_ = originalBlock;
3841 return true;
3842 }
3843
3844 // Consume the pending exception state from instance, and set up the slots
3845 // of the landing pad with the exception state.
3846 [[nodiscard]] bool setupLandingPadSlots(MBasicBlock** landingPad) {
3847 MBasicBlock* prevBlock = curBlock_;
3848 curBlock_ = *landingPad;
3849
3850 // Load the pending exception and tag
3851 MInstruction* exception;
3852 MInstruction* tag;
3853 loadPendingExceptionState(&exception, &tag);
3854
3855 // Clear the pending exception and tag
3856 auto* null = constantNullRef();
3857 if (!setPendingExceptionState(null, null)) {
3858 return false;
3859 }
3860
3861 // Push the exception and its tag on the stack to make them available
3862 // to the landing pad blocks.
3863 if (!curBlock_->ensureHasSlots(2)) {
3864 return false;
3865 }
3866 curBlock_->push(exception);
3867 curBlock_->push(tag);
3868 *landingPad = curBlock_;
3869
3870 curBlock_ = prevBlock;
3871 return true;
3872 }
3873
3874 [[nodiscard]] bool startTry() {
3875 Control& control = iter().controlItem();
3876 control.block = curBlock_;
3877 control.tryControl = newTryControl();
3878 if (!control.tryControl) {
3879 return false;
3880 }
3881 control.tryControl->inBody = true;
3882 return startBlock();
3883 }
3884
3885 [[nodiscard]] bool startTryTable(TryTableCatchVector&& catches) {
3886 Control& control = iter().controlItem();
3887 control.block = curBlock_;
3888 control.tryControl = newTryControl();
3889 if (!control.tryControl) {
3890 return false;
3891 }
3892 control.tryControl->inBody = true;
3893 control.tryControl->catches = std::move(catches);
3894 return startBlock();
3895 }
3896
3897 [[nodiscard]] bool joinTryOrCatchBlock(Control& control) {
3898 // If the try or catch block ended with dead code, there is no need to
3899 // do any control flow join.
3900 if (inDeadCode()) {
3901 return true;
3902 }
3903
3904 // This is a split path which we'll need to join later, using a control
3905 // flow patch.
3906 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"
, 3906); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!curBlock_->hasLastIns()"
")"); do { *((volatile int*)__null) = 3906; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3907 MGoto* jump = MGoto::New(alloc());
3908 if (!addControlFlowPatch(jump, 0, MGoto::TargetIndex)) {
3909 return false;
3910 }
3911
3912 // Finish the current block with the control flow patch instruction.
3913 curBlock_->end(jump);
3914 return true;
3915 }
3916
3917 // Finish the previous block (either a try or catch block) and then setup a
3918 // new catch block.
3919 [[nodiscard]] bool switchToCatch(Control& control, LabelKind fromKind,
3920 uint32_t tagIndex) {
3921 // Mark this control node as being no longer in the body of the try
3922 control.tryControl->inBody = false;
3923
3924 // If there is no control block, then either:
3925 // - the entry of the try block is dead code, or
3926 // - there is no landing pad for the try-catch.
3927 // In either case, any catch will be dead code.
3928 if (!control.block) {
3929 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"
, 3929); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 3929; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3930 return true;
3931 }
3932
3933 // Join the previous try or catch block with a patch to the future join of
3934 // the whole try-catch block.
3935 if (!joinTryOrCatchBlock(control)) {
3936 return false;
3937 }
3938
3939 // If we are switching from the try block, create the landing pad. This is
3940 // guaranteed to happen once and only once before processing catch blocks.
3941 if (fromKind == LabelKind::Try) {
3942 MBasicBlock* padBlock = nullptr;
3943 if (!createTryLandingPadIfNeeded(control.tryControl->landingPadPatches,
3944 &padBlock)) {
3945 return false;
3946 }
3947 // Set the control block for this try-catch to the landing pad.
3948 control.block = padBlock;
3949 }
3950
3951 // If there is no landing pad, then this and following catches are dead
3952 // code.
3953 if (!control.block) {
3954 curBlock_ = nullptr;
3955 return true;
3956 }
3957
3958 // Switch to the landing pad.
3959 curBlock_ = control.block;
3960
3961 // Handle a catch_all by immediately jumping to a new block. We require a
3962 // new block (as opposed to just emitting the catch_all code in the current
3963 // block) because rethrow requires the exception/tag to be present in the
3964 // landing pad's slots, while the catch_all block must not have the
3965 // exception/tag in slots.
3966 if (tagIndex == CatchAllIndex) {
3967 MBasicBlock* catchAllBlock = nullptr;
3968 if (!goToNewBlock(curBlock_, &catchAllBlock)) {
3969 return false;
3970 }
3971 // Compilation will continue in the catch_all block.
3972 curBlock_ = catchAllBlock;
3973 // Remove the tag and exception slots from the block, they are no
3974 // longer necessary.
3975 curBlock_->pop();
3976 curBlock_->pop();
3977 return true;
3978 }
3979
3980 // Handle a tagged catch by doing a compare and branch on the tag index,
3981 // jumping to a catch block if they match, or else to a fallthrough block
3982 // to continue the landing pad.
3983 MBasicBlock* catchBlock = nullptr;
3984 MBasicBlock* fallthroughBlock = nullptr;
3985 if (!newBlock(curBlock_, &catchBlock) ||
3986 !newBlock(curBlock_, &fallthroughBlock)) {
3987 return false;
3988 }
3989
3990 // Get the exception and its tag from the slots we pushed when adding
3991 // control flow patches.
3992 MDefinition* exceptionTag = curBlock_->pop();
3993 MDefinition* exception = curBlock_->pop();
Value stored to 'exception' during its initialization is never read
3994
3995 // Branch to the catch block if the exception's tag matches this catch
3996 // block's tag.
3997 MDefinition* catchTag = loadTag(tagIndex);
3998 MDefinition* matchesCatchTag =
3999 compare(exceptionTag, catchTag, JSOp::Eq, MCompare::Compare_WasmAnyRef);
4000 curBlock_->end(
4001 MTest::New(alloc(), matchesCatchTag, catchBlock, fallthroughBlock));
4002
4003 // The landing pad will continue in the fallthrough block
4004 control.block = fallthroughBlock;
4005
4006 // Set up the catch block by extracting the values from the exception
4007 // object.
4008 curBlock_ = catchBlock;
4009
4010 // Remove the tag and exception slots from the block, they are no
4011 // longer necessary.
4012 curBlock_->pop();
4013 exception = curBlock_->pop();
4014
4015 // Extract the exception values for the catch block
4016 DefVector values;
4017 if (!loadExceptionValues(exception, tagIndex, &values)) {
4018 return false;
4019 }
4020 iter().setResults(values.length(), values);
4021 return true;
4022 }
4023
4024 [[nodiscard]] bool loadExceptionValues(MDefinition* exception,
4025 uint32_t tagIndex, DefVector* values) {
4026 SharedTagType tagType = codeMeta().tags[tagIndex].type;
4027 const ValTypeVector& params = tagType->argTypes();
4028 const TagOffsetVector& offsets = tagType->argOffsets();
4029
4030 // Get the data pointer from the exception object
4031 auto* data = MWasmLoadField::New(
4032 alloc(), exception, WasmExceptionObject::offsetOfData(),
4033 MIRType::Pointer, MWideningOp::None, AliasSet::Load(AliasSet::Any));
4034 if (!data) {
4035 return false;
4036 }
4037 curBlock_->add(data);
4038
4039 // Presize the values vector to the number of params
4040 if (!values->reserve(params.length())) {
4041 return false;
4042 }
4043
4044 // Load each value from the data pointer
4045 for (size_t i = 0; i < params.length(); i++) {
4046 if (!mirGen_.ensureBallast()) {
4047 return false;
4048 }
4049 auto* load = MWasmLoadFieldKA::New(
4050 alloc(), exception, data, offsets[i], params[i].toMIRType(),
4051 MWideningOp::None, AliasSet::Load(AliasSet::Any));
4052 if (!load || !values->append(load)) {
4053 return false;
4054 }
4055 curBlock_->add(load);
4056 }
4057 return true;
4058 }
4059
4060 [[nodiscard]] bool finishTryCatch(LabelKind kind, Control& control,
4061 DefVector* defs) {
4062 switch (kind) {
4063 case LabelKind::Try: {
4064 // This is a catchless try, we must delegate all throwing instructions
4065 // to the nearest enclosing try block if one exists, or else to the
4066 // body block which will handle it in emitBodyDelegateThrowPad. We
4067 // specify a relativeDepth of '1' to delegate outside of the still
4068 // active try block.
4069 uint32_t relativeDepth = 1;
4070 if (!delegatePadPatches(control.tryControl->landingPadPatches,
4071 relativeDepth)) {
4072 return false;
4073 }
4074 break;
4075 }
4076 case LabelKind::Catch: {
4077 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"
, 4077); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl->inBody"
")"); do { *((volatile int*)__null) = 4077; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4078 // This is a try without a catch_all, we must have a rethrow at the end
4079 // of the landing pad (if any).
4080 MBasicBlock* padBlock = control.block;
4081 if (padBlock) {
4082 MBasicBlock* prevBlock = curBlock_;
4083 curBlock_ = padBlock;
4084 MDefinition* tag = curBlock_->pop();
4085 MDefinition* exception = curBlock_->pop();
4086 if (!throwFrom(exception, tag)) {
4087 return false;
4088 }
4089 curBlock_ = prevBlock;
4090 }
4091 break;
4092 }
4093 case LabelKind::CatchAll: {
4094 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"
, 4094); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl->inBody"
")"); do { *((volatile int*)__null) = 4094; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4095 // This is a try with a catch_all, and requires no special handling.
4096 break;
4097 }
4098 default:
4099 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4099); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 4099; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
4100 }
4101
4102 // Finish the block, joining the try and catch blocks
4103 return finishBlock(defs);
4104 }
4105
4106 [[nodiscard]] bool finishTryTable(Control& control, DefVector* defs) {
4107 // Mark this control as no longer in the body of the try
4108 control.tryControl->inBody = false;
4109 // Create a landing pad for all of the catches
4110 if (!createTryTableLandingPad(control.tryControl.get())) {
4111 return false;
4112 }
4113 // Finish the block, joining the try and catch blocks
4114 return finishBlock(defs);
4115 }
4116
4117 [[nodiscard]] bool emitBodyDelegateThrowPad(Control& control) {
4118 // Create a landing pad for any throwing instructions
4119 MBasicBlock* padBlock;
4120 if (!createTryLandingPadIfNeeded(bodyDelegatePadPatches_, &padBlock)) {
4121 return false;
4122 }
4123
4124 // If no landing pad was necessary, then we don't need to do anything here
4125 if (!padBlock) {
4126 return true;
4127 }
4128
4129 // Switch to the landing pad and rethrow the exception
4130 MBasicBlock* prevBlock = curBlock_;
4131 curBlock_ = padBlock;
4132 MDefinition* tag = curBlock_->pop();
4133 MDefinition* exception = curBlock_->pop();
4134 if (!throwFrom(exception, tag)) {
4135 return false;
4136 }
4137 curBlock_ = prevBlock;
4138 return true;
4139 }
4140
4141 [[nodiscard]] bool emitNewException(MDefinition* tag,
4142 MDefinition** exception) {
4143 return emitInstanceCall1(readBytecodeOffset(), SASigExceptionNew, tag,
4144 exception);
4145 }
4146
4147 [[nodiscard]] bool emitThrow(uint32_t tagIndex, const DefVector& argValues) {
4148 if (inDeadCode()) {
4149 return true;
4150 }
4151 uint32_t bytecodeOffset = readBytecodeOffset();
4152
4153 // Load the tag
4154 MDefinition* tag = loadTag(tagIndex);
4155 if (!tag) {
4156 return false;
4157 }
4158
4159 // Allocate an exception object
4160 MDefinition* exception;
4161 if (!emitNewException(tag, &exception)) {
4162 return false;
4163 }
4164
4165 // Load the data pointer from the object
4166 auto* data = MWasmLoadField::New(
4167 alloc(), exception, WasmExceptionObject::offsetOfData(),
4168 MIRType::Pointer, MWideningOp::None, AliasSet::Load(AliasSet::Any));
4169 if (!data) {
4170 return false;
4171 }
4172 curBlock_->add(data);
4173
4174 // Store the params into the data pointer
4175 SharedTagType tagType = codeMeta_.tags[tagIndex].type;
4176 for (size_t i = 0; i < tagType->argOffsets().length(); i++) {
4177 if (!mirGen_.ensureBallast()) {
4178 return false;
4179 }
4180 ValType type = tagType->argTypes()[i];
4181 uint32_t offset = tagType->argOffsets()[i];
4182
4183 if (!type.isRefRepr()) {
4184 auto* store = MWasmStoreFieldKA::New(alloc(), exception, data, offset,
4185 argValues[i], MNarrowingOp::None,
4186 AliasSet::Store(AliasSet::Any));
4187 if (!store) {
4188 return false;
4189 }
4190 curBlock_->add(store);
4191 continue;
4192 }
4193
4194 // Store the new value
4195 auto* store = MWasmStoreFieldRefKA::New(
4196 alloc(), instancePointer_, exception, data, offset, argValues[i],
4197 AliasSet::Store(AliasSet::Any), Nothing(), WasmPreBarrierKind::None);
4198 if (!store) {
4199 return false;
4200 }
4201 curBlock_->add(store);
4202
4203 // Call the post-write barrier
4204 if (!postBarrierImmediate(bytecodeOffset, exception, data, offset,
4205 argValues[i])) {
4206 return false;
4207 }
4208 }
4209
4210 // Throw the exception
4211 return throwFrom(exception, tag);
4212 }
4213
4214 [[nodiscard]] bool emitThrowRef(MDefinition* exnRef) {
4215 if (inDeadCode()) {
4216 return true;
4217 }
4218
4219 // The exception must be non-null
4220 if (!refAsNonNull(exnRef)) {
4221 return false;
4222 }
4223
4224 // Call Instance::throwException to perform tag unpacking and throw the
4225 // exception
4226 if (!emitInstanceCall1(readBytecodeOffset(), SASigThrowException, exnRef)) {
4227 return false;
4228 }
4229 unreachableTrap();
4230
4231 curBlock_ = nullptr;
4232 return true;
4233 }
4234
4235 [[nodiscard]] bool throwFrom(MDefinition* exn, MDefinition* tag) {
4236 if (inDeadCode()) {
4237 return true;
4238 }
4239
4240 // Check if there is a local catching try control, and if so, then add a
4241 // pad-patch to its tryPadPatches.
4242 uint32_t relativeTryDepth;
4243 if (inTryBlock(&relativeTryDepth)) {
4244 // Set the pending exception state, the landing pad will read from this
4245 if (!setPendingExceptionState(exn, tag)) {
4246 return false;
4247 }
4248
4249 // End with a pending jump to the landing pad
4250 if (!endWithPadPatch(relativeTryDepth)) {
4251 return false;
4252 }
4253 curBlock_ = nullptr;
4254 return true;
4255 }
4256
4257 // If there is no surrounding catching block, call an instance method to
4258 // throw the exception.
4259 if (!emitInstanceCall1(readBytecodeOffset(), SASigThrowException, exn)) {
4260 return false;
4261 }
4262 unreachableTrap();
4263
4264 curBlock_ = nullptr;
4265 return true;
4266 }
4267
4268 [[nodiscard]] bool emitRethrow(uint32_t relativeDepth) {
4269 if (inDeadCode()) {
4270 return true;
4271 }
4272
4273 Control& control = iter().controlItem(relativeDepth);
4274 MBasicBlock* pad = control.block;
4275 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"
, 4275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pad" ")"); do
{ *((volatile int*)__null) = 4275; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4276 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"
, 4276); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pad->nslots() > 1"
")"); do { *((volatile int*)__null) = 4276; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4277 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"
, 4278); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
")"); do { *((volatile int*)__null) = 4278; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4278 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"
, 4278); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
")"); do { *((volatile int*)__null) = 4278; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4279
4280 // The exception will always be the last slot in the landing pad.
4281 size_t exnSlotPosition = pad->nslots() - 2;
4282 MDefinition* tag = pad->getSlot(exnSlotPosition + 1);
4283 MDefinition* exception = pad->getSlot(exnSlotPosition);
4284 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"
, 4285); AnnotateMozCrashReason("MOZ_ASSERT" "(" "exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4285; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4285 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"
, 4285); AnnotateMozCrashReason("MOZ_ASSERT" "(" "exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4285; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4286 return throwFrom(exception, tag);
4287 }
4288
4289 /*********************************************** Instance call helpers ***/
4290
4291 // Do not call this function directly -- it offers no protection against
4292 // mis-counting of arguments. Instead call one of
4293 // ::emitInstanceCall{0,1,2,3,4,5,6}.
4294 //
4295 // Emits a call to the Instance function indicated by `callee`. This is
4296 // assumed to take an Instance pointer as its first argument. The remaining
4297 // args are taken from `args`, which is assumed to hold `numArgs` entries.
4298 // If `result` is non-null, the MDefinition* holding the return value is
4299 // written to `*result`.
4300 [[nodiscard]] bool emitInstanceCallN(uint32_t lineOrBytecode,
4301 const SymbolicAddressSignature& callee,
4302 MDefinition** args, size_t numArgs,
4303 MDefinition** result = nullptr) {
4304 // Check that the first formal parameter is plausibly an Instance pointer.
4305 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"
, 4305); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.numArgs > 0"
")"); do { *((volatile int*)__null) = 4305; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4306 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"
, 4306); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.argTypes[0] == MIRType::Pointer"
")"); do { *((volatile int*)__null) = 4306; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4307 // Check we agree on the number of args.
4308 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"
, 4308); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numArgs + 1 == callee.numArgs"
")"); do { *((volatile int*)__null) = 4308; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4309 // Check we agree on whether a value is returned.
4310 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"
, 4310); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(result == nullptr) == (callee.retType == MIRType::None)"
")"); do { *((volatile int*)__null) = 4310; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4311
4312 // If we are in dead code, it can happen that some of the `args` entries
4313 // are nullptr, which will look like an OOM to the logic below. So exit
4314 // at this point. `passInstance`, `passArg`, `finishCall` and
4315 // `builtinInstanceMethodCall` all do nothing in dead code, so it's valid
4316 // to exit here.
4317 if (inDeadCode()) {
4318 if (result) {
4319 *result = nullptr;
4320 }
4321 return true;
4322 }
4323
4324 // Check all args for signs of OOMness before attempting to allocating any
4325 // more memory.
4326 for (size_t i = 0; i < numArgs; i++) {
4327 if (!args[i]) {
4328 if (result) {
4329 *result = nullptr;
4330 }
4331 return false;
4332 }
4333 }
4334
4335 // Finally, construct the call.
4336 CallCompileState ccsArgs;
4337 if (!passInstance(callee.argTypes[0], &ccsArgs)) {
4338 return false;
4339 }
4340 for (size_t i = 0; i < numArgs; i++) {
4341 if (!passArg(args[i], callee.argTypes[i + 1], &ccsArgs)) {
4342 return false;
4343 }
4344 }
4345 if (!finishCall(&ccsArgs)) {
4346 return false;
4347 }
4348 return builtinInstanceMethodCall(callee, lineOrBytecode, ccsArgs, result);
4349 }
4350
4351 [[nodiscard]] bool emitInstanceCall0(uint32_t lineOrBytecode,
4352 const SymbolicAddressSignature& callee,
4353 MDefinition** result = nullptr) {
4354 MDefinition* args[0] = {};
4355 return emitInstanceCallN(lineOrBytecode, callee, args, 0, result);
4356 }
4357 [[nodiscard]] bool emitInstanceCall1(uint32_t lineOrBytecode,
4358 const SymbolicAddressSignature& callee,
4359 MDefinition* arg1,
4360 MDefinition** result = nullptr) {
4361 MDefinition* args[1] = {arg1};
4362 return emitInstanceCallN(lineOrBytecode, callee, args, 1, result);
4363 }
4364 [[nodiscard]] bool emitInstanceCall2(uint32_t lineOrBytecode,
4365 const SymbolicAddressSignature& callee,
4366 MDefinition* arg1, MDefinition* arg2,
4367 MDefinition** result = nullptr) {
4368 MDefinition* args[2] = {arg1, arg2};
4369 return emitInstanceCallN(lineOrBytecode, callee, args, 2, result);
4370 }
4371 [[nodiscard]] bool emitInstanceCall3(uint32_t lineOrBytecode,
4372 const SymbolicAddressSignature& callee,
4373 MDefinition* arg1, MDefinition* arg2,
4374 MDefinition* arg3,
4375 MDefinition** result = nullptr) {
4376 MDefinition* args[3] = {arg1, arg2, arg3};
4377 return emitInstanceCallN(lineOrBytecode, callee, args, 3, result);
4378 }
4379 [[nodiscard]] bool emitInstanceCall4(uint32_t lineOrBytecode,
4380 const SymbolicAddressSignature& callee,
4381 MDefinition* arg1, MDefinition* arg2,
4382 MDefinition* arg3, MDefinition* arg4,
4383 MDefinition** result = nullptr) {
4384 MDefinition* args[4] = {arg1, arg2, arg3, arg4};
4385 return emitInstanceCallN(lineOrBytecode, callee, args, 4, result);
4386 }
4387 [[nodiscard]] bool emitInstanceCall5(uint32_t lineOrBytecode,
4388 const SymbolicAddressSignature& callee,
4389 MDefinition* arg1, MDefinition* arg2,
4390 MDefinition* arg3, MDefinition* arg4,
4391 MDefinition* arg5,
4392 MDefinition** result = nullptr) {
4393 MDefinition* args[5] = {arg1, arg2, arg3, arg4, arg5};
4394 return emitInstanceCallN(lineOrBytecode, callee, args, 5, result);
4395 }
4396 [[nodiscard]] bool emitInstanceCall6(uint32_t lineOrBytecode,
4397 const SymbolicAddressSignature& callee,
4398 MDefinition* arg1, MDefinition* arg2,
4399 MDefinition* arg3, MDefinition* arg4,
4400 MDefinition* arg5, MDefinition* arg6,
4401 MDefinition** result = nullptr) {
4402 MDefinition* args[6] = {arg1, arg2, arg3, arg4, arg5, arg6};
4403 return emitInstanceCallN(lineOrBytecode, callee, args, 6, result);
4404 }
4405
4406 /******************************** WasmGC: low level load/store helpers ***/
4407
4408 // Given a (StorageType, FieldExtension) pair, produce the (MIRType,
4409 // MWideningOp) pair that will give the correct operation for reading the
4410 // value from memory.
4411 static void fieldLoadInfoToMIR(StorageType type, FieldWideningOp wideningOp,
4412 MIRType* mirType, MWideningOp* mirWideningOp) {
4413 switch (type.kind()) {
4414 case StorageType::I8: {
4415 switch (wideningOp) {
4416 case FieldWideningOp::Signed:
4417 *mirType = MIRType::Int32;
4418 *mirWideningOp = MWideningOp::FromS8;
4419 return;
4420 case FieldWideningOp::Unsigned:
4421 *mirType = MIRType::Int32;
4422 *mirWideningOp = MWideningOp::FromU8;
4423 return;
4424 default:
4425 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4425); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 4425; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
4426 }
4427 }
4428 case StorageType::I16: {
4429 switch (wideningOp) {
4430 case FieldWideningOp::Signed:
4431 *mirType = MIRType::Int32;
4432 *mirWideningOp = MWideningOp::FromS16;
4433 return;
4434 case FieldWideningOp::Unsigned:
4435 *mirType = MIRType::Int32;
4436 *mirWideningOp = MWideningOp::FromU16;
4437 return;
4438 default:
4439 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4439); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 4439; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
4440 }
4441 }
4442 default: {
4443 switch (wideningOp) {
4444 case FieldWideningOp::None:
4445 *mirType = type.toMIRType();
4446 *mirWideningOp = MWideningOp::None;
4447 return;
4448 default:
4449 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 4449); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 4449; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
4450 }
4451 }
4452 }
4453 }
4454
4455 // Given a StorageType, return the Scale required when accessing array
4456 // elements of this type.
4457 static Scale scaleFromFieldType(StorageType type) {
4458 if (type.kind() == StorageType::V128) {
4459 // V128 is accessed differently, so this scale will not be used.
4460 return Scale::Invalid;
4461 }
4462 return ShiftToScale(type.indexingShift());
4463 }
4464
4465 // Given a StorageType, produce the MNarrowingOp required for writing the
4466 // value to memory.
4467 static MNarrowingOp fieldStoreInfoToMIR(StorageType type) {
4468 switch (type.kind()) {
4469 case StorageType::I8:
4470 return MNarrowingOp::To8;
4471 case StorageType::I16:
4472 return MNarrowingOp::To16;
4473 default:
4474 return MNarrowingOp::None;
4475 }
4476 }
4477
4478 // Generate a write of `value` at address `base + offset`, where `offset` is
4479 // known at JIT time. If the written value is a reftype, the previous value
4480 // at `base + offset` will be retrieved and handed off to the post-write
4481 // barrier. `keepAlive` will be referenced by the instruction so as to hold
4482 // it live (from the GC's point of view).
4483 [[nodiscard]] bool writeGcValueAtBasePlusOffset(
4484 uint32_t lineOrBytecode, StorageType type, MDefinition* keepAlive,
4485 AliasSet::Flag aliasBitset, MDefinition* value, MDefinition* base,
4486 uint32_t offset, bool needsTrapInfo, WasmPreBarrierKind preBarrierKind) {
4487 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"
, 4487); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4487; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4488 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"
, 4488); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4488; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4489 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"
, 4489); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType().toMIRType() == value->type()"
")"); do { *((volatile int*)__null) = 4489; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4490 MNarrowingOp narrowingOp = fieldStoreInfoToMIR(type);
4491
4492 if (!type.isRefRepr()) {
4493 MaybeTrapSiteInfo maybeTrap;
4494 if (needsTrapInfo) {
4495 maybeTrap.emplace(getTrapSiteInfo());
4496 }
4497 auto* store = MWasmStoreFieldKA::New(
4498 alloc(), keepAlive, base, offset, value, narrowingOp,
4499 AliasSet::Store(aliasBitset), maybeTrap);
4500 if (!store) {
4501 return false;
4502 }
4503 curBlock_->add(store);
4504 return true;
4505 }
4506
4507 // Otherwise it's a ref store. Load the previous value so we can show it
4508 // to the post-write barrier.
4509 //
4510 // Optimisation opportunity: for the case where this field write results
4511 // from struct.new, the old value is always zero. So we should synthesise
4512 // a suitable zero constant rather than reading it from the object. See
4513 // also bug 1799999.
4514 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"
, 4514); AnnotateMozCrashReason("MOZ_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 4514; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4515 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"
, 4515); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType() == type.valType()"
")"); do { *((volatile int*)__null) = 4515; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4516
4517 // Store the new value
4518 auto* store = MWasmStoreFieldRefKA::New(
4519 alloc(), instancePointer_, keepAlive, base, offset, value,
4520 AliasSet::Store(aliasBitset), mozilla::Some(getTrapSiteInfo()),
4521 preBarrierKind);
4522 if (!store) {
4523 return false;
4524 }
4525 curBlock_->add(store);
4526
4527 // Call the post-write barrier
4528 return postBarrierImmediate(lineOrBytecode, keepAlive, base, offset, value);
4529 }
4530
4531 // Generate a write of `value` at address `base + index * scale`, where
4532 // `scale` is known at JIT-time. If the written value is a reftype, the
4533 // previous value at `base + index * scale` will be retrieved and handed off
4534 // to the post-write barrier. `keepAlive` will be referenced by the
4535 // instruction so as to hold it live (from the GC's point of view).
4536 [[nodiscard]] bool writeGcValueAtBasePlusScaledIndex(
4537 uint32_t lineOrBytecode, StorageType type, MDefinition* keepAlive,
4538 AliasSet::Flag aliasBitset, MDefinition* value, MDefinition* base,
4539 uint32_t scale, MDefinition* index, WasmPreBarrierKind preBarrierKind) {
4540 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"
, 4540); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4540; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4541 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"
, 4541); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4541; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4542 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"
, 4542); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType().toMIRType() == value->type()"
")"); do { *((volatile int*)__null) = 4542; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4543 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"
, 4544); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
")"); do { *((volatile int*)__null) = 4544; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4544 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"
, 4544); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
")"); do { *((volatile int*)__null) = 4544; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4545
4546 MNarrowingOp narrowingOp = fieldStoreInfoToMIR(type);
4547
4548 if (!type.isRefRepr()) {
4549 MaybeTrapSiteInfo maybeTrap;
4550 Scale scale = scaleFromFieldType(type);
4551 auto* store = MWasmStoreElementKA::New(
4552 alloc(), keepAlive, base, index, value, narrowingOp, scale,
4553 AliasSet::Store(aliasBitset), maybeTrap);
4554 if (!store) {
4555 return false;
4556 }
4557 curBlock_->add(store);
4558 return true;
4559 }
4560
4561 // Otherwise it's a ref store.
4562 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"
, 4562); AnnotateMozCrashReason("MOZ_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 4562; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4563 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"
, 4563); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType() == type.valType()"
")"); do { *((volatile int*)__null) = 4563; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4564
4565 // Store the new value
4566 auto* store = MWasmStoreElementRefKA::New(
4567 alloc(), instancePointer_, keepAlive, base, index, value,
4568 AliasSet::Store(aliasBitset), mozilla::Some(getTrapSiteInfo()),
4569 preBarrierKind);
4570 if (!store) {
4571 return false;
4572 }
4573 curBlock_->add(store);
4574
4575 return postBarrierIndex(lineOrBytecode, keepAlive, base, index,
4576 sizeof(void*), value);
4577 }
4578
4579 // Generate a read from address `base + offset`, where `offset` is known at
4580 // JIT time. The loaded value will be widened as described by `type` and
4581 // `fieldWideningOp`. `keepAlive` will be referenced by the instruction so as
4582 // to hold it live (from the GC's point of view).
4583 [[nodiscard]] MDefinition* readGcValueAtBasePlusOffset(
4584 StorageType type, FieldWideningOp fieldWideningOp, MDefinition* keepAlive,
4585 AliasSet::Flag aliasBitset, MDefinition* base, uint32_t offset,
4586 bool needsTrapInfo) {
4587 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"
, 4587); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4587; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4588 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"
, 4588); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4588; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4589 MIRType mirType;
4590 MWideningOp mirWideningOp;
4591 fieldLoadInfoToMIR(type, fieldWideningOp, &mirType, &mirWideningOp);
4592 MaybeTrapSiteInfo maybeTrap;
4593 if (needsTrapInfo) {
4594 maybeTrap.emplace(getTrapSiteInfo());
4595 }
4596 auto* load = MWasmLoadFieldKA::New(alloc(), keepAlive, base, offset,
4597 mirType, mirWideningOp,
4598 AliasSet::Load(aliasBitset), maybeTrap);
4599 if (!load) {
4600 return nullptr;
4601 }
4602 curBlock_->add(load);
4603 return load;
4604 }
4605
4606 // Generate a read from address `base + index * scale`, where `scale` is
4607 // known at JIT-time. The loaded value will be widened as described by
4608 // `type` and `fieldWideningOp`. `keepAlive` will be referenced by the
4609 // instruction so as to hold it live (from the GC's point of view).
4610 [[nodiscard]] MDefinition* readGcArrayValueAtIndex(
4611 StorageType type, FieldWideningOp fieldWideningOp, MDefinition* keepAlive,
4612 AliasSet::Flag aliasBitset, MDefinition* base, MDefinition* index) {
4613 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"
, 4613); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4613; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4614 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"
, 4614); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4614; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4615
4616 MIRType mirType;
4617 MWideningOp mirWideningOp;
4618 fieldLoadInfoToMIR(type, fieldWideningOp, &mirType, &mirWideningOp);
4619 Scale scale = scaleFromFieldType(type);
4620 auto* load = MWasmLoadElementKA::New(
4621 alloc(), keepAlive, base, index, mirType, mirWideningOp, scale,
4622 AliasSet::Load(aliasBitset), mozilla::Some(getTrapSiteInfo()));
4623 if (!load) {
4624 return nullptr;
4625 }
4626 curBlock_->add(load);
4627 return load;
4628 }
4629
4630 /************************************************ WasmGC: type helpers ***/
4631
4632 // Returns an MDefinition holding the supertype vector for `typeIndex`.
4633 [[nodiscard]] MDefinition* loadSuperTypeVector(uint32_t typeIndex) {
4634 uint32_t stvOffset = codeMeta().offsetOfSuperTypeVector(typeIndex);
4635
4636 auto* load =
4637 MWasmLoadInstanceDataField::New(alloc(), MIRType::Pointer, stvOffset,
4638 /*isConst=*/true, instancePointer_);
4639 if (!load) {
4640 return nullptr;
4641 }
4642 curBlock_->add(load);
4643 return load;
4644 }
4645
4646 [[nodiscard]] MDefinition* loadTypeDefInstanceData(uint32_t typeIndex) {
4647 size_t offset = Instance::offsetInData(
4648 codeMeta_.offsetOfTypeDefInstanceData(typeIndex));
4649 auto* result = MWasmDerivedPointer::New(alloc(), instancePointer_, offset);
4650 if (!result) {
4651 return nullptr;
4652 }
4653 curBlock_->add(result);
4654 return result;
4655 }
4656
4657 /********************************************** WasmGC: struct helpers ***/
4658
4659 [[nodiscard]] MDefinition* createStructObject(uint32_t typeIndex,
4660 bool zeroFields) {
4661 const TypeDef& typeDef = (*codeMeta().types)[typeIndex];
4662 gc::AllocKind allocKind = WasmStructObject::allocKindForTypeDef(&typeDef);
4663 bool isOutline =
4664 WasmStructObject::requiresOutlineBytes(typeDef.structType().size_);
4665
4666 // Allocate an uninitialized struct. This requires the type definition
4667 // for the struct.
4668 MDefinition* typeDefData = loadTypeDefInstanceData(typeIndex);
4669 if (!typeDefData) {
4670 return nullptr;
4671 }
4672
4673 auto* structObject =
4674 MWasmNewStructObject::New(alloc(), instancePointer_, typeDefData,
4675 isOutline, zeroFields, allocKind);
4676 if (!structObject) {
4677 return nullptr;
4678 }
4679 curBlock_->add(structObject);
4680
4681 return structObject;
4682 }
4683
4684 // Helper function for EmitStruct{New,Set}: given a MIR pointer to a
4685 // WasmStructObject, a MIR pointer to a value, and a field descriptor,
4686 // generate MIR to write the value to the relevant field in the object.
4687 [[nodiscard]] bool writeValueToStructField(
4688 uint32_t lineOrBytecode, const StructType& structType,
4689 uint32_t fieldIndex, MDefinition* structObject, MDefinition* value,
4690 WasmPreBarrierKind preBarrierKind) {
4691 StorageType fieldType = structType.fields_[fieldIndex].type;
4692 uint32_t fieldOffset = structType.fieldOffset(fieldIndex);
4693
4694 bool areaIsOutline;
4695 uint32_t areaOffset;
4696 WasmStructObject::fieldOffsetToAreaAndOffset(fieldType, fieldOffset,
4697 &areaIsOutline, &areaOffset);
4698
4699 // Make `base` point at the first byte of either the struct object as a
4700 // whole or of the out-of-line data area. And adjust `areaOffset`
4701 // accordingly.
4702 MDefinition* base;
4703 bool needsTrapInfo;
4704 if (areaIsOutline) {
4705 auto* load = MWasmLoadField::New(
4706 alloc(), structObject, WasmStructObject::offsetOfOutlineData(),
4707 MIRType::Pointer, MWideningOp::None,
4708 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer),
4709 mozilla::Some(getTrapSiteInfo()));
4710 if (!load) {
4711 return false;
4712 }
4713 curBlock_->add(load);
4714 base = load;
4715 needsTrapInfo = false;
4716 } else {
4717 base = structObject;
4718 needsTrapInfo = true;
4719 areaOffset += WasmStructObject::offsetOfInlineData();
4720 }
4721 // The transaction is to happen at `base + areaOffset`, so to speak.
4722 // After this point we must ignore `fieldOffset`.
4723
4724 // The alias set denoting the field's location, although lacking a
4725 // Load-vs-Store indication at this point.
4726 AliasSet::Flag fieldAliasSet = areaIsOutline
4727 ? AliasSet::WasmStructOutlineDataArea
4728 : AliasSet::WasmStructInlineDataArea;
4729
4730 return writeGcValueAtBasePlusOffset(lineOrBytecode, fieldType, structObject,
4731 fieldAliasSet, value, base, areaOffset,
4732 needsTrapInfo, preBarrierKind);
4733 }
4734
4735 // Helper function for EmitStructGet: given a MIR pointer to a
4736 // WasmStructObject, a field descriptor and a field widening operation,
4737 // generate MIR to read the value from the relevant field in the object.
4738 [[nodiscard]] MDefinition* readValueFromStructField(
4739 const StructType& structType, uint32_t fieldIndex,
4740 FieldWideningOp wideningOp, MDefinition* structObject) {
4741 StorageType fieldType = structType.fields_[fieldIndex].type;
4742 uint32_t fieldOffset = structType.fieldOffset(fieldIndex);
4743
4744 bool areaIsOutline;
4745 uint32_t areaOffset;
4746 WasmStructObject::fieldOffsetToAreaAndOffset(fieldType, fieldOffset,
4747 &areaIsOutline, &areaOffset);
4748
4749 // Make `base` point at the first byte of either the struct object as a
4750 // whole or of the out-of-line data area. And adjust `areaOffset`
4751 // accordingly.
4752 MDefinition* base;
4753 bool needsTrapInfo;
4754 if (areaIsOutline) {
4755 auto* loadOOLptr = MWasmLoadField::New(
4756 alloc(), structObject, WasmStructObject::offsetOfOutlineData(),
4757 MIRType::Pointer, MWideningOp::None,
4758 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer),
4759 mozilla::Some(getTrapSiteInfo()));
4760 if (!loadOOLptr) {
4761 return nullptr;
4762 }
4763 curBlock_->add(loadOOLptr);
4764 base = loadOOLptr;
4765 needsTrapInfo = false;
4766 } else {
4767 base = structObject;
4768 needsTrapInfo = true;
4769 areaOffset += WasmStructObject::offsetOfInlineData();
4770 }
4771 // The transaction is to happen at `base + areaOffset`, so to speak.
4772 // After this point we must ignore `fieldOffset`.
4773
4774 // The alias set denoting the field's location, although lacking a
4775 // Load-vs-Store indication at this point.
4776 AliasSet::Flag fieldAliasSet = areaIsOutline
4777 ? AliasSet::WasmStructOutlineDataArea
4778 : AliasSet::WasmStructInlineDataArea;
4779
4780 return readGcValueAtBasePlusOffset(fieldType, wideningOp, structObject,
4781 fieldAliasSet, base, areaOffset,
4782 needsTrapInfo);
4783 }
4784
4785 /********************************* WasmGC: address-arithmetic helpers ***/
4786
4787 inline bool targetIs64Bit() const {
4788#ifdef JS_64BIT1
4789 return true;
4790#else
4791 return false;
4792#endif
4793 }
4794
4795 // Generate MIR to unsigned widen `val` out to the target word size. If
4796 // `val` is already at the target word size, this is a no-op. The only
4797 // other allowed case is where `val` is Int32 and we're compiling for a
4798 // 64-bit target, in which case a widen is generated.
4799 [[nodiscard]] MDefinition* unsignedWidenToTargetWord(MDefinition* val) {
4800 if (targetIs64Bit()) {
4801 if (val->type() == MIRType::Int32) {
4802 auto* ext = MExtendInt32ToInt64::New(alloc(), val, /*isUnsigned=*/true);
4803 if (!ext) {
4804 return nullptr;
4805 }
4806 curBlock_->add(ext);
4807 return ext;
4808 }
4809 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"
, 4809); AnnotateMozCrashReason("MOZ_ASSERT" "(" "val->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 4809; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4810 return val;
4811 }
4812 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"
, 4812); AnnotateMozCrashReason("MOZ_ASSERT" "(" "val->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4812; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4813 return val;
4814 }
4815
4816 /********************************************** WasmGC: array helpers ***/
4817
4818 // Given `arrayObject`, the address of a WasmArrayObject, generate MIR to
4819 // return the contents of the WasmArrayObject::numElements_ field.
4820 // Adds trap site info for the null check.
4821 [[nodiscard]] MDefinition* getWasmArrayObjectNumElements(
4822 MDefinition* arrayObject) {
4823 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"
, 4823); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4823; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4824
4825 auto* numElements = MWasmLoadField::New(
4826 alloc(), arrayObject, WasmArrayObject::offsetOfNumElements(),
4827 MIRType::Int32, MWideningOp::None,
4828 AliasSet::Load(AliasSet::WasmArrayNumElements),
4829 mozilla::Some(getTrapSiteInfo()));
4830 if (!numElements) {
4831 return nullptr;
4832 }
4833 curBlock_->add(numElements);
4834
4835 return numElements;
4836 }
4837
4838 // Given `arrayObject`, the address of a WasmArrayObject, generate MIR to
4839 // return the contents of the WasmArrayObject::data_ field.
4840 [[nodiscard]] MDefinition* getWasmArrayObjectData(MDefinition* arrayObject) {
4841 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"
, 4841); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4841; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4842
4843 auto* data = MWasmLoadField::New(
4844 alloc(), arrayObject, WasmArrayObject::offsetOfData(),
4845 MIRType::WasmArrayData, MWideningOp::None,
4846 AliasSet::Load(AliasSet::WasmArrayDataPointer),
4847 mozilla::Some(getTrapSiteInfo()));
4848 if (!data) {
4849 return nullptr;
4850 }
4851 curBlock_->add(data);
4852
4853 return data;
4854 }
4855
4856 // Given a JIT-time-known type index `typeIndex` and a run-time known number
4857 // of elements `numElements`, create MIR to allocate a new wasm array,
4858 // possibly initialized with `typeIndex`s default value.
4859 [[nodiscard]] MDefinition* createArrayObject(uint32_t lineOrBytecode,
4860 uint32_t typeIndex,
4861 MDefinition* numElements,
4862 uint32_t elemSize,
4863 bool zeroFields) {
4864 // Get the type definition for the array as a whole.
4865 MDefinition* typeDefData = loadTypeDefInstanceData(typeIndex);
4866 if (!typeDefData) {
4867 return nullptr;
4868 }
4869
4870 auto* arrayObject = MWasmNewArrayObject::New(
4871 alloc(), instancePointer_, numElements, typeDefData, elemSize,
4872 zeroFields, bytecodeOffset());
4873 if (!arrayObject) {
4874 return nullptr;
4875 }
4876 curBlock_->add(arrayObject);
4877
4878 return arrayObject;
4879 }
4880
4881 // This emits MIR to perform several actions common to array loads and
4882 // stores. Given `arrayObject`, that points to a WasmArrayObject, and an
4883 // index value `index`, it:
4884 //
4885 // * Generates a trap if the array pointer is null
4886 // * Gets the size of the array
4887 // * Emits a bounds check of `index` against the array size
4888 // * Retrieves the OOL object pointer from the array
4889 // * Includes check for null via signal handler.
4890 //
4891 // The returned value is for the OOL object pointer.
4892 [[nodiscard]] MDefinition* setupForArrayAccess(MDefinition* arrayObject,
4893 MDefinition* index) {
4894 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"
, 4894); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4894; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4895 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"
, 4895); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4895; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4896
4897 // Check for null is done in getWasmArrayObjectNumElements.
4898
4899 // Get the size value for the array.
4900 MDefinition* numElements = getWasmArrayObjectNumElements(arrayObject);
4901 if (!numElements) {
4902 return nullptr;
4903 }
4904
4905 // Create a bounds check.
4906 auto* boundsCheck =
4907 MWasmBoundsCheck::New(alloc(), index, numElements, bytecodeOffset(),
4908 MWasmBoundsCheck::Target::Unknown);
4909 if (!boundsCheck) {
4910 return nullptr;
4911 }
4912 curBlock_->add(boundsCheck);
4913
4914 // Get the address of the first byte of the (OOL) data area.
4915 return getWasmArrayObjectData(arrayObject);
4916 }
4917
4918 [[nodiscard]] bool fillArray(uint32_t lineOrBytecode,
4919 const ArrayType& arrayType,
4920 MDefinition* arrayObject, MDefinition* index,
4921 MDefinition* numElements, MDefinition* val,
4922 WasmPreBarrierKind preBarrierKind) {
4923 mozilla::DebugOnly<MIRType> valMIRType = val->type();
4924 StorageType elemType = arrayType.elementType();
4925 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"
, 4925); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemType.widenToValType().toMIRType() == valMIRType"
")"); do { *((volatile int*)__null) = 4925; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4926
4927 uint32_t elemSize = elemType.size();
4928 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"
, 4928); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize >= 1 && elemSize <= 16"
")"); do { *((volatile int*)__null) = 4928; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4929
4930 // Make `arrayBase` point at the first byte of the (OOL) data area.
4931 MDefinition* arrayBase = getWasmArrayObjectData(arrayObject);
4932 if (!arrayBase) {
4933 return false;
4934 }
4935
4936 // We have:
4937 // arrayBase : TargetWord
4938 // index : Int32
4939 // numElements : Int32
4940 // val : <any StorageType>
4941 // $elemSize = arrayType.elementType_.size(); 1, 2, 4, 8 or 16
4942 //
4943 // Generate MIR:
4944 // <in current block>
4945 // limit : Int32 = index + numElements
4946 // if (limit == index) goto after; // skip loop if trip count == 0
4947 // loop:
4948 // indexPhi = phi(index, indexNext)
4949 // arrayBase[index * $elemSize] = val
4950 // indexNext = indexPhi + 1
4951 // if (indexNext <u limit) goto loop;
4952 // after:
4953 //
4954 // We construct the loop "manually" rather than using
4955 // FunctionCompiler::{startLoop,closeLoop} as the latter have awareness of
4956 // the wasm view of loops, whereas the loop we're building here is not a
4957 // wasm-level loop.
4958 // ==== Create the "loop" and "after" blocks ====
4959 MBasicBlock* loopBlock;
4960 if (!newBlock(curBlock_, &loopBlock, MBasicBlock::LOOP_HEADER)) {
4961 return false;
4962 }
4963 MBasicBlock* afterBlock;
4964 if (!newBlock(loopBlock, &afterBlock)) {
4965 return false;
4966 }
4967
4968 // ==== Fill in the remainder of the block preceding the loop ====
4969 MAdd* limit = MAdd::NewWasm(alloc(), index, numElements, MIRType::Int32);
4970 if (!limit) {
4971 return false;
4972 }
4973 curBlock_->add(limit);
4974
4975 // Use JSOp::StrictEq, not ::Eq, so that the comparison (and eventually
4976 // the entire initialisation loop) will be folded out in the case where
4977 // the number of elements is zero. See MCompare::tryFoldEqualOperands.
4978 MDefinition* limitEqualsBase =
4979 compare(limit, index, JSOp::StrictEq, MCompare::Compare_UInt32);
4980 if (!limitEqualsBase) {
4981 return false;
4982 }
4983 MTest* skipIfLimitEqualsBase =
4984 MTest::New(alloc(), limitEqualsBase, afterBlock, loopBlock);
4985 if (!skipIfLimitEqualsBase) {
4986 return false;
4987 }
4988 curBlock_->end(skipIfLimitEqualsBase);
4989 if (!afterBlock->addPredecessor(alloc(), curBlock_)) {
4990 return false;
4991 }
4992
4993 // ==== Fill in the loop block as best we can ====
4994 curBlock_ = loopBlock;
4995 MPhi* indexPhi = MPhi::New(alloc(), MIRType::Int32);
4996 if (!indexPhi) {
4997 return false;
4998 }
4999 if (!indexPhi->reserveLength(2)) {
5000 return false;
5001 }
5002 indexPhi->addInput(index);
5003 curBlock_->addPhi(indexPhi);
5004 curBlock_->setLoopDepth(loopDepth_ + 1);
5005
5006 if (!writeGcValueAtBasePlusScaledIndex(
5007 lineOrBytecode, elemType, arrayObject, AliasSet::WasmArrayDataArea,
5008 val, arrayBase, elemSize, indexPhi, preBarrierKind)) {
5009 return false;
5010 }
5011
5012 auto* indexNext =
5013 MAdd::NewWasm(alloc(), indexPhi, constantI32(1), MIRType::Int32);
5014 if (!indexNext) {
5015 return false;
5016 }
5017 curBlock_->add(indexNext);
5018 indexPhi->addInput(indexNext);
5019
5020 MDefinition* indexNextLtuLimit =
5021 compare(indexNext, limit, JSOp::Lt, MCompare::Compare_UInt32);
5022 if (!indexNextLtuLimit) {
5023 return false;
5024 }
5025 auto* continueIfIndexNextLtuLimit =
5026 MTest::New(alloc(), indexNextLtuLimit, loopBlock, afterBlock);
5027 if (!continueIfIndexNextLtuLimit) {
5028 return false;
5029 }
5030 curBlock_->end(continueIfIndexNextLtuLimit);
5031 if (!loopBlock->addPredecessor(alloc(), loopBlock)) {
5032 return false;
5033 }
5034 // ==== Loop block completed ====
5035
5036 curBlock_ = afterBlock;
5037 return true;
5038 }
5039
5040 // This routine generates all MIR required for `array.new`. The returned
5041 // value is for the newly created array.
5042 [[nodiscard]] MDefinition* createArrayNewCallAndLoop(uint32_t lineOrBytecode,
5043 uint32_t typeIndex,
5044 MDefinition* numElements,
5045 MDefinition* fillValue) {
5046 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType();
5047
5048 // Create the array object, uninitialized.
5049 MDefinition* arrayObject =
5050 createArrayObject(lineOrBytecode, typeIndex, numElements,
5051 arrayType.elementType().size(), /*zeroFields=*/false);
5052 if (!arrayObject) {
5053 return nullptr;
5054 }
5055
5056 // Optimisation opportunity: if the fill value is zero, maybe we should
5057 // likewise skip over the initialisation loop entirely (and, if the zero
5058 // value is visible at JIT time, the loop will be removed). For the
5059 // reftyped case, that would be a big win since each iteration requires a
5060 // call to the post-write barrier routine.
5061
5062 if (!fillArray(lineOrBytecode, arrayType, arrayObject, constantI32(0),
5063 numElements, fillValue, WasmPreBarrierKind::None)) {
5064 return nullptr;
5065 }
5066
5067 return arrayObject;
5068 }
5069
5070 [[nodiscard]] bool createArrayFill(uint32_t lineOrBytecode,
5071 uint32_t typeIndex,
5072 MDefinition* arrayObject,
5073 MDefinition* index, MDefinition* val,
5074 MDefinition* numElements) {
5075 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"
, 5075); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 5075; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5076 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"
, 5076); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 5076; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5077 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"
, 5077); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numElements->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 5077; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5078
5079 const ArrayType& arrayType = (*codeMeta_.types)[typeIndex].arrayType();
5080
5081 // Check for null is done in getWasmArrayObjectNumElements.
5082
5083 // Get the array's actual size.
5084 MDefinition* actualNumElements = getWasmArrayObjectNumElements(arrayObject);
5085 if (!actualNumElements) {
5086 return false;
5087 }
5088
5089 // Create a bounds check.
5090 auto* boundsCheck = MWasmBoundsCheckRange32::New(
5091 alloc(), index, numElements, actualNumElements, bytecodeOffset());
5092 if (!boundsCheck) {
5093 return false;
5094 }
5095 curBlock_->add(boundsCheck);
5096
5097 return fillArray(lineOrBytecode, arrayType, arrayObject, index, numElements,
5098 val, WasmPreBarrierKind::Normal);
5099 }
5100
5101 /*********************************************** WasmGC: other helpers ***/
5102
5103 // Generate MIR that causes a trap of kind `trapKind` if `arg` is zero.
5104 // Currently `arg` may only be a MIRType::Int32, but that requirement could
5105 // be relaxed if needed in future.
5106 [[nodiscard]] bool trapIfZero(wasm::Trap trapKind, MDefinition* arg) {
5107 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"
, 5107); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 5107; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5108
5109 MBasicBlock* trapBlock = nullptr;
5110 if (!newBlock(curBlock_, &trapBlock)) {
5111 return false;
5112 }
5113
5114 auto* trap = MWasmTrap::New(alloc(), trapKind, bytecodeOffset());
5115 if (!trap) {
5116 return false;
5117 }
5118 trapBlock->end(trap);
5119
5120 MBasicBlock* joinBlock = nullptr;
5121 if (!newBlock(curBlock_, &joinBlock)) {
5122 return false;
5123 }
5124
5125 auto* test = MTest::New(alloc(), arg, joinBlock, trapBlock);
5126 if (!test) {
5127 return false;
5128 }
5129 curBlock_->end(test);
5130 curBlock_ = joinBlock;
5131 return true;
5132 }
5133
5134 [[nodiscard]] MDefinition* isRefSubtypeOf(MDefinition* ref,
5135 RefType sourceType,
5136 RefType destType) {
5137 MInstruction* isSubTypeOf = nullptr;
5138 if (destType.isTypeRef()) {
5139 uint32_t typeIndex = codeMeta_.types->indexOf(*destType.typeDef());
5140 MDefinition* superSTV = loadSuperTypeVector(typeIndex);
5141 isSubTypeOf = MWasmRefIsSubtypeOfConcrete::New(alloc(), ref, superSTV,
5142 sourceType, destType);
5143 } else {
5144 isSubTypeOf =
5145 MWasmRefIsSubtypeOfAbstract::New(alloc(), ref, sourceType, destType);
5146 }
5147 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"
, 5147); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSubTypeOf"
")"); do { *((volatile int*)__null) = 5147; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5148
5149 curBlock_->add(isSubTypeOf);
5150 return isSubTypeOf;
5151 }
5152
5153 // Generate MIR that attempts to downcast `ref` to `castToTypeDef`. If the
5154 // downcast fails, we trap. If it succeeds, then `ref` can be assumed to
5155 // have a type that is a subtype of (or the same as) `castToTypeDef` after
5156 // this point.
5157 [[nodiscard]] bool refCast(MDefinition* ref, RefType sourceType,
5158 RefType destType) {
5159 MDefinition* success = isRefSubtypeOf(ref, sourceType, destType);
5160 if (!success) {
5161 return false;
5162 }
5163
5164 // Trap if `success` is zero. If it's nonzero, we have established that
5165 // `ref <: castToTypeDef`.
5166 return trapIfZero(wasm::Trap::BadCast, success);
5167 }
5168
5169 // Generate MIR that computes a boolean value indicating whether or not it
5170 // is possible to downcast `ref` to `destType`.
5171 [[nodiscard]] MDefinition* refTest(MDefinition* ref, RefType sourceType,
5172 RefType destType) {
5173 return isRefSubtypeOf(ref, sourceType, destType);
5174 }
5175
5176 // Generates MIR for br_on_cast and br_on_cast_fail.
5177 [[nodiscard]] bool brOnCastCommon(bool onSuccess, uint32_t labelRelativeDepth,
5178 RefType sourceType, RefType destType,
5179 const ResultType& labelType,
5180 const DefVector& values) {
5181 if (inDeadCode()) {
5182 return true;
5183 }
5184
5185 MBasicBlock* fallthroughBlock = nullptr;
5186 if (!newBlock(curBlock_, &fallthroughBlock)) {
5187 return false;
5188 }
5189
5190 // `values` are the values in the top block-value on the stack. Since the
5191 // argument to `br_on_cast{_fail}` is at the top of the stack, it is the
5192 // last element in `values`.
5193 //
5194 // For both br_on_cast and br_on_cast_fail, the OpIter validation routines
5195 // ensure that `values` is non-empty (by rejecting the case
5196 // `labelType->length() < 1`) and that the last value in `values` is
5197 // reftyped.
5198 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"
, 5198); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "values.length() > 0"
")"); do { *((volatile int*)__null) = 5198; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5199 MDefinition* ref = values.back();
5200 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"
, 5200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 5200; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5201
5202 MDefinition* success = isRefSubtypeOf(ref, sourceType, destType);
5203 if (!success) {
5204 return false;
5205 }
5206
5207 MTest* test;
5208 if (onSuccess) {
5209 test = MTest::New(alloc(), success, nullptr, fallthroughBlock);
5210 if (!test || !addControlFlowPatch(test, labelRelativeDepth,
5211 MTest::TrueBranchIndex)) {
5212 return false;
5213 }
5214 } else {
5215 test = MTest::New(alloc(), success, fallthroughBlock, nullptr);
5216 if (!test || !addControlFlowPatch(test, labelRelativeDepth,
5217 MTest::FalseBranchIndex)) {
5218 return false;
5219 }
5220 }
5221
5222 if (!pushDefs(values)) {
5223 return false;
5224 }
5225
5226 curBlock_->end(test);
5227 curBlock_ = fallthroughBlock;
5228 return true;
5229 }
5230
5231 [[nodiscard]] bool brOnNonStruct(const DefVector& values) {
5232 if (inDeadCode()) {
5233 return true;
5234 }
5235
5236 MBasicBlock* fallthroughBlock = nullptr;
5237 if (!newBlock(curBlock_, &fallthroughBlock)) {
5238 return false;
5239 }
5240
5241 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"
, 5241); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.length() > 0"
")"); do { *((volatile int*)__null) = 5241; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5242 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"
, 5242); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.back()->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 5242; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5243
5244 MGoto* jump = MGoto::New(alloc(), fallthroughBlock);
5245 if (!jump) {
5246 return false;
5247 }
5248 if (!pushDefs(values)) {
5249 return false;
5250 }
5251
5252 curBlock_->end(jump);
5253 curBlock_ = fallthroughBlock;
5254 return true;
5255 }
5256
5257 /************************************************************ DECODING ***/
5258
5259 // AsmJS adds a line number to `callSiteLineNums` for certain operations that
5260 // are represented by a JS call, such as math builtins. We use these line
5261 // numbers when calling builtins. This method will read from
5262 // `callSiteLineNums` when we are using AsmJS, or else return the current
5263 // bytecode offset.
5264 //
5265 // This method MUST be called from opcodes that AsmJS will emit a call site
5266 // line number for, or else the arrays will get out of sync. Other opcodes
5267 // must use `readBytecodeOffset` below.
5268 uint32_t readCallSiteLineOrBytecode() {
5269 if (!func_.callSiteLineNums.empty()) {
5270 return func_.callSiteLineNums[lastReadCallSite_++];
5271 }
5272 return iter_.lastOpcodeOffset();
5273 }
5274
5275 // Return the current bytecode offset.
5276 uint32_t readBytecodeOffset() { return iter_.lastOpcodeOffset(); }
5277
5278 TrapSiteInfo getTrapSiteInfo() {
5279 return TrapSiteInfo(wasm::BytecodeOffset(readBytecodeOffset()));
5280 }
5281
5282 CallRefHint readCallRefHint() {
5283 // We don't track anything if we're not using lazy tiering
5284 if (compilerEnv_.mode() != CompileMode::LazyTiering) {
5285 return CallRefHint::unknown();
5286 }
5287
5288 CallRefMetricsRange rangeInModule =
5289 codeMeta_.getFuncDefCallRefs(funcIndex());
5290 uint32_t localIndex = numCallRefs_++;
5291 MOZ_RELEASE_ASSERT(localIndex < rangeInModule.length)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(localIndex < rangeInModule.length)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(localIndex < rangeInModule
.length))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("localIndex < rangeInModule.length", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5291); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "localIndex < rangeInModule.length"
")"); do { *((volatile int*)__null) = 5291; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5292 uint32_t moduleIndex = rangeInModule.begin + localIndex;
5293 return codeMeta_.getCallRefHint(moduleIndex);
5294 }
5295
5296#if DEBUG1
5297 bool done() const { return iter_.done(); }
5298#endif
5299
5300 /*************************************************************************/
5301 private:
5302 [[nodiscard]] bool newBlock(MBasicBlock* pred, MBasicBlock** block,
5303 MBasicBlock::Kind kind = MBasicBlock::NORMAL) {
5304 *block = MBasicBlock::New(mirGraph(), info(), pred, kind);
5305 if (!*block) {
5306 return false;
5307 }
5308 mirGraph().addBlock(*block);
5309 (*block)->setLoopDepth(loopDepth_);
5310 return true;
5311 }
5312
5313 [[nodiscard]] bool goToNewBlock(MBasicBlock* pred, MBasicBlock** block) {
5314 if (!newBlock(pred, block)) {
5315 return false;
5316 }
5317 pred->end(MGoto::New(alloc(), *block));
5318 return true;
5319 }
5320
5321 [[nodiscard]] bool goToExistingBlock(MBasicBlock* prev, MBasicBlock* next) {
5322 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"
, 5322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "prev" ")");
do { *((volatile int*)__null) = 5322; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5323 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"
, 5323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "next" ")");
do { *((volatile int*)__null) = 5323; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5324 prev->end(MGoto::New(alloc(), next));
5325 return next->addPredecessor(alloc(), prev);
5326 }
5327
5328 [[nodiscard]] bool bindBranches(uint32_t absolute, DefVector* defs) {
5329 if (absolute >= pendingBlocks_.length() ||
5330 pendingBlocks_[absolute].patches.empty()) {
5331 return inDeadCode() || popPushedDefs(defs);
5332 }
5333
5334 ControlFlowPatchVector& patches = pendingBlocks_[absolute].patches;
5335 MControlInstruction* ins = patches[0].ins;
5336 MBasicBlock* pred = ins->block();
5337
5338 MBasicBlock* join = nullptr;
5339 if (!newBlock(pred, &join)) {
5340 return false;
5341 }
5342
5343 // Use branch hinting information if any.
5344 if (pendingBlocks_[absolute].hint != BranchHint::Invalid) {
5345 join->setBranchHinting(pendingBlocks_[absolute].hint);
5346 }
5347
5348 pred->mark();
5349 ins->replaceSuccessor(patches[0].index, join);
5350
5351 for (size_t i = 1; i < patches.length(); i++) {
5352 ins = patches[i].ins;
5353
5354 pred = ins->block();
5355 if (!pred->isMarked()) {
5356 if (!join->addPredecessor(alloc(), pred)) {
5357 return false;
5358 }
5359 pred->mark();
5360 }
5361
5362 ins->replaceSuccessor(patches[i].index, join);
5363 }
5364
5365 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"
, 5365); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!curBlock_->isMarked()"
")"); do { *((volatile int*)__null) = 5365; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5366 for (uint32_t i = 0; i < join->numPredecessors(); i++) {
5367 join->getPredecessor(i)->unmark();
5368 }
5369
5370 if (curBlock_ && !goToExistingBlock(curBlock_, join)) {
5371 return false;
5372 }
5373
5374 curBlock_ = join;
5375
5376 if (!popPushedDefs(defs)) {
5377 return false;
5378 }
5379
5380 patches.clear();
5381 return true;
5382 }
5383};
5384
5385template <>
5386MDefinition* FunctionCompiler::unary<MToFloat32>(MDefinition* op) {
5387 if (inDeadCode()) {
5388 return nullptr;
5389 }
5390 auto* ins = MToFloat32::New(alloc(), op, mustPreserveNaN(op->type()));
5391 curBlock_->add(ins);
5392 return ins;
5393}
5394
5395template <>
5396MDefinition* FunctionCompiler::unary<MWasmBuiltinTruncateToInt32>(
5397 MDefinition* op) {
5398 if (inDeadCode()) {
5399 return nullptr;
5400 }
5401 auto* ins = MWasmBuiltinTruncateToInt32::New(
5402 alloc(), op, instancePointer_,
5403 BytecodeOffset(readCallSiteLineOrBytecode()));
5404 curBlock_->add(ins);
5405 return ins;
5406}
5407
5408template <>
5409MDefinition* FunctionCompiler::unary<MNot>(MDefinition* op) {
5410 if (inDeadCode()) {
5411 return nullptr;
5412 }
5413 auto* ins = MNot::NewInt32(alloc(), op);
5414 curBlock_->add(ins);
5415 return ins;
5416}
5417
5418template <>
5419MDefinition* FunctionCompiler::unary<MAbs>(MDefinition* op, MIRType type) {
5420 if (inDeadCode()) {
5421 return nullptr;
5422 }
5423 auto* ins = MAbs::NewWasm(alloc(), op, type);
5424 curBlock_->add(ins);
5425 return ins;
5426}
5427
5428} // end anonymous namespace
5429
5430bool EmitBodyExprs(FunctionCompiler& f);
5431
5432static bool EmitI32Const(FunctionCompiler& f) {
5433 int32_t i32;
5434 if (!f.iter().readI32Const(&i32)) {
5435 return false;
5436 }
5437
5438 f.iter().setResult(f.constantI32(i32));
5439 return true;
5440}
5441
5442static bool EmitI64Const(FunctionCompiler& f) {
5443 int64_t i64;
5444 if (!f.iter().readI64Const(&i64)) {
5445 return false;
5446 }
5447
5448 f.iter().setResult(f.constantI64(i64));
5449 return true;
5450}
5451
5452static bool EmitF32Const(FunctionCompiler& f) {
5453 float f32;
5454 if (!f.iter().readF32Const(&f32)) {
5455 return false;
5456 }
5457
5458 f.iter().setResult(f.constantF32(f32));
5459 return true;
5460}
5461
5462static bool EmitF64Const(FunctionCompiler& f) {
5463 double f64;
5464 if (!f.iter().readF64Const(&f64)) {
5465 return false;
5466 }
5467
5468 f.iter().setResult(f.constantF64(f64));
5469 return true;
5470}
5471
5472static bool EmitBlock(FunctionCompiler& f) {
5473 ResultType params;
5474 return f.iter().readBlock(&params) && f.startBlock();
5475}
5476
5477static bool EmitLoop(FunctionCompiler& f) {
5478 ResultType params;
5479 if (!f.iter().readLoop(&params)) {
5480 return false;
5481 }
5482
5483 MBasicBlock* loopHeader;
5484 if (!f.startLoop(&loopHeader, params.length())) {
5485 return false;
5486 }
5487
5488 f.addInterruptCheck();
5489
5490 f.iter().controlItem().block = loopHeader;
5491 return true;
5492}
5493
5494static bool EmitIf(FunctionCompiler& f) {
5495 BranchHint branchHint =
5496 f.iter().getBranchHint(f.funcIndex(), f.relativeBytecodeOffset());
5497
5498 ResultType params;
5499 MDefinition* condition = nullptr;
5500 if (!f.iter().readIf(&params, &condition)) {
5501 return false;
5502 }
5503
5504 MBasicBlock* elseBlock;
5505 if (!f.branchAndStartThen(condition, &elseBlock)) {
5506 return false;
5507 }
5508
5509 // Store the branch hint in the basic block.
5510 if (!f.inDeadCode() && branchHint != BranchHint::Invalid) {
5511 f.getCurBlock()->setBranchHinting(branchHint);
5512 }
5513
5514 f.iter().controlItem().block = elseBlock;
5515 return true;
5516}
5517
5518static bool EmitElse(FunctionCompiler& f) {
5519 ResultType paramType;
5520 ResultType resultType;
5521 DefVector thenValues;
5522 if (!f.iter().readElse(&paramType, &resultType, &thenValues)) {
5523 return false;
5524 }
5525
5526 if (!f.pushDefs(thenValues)) {
5527 return false;
5528 }
5529
5530 Control& control = f.iter().controlItem();
5531 return f.switchToElse(control.block, &control.block);
5532}
5533
5534static bool EmitEnd(FunctionCompiler& f) {
5535 LabelKind kind;
5536 ResultType type;
5537 DefVector preJoinDefs;
5538 DefVector resultsForEmptyElse;
5539 if (!f.iter().readEnd(&kind, &type, &preJoinDefs, &resultsForEmptyElse)) {
5540 return false;
5541 }
5542
5543 Control& control = f.iter().controlItem();
5544 MBasicBlock* block = control.block;
5545
5546 if (!f.pushDefs(preJoinDefs)) {
5547 return false;
5548 }
5549
5550 // Every label case is responsible to pop the control item at the appropriate
5551 // time for the label case
5552 DefVector postJoinDefs;
5553 switch (kind) {
5554 case LabelKind::Body: {
5555 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"
, 5555); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5555; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5556 if (!f.emitBodyDelegateThrowPad(control)) {
5557 return false;
5558 }
5559 if (!f.finishBlock(&postJoinDefs)) {
5560 return false;
5561 }
5562 if (!f.returnValues(std::move(postJoinDefs))) {
5563 return false;
5564 }
5565 f.iter().popEnd();
5566 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"
, 5566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f.iter().controlStackEmpty()"
")"); do { *((volatile int*)__null) = 5566; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5567 return f.iter().endFunction(f.iter().end());
5568 }
5569 case LabelKind::Block:
5570 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"
, 5570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5570; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5571 if (!f.finishBlock(&postJoinDefs)) {
5572 return false;
5573 }
5574 f.iter().popEnd();
5575 break;
5576 case LabelKind::Loop:
5577 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"
, 5577); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5577; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5578 if (!f.closeLoop(block, &postJoinDefs)) {
5579 return false;
5580 }
5581 f.iter().popEnd();
5582 break;
5583 case LabelKind::Then: {
5584 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"
, 5584); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5584; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5585 // If we didn't see an Else, create a trivial else block so that we create
5586 // a diamond anyway, to preserve Ion invariants.
5587 if (!f.switchToElse(block, &block)) {
5588 return false;
5589 }
5590
5591 if (!f.pushDefs(resultsForEmptyElse)) {
5592 return false;
5593 }
5594
5595 if (!f.joinIfElse(block, &postJoinDefs)) {
5596 return false;
5597 }
5598 f.iter().popEnd();
5599 break;
5600 }
5601 case LabelKind::Else:
5602 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"
, 5602); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5602; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5603 if (!f.joinIfElse(block, &postJoinDefs)) {
5604 return false;
5605 }
5606 f.iter().popEnd();
5607 break;
5608 case LabelKind::Try:
5609 case LabelKind::Catch:
5610 case LabelKind::CatchAll:
5611 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"
, 5611); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5611; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5612 if (!f.finishTryCatch(kind, control, &postJoinDefs)) {
5613 return false;
5614 }
5615 f.freeTryControl(std::move(control.tryControl));
5616 f.iter().popEnd();
5617 break;
5618 case LabelKind::TryTable:
5619 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"
, 5619); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5619; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5620 if (!f.finishTryTable(control, &postJoinDefs)) {
5621 return false;
5622 }
5623 f.freeTryControl(std::move(control.tryControl));
5624 f.iter().popEnd();
5625 break;
5626 }
5627
5628 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"
, 5628); AnnotateMozCrashReason("MOZ_ASSERT" "(" "postJoinDefs.length() == type.length()"
")"); do { *((volatile int*)__null) = 5628; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5629 f.iter().setResults(postJoinDefs.length(), postJoinDefs);
5630
5631 return true;
5632}
5633
5634static bool EmitBr(FunctionCompiler& f) {
5635 uint32_t relativeDepth;
5636 ResultType type;
5637 DefVector values;
5638 if (!f.iter().readBr(&relativeDepth, &type, &values)) {
5639 return false;
5640 }
5641
5642 return f.br(relativeDepth, values);
5643}
5644
5645static bool EmitBrIf(FunctionCompiler& f) {
5646 uint32_t relativeDepth;
5647 ResultType type;
5648 DefVector values;
5649 MDefinition* condition;
5650
5651 BranchHint branchHint =
5652 f.iter().getBranchHint(f.funcIndex(), f.relativeBytecodeOffset());
5653
5654 if (!f.iter().readBrIf(&relativeDepth, &type, &values, &condition)) {
5655 return false;
5656 }
5657
5658 return f.brIf(relativeDepth, values, condition, branchHint);
5659}
5660
5661static bool EmitBrTable(FunctionCompiler& f) {
5662 Uint32Vector depths;
5663 uint32_t defaultDepth;
5664 ResultType branchValueType;
5665 DefVector branchValues;
5666 MDefinition* index;
5667 if (!f.iter().readBrTable(&depths, &defaultDepth, &branchValueType,
5668 &branchValues, &index)) {
5669 return false;
5670 }
5671
5672 // If all the targets are the same, or there are no targets, we can just
5673 // use a goto. This is not just an optimization: MaybeFoldConditionBlock
5674 // assumes that tables have more than one successor.
5675 bool allSameDepth = true;
5676 for (uint32_t depth : depths) {
5677 if (depth != defaultDepth) {
5678 allSameDepth = false;
5679 break;
5680 }
5681 }
5682
5683 if (allSameDepth) {
5684 return f.br(defaultDepth, branchValues);
5685 }
5686
5687 return f.brTable(index, defaultDepth, depths, branchValues);
5688}
5689
5690static bool EmitReturn(FunctionCompiler& f) {
5691 DefVector values;
5692 if (!f.iter().readReturn(&values)) {
5693 return false;
5694 }
5695
5696 return f.returnValues(std::move(values));
5697}
5698
5699static bool EmitUnreachable(FunctionCompiler& f) {
5700 if (!f.iter().readUnreachable()) {
5701 return false;
5702 }
5703
5704 f.unreachableTrap();
5705 return true;
5706}
5707
5708static bool EmitTry(FunctionCompiler& f) {
5709 ResultType params;
5710 if (!f.iter().readTry(&params)) {
5711 return false;
5712 }
5713
5714 return f.startTry();
5715}
5716
5717static bool EmitCatch(FunctionCompiler& f) {
5718 LabelKind kind;
5719 uint32_t tagIndex;
5720 ResultType paramType, resultType;
5721 DefVector tryValues;
5722 if (!f.iter().readCatch(&kind, &tagIndex, &paramType, &resultType,
5723 &tryValues)) {
5724 return false;
5725 }
5726
5727 // Pushing the results of the previous block, to properly join control flow
5728 // after the try and after each handler, as well as potential control flow
5729 // patches from other instrunctions. This is similar to what is done for
5730 // if-then-else control flow and for most other control control flow joins.
5731 if (!f.pushDefs(tryValues)) {
5732 return false;
5733 }
5734
5735 return f.switchToCatch(f.iter().controlItem(), kind, tagIndex);
5736}
5737
5738static bool EmitCatchAll(FunctionCompiler& f) {
5739 LabelKind kind;
5740 ResultType paramType, resultType;
5741 DefVector tryValues;
5742 if (!f.iter().readCatchAll(&kind, &paramType, &resultType, &tryValues)) {
5743 return false;
5744 }
5745
5746 // Pushing the results of the previous block, to properly join control flow
5747 // after the try and after each handler, as well as potential control flow
5748 // patches from other instrunctions.
5749 if (!f.pushDefs(tryValues)) {
5750 return false;
5751 }
5752
5753 return f.switchToCatch(f.iter().controlItem(), kind, CatchAllIndex);
5754}
5755
5756static bool EmitTryTable(FunctionCompiler& f) {
5757 ResultType params;
5758 TryTableCatchVector catches;
5759 if (!f.iter().readTryTable(&params, &catches)) {
5760 return false;
5761 }
5762
5763 return f.startTryTable(std::move(catches));
5764}
5765
5766static bool EmitDelegate(FunctionCompiler& f) {
5767 uint32_t relativeDepth;
5768 ResultType resultType;
5769 DefVector tryValues;
5770 if (!f.iter().readDelegate(&relativeDepth, &resultType, &tryValues)) {
5771 return false;
5772 }
5773
5774 Control& control = f.iter().controlItem();
5775 MBasicBlock* block = control.block;
5776 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"
, 5776); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5776; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5777
5778 // Unless the entire try-delegate is dead code, delegate any pad-patches from
5779 // this try to the next try-block above relativeDepth.
5780 if (block) {
5781 ControlInstructionVector& delegatePadPatches =
5782 control.tryControl->landingPadPatches;
5783 if (!f.delegatePadPatches(delegatePadPatches, relativeDepth)) {
5784 return false;
5785 }
5786 }
5787 f.freeTryControl(std::move(control.tryControl));
5788 f.iter().popDelegate();
5789
5790 // Push the results of the previous block, and join control flow with
5791 // potential control flow patches from other instrunctions in the try code.
5792 // This is similar to what is done for EmitEnd.
5793 if (!f.pushDefs(tryValues)) {
5794 return false;
5795 }
5796 DefVector postJoinDefs;
5797 if (!f.finishBlock(&postJoinDefs)) {
5798 return false;
5799 }
5800 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"
, 5800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "postJoinDefs.length() == resultType.length()"
")"); do { *((volatile int*)__null) = 5800; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5801 f.iter().setResults(postJoinDefs.length(), postJoinDefs);
5802
5803 return true;
5804}
5805
5806static bool EmitThrow(FunctionCompiler& f) {
5807 uint32_t tagIndex;
5808 DefVector argValues;
5809 if (!f.iter().readThrow(&tagIndex, &argValues)) {
5810 return false;
5811 }
5812
5813 return f.emitThrow(tagIndex, argValues);
5814}
5815
5816static bool EmitThrowRef(FunctionCompiler& f) {
5817 MDefinition* exnRef;
5818 if (!f.iter().readThrowRef(&exnRef)) {
5819 return false;
5820 }
5821
5822 return f.emitThrowRef(exnRef);
5823}
5824
5825static bool EmitRethrow(FunctionCompiler& f) {
5826 uint32_t relativeDepth;
5827 if (!f.iter().readRethrow(&relativeDepth)) {
5828 return false;
5829 }
5830
5831 return f.emitRethrow(relativeDepth);
5832}
5833
5834static bool EmitInlineCall(FunctionCompiler& callerCompiler,
5835 const FuncType& funcType, uint32_t funcIndex,
5836 InliningHeuristics::CallKind callKind,
5837 const DefVector& args, DefVector* results) {
5838 UniqueChars error;
5839 const Bytes& bytecode = callerCompiler.codeMeta().bytecode->bytes;
5840 const FuncDefRange& funcRange =
5841 callerCompiler.codeMeta().funcDefRange(funcIndex);
5842 const uint8_t* bodyBegin = bytecode.begin() + funcRange.bytecodeOffset;
5843 const uint8_t* bodyEnd = bodyBegin + funcRange.bodyLength;
5844 FuncCompileInput func(funcIndex, funcRange.bytecodeOffset, bodyBegin, bodyEnd,
5845 Uint32Vector());
5846 Decoder d(func.begin, func.end, func.lineOrBytecode, &error);
5847
5848 ValTypeVector locals;
5849 if (!DecodeLocalEntriesWithParams(d, callerCompiler.codeMeta(), funcIndex,
5850 &locals)) {
5851 return false;
5852 }
5853
5854 CompileInfo* compileInfo = callerCompiler.addInlineCallInfo(locals.length());
5855 if (!compileInfo) {
5856 return false;
5857 }
5858
5859 // Find the top level compiler for the function
5860 FunctionCompiler* toplevel = nullptr;
5861 if (callerCompiler.toplevelCompiler()) {
5862 toplevel = callerCompiler.toplevelCompiler();
5863 } else {
5864 toplevel = &callerCompiler;
5865 }
5866
5867 // Update inlining stats
5868 toplevel->updateInliningStats(funcRange.bodyLength, callKind);
5869
5870 FunctionCompiler calleeCompiler(toplevel, &callerCompiler, d, func, locals,
5871 *compileInfo);
5872 if (!calleeCompiler.initInline(args)) {
5873 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"
, 5873); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!error" ")"
); do { *((volatile int*)__null) = 5873; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5874 return false;
5875 }
5876
5877 if (!calleeCompiler.startBlock()) {
5878 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"
, 5878); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!error" ")"
); do { *((volatile int*)__null) = 5878; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5879 return false;
5880 }
5881
5882 if (!EmitBodyExprs(calleeCompiler)) {
5883 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"
, 5883); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!error" ")"
); do { *((volatile int*)__null) = 5883; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5884 return false;
5885 }
5886
5887 calleeCompiler.finish();
5888
5889 return callerCompiler.finishInlinedCallDirect(calleeCompiler, results);
5890}
5891
5892static bool EmitCallArgs(FunctionCompiler& f, const FuncType& funcType,
5893 const DefVector& args, CallCompileState* call) {
5894 for (size_t i = 0, n = funcType.args().length(); i < n; ++i) {
5895 if (!f.mirGen().ensureBallast()) {
5896 return false;
5897 }
5898 if (!f.passArg(args[i], funcType.args()[i], call)) {
5899 return false;
5900 }
5901 }
5902
5903 ResultType resultType = ResultType::Vector(funcType.results());
5904 if (!f.passStackResultAreaCallArg(resultType, call)) {
5905 return false;
5906 }
5907
5908 return f.finishCall(call);
5909}
5910
5911static bool EmitCall(FunctionCompiler& f, bool asmJSFuncDef) {
5912 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5913
5914 uint32_t funcIndex;
5915 DefVector args;
5916 if (asmJSFuncDef) {
5917 if (!f.iter().readOldCallDirect(f.codeMeta().numFuncImports, &funcIndex,
5918 &args)) {
5919 return false;
5920 }
5921 } else {
5922 if (!f.iter().readCall(&funcIndex, &args)) {
5923 return false;
5924 }
5925 }
5926
5927 if (f.inDeadCode()) {
5928 return true;
5929 }
5930
5931 const FuncType& funcType = f.codeMeta().getFuncType(funcIndex);
5932
5933 DefVector results;
5934 if (f.codeMeta().funcIsImport(funcIndex)) {
5935 BuiltinModuleFuncId knownFuncImport =
5936 f.codeMeta().knownFuncImport(funcIndex);
5937 if (knownFuncImport != BuiltinModuleFuncId::None) {
5938 const BuiltinModuleFunc& builtinModuleFunc =
5939 BuiltinModuleFuncs::getFromId(knownFuncImport);
5940 return f.callBuiltinModuleFunc(builtinModuleFunc, args);
5941 }
5942
5943 CallCompileState call;
5944 if (!EmitCallArgs(f, funcType, args, &call)) {
5945 return false;
5946 }
5947
5948 uint32_t instanceDataOffset =
5949 f.codeMeta().offsetOfFuncImportInstanceData(funcIndex);
5950 if (!f.callImport(instanceDataOffset, lineOrBytecode, call, funcType,
5951 &results)) {
5952 return false;
5953 }
5954 } else {
5955 const auto callKind = InliningHeuristics::CallKind::Direct;
5956 if (f.shouldInlineCall(callKind, funcIndex)) {
5957 if (!EmitInlineCall(f, funcType, funcIndex, callKind, args, &results)) {
5958 return false;
5959 }
5960 } else {
5961 CallCompileState call;
5962 if (!EmitCallArgs(f, funcType, args, &call)) {
5963 return false;
5964 }
5965 if (!f.callDirect(funcType, funcIndex, lineOrBytecode, call, &results)) {
5966 return false;
5967 }
5968 }
5969 }
5970
5971 f.iter().setResults(results.length(), results);
5972 return true;
5973}
5974
5975static bool EmitCallIndirect(FunctionCompiler& f, bool oldStyle) {
5976 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5977
5978 uint32_t funcTypeIndex;
5979 uint32_t tableIndex;
5980 MDefinition* callee;
5981 DefVector args;
5982 if (oldStyle) {
5983 tableIndex = 0;
5984 if (!f.iter().readOldCallIndirect(&funcTypeIndex, &callee, &args)) {
5985 return false;
5986 }
5987 } else {
5988 if (!f.iter().readCallIndirect(&funcTypeIndex, &tableIndex, &callee,
5989 &args)) {
5990 return false;
5991 }
5992 }
5993
5994 if (f.inDeadCode()) {
5995 return true;
5996 }
5997
5998 const FuncType& funcType = (*f.codeMeta().types)[funcTypeIndex].funcType();
5999
6000 CallCompileState call;
6001 if (!EmitCallArgs(f, funcType, args, &call)) {
6002 return false;
6003 }
6004
6005 DefVector results;
6006 if (!f.callIndirect(funcTypeIndex, tableIndex, callee, lineOrBytecode, call,
6007 &results)) {
6008 return false;
6009 }
6010
6011 f.iter().setResults(results.length(), results);
6012 return true;
6013}
6014
6015#ifdef ENABLE_WASM_JSPI1
6016static bool EmitStackSwitch(FunctionCompiler& f) {
6017 StackSwitchKind kind;
6018 MDefinition* suspender;
6019 MDefinition* fn;
6020 MDefinition* data;
6021 if (!f.iter().readStackSwitch(&kind, &suspender, &fn, &data)) {
6022 return false;
6023 }
6024 if (!f.stackSwitch(suspender, fn, data, kind)) {
6025 return false;
6026 }
6027 return true;
6028}
6029#endif
6030
6031#ifdef ENABLE_WASM_TAIL_CALLS1
6032static bool EmitReturnCall(FunctionCompiler& f) {
6033 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
6034
6035 uint32_t funcIndex;
6036 DefVector args;
6037 if (!f.iter().readReturnCall(&funcIndex, &args)) {
6038 return false;
6039 }
6040
6041 if (f.inDeadCode()) {
6042 return true;
6043 }
6044
6045 const FuncType& funcType = f.codeMeta().getFuncType(funcIndex);
6046
6047 CallCompileState call;
6048 f.markReturnCall(&call);
6049 if (!EmitCallArgs(f, funcType, args, &call)) {
6050 return false;
6051 }
6052
6053 DefVector results;
6054 if (f.codeMeta().funcIsImport(funcIndex)) {
6055 uint32_t globalDataOffset =
6056 f.codeMeta().offsetOfFuncImportInstanceData(funcIndex);
6057 if (!f.returnCallImport(globalDataOffset, lineOrBytecode, call, funcType,
6058 &results)) {
6059 return false;
6060 }
6061 } else {
6062 if (!f.returnCallDirect(funcType, funcIndex, lineOrBytecode, call,
6063 &results)) {
6064 return false;
6065 }
6066 }
6067 return true;
6068}
6069
6070static bool EmitReturnCallIndirect(FunctionCompiler& f) {
6071 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
6072
6073 uint32_t funcTypeIndex;
6074 uint32_t tableIndex;
6075 MDefinition* callee;
6076 DefVector args;
6077 if (!f.iter().readReturnCallIndirect(&funcTypeIndex, &tableIndex, &callee,
6078 &args)) {
6079 return false;
6080 }
6081
6082 if (f.inDeadCode()) {
6083 return true;
6084 }
6085
6086 const FuncType& funcType = (*f.codeMeta().types)[funcTypeIndex].funcType();
6087
6088 CallCompileState call;
6089 f.markReturnCall(&call);
6090 if (!EmitCallArgs(f, funcType, args, &call)) {
6091 return false;
6092 }
6093
6094 DefVector results;
6095 return f.returnCallIndirect(funcTypeIndex, tableIndex, callee, lineOrBytecode,
6096 call, &results);
6097}
6098#endif
6099
6100#if defined(ENABLE_WASM_TAIL_CALLS1) && defined(ENABLE_WASM_GC1)
6101static bool EmitReturnCallRef(FunctionCompiler& f) {
6102 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
6103
6104 const FuncType* funcType;
6105 MDefinition* callee;
6106 DefVector args;
6107
6108 if (!f.iter().readReturnCallRef(&funcType, &callee, &args)) {
6109 return false;
6110 }
6111
6112 if (f.inDeadCode()) {
6113 return true;
6114 }
6115
6116 CallCompileState call;
6117 f.markReturnCall(&call);
6118 if (!EmitCallArgs(f, *funcType, args, &call)) {
6119 return false;
6120 }
6121
6122 DefVector results;
6123 return f.returnCallRef(*funcType, callee, lineOrBytecode, call, &results);
6124}
6125#endif
6126
6127static bool EmitGetLocal(FunctionCompiler& f) {
6128 uint32_t id;
6129 if (!f.iter().readGetLocal(f.locals(), &id)) {
6130 return false;
6131 }
6132
6133 f.iter().setResult(f.getLocalDef(id));
6134 return true;
6135}
6136
6137static bool EmitSetLocal(FunctionCompiler& f) {
6138 uint32_t id;
6139 MDefinition* value;
6140 if (!f.iter().readSetLocal(f.locals(), &id, &value)) {
6141 return false;
6142 }
6143
6144 f.assign(id, value);
6145 return true;
6146}
6147
6148static bool EmitTeeLocal(FunctionCompiler& f) {
6149 uint32_t id;
6150 MDefinition* value;
6151 if (!f.iter().readTeeLocal(f.locals(), &id, &value)) {
6152 return false;
6153 }
6154
6155 f.assign(id, value);
6156 return true;
6157}
6158
6159static bool EmitGetGlobal(FunctionCompiler& f) {
6160 uint32_t id;
6161 if (!f.iter().readGetGlobal(&id)) {
6162 return false;
6163 }
6164
6165 const GlobalDesc& global = f.codeMeta().globals[id];
6166 if (!global.isConstant()) {
6167 f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(),
6168 global.isIndirect(),
6169 global.type().toMIRType()));
6170 return true;
6171 }
6172
6173 LitVal value = global.constantValue();
6174
6175 MDefinition* result;
6176 switch (value.type().kind()) {
6177 case ValType::I32:
6178 result = f.constantI32(int32_t(value.i32()));
6179 break;
6180 case ValType::I64:
6181 result = f.constantI64(int64_t(value.i64()));
6182 break;
6183 case ValType::F32:
6184 result = f.constantF32(value.f32());
6185 break;
6186 case ValType::F64:
6187 result = f.constantF64(value.f64());
6188 break;
6189 case ValType::V128:
6190#ifdef ENABLE_WASM_SIMD1
6191 result = f.constantV128(value.v128());
6192 break;
6193#else
6194 return f.iter().fail("Ion has no SIMD support yet");
6195#endif
6196 case ValType::Ref:
6197 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"
, 6197); AnnotateMozCrashReason("MOZ_ASSERT" "(" "value.ref().isNull()"
")"); do { *((volatile int*)__null) = 6197; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6198 result = f.constantNullRef();
6199 break;
6200 default:
6201 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"
, 6201); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in EmitGetGlobal"
")"); do { *((volatile int*)__null) = 6201; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
6202 }
6203
6204 f.iter().setResult(result);
6205 return true;
6206}
6207
6208static bool EmitSetGlobal(FunctionCompiler& f) {
6209 uint32_t bytecodeOffset = f.readBytecodeOffset();
6210
6211 uint32_t id;
6212 MDefinition* value;
6213 if (!f.iter().readSetGlobal(&id, &value)) {
6214 return false;
6215 }
6216
6217 const GlobalDesc& global = f.codeMeta().globals[id];
6218 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"
, 6218); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global.isMutable()"
")"); do { *((volatile int*)__null) = 6218; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6219 return f.storeGlobalVar(bytecodeOffset, global.offset(), global.isIndirect(),
6220 value);
6221}
6222
6223static bool EmitTeeGlobal(FunctionCompiler& f) {
6224 uint32_t bytecodeOffset = f.readBytecodeOffset();
6225
6226 uint32_t id;
6227 MDefinition* value;
6228 if (!f.iter().readTeeGlobal(&id, &value)) {
6229 return false;
6230 }
6231
6232 const GlobalDesc& global = f.codeMeta().globals[id];
6233 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"
, 6233); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global.isMutable()"
")"); do { *((volatile int*)__null) = 6233; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6234
6235 return f.storeGlobalVar(bytecodeOffset, global.offset(), global.isIndirect(),
6236 value);
6237}
6238
6239template <typename MIRClass>
6240static bool EmitUnary(FunctionCompiler& f, ValType operandType) {
6241 MDefinition* input;
6242 if (!f.iter().readUnary(operandType, &input)) {
6243 return false;
6244 }
6245
6246 f.iter().setResult(f.unary<MIRClass>(input));
6247 return true;
6248}
6249
6250template <typename MIRClass>
6251static bool EmitConversion(FunctionCompiler& f, ValType operandType,
6252 ValType resultType) {
6253 MDefinition* input;
6254 if (!f.iter().readConversion(operandType, resultType, &input)) {
6255 return false;
6256 }
6257
6258 f.iter().setResult(f.unary<MIRClass>(input));
6259 return true;
6260}
6261
6262template <typename MIRClass>
6263static bool EmitUnaryWithType(FunctionCompiler& f, ValType operandType,
6264 MIRType mirType) {
6265 MDefinition* input;
6266 if (!f.iter().readUnary(operandType, &input)) {
6267 return false;
6268 }
6269
6270 f.iter().setResult(f.unary<MIRClass>(input, mirType));
6271 return true;
6272}
6273
6274template <typename MIRClass>
6275static bool EmitConversionWithType(FunctionCompiler& f, ValType operandType,
6276 ValType resultType, MIRType mirType) {
6277 MDefinition* input;
6278 if (!f.iter().readConversion(operandType, resultType, &input)) {
6279 return false;
6280 }
6281
6282 f.iter().setResult(f.unary<MIRClass>(input, mirType));
6283 return true;
6284}
6285
6286static bool EmitTruncate(FunctionCompiler& f, ValType operandType,
6287 ValType resultType, bool isUnsigned,
6288 bool isSaturating) {
6289 MDefinition* input = nullptr;
6290 if (!f.iter().readConversion(operandType, resultType, &input)) {
6291 return false;
6292 }
6293
6294 TruncFlags flags = 0;
6295 if (isUnsigned) {
6296 flags |= TRUNC_UNSIGNED;
6297 }
6298 if (isSaturating) {
6299 flags |= TRUNC_SATURATING;
6300 }
6301 if (resultType == ValType::I32) {
6302 if (f.codeMeta().isAsmJS()) {
6303 if (f.inDeadCode()) {
6304 // The read callsite line, produced by prepareCall, has to be
6305 // consumed -- the MWasmBuiltinTruncateToInt32 and MTruncateToInt32
6306 // will not create MIR node.
6307 (void)f.readCallSiteLineOrBytecode();
6308 f.iter().setResult(nullptr);
6309 } else if (input && (input->type() == MIRType::Double ||
6310 input->type() == MIRType::Float32)) {
6311 f.iter().setResult(f.unary<MWasmBuiltinTruncateToInt32>(input));
6312 } else {
6313 f.iter().setResult(f.unary<MTruncateToInt32>(input));
6314 }
6315 } else {
6316 f.iter().setResult(f.truncate<MWasmTruncateToInt32>(input, flags));
6317 }
6318 } else {
6319 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"
, 6319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "resultType == ValType::I64"
")"); do { *((volatile int*)__null) = 6319; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6320 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"
, 6320); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!f.codeMeta().isAsmJS()"
")"); do { *((volatile int*)__null) = 6320; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6321#if defined(JS_CODEGEN_ARM)
6322 f.iter().setResult(f.truncateWithInstance(input, flags));
6323#else
6324 f.iter().setResult(f.truncate<MWasmTruncateToInt64>(input, flags));
6325#endif
6326 }
6327 return true;
6328}
6329
6330static bool EmitSignExtend(FunctionCompiler& f, uint32_t srcSize,
6331 uint32_t targetSize) {
6332 MDefinition* input;
6333 ValType type = targetSize == 4 ? ValType::I32 : ValType::I64;
6334 if (!f.iter().readConversion(type, type, &input)) {
6335 return false;
6336 }
6337
6338 f.iter().setResult(f.signExtend(input, srcSize, targetSize));
6339 return true;
6340}
6341
6342static bool EmitExtendI32(FunctionCompiler& f, bool isUnsigned) {
6343 MDefinition* input;
6344 if (!f.iter().readConversion(ValType::I32, ValType::I64, &input)) {
6345 return false;
6346 }
6347
6348 f.iter().setResult(f.extendI32(input, isUnsigned));
6349 return true;
6350}
6351
6352static bool EmitConvertI64ToFloatingPoint(FunctionCompiler& f,
6353 ValType resultType, MIRType mirType,
6354 bool isUnsigned) {
6355 MDefinition* input;
6356 if (!f.iter().readConversion(ValType::I64, resultType, &input)) {
6357 return false;
6358 }
6359
6360 f.iter().setResult(f.convertI64ToFloatingPoint(input, mirType, isUnsigned));
6361 return true;
6362}
6363
6364static bool EmitReinterpret(FunctionCompiler& f, ValType resultType,
6365 ValType operandType, MIRType mirType) {
6366 MDefinition* input;
6367 if (!f.iter().readConversion(operandType, resultType, &input)) {
6368 return false;
6369 }
6370
6371 f.iter().setResult(f.unary<MWasmReinterpret>(input, mirType));
6372 return true;
6373}
6374
6375static bool EmitAdd(FunctionCompiler& f, ValType type, MIRType mirType) {
6376 MDefinition* lhs;
6377 MDefinition* rhs;
6378 if (!f.iter().readBinary(type, &lhs, &rhs)) {
6379 return false;
6380 }
6381
6382 f.iter().setResult(f.add(lhs, rhs, mirType));
6383 return true;
6384}
6385
6386static bool EmitSub(FunctionCompiler& f, ValType type, MIRType mirType) {
6387 MDefinition* lhs;
6388 MDefinition* rhs;
6389 if (!f.iter().readBinary(type, &lhs, &rhs)) {
6390 return false;
6391 }
6392
6393 f.iter().setResult(f.sub(lhs, rhs, mirType));
6394 return true;
6395}
6396
6397static bool EmitRotate(FunctionCompiler& f, ValType type, bool isLeftRotation) {
6398 MDefinition* lhs;
6399 MDefinition* rhs;
6400 if (!f.iter().readBinary(type, &lhs, &rhs)) {
6401 return false;
6402 }
6403
6404 MDefinition* result = f.rotate(lhs, rhs, type.toMIRType(), isLeftRotation);
6405 f.iter().setResult(result);
6406 return true;
6407}
6408
6409static bool EmitBitNot(FunctionCompiler& f, ValType operandType) {
6410 MDefinition* input;
6411 if (!f.iter().readUnary(operandType, &input)) {
6412 return false;
6413 }
6414
6415 f.iter().setResult(f.bitnot(input));
6416 return true;
6417}
6418
6419static bool EmitBitwiseAndOrXor(FunctionCompiler& f, ValType operandType,
6420 MIRType mirType,
6421 MWasmBinaryBitwise::SubOpcode subOpc) {
6422 MDefinition* lhs;
6423 MDefinition* rhs;
6424 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6425 return false;
6426 }
6427
6428 f.iter().setResult(f.binary<MWasmBinaryBitwise>(lhs, rhs, mirType, subOpc));
6429 return true;
6430}
6431
6432template <typename MIRClass>
6433static bool EmitShift(FunctionCompiler& f, ValType operandType,
6434 MIRType mirType) {
6435 MDefinition* lhs;
6436 MDefinition* rhs;
6437 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6438 return false;
6439 }
6440
6441 f.iter().setResult(f.binary<MIRClass>(lhs, rhs, mirType));
6442 return true;
6443}
6444
6445static bool EmitUrsh(FunctionCompiler& f, ValType operandType,
6446 MIRType mirType) {
6447 MDefinition* lhs;
6448 MDefinition* rhs;
6449 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6450 return false;
6451 }
6452
6453 f.iter().setResult(f.ursh(lhs, rhs, mirType));
6454 return true;
6455}
6456
6457static bool EmitMul(FunctionCompiler& f, ValType operandType, MIRType mirType) {
6458 MDefinition* lhs;
6459 MDefinition* rhs;
6460 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6461 return false;
6462 }
6463
6464 f.iter().setResult(
6465 f.mul(lhs, rhs, mirType,
6466 mirType == MIRType::Int32 ? MMul::Integer : MMul::Normal));
6467 return true;
6468}
6469
6470static bool EmitDiv(FunctionCompiler& f, ValType operandType, MIRType mirType,
6471 bool isUnsigned) {
6472 MDefinition* lhs;
6473 MDefinition* rhs;
6474 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6475 return false;
6476 }
6477
6478 f.iter().setResult(f.div(lhs, rhs, mirType, isUnsigned));
6479 return true;
6480}
6481
6482static bool EmitRem(FunctionCompiler& f, ValType operandType, MIRType mirType,
6483 bool isUnsigned) {
6484 MDefinition* lhs;
6485 MDefinition* rhs;
6486 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6487 return false;
6488 }
6489
6490 f.iter().setResult(f.mod(lhs, rhs, mirType, isUnsigned));
6491 return true;
6492}
6493
6494static bool EmitMinMax(FunctionCompiler& f, ValType operandType,
6495 MIRType mirType, bool isMax) {
6496 MDefinition* lhs;
6497 MDefinition* rhs;
6498 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6499 return false;
6500 }
6501
6502 f.iter().setResult(f.minMax(lhs, rhs, mirType, isMax));
6503 return true;
6504}
6505
6506static bool EmitCopySign(FunctionCompiler& f, ValType operandType) {
6507 MDefinition* lhs;
6508 MDefinition* rhs;
6509 if (!f.iter().readBinary(operandType, &lhs, &rhs)) {
6510 return false;
6511 }
6512
6513 f.iter().setResult(f.binary<MCopySign>(lhs, rhs, operandType.toMIRType()));
6514 return true;
6515}
6516
6517static bool EmitComparison(FunctionCompiler& f, ValType operandType,
6518 JSOp compareOp, MCompare::CompareType compareType) {
6519 MDefinition* lhs;
6520 MDefinition* rhs;
6521 if (!f.iter().readComparison(operandType, &lhs, &rhs)) {
6522 return false;
6523 }
6524
6525 f.iter().setResult(f.compare(lhs, rhs, compareOp, compareType));
6526 return true;
6527}
6528
6529static bool EmitSelect(FunctionCompiler& f, bool typed) {
6530 StackType type;
6531 MDefinition* trueValue;
6532 MDefinition* falseValue;
6533 MDefinition* condition;
6534 if (!f.iter().readSelect(typed, &type, &trueValue, &falseValue, &condition)) {
6535 return false;
6536 }
6537
6538 f.iter().setResult(f.select(trueValue, falseValue, condition));
6539 return true;
6540}
6541
6542static bool EmitLoad(FunctionCompiler& f, ValType type, Scalar::Type viewType) {
6543 LinearMemoryAddress<MDefinition*> addr;
6544 if (!f.iter().readLoad(type, Scalar::byteSize(viewType), &addr)) {
6545 return false;
6546 }
6547
6548 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6549 f.bytecodeIfNotAsmJS(),
6550 f.hugeMemoryEnabled(addr.memoryIndex));
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 EmitStore(FunctionCompiler& f, ValType resultType,
6561 Scalar::Type viewType) {
6562 LinearMemoryAddress<MDefinition*> addr;
6563 MDefinition* value;
6564 if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr,
6565 &value)) {
6566 return false;
6567 }
6568
6569 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6570 f.bytecodeIfNotAsmJS(),
6571 f.hugeMemoryEnabled(addr.memoryIndex));
6572
6573 f.store(addr.base, &access, value);
6574 return true;
6575}
6576
6577static bool EmitTeeStore(FunctionCompiler& f, ValType resultType,
6578 Scalar::Type viewType) {
6579 LinearMemoryAddress<MDefinition*> addr;
6580 MDefinition* value;
6581 if (!f.iter().readTeeStore(resultType, Scalar::byteSize(viewType), &addr,
6582 &value)) {
6583 return false;
6584 }
6585
6586 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"
, 6586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f.isMem32(addr.memoryIndex)"
")"); do { *((volatile int*)__null) = 6586; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
; // asm.js opcode
6587 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6588 f.bytecodeIfNotAsmJS(),
6589 f.hugeMemoryEnabled(addr.memoryIndex));
6590
6591 f.store(addr.base, &access, value);
6592 return true;
6593}
6594
6595static bool EmitTeeStoreWithCoercion(FunctionCompiler& f, ValType resultType,
6596 Scalar::Type viewType) {
6597 LinearMemoryAddress<MDefinition*> addr;
6598 MDefinition* value;
6599 if (!f.iter().readTeeStore(resultType, Scalar::byteSize(viewType), &addr,
6600 &value)) {
6601 return false;
6602 }
6603
6604 if (resultType == ValType::F32 && viewType == Scalar::Float64) {
6605 value = f.unary<MToDouble>(value);
6606 } else if (resultType == ValType::F64 && viewType == Scalar::Float32) {
6607 value = f.unary<MToFloat32>(value);
6608 } else {
6609 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"
, 6609); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected coerced store"
")"); do { *((volatile int*)__null) = 6609; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
6610 }
6611
6612 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"
, 6612); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f.isMem32(addr.memoryIndex)"
")"); do { *((volatile int*)__null) = 6612; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
; // asm.js opcode
6613 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
6614 f.bytecodeIfNotAsmJS(),
6615 f.hugeMemoryEnabled(addr.memoryIndex));
6616
6617 f.store(addr.base, &access, value);
6618 return true;
6619}
6620
6621static bool TryInlineUnaryBuiltin(FunctionCompiler& f, SymbolicAddress callee,
6622 MDefinition* input) {
6623 if (!input) {
6624 return false;
6625 }
6626
6627 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"
, 6627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(input->type())"
")"); do { *((volatile int*)__null) = 6627; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6628
6629 RoundingMode mode;
6630 if (!IsRoundingFunction(callee, &mode)) {
6631 return false;
6632 }
6633
6634 if (!MNearbyInt::HasAssemblerSupport(mode)) {
6635 return false;
6636 }
6637
6638 f.iter().setResult(f.nearbyInt(input, mode));
6639 return true;
6640}
6641
6642static bool EmitUnaryMathBuiltinCall(FunctionCompiler& f,
6643 const SymbolicAddressSignature& callee) {
6644 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"
, 6644); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.numArgs == 1"
")"); do { *((volatile int*)__null) = 6644; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6645
6646 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
6647
6648 MDefinition* input;
6649 if (!f.iter().readUnary(ValType::fromMIRType(callee.argTypes[0]), &input)) {
6650 return false;
6651 }
6652
6653 if (TryInlineUnaryBuiltin(f, callee.identity, input)) {
6654 return true;
6655 }
6656
6657 CallCompileState call;
6658 if (!f.passArg(input, callee.argTypes[0], &call)) {
6659 return false;
6660 }
6661
6662 if (!f.finishCall(&call)) {
6663 return false;
6664 }
6665
6666 MDefinition* def;
6667 if (!f.builtinCall(callee, lineOrBytecode, call, &def)) {
6668 return false;
6669 }
6670
6671 f.iter().setResult(def);
6672 return true;
6673}
6674
6675static bool EmitBinaryMathBuiltinCall(FunctionCompiler& f,
6676 const SymbolicAddressSignature& callee) {
6677 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"
, 6677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.numArgs == 2"
")"); do { *((volatile int*)__null) = 6677; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6678 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"
, 6678); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.argTypes[0] == callee.argTypes[1]"
")"); do { *((volatile int*)__null) = 6678; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6679
6680 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
6681
6682 CallCompileState call;
6683 MDefinition* lhs;
6684 MDefinition* rhs;
6685 // This call to readBinary assumes both operands have the same type.
6686 if (!f.iter().readBinary(ValType::fromMIRType(callee.argTypes[0]), &lhs,
6687 &rhs)) {
6688 return false;
6689 }
6690
6691 if (!f.passArg(lhs, callee.argTypes[0], &call)) {
6692 return false;
6693 }
6694
6695 if (!f.passArg(rhs, callee.argTypes[1], &call)) {
6696 return false;
6697 }
6698
6699 if (!f.finishCall(&call)) {
6700 return false;
6701 }
6702
6703 MDefinition* def;
6704 if (!f.builtinCall(callee, lineOrBytecode, call, &def)) {
6705 return false;
6706 }
6707
6708 f.iter().setResult(def);
6709 return true;
6710}
6711
6712static bool EmitMemoryGrow(FunctionCompiler& f) {
6713 uint32_t bytecodeOffset = f.readBytecodeOffset();
6714
6715 MDefinition* delta;
6716 uint32_t memoryIndex;
6717 if (!f.iter().readMemoryGrow(&memoryIndex, &delta)) {
6718 return false;
6719 }
6720
6721 if (f.inDeadCode()) {
6722 return true;
6723 }
6724
6725 MDefinition* memoryIndexValue = f.constantI32(int32_t(memoryIndex));
6726 if (!memoryIndexValue) {
6727 return false;
6728 }
6729
6730 const SymbolicAddressSignature& callee =
6731 f.isMem32(memoryIndex) ? SASigMemoryGrowM32 : SASigMemoryGrowM64;
6732
6733 MDefinition* ret;
6734 if (!f.emitInstanceCall2(bytecodeOffset, callee, delta, memoryIndexValue,
6735 &ret)) {
6736 return false;
6737 }
6738
6739 f.iter().setResult(ret);
6740 return true;
6741}
6742
6743static bool EmitMemorySize(FunctionCompiler& f) {
6744 uint32_t bytecodeOffset = f.readBytecodeOffset();
6745
6746 uint32_t memoryIndex;
6747 if (!f.iter().readMemorySize(&memoryIndex)) {
6748 return false;
6749 }
6750
6751 if (f.inDeadCode()) {
6752 return true;
6753 }
6754
6755 MDefinition* memoryIndexValue = f.constantI32(int32_t(memoryIndex));
6756 if (!memoryIndexValue) {
6757 return false;
6758 }
6759
6760 const SymbolicAddressSignature& callee =
6761 f.isMem32(memoryIndex) ? SASigMemorySizeM32 : SASigMemorySizeM64;
6762
6763 MDefinition* ret;
6764 if (!f.emitInstanceCall1(bytecodeOffset, callee, memoryIndexValue, &ret)) {
6765 return false;
6766 }
6767
6768 f.iter().setResult(ret);
6769 return true;
6770}
6771
6772static bool EmitAtomicCmpXchg(FunctionCompiler& f, ValType type,
6773 Scalar::Type viewType) {
6774 LinearMemoryAddress<MDefinition*> addr;
6775 MDefinition* oldValue;
6776 MDefinition* newValue;
6777 if (!f.iter().readAtomicCmpXchg(&addr, type, byteSize(viewType), &oldValue,
6778 &newValue)) {
6779 return false;
6780 }
6781
6782 MemoryAccessDesc access(
6783 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6784 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Full());
6785 auto* ins =
6786 f.atomicCompareExchangeHeap(addr.base, &access, type, oldValue, newValue);
6787 if (!f.inDeadCode() && !ins) {
6788 return false;
6789 }
6790
6791 f.iter().setResult(ins);
6792 return true;
6793}
6794
6795static bool EmitAtomicLoad(FunctionCompiler& f, ValType type,
6796 Scalar::Type viewType) {
6797 LinearMemoryAddress<MDefinition*> addr;
6798 if (!f.iter().readAtomicLoad(&addr, type, byteSize(viewType))) {
6799 return false;
6800 }
6801
6802 MemoryAccessDesc access(
6803 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6804 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Load());
6805 auto* ins = f.load(addr.base, &access, type);
6806 if (!f.inDeadCode() && !ins) {
6807 return false;
6808 }
6809
6810 f.iter().setResult(ins);
6811 return true;
6812}
6813
6814static bool EmitAtomicRMW(FunctionCompiler& f, ValType type,
6815 Scalar::Type viewType, jit::AtomicOp op) {
6816 LinearMemoryAddress<MDefinition*> addr;
6817 MDefinition* value;
6818 if (!f.iter().readAtomicRMW(&addr, type, byteSize(viewType), &value)) {
6819 return false;
6820 }
6821
6822 MemoryAccessDesc access(
6823 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6824 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Full());
6825 auto* ins = f.atomicBinopHeap(op, addr.base, &access, type, value);
6826 if (!f.inDeadCode() && !ins) {
6827 return false;
6828 }
6829
6830 f.iter().setResult(ins);
6831 return true;
6832}
6833
6834static bool EmitAtomicStore(FunctionCompiler& f, ValType type,
6835 Scalar::Type viewType) {
6836 LinearMemoryAddress<MDefinition*> addr;
6837 MDefinition* value;
6838 if (!f.iter().readAtomicStore(&addr, type, byteSize(viewType), &value)) {
6839 return false;
6840 }
6841
6842 MemoryAccessDesc access(
6843 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6844 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Store());
6845 f.store(addr.base, &access, value);
6846 return true;
6847}
6848
6849static bool EmitWait(FunctionCompiler& f, ValType type, uint32_t byteSize) {
6850 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"
, 6850); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == ValType::I32 || type == ValType::I64"
")"); do { *((volatile int*)__null) = 6850; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6851 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"
, 6851); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.size() == byteSize"
")"); do { *((volatile int*)__null) = 6851; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6852
6853 uint32_t bytecodeOffset = f.readBytecodeOffset();
6854
6855 LinearMemoryAddress<MDefinition*> addr;
6856 MDefinition* expected;
6857 MDefinition* timeout;
6858 if (!f.iter().readWait(&addr, type, byteSize, &expected, &timeout)) {
6859 return false;
6860 }
6861
6862 if (f.inDeadCode()) {
6863 return true;
6864 }
6865
6866 MemoryAccessDesc access(addr.memoryIndex,
6867 type == ValType::I32 ? Scalar::Int32 : Scalar::Int64,
6868 addr.align, addr.offset, f.bytecodeOffset(),
6869 f.hugeMemoryEnabled(addr.memoryIndex));
6870 MDefinition* ptr = f.computeEffectiveAddress(addr.base, &access);
6871 if (!ptr) {
6872 return false;
6873 }
6874
6875 MDefinition* memoryIndex = f.constantI32(int32_t(addr.memoryIndex));
6876 if (!memoryIndex) {
6877 return false;
6878 }
6879
6880 const SymbolicAddressSignature& callee =
6881 f.isMem32(addr.memoryIndex)
6882 ? (type == ValType::I32 ? SASigWaitI32M32 : SASigWaitI64M32)
6883 : (type == ValType::I32 ? SASigWaitI32M64 : SASigWaitI64M64);
6884
6885 MDefinition* ret;
6886 if (!f.emitInstanceCall4(bytecodeOffset, callee, ptr, expected, timeout,
6887 memoryIndex, &ret)) {
6888 return false;
6889 }
6890
6891 f.iter().setResult(ret);
6892 return true;
6893}
6894
6895static bool EmitFence(FunctionCompiler& f) {
6896 if (!f.iter().readFence()) {
6897 return false;
6898 }
6899
6900 f.fence();
6901 return true;
6902}
6903
6904static bool EmitWake(FunctionCompiler& f) {
6905 uint32_t bytecodeOffset = f.readBytecodeOffset();
6906
6907 LinearMemoryAddress<MDefinition*> addr;
6908 MDefinition* count;
6909 if (!f.iter().readWake(&addr, &count)) {
6910 return false;
6911 }
6912
6913 if (f.inDeadCode()) {
6914 return true;
6915 }
6916
6917 MemoryAccessDesc access(addr.memoryIndex, Scalar::Int32, addr.align,
6918 addr.offset, f.bytecodeOffset(),
6919 f.hugeMemoryEnabled(addr.memoryIndex));
6920 MDefinition* ptr = f.computeEffectiveAddress(addr.base, &access);
6921 if (!ptr) {
6922 return false;
6923 }
6924
6925 MDefinition* memoryIndex = f.constantI32(int32_t(addr.memoryIndex));
6926 if (!memoryIndex) {
6927 return false;
6928 }
6929
6930 const SymbolicAddressSignature& callee =
6931 f.isMem32(addr.memoryIndex) ? SASigWakeM32 : SASigWakeM64;
6932
6933 MDefinition* ret;
6934 if (!f.emitInstanceCall3(bytecodeOffset, callee, ptr, count, memoryIndex,
6935 &ret)) {
6936 return false;
6937 }
6938
6939 f.iter().setResult(ret);
6940 return true;
6941}
6942
6943static bool EmitAtomicXchg(FunctionCompiler& f, ValType type,
6944 Scalar::Type viewType) {
6945 LinearMemoryAddress<MDefinition*> addr;
6946 MDefinition* value;
6947 if (!f.iter().readAtomicRMW(&addr, type, byteSize(viewType), &value)) {
6948 return false;
6949 }
6950
6951 MemoryAccessDesc access(
6952 addr.memoryIndex, viewType, addr.align, addr.offset, f.bytecodeOffset(),
6953 f.hugeMemoryEnabled(addr.memoryIndex), Synchronization::Full());
6954 MDefinition* ins = f.atomicExchangeHeap(addr.base, &access, type, value);
6955 if (!f.inDeadCode() && !ins) {
6956 return false;
6957 }
6958
6959 f.iter().setResult(ins);
6960 return true;
6961}
6962
6963static bool EmitMemCopyCall(FunctionCompiler& f, uint32_t dstMemIndex,
6964 uint32_t srcMemIndex, MDefinition* dst,
6965 MDefinition* src, MDefinition* len) {
6966 uint32_t bytecodeOffset = f.readBytecodeOffset();
6967
6968 if (dstMemIndex == srcMemIndex) {
6969 const SymbolicAddressSignature& callee =
6970 (f.codeMeta().usesSharedMemory(dstMemIndex)
6971 ? (f.isMem32(dstMemIndex) ? SASigMemCopySharedM32
6972 : SASigMemCopySharedM64)
6973 : (f.isMem32(dstMemIndex) ? SASigMemCopyM32 : SASigMemCopyM64));
6974 MDefinition* memoryBase = f.memoryBase(dstMemIndex);
6975 if (!memoryBase) {
6976 return false;
6977 }
6978 return f.emitInstanceCall4(bytecodeOffset, callee, dst, src, len,
6979 memoryBase);
6980 }
6981
6982 IndexType dstIndexType = f.codeMeta().memories[dstMemIndex].indexType();
6983 IndexType srcIndexType = f.codeMeta().memories[srcMemIndex].indexType();
6984
6985 if (dstIndexType == IndexType::I32) {
6986 dst = f.extendI32(dst, /*isUnsigned=*/true);
6987 if (!dst) {
6988 return false;
6989 }
6990 }
6991 if (srcIndexType == IndexType::I32) {
6992 src = f.extendI32(src, /*isUnsigned=*/true);
6993 if (!src) {
6994 return false;
6995 }
6996 }
6997 if (dstIndexType == IndexType::I32 || srcIndexType == IndexType::I32) {
6998 len = f.extendI32(len, /*isUnsigned=*/true);
6999 if (!len) {
7000 return false;
7001 }
7002 }
7003
7004 MDefinition* dstMemIndexValue = f.constantI32(int32_t(dstMemIndex));
7005 if (!dstMemIndexValue) {
7006 return false;
7007 }
7008
7009 MDefinition* srcMemIndexValue = f.constantI32(int32_t(srcMemIndex));
7010 if (!srcMemIndexValue) {
7011 return false;
7012 }
7013
7014 return f.emitInstanceCall5(bytecodeOffset, SASigMemCopyAny, dst, src, len,
7015 dstMemIndexValue, srcMemIndexValue);
7016}
7017
7018static bool EmitMemCopyInline(FunctionCompiler& f, uint32_t memoryIndex,
7019 MDefinition* dst, MDefinition* src,
7020 uint32_t length) {
7021 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"
, 7021); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length != 0 && length <= MaxInlineMemoryCopyLength"
")"); do { *((volatile int*)__null) = 7021; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7022
7023 // Compute the number of copies of each width we will need to do
7024 size_t remainder = length;
7025#ifdef ENABLE_WASM_SIMD1
7026 size_t numCopies16 = 0;
7027 if (MacroAssembler::SupportsFastUnalignedFPAccesses()) {
7028 numCopies16 = remainder / sizeof(V128);
7029 remainder %= sizeof(V128);
7030 }
7031#endif
7032#ifdef JS_64BIT1
7033 size_t numCopies8 = remainder / sizeof(uint64_t);
7034 remainder %= sizeof(uint64_t);
7035#endif
7036 size_t numCopies4 = remainder / sizeof(uint32_t);
7037 remainder %= sizeof(uint32_t);
7038 size_t numCopies2 = remainder / sizeof(uint16_t);
7039 remainder %= sizeof(uint16_t);
7040 size_t numCopies1 = remainder;
7041
7042 // Load all source bytes from low to high using the widest transfer width we
7043 // can for the system. We will trap without writing anything if any source
7044 // byte is out-of-bounds.
7045 size_t offset = 0;
7046 DefVector loadedValues;
7047
7048#ifdef ENABLE_WASM_SIMD1
7049 for (uint32_t i = 0; i < numCopies16; i++) {
7050 MemoryAccessDesc access(memoryIndex, Scalar::Simd128, 1, offset,
7051 f.bytecodeOffset(),
7052 f.hugeMemoryEnabled(memoryIndex));
7053 auto* load = f.load(src, &access, ValType::V128);
7054 if (!load || !loadedValues.append(load)) {
7055 return false;
7056 }
7057
7058 offset += sizeof(V128);
7059 }
7060#endif
7061
7062#ifdef JS_64BIT1
7063 for (uint32_t i = 0; i < numCopies8; i++) {
7064 MemoryAccessDesc access(memoryIndex, Scalar::Int64, 1, offset,
7065 f.bytecodeOffset(),
7066 f.hugeMemoryEnabled(memoryIndex));
7067 auto* load = f.load(src, &access, ValType::I64);
7068 if (!load || !loadedValues.append(load)) {
7069 return false;
7070 }
7071
7072 offset += sizeof(uint64_t);
7073 }
7074#endif
7075
7076 for (uint32_t i = 0; i < numCopies4; i++) {
7077 MemoryAccessDesc access(memoryIndex, Scalar::Uint32, 1, offset,
7078 f.bytecodeOffset(),
7079 f.hugeMemoryEnabled(memoryIndex));
7080 auto* load = f.load(src, &access, ValType::I32);
7081 if (!load || !loadedValues.append(load)) {
7082 return false;
7083 }
7084
7085 offset += sizeof(uint32_t);
7086 }
7087
7088 if (numCopies2) {
7089 MemoryAccessDesc access(memoryIndex, Scalar::Uint16, 1, offset,
7090 f.bytecodeOffset(),
7091 f.hugeMemoryEnabled(memoryIndex));
7092 auto* load = f.load(src, &access, ValType::I32);
7093 if (!load || !loadedValues.append(load)) {
7094 return false;
7095 }
7096
7097 offset += sizeof(uint16_t);
7098 }
7099
7100 if (numCopies1) {
7101 MemoryAccessDesc access(memoryIndex, Scalar::Uint8, 1, offset,
7102 f.bytecodeOffset(),
7103 f.hugeMemoryEnabled(memoryIndex));
7104 auto* load = f.load(src, &access, ValType::I32);
7105 if (!load || !loadedValues.append(load)) {
7106 return false;
7107 }
7108 }
7109
7110 // Store all source bytes to the destination from high to low. We will trap
7111 // without writing anything on the first store if any dest byte is
7112 // out-of-bounds.
7113 offset = length;
7114
7115 if (numCopies1) {
7116 offset -= sizeof(uint8_t);
7117
7118 MemoryAccessDesc access(memoryIndex, Scalar::Uint8, 1, offset,
7119 f.bytecodeOffset(),
7120 f.hugeMemoryEnabled(memoryIndex));
7121 auto* value = loadedValues.popCopy();
7122 f.store(dst, &access, value);
7123 }
7124
7125 if (numCopies2) {
7126 offset -= sizeof(uint16_t);
7127
7128 MemoryAccessDesc access(memoryIndex, Scalar::Uint16, 1, offset,
7129 f.bytecodeOffset(),
7130 f.hugeMemoryEnabled(memoryIndex));
7131 auto* value = loadedValues.popCopy();
7132 f.store(dst, &access, value);
7133 }
7134
7135 for (uint32_t i = 0; i < numCopies4; i++) {
7136 offset -= sizeof(uint32_t);
7137
7138 MemoryAccessDesc access(memoryIndex, Scalar::Uint32, 1, offset,
7139 f.bytecodeOffset(),
7140 f.hugeMemoryEnabled(memoryIndex));
7141 auto* value = loadedValues.popCopy();
7142 f.store(dst, &access, value);
7143 }
7144
7145#ifdef JS_64BIT1
7146 for (uint32_t i = 0; i < numCopies8; i++) {
7147 offset -= sizeof(uint64_t);
7148
7149 MemoryAccessDesc access(memoryIndex, Scalar::Int64, 1, offset,
7150 f.bytecodeOffset(),
7151 f.hugeMemoryEnabled(memoryIndex));
7152 auto* value = loadedValues.popCopy();
7153 f.store(dst, &access, value);
7154 }
7155#endif
7156
7157#ifdef ENABLE_WASM_SIMD1
7158 for (uint32_t i = 0; i < numCopies16; i++) {
7159 offset -= sizeof(V128);
7160
7161 MemoryAccessDesc access(memoryIndex, Scalar::Simd128, 1, offset,
7162 f.bytecodeOffset(),
7163 f.hugeMemoryEnabled(memoryIndex));
7164 auto* value = loadedValues.popCopy();
7165 f.store(dst, &access, value);
7166 }
7167#endif
7168
7169 return true;
7170}
7171
7172static bool EmitMemCopy(FunctionCompiler& f) {
7173 MDefinition *dst, *src, *len;
7174 uint32_t dstMemIndex;
7175 uint32_t srcMemIndex;
7176 if (!f.iter().readMemOrTableCopy(true, &dstMemIndex, &dst, &srcMemIndex, &src,
7177 &len)) {
7178 return false;
7179 }
7180
7181 if (f.inDeadCode()) {
7182 return true;
7183 }
7184
7185 if (dstMemIndex == srcMemIndex && len->isConstant()) {
7186 uint64_t length = f.isMem32(dstMemIndex) ? len->toConstant()->toInt32()
7187 : len->toConstant()->toInt64();
7188 static_assert(MaxInlineMemoryCopyLength <= UINT32_MAX(4294967295U));
7189 if (length != 0 && length <= MaxInlineMemoryCopyLength) {
7190 return EmitMemCopyInline(f, dstMemIndex, dst, src, uint32_t(length));
7191 }
7192 }
7193
7194 return EmitMemCopyCall(f, dstMemIndex, srcMemIndex, dst, src, len);
7195}
7196
7197static bool EmitTableCopy(FunctionCompiler& f) {
7198 MDefinition *dst, *src, *len;
7199 uint32_t dstTableIndex;
7200 uint32_t srcTableIndex;
7201 if (!f.iter().readMemOrTableCopy(false, &dstTableIndex, &dst, &srcTableIndex,
7202 &src, &len)) {
7203 return false;
7204 }
7205
7206 if (f.inDeadCode()) {
7207 return true;
7208 }
7209
7210 uint32_t bytecodeOffset = f.readBytecodeOffset();
7211 const TableDesc& dstTable = f.codeMeta().tables[dstTableIndex];
7212 const TableDesc& srcTable = f.codeMeta().tables[srcTableIndex];
7213
7214 IndexType dstIndexType = dstTable.indexType();
7215 IndexType srcIndexType = srcTable.indexType();
7216 IndexType lenIndexType =
7217 dstIndexType == IndexType::I64 && srcIndexType == IndexType::I64
7218 ? IndexType::I64
7219 : IndexType::I32;
7220
7221 MDefinition* dst32 = f.tableIndexToI32(dstIndexType, dst);
7222 if (!dst32) {
7223 return false;
7224 }
7225
7226 MDefinition* src32 = f.tableIndexToI32(srcIndexType, src);
7227 if (!src32) {
7228 return false;
7229 }
7230
7231 MDefinition* len32 = f.tableIndexToI32(lenIndexType, len);
7232 if (!len32) {
7233 return false;
7234 }
7235
7236 MDefinition* dti = f.constantI32(int32_t(dstTableIndex));
7237 MDefinition* sti = f.constantI32(int32_t(srcTableIndex));
7238
7239 return f.emitInstanceCall5(bytecodeOffset, SASigTableCopy, dst32, src32,
7240 len32, dti, sti);
7241}
7242
7243static bool EmitDataOrElemDrop(FunctionCompiler& f, bool isData) {
7244 uint32_t segIndexVal = 0;
7245 if (!f.iter().readDataOrElemDrop(isData, &segIndexVal)) {
7246 return false;
7247 }
7248
7249 if (f.inDeadCode()) {
7250 return true;
7251 }
7252
7253 uint32_t bytecodeOffset = f.readBytecodeOffset();
7254
7255 MDefinition* segIndex = f.constantI32(int32_t(segIndexVal));
7256
7257 const SymbolicAddressSignature& callee =
7258 isData ? SASigDataDrop : SASigElemDrop;
7259 return f.emitInstanceCall1(bytecodeOffset, callee, segIndex);
7260}
7261
7262static bool EmitMemFillCall(FunctionCompiler& f, uint32_t memoryIndex,
7263 MDefinition* start, MDefinition* val,
7264 MDefinition* len) {
7265 MDefinition* memoryBase = f.memoryBase(memoryIndex);
7266
7267 uint32_t bytecodeOffset = f.readBytecodeOffset();
7268 const SymbolicAddressSignature& callee =
7269 (f.codeMeta().usesSharedMemory(memoryIndex)
7270 ? (f.isMem32(memoryIndex) ? SASigMemFillSharedM32
7271 : SASigMemFillSharedM64)
7272 : (f.isMem32(memoryIndex) ? SASigMemFillM32 : SASigMemFillM64));
7273 return f.emitInstanceCall4(bytecodeOffset, callee, start, val, len,
7274 memoryBase);
7275}
7276
7277static bool EmitMemFillInline(FunctionCompiler& f, uint32_t memoryIndex,
7278 MDefinition* start, MDefinition* val,
7279 uint32_t length) {
7280 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"
, 7280); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length != 0 && length <= MaxInlineMemoryFillLength"
")"); do { *((volatile int*)__null) = 7280; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7281 uint32_t value = val->toConstant()->toInt32();
7282
7283 // Compute the number of copies of each width we will need to do
7284 size_t remainder = length;
7285#ifdef ENABLE_WASM_SIMD1
7286 size_t numCopies16 = 0;
7287 if (MacroAssembler::SupportsFastUnalignedFPAccesses()) {
7288 numCopies16 = remainder / sizeof(V128);
7289 remainder %= sizeof(V128);
7290 }
7291#endif
7292#ifdef JS_64BIT1
7293 size_t numCopies8 = remainder / sizeof(uint64_t);
7294 remainder %= sizeof(uint64_t);
7295#endif
7296 size_t numCopies4 = remainder / sizeof(uint32_t);
7297 remainder %= sizeof(uint32_t);
7298 size_t numCopies2 = remainder / sizeof(uint16_t);
7299 remainder %= sizeof(uint16_t);
7300 size_t numCopies1 = remainder;
7301
7302 // Generate splatted definitions for wider fills as needed
7303#ifdef ENABLE_WASM_SIMD1
7304 MDefinition* val16 = numCopies16 ? f.constantV128(V128(value)) : nullptr;
7305#endif
7306#ifdef JS_64BIT1
7307 MDefinition* val8 =
7308 numCopies8 ? f.constantI64(int64_t(SplatByteToUInt<uint64_t>(value, 8)))
7309 : nullptr;
7310#endif
7311 MDefinition* val4 =
7312 numCopies4 ? f.constantI32(int32_t(SplatByteToUInt<uint32_t>(value, 4)))
7313 : nullptr;
7314 MDefinition* val2 =
7315 numCopies2 ? f.constantI32(int32_t(SplatByteToUInt<uint32_t>(value, 2)))
7316 : nullptr;
7317
7318 // Store the fill value to the destination from high to low. We will trap
7319 // without writing anything on the first store if any dest byte is
7320 // out-of-bounds.
7321 size_t offset = length;
7322
7323 if (numCopies1) {
7324 offset -= sizeof(uint8_t);
7325
7326 MemoryAccessDesc access(memoryIndex, Scalar::Uint8, 1, offset,
7327 f.bytecodeOffset(),
7328 f.hugeMemoryEnabled(memoryIndex));
7329 f.store(start, &access, val);
7330 }
7331
7332 if (numCopies2) {
7333 offset -= sizeof(uint16_t);
7334
7335 MemoryAccessDesc access(memoryIndex, Scalar::Uint16, 1, offset,
7336 f.bytecodeOffset(),
7337 f.hugeMemoryEnabled(memoryIndex));
7338 f.store(start, &access, val2);
7339 }
7340
7341 for (uint32_t i = 0; i < numCopies4; i++) {
7342 offset -= sizeof(uint32_t);
7343
7344 MemoryAccessDesc access(memoryIndex, Scalar::Uint32, 1, offset,
7345 f.bytecodeOffset(),
7346 f.hugeMemoryEnabled(memoryIndex));
7347 f.store(start, &access, val4);
7348 }
7349
7350#ifdef JS_64BIT1
7351 for (uint32_t i = 0; i < numCopies8; i++) {
7352 offset -= sizeof(uint64_t);
7353
7354 MemoryAccessDesc access(memoryIndex, Scalar::Int64, 1, offset,
7355 f.bytecodeOffset(),
7356 f.hugeMemoryEnabled(memoryIndex));
7357 f.store(start, &access, val8);
7358 }
7359#endif
7360
7361#ifdef ENABLE_WASM_SIMD1
7362 for (uint32_t i = 0; i < numCopies16; i++) {
7363 offset -= sizeof(V128);
7364
7365 MemoryAccessDesc access(memoryIndex, Scalar::Simd128, 1, offset,
7366 f.bytecodeOffset(),
7367 f.hugeMemoryEnabled(memoryIndex));
7368 f.store(start, &access, val16);
7369 }
7370#endif
7371
7372 return true;
7373}
7374
7375static bool EmitMemFill(FunctionCompiler& f) {
7376 uint32_t memoryIndex;
7377 MDefinition *start, *val, *len;
7378 if (!f.iter().readMemFill(&memoryIndex, &start, &val, &len)) {
7379 return false;
7380 }
7381
7382 if (f.inDeadCode()) {
7383 return true;
7384 }
7385
7386 if (len->isConstant() && val->isConstant()) {
7387 uint64_t length = f.isMem32(memoryIndex) ? len->toConstant()->toInt32()
7388 : len->toConstant()->toInt64();
7389 static_assert(MaxInlineMemoryFillLength <= UINT32_MAX(4294967295U));
7390 if (length != 0 && length <= MaxInlineMemoryFillLength) {
7391 return EmitMemFillInline(f, memoryIndex, start, val, uint32_t(length));
7392 }
7393 }
7394
7395 return EmitMemFillCall(f, memoryIndex, start, val, len);
7396}
7397
7398static bool EmitMemInit(FunctionCompiler& f) {
7399 uint32_t segIndexVal = 0, dstMemIndex = 0;
7400 MDefinition *dstOff, *srcOff, *len;
7401 if (!f.iter().readMemOrTableInit(true, &segIndexVal, &dstMemIndex, &dstOff,
7402 &srcOff, &len)) {
7403 return false;
7404 }
7405
7406 if (f.inDeadCode()) {
7407 return true;
7408 }
7409
7410 uint32_t bytecodeOffset = f.readBytecodeOffset();
7411 const SymbolicAddressSignature& callee =
7412 (f.isMem32(dstMemIndex) ? SASigMemInitM32 : SASigMemInitM64);
7413
7414 MDefinition* segIndex = f.constantI32(int32_t(segIndexVal));
7415 if (!segIndex) {
7416 return false;
7417 }
7418
7419 MDefinition* dti = f.constantI32(int32_t(dstMemIndex));
7420 if (!dti) {
7421 return false;
7422 }
7423
7424 return f.emitInstanceCall5(bytecodeOffset, callee, dstOff, srcOff, len,
7425 segIndex, dti);
7426}
7427
7428static bool EmitTableInit(FunctionCompiler& f) {
7429 uint32_t segIndexVal = 0, dstTableIndex = 0;
7430 MDefinition *dstOff, *srcOff, *len;
7431 if (!f.iter().readMemOrTableInit(false, &segIndexVal, &dstTableIndex, &dstOff,
7432 &srcOff, &len)) {
7433 return false;
7434 }
7435
7436 if (f.inDeadCode()) {
7437 return true;
7438 }
7439
7440 uint32_t bytecodeOffset = f.readBytecodeOffset();
7441 const TableDesc& table = f.codeMeta().tables[dstTableIndex];
7442
7443 MDefinition* dstOff32 = f.tableIndexToI32(table.indexType(), dstOff);
7444 if (!dstOff32) {
7445 return false;
7446 }
7447
7448 MDefinition* segIndex = f.constantI32(int32_t(segIndexVal));
7449 if (!segIndex) {
7450 return false;
7451 }
7452
7453 MDefinition* dti = f.constantI32(int32_t(dstTableIndex));
7454 if (!dti) {
7455 return false;
7456 }
7457
7458 return f.emitInstanceCall5(bytecodeOffset, SASigTableInit, dstOff32, srcOff,
7459 len, segIndex, dti);
7460}
7461
7462static bool EmitTableFill(FunctionCompiler& f) {
7463 uint32_t tableIndex;
7464 MDefinition *start, *val, *len;
7465 if (!f.iter().readTableFill(&tableIndex, &start, &val, &len)) {
7466 return false;
7467 }
7468
7469 if (f.inDeadCode()) {
7470 return true;
7471 }
7472
7473 uint32_t bytecodeOffset = f.readBytecodeOffset();
7474 const TableDesc& table = f.codeMeta().tables[tableIndex];
7475
7476 MDefinition* start32 = f.tableIndexToI32(table.indexType(), start);
7477 if (!start32) {
7478 return false;
7479 }
7480
7481 MDefinition* len32 = f.tableIndexToI32(table.indexType(), len);
7482 if (!len32) {
7483 return false;
7484 }
7485
7486 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7487 if (!tableIndexArg) {
7488 return false;
7489 }
7490
7491 return f.emitInstanceCall4(bytecodeOffset, SASigTableFill, start32, val,
7492 len32, tableIndexArg);
7493}
7494
7495#if ENABLE_WASM_MEMORY_CONTROL1
7496static bool EmitMemDiscard(FunctionCompiler& f) {
7497 uint32_t memoryIndex;
7498 MDefinition *start, *len;
7499 if (!f.iter().readMemDiscard(&memoryIndex, &start, &len)) {
7500 return false;
7501 }
7502
7503 if (f.inDeadCode()) {
7504 return true;
7505 }
7506
7507 uint32_t bytecodeOffset = f.readBytecodeOffset();
7508
7509 MDefinition* memoryBase = f.memoryBase(memoryIndex);
7510 bool isMem32 = f.isMem32(memoryIndex);
7511
7512 const SymbolicAddressSignature& callee =
7513 (f.codeMeta().usesSharedMemory(memoryIndex)
7514 ? (isMem32 ? SASigMemDiscardSharedM32 : SASigMemDiscardSharedM64)
7515 : (isMem32 ? SASigMemDiscardM32 : SASigMemDiscardM64));
7516 return f.emitInstanceCall3(bytecodeOffset, callee, start, len, memoryBase);
7517}
7518#endif
7519
7520static bool EmitTableGet(FunctionCompiler& f) {
7521 uint32_t tableIndex;
7522 MDefinition* index;
7523 if (!f.iter().readTableGet(&tableIndex, &index)) {
7524 return false;
7525 }
7526
7527 if (f.inDeadCode()) {
7528 return true;
7529 }
7530
7531 const TableDesc& table = f.codeMeta().tables[tableIndex];
7532
7533 MDefinition* index32 = f.tableIndexToI32(table.indexType(), index);
7534 if (!index32) {
7535 return false;
7536 }
7537
7538 if (table.elemType.tableRepr() == TableRepr::Ref) {
7539 MDefinition* ret = f.tableGetAnyRef(tableIndex, index32);
7540 if (!ret) {
7541 return false;
7542 }
7543 f.iter().setResult(ret);
7544 return true;
7545 }
7546
7547 uint32_t bytecodeOffset = f.readBytecodeOffset();
7548
7549 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7550 if (!tableIndexArg) {
7551 return false;
7552 }
7553
7554 // The return value here is either null, denoting an error, or a short-lived
7555 // pointer to a location containing a possibly-null ref.
7556 MDefinition* ret;
7557 if (!f.emitInstanceCall2(bytecodeOffset, SASigTableGet, index32,
7558 tableIndexArg, &ret)) {
7559 return false;
7560 }
7561
7562 f.iter().setResult(ret);
7563 return true;
7564}
7565
7566static bool EmitTableGrow(FunctionCompiler& f) {
7567 uint32_t tableIndex;
7568 MDefinition* initValue;
7569 MDefinition* delta;
7570 if (!f.iter().readTableGrow(&tableIndex, &initValue, &delta)) {
7571 return false;
7572 }
7573
7574 if (f.inDeadCode()) {
7575 return true;
7576 }
7577
7578 uint32_t bytecodeOffset = f.readBytecodeOffset();
7579 const TableDesc& table = f.codeMeta().tables[tableIndex];
7580
7581 MDefinition* delta32 = f.tableIndexToI32(table.indexType(), delta);
7582 if (!delta32) {
7583 return false;
7584 }
7585
7586 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7587 if (!tableIndexArg) {
7588 return false;
7589 }
7590
7591 MDefinition* ret;
7592 if (!f.emitInstanceCall3(bytecodeOffset, SASigTableGrow, initValue, delta32,
7593 tableIndexArg, &ret)) {
7594 return false;
7595 }
7596
7597 if (table.indexType() == IndexType::I64) {
7598 ret = f.extendI32(ret, false);
7599 if (!ret) {
7600 return false;
7601 }
7602 }
7603
7604 f.iter().setResult(ret);
7605 return true;
7606}
7607
7608static bool EmitTableSet(FunctionCompiler& f) {
7609 uint32_t tableIndex;
7610 MDefinition* index;
7611 MDefinition* value;
7612 if (!f.iter().readTableSet(&tableIndex, &index, &value)) {
7613 return false;
7614 }
7615
7616 if (f.inDeadCode()) {
7617 return true;
7618 }
7619
7620 uint32_t bytecodeOffset = f.readBytecodeOffset();
7621
7622 const TableDesc& table = f.codeMeta().tables[tableIndex];
7623
7624 MDefinition* index32 = f.tableIndexToI32(table.indexType(), index);
7625 if (!index32) {
7626 return false;
7627 }
7628
7629 if (table.elemType.tableRepr() == TableRepr::Ref) {
7630 return f.tableSetAnyRef(tableIndex, index32, value, bytecodeOffset);
7631 }
7632
7633 MDefinition* tableIndexArg = f.constantI32(int32_t(tableIndex));
7634 if (!tableIndexArg) {
7635 return false;
7636 }
7637
7638 return f.emitInstanceCall3(bytecodeOffset, SASigTableSet, index32, value,
7639 tableIndexArg);
7640}
7641
7642static bool EmitTableSize(FunctionCompiler& f) {
7643 uint32_t tableIndex;
7644 if (!f.iter().readTableSize(&tableIndex)) {
7645 return false;
7646 }
7647
7648 if (f.inDeadCode()) {
7649 return true;
7650 }
7651
7652 MDefinition* length = f.loadTableLength(tableIndex);
7653 if (!length) {
7654 return false;
7655 }
7656
7657 if (f.codeMeta().tables[tableIndex].indexType() == IndexType::I64) {
7658 length = f.extendI32(length, true);
7659 if (!length) {
7660 return false;
7661 }
7662 }
7663
7664 f.iter().setResult(length);
7665 return true;
7666}
7667
7668static bool EmitRefFunc(FunctionCompiler& f) {
7669 uint32_t funcIndex;
7670 if (!f.iter().readRefFunc(&funcIndex)) {
7671 return false;
7672 }
7673
7674 if (f.inDeadCode()) {
7675 return true;
7676 }
7677
7678 uint32_t bytecodeOffset = f.readBytecodeOffset();
7679
7680 MDefinition* funcIndexArg = f.constantI32(int32_t(funcIndex));
7681 if (!funcIndexArg) {
7682 return false;
7683 }
7684
7685 // The return value here is either null, denoting an error, or a short-lived
7686 // pointer to a location containing a possibly-null ref.
7687 MDefinition* ret;
7688 if (!f.emitInstanceCall1(bytecodeOffset, SASigRefFunc, funcIndexArg, &ret)) {
7689 return false;
7690 }
7691
7692 f.iter().setResult(ret);
7693 return true;
7694}
7695
7696static bool EmitRefNull(FunctionCompiler& f) {
7697 RefType type;
7698 if (!f.iter().readRefNull(&type)) {
7699 return false;
7700 }
7701
7702 if (f.inDeadCode()) {
7703 return true;
7704 }
7705
7706 MDefinition* nullVal = f.constantNullRef();
7707 if (!nullVal) {
7708 return false;
7709 }
7710 f.iter().setResult(nullVal);
7711 return true;
7712}
7713
7714static bool EmitRefIsNull(FunctionCompiler& f) {
7715 MDefinition* input;
7716 if (!f.iter().readRefIsNull(&input)) {
7717 return false;
7718 }
7719
7720 if (f.inDeadCode()) {
7721 return true;
7722 }
7723
7724 MDefinition* nullVal = f.constantNullRef();
7725 if (!nullVal) {
7726 return false;
7727 }
7728 f.iter().setResult(
7729 f.compare(input, nullVal, JSOp::Eq, MCompare::Compare_WasmAnyRef));
7730 return true;
7731}
7732
7733#ifdef ENABLE_WASM_SIMD1
7734static bool EmitConstSimd128(FunctionCompiler& f) {
7735 V128 v128;
7736 if (!f.iter().readV128Const(&v128)) {
7737 return false;
7738 }
7739
7740 f.iter().setResult(f.constantV128(v128));
7741 return true;
7742}
7743
7744static bool EmitBinarySimd128(FunctionCompiler& f, bool commutative,
7745 SimdOp op) {
7746 MDefinition* lhs;
7747 MDefinition* rhs;
7748 if (!f.iter().readBinary(ValType::V128, &lhs, &rhs)) {
7749 return false;
7750 }
7751
7752 f.iter().setResult(f.binarySimd128(lhs, rhs, commutative, op));
7753 return true;
7754}
7755
7756static bool EmitTernarySimd128(FunctionCompiler& f, wasm::SimdOp op) {
7757 MDefinition* v0;
7758 MDefinition* v1;
7759 MDefinition* v2;
7760 if (!f.iter().readTernary(ValType::V128, &v0, &v1, &v2)) {
7761 return false;
7762 }
7763
7764 f.iter().setResult(f.ternarySimd128(v0, v1, v2, op));
7765 return true;
7766}
7767
7768static bool EmitShiftSimd128(FunctionCompiler& f, SimdOp op) {
7769 MDefinition* lhs;
7770 MDefinition* rhs;
7771 if (!f.iter().readVectorShift(&lhs, &rhs)) {
7772 return false;
7773 }
7774
7775 f.iter().setResult(f.shiftSimd128(lhs, rhs, op));
7776 return true;
7777}
7778
7779static bool EmitSplatSimd128(FunctionCompiler& f, ValType inType, SimdOp op) {
7780 MDefinition* src;
7781 if (!f.iter().readConversion(inType, ValType::V128, &src)) {
7782 return false;
7783 }
7784
7785 f.iter().setResult(f.scalarToSimd128(src, op));
7786 return true;
7787}
7788
7789static bool EmitUnarySimd128(FunctionCompiler& f, SimdOp op) {
7790 MDefinition* src;
7791 if (!f.iter().readUnary(ValType::V128, &src)) {
7792 return false;
7793 }
7794
7795 f.iter().setResult(f.unarySimd128(src, op));
7796 return true;
7797}
7798
7799static bool EmitReduceSimd128(FunctionCompiler& f, SimdOp op) {
7800 MDefinition* src;
7801 if (!f.iter().readConversion(ValType::V128, ValType::I32, &src)) {
7802 return false;
7803 }
7804
7805 f.iter().setResult(f.reduceSimd128(src, op, ValType::I32));
7806 return true;
7807}
7808
7809static bool EmitExtractLaneSimd128(FunctionCompiler& f, ValType outType,
7810 uint32_t laneLimit, SimdOp op) {
7811 uint32_t laneIndex;
7812 MDefinition* src;
7813 if (!f.iter().readExtractLane(outType, laneLimit, &laneIndex, &src)) {
7814 return false;
7815 }
7816
7817 f.iter().setResult(f.reduceSimd128(src, op, outType, laneIndex));
7818 return true;
7819}
7820
7821static bool EmitReplaceLaneSimd128(FunctionCompiler& f, ValType laneType,
7822 uint32_t laneLimit, SimdOp op) {
7823 uint32_t laneIndex;
7824 MDefinition* lhs;
7825 MDefinition* rhs;
7826 if (!f.iter().readReplaceLane(laneType, laneLimit, &laneIndex, &lhs, &rhs)) {
7827 return false;
7828 }
7829
7830 f.iter().setResult(f.replaceLaneSimd128(lhs, rhs, laneIndex, op));
7831 return true;
7832}
7833
7834static bool EmitShuffleSimd128(FunctionCompiler& f) {
7835 MDefinition* v1;
7836 MDefinition* v2;
7837 V128 control;
7838 if (!f.iter().readVectorShuffle(&v1, &v2, &control)) {
7839 return false;
7840 }
7841
7842 f.iter().setResult(f.shuffleSimd128(v1, v2, control));
7843 return true;
7844}
7845
7846static bool EmitLoadSplatSimd128(FunctionCompiler& f, Scalar::Type viewType,
7847 wasm::SimdOp splatOp) {
7848 LinearMemoryAddress<MDefinition*> addr;
7849 if (!f.iter().readLoadSplat(Scalar::byteSize(viewType), &addr)) {
7850 return false;
7851 }
7852
7853 auto* ins = f.loadSplatSimd128(viewType, addr, splatOp);
7854 if (!f.inDeadCode() && !ins) {
7855 return false;
7856 }
7857 f.iter().setResult(ins);
7858 return true;
7859}
7860
7861static bool EmitLoadExtendSimd128(FunctionCompiler& f, wasm::SimdOp op) {
7862 LinearMemoryAddress<MDefinition*> addr;
7863 if (!f.iter().readLoadExtend(&addr)) {
7864 return false;
7865 }
7866
7867 auto* ins = f.loadExtendSimd128(addr, op);
7868 if (!f.inDeadCode() && !ins) {
7869 return false;
7870 }
7871 f.iter().setResult(ins);
7872 return true;
7873}
7874
7875static bool EmitLoadZeroSimd128(FunctionCompiler& f, Scalar::Type viewType,
7876 size_t numBytes) {
7877 LinearMemoryAddress<MDefinition*> addr;
7878 if (!f.iter().readLoadSplat(numBytes, &addr)) {
7879 return false;
7880 }
7881
7882 auto* ins = f.loadZeroSimd128(viewType, numBytes, addr);
7883 if (!f.inDeadCode() && !ins) {
7884 return false;
7885 }
7886 f.iter().setResult(ins);
7887 return true;
7888}
7889
7890static bool EmitLoadLaneSimd128(FunctionCompiler& f, uint32_t laneSize) {
7891 uint32_t laneIndex;
7892 MDefinition* src;
7893 LinearMemoryAddress<MDefinition*> addr;
7894 if (!f.iter().readLoadLane(laneSize, &addr, &laneIndex, &src)) {
7895 return false;
7896 }
7897
7898 auto* ins = f.loadLaneSimd128(laneSize, addr, laneIndex, src);
7899 if (!f.inDeadCode() && !ins) {
7900 return false;
7901 }
7902 f.iter().setResult(ins);
7903 return true;
7904}
7905
7906static bool EmitStoreLaneSimd128(FunctionCompiler& f, uint32_t laneSize) {
7907 uint32_t laneIndex;
7908 MDefinition* src;
7909 LinearMemoryAddress<MDefinition*> addr;
7910 if (!f.iter().readStoreLane(laneSize, &addr, &laneIndex, &src)) {
7911 return false;
7912 }
7913
7914 f.storeLaneSimd128(laneSize, addr, laneIndex, src);
7915 return true;
7916}
7917
7918#endif // ENABLE_WASM_SIMD
7919
7920#ifdef ENABLE_WASM_GC1
7921static bool EmitRefAsNonNull(FunctionCompiler& f) {
7922 MDefinition* ref;
7923 if (!f.iter().readRefAsNonNull(&ref)) {
7924 return false;
7925 }
7926
7927 return f.refAsNonNull(ref);
7928}
7929
7930static bool EmitBrOnNull(FunctionCompiler& f) {
7931 uint32_t relativeDepth;
7932 ResultType type;
7933 DefVector values;
7934 MDefinition* condition;
7935 if (!f.iter().readBrOnNull(&relativeDepth, &type, &values, &condition)) {
7936 return false;
7937 }
7938
7939 return f.brOnNull(relativeDepth, values, type, condition);
7940}
7941
7942static bool EmitBrOnNonNull(FunctionCompiler& f) {
7943 uint32_t relativeDepth;
7944 ResultType type;
7945 DefVector values;
7946 MDefinition* condition;
7947 if (!f.iter().readBrOnNonNull(&relativeDepth, &type, &values, &condition)) {
7948 return false;
7949 }
7950
7951 return f.brOnNonNull(relativeDepth, values, type, condition);
7952}
7953
7954// Speculatively inline a call_ref that is likely to target the expected
7955// function index in this module. A fallback for if the actual callee is not
7956// the speculated expected callee is always generated. This leads to a control
7957// flow diamond that is roughly:
7958//
7959// if (ref.func $expectedFuncIndex) == actualCalleeFunc:
7960// (call_inline $expectedFuncIndex)
7961// else:
7962// (call_ref actualCalleeFunc)
7963static bool EmitSpeculativeInlineCallRef(
7964 FunctionCompiler& f, uint32_t bytecodeOffset, const FuncType& funcType,
7965 uint32_t expectedFuncIndex, MDefinition* actualCalleeFunc,
7966 const DefVector& args, DefVector* results) {
7967 // Perform an up front null check on the callee function reference.
7968 if (!f.refAsNonNull(actualCalleeFunc)) {
7969 return false;
7970 }
7971
7972 // Load the cached value of `ref.func $expectedFuncIndex` for comparing
7973 // against `actualCalleeFunc`. This cached value may be null if the `ref.func`
7974 // for the expected function has not been executed in this runtime session.
7975 //
7976 // This is okay because we have done a null check on the `actualCalleeFunc`
7977 // already and so comparing it against a null expected callee func will
7978 // return false and fall back to the general case. This can only happen if
7979 // we've deserialized a cached module in a different session, and then run
7980 // the code without ever acquiring a reference to the expected function. In
7981 // that case, the expected callee could never be the target of this call_ref,
7982 // so performing the fallback path is the right thing to do anyways.
7983 MDefinition* expectedCalleeFunc = f.loadCachedRefFunc(expectedFuncIndex);
7984 if (!expectedCalleeFunc) {
7985 return false;
7986 }
7987
7988 // Check if the callee funcref we have is equals to the expected callee
7989 // funcref we're inlining.
7990 MDefinition* isExpectedCallee =
7991 f.compare(actualCalleeFunc, expectedCalleeFunc, JSOp::Eq,
7992 MCompare::Compare_WasmAnyRef);
7993 if (!isExpectedCallee) {
7994 return false;
7995 }
7996
7997 // Start the 'then' block which will have the inlined code
7998 MBasicBlock* elseBlock;
7999 if (!f.branchAndStartThen(isExpectedCallee, &elseBlock)) {
8000 return false;
8001 }
8002
8003 // Inline the expected callee as we do with direct calls
8004 DefVector inlineResults;
8005 if (!EmitInlineCall(f, funcType, expectedFuncIndex,
8006 InliningHeuristics::CallKind::CallRef, args,
8007 &inlineResults)) {
8008 return false;
8009 }
8010
8011 // Push the results for joining with the 'else' block
8012 if (!f.pushDefs(inlineResults)) {
8013 return false;
8014 }
8015
8016 // Switch to the 'else' block which will have the fallback `call_ref`
8017 if (!f.switchToElse(elseBlock, &elseBlock)) {
8018 return false;
8019 }
8020
8021 // Perform a general indirect call to the callee func we have
8022 CallCompileState call;
8023 if (!EmitCallArgs(f, funcType, args, &call)) {
8024 return false;
8025 }
8026
8027 DefVector callResults;
8028 if (!f.callRef(funcType, actualCalleeFunc, bytecodeOffset, call,
8029 &callResults)) {
8030 return false;
8031 }
8032
8033 // Push the results for joining with the 'then' block
8034 if (!f.pushDefs(callResults)) {
8035 return false;
8036 }
8037
8038 // Join the two branches together
8039 return f.joinIfElse(elseBlock, results);
8040}
8041
8042static bool EmitCallRef(FunctionCompiler& f) {
8043 uint32_t bytecodeOffset = f.readBytecodeOffset();
8044 const FuncType* funcType;
8045 MDefinition* callee;
8046 DefVector args;
8047
8048 if (!f.iter().readCallRef(&funcType, &callee, &args)) {
8049 return false;
8050 }
8051
8052 // We must unconditionally read a call_ref hint so that we stay in sync with
8053 // how baseline generates them.
8054 CallRefHint hint = f.readCallRefHint();
8055
8056 if (f.inDeadCode()) {
8057 return true;
8058 }
8059
8060 if (hint.isInlineFunc() &&
8061 f.shouldInlineCall(InliningHeuristics::CallKind::CallRef,
8062 hint.inlineFuncIndex())) {
8063 DefVector results;
8064 if (!EmitSpeculativeInlineCallRef(f, bytecodeOffset, *funcType,
8065 hint.inlineFuncIndex(), callee, args,
8066 &results)) {
8067 return false;
8068 }
8069 f.iter().setResults(results.length(), results);
8070 return true;
8071 }
8072
8073 CallCompileState call;
8074 if (!EmitCallArgs(f, *funcType, args, &call)) {
8075 return false;
8076 }
8077
8078 DefVector results;
8079 if (!f.callRef(*funcType, callee, bytecodeOffset, call, &results)) {
8080 return false;
8081 }
8082
8083 f.iter().setResults(results.length(), results);
8084 return true;
8085}
8086
8087#endif // ENABLE_WASM_GC
8088
8089#ifdef ENABLE_WASM_GC1
8090
8091static bool EmitStructNew(FunctionCompiler& f) {
8092 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8093
8094 uint32_t typeIndex;
8095 DefVector args;
8096 if (!f.iter().readStructNew(&typeIndex, &args)) {
8097 return false;
8098 }
8099
8100 if (f.inDeadCode()) {
8101 return true;
8102 }
8103
8104 const TypeDef& typeDef = (*f.codeMeta().types)[typeIndex];
8105 const StructType& structType = typeDef.structType();
8106 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"
, 8106); AnnotateMozCrashReason("MOZ_ASSERT" "(" "args.length() == structType.fields_.length()"
")"); do { *((volatile int*)__null) = 8106; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8107
8108 MDefinition* structObject = f.createStructObject(typeIndex, false);
8109 if (!structObject) {
8110 return false;
8111 }
8112
8113 // And fill in the fields.
8114 for (uint32_t fieldIndex = 0; fieldIndex < structType.fields_.length();
8115 fieldIndex++) {
8116 if (!f.mirGen().ensureBallast()) {
8117 return false;
8118 }
8119 if (!f.writeValueToStructField(lineOrBytecode, structType, fieldIndex,
8120 structObject, args[fieldIndex],
8121 WasmPreBarrierKind::None)) {
8122 return false;
8123 }
8124 }
8125
8126 f.iter().setResult(structObject);
8127 return true;
8128}
8129
8130static bool EmitStructNewDefault(FunctionCompiler& f) {
8131 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8132
8133 uint32_t typeIndex;
8134 if (!f.iter().readStructNewDefault(&typeIndex)) {
8135 return false;
8136 }
8137
8138 if (f.inDeadCode()) {
8139 return true;
8140 }
8141
8142 const StructType& structType = (*f.codeMeta().types)[typeIndex].structType();
8143
8144 // Allocate a default initialized struct. This requires the type definition
8145 // for the struct.
8146 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8147 if (!typeDefData) {
8148 return false;
8149 }
8150
8151 // Figure out whether we need an OOL storage area, and hence which routine
8152 // to call.
8153 SymbolicAddressSignature calleeSASig =
8154 WasmStructObject::requiresOutlineBytes(structType.size_)
8155 ? SASigStructNewOOL_true
8156 : SASigStructNewIL_true;
8157
8158 // Create call: structObject = Instance::structNew{IL,OOL}<true>(typeDefData)
8159 MDefinition* structObject;
8160 if (!f.emitInstanceCall1(lineOrBytecode, calleeSASig, typeDefData,
8161 &structObject)) {
8162 return false;
8163 }
8164
8165 f.iter().setResult(structObject);
8166 return true;
8167}
8168
8169static bool EmitStructSet(FunctionCompiler& f) {
8170 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8171
8172 uint32_t typeIndex;
8173 uint32_t fieldIndex;
8174 MDefinition* structObject;
8175 MDefinition* value;
8176 if (!f.iter().readStructSet(&typeIndex, &fieldIndex, &structObject, &value)) {
8177 return false;
8178 }
8179
8180 if (f.inDeadCode()) {
8181 return true;
8182 }
8183
8184 // Check for null is done at writeValueToStructField.
8185
8186 // And fill in the field.
8187 const StructType& structType = (*f.codeMeta().types)[typeIndex].structType();
8188 return f.writeValueToStructField(lineOrBytecode, structType, fieldIndex,
8189 structObject, value,
8190 WasmPreBarrierKind::Normal);
8191}
8192
8193static bool EmitStructGet(FunctionCompiler& f, FieldWideningOp wideningOp) {
8194 uint32_t typeIndex;
8195 uint32_t fieldIndex;
8196 MDefinition* structObject;
8197 if (!f.iter().readStructGet(&typeIndex, &fieldIndex, wideningOp,
8198 &structObject)) {
8199 return false;
8200 }
8201
8202 if (f.inDeadCode()) {
8203 return true;
8204 }
8205
8206 // Check for null is done at readValueFromStructField.
8207
8208 // And fetch the data.
8209 const StructType& structType = (*f.codeMeta().types)[typeIndex].structType();
8210 MDefinition* load = f.readValueFromStructField(structType, fieldIndex,
8211 wideningOp, structObject);
8212 if (!load) {
8213 return false;
8214 }
8215
8216 f.iter().setResult(load);
8217 return true;
8218}
8219
8220static bool EmitArrayNew(FunctionCompiler& f) {
8221 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8222
8223 uint32_t typeIndex;
8224 MDefinition* numElements;
8225 MDefinition* fillValue;
8226 if (!f.iter().readArrayNew(&typeIndex, &numElements, &fillValue)) {
8227 return false;
8228 }
8229
8230 if (f.inDeadCode()) {
8231 return true;
8232 }
8233
8234 // If the requested size exceeds MaxArrayPayloadBytes, the MIR generated by
8235 // this helper will trap.
8236 MDefinition* arrayObject = f.createArrayNewCallAndLoop(
8237 lineOrBytecode, typeIndex, numElements, fillValue);
8238 if (!arrayObject) {
8239 return false;
8240 }
8241
8242 f.iter().setResult(arrayObject);
8243 return true;
8244}
8245
8246static bool EmitArrayNewDefault(FunctionCompiler& f) {
8247 // This is almost identical to EmitArrayNew, except we skip the
8248 // initialisation loop.
8249 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8250
8251 uint32_t typeIndex;
8252 MDefinition* numElements;
8253 if (!f.iter().readArrayNewDefault(&typeIndex, &numElements)) {
8254 return false;
8255 }
8256
8257 if (f.inDeadCode()) {
8258 return true;
8259 }
8260
8261 // Create the array object, default-initialized.
8262 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
8263 MDefinition* arrayObject =
8264 f.createArrayObject(lineOrBytecode, typeIndex, numElements,
8265 arrayType.elementType().size(), /*zeroFields=*/true);
8266 if (!arrayObject) {
8267 return false;
8268 }
8269
8270 f.iter().setResult(arrayObject);
8271 return true;
8272}
8273
8274static bool EmitArrayNewFixed(FunctionCompiler& f) {
8275 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8276
8277 uint32_t typeIndex, numElements;
8278 DefVector values;
8279
8280 if (!f.iter().readArrayNewFixed(&typeIndex, &numElements, &values)) {
8281 return false;
8282 }
8283 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"
, 8283); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.length() == numElements"
")"); do { *((volatile int*)__null) = 8283; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8284
8285 if (f.inDeadCode()) {
8286 return true;
8287 }
8288
8289 MDefinition* numElementsDef = f.constantI32(int32_t(numElements));
8290 if (!numElementsDef) {
8291 return false;
8292 }
8293
8294 // Create the array object, uninitialized.
8295 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
8296 StorageType elemType = arrayType.elementType();
8297 uint32_t elemSize = elemType.size();
8298 MDefinition* arrayObject =
8299 f.createArrayObject(lineOrBytecode, typeIndex, numElementsDef, elemSize,
8300 /*zeroFields=*/false);
8301 if (!arrayObject) {
8302 return false;
8303 }
8304
8305 // Make `base` point at the first byte of the (OOL) data area.
8306 MDefinition* base = f.getWasmArrayObjectData(arrayObject);
8307 if (!base) {
8308 return false;
8309 }
8310
8311 // Write each element in turn.
8312
8313 // How do we know that the offset expression `i * elemSize` below remains
8314 // within 2^31 (signed-i32) range? In the worst case we will have 16-byte
8315 // values, and there can be at most MaxFunctionBytes expressions, if it were
8316 // theoretically possible to generate one expression per instruction byte.
8317 // Hence the max offset we can be expected to generate is
8318 // `16 * MaxFunctionBytes`.
8319 static_assert(16 /* sizeof v128 */ * MaxFunctionBytes <=
8320 MaxArrayPayloadBytes);
8321 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"
, 8321); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "numElements <= MaxFunctionBytes"
")"); do { *((volatile int*)__null) = 8321; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8322
8323 for (uint32_t i = 0; i < numElements; i++) {
8324 if (!f.mirGen().ensureBallast()) {
8325 return false;
8326 }
8327 // `i * elemSize` is made safe by the assertions above.
8328 if (!f.writeGcValueAtBasePlusOffset(
8329 lineOrBytecode, elemType, arrayObject, AliasSet::WasmArrayDataArea,
8330 values[numElements - 1 - i], base, i * elemSize, false,
8331 WasmPreBarrierKind::None)) {
8332 return false;
8333 }
8334 }
8335
8336 f.iter().setResult(arrayObject);
8337 return true;
8338}
8339
8340static bool EmitArrayNewData(FunctionCompiler& f) {
8341 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8342
8343 uint32_t typeIndex, segIndex;
8344 MDefinition* segByteOffset;
8345 MDefinition* numElements;
8346 if (!f.iter().readArrayNewData(&typeIndex, &segIndex, &segByteOffset,
8347 &numElements)) {
8348 return false;
8349 }
8350
8351 if (f.inDeadCode()) {
8352 return true;
8353 }
8354
8355 // Get the type definition data for the array as a whole.
8356 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8357 if (!typeDefData) {
8358 return false;
8359 }
8360
8361 // Other values we need to pass to the instance call:
8362 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8363 if (!segIndexM) {
8364 return false;
8365 }
8366
8367 // Create call:
8368 // arrayObject = Instance::arrayNewData(segByteOffset:u32, numElements:u32,
8369 // typeDefData:word, segIndex:u32)
8370 // If the requested size exceeds MaxArrayPayloadBytes, the MIR generated by
8371 // this call will trap.
8372 MDefinition* arrayObject;
8373 if (!f.emitInstanceCall4(lineOrBytecode, SASigArrayNewData, segByteOffset,
8374 numElements, typeDefData, segIndexM, &arrayObject)) {
8375 return false;
8376 }
8377
8378 f.iter().setResult(arrayObject);
8379 return true;
8380}
8381
8382static bool EmitArrayNewElem(FunctionCompiler& f) {
8383 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8384
8385 uint32_t typeIndex, segIndex;
8386 MDefinition* segElemIndex;
8387 MDefinition* numElements;
8388 if (!f.iter().readArrayNewElem(&typeIndex, &segIndex, &segElemIndex,
8389 &numElements)) {
8390 return false;
8391 }
8392
8393 if (f.inDeadCode()) {
8394 return true;
8395 }
8396
8397 // Get the type definition for the array as a whole.
8398 // Get the type definition data for the array as a whole.
8399 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8400 if (!typeDefData) {
8401 return false;
8402 }
8403
8404 // Other values we need to pass to the instance call:
8405 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8406 if (!segIndexM) {
8407 return false;
8408 }
8409
8410 // Create call:
8411 // arrayObject = Instance::arrayNewElem(segElemIndex:u32, numElements:u32,
8412 // typeDefData:word, segIndex:u32)
8413 // If the requested size exceeds MaxArrayPayloadBytes, the MIR generated by
8414 // this call will trap.
8415 MDefinition* arrayObject;
8416 if (!f.emitInstanceCall4(lineOrBytecode, SASigArrayNewElem, segElemIndex,
8417 numElements, typeDefData, segIndexM, &arrayObject)) {
8418 return false;
8419 }
8420
8421 f.iter().setResult(arrayObject);
8422 return true;
8423}
8424
8425static bool EmitArrayInitData(FunctionCompiler& f) {
8426 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8427
8428 uint32_t typeIndex, segIndex;
8429 MDefinition* array;
8430 MDefinition* arrayIndex;
8431 MDefinition* segOffset;
8432 MDefinition* length;
8433 if (!f.iter().readArrayInitData(&typeIndex, &segIndex, &array, &arrayIndex,
8434 &segOffset, &length)) {
8435 return false;
8436 }
8437
8438 if (f.inDeadCode()) {
8439 return true;
8440 }
8441
8442 // Get the type definition data for the array as a whole.
8443 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8444 if (!typeDefData) {
8445 return false;
8446 }
8447
8448 // Other values we need to pass to the instance call:
8449 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8450 if (!segIndexM) {
8451 return false;
8452 }
8453
8454 // Create call:
8455 // Instance::arrayInitData(array:word, index:u32, segByteOffset:u32,
8456 // numElements:u32, typeDefData:word, segIndex:u32) If the requested size
8457 // exceeds MaxArrayPayloadBytes, the MIR generated by this call will trap.
8458 return f.emitInstanceCall6(lineOrBytecode, SASigArrayInitData, array,
8459 arrayIndex, segOffset, length, typeDefData,
8460 segIndexM);
8461}
8462
8463static bool EmitArrayInitElem(FunctionCompiler& f) {
8464 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8465
8466 uint32_t typeIndex, segIndex;
8467 MDefinition* array;
8468 MDefinition* arrayIndex;
8469 MDefinition* segOffset;
8470 MDefinition* length;
8471 if (!f.iter().readArrayInitElem(&typeIndex, &segIndex, &array, &arrayIndex,
8472 &segOffset, &length)) {
8473 return false;
8474 }
8475
8476 if (f.inDeadCode()) {
8477 return true;
8478 }
8479
8480 // Get the type definition data for the array as a whole.
8481 MDefinition* typeDefData = f.loadTypeDefInstanceData(typeIndex);
8482 if (!typeDefData) {
8483 return false;
8484 }
8485
8486 // Other values we need to pass to the instance call:
8487 MDefinition* segIndexM = f.constantI32(int32_t(segIndex));
8488 if (!segIndexM) {
8489 return false;
8490 }
8491
8492 // Create call:
8493 // Instance::arrayInitElem(array:word, index:u32, segByteOffset:u32,
8494 // numElements:u32, typeDefData:word, segIndex:u32) If the requested size
8495 // exceeds MaxArrayPayloadBytes, the MIR generated by this call will trap.
8496 return f.emitInstanceCall6(lineOrBytecode, SASigArrayInitElem, array,
8497 arrayIndex, segOffset, length, typeDefData,
8498 segIndexM);
8499}
8500
8501static bool EmitArraySet(FunctionCompiler& f) {
8502 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8503
8504 uint32_t typeIndex;
8505 MDefinition* value;
8506 MDefinition* index;
8507 MDefinition* arrayObject;
8508 if (!f.iter().readArraySet(&typeIndex, &value, &index, &arrayObject)) {
8509 return false;
8510 }
8511
8512 if (f.inDeadCode()) {
8513 return true;
8514 }
8515
8516 // Check for null is done at setupForArrayAccess.
8517
8518 // Create the object null check and the array bounds check and get the OOL
8519 // data pointer.
8520 MDefinition* base = f.setupForArrayAccess(arrayObject, index);
8521 if (!base) {
8522 return false;
8523 }
8524
8525 // And do the store.
8526 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
8527 StorageType elemType = arrayType.elementType();
8528 uint32_t elemSize = elemType.size();
8529 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"
, 8529); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize >= 1 && elemSize <= 16"
")"); do { *((volatile int*)__null) = 8529; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8530
8531 return f.writeGcValueAtBasePlusScaledIndex(
8532 lineOrBytecode, elemType, arrayObject, AliasSet::WasmArrayDataArea, value,
8533 base, elemSize, index, WasmPreBarrierKind::Normal);
8534}
8535
8536static bool EmitArrayGet(FunctionCompiler& f, FieldWideningOp wideningOp) {
8537 uint32_t typeIndex;
8538 MDefinition* index;
8539 MDefinition* arrayObject;
8540 if (!f.iter().readArrayGet(&typeIndex, wideningOp, &index, &arrayObject)) {
8541 return false;
8542 }
8543
8544 if (f.inDeadCode()) {
8545 return true;
8546 }
8547
8548 // Check for null is done at setupForArrayAccess.
8549
8550 // Create the object null check and the array bounds check and get the data
8551 // pointer.
8552 MDefinition* base = f.setupForArrayAccess(arrayObject, index);
8553 if (!base) {
8554 return false;
8555 }
8556
8557 // And do the load.
8558 const ArrayType& arrayType = (*f.codeMeta().types)[typeIndex].arrayType();
8559 StorageType elemType = arrayType.elementType();
8560
8561 MDefinition* load =
8562 f.readGcArrayValueAtIndex(elemType, wideningOp, arrayObject,
8563 AliasSet::WasmArrayDataArea, base, index);
8564 if (!load) {
8565 return false;
8566 }
8567
8568 f.iter().setResult(load);
8569 return true;
8570}
8571
8572static bool EmitArrayLen(FunctionCompiler& f) {
8573 MDefinition* arrayObject;
8574 if (!f.iter().readArrayLen(&arrayObject)) {
8575 return false;
8576 }
8577
8578 if (f.inDeadCode()) {
8579 return true;
8580 }
8581
8582 // Check for null is done at getWasmArrayObjectNumElements.
8583
8584 // Get the size value for the array
8585 MDefinition* numElements = f.getWasmArrayObjectNumElements(arrayObject);
8586 if (!numElements) {
8587 return false;
8588 }
8589
8590 f.iter().setResult(numElements);
8591 return true;
8592}
8593
8594static bool EmitArrayCopy(FunctionCompiler& f) {
8595 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8596
8597 int32_t elemSize;
8598 bool elemsAreRefTyped;
8599 MDefinition* dstArrayObject;
8600 MDefinition* dstArrayIndex;
8601 MDefinition* srcArrayObject;
8602 MDefinition* srcArrayIndex;
8603 MDefinition* numElements;
8604 if (!f.iter().readArrayCopy(&elemSize, &elemsAreRefTyped, &dstArrayObject,
8605 &dstArrayIndex, &srcArrayObject, &srcArrayIndex,
8606 &numElements)) {
8607 return false;
8608 }
8609
8610 if (f.inDeadCode()) {
8611 return true;
8612 }
8613
8614 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"
, 8615); AnnotateMozCrashReason("MOZ_ASSERT" "(" "size_t(elemSize) == MIRTypeToSize(TargetWordMIRType())"
")"); do { *((volatile int*)__null) = 8615; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
8615 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"
, 8615); AnnotateMozCrashReason("MOZ_ASSERT" "(" "size_t(elemSize) == MIRTypeToSize(TargetWordMIRType())"
")"); do { *((volatile int*)__null) = 8615; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
8616 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"
, 8618); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 8618; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
8617 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"
, 8618); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 8618; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
8618 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"
, 8618); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 8618; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
8619
8620 // A negative element size is used to inform Instance::arrayCopy that the
8621 // values are reftyped. This avoids having to pass it an extra boolean
8622 // argument.
8623 MDefinition* elemSizeDef =
8624 f.constantI32(elemsAreRefTyped ? -elemSize : elemSize);
8625 if (!elemSizeDef) {
8626 return false;
8627 }
8628
8629 // Create call:
8630 // Instance::arrayCopy(dstArrayObject:word, dstArrayIndex:u32,
8631 // srcArrayObject:word, srcArrayIndex:u32,
8632 // numElements:u32,
8633 // (elemsAreRefTyped ? -elemSize : elemSize):u32))
8634 return f.emitInstanceCall6(lineOrBytecode, SASigArrayCopy, dstArrayObject,
8635 dstArrayIndex, srcArrayObject, srcArrayIndex,
8636 numElements, elemSizeDef);
8637}
8638
8639static bool EmitArrayFill(FunctionCompiler& f) {
8640 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
8641
8642 uint32_t typeIndex;
8643 MDefinition* array;
8644 MDefinition* index;
8645 MDefinition* val;
8646 MDefinition* numElements;
8647 if (!f.iter().readArrayFill(&typeIndex, &array, &index, &val, &numElements)) {
8648 return false;
8649 }
8650
8651 if (f.inDeadCode()) {
8652 return true;
8653 }
8654
8655 return f.createArrayFill(lineOrBytecode, typeIndex, array, index, val,
8656 numElements);
8657}
8658
8659static bool EmitRefI31(FunctionCompiler& f) {
8660 MDefinition* input;
8661 if (!f.iter().readConversion(
8662 ValType::I32, ValType(RefType::i31().asNonNullable()), &input)) {
8663 return false;
8664 }
8665
8666 if (f.inDeadCode()) {
8667 return true;
8668 }
8669
8670 MDefinition* output = f.refI31(input);
8671 if (!output) {
8672 return false;
8673 }
8674 f.iter().setResult(output);
8675 return true;
8676}
8677
8678static bool EmitI31Get(FunctionCompiler& f, FieldWideningOp wideningOp) {
8679 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"
, 8679); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp != FieldWideningOp::None"
")"); do { *((volatile int*)__null) = 8679; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8680
8681 MDefinition* input;
8682 if (!f.iter().readConversion(ValType(RefType::i31()), ValType::I32, &input)) {
8683 return false;
8684 }
8685
8686 if (f.inDeadCode()) {
8687 return true;
8688 }
8689
8690 if (!f.refAsNonNull(input)) {
8691 return false;
8692 }
8693 MDefinition* output = f.i31Get(input, wideningOp);
8694 if (!output) {
8695 return false;
8696 }
8697 f.iter().setResult(output);
8698 return true;
8699}
8700
8701static bool EmitRefTest(FunctionCompiler& f, bool nullable) {
8702 MDefinition* ref;
8703 RefType sourceType;
8704 RefType destType;
8705 if (!f.iter().readRefTest(nullable, &sourceType, &destType, &ref)) {
8706 return false;
8707 }
8708
8709 if (f.inDeadCode()) {
8710 return true;
8711 }
8712
8713 MDefinition* success = f.refTest(ref, sourceType, destType);
8714 if (!success) {
8715 return false;
8716 }
8717
8718 f.iter().setResult(success);
8719 return true;
8720}
8721
8722static bool EmitRefCast(FunctionCompiler& f, bool nullable) {
8723 MDefinition* ref;
8724 RefType sourceType;
8725 RefType destType;
8726 if (!f.iter().readRefCast(nullable, &sourceType, &destType, &ref)) {
8727 return false;
8728 }
8729
8730 if (f.inDeadCode()) {
8731 return true;
8732 }
8733
8734 if (!f.refCast(ref, sourceType, destType)) {
8735 return false;
8736 }
8737
8738 f.iter().setResult(ref);
8739 return true;
8740}
8741
8742static bool EmitBrOnCast(FunctionCompiler& f, bool onSuccess) {
8743 uint32_t labelRelativeDepth;
8744 RefType sourceType;
8745 RefType destType;
8746 ResultType labelType;
8747 DefVector values;
8748 if (!f.iter().readBrOnCast(onSuccess, &labelRelativeDepth, &sourceType,
8749 &destType, &labelType, &values)) {
8750 return false;
8751 }
8752
8753 return f.brOnCastCommon(onSuccess, labelRelativeDepth, sourceType, destType,
8754 labelType, values);
8755}
8756
8757static bool EmitAnyConvertExtern(FunctionCompiler& f) {
8758 // any.convert_extern is a no-op because anyref and extern share the same
8759 // representation
8760 MDefinition* ref;
8761 if (!f.iter().readRefConversion(RefType::extern_(), RefType::any(), &ref)) {
8762 return false;
8763 }
8764
8765 f.iter().setResult(ref);
8766 return true;
8767}
8768
8769static bool EmitExternConvertAny(FunctionCompiler& f) {
8770 // extern.convert_any is a no-op because anyref and extern share the same
8771 // representation
8772 MDefinition* ref;
8773 if (!f.iter().readRefConversion(RefType::any(), RefType::extern_(), &ref)) {
8774 return false;
8775 }
8776
8777 f.iter().setResult(ref);
8778 return true;
8779}
8780
8781#endif // ENABLE_WASM_GC
8782
8783static bool EmitCallBuiltinModuleFunc(FunctionCompiler& f) {
8784 const BuiltinModuleFunc* builtinModuleFunc;
8785
8786 DefVector params;
8787 if (!f.iter().readCallBuiltinModuleFunc(&builtinModuleFunc, &params)) {
8788 return false;
8789 }
8790
8791 return f.callBuiltinModuleFunc(*builtinModuleFunc, params);
8792}
8793
8794bool EmitBodyExprs(FunctionCompiler& f) {
8795 if (!f.iter().startFunction(f.funcIndex(), f.locals())) {
8796 return false;
8797 }
8798
8799#define CHECK(c) \
8800 if (!(c)) return false; \
8801 break
8802
8803 while (true) {
8804 if (!f.mirGen().ensureBallast()) {
8805 return false;
8806 }
8807
8808 OpBytes op;
8809 if (!f.iter().readOp(&op)) {
8810 return false;
8811 }
8812
8813 switch (op.b0) {
8814 case uint16_t(Op::End):
8815 if (!EmitEnd(f)) {
8816 return false;
8817 }
8818 if (f.iter().controlStackEmpty()) {
8819 return true;
8820 }
8821 break;
8822
8823 // Control opcodes
8824 case uint16_t(Op::Unreachable):
8825 CHECK(EmitUnreachable(f));
8826 case uint16_t(Op::Nop):
8827 CHECK(f.iter().readNop());
8828 case uint16_t(Op::Block):
8829 CHECK(EmitBlock(f));
8830 case uint16_t(Op::Loop):
8831 CHECK(EmitLoop(f));
8832 case uint16_t(Op::If):
8833 CHECK(EmitIf(f));
8834 case uint16_t(Op::Else):
8835 CHECK(EmitElse(f));
8836 case uint16_t(Op::Try):
8837 CHECK(EmitTry(f));
8838 case uint16_t(Op::Catch):
8839 CHECK(EmitCatch(f));
8840 case uint16_t(Op::CatchAll):
8841 CHECK(EmitCatchAll(f));
8842 case uint16_t(Op::Delegate):
8843 if (!EmitDelegate(f)) {
8844 return false;
8845 }
8846 break;
8847 case uint16_t(Op::Throw):
8848 CHECK(EmitThrow(f));
8849 case uint16_t(Op::Rethrow):
8850 CHECK(EmitRethrow(f));
8851 case uint16_t(Op::ThrowRef):
8852 if (!f.codeMeta().exnrefEnabled()) {
8853 return f.iter().unrecognizedOpcode(&op);
8854 }
8855 CHECK(EmitThrowRef(f));
8856 case uint16_t(Op::TryTable):
8857 if (!f.codeMeta().exnrefEnabled()) {
8858 return f.iter().unrecognizedOpcode(&op);
8859 }
8860 CHECK(EmitTryTable(f));
8861 case uint16_t(Op::Br):
8862 CHECK(EmitBr(f));
8863 case uint16_t(Op::BrIf):
8864 CHECK(EmitBrIf(f));
8865 case uint16_t(Op::BrTable):
8866 CHECK(EmitBrTable(f));
8867 case uint16_t(Op::Return):
8868 CHECK(EmitReturn(f));
8869
8870 // Calls
8871 case uint16_t(Op::Call):
8872 CHECK(EmitCall(f, /* asmJSFuncDef = */ false));
8873 case uint16_t(Op::CallIndirect):
8874 CHECK(EmitCallIndirect(f, /* oldStyle = */ false));
8875
8876 // Parametric operators
8877 case uint16_t(Op::Drop):
8878 CHECK(f.iter().readDrop());
8879 case uint16_t(Op::SelectNumeric):
8880 CHECK(EmitSelect(f, /*typed*/ false));
8881 case uint16_t(Op::SelectTyped):
8882 CHECK(EmitSelect(f, /*typed*/ true));
8883
8884 // Locals and globals
8885 case uint16_t(Op::LocalGet):
8886 CHECK(EmitGetLocal(f));
8887 case uint16_t(Op::LocalSet):
8888 CHECK(EmitSetLocal(f));
8889 case uint16_t(Op::LocalTee):
8890 CHECK(EmitTeeLocal(f));
8891 case uint16_t(Op::GlobalGet):
8892 CHECK(EmitGetGlobal(f));
8893 case uint16_t(Op::GlobalSet):
8894 CHECK(EmitSetGlobal(f));
8895 case uint16_t(Op::TableGet):
8896 CHECK(EmitTableGet(f));
8897 case uint16_t(Op::TableSet):
8898 CHECK(EmitTableSet(f));
8899
8900 // Memory-related operators
8901 case uint16_t(Op::I32Load):
8902 CHECK(EmitLoad(f, ValType::I32, Scalar::Int32));
8903 case uint16_t(Op::I64Load):
8904 CHECK(EmitLoad(f, ValType::I64, Scalar::Int64));
8905 case uint16_t(Op::F32Load):
8906 CHECK(EmitLoad(f, ValType::F32, Scalar::Float32));
8907 case uint16_t(Op::F64Load):
8908 CHECK(EmitLoad(f, ValType::F64, Scalar::Float64));
8909 case uint16_t(Op::I32Load8S):
8910 CHECK(EmitLoad(f, ValType::I32, Scalar::Int8));
8911 case uint16_t(Op::I32Load8U):
8912 CHECK(EmitLoad(f, ValType::I32, Scalar::Uint8));
8913 case uint16_t(Op::I32Load16S):
8914 CHECK(EmitLoad(f, ValType::I32, Scalar::Int16));
8915 case uint16_t(Op::I32Load16U):
8916 CHECK(EmitLoad(f, ValType::I32, Scalar::Uint16));
8917 case uint16_t(Op::I64Load8S):
8918 CHECK(EmitLoad(f, ValType::I64, Scalar::Int8));
8919 case uint16_t(Op::I64Load8U):
8920 CHECK(EmitLoad(f, ValType::I64, Scalar::Uint8));
8921 case uint16_t(Op::I64Load16S):
8922 CHECK(EmitLoad(f, ValType::I64, Scalar::Int16));
8923 case uint16_t(Op::I64Load16U):
8924 CHECK(EmitLoad(f, ValType::I64, Scalar::Uint16));
8925 case uint16_t(Op::I64Load32S):
8926 CHECK(EmitLoad(f, ValType::I64, Scalar::Int32));
8927 case uint16_t(Op::I64Load32U):
8928 CHECK(EmitLoad(f, ValType::I64, Scalar::Uint32));
8929 case uint16_t(Op::I32Store):
8930 CHECK(EmitStore(f, ValType::I32, Scalar::Int32));
8931 case uint16_t(Op::I64Store):
8932 CHECK(EmitStore(f, ValType::I64, Scalar::Int64));
8933 case uint16_t(Op::F32Store):
8934 CHECK(EmitStore(f, ValType::F32, Scalar::Float32));
8935 case uint16_t(Op::F64Store):
8936 CHECK(EmitStore(f, ValType::F64, Scalar::Float64));
8937 case uint16_t(Op::I32Store8):
8938 CHECK(EmitStore(f, ValType::I32, Scalar::Int8));
8939 case uint16_t(Op::I32Store16):
8940 CHECK(EmitStore(f, ValType::I32, Scalar::Int16));
8941 case uint16_t(Op::I64Store8):
8942 CHECK(EmitStore(f, ValType::I64, Scalar::Int8));
8943 case uint16_t(Op::I64Store16):
8944 CHECK(EmitStore(f, ValType::I64, Scalar::Int16));
8945 case uint16_t(Op::I64Store32):
8946 CHECK(EmitStore(f, ValType::I64, Scalar::Int32));
8947 case uint16_t(Op::MemorySize):
8948 CHECK(EmitMemorySize(f));
8949 case uint16_t(Op::MemoryGrow):
8950 CHECK(EmitMemoryGrow(f));
8951
8952 // Constants
8953 case uint16_t(Op::I32Const):
8954 CHECK(EmitI32Const(f));
8955 case uint16_t(Op::I64Const):
8956 CHECK(EmitI64Const(f));
8957 case uint16_t(Op::F32Const):
8958 CHECK(EmitF32Const(f));
8959 case uint16_t(Op::F64Const):
8960 CHECK(EmitF64Const(f));
8961
8962 // Comparison operators
8963 case uint16_t(Op::I32Eqz):
8964 CHECK(EmitConversion<MNot>(f, ValType::I32, ValType::I32));
8965 case uint16_t(Op::I32Eq):
8966 CHECK(
8967 EmitComparison(f, ValType::I32, JSOp::Eq, MCompare::Compare_Int32));
8968 case uint16_t(Op::I32Ne):
8969 CHECK(
8970 EmitComparison(f, ValType::I32, JSOp::Ne, MCompare::Compare_Int32));
8971 case uint16_t(Op::I32LtS):
8972 CHECK(
8973 EmitComparison(f, ValType::I32, JSOp::Lt, MCompare::Compare_Int32));
8974 case uint16_t(Op::I32LtU):
8975 CHECK(EmitComparison(f, ValType::I32, JSOp::Lt,
8976 MCompare::Compare_UInt32));
8977 case uint16_t(Op::I32GtS):
8978 CHECK(
8979 EmitComparison(f, ValType::I32, JSOp::Gt, MCompare::Compare_Int32));
8980 case uint16_t(Op::I32GtU):
8981 CHECK(EmitComparison(f, ValType::I32, JSOp::Gt,
8982 MCompare::Compare_UInt32));
8983 case uint16_t(Op::I32LeS):
8984 CHECK(
8985 EmitComparison(f, ValType::I32, JSOp::Le, MCompare::Compare_Int32));
8986 case uint16_t(Op::I32LeU):
8987 CHECK(EmitComparison(f, ValType::I32, JSOp::Le,
8988 MCompare::Compare_UInt32));
8989 case uint16_t(Op::I32GeS):
8990 CHECK(
8991 EmitComparison(f, ValType::I32, JSOp::Ge, MCompare::Compare_Int32));
8992 case uint16_t(Op::I32GeU):
8993 CHECK(EmitComparison(f, ValType::I32, JSOp::Ge,
8994 MCompare::Compare_UInt32));
8995 case uint16_t(Op::I64Eqz):
8996 CHECK(EmitConversion<MNot>(f, ValType::I64, ValType::I32));
8997 case uint16_t(Op::I64Eq):
8998 CHECK(
8999 EmitComparison(f, ValType::I64, JSOp::Eq, MCompare::Compare_Int64));
9000 case uint16_t(Op::I64Ne):
9001 CHECK(
9002 EmitComparison(f, ValType::I64, JSOp::Ne, MCompare::Compare_Int64));
9003 case uint16_t(Op::I64LtS):
9004 CHECK(
9005 EmitComparison(f, ValType::I64, JSOp::Lt, MCompare::Compare_Int64));
9006 case uint16_t(Op::I64LtU):
9007 CHECK(EmitComparison(f, ValType::I64, JSOp::Lt,
9008 MCompare::Compare_UInt64));
9009 case uint16_t(Op::I64GtS):
9010 CHECK(
9011 EmitComparison(f, ValType::I64, JSOp::Gt, MCompare::Compare_Int64));
9012 case uint16_t(Op::I64GtU):
9013 CHECK(EmitComparison(f, ValType::I64, JSOp::Gt,
9014 MCompare::Compare_UInt64));
9015 case uint16_t(Op::I64LeS):
9016 CHECK(
9017 EmitComparison(f, ValType::I64, JSOp::Le, MCompare::Compare_Int64));
9018 case uint16_t(Op::I64LeU):
9019 CHECK(EmitComparison(f, ValType::I64, JSOp::Le,
9020 MCompare::Compare_UInt64));
9021 case uint16_t(Op::I64GeS):
9022 CHECK(
9023 EmitComparison(f, ValType::I64, JSOp::Ge, MCompare::Compare_Int64));
9024 case uint16_t(Op::I64GeU):
9025 CHECK(EmitComparison(f, ValType::I64, JSOp::Ge,
9026 MCompare::Compare_UInt64));
9027 case uint16_t(Op::F32Eq):
9028 CHECK(EmitComparison(f, ValType::F32, JSOp::Eq,
9029 MCompare::Compare_Float32));
9030 case uint16_t(Op::F32Ne):
9031 CHECK(EmitComparison(f, ValType::F32, JSOp::Ne,
9032 MCompare::Compare_Float32));
9033 case uint16_t(Op::F32Lt):
9034 CHECK(EmitComparison(f, ValType::F32, JSOp::Lt,
9035 MCompare::Compare_Float32));
9036 case uint16_t(Op::F32Gt):
9037 CHECK(EmitComparison(f, ValType::F32, JSOp::Gt,
9038 MCompare::Compare_Float32));
9039 case uint16_t(Op::F32Le):
9040 CHECK(EmitComparison(f, ValType::F32, JSOp::Le,
9041 MCompare::Compare_Float32));
9042 case uint16_t(Op::F32Ge):
9043 CHECK(EmitComparison(f, ValType::F32, JSOp::Ge,
9044 MCompare::Compare_Float32));
9045 case uint16_t(Op::F64Eq):
9046 CHECK(EmitComparison(f, ValType::F64, JSOp::Eq,
9047 MCompare::Compare_Double));
9048 case uint16_t(Op::F64Ne):
9049 CHECK(EmitComparison(f, ValType::F64, JSOp::Ne,
9050 MCompare::Compare_Double));
9051 case uint16_t(Op::F64Lt):
9052 CHECK(EmitComparison(f, ValType::F64, JSOp::Lt,
9053 MCompare::Compare_Double));
9054 case uint16_t(Op::F64Gt):
9055 CHECK(EmitComparison(f, ValType::F64, JSOp::Gt,
9056 MCompare::Compare_Double));
9057 case uint16_t(Op::F64Le):
9058 CHECK(EmitComparison(f, ValType::F64, JSOp::Le,
9059 MCompare::Compare_Double));
9060 case uint16_t(Op::F64Ge):
9061 CHECK(EmitComparison(f, ValType::F64, JSOp::Ge,
9062 MCompare::Compare_Double));
9063
9064 // Numeric operators
9065 case uint16_t(Op::I32Clz):
9066 CHECK(EmitUnaryWithType<MClz>(f, ValType::I32, MIRType::Int32));
9067 case uint16_t(Op::I32Ctz):
9068 CHECK(EmitUnaryWithType<MCtz>(f, ValType::I32, MIRType::Int32));
9069 case uint16_t(Op::I32Popcnt):
9070 CHECK(EmitUnaryWithType<MPopcnt>(f, ValType::I32, MIRType::Int32));
9071 case uint16_t(Op::I32Add):
9072 CHECK(EmitAdd(f, ValType::I32, MIRType::Int32));
9073 case uint16_t(Op::I32Sub):
9074 CHECK(EmitSub(f, ValType::I32, MIRType::Int32));
9075 case uint16_t(Op::I32Mul):
9076 CHECK(EmitMul(f, ValType::I32, MIRType::Int32));
9077 case uint16_t(Op::I32DivS):
9078 case uint16_t(Op::I32DivU):
9079 CHECK(
9080 EmitDiv(f, ValType::I32, MIRType::Int32, Op(op.b0) == Op::I32DivU));
9081 case uint16_t(Op::I32RemS):
9082 case uint16_t(Op::I32RemU):
9083 CHECK(
9084 EmitRem(f, ValType::I32, MIRType::Int32, Op(op.b0) == Op::I32RemU));
9085 case uint16_t(Op::I32And):
9086 CHECK(EmitBitwiseAndOrXor(f, ValType::I32, MIRType::Int32,
9087 MWasmBinaryBitwise::SubOpcode::And));
9088 case uint16_t(Op::I32Or):
9089 CHECK(EmitBitwiseAndOrXor(f, ValType::I32, MIRType::Int32,
9090 MWasmBinaryBitwise::SubOpcode::Or));
9091 case uint16_t(Op::I32Xor):
9092 CHECK(EmitBitwiseAndOrXor(f, ValType::I32, MIRType::Int32,
9093 MWasmBinaryBitwise::SubOpcode::Xor));
9094 case uint16_t(Op::I32Shl):
9095 CHECK(EmitShift<MLsh>(f, ValType::I32, MIRType::Int32));
9096 case uint16_t(Op::I32ShrS):
9097 CHECK(EmitShift<MRsh>(f, ValType::I32, MIRType::Int32));
9098 case uint16_t(Op::I32ShrU):
9099 CHECK(EmitUrsh(f, ValType::I32, MIRType::Int32));
9100 case uint16_t(Op::I32Rotl):
9101 case uint16_t(Op::I32Rotr):
9102 CHECK(EmitRotate(f, ValType::I32, Op(op.b0) == Op::I32Rotl));
9103 case uint16_t(Op::I64Clz):
9104 CHECK(EmitUnaryWithType<MClz>(f, ValType::I64, MIRType::Int64));
9105 case uint16_t(Op::I64Ctz):
9106 CHECK(EmitUnaryWithType<MCtz>(f, ValType::I64, MIRType::Int64));
9107 case uint16_t(Op::I64Popcnt):
9108 CHECK(EmitUnaryWithType<MPopcnt>(f, ValType::I64, MIRType::Int64));
9109 case uint16_t(Op::I64Add):
9110 CHECK(EmitAdd(f, ValType::I64, MIRType::Int64));
9111 case uint16_t(Op::I64Sub):
9112 CHECK(EmitSub(f, ValType::I64, MIRType::Int64));
9113 case uint16_t(Op::I64Mul):
9114 CHECK(EmitMul(f, ValType::I64, MIRType::Int64));
9115 case uint16_t(Op::I64DivS):
9116 case uint16_t(Op::I64DivU):
9117 CHECK(
9118 EmitDiv(f, ValType::I64, MIRType::Int64, Op(op.b0) == Op::I64DivU));
9119 case uint16_t(Op::I64RemS):
9120 case uint16_t(Op::I64RemU):
9121 CHECK(
9122 EmitRem(f, ValType::I64, MIRType::Int64, Op(op.b0) == Op::I64RemU));
9123 case uint16_t(Op::I64And):
9124 CHECK(EmitBitwiseAndOrXor(f, ValType::I64, MIRType::Int64,
9125 MWasmBinaryBitwise::SubOpcode::And));
9126 case uint16_t(Op::I64Or):
9127 CHECK(EmitBitwiseAndOrXor(f, ValType::I64, MIRType::Int64,
9128 MWasmBinaryBitwise::SubOpcode::Or));
9129 case uint16_t(Op::I64Xor):
9130 CHECK(EmitBitwiseAndOrXor(f, ValType::I64, MIRType::Int64,
9131 MWasmBinaryBitwise::SubOpcode::Xor));
9132 case uint16_t(Op::I64Shl):
9133 CHECK(EmitShift<MLsh>(f, ValType::I64, MIRType::Int64));
9134 case uint16_t(Op::I64ShrS):
9135 CHECK(EmitShift<MRsh>(f, ValType::I64, MIRType::Int64));
9136 case uint16_t(Op::I64ShrU):
9137 CHECK(EmitUrsh(f, ValType::I64, MIRType::Int64));
9138 case uint16_t(Op::I64Rotl):
9139 case uint16_t(Op::I64Rotr):
9140 CHECK(EmitRotate(f, ValType::I64, Op(op.b0) == Op::I64Rotl));
9141 case uint16_t(Op::F32Abs):
9142 CHECK(EmitUnaryWithType<MAbs>(f, ValType::F32, MIRType::Float32));
9143 case uint16_t(Op::F32Neg):
9144 CHECK(EmitUnaryWithType<MWasmNeg>(f, ValType::F32, MIRType::Float32));
9145 case uint16_t(Op::F32Ceil):
9146 CHECK(EmitUnaryMathBuiltinCall(f, SASigCeilF));
9147 case uint16_t(Op::F32Floor):
9148 CHECK(EmitUnaryMathBuiltinCall(f, SASigFloorF));
9149 case uint16_t(Op::F32Trunc):
9150 CHECK(EmitUnaryMathBuiltinCall(f, SASigTruncF));
9151 case uint16_t(Op::F32Nearest):
9152 CHECK(EmitUnaryMathBuiltinCall(f, SASigNearbyIntF));
9153 case uint16_t(Op::F32Sqrt):
9154 CHECK(EmitUnaryWithType<MSqrt>(f, ValType::F32, MIRType::Float32));
9155 case uint16_t(Op::F32Add):
9156 CHECK(EmitAdd(f, ValType::F32, MIRType::Float32));
9157 case uint16_t(Op::F32Sub):
9158 CHECK(EmitSub(f, ValType::F32, MIRType::Float32));
9159 case uint16_t(Op::F32Mul):
9160 CHECK(EmitMul(f, ValType::F32, MIRType::Float32));
9161 case uint16_t(Op::F32Div):
9162 CHECK(EmitDiv(f, ValType::F32, MIRType::Float32,
9163 /* isUnsigned = */ false));
9164 case uint16_t(Op::F32Min):
9165 case uint16_t(Op::F32Max):
9166 CHECK(EmitMinMax(f, ValType::F32, MIRType::Float32,
9167 Op(op.b0) == Op::F32Max));
9168 case uint16_t(Op::F32CopySign):
9169 CHECK(EmitCopySign(f, ValType::F32));
9170 case uint16_t(Op::F64Abs):
9171 CHECK(EmitUnaryWithType<MAbs>(f, ValType::F64, MIRType::Double));
9172 case uint16_t(Op::F64Neg):
9173 CHECK(EmitUnaryWithType<MWasmNeg>(f, ValType::F64, MIRType::Double));
9174 case uint16_t(Op::F64Ceil):
9175 CHECK(EmitUnaryMathBuiltinCall(f, SASigCeilD));
9176 case uint16_t(Op::F64Floor):
9177 CHECK(EmitUnaryMathBuiltinCall(f, SASigFloorD));
9178 case uint16_t(Op::F64Trunc):
9179 CHECK(EmitUnaryMathBuiltinCall(f, SASigTruncD));
9180 case uint16_t(Op::F64Nearest):
9181 CHECK(EmitUnaryMathBuiltinCall(f, SASigNearbyIntD));
9182 case uint16_t(Op::F64Sqrt):
9183 CHECK(EmitUnaryWithType<MSqrt>(f, ValType::F64, MIRType::Double));
9184 case uint16_t(Op::F64Add):
9185 CHECK(EmitAdd(f, ValType::F64, MIRType::Double));
9186 case uint16_t(Op::F64Sub):
9187 CHECK(EmitSub(f, ValType::F64, MIRType::Double));
9188 case uint16_t(Op::F64Mul):
9189 CHECK(EmitMul(f, ValType::F64, MIRType::Double));
9190 case uint16_t(Op::F64Div):
9191 CHECK(EmitDiv(f, ValType::F64, MIRType::Double,
9192 /* isUnsigned = */ false));
9193 case uint16_t(Op::F64Min):
9194 case uint16_t(Op::F64Max):
9195 CHECK(EmitMinMax(f, ValType::F64, MIRType::Double,
9196 Op(op.b0) == Op::F64Max));
9197 case uint16_t(Op::F64CopySign):
9198 CHECK(EmitCopySign(f, ValType::F64));
9199
9200 // Conversions
9201 case uint16_t(Op::I32WrapI64):
9202 CHECK(EmitConversion<MWrapInt64ToInt32>(f, ValType::I64, ValType::I32));
9203 case uint16_t(Op::I32TruncF32S):
9204 case uint16_t(Op::I32TruncF32U):
9205 CHECK(EmitTruncate(f, ValType::F32, ValType::I32,
9206 Op(op.b0) == Op::I32TruncF32U, false));
9207 case uint16_t(Op::I32TruncF64S):
9208 case uint16_t(Op::I32TruncF64U):
9209 CHECK(EmitTruncate(f, ValType::F64, ValType::I32,
9210 Op(op.b0) == Op::I32TruncF64U, false));
9211 case uint16_t(Op::I64ExtendI32S):
9212 case uint16_t(Op::I64ExtendI32U):
9213 CHECK(EmitExtendI32(f, Op(op.b0) == Op::I64ExtendI32U));
9214 case uint16_t(Op::I64TruncF32S):
9215 case uint16_t(Op::I64TruncF32U):
9216 CHECK(EmitTruncate(f, ValType::F32, ValType::I64,
9217 Op(op.b0) == Op::I64TruncF32U, false));
9218 case uint16_t(Op::I64TruncF64S):
9219 case uint16_t(Op::I64TruncF64U):
9220 CHECK(EmitTruncate(f, ValType::F64, ValType::I64,
9221 Op(op.b0) == Op::I64TruncF64U, false));
9222 case uint16_t(Op::F32ConvertI32S):
9223 CHECK(EmitConversion<MToFloat32>(f, ValType::I32, ValType::F32));
9224 case uint16_t(Op::F32ConvertI32U):
9225 CHECK(EmitConversion<MWasmUnsignedToFloat32>(f, ValType::I32,
9226 ValType::F32));
9227 case uint16_t(Op::F32ConvertI64S):
9228 case uint16_t(Op::F32ConvertI64U):
9229 CHECK(EmitConvertI64ToFloatingPoint(f, ValType::F32, MIRType::Float32,
9230 Op(op.b0) == Op::F32ConvertI64U));
9231 case uint16_t(Op::F32DemoteF64):
9232 CHECK(EmitConversion<MToFloat32>(f, ValType::F64, ValType::F32));
9233 case uint16_t(Op::F64ConvertI32S):
9234 CHECK(EmitConversion<MToDouble>(f, ValType::I32, ValType::F64));
9235 case uint16_t(Op::F64ConvertI32U):
9236 CHECK(EmitConversion<MWasmUnsignedToDouble>(f, ValType::I32,
9237 ValType::F64));
9238 case uint16_t(Op::F64ConvertI64S):
9239 case uint16_t(Op::F64ConvertI64U):
9240 CHECK(EmitConvertI64ToFloatingPoint(f, ValType::F64, MIRType::Double,
9241 Op(op.b0) == Op::F64ConvertI64U));
9242 case uint16_t(Op::F64PromoteF32):
9243 CHECK(EmitConversion<MToDouble>(f, ValType::F32, ValType::F64));
9244
9245 // Reinterpretations
9246 case uint16_t(Op::I32ReinterpretF32):
9247 CHECK(EmitReinterpret(f, ValType::I32, ValType::F32, MIRType::Int32));
9248 case uint16_t(Op::I64ReinterpretF64):
9249 CHECK(EmitReinterpret(f, ValType::I64, ValType::F64, MIRType::Int64));
9250 case uint16_t(Op::F32ReinterpretI32):
9251 CHECK(EmitReinterpret(f, ValType::F32, ValType::I32, MIRType::Float32));
9252 case uint16_t(Op::F64ReinterpretI64):
9253 CHECK(EmitReinterpret(f, ValType::F64, ValType::I64, MIRType::Double));
9254
9255#ifdef ENABLE_WASM_GC1
9256 case uint16_t(Op::RefEq):
9257 if (!f.codeMeta().gcEnabled()) {
9258 return f.iter().unrecognizedOpcode(&op);
9259 }
9260 CHECK(EmitComparison(f, RefType::eq(), JSOp::Eq,
9261 MCompare::Compare_WasmAnyRef));
9262#endif
9263 case uint16_t(Op::RefFunc):
9264 CHECK(EmitRefFunc(f));
9265 case uint16_t(Op::RefNull):
9266 CHECK(EmitRefNull(f));
9267 case uint16_t(Op::RefIsNull):
9268 CHECK(EmitRefIsNull(f));
9269
9270 // Sign extensions
9271 case uint16_t(Op::I32Extend8S):
9272 CHECK(EmitSignExtend(f, 1, 4));
9273 case uint16_t(Op::I32Extend16S):
9274 CHECK(EmitSignExtend(f, 2, 4));
9275 case uint16_t(Op::I64Extend8S):
9276 CHECK(EmitSignExtend(f, 1, 8));
9277 case uint16_t(Op::I64Extend16S):
9278 CHECK(EmitSignExtend(f, 2, 8));
9279 case uint16_t(Op::I64Extend32S):
9280 CHECK(EmitSignExtend(f, 4, 8));
9281
9282#ifdef ENABLE_WASM_TAIL_CALLS1
9283 case uint16_t(Op::ReturnCall): {
9284 if (!f.codeMeta().tailCallsEnabled()) {
9285 return f.iter().unrecognizedOpcode(&op);
9286 }
9287 CHECK(EmitReturnCall(f));
9288 }
9289 case uint16_t(Op::ReturnCallIndirect): {
9290 if (!f.codeMeta().tailCallsEnabled()) {
9291 return f.iter().unrecognizedOpcode(&op);
9292 }
9293 CHECK(EmitReturnCallIndirect(f));
9294 }
9295#endif
9296
9297#ifdef ENABLE_WASM_GC1
9298 case uint16_t(Op::RefAsNonNull):
9299 if (!f.codeMeta().gcEnabled()) {
9300 return f.iter().unrecognizedOpcode(&op);
9301 }
9302 CHECK(EmitRefAsNonNull(f));
9303 case uint16_t(Op::BrOnNull): {
9304 if (!f.codeMeta().gcEnabled()) {
9305 return f.iter().unrecognizedOpcode(&op);
9306 }
9307 CHECK(EmitBrOnNull(f));
9308 }
9309 case uint16_t(Op::BrOnNonNull): {
9310 if (!f.codeMeta().gcEnabled()) {
9311 return f.iter().unrecognizedOpcode(&op);
9312 }
9313 CHECK(EmitBrOnNonNull(f));
9314 }
9315 case uint16_t(Op::CallRef): {
9316 if (!f.codeMeta().gcEnabled()) {
9317 return f.iter().unrecognizedOpcode(&op);
9318 }
9319 CHECK(EmitCallRef(f));
9320 }
9321#endif
9322
9323#if defined(ENABLE_WASM_TAIL_CALLS1) && defined(ENABLE_WASM_GC1)
9324 case uint16_t(Op::ReturnCallRef): {
9325 if (!f.codeMeta().gcEnabled() || !f.codeMeta().tailCallsEnabled()) {
9326 return f.iter().unrecognizedOpcode(&op);
9327 }
9328 CHECK(EmitReturnCallRef(f));
9329 }
9330#endif
9331
9332 // Gc operations
9333#ifdef ENABLE_WASM_GC1
9334 case uint16_t(Op::GcPrefix): {
9335 if (!f.codeMeta().gcEnabled()) {
9336 return f.iter().unrecognizedOpcode(&op);
9337 }
9338 switch (op.b1) {
9339 case uint32_t(GcOp::StructNew):
9340 CHECK(EmitStructNew(f));
9341 case uint32_t(GcOp::StructNewDefault):
9342 CHECK(EmitStructNewDefault(f));
9343 case uint32_t(GcOp::StructSet):
9344 CHECK(EmitStructSet(f));
9345 case uint32_t(GcOp::StructGet):
9346 CHECK(EmitStructGet(f, FieldWideningOp::None));
9347 case uint32_t(GcOp::StructGetS):
9348 CHECK(EmitStructGet(f, FieldWideningOp::Signed));
9349 case uint32_t(GcOp::StructGetU):
9350 CHECK(EmitStructGet(f, FieldWideningOp::Unsigned));
9351 case uint32_t(GcOp::ArrayNew):
9352 CHECK(EmitArrayNew(f));
9353 case uint32_t(GcOp::ArrayNewDefault):
9354 CHECK(EmitArrayNewDefault(f));
9355 case uint32_t(GcOp::ArrayNewFixed):
9356 CHECK(EmitArrayNewFixed(f));
9357 case uint32_t(GcOp::ArrayNewData):
9358 CHECK(EmitArrayNewData(f));
9359 case uint32_t(GcOp::ArrayNewElem):
9360 CHECK(EmitArrayNewElem(f));
9361 case uint32_t(GcOp::ArrayInitData):
9362 CHECK(EmitArrayInitData(f));
9363 case uint32_t(GcOp::ArrayInitElem):
9364 CHECK(EmitArrayInitElem(f));
9365 case uint32_t(GcOp::ArraySet):
9366 CHECK(EmitArraySet(f));
9367 case uint32_t(GcOp::ArrayGet):
9368 CHECK(EmitArrayGet(f, FieldWideningOp::None));
9369 case uint32_t(GcOp::ArrayGetS):
9370 CHECK(EmitArrayGet(f, FieldWideningOp::Signed));
9371 case uint32_t(GcOp::ArrayGetU):
9372 CHECK(EmitArrayGet(f, FieldWideningOp::Unsigned));
9373 case uint32_t(GcOp::ArrayLen):
9374 CHECK(EmitArrayLen(f));
9375 case uint32_t(GcOp::ArrayCopy):
9376 CHECK(EmitArrayCopy(f));
9377 case uint32_t(GcOp::ArrayFill):
9378 CHECK(EmitArrayFill(f));
9379 case uint32_t(GcOp::RefI31):
9380 CHECK(EmitRefI31(f));
9381 case uint32_t(GcOp::I31GetS):
9382 CHECK(EmitI31Get(f, FieldWideningOp::Signed));
9383 case uint32_t(GcOp::I31GetU):
9384 CHECK(EmitI31Get(f, FieldWideningOp::Unsigned));
9385 case uint32_t(GcOp::BrOnCast):
9386 CHECK(EmitBrOnCast(f, /*onSuccess=*/true));
9387 case uint32_t(GcOp::BrOnCastFail):
9388 CHECK(EmitBrOnCast(f, /*onSuccess=*/false));
9389 case uint32_t(GcOp::RefTest):
9390 CHECK(EmitRefTest(f, /*nullable=*/false));
9391 case uint32_t(GcOp::RefTestNull):
9392 CHECK(EmitRefTest(f, /*nullable=*/true));
9393 case uint32_t(GcOp::RefCast):
9394 CHECK(EmitRefCast(f, /*nullable=*/false));
9395 case uint32_t(GcOp::RefCastNull):
9396 CHECK(EmitRefCast(f, /*nullable=*/true));
9397 case uint16_t(GcOp::AnyConvertExtern):
9398 CHECK(EmitAnyConvertExtern(f));
9399 case uint16_t(GcOp::ExternConvertAny):
9400 CHECK(EmitExternConvertAny(f));
9401 default:
9402 return f.iter().unrecognizedOpcode(&op);
9403 } // switch (op.b1)
9404 break;
9405 }
9406#endif
9407
9408 // SIMD operations
9409#ifdef ENABLE_WASM_SIMD1
9410 case uint16_t(Op::SimdPrefix): {
9411 if (!f.codeMeta().simdAvailable()) {
9412 return f.iter().unrecognizedOpcode(&op);
9413 }
9414 switch (op.b1) {
9415 case uint32_t(SimdOp::V128Const):
9416 CHECK(EmitConstSimd128(f));
9417 case uint32_t(SimdOp::V128Load):
9418 CHECK(EmitLoad(f, ValType::V128, Scalar::Simd128));
9419 case uint32_t(SimdOp::V128Store):
9420 CHECK(EmitStore(f, ValType::V128, Scalar::Simd128));
9421 case uint32_t(SimdOp::V128And):
9422 case uint32_t(SimdOp::V128Or):
9423 case uint32_t(SimdOp::V128Xor):
9424 case uint32_t(SimdOp::I8x16AvgrU):
9425 case uint32_t(SimdOp::I16x8AvgrU):
9426 case uint32_t(SimdOp::I8x16Add):
9427 case uint32_t(SimdOp::I8x16AddSatS):
9428 case uint32_t(SimdOp::I8x16AddSatU):
9429 case uint32_t(SimdOp::I8x16MinS):
9430 case uint32_t(SimdOp::I8x16MinU):
9431 case uint32_t(SimdOp::I8x16MaxS):
9432 case uint32_t(SimdOp::I8x16MaxU):
9433 case uint32_t(SimdOp::I16x8Add):
9434 case uint32_t(SimdOp::I16x8AddSatS):
9435 case uint32_t(SimdOp::I16x8AddSatU):
9436 case uint32_t(SimdOp::I16x8Mul):
9437 case uint32_t(SimdOp::I16x8MinS):
9438 case uint32_t(SimdOp::I16x8MinU):
9439 case uint32_t(SimdOp::I16x8MaxS):
9440 case uint32_t(SimdOp::I16x8MaxU):
9441 case uint32_t(SimdOp::I32x4Add):
9442 case uint32_t(SimdOp::I32x4Mul):
9443 case uint32_t(SimdOp::I32x4MinS):
9444 case uint32_t(SimdOp::I32x4MinU):
9445 case uint32_t(SimdOp::I32x4MaxS):
9446 case uint32_t(SimdOp::I32x4MaxU):
9447 case uint32_t(SimdOp::I64x2Add):
9448 case uint32_t(SimdOp::I64x2Mul):
9449 case uint32_t(SimdOp::F32x4Add):
9450 case uint32_t(SimdOp::F32x4Mul):
9451 case uint32_t(SimdOp::F32x4Min):
9452 case uint32_t(SimdOp::F32x4Max):
9453 case uint32_t(SimdOp::F64x2Add):
9454 case uint32_t(SimdOp::F64x2Mul):
9455 case uint32_t(SimdOp::F64x2Min):
9456 case uint32_t(SimdOp::F64x2Max):
9457 case uint32_t(SimdOp::I8x16Eq):
9458 case uint32_t(SimdOp::I8x16Ne):
9459 case uint32_t(SimdOp::I16x8Eq):
9460 case uint32_t(SimdOp::I16x8Ne):
9461 case uint32_t(SimdOp::I32x4Eq):
9462 case uint32_t(SimdOp::I32x4Ne):
9463 case uint32_t(SimdOp::I64x2Eq):
9464 case uint32_t(SimdOp::I64x2Ne):
9465 case uint32_t(SimdOp::F32x4Eq):
9466 case uint32_t(SimdOp::F32x4Ne):
9467 case uint32_t(SimdOp::F64x2Eq):
9468 case uint32_t(SimdOp::F64x2Ne):
9469 case uint32_t(SimdOp::I32x4DotI16x8S):
9470 case uint32_t(SimdOp::I16x8ExtmulLowI8x16S):
9471 case uint32_t(SimdOp::I16x8ExtmulHighI8x16S):
9472 case uint32_t(SimdOp::I16x8ExtmulLowI8x16U):
9473 case uint32_t(SimdOp::I16x8ExtmulHighI8x16U):
9474 case uint32_t(SimdOp::I32x4ExtmulLowI16x8S):
9475 case uint32_t(SimdOp::I32x4ExtmulHighI16x8S):
9476 case uint32_t(SimdOp::I32x4ExtmulLowI16x8U):
9477 case uint32_t(SimdOp::I32x4ExtmulHighI16x8U):
9478 case uint32_t(SimdOp::I64x2ExtmulLowI32x4S):
9479 case uint32_t(SimdOp::I64x2ExtmulHighI32x4S):
9480 case uint32_t(SimdOp::I64x2ExtmulLowI32x4U):
9481 case uint32_t(SimdOp::I64x2ExtmulHighI32x4U):
9482 case uint32_t(SimdOp::I16x8Q15MulrSatS):
9483 CHECK(EmitBinarySimd128(f, /* commutative= */ true, SimdOp(op.b1)));
9484 case uint32_t(SimdOp::V128AndNot):
9485 case uint32_t(SimdOp::I8x16Sub):
9486 case uint32_t(SimdOp::I8x16SubSatS):
9487 case uint32_t(SimdOp::I8x16SubSatU):
9488 case uint32_t(SimdOp::I16x8Sub):
9489 case uint32_t(SimdOp::I16x8SubSatS):
9490 case uint32_t(SimdOp::I16x8SubSatU):
9491 case uint32_t(SimdOp::I32x4Sub):
9492 case uint32_t(SimdOp::I64x2Sub):
9493 case uint32_t(SimdOp::F32x4Sub):
9494 case uint32_t(SimdOp::F32x4Div):
9495 case uint32_t(SimdOp::F64x2Sub):
9496 case uint32_t(SimdOp::F64x2Div):
9497 case uint32_t(SimdOp::I8x16NarrowI16x8S):
9498 case uint32_t(SimdOp::I8x16NarrowI16x8U):
9499 case uint32_t(SimdOp::I16x8NarrowI32x4S):
9500 case uint32_t(SimdOp::I16x8NarrowI32x4U):
9501 case uint32_t(SimdOp::I8x16LtS):
9502 case uint32_t(SimdOp::I8x16LtU):
9503 case uint32_t(SimdOp::I8x16GtS):
9504 case uint32_t(SimdOp::I8x16GtU):
9505 case uint32_t(SimdOp::I8x16LeS):
9506 case uint32_t(SimdOp::I8x16LeU):
9507 case uint32_t(SimdOp::I8x16GeS):
9508 case uint32_t(SimdOp::I8x16GeU):
9509 case uint32_t(SimdOp::I16x8LtS):
9510 case uint32_t(SimdOp::I16x8LtU):
9511 case uint32_t(SimdOp::I16x8GtS):
9512 case uint32_t(SimdOp::I16x8GtU):
9513 case uint32_t(SimdOp::I16x8LeS):
9514 case uint32_t(SimdOp::I16x8LeU):
9515 case uint32_t(SimdOp::I16x8GeS):
9516 case uint32_t(SimdOp::I16x8GeU):
9517 case uint32_t(SimdOp::I32x4LtS):
9518 case uint32_t(SimdOp::I32x4LtU):
9519 case uint32_t(SimdOp::I32x4GtS):
9520 case uint32_t(SimdOp::I32x4GtU):
9521 case uint32_t(SimdOp::I32x4LeS):
9522 case uint32_t(SimdOp::I32x4LeU):
9523 case uint32_t(SimdOp::I32x4GeS):
9524 case uint32_t(SimdOp::I32x4GeU):
9525 case uint32_t(SimdOp::I64x2LtS):
9526 case uint32_t(SimdOp::I64x2GtS):
9527 case uint32_t(SimdOp::I64x2LeS):
9528 case uint32_t(SimdOp::I64x2GeS):
9529 case uint32_t(SimdOp::F32x4Lt):
9530 case uint32_t(SimdOp::F32x4Gt):
9531 case uint32_t(SimdOp::F32x4Le):
9532 case uint32_t(SimdOp::F32x4Ge):
9533 case uint32_t(SimdOp::F64x2Lt):
9534 case uint32_t(SimdOp::F64x2Gt):
9535 case uint32_t(SimdOp::F64x2Le):
9536 case uint32_t(SimdOp::F64x2Ge):
9537 case uint32_t(SimdOp::I8x16Swizzle):
9538 case uint32_t(SimdOp::F32x4PMax):
9539 case uint32_t(SimdOp::F32x4PMin):
9540 case uint32_t(SimdOp::F64x2PMax):
9541 case uint32_t(SimdOp::F64x2PMin):
9542 CHECK(
9543 EmitBinarySimd128(f, /* commutative= */ false, SimdOp(op.b1)));
9544 case uint32_t(SimdOp::I8x16Splat):
9545 case uint32_t(SimdOp::I16x8Splat):
9546 case uint32_t(SimdOp::I32x4Splat):
9547 CHECK(EmitSplatSimd128(f, ValType::I32, SimdOp(op.b1)));
9548 case uint32_t(SimdOp::I64x2Splat):
9549 CHECK(EmitSplatSimd128(f, ValType::I64, SimdOp(op.b1)));
9550 case uint32_t(SimdOp::F32x4Splat):
9551 CHECK(EmitSplatSimd128(f, ValType::F32, SimdOp(op.b1)));
9552 case uint32_t(SimdOp::F64x2Splat):
9553 CHECK(EmitSplatSimd128(f, ValType::F64, SimdOp(op.b1)));
9554 case uint32_t(SimdOp::I8x16Neg):
9555 case uint32_t(SimdOp::I16x8Neg):
9556 case uint32_t(SimdOp::I16x8ExtendLowI8x16S):
9557 case uint32_t(SimdOp::I16x8ExtendHighI8x16S):
9558 case uint32_t(SimdOp::I16x8ExtendLowI8x16U):
9559 case uint32_t(SimdOp::I16x8ExtendHighI8x16U):
9560 case uint32_t(SimdOp::I32x4Neg):
9561 case uint32_t(SimdOp::I32x4ExtendLowI16x8S):
9562 case uint32_t(SimdOp::I32x4ExtendHighI16x8S):
9563 case uint32_t(SimdOp::I32x4ExtendLowI16x8U):
9564 case uint32_t(SimdOp::I32x4ExtendHighI16x8U):
9565 case uint32_t(SimdOp::I32x4TruncSatF32x4S):
9566 case uint32_t(SimdOp::I32x4TruncSatF32x4U):
9567 case uint32_t(SimdOp::I64x2Neg):
9568 case uint32_t(SimdOp::I64x2ExtendLowI32x4S):
9569 case uint32_t(SimdOp::I64x2ExtendHighI32x4S):
9570 case uint32_t(SimdOp::I64x2ExtendLowI32x4U):
9571 case uint32_t(SimdOp::I64x2ExtendHighI32x4U):
9572 case uint32_t(SimdOp::F32x4Abs):
9573 case uint32_t(SimdOp::F32x4Neg):
9574 case uint32_t(SimdOp::F32x4Sqrt):
9575 case uint32_t(SimdOp::F32x4ConvertI32x4S):
9576 case uint32_t(SimdOp::F32x4ConvertI32x4U):
9577 case uint32_t(SimdOp::F64x2Abs):
9578 case uint32_t(SimdOp::F64x2Neg):
9579 case uint32_t(SimdOp::F64x2Sqrt):
9580 case uint32_t(SimdOp::V128Not):
9581 case uint32_t(SimdOp::I8x16Popcnt):
9582 case uint32_t(SimdOp::I8x16Abs):
9583 case uint32_t(SimdOp::I16x8Abs):
9584 case uint32_t(SimdOp::I32x4Abs):
9585 case uint32_t(SimdOp::I64x2Abs):
9586 case uint32_t(SimdOp::F32x4Ceil):
9587 case uint32_t(SimdOp::F32x4Floor):
9588 case uint32_t(SimdOp::F32x4Trunc):
9589 case uint32_t(SimdOp::F32x4Nearest):
9590 case uint32_t(SimdOp::F64x2Ceil):
9591 case uint32_t(SimdOp::F64x2Floor):
9592 case uint32_t(SimdOp::F64x2Trunc):
9593 case uint32_t(SimdOp::F64x2Nearest):
9594 case uint32_t(SimdOp::F32x4DemoteF64x2Zero):
9595 case uint32_t(SimdOp::F64x2PromoteLowF32x4):
9596 case uint32_t(SimdOp::F64x2ConvertLowI32x4S):
9597 case uint32_t(SimdOp::F64x2ConvertLowI32x4U):
9598 case uint32_t(SimdOp::I32x4TruncSatF64x2SZero):
9599 case uint32_t(SimdOp::I32x4TruncSatF64x2UZero):
9600 case uint32_t(SimdOp::I16x8ExtaddPairwiseI8x16S):
9601 case uint32_t(SimdOp::I16x8ExtaddPairwiseI8x16U):
9602 case uint32_t(SimdOp::I32x4ExtaddPairwiseI16x8S):
9603 case uint32_t(SimdOp::I32x4ExtaddPairwiseI16x8U):
9604 CHECK(EmitUnarySimd128(f, SimdOp(op.b1)));
9605 case uint32_t(SimdOp::V128AnyTrue):
9606 case uint32_t(SimdOp::I8x16AllTrue):
9607 case uint32_t(SimdOp::I16x8AllTrue):
9608 case uint32_t(SimdOp::I32x4AllTrue):
9609 case uint32_t(SimdOp::I64x2AllTrue):
9610 case uint32_t(SimdOp::I8x16Bitmask):
9611 case uint32_t(SimdOp::I16x8Bitmask):
9612 case uint32_t(SimdOp::I32x4Bitmask):
9613 case uint32_t(SimdOp::I64x2Bitmask):
9614 CHECK(EmitReduceSimd128(f, SimdOp(op.b1)));
9615 case uint32_t(SimdOp::I8x16Shl):
9616 case uint32_t(SimdOp::I8x16ShrS):
9617 case uint32_t(SimdOp::I8x16ShrU):
9618 case uint32_t(SimdOp::I16x8Shl):
9619 case uint32_t(SimdOp::I16x8ShrS):
9620 case uint32_t(SimdOp::I16x8ShrU):
9621 case uint32_t(SimdOp::I32x4Shl):
9622 case uint32_t(SimdOp::I32x4ShrS):
9623 case uint32_t(SimdOp::I32x4ShrU):
9624 case uint32_t(SimdOp::I64x2Shl):
9625 case uint32_t(SimdOp::I64x2ShrS):
9626 case uint32_t(SimdOp::I64x2ShrU):
9627 CHECK(EmitShiftSimd128(f, SimdOp(op.b1)));
9628 case uint32_t(SimdOp::I8x16ExtractLaneS):
9629 case uint32_t(SimdOp::I8x16ExtractLaneU):
9630 CHECK(EmitExtractLaneSimd128(f, ValType::I32, 16, SimdOp(op.b1)));
9631 case uint32_t(SimdOp::I16x8ExtractLaneS):
9632 case uint32_t(SimdOp::I16x8ExtractLaneU):
9633 CHECK(EmitExtractLaneSimd128(f, ValType::I32, 8, SimdOp(op.b1)));
9634 case uint32_t(SimdOp::I32x4ExtractLane):
9635 CHECK(EmitExtractLaneSimd128(f, ValType::I32, 4, SimdOp(op.b1)));
9636 case uint32_t(SimdOp::I64x2ExtractLane):
9637 CHECK(EmitExtractLaneSimd128(f, ValType::I64, 2, SimdOp(op.b1)));
9638 case uint32_t(SimdOp::F32x4ExtractLane):
9639 CHECK(EmitExtractLaneSimd128(f, ValType::F32, 4, SimdOp(op.b1)));
9640 case uint32_t(SimdOp::F64x2ExtractLane):
9641 CHECK(EmitExtractLaneSimd128(f, ValType::F64, 2, SimdOp(op.b1)));
9642 case uint32_t(SimdOp::I8x16ReplaceLane):
9643 CHECK(EmitReplaceLaneSimd128(f, ValType::I32, 16, SimdOp(op.b1)));
9644 case uint32_t(SimdOp::I16x8ReplaceLane):
9645 CHECK(EmitReplaceLaneSimd128(f, ValType::I32, 8, SimdOp(op.b1)));
9646 case uint32_t(SimdOp::I32x4ReplaceLane):
9647 CHECK(EmitReplaceLaneSimd128(f, ValType::I32, 4, SimdOp(op.b1)));
9648 case uint32_t(SimdOp::I64x2ReplaceLane):
9649 CHECK(EmitReplaceLaneSimd128(f, ValType::I64, 2, SimdOp(op.b1)));
9650 case uint32_t(SimdOp::F32x4ReplaceLane):
9651 CHECK(EmitReplaceLaneSimd128(f, ValType::F32, 4, SimdOp(op.b1)));
9652 case uint32_t(SimdOp::F64x2ReplaceLane):
9653 CHECK(EmitReplaceLaneSimd128(f, ValType::F64, 2, SimdOp(op.b1)));
9654 case uint32_t(SimdOp::V128Bitselect):
9655 CHECK(EmitTernarySimd128(f, SimdOp(op.b1)));
9656 case uint32_t(SimdOp::I8x16Shuffle):
9657 CHECK(EmitShuffleSimd128(f));
9658 case uint32_t(SimdOp::V128Load8Splat):
9659 CHECK(EmitLoadSplatSimd128(f, Scalar::Uint8, SimdOp::I8x16Splat));
9660 case uint32_t(SimdOp::V128Load16Splat):
9661 CHECK(EmitLoadSplatSimd128(f, Scalar::Uint16, SimdOp::I16x8Splat));
9662 case uint32_t(SimdOp::V128Load32Splat):
9663 CHECK(EmitLoadSplatSimd128(f, Scalar::Float32, SimdOp::I32x4Splat));
9664 case uint32_t(SimdOp::V128Load64Splat):
9665 CHECK(EmitLoadSplatSimd128(f, Scalar::Float64, SimdOp::I64x2Splat));
9666 case uint32_t(SimdOp::V128Load8x8S):
9667 case uint32_t(SimdOp::V128Load8x8U):
9668 case uint32_t(SimdOp::V128Load16x4S):
9669 case uint32_t(SimdOp::V128Load16x4U):
9670 case uint32_t(SimdOp::V128Load32x2S):
9671 case uint32_t(SimdOp::V128Load32x2U):
9672 CHECK(EmitLoadExtendSimd128(f, SimdOp(op.b1)));
9673 case uint32_t(SimdOp::V128Load32Zero):
9674 CHECK(EmitLoadZeroSimd128(f, Scalar::Float32, 4));
9675 case uint32_t(SimdOp::V128Load64Zero):
9676 CHECK(EmitLoadZeroSimd128(f, Scalar::Float64, 8));
9677 case uint32_t(SimdOp::V128Load8Lane):
9678 CHECK(EmitLoadLaneSimd128(f, 1));
9679 case uint32_t(SimdOp::V128Load16Lane):
9680 CHECK(EmitLoadLaneSimd128(f, 2));
9681 case uint32_t(SimdOp::V128Load32Lane):
9682 CHECK(EmitLoadLaneSimd128(f, 4));
9683 case uint32_t(SimdOp::V128Load64Lane):
9684 CHECK(EmitLoadLaneSimd128(f, 8));
9685 case uint32_t(SimdOp::V128Store8Lane):
9686 CHECK(EmitStoreLaneSimd128(f, 1));
9687 case uint32_t(SimdOp::V128Store16Lane):
9688 CHECK(EmitStoreLaneSimd128(f, 2));
9689 case uint32_t(SimdOp::V128Store32Lane):
9690 CHECK(EmitStoreLaneSimd128(f, 4));
9691 case uint32_t(SimdOp::V128Store64Lane):
9692 CHECK(EmitStoreLaneSimd128(f, 8));
9693# ifdef ENABLE_WASM_RELAXED_SIMD1
9694 case uint32_t(SimdOp::F32x4RelaxedMadd):
9695 case uint32_t(SimdOp::F32x4RelaxedNmadd):
9696 case uint32_t(SimdOp::F64x2RelaxedMadd):
9697 case uint32_t(SimdOp::F64x2RelaxedNmadd):
9698 case uint32_t(SimdOp::I8x16RelaxedLaneSelect):
9699 case uint32_t(SimdOp::I16x8RelaxedLaneSelect):
9700 case uint32_t(SimdOp::I32x4RelaxedLaneSelect):
9701 case uint32_t(SimdOp::I64x2RelaxedLaneSelect):
9702 case uint32_t(SimdOp::I32x4DotI8x16I7x16AddS): {
9703 if (!f.codeMeta().v128RelaxedEnabled()) {
9704 return f.iter().unrecognizedOpcode(&op);
9705 }
9706 CHECK(EmitTernarySimd128(f, SimdOp(op.b1)));
9707 }
9708 case uint32_t(SimdOp::F32x4RelaxedMin):
9709 case uint32_t(SimdOp::F32x4RelaxedMax):
9710 case uint32_t(SimdOp::F64x2RelaxedMin):
9711 case uint32_t(SimdOp::F64x2RelaxedMax):
9712 case uint32_t(SimdOp::I16x8RelaxedQ15MulrS): {
9713 if (!f.codeMeta().v128RelaxedEnabled()) {
9714 return f.iter().unrecognizedOpcode(&op);
9715 }
9716 CHECK(EmitBinarySimd128(f, /* commutative= */ true, SimdOp(op.b1)));
9717 }
9718 case uint32_t(SimdOp::I32x4RelaxedTruncF32x4S):
9719 case uint32_t(SimdOp::I32x4RelaxedTruncF32x4U):
9720 case uint32_t(SimdOp::I32x4RelaxedTruncF64x2SZero):
9721 case uint32_t(SimdOp::I32x4RelaxedTruncF64x2UZero): {
9722 if (!f.codeMeta().v128RelaxedEnabled()) {
9723 return f.iter().unrecognizedOpcode(&op);
9724 }
9725 CHECK(EmitUnarySimd128(f, SimdOp(op.b1)));
9726 }
9727 case uint32_t(SimdOp::I8x16RelaxedSwizzle):
9728 case uint32_t(SimdOp::I16x8DotI8x16I7x16S): {
9729 if (!f.codeMeta().v128RelaxedEnabled()) {
9730 return f.iter().unrecognizedOpcode(&op);
9731 }
9732 CHECK(
9733 EmitBinarySimd128(f, /* commutative= */ false, SimdOp(op.b1)));
9734 }
9735# endif
9736
9737 default:
9738 return f.iter().unrecognizedOpcode(&op);
9739 } // switch (op.b1)
9740 break;
9741 }
9742#endif
9743
9744 // Miscellaneous operations
9745 case uint16_t(Op::MiscPrefix): {
9746 switch (op.b1) {
9747 case uint32_t(MiscOp::I32TruncSatF32S):
9748 case uint32_t(MiscOp::I32TruncSatF32U):
9749 CHECK(EmitTruncate(f, ValType::F32, ValType::I32,
9750 MiscOp(op.b1) == MiscOp::I32TruncSatF32U, true));
9751 case uint32_t(MiscOp::I32TruncSatF64S):
9752 case uint32_t(MiscOp::I32TruncSatF64U):
9753 CHECK(EmitTruncate(f, ValType::F64, ValType::I32,
9754 MiscOp(op.b1) == MiscOp::I32TruncSatF64U, true));
9755 case uint32_t(MiscOp::I64TruncSatF32S):
9756 case uint32_t(MiscOp::I64TruncSatF32U):
9757 CHECK(EmitTruncate(f, ValType::F32, ValType::I64,
9758 MiscOp(op.b1) == MiscOp::I64TruncSatF32U, true));
9759 case uint32_t(MiscOp::I64TruncSatF64S):
9760 case uint32_t(MiscOp::I64TruncSatF64U):
9761 CHECK(EmitTruncate(f, ValType::F64, ValType::I64,
9762 MiscOp(op.b1) == MiscOp::I64TruncSatF64U, true));
9763 case uint32_t(MiscOp::MemoryCopy):
9764 CHECK(EmitMemCopy(f));
9765 case uint32_t(MiscOp::DataDrop):
9766 CHECK(EmitDataOrElemDrop(f, /*isData=*/true));
9767 case uint32_t(MiscOp::MemoryFill):
9768 CHECK(EmitMemFill(f));
9769 case uint32_t(MiscOp::MemoryInit):
9770 CHECK(EmitMemInit(f));
9771 case uint32_t(MiscOp::TableCopy):
9772 CHECK(EmitTableCopy(f));
9773 case uint32_t(MiscOp::ElemDrop):
9774 CHECK(EmitDataOrElemDrop(f, /*isData=*/false));
9775 case uint32_t(MiscOp::TableInit):
9776 CHECK(EmitTableInit(f));
9777 case uint32_t(MiscOp::TableFill):
9778 CHECK(EmitTableFill(f));
9779#if ENABLE_WASM_MEMORY_CONTROL1
9780 case uint32_t(MiscOp::MemoryDiscard): {
9781 if (!f.codeMeta().memoryControlEnabled()) {
9782 return f.iter().unrecognizedOpcode(&op);
9783 }
9784 CHECK(EmitMemDiscard(f));
9785 }
9786#endif
9787 case uint32_t(MiscOp::TableGrow):
9788 CHECK(EmitTableGrow(f));
9789 case uint32_t(MiscOp::TableSize):
9790 CHECK(EmitTableSize(f));
9791 default:
9792 return f.iter().unrecognizedOpcode(&op);
9793 }
9794 break;
9795 }
9796
9797 // Thread operations
9798 case uint16_t(Op::ThreadPrefix): {
9799 // Though thread ops can be used on nonshared memories, we make them
9800 // unavailable if shared memory has been disabled in the prefs, for
9801 // maximum predictability and safety and consistency with JS.
9802 if (f.codeMeta().sharedMemoryEnabled() == Shareable::False) {
9803 return f.iter().unrecognizedOpcode(&op);
9804 }
9805 switch (op.b1) {
9806 case uint32_t(ThreadOp::Wake):
9807 CHECK(EmitWake(f));
9808
9809 case uint32_t(ThreadOp::I32Wait):
9810 CHECK(EmitWait(f, ValType::I32, 4));
9811 case uint32_t(ThreadOp::I64Wait):
9812 CHECK(EmitWait(f, ValType::I64, 8));
9813 case uint32_t(ThreadOp::Fence):
9814 CHECK(EmitFence(f));
9815
9816 case uint32_t(ThreadOp::I32AtomicLoad):
9817 CHECK(EmitAtomicLoad(f, ValType::I32, Scalar::Int32));
9818 case uint32_t(ThreadOp::I64AtomicLoad):
9819 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Int64));
9820 case uint32_t(ThreadOp::I32AtomicLoad8U):
9821 CHECK(EmitAtomicLoad(f, ValType::I32, Scalar::Uint8));
9822 case uint32_t(ThreadOp::I32AtomicLoad16U):
9823 CHECK(EmitAtomicLoad(f, ValType::I32, Scalar::Uint16));
9824 case uint32_t(ThreadOp::I64AtomicLoad8U):
9825 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Uint8));
9826 case uint32_t(ThreadOp::I64AtomicLoad16U):
9827 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Uint16));
9828 case uint32_t(ThreadOp::I64AtomicLoad32U):
9829 CHECK(EmitAtomicLoad(f, ValType::I64, Scalar::Uint32));
9830
9831 case uint32_t(ThreadOp::I32AtomicStore):
9832 CHECK(EmitAtomicStore(f, ValType::I32, Scalar::Int32));
9833 case uint32_t(ThreadOp::I64AtomicStore):
9834 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Int64));
9835 case uint32_t(ThreadOp::I32AtomicStore8U):
9836 CHECK(EmitAtomicStore(f, ValType::I32, Scalar::Uint8));
9837 case uint32_t(ThreadOp::I32AtomicStore16U):
9838 CHECK(EmitAtomicStore(f, ValType::I32, Scalar::Uint16));
9839 case uint32_t(ThreadOp::I64AtomicStore8U):
9840 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint8));
9841 case uint32_t(ThreadOp::I64AtomicStore16U):
9842 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint16));
9843 case uint32_t(ThreadOp::I64AtomicStore32U):
9844 CHECK(EmitAtomicStore(f, ValType::I64, Scalar::Uint32));
9845
9846 case uint32_t(ThreadOp::I32AtomicAdd):
9847 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Add));
9848 case uint32_t(ThreadOp::I64AtomicAdd):
9849 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Add));
9850 case uint32_t(ThreadOp::I32AtomicAdd8U):
9851 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Add));
9852 case uint32_t(ThreadOp::I32AtomicAdd16U):
9853 CHECK(
9854 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Add));
9855 case uint32_t(ThreadOp::I64AtomicAdd8U):
9856 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Add));
9857 case uint32_t(ThreadOp::I64AtomicAdd16U):
9858 CHECK(
9859 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Add));
9860 case uint32_t(ThreadOp::I64AtomicAdd32U):
9861 CHECK(
9862 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Add));
9863
9864 case uint32_t(ThreadOp::I32AtomicSub):
9865 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Sub));
9866 case uint32_t(ThreadOp::I64AtomicSub):
9867 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Sub));
9868 case uint32_t(ThreadOp::I32AtomicSub8U):
9869 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Sub));
9870 case uint32_t(ThreadOp::I32AtomicSub16U):
9871 CHECK(
9872 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Sub));
9873 case uint32_t(ThreadOp::I64AtomicSub8U):
9874 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Sub));
9875 case uint32_t(ThreadOp::I64AtomicSub16U):
9876 CHECK(
9877 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Sub));
9878 case uint32_t(ThreadOp::I64AtomicSub32U):
9879 CHECK(
9880 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Sub));
9881
9882 case uint32_t(ThreadOp::I32AtomicAnd):
9883 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::And));
9884 case uint32_t(ThreadOp::I64AtomicAnd):
9885 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::And));
9886 case uint32_t(ThreadOp::I32AtomicAnd8U):
9887 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::And));
9888 case uint32_t(ThreadOp::I32AtomicAnd16U):
9889 CHECK(
9890 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::And));
9891 case uint32_t(ThreadOp::I64AtomicAnd8U):
9892 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::And));
9893 case uint32_t(ThreadOp::I64AtomicAnd16U):
9894 CHECK(
9895 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::And));
9896 case uint32_t(ThreadOp::I64AtomicAnd32U):
9897 CHECK(
9898 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::And));
9899
9900 case uint32_t(ThreadOp::I32AtomicOr):
9901 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Or));
9902 case uint32_t(ThreadOp::I64AtomicOr):
9903 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Or));
9904 case uint32_t(ThreadOp::I32AtomicOr8U):
9905 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Or));
9906 case uint32_t(ThreadOp::I32AtomicOr16U):
9907 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Or));
9908 case uint32_t(ThreadOp::I64AtomicOr8U):
9909 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Or));
9910 case uint32_t(ThreadOp::I64AtomicOr16U):
9911 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Or));
9912 case uint32_t(ThreadOp::I64AtomicOr32U):
9913 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Or));
9914
9915 case uint32_t(ThreadOp::I32AtomicXor):
9916 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Int32, AtomicOp::Xor));
9917 case uint32_t(ThreadOp::I64AtomicXor):
9918 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Int64, AtomicOp::Xor));
9919 case uint32_t(ThreadOp::I32AtomicXor8U):
9920 CHECK(EmitAtomicRMW(f, ValType::I32, Scalar::Uint8, AtomicOp::Xor));
9921 case uint32_t(ThreadOp::I32AtomicXor16U):
9922 CHECK(
9923 EmitAtomicRMW(f, ValType::I32, Scalar::Uint16, AtomicOp::Xor));
9924 case uint32_t(ThreadOp::I64AtomicXor8U):
9925 CHECK(EmitAtomicRMW(f, ValType::I64, Scalar::Uint8, AtomicOp::Xor));
9926 case uint32_t(ThreadOp::I64AtomicXor16U):
9927 CHECK(
9928 EmitAtomicRMW(f, ValType::I64, Scalar::Uint16, AtomicOp::Xor));
9929 case uint32_t(ThreadOp::I64AtomicXor32U):
9930 CHECK(
9931 EmitAtomicRMW(f, ValType::I64, Scalar::Uint32, AtomicOp::Xor));
9932
9933 case uint32_t(ThreadOp::I32AtomicXchg):
9934 CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Int32));
9935 case uint32_t(ThreadOp::I64AtomicXchg):
9936 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Int64));
9937 case uint32_t(ThreadOp::I32AtomicXchg8U):
9938 CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Uint8));
9939 case uint32_t(ThreadOp::I32AtomicXchg16U):
9940 CHECK(EmitAtomicXchg(f, ValType::I32, Scalar::Uint16));
9941 case uint32_t(ThreadOp::I64AtomicXchg8U):
9942 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Uint8));
9943 case uint32_t(ThreadOp::I64AtomicXchg16U):
9944 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Uint16));
9945 case uint32_t(ThreadOp::I64AtomicXchg32U):
9946 CHECK(EmitAtomicXchg(f, ValType::I64, Scalar::Uint32));
9947
9948 case uint32_t(ThreadOp::I32AtomicCmpXchg):
9949 CHECK(EmitAtomicCmpXchg(f, ValType::I32, Scalar::Int32));
9950 case uint32_t(ThreadOp::I64AtomicCmpXchg):
9951 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Int64));
9952 case uint32_t(ThreadOp::I32AtomicCmpXchg8U):
9953 CHECK(EmitAtomicCmpXchg(f, ValType::I32, Scalar::Uint8));
9954 case uint32_t(ThreadOp::I32AtomicCmpXchg16U):
9955 CHECK(EmitAtomicCmpXchg(f, ValType::I32, Scalar::Uint16));
9956 case uint32_t(ThreadOp::I64AtomicCmpXchg8U):
9957 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Uint8));
9958 case uint32_t(ThreadOp::I64AtomicCmpXchg16U):
9959 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Uint16));
9960 case uint32_t(ThreadOp::I64AtomicCmpXchg32U):
9961 CHECK(EmitAtomicCmpXchg(f, ValType::I64, Scalar::Uint32));
9962
9963 default:
9964 return f.iter().unrecognizedOpcode(&op);
9965 }
9966 break;
9967 }
9968
9969 // asm.js-specific operators
9970 case uint16_t(Op::MozPrefix): {
9971 if (op.b1 == uint32_t(MozOp::CallBuiltinModuleFunc)) {
9972 if (!f.codeMeta().isBuiltinModule()) {
9973 return f.iter().unrecognizedOpcode(&op);
9974 }
9975 CHECK(EmitCallBuiltinModuleFunc(f));
9976 }
9977#ifdef ENABLE_WASM_JSPI1
9978 if (op.b1 == uint32_t(MozOp::StackSwitch)) {
9979 if (!f.codeMeta().isBuiltinModule() ||
9980 !f.codeMeta().jsPromiseIntegrationEnabled()) {
9981 return f.iter().unrecognizedOpcode(&op);
9982 }
9983 CHECK(EmitStackSwitch(f));
9984 }
9985#endif
9986
9987 if (!f.codeMeta().isAsmJS()) {
9988 return f.iter().unrecognizedOpcode(&op);
9989 }
9990 switch (op.b1) {
9991 case uint32_t(MozOp::TeeGlobal):
9992 CHECK(EmitTeeGlobal(f));
9993 case uint32_t(MozOp::I32Min):
9994 case uint32_t(MozOp::I32Max):
9995 CHECK(EmitMinMax(f, ValType::I32, MIRType::Int32,
9996 MozOp(op.b1) == MozOp::I32Max));
9997 case uint32_t(MozOp::I32Neg):
9998 CHECK(EmitUnaryWithType<MWasmNeg>(f, ValType::I32, MIRType::Int32));
9999 case uint32_t(MozOp::I32BitNot):
10000 CHECK(EmitBitNot(f, ValType::I32));
10001 case uint32_t(MozOp::I32Abs):
10002 CHECK(EmitUnaryWithType<MAbs>(f, ValType::I32, MIRType::Int32));
10003 case uint32_t(MozOp::F32TeeStoreF64):
10004 CHECK(EmitTeeStoreWithCoercion(f, ValType::F32, Scalar::Float64));
10005 case uint32_t(MozOp::F64TeeStoreF32):
10006 CHECK(EmitTeeStoreWithCoercion(f, ValType::F64, Scalar::Float32));
10007 case uint32_t(MozOp::I32TeeStore8):
10008 CHECK(EmitTeeStore(f, ValType::I32, Scalar::Int8));
10009 case uint32_t(MozOp::I32TeeStore16):
10010 CHECK(EmitTeeStore(f, ValType::I32, Scalar::Int16));
10011 case uint32_t(MozOp::I64TeeStore8):
10012 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int8));
10013 case uint32_t(MozOp::I64TeeStore16):
10014 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int16));
10015 case uint32_t(MozOp::I64TeeStore32):
10016 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int32));
10017 case uint32_t(MozOp::I32TeeStore):
10018 CHECK(EmitTeeStore(f, ValType::I32, Scalar::Int32));
10019 case uint32_t(MozOp::I64TeeStore):
10020 CHECK(EmitTeeStore(f, ValType::I64, Scalar::Int64));
10021 case uint32_t(MozOp::F32TeeStore):
10022 CHECK(EmitTeeStore(f, ValType::F32, Scalar::Float32));
10023 case uint32_t(MozOp::F64TeeStore):
10024 CHECK(EmitTeeStore(f, ValType::F64, Scalar::Float64));
10025 case uint32_t(MozOp::F64Mod):
10026 CHECK(EmitRem(f, ValType::F64, MIRType::Double,
10027 /* isUnsigned = */ false));
10028 case uint32_t(MozOp::F64SinNative):
10029 CHECK(EmitUnaryMathBuiltinCall(f, SASigSinNativeD));
10030 case uint32_t(MozOp::F64SinFdlibm):
10031 CHECK(EmitUnaryMathBuiltinCall(f, SASigSinFdlibmD));
10032 case uint32_t(MozOp::F64CosNative):
10033 CHECK(EmitUnaryMathBuiltinCall(f, SASigCosNativeD));
10034 case uint32_t(MozOp::F64CosFdlibm):
10035 CHECK(EmitUnaryMathBuiltinCall(f, SASigCosFdlibmD));
10036 case uint32_t(MozOp::F64TanNative):
10037 CHECK(EmitUnaryMathBuiltinCall(f, SASigTanNativeD));
10038 case uint32_t(MozOp::F64TanFdlibm):
10039 CHECK(EmitUnaryMathBuiltinCall(f, SASigTanFdlibmD));
10040 case uint32_t(MozOp::F64Asin):
10041 CHECK(EmitUnaryMathBuiltinCall(f, SASigASinD));
10042 case uint32_t(MozOp::F64Acos):
10043 CHECK(EmitUnaryMathBuiltinCall(f, SASigACosD));
10044 case uint32_t(MozOp::F64Atan):
10045 CHECK(EmitUnaryMathBuiltinCall(f, SASigATanD));
10046 case uint32_t(MozOp::F64Exp):
10047 CHECK(EmitUnaryMathBuiltinCall(f, SASigExpD));
10048 case uint32_t(MozOp::F64Log):
10049 CHECK(EmitUnaryMathBuiltinCall(f, SASigLogD));
10050 case uint32_t(MozOp::F64Pow):
10051 CHECK(EmitBinaryMathBuiltinCall(f, SASigPowD));
10052 case uint32_t(MozOp::F64Atan2):
10053 CHECK(EmitBinaryMathBuiltinCall(f, SASigATan2D));
10054 case uint32_t(MozOp::OldCallDirect):
10055 CHECK(EmitCall(f, /* asmJSFuncDef = */ true));
10056 case uint32_t(MozOp::OldCallIndirect):
10057 CHECK(EmitCallIndirect(f, /* oldStyle = */ true));
10058
10059 default:
10060 return f.iter().unrecognizedOpcode(&op);
10061 }
10062 break;
10063 }
10064
10065 default:
10066 return f.iter().unrecognizedOpcode(&op);
10067 }
10068 }
10069
10070 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 10070); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { *((volatile int*)__null) = 10070; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
10071
10072#undef CHECK
10073}
10074
10075static bool IonBuildMIR(Decoder& d, const CompilerEnvironment& compilerEnv,
10076 const CodeMetadata& codeMeta,
10077 const FuncCompileInput& func,
10078 const ValTypeVector& locals, MIRGenerator& mir,
10079 TryNoteVector& tryNotes,
10080 UniqueCompileInfoVector& compileInfos,
10081 FeatureUsage* observedFeatures, UniqueChars* error) {
10082 // Initialize MIR global information used for optimization
10083 if (codeMeta.numMemories() > 0) {
10084 if (codeMeta.memories[0].indexType() == IndexType::I32) {
10085 mir.initMinWasmMemory0Length(codeMeta.memories[0].initialLength32());
10086 } else {
10087 mir.initMinWasmMemory0Length(codeMeta.memories[0].initialLength64());
10088 }
10089 }
10090
10091 // Build MIR graph
10092 FunctionCompiler f(compilerEnv, codeMeta, d, func, locals, mir,
10093 mir.outerInfo(), tryNotes, compileInfos);
10094 if (!f.initTopLevel()) {
10095 return false;
10096 }
10097
10098 if (!f.startBlock()) {
10099 return false;
10100 }
10101
10102 if (!EmitBodyExprs(f)) {
10103 return false;
10104 }
10105
10106 f.finish();
10107
10108 *observedFeatures = f.featureUsage();
10109
10110 return true;
10111}
10112
10113bool wasm::IonCompileFunctions(const CodeMetadata& codeMeta,
10114 const CompilerEnvironment& compilerEnv,
10115 LifoAlloc& lifo,
10116 const FuncCompileInputVector& inputs,
10117 CompiledCode* code, UniqueChars* error) {
10118 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"
, 10118); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compilerEnv.tier() == Tier::Optimized"
")"); do { *((volatile int*)__null) = 10118; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10119 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"
, 10119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compilerEnv.debug() == DebugEnabled::False"
")"); do { *((volatile int*)__null) = 10119; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10120
10121 TempAllocator alloc(&lifo);
10122 JitContext jitContext;
10123 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"
, 10123); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsCompilingWasm()"
")"); do { *((volatile int*)__null) = 10123; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10124 WasmMacroAssembler masm(alloc);
10125#if defined(JS_CODEGEN_ARM64)
10126 masm.SetStackPointer64(PseudoStackPointer64);
10127#endif
10128
10129 // Swap in already-allocated empty vectors to avoid malloc/free.
10130 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"
, 10130); AnnotateMozCrashReason("MOZ_ASSERT" "(" "code->empty()"
")"); do { *((volatile int*)__null) = 10130; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10131 if (!code->swap(masm)) {
10132 return false;
10133 }
10134
10135 // Create a description of the stack layout created by GenerateTrapExit().
10136 RegisterOffsets trapExitLayout;
10137 size_t trapExitLayoutNumWords;
10138 GenerateTrapExitRegisterOffsets(&trapExitLayout, &trapExitLayoutNumWords);
10139
10140 for (const FuncCompileInput& func : inputs) {
10141 JitSpewCont(JitSpew_Codegen, "\n");
10142 JitSpew(JitSpew_Codegen,
10143 "# ================================"
10144 "==================================");
10145 JitSpew(JitSpew_Codegen, "# ==");
10146 JitSpew(JitSpew_Codegen,
10147 "# wasm::IonCompileFunctions: starting on function index %d",
10148 (int)func.index);
10149
10150 Decoder d(func.begin, func.end, func.lineOrBytecode, error);
10151
10152 // Build the local types vector.
10153 ValTypeVector locals;
10154 if (!DecodeLocalEntriesWithParams(d, codeMeta, func.index, &locals)) {
10155 return false;
10156 }
10157
10158 // Set up for Ion compilation.
10159 const JitCompileOptions options;
10160 MIRGraph graph(&alloc);
10161 CompileInfo compileInfo(locals.length());
10162 // Only activate branch hinting if the option is enabled and some hints were
10163 // parsed.
10164 if (codeMeta.branchHintingEnabled() && !codeMeta.branchHints.isEmpty()) {
10165 compileInfo.setBranchHinting(true);
10166 }
10167
10168 MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo,
10169 IonOptimizations.get(OptimizationLevel::Wasm));
10170 UniqueCompileInfoVector compileInfos;
10171
10172 // Build MIR graph
10173 FeatureUsage observedFeatures;
10174 if (!IonBuildMIR(d, compilerEnv, codeMeta, func, locals, mir,
10175 masm.tryNotes(), compileInfos, &observedFeatures, error)) {
10176 return false;
10177 }
10178
10179 // Record observed feature usage
10180 code->featureUsage |= observedFeatures;
10181
10182 // Compile MIR graph
10183 {
10184 jit::SpewBeginWasmFunction(&mir, func.index);
10185 jit::AutoSpewEndFunction spewEndFunction(&mir);
10186
10187 if (!OptimizeMIR(&mir)) {
10188 return false;
10189 }
10190
10191 LIRGraph* lir = GenerateLIR(&mir);
10192 if (!lir) {
10193 return false;
10194 }
10195
10196 size_t unwindInfoBefore = masm.codeRangeUnwindInfos().length();
10197
10198 CodeGenerator codegen(&mir, lir, &masm);
10199
10200 BytecodeOffset prologueTrapOffset(func.lineOrBytecode);
10201 FuncOffsets offsets;
10202 ArgTypeVector args(codeMeta.getFuncType(func.index));
10203 if (!codegen.generateWasm(CallIndirectId::forFunc(codeMeta, func.index),
10204 prologueTrapOffset, args, trapExitLayout,
10205 trapExitLayoutNumWords, &offsets,
10206 &code->stackMaps, &d)) {
10207 return false;
10208 }
10209
10210 bool hasUnwindInfo =
10211 unwindInfoBefore != masm.codeRangeUnwindInfos().length();
10212
10213 // Record this function's code range
10214 if (!code->codeRanges.emplaceBack(func.index, offsets, hasUnwindInfo)) {
10215 return false;
10216 }
10217 }
10218
10219 // Record this function's specific feature usage
10220 if (!code->funcs.emplaceBack(func.index, observedFeatures)) {
10221 return false;
10222 }
10223
10224 JitSpew(JitSpew_Codegen,
10225 "# wasm::IonCompileFunctions: completed function index %d",
10226 (int)func.index);
10227 JitSpew(JitSpew_Codegen, "# ==");
10228 JitSpew(JitSpew_Codegen,
10229 "# ================================"
10230 "==================================");
10231 JitSpewCont(JitSpew_Codegen, "\n");
10232 }
10233
10234 masm.finish();
10235 if (masm.oom()) {
10236 return false;
10237 }
10238
10239 return code->swap(masm);
10240}
10241
10242bool wasm::IonDumpFunction(const CompilerEnvironment& compilerEnv,
10243 const CodeMetadata& codeMeta,
10244 const FuncCompileInput& func,
10245 IonDumpContents contents, GenericPrinter& out,
10246 UniqueChars* error) {
10247 LifoAlloc lifo(TempAllocator::PreferredLifoChunkSize,
10248 js::BackgroundMallocArena);
10249 TempAllocator alloc(&lifo);
10250 JitContext jitContext;
10251 Decoder d(func.begin, func.end, func.lineOrBytecode, error);
10252
10253 // Decode the locals.
10254 ValTypeVector locals;
10255 if (!DecodeLocalEntriesWithParams(d, codeMeta, func.index, &locals)) {
10256 return false;
10257 }
10258
10259 // Set up for Ion compilation.
10260 const JitCompileOptions options;
10261 MIRGraph graph(&alloc);
10262 CompileInfo compileInfo(locals.length());
10263 MIRGenerator mir(nullptr, options, &alloc, &graph, &compileInfo,
10264 IonOptimizations.get(OptimizationLevel::Wasm));
10265
10266 // Build MIR graph
10267 TryNoteVector tryNotes;
10268 UniqueCompileInfoVector compileInfos;
10269 FeatureUsage observedFeatures;
10270 if (!IonBuildMIR(d, compilerEnv, codeMeta, func, locals, mir, tryNotes,
10271 compileInfos, &observedFeatures, error)) {
10272 return false;
10273 }
10274
10275 if (contents == IonDumpContents::UnoptimizedMIR) {
10276 graph.dump(out);
10277 return true;
10278 }
10279
10280 // Optimize the MIR graph
10281 if (!OptimizeMIR(&mir)) {
10282 return false;
10283 }
10284
10285 if (contents == IonDumpContents::OptimizedMIR) {
10286 graph.dump(out);
10287 return true;
10288 }
10289
10290#ifdef JS_JITSPEW1
10291 // Generate the LIR graph
10292 LIRGraph* lir = GenerateLIR(&mir);
10293 if (!lir) {
10294 return false;
10295 }
10296
10297 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"
, 10297); AnnotateMozCrashReason("MOZ_ASSERT" "(" "contents == IonDumpContents::LIR"
")"); do { *((volatile int*)__null) = 10297; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10298 lir->dump(out);
10299#else
10300 out.printf("cannot dump LIR without --enable-jitspew");
10301#endif
10302 return true;
10303}
10304
10305bool js::wasm::IonPlatformSupport() {
10306#if defined(JS_CODEGEN_X641) || defined(JS_CODEGEN_X86) || \
10307 defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \
10308 defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONG64) || \
10309 defined(JS_CODEGEN_RISCV64)
10310 return true;
10311#else
10312 return false;
10313#endif
10314}