Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_wasm3.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/js-confdefs.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 -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/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-16-034744-15991-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.h"
32#include "jit/ShuffleAnalysis.h"
33#include "js/ScalarType.h" // js::Scalar::Type
34#include "wasm/WasmBaselineCompile.h"
35#include "wasm/WasmBuiltinModule.h"
36#include "wasm/WasmBuiltins.h"
37#include "wasm/WasmCodegenTypes.h"
38#include "wasm/WasmGC.h"
39#include "wasm/WasmGcObject.h"
40#include "wasm/WasmGenerator.h"
41#include "wasm/WasmOpIter.h"
42#include "wasm/WasmSignalHandlers.h"
43#include "wasm/WasmStubs.h"
44#include "wasm/WasmValidate.h"
45
46using namespace js;
47using namespace js::jit;
48using namespace js::wasm;
49
50using mozilla::IsPowerOfTwo;
51using mozilla::Maybe;
52using mozilla::Nothing;
53using mozilla::Some;
54
55namespace {
56
57using BlockVector = Vector<MBasicBlock*, 8, SystemAllocPolicy>;
58using DefVector = Vector<MDefinition*, 8, SystemAllocPolicy>;
59
60// To compile try-catch blocks, we extend the IonCompilePolicy's ControlItem
61// from being just an MBasicBlock* to a Control structure collecting additional
62// information.
63using ControlInstructionVector =
64 Vector<MControlInstruction*, 8, SystemAllocPolicy>;
65
66struct TryControl {
67 // Branches to bind to the try's landing pad.
68 ControlInstructionVector landingPadPatches;
69 // For `try_table`, the list of tagged catches and labels to branch to.
70 TryTableCatchVector catches;
71 // Whether this try is in the body and should catch any thrown exception.
72 bool inBody;
73
74 TryControl() : inBody(false) {}
75
76 // Reset the try control for when it is cached in FunctionCompiler.
77 void reset() {
78 landingPadPatches.clearAndFree();
79 catches.clearAndFree();
80 inBody = false;
81 }
82};
83using UniqueTryControl = UniquePtr<TryControl>;
84using VectorUniqueTryControl = Vector<UniqueTryControl, 2, SystemAllocPolicy>;
85
86struct Control {
87 MBasicBlock* block;
88 UniqueTryControl tryControl;
89
90 Control() : block(nullptr), tryControl(nullptr) {}
91 Control(Control&&) = default;
92 Control(const Control&) = delete;
93};
94
95// [SMDOC] WebAssembly Exception Handling in Ion
96// =======================================================
97//
98// ## Throwing instructions
99//
100// Wasm exceptions can be thrown by either a throw instruction (local throw),
101// or by a wasm call.
102//
103// ## The "catching try control"
104//
105// We know we are in try-code if there is a surrounding ControlItem with
106// LabelKind::Try. The innermost such control is called the
107// "catching try control".
108//
109// ## Throws without a catching try control
110//
111// Such throws are implemented with an instance call that triggers the exception
112// unwinding runtime. The exception unwinding runtime will not return to the
113// function.
114//
115// ## "landing pad" and "pre-pad" blocks
116//
117// When an exception is thrown, the unwinder will search for the nearest
118// enclosing try block and redirect control flow to it. The code that executes
119// before any catch blocks is called the 'landing pad'. The 'landing pad' is
120// responsible to:
121// 1. Consume the pending exception state from
122// Instance::pendingException(Tag)
123// 2. Branch to the correct catch block, or else rethrow
124//
125// There is one landing pad for each try block. The immediate predecessors of
126// the landing pad are called 'pre-pad' blocks. There is one pre-pad block per
127// throwing instruction.
128//
129// ## Creating pre-pad blocks
130//
131// There are two possible sorts of pre-pad blocks, depending on whether we
132// are branching after a local throw instruction, or after a wasm call:
133//
134// - If we encounter a local throw, we create the exception and tag objects,
135// store them to Instance::pendingException(Tag), and then jump to the
136// landing pad.
137//
138// - If we encounter a wasm call, we construct a MWasmCallCatchable which is a
139// control instruction with either a branch to a fallthrough block or
140// to a pre-pad block.
141//
142// The pre-pad block for a wasm call is empty except for a jump to the
143// landing pad. It only exists to avoid critical edges which when split would
144// violate the invariants of MWasmCallCatchable. The pending exception state
145// is taken care of by the unwinder.
146//
147// Each pre-pad ends with a pending jump to the landing pad. The pending jumps
148// to the landing pad are tracked in `tryPadPatches`. These are called
149// "pad patches".
150//
151// ## Creating the landing pad
152//
153// When we exit try-code, we check if tryPadPatches has captured any control
154// instructions (pad patches). If not, we don't compile any catches and we mark
155// the rest as dead code.
156//
157// If there are pre-pad blocks, we join them to create a landing pad (or just
158// "pad"). The pad's last two slots are the caught exception, and the
159// exception's tag object.
160//
161// There are three different forms of try-catch/catch_all Wasm instructions,
162// which result in different form of landing pad.
163//
164// 1. A catchless try, so a Wasm instruction of the form "try ... end".
165// - In this case, we end the pad by rethrowing the caught exception.
166//
167// 2. A single catch_all after a try.
168// - If the first catch after a try is a catch_all, then there won't be
169// any more catches, but we need the exception and its tag object, in
170// case the code in a catch_all contains "rethrow" instructions.
171// - The Wasm instruction "rethrow", gets the exception and tag object to
172// rethrow from the last two slots of the landing pad which, due to
173// validation, is the l'th surrounding ControlItem.
174// - We immediately GoTo to a new block after the pad and pop both the
175// exception and tag object, as we don't need them anymore in this case.
176//
177// 3. Otherwise, there is one or more catch code blocks following.
178// - In this case, we construct the landing pad by creating a sequence
179// of compare and branch blocks that compare the pending exception tag
180// object to the tag object of the current tagged catch block. This is
181// done incrementally as we visit each tagged catch block in the bytecode
182// stream. At every step, we update the ControlItem's block to point to
183// the next block to be created in the landing pad sequence. The final
184// block will either be a rethrow, if there is no catch_all, or else a
185// jump to a catch_all block.
186
187struct IonCompilePolicy {
188 // We store SSA definitions in the value stack.
189 using Value = MDefinition*;
190 using ValueVector = DefVector;
191
192 // We store loop headers and then/else blocks in the control flow stack.
193 // In the case of try-catch control blocks, we collect additional information
194 // regarding the possible paths from throws and calls to a landing pad, as
195 // well as information on the landing pad's handlers (its catches).
196 using ControlItem = Control;
197};
198
199using IonOpIter = OpIter<IonCompilePolicy>;
200
201class FunctionCompiler;
202
203// CallCompileState describes a call that is being compiled.
204
205class CallCompileState {
206 // A generator object that is passed each argument as it is compiled.
207 WasmABIArgGenerator abi_;
208
209 // Accumulates the register arguments while compiling arguments.
210 MWasmCallBase::Args regArgs_;
211
212 // Reserved argument for passing Instance* to builtin instance method calls.
213 ABIArg instanceArg_;
214
215 // The stack area in which the callee will write stack return values, or
216 // nullptr if no stack results.
217 MWasmStackResultArea* stackResultArea_ = nullptr;
218
219 // Indicates that the call is a return/tail call.
220 bool returnCall = false;
221
222 // Only FunctionCompiler should be directly manipulating CallCompileState.
223 friend class FunctionCompiler;
224};
225
226// Encapsulates the compilation of a single function in an asm.js module. The
227// function compiler handles the creation and final backend compilation of the
228// MIR graph.
229class FunctionCompiler {
230 struct ControlFlowPatch {
231 MControlInstruction* ins;
232 uint32_t index;
233 ControlFlowPatch(MControlInstruction* ins, uint32_t index)
234 : ins(ins), index(index) {}
235 };
236
237 using ControlFlowPatchVector = Vector<ControlFlowPatch, 0, SystemAllocPolicy>;
238
239 struct PendingBlockTarget {
240 ControlFlowPatchVector patches;
241 BranchHint hint = BranchHint::Invalid;
242 };
243
244 using PendingBlockTargetVector =
245 Vector<PendingBlockTarget, 0, SystemAllocPolicy>;
246
247 const ModuleEnvironment& moduleEnv_;
248 IonOpIter iter_;
249 uint32_t functionBodyOffset_;
250 const FuncCompileInput& func_;
251 const ValTypeVector& locals_;
252 size_t lastReadCallSite_;
253
254 TempAllocator& alloc_;
255 MIRGraph& graph_;
256 const CompileInfo& info_;
257 MIRGenerator& mirGen_;
258
259 MBasicBlock* curBlock_;
260 uint32_t maxStackArgBytes_;
261
262 uint32_t loopDepth_;
263 uint32_t blockDepth_;
264 PendingBlockTargetVector pendingBlocks_;
265 // Control flow patches created by `delegate` instructions that target the
266 // outermost label of this function. These will be bound to a pad that will
267 // do a rethrow in `emitBodyDelegateThrowPad`.
268 ControlInstructionVector bodyDelegatePadPatches_;
269
270 // Instance pointer argument to the current function.
271 MWasmParameter* instancePointer_;
272 MWasmParameter* stackResultPointer_;
273
274 // Reference to masm.tryNotes_
275 wasm::TryNoteVector& tryNotes_;
276
277 // Cache of TryControl to minimize heap allocations
278 VectorUniqueTryControl tryControlCache_;
279
280 public:
281 FunctionCompiler(const ModuleEnvironment& moduleEnv, Decoder& decoder,
282 const FuncCompileInput& func, const ValTypeVector& locals,
283 MIRGenerator& mirGen, TryNoteVector& tryNotes)
284 : moduleEnv_(moduleEnv),
285 iter_(moduleEnv, decoder),
286 functionBodyOffset_(decoder.beginOffset()),
287 func_(func),
288 locals_(locals),
289 lastReadCallSite_(0),
290 alloc_(mirGen.alloc()),
291 graph_(mirGen.graph()),
292 info_(mirGen.outerInfo()),
293 mirGen_(mirGen),
294 curBlock_(nullptr),
295 maxStackArgBytes_(0),
296 loopDepth_(0),
297 blockDepth_(0),
298 instancePointer_(nullptr),
299 stackResultPointer_(nullptr),
300 tryNotes_(tryNotes) {}
301
302 const ModuleEnvironment& moduleEnv() const { return moduleEnv_; }
303
304 IonOpIter& iter() { return iter_; }
305 uint32_t relativeBytecodeOffset() {
306 return readBytecodeOffset() - functionBodyOffset_;
307 }
308 TempAllocator& alloc() const { return alloc_; }
309 // FIXME(1401675): Replace with BlockType.
310 uint32_t funcIndex() const { return func_.index; }
311 const FuncType& funcType() const {
312 return *moduleEnv_.funcs[func_.index].type;
313 }
314
315 MBasicBlock* getCurBlock() const { return curBlock_; }
316 BytecodeOffset bytecodeOffset() const { return iter_.bytecodeOffset(); }
317 BytecodeOffset bytecodeIfNotAsmJS() const {
318 return moduleEnv_.isAsmJS() ? BytecodeOffset() : iter_.bytecodeOffset();
319 }
320 FeatureUsage featureUsage() const { return iter_.featureUsage(); }
321
322 // Try to get a free TryControl from the cache, or allocate a new one.
323 [[nodiscard]] UniqueTryControl newTryControl() {
324 if (tryControlCache_.empty()) {
325 return UniqueTryControl(js_new<TryControl>());
326 }
327 UniqueTryControl tryControl = std::move(tryControlCache_.back());
328 tryControlCache_.popBack();
329 return tryControl;
330 }
331
332 // Release the TryControl to the cache.
333 void freeTryControl(UniqueTryControl&& tryControl) {
334 // Ensure that it's in a consistent state
335 tryControl->reset();
336 // Ignore any OOM, as we'll fail later
337 (void)tryControlCache_.append(std::move(tryControl));
338 }
339
340 [[nodiscard]] bool init() {
341 // Prepare the entry block for MIR generation:
342
343 const ArgTypeVector args(funcType());
344
345 if (!mirGen_.ensureBallast()) {
346 return false;
347 }
348 if (!newBlock(/* prev */ nullptr, &curBlock_)) {
349 return false;
350 }
351
352 for (WasmABIArgIter i(args); !i.done(); i++) {
353 MWasmParameter* ins = MWasmParameter::New(alloc(), *i, i.mirType());
354 curBlock_->add(ins);
355 if (args.isSyntheticStackResultPointerArg(i.index())) {
356 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"
, 356); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultPointer_ == nullptr"
")"); do { *((volatile int*)__null) = 356; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
357 stackResultPointer_ = ins;
358 } else {
359 curBlock_->initSlot(info().localSlot(args.naturalIndex(i.index())),
360 ins);
361 }
362 if (!mirGen_.ensureBallast()) {
363 return false;
364 }
365 }
366
367 // Set up a parameter that receives the hidden instance pointer argument.
368 instancePointer_ =
369 MWasmParameter::New(alloc(), ABIArg(InstanceReg), MIRType::Pointer);
370 curBlock_->add(instancePointer_);
371 if (!mirGen_.ensureBallast()) {
372 return false;
373 }
374
375 for (size_t i = args.lengthWithoutStackResults(); i < locals_.length();
376 i++) {
377 ValType slotValType = locals_[i];
378#ifndef ENABLE_WASM_SIMD1
379 if (slotValType == ValType::V128) {
380 return iter().fail("Ion has no SIMD support yet");
381 }
382#endif
383 MDefinition* zero = constantZeroOfValType(slotValType);
384 curBlock_->initSlot(info().localSlot(i), zero);
385 if (!mirGen_.ensureBallast()) {
386 return false;
387 }
388 }
389
390 return true;
391 }
392
393 void finish() {
394 mirGen().initWasmMaxStackArgBytes(maxStackArgBytes_);
395
396 MOZ_ASSERT(loopDepth_ == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(loopDepth_ == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(loopDepth_ == 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("loopDepth_ == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 396); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loopDepth_ == 0"
")"); do { *((volatile int*)__null) = 396; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
397 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"
, 397); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_ == 0"
")"); do { *((volatile int*)__null) = 397; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
398#ifdef DEBUG1
399 for (PendingBlockTarget& targets : pendingBlocks_) {
400 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"
, 400); AnnotateMozCrashReason("MOZ_ASSERT" "(" "targets.patches.empty()"
")"); do { *((volatile int*)__null) = 400; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
401 }
402#endif
403 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"
, 403); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 403; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
404 MOZ_ASSERT(done(), "all bytes must be consumed")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(done())>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(done()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("done()" " (" "all bytes must be consumed"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 404); AnnotateMozCrashReason("MOZ_ASSERT" "(" "done()" ") ("
"all bytes must be consumed" ")"); do { *((volatile int*)__null
) = 404; __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
405 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"
, 405); AnnotateMozCrashReason("MOZ_ASSERT" "(" "func_.callSiteLineNums.length() == lastReadCallSite_"
")"); do { *((volatile int*)__null) = 405; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
406 }
407
408 /************************* Read-only interface (after local scope setup) */
409
410 MIRGenerator& mirGen() const { return mirGen_; }
411 MIRGraph& mirGraph() const { return graph_; }
412 const CompileInfo& info() const { return info_; }
413
414 MDefinition* getLocalDef(unsigned slot) {
415 if (inDeadCode()) {
416 return nullptr;
417 }
418 return curBlock_->getSlot(info().localSlot(slot));
419 }
420
421 const ValTypeVector& locals() const { return locals_; }
422
423 /*********************************************************** Constants ***/
424
425 MDefinition* constantF32(float f) {
426 if (inDeadCode()) {
427 return nullptr;
428 }
429 auto* cst = MWasmFloatConstant::NewFloat32(alloc(), f);
430 curBlock_->add(cst);
431 return cst;
432 }
433 // Hide all other overloads, to guarantee no implicit argument conversion.
434 template <typename T>
435 MDefinition* constantF32(T) = delete;
436
437 MDefinition* constantF64(double d) {
438 if (inDeadCode()) {
439 return nullptr;
440 }
441 auto* cst = MWasmFloatConstant::NewDouble(alloc(), d);
442 curBlock_->add(cst);
443 return cst;
444 }
445 template <typename T>
446 MDefinition* constantF64(T) = delete;
447
448 MDefinition* constantI32(int32_t i) {
449 if (inDeadCode()) {
450 return nullptr;
451 }
452 MConstant* constant =
453 MConstant::New(alloc(), Int32Value(i), MIRType::Int32);
454 curBlock_->add(constant);
455 return constant;
456 }
457 template <typename T>
458 MDefinition* constantI32(T) = delete;
459
460 MDefinition* constantI64(int64_t i) {
461 if (inDeadCode()) {
462 return nullptr;
463 }
464 MConstant* constant = MConstant::NewInt64(alloc(), i);
465 curBlock_->add(constant);
466 return constant;
467 }
468 template <typename T>
469 MDefinition* constantI64(T) = delete;
470
471 // Produce an MConstant of the machine's target int type (Int32 or Int64).
472 MDefinition* constantTargetWord(intptr_t n) {
473 return targetIs64Bit() ? constantI64(int64_t(n)) : constantI32(int32_t(n));
474 }
475 template <typename T>
476 MDefinition* constantTargetWord(T) = delete;
477
478#ifdef ENABLE_WASM_SIMD1
479 MDefinition* constantV128(V128 v) {
480 if (inDeadCode()) {
481 return nullptr;
482 }
483 MWasmFloatConstant* constant = MWasmFloatConstant::NewSimd128(
484 alloc(), SimdConstant::CreateSimd128((int8_t*)v.bytes));
485 curBlock_->add(constant);
486 return constant;
487 }
488 template <typename T>
489 MDefinition* constantV128(T) = delete;
490#endif
491
492 MDefinition* constantNullRef() {
493 if (inDeadCode()) {
494 return nullptr;
495 }
496 // MConstant has a lot of baggage so we don't use that here.
497 MWasmNullConstant* constant = MWasmNullConstant::New(alloc());
498 curBlock_->add(constant);
499 return constant;
500 }
501
502 // Produce a zero constant for the specified ValType.
503 MDefinition* constantZeroOfValType(ValType valType) {
504 switch (valType.kind()) {
505 case ValType::I32:
506 return constantI32(0);
507 case ValType::I64:
508 return constantI64(int64_t(0));
509#ifdef ENABLE_WASM_SIMD1
510 case ValType::V128:
511 return constantV128(V128(0));
512#endif
513 case ValType::F32:
514 return constantF32(0.0f);
515 case ValType::F64:
516 return constantF64(0.0);
517 case ValType::Ref:
518 return constantNullRef();
519 default:
520 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 520); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 520; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
521 }
522 }
523
524 /***************************** Code generation (after local scope setup) */
525
526 void fence() {
527 if (inDeadCode()) {
528 return;
529 }
530 MWasmFence* ins = MWasmFence::New(alloc());
531 curBlock_->add(ins);
532 }
533
534 template <class T>
535 MDefinition* unary(MDefinition* op) {
536 if (inDeadCode()) {
537 return nullptr;
538 }
539 T* ins = T::New(alloc(), op);
540 curBlock_->add(ins);
541 return ins;
542 }
543
544 template <class T>
545 MDefinition* unary(MDefinition* op, MIRType type) {
546 if (inDeadCode()) {
547 return nullptr;
548 }
549 T* ins = T::New(alloc(), op, type);
550 curBlock_->add(ins);
551 return ins;
552 }
553
554 template <class T>
555 MDefinition* binary(MDefinition* lhs, MDefinition* rhs) {
556 if (inDeadCode()) {
557 return nullptr;
558 }
559 T* ins = T::New(alloc(), lhs, rhs);
560 curBlock_->add(ins);
561 return ins;
562 }
563
564 template <class T>
565 MDefinition* binary(MDefinition* lhs, MDefinition* rhs, MIRType type) {
566 if (inDeadCode()) {
567 return nullptr;
568 }
569 T* ins = T::New(alloc(), lhs, rhs, type);
570 curBlock_->add(ins);
571 return ins;
572 }
573
574 template <class T>
575 MDefinition* binary(MDefinition* lhs, MDefinition* rhs, MIRType type,
576 MWasmBinaryBitwise::SubOpcode subOpc) {
577 if (inDeadCode()) {
578 return nullptr;
579 }
580 T* ins = T::New(alloc(), lhs, rhs, type, subOpc);
581 curBlock_->add(ins);
582 return ins;
583 }
584
585 MDefinition* ursh(MDefinition* lhs, MDefinition* rhs, MIRType type) {
586 if (inDeadCode()) {
587 return nullptr;
588 }
589 auto* ins = MUrsh::NewWasm(alloc(), lhs, rhs, type);
590 curBlock_->add(ins);
591 return ins;
592 }
593
594 MDefinition* add(MDefinition* lhs, MDefinition* rhs, MIRType type) {
595 if (inDeadCode()) {
596 return nullptr;
597 }
598 auto* ins = MAdd::NewWasm(alloc(), lhs, rhs, type);
599 curBlock_->add(ins);
600 return ins;
601 }
602
603 bool mustPreserveNaN(MIRType type) {
604 return IsFloatingPointType(type) && !moduleEnv().isAsmJS();
605 }
606
607 MDefinition* sub(MDefinition* lhs, MDefinition* rhs, MIRType type) {
608 if (inDeadCode()) {
609 return nullptr;
610 }
611
612 // wasm can't fold x - 0.0 because of NaN with custom payloads.
613 MSub* ins = MSub::NewWasm(alloc(), lhs, rhs, type, mustPreserveNaN(type));
614 curBlock_->add(ins);
615 return ins;
616 }
617
618 MDefinition* nearbyInt(MDefinition* input, RoundingMode roundingMode) {
619 if (inDeadCode()) {
620 return nullptr;
621 }
622
623 auto* ins = MNearbyInt::New(alloc(), input, input->type(), roundingMode);
624 curBlock_->add(ins);
625 return ins;
626 }
627
628 MDefinition* minMax(MDefinition* lhs, MDefinition* rhs, MIRType type,
629 bool isMax) {
630 if (inDeadCode()) {
631 return nullptr;
632 }
633
634 if (mustPreserveNaN(type)) {
635 // Convert signaling NaN to quiet NaNs.
636 MDefinition* zero = constantZeroOfValType(ValType::fromMIRType(type));
637 lhs = sub(lhs, zero, type);
638 rhs = sub(rhs, zero, type);
639 }
640
641 MMinMax* ins = MMinMax::NewWasm(alloc(), lhs, rhs, type, isMax);
642 curBlock_->add(ins);
643 return ins;
644 }
645
646 MDefinition* mul(MDefinition* lhs, MDefinition* rhs, MIRType type,
647 MMul::Mode mode) {
648 if (inDeadCode()) {
649 return nullptr;
650 }
651
652 // wasm can't fold x * 1.0 because of NaN with custom payloads.
653 auto* ins =
654 MMul::NewWasm(alloc(), lhs, rhs, type, mode, mustPreserveNaN(type));
655 curBlock_->add(ins);
656 return ins;
657 }
658
659 MDefinition* div(MDefinition* lhs, MDefinition* rhs, MIRType type,
660 bool unsignd) {
661 if (inDeadCode()) {
662 return nullptr;
663 }
664 bool trapOnError = !moduleEnv().isAsmJS();
665 if (!unsignd && type == MIRType::Int32) {
666 // Enforce the signedness of the operation by coercing the operands
667 // to signed. Otherwise, operands that "look" unsigned to Ion but
668 // are not unsigned to Baldr (eg, unsigned right shifts) may lead to
669 // the operation being executed unsigned. Applies to mod() as well.
670 //
671 // Do this for Int32 only since Int64 is not subject to the same
672 // issues.
673 //
674 // Note the offsets passed to MWasmBuiltinTruncateToInt32 are wrong here,
675 // but it doesn't matter: they're not codegen'd to calls since inputs
676 // already are int32.
677 auto* lhs2 = createTruncateToInt32(lhs);
678 curBlock_->add(lhs2);
679 lhs = lhs2;
680 auto* rhs2 = createTruncateToInt32(rhs);
681 curBlock_->add(rhs2);
682 rhs = rhs2;
683 }
684
685 // For x86 and arm we implement i64 div via c++ builtin.
686 // A call to c++ builtin requires instance pointer.
687#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
688 if (type == MIRType::Int64) {
689 auto* ins =
690 MWasmBuiltinDivI64::New(alloc(), lhs, rhs, instancePointer_, unsignd,
691 trapOnError, bytecodeOffset());
692 curBlock_->add(ins);
693 return ins;
694 }
695#endif
696
697 auto* ins = MDiv::New(alloc(), lhs, rhs, type, unsignd, trapOnError,
698 bytecodeOffset(), mustPreserveNaN(type));
699 curBlock_->add(ins);
700 return ins;
701 }
702
703 MInstruction* createTruncateToInt32(MDefinition* op) {
704 if (op->type() == MIRType::Double || op->type() == MIRType::Float32) {
705 return MWasmBuiltinTruncateToInt32::New(alloc(), op, instancePointer_);
706 }
707
708 return MTruncateToInt32::New(alloc(), op);
709 }
710
711 MDefinition* mod(MDefinition* lhs, MDefinition* rhs, MIRType type,
712 bool unsignd) {
713 if (inDeadCode()) {
714 return nullptr;
715 }
716 bool trapOnError = !moduleEnv().isAsmJS();
717 if (!unsignd && type == MIRType::Int32) {
718 // See block comment in div().
719 auto* lhs2 = createTruncateToInt32(lhs);
720 curBlock_->add(lhs2);
721 lhs = lhs2;
722 auto* rhs2 = createTruncateToInt32(rhs);
723 curBlock_->add(rhs2);
724 rhs = rhs2;
725 }
726
727 // For x86 and arm we implement i64 mod via c++ builtin.
728 // A call to c++ builtin requires instance pointer.
729#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
730 if (type == MIRType::Int64) {
731 auto* ins =
732 MWasmBuiltinModI64::New(alloc(), lhs, rhs, instancePointer_, unsignd,
733 trapOnError, bytecodeOffset());
734 curBlock_->add(ins);
735 return ins;
736 }
737#endif
738
739 // Should be handled separately because we call BuiltinThunk for this case
740 // and so, need to add the dependency from instancePointer.
741 if (type == MIRType::Double) {
742 auto* ins = MWasmBuiltinModD::New(alloc(), lhs, rhs, instancePointer_,
743 type, bytecodeOffset());
744 curBlock_->add(ins);
745 return ins;
746 }
747
748 auto* ins = MMod::New(alloc(), lhs, rhs, type, unsignd, trapOnError,
749 bytecodeOffset());
750 curBlock_->add(ins);
751 return ins;
752 }
753
754 MDefinition* bitnot(MDefinition* op) {
755 if (inDeadCode()) {
756 return nullptr;
757 }
758 auto* ins = MBitNot::New(alloc(), op);
759 curBlock_->add(ins);
760 return ins;
761 }
762
763 MDefinition* select(MDefinition* trueExpr, MDefinition* falseExpr,
764 MDefinition* condExpr) {
765 if (inDeadCode()) {
766 return nullptr;
767 }
768 auto* ins = MWasmSelect::New(alloc(), trueExpr, falseExpr, condExpr);
769 curBlock_->add(ins);
770 return ins;
771 }
772
773 MDefinition* extendI32(MDefinition* op, bool isUnsigned) {
774 if (inDeadCode()) {
775 return nullptr;
776 }
777 auto* ins = MExtendInt32ToInt64::New(alloc(), op, isUnsigned);
778 curBlock_->add(ins);
779 return ins;
780 }
781
782 MDefinition* signExtend(MDefinition* op, uint32_t srcSize,
783 uint32_t targetSize) {
784 if (inDeadCode()) {
785 return nullptr;
786 }
787 MInstruction* ins;
788 switch (targetSize) {
789 case 4: {
790 MSignExtendInt32::Mode mode;
791 switch (srcSize) {
792 case 1:
793 mode = MSignExtendInt32::Byte;
794 break;
795 case 2:
796 mode = MSignExtendInt32::Half;
797 break;
798 default:
799 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"
, 799); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 799; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
800 }
801 ins = MSignExtendInt32::New(alloc(), op, mode);
802 break;
803 }
804 case 8: {
805 MSignExtendInt64::Mode mode;
806 switch (srcSize) {
807 case 1:
808 mode = MSignExtendInt64::Byte;
809 break;
810 case 2:
811 mode = MSignExtendInt64::Half;
812 break;
813 case 4:
814 mode = MSignExtendInt64::Word;
815 break;
816 default:
817 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"
, 817); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 817; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
818 }
819 ins = MSignExtendInt64::New(alloc(), op, mode);
820 break;
821 }
822 default: {
823 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"
, 823); AnnotateMozCrashReason("MOZ_CRASH(" "Bad sign extension"
")"); do { *((volatile int*)__null) = 823; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
824 }
825 }
826 curBlock_->add(ins);
827 return ins;
828 }
829
830 MDefinition* convertI64ToFloatingPoint(MDefinition* op, MIRType type,
831 bool isUnsigned) {
832 if (inDeadCode()) {
833 return nullptr;
834 }
835#if defined(JS_CODEGEN_ARM)
836 auto* ins = MBuiltinInt64ToFloatingPoint::New(
837 alloc(), op, instancePointer_, type, bytecodeOffset(), isUnsigned);
838#else
839 auto* ins = MInt64ToFloatingPoint::New(alloc(), op, type, bytecodeOffset(),
840 isUnsigned);
841#endif
842 curBlock_->add(ins);
843 return ins;
844 }
845
846 MDefinition* rotate(MDefinition* input, MDefinition* count, MIRType type,
847 bool left) {
848 if (inDeadCode()) {
849 return nullptr;
850 }
851 auto* ins = MRotate::New(alloc(), input, count, type, left);
852 curBlock_->add(ins);
853 return ins;
854 }
855
856 template <class T>
857 MDefinition* truncate(MDefinition* op, TruncFlags flags) {
858 if (inDeadCode()) {
859 return nullptr;
860 }
861 auto* ins = T::New(alloc(), op, flags, bytecodeOffset());
862 curBlock_->add(ins);
863 return ins;
864 }
865
866#if defined(JS_CODEGEN_ARM)
867 MDefinition* truncateWithInstance(MDefinition* op, TruncFlags flags) {
868 if (inDeadCode()) {
869 return nullptr;
870 }
871 auto* ins = MWasmBuiltinTruncateToInt64::New(alloc(), op, instancePointer_,
872 flags, bytecodeOffset());
873 curBlock_->add(ins);
874 return ins;
875 }
876#endif
877
878 MDefinition* compare(MDefinition* lhs, MDefinition* rhs, JSOp op,
879 MCompare::CompareType type) {
880 if (inDeadCode()) {
881 return nullptr;
882 }
883 auto* ins = MCompare::NewWasm(alloc(), lhs, rhs, op, type);
884 curBlock_->add(ins);
885 return ins;
886 }
887
888 void assign(unsigned slot, MDefinition* def) {
889 if (inDeadCode()) {
890 return;
891 }
892 curBlock_->setSlot(info().localSlot(slot), def);
893 }
894
895 MDefinition* compareIsNull(MDefinition* ref, JSOp compareOp) {
896 MDefinition* nullVal = constantNullRef();
897 if (!nullVal) {
898 return nullptr;
899 }
900 return compare(ref, nullVal, compareOp, MCompare::Compare_WasmAnyRef);
901 }
902
903 [[nodiscard]] bool refAsNonNull(MDefinition* ref) {
904 if (inDeadCode()) {
905 return true;
906 }
907
908 auto* ins = MWasmTrapIfNull::New(
909 alloc(), ref, wasm::Trap::NullPointerDereference, bytecodeOffset());
910
911 curBlock_->add(ins);
912 return true;
913 }
914
915#ifdef ENABLE_WASM_GC1
916 [[nodiscard]] bool brOnNull(uint32_t relativeDepth, const DefVector& values,
917 const ResultType& type, MDefinition* condition) {
918 if (inDeadCode()) {
919 return true;
920 }
921
922 MBasicBlock* fallthroughBlock = nullptr;
923 if (!newBlock(curBlock_, &fallthroughBlock)) {
924 return false;
925 }
926
927 MDefinition* check = compareIsNull(condition, JSOp::Eq);
928 if (!check) {
929 return false;
930 }
931 MTest* test = MTest::New(alloc(), check, nullptr, fallthroughBlock);
932 if (!test ||
933 !addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) {
934 return false;
935 }
936
937 if (!pushDefs(values)) {
938 return false;
939 }
940
941 curBlock_->end(test);
942 curBlock_ = fallthroughBlock;
943 return true;
944 }
945
946 [[nodiscard]] bool brOnNonNull(uint32_t relativeDepth,
947 const DefVector& values,
948 const ResultType& type,
949 MDefinition* condition) {
950 if (inDeadCode()) {
951 return true;
952 }
953
954 MBasicBlock* fallthroughBlock = nullptr;
955 if (!newBlock(curBlock_, &fallthroughBlock)) {
956 return false;
957 }
958
959 MDefinition* check = compareIsNull(condition, JSOp::Ne);
960 if (!check) {
961 return false;
962 }
963 MTest* test = MTest::New(alloc(), check, nullptr, fallthroughBlock);
964 if (!test ||
965 !addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) {
966 return false;
967 }
968
969 if (!pushDefs(values)) {
970 return false;
971 }
972
973 curBlock_->end(test);
974 curBlock_ = fallthroughBlock;
975 return true;
976 }
977
978#endif // ENABLE_WASM_GC
979
980#ifdef ENABLE_WASM_GC1
981 MDefinition* refI31(MDefinition* input) {
982 auto* ins = MWasmNewI31Ref::New(alloc(), input);
983 curBlock_->add(ins);
984 return ins;
985 }
986
987 MDefinition* i31Get(MDefinition* input, FieldWideningOp wideningOp) {
988 auto* ins = MWasmI31RefGet::New(alloc(), input, wideningOp);
989 curBlock_->add(ins);
990 return ins;
991 }
992#endif // ENABLE_WASM_GC
993
994#ifdef ENABLE_WASM_SIMD1
995 // About Wasm SIMD as supported by Ion:
996 //
997 // The expectation is that Ion will only ever support SIMD on x86 and x64,
998 // since ARMv7 will cease to be a tier-1 platform soon, and MIPS64 will never
999 // implement SIMD.
1000 //
1001 // The division of the operations into MIR nodes reflects that expectation,
1002 // and is a good fit for x86/x64. Should the expectation change we'll
1003 // possibly want to re-architect the SIMD support to be a little more general.
1004 //
1005 // Most SIMD operations map directly to a single MIR node that ultimately ends
1006 // up being expanded in the macroassembler.
1007 //
1008 // Some SIMD operations that do have a complete macroassembler expansion are
1009 // open-coded into multiple MIR nodes here; in some cases that's just
1010 // convenience, in other cases it may also allow them to benefit from Ion
1011 // optimizations. The reason for the expansions will be documented by a
1012 // comment.
1013
1014 // (v128,v128) -> v128 effect-free binary operations
1015 MDefinition* binarySimd128(MDefinition* lhs, MDefinition* rhs,
1016 bool commutative, SimdOp op) {
1017 if (inDeadCode()) {
1018 return nullptr;
1019 }
1020
1021 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"
, 1022); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1022; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1022 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"
, 1022); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1022; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1023
1024 auto* ins = MWasmBinarySimd128::New(alloc(), lhs, rhs, commutative, op);
1025 curBlock_->add(ins);
1026 return ins;
1027 }
1028
1029 // (v128,i32) -> v128 effect-free shift operations
1030 MDefinition* shiftSimd128(MDefinition* lhs, MDefinition* rhs, SimdOp op) {
1031 if (inDeadCode()) {
1032 return nullptr;
1033 }
1034
1035 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"
, 1036); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1036; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1036 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"
, 1036); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128 && rhs->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1036; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1037
1038 int32_t maskBits;
1039 if (MacroAssembler::MustMaskShiftCountSimd128(op, &maskBits)) {
1040 MDefinition* mask = constantI32(maskBits);
1041 auto* rhs2 = MBitAnd::New(alloc(), rhs, mask, MIRType::Int32);
1042 curBlock_->add(rhs2);
1043 rhs = rhs2;
1044 }
1045
1046 auto* ins = MWasmShiftSimd128::New(alloc(), lhs, rhs, op);
1047 curBlock_->add(ins);
1048 return ins;
1049 }
1050
1051 // (v128,scalar,imm) -> v128
1052 MDefinition* replaceLaneSimd128(MDefinition* lhs, MDefinition* rhs,
1053 uint32_t laneIndex, SimdOp op) {
1054 if (inDeadCode()) {
1055 return nullptr;
1056 }
1057
1058 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"
, 1058); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1058; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1059
1060 auto* ins = MWasmReplaceLaneSimd128::New(alloc(), lhs, rhs, laneIndex, op);
1061 curBlock_->add(ins);
1062 return ins;
1063 }
1064
1065 // (scalar) -> v128 effect-free unary operations
1066 MDefinition* scalarToSimd128(MDefinition* src, SimdOp op) {
1067 if (inDeadCode()) {
1068 return nullptr;
1069 }
1070
1071 auto* ins = MWasmScalarToSimd128::New(alloc(), src, op);
1072 curBlock_->add(ins);
1073 return ins;
1074 }
1075
1076 // (v128) -> v128 effect-free unary operations
1077 MDefinition* unarySimd128(MDefinition* src, SimdOp op) {
1078 if (inDeadCode()) {
1079 return nullptr;
1080 }
1081
1082 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"
, 1082); AnnotateMozCrashReason("MOZ_ASSERT" "(" "src->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1082; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1083 auto* ins = MWasmUnarySimd128::New(alloc(), src, op);
1084 curBlock_->add(ins);
1085 return ins;
1086 }
1087
1088 // (v128, imm) -> scalar effect-free unary operations
1089 MDefinition* reduceSimd128(MDefinition* src, SimdOp op, ValType outType,
1090 uint32_t imm = 0) {
1091 if (inDeadCode()) {
1092 return nullptr;
1093 }
1094
1095 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"
, 1095); AnnotateMozCrashReason("MOZ_ASSERT" "(" "src->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1095; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1096 auto* ins =
1097 MWasmReduceSimd128::New(alloc(), src, op, outType.toMIRType(), imm);
1098 curBlock_->add(ins);
1099 return ins;
1100 }
1101
1102 // (v128, v128, v128) -> v128 effect-free operations
1103 MDefinition* ternarySimd128(MDefinition* v0, MDefinition* v1, MDefinition* v2,
1104 SimdOp op) {
1105 if (inDeadCode()) {
1106 return nullptr;
1107 }
1108
1109 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"
, 1111); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1111; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1110 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"
, 1111); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1111; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1111 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"
, 1111); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v0->type() == MIRType::Simd128 && v1->type() == MIRType::Simd128 && v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1111; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1112
1113 auto* ins = MWasmTernarySimd128::New(alloc(), v0, v1, v2, op);
1114 curBlock_->add(ins);
1115 return ins;
1116 }
1117
1118 // (v128, v128, imm_v128) -> v128 effect-free operations
1119 MDefinition* shuffleSimd128(MDefinition* v1, MDefinition* v2, V128 control) {
1120 if (inDeadCode()) {
1121 return nullptr;
1122 }
1123
1124 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"
, 1124); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v1->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1124; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1125 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"
, 1125); AnnotateMozCrashReason("MOZ_ASSERT" "(" "v2->type() == MIRType::Simd128"
")"); do { *((volatile int*)__null) = 1125; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1126 auto* ins = BuildWasmShuffleSimd128(
1127 alloc(), reinterpret_cast<int8_t*>(control.bytes), v1, v2);
1128 curBlock_->add(ins);
1129 return ins;
1130 }
1131
1132 // Also see below for SIMD memory references
1133
1134#endif // ENABLE_WASM_SIMD
1135
1136 /************************************************ Linear memory accesses */
1137
1138 // For detailed information about memory accesses, see "Linear memory
1139 // addresses and bounds checking" in WasmMemory.cpp.
1140
1141 private:
1142 // If the platform does not have a HeapReg, load the memory base from
1143 // instance.
1144 MDefinition* maybeLoadMemoryBase(uint32_t memoryIndex) {
1145#ifdef WASM_HAS_HEAPREG1
1146 if (memoryIndex == 0) {
1147 return nullptr;
1148 }
1149#endif
1150 return memoryBase(memoryIndex);
1151 }
1152
1153 public:
1154 // A value holding the memory base, whether that's HeapReg or some other
1155 // register.
1156 MDefinition* memoryBase(uint32_t memoryIndex) {
1157 AliasSet aliases = !moduleEnv_.memories[memoryIndex].canMovingGrow()
1158 ? AliasSet::None()
1159 : AliasSet::Load(AliasSet::WasmHeapMeta);
1160#ifdef WASM_HAS_HEAPREG1
1161 if (memoryIndex == 0) {
1162 MWasmHeapReg* base = MWasmHeapReg::New(alloc(), aliases);
1163 curBlock_->add(base);
1164 return base;
1165 }
1166#endif
1167 uint32_t offset =
1168 memoryIndex == 0
1169 ? Instance::offsetOfMemory0Base()
1170 : (Instance::offsetInData(
1171 moduleEnv_.offsetOfMemoryInstanceData(memoryIndex) +
1172 offsetof(MemoryInstanceData, base)__builtin_offsetof(MemoryInstanceData, base)));
1173 MWasmLoadInstance* base = MWasmLoadInstance::New(
1174 alloc(), instancePointer_, offset, MIRType::Pointer, aliases);
1175 curBlock_->add(base);
1176 return base;
1177 }
1178
1179 private:
1180 // If the bounds checking strategy requires it, load the bounds check limit
1181 // from the instance.
1182 MWasmLoadInstance* maybeLoadBoundsCheckLimit(uint32_t memoryIndex,
1183 MIRType type) {
1184 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"
, 1184); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Int32 || type == MIRType::Int64"
")"); do { *((volatile int*)__null) = 1184; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1185 if (moduleEnv_.hugeMemoryEnabled(memoryIndex)) {
1186 return nullptr;
1187 }
1188 uint32_t offset =
1189 memoryIndex == 0
1190 ? Instance::offsetOfMemory0BoundsCheckLimit()
1191 : (Instance::offsetInData(
1192 moduleEnv_.offsetOfMemoryInstanceData(memoryIndex) +
1193 offsetof(MemoryInstanceData, boundsCheckLimit)__builtin_offsetof(MemoryInstanceData, boundsCheckLimit)));
1194 AliasSet aliases = !moduleEnv_.memories[memoryIndex].canMovingGrow()
1195 ? AliasSet::None()
1196 : AliasSet::Load(AliasSet::WasmHeapMeta);
1197 auto* load = MWasmLoadInstance::New(alloc(), instancePointer_, offset, type,
1198 aliases);
1199 curBlock_->add(load);
1200 return load;
1201 }
1202
1203 // Return true if the access requires an alignment check. If so, sets
1204 // *mustAdd to true if the offset must be added to the pointer before
1205 // checking.
1206 bool needAlignmentCheck(MemoryAccessDesc* access, MDefinition* base,
1207 bool* mustAdd) {
1208 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"
, 1208); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*mustAdd" ")"
); do { *((volatile int*)__null) = 1208; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1209
1210 // asm.js accesses are always aligned and need no checks.
1211 if (moduleEnv_.isAsmJS() || !access->isAtomic()) {
1212 return false;
1213 }
1214
1215 // If the EA is known and aligned it will need no checks.
1216 if (base->isConstant()) {
1217 // We only care about the low bits, so overflow is OK, as is chopping off
1218 // the high bits of an i64 pointer.
1219 uint32_t ptr = 0;
1220 if (isMem64(access->memoryIndex())) {
1221 ptr = uint32_t(base->toConstant()->toInt64());
1222 } else {
1223 ptr = base->toConstant()->toInt32();
1224 }
1225 if (((ptr + access->offset64()) & (access->byteSize() - 1)) == 0) {
1226 return false;
1227 }
1228 }
1229
1230 // If the offset is aligned then the EA is just the pointer, for
1231 // the purposes of this check.
1232 *mustAdd = (access->offset64() & (access->byteSize() - 1)) != 0;
1233 return true;
1234 }
1235
1236 // Fold a constant base into the offset and make the base 0, provided the
1237 // offset stays below the guard limit. The reason for folding the base into
1238 // the offset rather than vice versa is that a small offset can be ignored
1239 // by both explicit bounds checking and bounds check elimination.
1240 void foldConstantPointer(MemoryAccessDesc* access, MDefinition** base) {
1241 uint32_t offsetGuardLimit = GetMaxOffsetGuardLimit(
1242 moduleEnv_.hugeMemoryEnabled(access->memoryIndex()));
1243
1244 if ((*base)->isConstant()) {
1245 uint64_t basePtr = 0;
1246 if (isMem64(access->memoryIndex())) {
1247 basePtr = uint64_t((*base)->toConstant()->toInt64());
1248 } else {
1249 basePtr = uint64_t(int64_t((*base)->toConstant()->toInt32()));
1250 }
1251
1252 uint64_t offset = access->offset64();
1253
1254 if (offset < offsetGuardLimit && basePtr < offsetGuardLimit - offset) {
1255 offset += uint32_t(basePtr);
1256 access->setOffset32(uint32_t(offset));
1257 *base = isMem64(access->memoryIndex()) ? constantI64(int64_t(0))
1258 : constantI32(0);
1259 }
1260 }
1261 }
1262
1263 // If the offset must be added because it is large or because the true EA must
1264 // be checked, compute the effective address, trapping on overflow.
1265 void maybeComputeEffectiveAddress(MemoryAccessDesc* access,
1266 MDefinition** base, bool mustAddOffset) {
1267 uint32_t offsetGuardLimit = GetMaxOffsetGuardLimit(
1268 moduleEnv_.hugeMemoryEnabled(access->memoryIndex()));
1269
1270 if (access->offset64() >= offsetGuardLimit ||
1271 access->offset64() > UINT32_MAX(4294967295U) || mustAddOffset ||
1272 !JitOptions.wasmFoldOffsets) {
1273 *base = computeEffectiveAddress(*base, access);
1274 }
1275 }
1276
1277 MWasmLoadInstance* needBoundsCheck(uint32_t memoryIndex) {
1278#ifdef JS_64BIT1
1279 // For 32-bit base pointers:
1280 //
1281 // If the bounds check uses the full 64 bits of the bounds check limit, then
1282 // the base pointer must be zero-extended to 64 bits before checking and
1283 // wrapped back to 32-bits after Spectre masking. (And it's important that
1284 // the value we end up with has flowed through the Spectre mask.)
1285 //
1286 // If the memory's max size is known to be smaller than 64K pages exactly,
1287 // we can use a 32-bit check and avoid extension and wrapping.
1288 static_assert(0x100000000 % PageSize == 0);
1289 bool mem32LimitIs64Bits =
1290 isMem32(memoryIndex) &&
1291 !moduleEnv_.memories[memoryIndex].boundsCheckLimitIs32Bits() &&
1292 MaxMemoryPages(moduleEnv_.memories[memoryIndex].indexType()) >=
1293 Pages(0x100000000 / PageSize);
1294#else
1295 // On 32-bit platforms we have no more than 2GB memory and the limit for a
1296 // 32-bit base pointer is never a 64-bit value.
1297 bool mem32LimitIs64Bits = false;
1298#endif
1299 return maybeLoadBoundsCheckLimit(memoryIndex,
1300 mem32LimitIs64Bits || isMem64(memoryIndex)
1301 ? MIRType::Int64
1302 : MIRType::Int32);
1303 }
1304
1305 void performBoundsCheck(uint32_t memoryIndex, MDefinition** base,
1306 MWasmLoadInstance* boundsCheckLimit) {
1307 // At the outset, actualBase could be the result of pretty much any integer
1308 // operation, or it could be the load of an integer constant. If its type
1309 // is i32, we may assume the value has a canonical representation for the
1310 // platform, see doc block in MacroAssembler.h.
1311 MDefinition* actualBase = *base;
1312
1313 // Extend an i32 index value to perform a 64-bit bounds check if the memory
1314 // can be 4GB or larger.
1315 bool extendAndWrapIndex =
1316 isMem32(memoryIndex) && boundsCheckLimit->type() == MIRType::Int64;
1317 if (extendAndWrapIndex) {
1318 auto* extended = MWasmExtendU32Index::New(alloc(), actualBase);
1319 curBlock_->add(extended);
1320 actualBase = extended;
1321 }
1322
1323 auto target = memoryIndex == 0 ? MWasmBoundsCheck::Memory0
1324 : MWasmBoundsCheck::Unknown;
1325 auto* ins = MWasmBoundsCheck::New(alloc(), actualBase, boundsCheckLimit,
1326 bytecodeOffset(), target);
1327 curBlock_->add(ins);
1328 actualBase = ins;
1329
1330 // If we're masking, then we update *base to create a dependency chain
1331 // through the masked index. But we will first need to wrap the index
1332 // value if it was extended above.
1333 if (JitOptions.spectreIndexMasking) {
1334 if (extendAndWrapIndex) {
1335 auto* wrapped = MWasmWrapU32Index::New(alloc(), actualBase);
1336 curBlock_->add(wrapped);
1337 actualBase = wrapped;
1338 }
1339 *base = actualBase;
1340 }
1341 }
1342
1343 // Perform all necessary checking before a wasm heap access, based on the
1344 // attributes of the access and base pointer.
1345 //
1346 // For 64-bit indices on platforms that are limited to indices that fit into
1347 // 32 bits (all 32-bit platforms and mips64), this returns a bounds-checked
1348 // `base` that has type Int32. Lowering code depends on this and will assert
1349 // that the base has this type. See the end of this function.
1350
1351 void checkOffsetAndAlignmentAndBounds(MemoryAccessDesc* access,
1352 MDefinition** base) {
1353 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"
, 1353); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 1353; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1354 MOZ_ASSERT(!moduleEnv_.isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!moduleEnv_.isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!moduleEnv_.isAsmJS()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!moduleEnv_.isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!moduleEnv_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1354; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1355
1356 // Attempt to fold an offset into a constant base pointer so as to simplify
1357 // the addressing expression. This may update *base.
1358 foldConstantPointer(access, base);
1359
1360 // Determine whether an alignment check is needed and whether the offset
1361 // must be checked too.
1362 bool mustAddOffsetForAlignmentCheck = false;
1363 bool alignmentCheck =
1364 needAlignmentCheck(access, *base, &mustAddOffsetForAlignmentCheck);
1365
1366 // If bounds checking or alignment checking requires it, compute the
1367 // effective address: add the offset into the pointer and trap on overflow.
1368 // This may update *base.
1369 maybeComputeEffectiveAddress(access, base, mustAddOffsetForAlignmentCheck);
1370
1371 // Emit the alignment check if necessary; it traps if it fails.
1372 if (alignmentCheck) {
1373 curBlock_->add(MWasmAlignmentCheck::New(
1374 alloc(), *base, access->byteSize(), bytecodeOffset()));
1375 }
1376
1377 // Emit the bounds check if necessary; it traps if it fails. This may
1378 // update *base.
1379 MWasmLoadInstance* boundsCheckLimit =
1380 needBoundsCheck(access->memoryIndex());
1381 if (boundsCheckLimit) {
1382 performBoundsCheck(access->memoryIndex(), base, boundsCheckLimit);
1383 }
1384
1385#ifndef JS_64BIT1
1386 if (isMem64(access->memoryIndex())) {
1387 // We must have had an explicit bounds check (or one was elided if it was
1388 // proved redundant), and on 32-bit systems the index will for sure fit in
1389 // 32 bits: the max memory is 2GB. So chop the index down to 32-bit to
1390 // simplify the back-end.
1391 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"
, 1391); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(*base)->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 1391; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1392 MOZ_ASSERT(!moduleEnv_.hugeMemoryEnabled(access->memoryIndex()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!moduleEnv_.hugeMemoryEnabled(access->memoryIndex
()))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!moduleEnv_.hugeMemoryEnabled(access->memoryIndex
())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!moduleEnv_.hugeMemoryEnabled(access->memoryIndex())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1392); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!moduleEnv_.hugeMemoryEnabled(access->memoryIndex())"
")"); do { *((volatile int*)__null) = 1392; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1393 auto* chopped = MWasmWrapU32Index::New(alloc(), *base);
1394 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"
, 1394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "chopped->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1394; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1395 curBlock_->add(chopped);
1396 *base = chopped;
1397 }
1398#endif
1399 }
1400
1401 bool isSmallerAccessForI64(ValType result, const MemoryAccessDesc* access) {
1402 if (result == ValType::I64 && access->byteSize() <= 4) {
1403 // These smaller accesses should all be zero-extending.
1404 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"
, 1404); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isSignedIntType(access->type())"
")"); do { *((volatile int*)__null) = 1404; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1405 return true;
1406 }
1407 return false;
1408 }
1409
1410 public:
1411 bool isMem32(uint32_t memoryIndex) {
1412 return moduleEnv_.memories[memoryIndex].indexType() == IndexType::I32;
1413 }
1414 bool isMem64(uint32_t memoryIndex) {
1415 return moduleEnv_.memories[memoryIndex].indexType() == IndexType::I64;
1416 }
1417 bool hugeMemoryEnabled(uint32_t memoryIndex) {
1418 return moduleEnv_.hugeMemoryEnabled(memoryIndex);
1419 }
1420
1421 // Add the offset into the pointer to yield the EA; trap on overflow.
1422 MDefinition* computeEffectiveAddress(MDefinition* base,
1423 MemoryAccessDesc* access) {
1424 if (inDeadCode()) {
1425 return nullptr;
1426 }
1427 uint64_t offset = access->offset64();
1428 if (offset == 0) {
1429 return base;
1430 }
1431 auto* ins = MWasmAddOffset::New(alloc(), base, offset, bytecodeOffset());
1432 curBlock_->add(ins);
1433 access->clearOffset();
1434 return ins;
1435 }
1436
1437 MDefinition* load(MDefinition* base, MemoryAccessDesc* access,
1438 ValType result) {
1439 if (inDeadCode()) {
1440 return nullptr;
1441 }
1442
1443 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1444 MInstruction* load = nullptr;
1445 if (moduleEnv_.isAsmJS()) {
1446 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"
, 1446); AnnotateMozCrashReason("MOZ_ASSERT" "(" "access->offset64() == 0"
")"); do { *((volatile int*)__null) = 1446; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1447 MWasmLoadInstance* boundsCheckLimit =
1448 maybeLoadBoundsCheckLimit(access->memoryIndex(), MIRType::Int32);
1449 load = MAsmJSLoadHeap::New(alloc(), memoryBase, base, boundsCheckLimit,
1450 access->type());
1451 } else {
1452 checkOffsetAndAlignmentAndBounds(access, &base);
1453#ifndef JS_64BIT1
1454 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"
, 1454); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1454; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1455#endif
1456 load = MWasmLoad::New(alloc(), memoryBase, base, *access,
1457 result.toMIRType());
1458 }
1459 if (!load) {
1460 return nullptr;
1461 }
1462 curBlock_->add(load);
1463 return load;
1464 }
1465
1466 void store(MDefinition* base, MemoryAccessDesc* access, MDefinition* v) {
1467 if (inDeadCode()) {
1468 return;
1469 }
1470
1471 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1472 MInstruction* store = nullptr;
1473 if (moduleEnv_.isAsmJS()) {
1474 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"
, 1474); AnnotateMozCrashReason("MOZ_ASSERT" "(" "access->offset64() == 0"
")"); do { *((volatile int*)__null) = 1474; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1475 MWasmLoadInstance* boundsCheckLimit =
1476 maybeLoadBoundsCheckLimit(access->memoryIndex(), MIRType::Int32);
1477 store = MAsmJSStoreHeap::New(alloc(), memoryBase, base, boundsCheckLimit,
1478 access->type(), v);
1479 } else {
1480 checkOffsetAndAlignmentAndBounds(access, &base);
1481#ifndef JS_64BIT1
1482 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"
, 1482); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1482; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1483#endif
1484 store = MWasmStore::New(alloc(), memoryBase, base, *access, v);
1485 }
1486 if (!store) {
1487 return;
1488 }
1489 curBlock_->add(store);
1490 }
1491
1492 MDefinition* atomicCompareExchangeHeap(MDefinition* base,
1493 MemoryAccessDesc* access,
1494 ValType result, MDefinition* oldv,
1495 MDefinition* newv) {
1496 if (inDeadCode()) {
1497 return nullptr;
1498 }
1499
1500 checkOffsetAndAlignmentAndBounds(access, &base);
1501#ifndef JS_64BIT1
1502 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"
, 1502); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1502; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1503#endif
1504
1505 if (isSmallerAccessForI64(result, access)) {
1506 auto* cvtOldv =
1507 MWrapInt64ToInt32::New(alloc(), oldv, /*bottomHalf=*/true);
1508 curBlock_->add(cvtOldv);
1509 oldv = cvtOldv;
1510
1511 auto* cvtNewv =
1512 MWrapInt64ToInt32::New(alloc(), newv, /*bottomHalf=*/true);
1513 curBlock_->add(cvtNewv);
1514 newv = cvtNewv;
1515 }
1516
1517 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1518 MInstruction* cas = MWasmCompareExchangeHeap::New(
1519 alloc(), bytecodeOffset(), memoryBase, base, *access, oldv, newv,
1520 instancePointer_);
1521 if (!cas) {
1522 return nullptr;
1523 }
1524 curBlock_->add(cas);
1525
1526 if (isSmallerAccessForI64(result, access)) {
1527 cas = MExtendInt32ToInt64::New(alloc(), cas, true);
1528 curBlock_->add(cas);
1529 }
1530
1531 return cas;
1532 }
1533
1534 MDefinition* atomicExchangeHeap(MDefinition* base, MemoryAccessDesc* access,
1535 ValType result, MDefinition* value) {
1536 if (inDeadCode()) {
1537 return nullptr;
1538 }
1539
1540 checkOffsetAndAlignmentAndBounds(access, &base);
1541#ifndef JS_64BIT1
1542 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"
, 1542); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1542; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1543#endif
1544
1545 if (isSmallerAccessForI64(result, access)) {
1546 auto* cvtValue =
1547 MWrapInt64ToInt32::New(alloc(), value, /*bottomHalf=*/true);
1548 curBlock_->add(cvtValue);
1549 value = cvtValue;
1550 }
1551
1552 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1553 MInstruction* xchg =
1554 MWasmAtomicExchangeHeap::New(alloc(), bytecodeOffset(), memoryBase,
1555 base, *access, value, instancePointer_);
1556 if (!xchg) {
1557 return nullptr;
1558 }
1559 curBlock_->add(xchg);
1560
1561 if (isSmallerAccessForI64(result, access)) {
1562 xchg = MExtendInt32ToInt64::New(alloc(), xchg, true);
1563 curBlock_->add(xchg);
1564 }
1565
1566 return xchg;
1567 }
1568
1569 MDefinition* atomicBinopHeap(AtomicOp op, MDefinition* base,
1570 MemoryAccessDesc* access, ValType result,
1571 MDefinition* value) {
1572 if (inDeadCode()) {
1573 return nullptr;
1574 }
1575
1576 checkOffsetAndAlignmentAndBounds(access, &base);
1577#ifndef JS_64BIT1
1578 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"
, 1578); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1578; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1579#endif
1580
1581 if (isSmallerAccessForI64(result, access)) {
1582 auto* cvtValue =
1583 MWrapInt64ToInt32::New(alloc(), value, /*bottomHalf=*/true);
1584 curBlock_->add(cvtValue);
1585 value = cvtValue;
1586 }
1587
1588 MDefinition* memoryBase = maybeLoadMemoryBase(access->memoryIndex());
1589 MInstruction* binop =
1590 MWasmAtomicBinopHeap::New(alloc(), bytecodeOffset(), op, memoryBase,
1591 base, *access, value, instancePointer_);
1592 if (!binop) {
1593 return nullptr;
1594 }
1595 curBlock_->add(binop);
1596
1597 if (isSmallerAccessForI64(result, access)) {
1598 binop = MExtendInt32ToInt64::New(alloc(), binop, true);
1599 curBlock_->add(binop);
1600 }
1601
1602 return binop;
1603 }
1604
1605#ifdef ENABLE_WASM_SIMD1
1606 MDefinition* loadSplatSimd128(Scalar::Type viewType,
1607 const LinearMemoryAddress<MDefinition*>& addr,
1608 wasm::SimdOp splatOp) {
1609 if (inDeadCode()) {
1610 return nullptr;
1611 }
1612
1613 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
1614 bytecodeIfNotAsmJS(),
1615 hugeMemoryEnabled(addr.memoryIndex));
1616
1617 // Generate better code (on x86)
1618 // If AVX2 is enabled, more broadcast operators are available.
1619 if (viewType == Scalar::Float64
1620# if defined(JS_CODEGEN_X641) || defined(JS_CODEGEN_X86)
1621 || (js::jit::CPUInfo::IsAVX2Present() &&
1622 (viewType == Scalar::Uint8 || viewType == Scalar::Uint16 ||
1623 viewType == Scalar::Float32))
1624# endif
1625 ) {
1626 access.setSplatSimd128Load();
1627 return load(addr.base, &access, ValType::V128);
1628 }
1629
1630 ValType resultType = ValType::I32;
1631 if (viewType == Scalar::Float32) {
1632 resultType = ValType::F32;
1633 splatOp = wasm::SimdOp::F32x4Splat;
1634 }
1635 auto* scalar = load(addr.base, &access, resultType);
1636 if (!inDeadCode() && !scalar) {
1637 return nullptr;
1638 }
1639 return scalarToSimd128(scalar, splatOp);
1640 }
1641
1642 MDefinition* loadExtendSimd128(const LinearMemoryAddress<MDefinition*>& addr,
1643 wasm::SimdOp op) {
1644 if (inDeadCode()) {
1645 return nullptr;
1646 }
1647
1648 // Generate better code (on x86) by loading as a double with an
1649 // operation that sign extends directly.
1650 MemoryAccessDesc access(addr.memoryIndex, Scalar::Float64, addr.align,
1651 addr.offset, bytecodeIfNotAsmJS(),
1652 hugeMemoryEnabled(addr.memoryIndex));
1653 access.setWidenSimd128Load(op);
1654 return load(addr.base, &access, ValType::V128);
1655 }
1656
1657 MDefinition* loadZeroSimd128(Scalar::Type viewType, size_t numBytes,
1658 const LinearMemoryAddress<MDefinition*>& addr) {
1659 if (inDeadCode()) {
1660 return nullptr;
1661 }
1662
1663 MemoryAccessDesc access(addr.memoryIndex, viewType, addr.align, addr.offset,
1664 bytecodeIfNotAsmJS(),
1665 hugeMemoryEnabled(addr.memoryIndex));
1666 access.setZeroExtendSimd128Load();
1667 return load(addr.base, &access, ValType::V128);
1668 }
1669
1670 MDefinition* loadLaneSimd128(uint32_t laneSize,
1671 const LinearMemoryAddress<MDefinition*>& addr,
1672 uint32_t laneIndex, MDefinition* src) {
1673 if (inDeadCode()) {
1674 return nullptr;
1675 }
1676
1677 MemoryAccessDesc access(addr.memoryIndex, Scalar::Simd128, addr.align,
1678 addr.offset, bytecodeIfNotAsmJS(),
1679 hugeMemoryEnabled(addr.memoryIndex));
1680 MDefinition* memoryBase = maybeLoadMemoryBase(access.memoryIndex());
1681 MDefinition* base = addr.base;
1682 MOZ_ASSERT(!moduleEnv_.isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!moduleEnv_.isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!moduleEnv_.isAsmJS()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!moduleEnv_.isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1682); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!moduleEnv_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1682; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1683 checkOffsetAndAlignmentAndBounds(&access, &base);
1684# ifndef JS_64BIT1
1685 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"
, 1685); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1685; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1686# endif
1687 MInstruction* load = MWasmLoadLaneSimd128::New(
1688 alloc(), memoryBase, base, access, laneSize, laneIndex, src);
1689 if (!load) {
1690 return nullptr;
1691 }
1692 curBlock_->add(load);
1693 return load;
1694 }
1695
1696 void storeLaneSimd128(uint32_t laneSize,
1697 const LinearMemoryAddress<MDefinition*>& addr,
1698 uint32_t laneIndex, MDefinition* src) {
1699 if (inDeadCode()) {
1700 return;
1701 }
1702 MemoryAccessDesc access(addr.memoryIndex, Scalar::Simd128, addr.align,
1703 addr.offset, bytecodeIfNotAsmJS(),
1704 hugeMemoryEnabled(addr.memoryIndex));
1705 MDefinition* memoryBase = maybeLoadMemoryBase(access.memoryIndex());
1706 MDefinition* base = addr.base;
1707 MOZ_ASSERT(!moduleEnv_.isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!moduleEnv_.isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!moduleEnv_.isAsmJS()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!moduleEnv_.isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 1707); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!moduleEnv_.isAsmJS()"
")"); do { *((volatile int*)__null) = 1707; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1708 checkOffsetAndAlignmentAndBounds(&access, &base);
1709# ifndef JS_64BIT1
1710 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"
, 1710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 1710; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1711# endif
1712 MInstruction* store = MWasmStoreLaneSimd128::New(
1713 alloc(), memoryBase, base, access, laneSize, laneIndex, src);
1714 if (!store) {
1715 return;
1716 }
1717 curBlock_->add(store);
1718 }
1719#endif // ENABLE_WASM_SIMD
1720
1721 /************************************************ Global variable accesses */
1722
1723 MDefinition* loadGlobalVar(unsigned instanceDataOffset, bool isConst,
1724 bool isIndirect, MIRType type) {
1725 if (inDeadCode()) {
1726 return nullptr;
1727 }
1728
1729 MInstruction* load;
1730 if (isIndirect) {
1731 // Pull a pointer to the value out of Instance::globalArea, then
1732 // load from that pointer. Note that the pointer is immutable
1733 // even though the value it points at may change, hence the use of
1734 // |true| for the first node's |isConst| value, irrespective of
1735 // the |isConst| formal parameter to this method. The latter
1736 // applies to the denoted value as a whole.
1737 auto* cellPtr = MWasmLoadInstanceDataField::New(
1738 alloc(), MIRType::Pointer, instanceDataOffset,
1739 /*isConst=*/true, instancePointer_);
1740 curBlock_->add(cellPtr);
1741 load = MWasmLoadGlobalCell::New(alloc(), type, cellPtr);
1742 } else {
1743 // Pull the value directly out of Instance::globalArea.
1744 load = MWasmLoadInstanceDataField::New(alloc(), type, instanceDataOffset,
1745 isConst, instancePointer_);
1746 }
1747 curBlock_->add(load);
1748 return load;
1749 }
1750
1751 [[nodiscard]] bool storeGlobalVar(uint32_t lineOrBytecode,
1752 uint32_t instanceDataOffset,
1753 bool isIndirect, MDefinition* v) {
1754 if (inDeadCode()) {
1755 return true;
1756 }
1757
1758 if (isIndirect) {
1759 // Pull a pointer to the value out of Instance::globalArea, then
1760 // store through that pointer.
1761 auto* valueAddr = MWasmLoadInstanceDataField::New(
1762 alloc(), MIRType::Pointer, instanceDataOffset,
1763 /*isConst=*/true, instancePointer_);
1764 curBlock_->add(valueAddr);
1765
1766 // Handle a store to a ref-typed field specially
1767 if (v->type() == MIRType::WasmAnyRef) {
1768 // Load the previous value for the post-write barrier
1769 auto* prevValue =
1770 MWasmLoadGlobalCell::New(alloc(), MIRType::WasmAnyRef, valueAddr);
1771 curBlock_->add(prevValue);
1772
1773 // Store the new value
1774 auto* store =
1775 MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
1776 /*valueOffset=*/0, v, AliasSet::WasmGlobalCell,
1777 WasmPreBarrierKind::Normal);
1778 curBlock_->add(store);
1779
1780 // Call the post-write barrier
1781 return postBarrierPrecise(lineOrBytecode, valueAddr, prevValue);
1782 }
1783
1784 auto* store = MWasmStoreGlobalCell::New(alloc(), v, valueAddr);
1785 curBlock_->add(store);
1786 return true;
1787 }
1788 // Or else store the value directly in Instance::globalArea.
1789
1790 // Handle a store to a ref-typed field specially
1791 if (v->type() == MIRType::WasmAnyRef) {
1792 // Compute the address of the ref-typed global
1793 auto* valueAddr = MWasmDerivedPointer::New(
1794 alloc(), instancePointer_,
1795 wasm::Instance::offsetInData(instanceDataOffset));
1796 curBlock_->add(valueAddr);
1797
1798 // Load the previous value for the post-write barrier
1799 auto* prevValue =
1800 MWasmLoadGlobalCell::New(alloc(), MIRType::WasmAnyRef, valueAddr);
1801 curBlock_->add(prevValue);
1802
1803 // Store the new value
1804 auto* store =
1805 MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
1806 /*valueOffset=*/0, v, AliasSet::WasmInstanceData,
1807 WasmPreBarrierKind::Normal);
1808 curBlock_->add(store);
1809
1810 // Call the post-write barrier
1811 return postBarrierPrecise(lineOrBytecode, valueAddr, prevValue);
1812 }
1813
1814 auto* store = MWasmStoreInstanceDataField::New(alloc(), instanceDataOffset,
1815 v, instancePointer_);
1816 curBlock_->add(store);
1817 return true;
1818 }
1819
1820 MDefinition* loadTableField(uint32_t tableIndex, unsigned fieldOffset,
1821 MIRType type) {
1822 uint32_t instanceDataOffset = wasm::Instance::offsetInData(
1823 moduleEnv_.offsetOfTableInstanceData(tableIndex) + fieldOffset);
1824 auto* load =
1825 MWasmLoadInstance::New(alloc(), instancePointer_, instanceDataOffset,
1826 type, AliasSet::Load(AliasSet::WasmTableMeta));
1827 curBlock_->add(load);
1828 return load;
1829 }
1830
1831 MDefinition* loadTableLength(uint32_t tableIndex) {
1832 return loadTableField(tableIndex, offsetof(TableInstanceData, length)__builtin_offsetof(TableInstanceData, length),
1833 MIRType::Int32);
1834 }
1835
1836 MDefinition* loadTableElements(uint32_t tableIndex) {
1837 return loadTableField(tableIndex, offsetof(TableInstanceData, elements)__builtin_offsetof(TableInstanceData, elements),
1838 MIRType::Pointer);
1839 }
1840
1841 MDefinition* tableGetAnyRef(uint32_t tableIndex, MDefinition* index) {
1842 // Load the table length and perform a bounds check with spectre index
1843 // masking
1844 auto* length = loadTableLength(tableIndex);
1845 auto* check = MWasmBoundsCheck::New(
1846 alloc(), index, length, bytecodeOffset(), MWasmBoundsCheck::Unknown);
1847 curBlock_->add(check);
1848 if (JitOptions.spectreIndexMasking) {
1849 index = check;
1850 }
1851
1852 // Load the table elements and load the element
1853 auto* elements = loadTableElements(tableIndex);
1854 auto* element = MWasmLoadTableElement::New(alloc(), elements, index);
1855 curBlock_->add(element);
1856 return element;
1857 }
1858
1859 [[nodiscard]] bool tableSetAnyRef(uint32_t tableIndex, MDefinition* index,
1860 MDefinition* value,
1861 uint32_t lineOrBytecode) {
1862 // Load the table length and perform a bounds check with spectre index
1863 // masking
1864 auto* length = loadTableLength(tableIndex);
1865 auto* check = MWasmBoundsCheck::New(
1866 alloc(), index, length, bytecodeOffset(), MWasmBoundsCheck::Unknown);
1867 curBlock_->add(check);
1868 if (JitOptions.spectreIndexMasking) {
1869 index = check;
1870 }
1871
1872 // Load the table elements
1873 auto* elements = loadTableElements(tableIndex);
1874
1875 // Load the previous value
1876 auto* prevValue = MWasmLoadTableElement::New(alloc(), elements, index);
1877 curBlock_->add(prevValue);
1878
1879 // Compute the value's location for the post barrier
1880 auto* loc =
1881 MWasmDerivedIndexPointer::New(alloc(), elements, index, ScalePointer);
1882 curBlock_->add(loc);
1883
1884 // Store the new value
1885 auto* store = MWasmStoreRef::New(
1886 alloc(), instancePointer_, loc, /*valueOffset=*/0, value,
1887 AliasSet::WasmTableElement, WasmPreBarrierKind::Normal);
1888 curBlock_->add(store);
1889
1890 // Perform the post barrier
1891 return postBarrierPrecise(lineOrBytecode, loc, prevValue);
1892 }
1893
1894 void addInterruptCheck() {
1895 if (inDeadCode()) {
1896 return;
1897 }
1898 curBlock_->add(
1899 MWasmInterruptCheck::New(alloc(), instancePointer_, bytecodeOffset()));
1900 }
1901
1902 // Perform a post-write barrier to update the generational store buffer. This
1903 // version will remove a previous store buffer entry if it is no longer
1904 // needed.
1905 [[nodiscard]] bool postBarrierPrecise(uint32_t lineOrBytecode,
1906 MDefinition* valueAddr,
1907 MDefinition* value) {
1908 return emitInstanceCall2(lineOrBytecode, SASigPostBarrierPrecise, valueAddr,
1909 value);
1910 }
1911
1912 // Perform a post-write barrier to update the generational store buffer. This
1913 // version will remove a previous store buffer entry if it is no longer
1914 // needed.
1915 [[nodiscard]] bool postBarrierPreciseWithOffset(uint32_t lineOrBytecode,
1916 MDefinition* valueBase,
1917 uint32_t valueOffset,
1918 MDefinition* value) {
1919 MDefinition* valueOffsetDef = constantI32(int32_t(valueOffset));
1920 if (!valueOffsetDef) {
1921 return false;
1922 }
1923 return emitInstanceCall3(lineOrBytecode, SASigPostBarrierPreciseWithOffset,
1924 valueBase, valueOffsetDef, value);
1925 }
1926
1927 // Perform a post-write barrier to update the generational store buffer. This
1928 // version is the most efficient and only requires the address to store the
1929 // value and the new value. It does not remove a previous store buffer entry
1930 // if it is no longer needed, you must use a precise post-write barrier for
1931 // that.
1932 [[nodiscard]] bool postBarrierImmediate(uint32_t lineOrBytecode,
1933 MDefinition* object,
1934 MDefinition* valueBase,
1935 uint32_t valueOffset,
1936 MDefinition* newValue) {
1937 auto* barrier = MWasmPostWriteBarrierImmediate::New(
1938 alloc(), instancePointer_, object, valueBase, valueOffset, newValue);
1939 if (!barrier) {
1940 return false;
1941 }
1942 curBlock_->add(barrier);
1943 return true;
1944 }
1945
1946 [[nodiscard]] bool postBarrierIndex(uint32_t lineOrBytecode,
1947 MDefinition* object,
1948 MDefinition* valueBase,
1949 MDefinition* index, uint32_t scale,
1950 MDefinition* newValue) {
1951 auto* barrier = MWasmPostWriteBarrierIndex::New(
1952 alloc(), instancePointer_, object, valueBase, index, scale, newValue);
1953 if (!barrier) {
1954 return false;
1955 }
1956 curBlock_->add(barrier);
1957 return true;
1958 }
1959
1960 /***************************************************************** Calls */
1961
1962 // The IonMonkey backend maintains a single stack offset (from the stack
1963 // pointer to the base of the frame) by adding the total amount of spill
1964 // space required plus the maximum stack required for argument passing.
1965 // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must
1966 // manually accumulate, for the entire function, the maximum required stack
1967 // space for argument passing. (This is passed to the CodeGenerator via
1968 // MIRGenerator::maxWasmStackArgBytes.) This is just be the maximum of the
1969 // stack space required for each individual call (as determined by the call
1970 // ABI).
1971
1972 // Operations that modify a CallCompileState.
1973
1974 [[nodiscard]] bool passInstance(MIRType instanceType,
1975 CallCompileState* args) {
1976 if (inDeadCode()) {
1977 return true;
1978 }
1979
1980 // Should only pass an instance once. And it must be a non-GC pointer.
1981 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"
, 1981); AnnotateMozCrashReason("MOZ_ASSERT" "(" "args->instanceArg_ == ABIArg()"
")"); do { *((volatile int*)__null) = 1981; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1982 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"
, 1982); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instanceType == MIRType::Pointer"
")"); do { *((volatile int*)__null) = 1982; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1983 args->instanceArg_ = args->abi_.next(MIRType::Pointer);
1984 return true;
1985 }
1986
1987 // Do not call this directly. Call one of the passArg() variants instead.
1988 [[nodiscard]] bool passArgWorker(MDefinition* argDef, MIRType type,
1989 CallCompileState* call) {
1990 ABIArg arg = call->abi_.next(type);
1991 switch (arg.kind()) {
1992#ifdef JS_CODEGEN_REGISTER_PAIR
1993 case ABIArg::GPR_PAIR: {
1994 auto mirLow =
1995 MWrapInt64ToInt32::New(alloc(), argDef, /* bottomHalf = */ true);
1996 curBlock_->add(mirLow);
1997 auto mirHigh =
1998 MWrapInt64ToInt32::New(alloc(), argDef, /* bottomHalf = */ false);
1999 curBlock_->add(mirHigh);
2000 return call->regArgs_.append(
2001 MWasmCallBase::Arg(AnyRegister(arg.gpr64().low), mirLow)) &&
2002 call->regArgs_.append(
2003 MWasmCallBase::Arg(AnyRegister(arg.gpr64().high), mirHigh));
2004 }
2005#endif
2006 case ABIArg::GPR:
2007 case ABIArg::FPU:
2008 return call->regArgs_.append(MWasmCallBase::Arg(arg.reg(), argDef));
2009 case ABIArg::Stack: {
2010 auto* mir =
2011 MWasmStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
2012 curBlock_->add(mir);
2013 return true;
2014 }
2015 case ABIArg::Uninitialized:
2016 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"
, 2016); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Uninitialized ABIArg kind" ")");
do { *((volatile int*)__null) = 2016; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2017 }
2018 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"
, 2018); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown ABIArg kind."
")"); do { *((volatile int*)__null) = 2018; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2019 }
2020
2021 template <typename VecT>
2022 [[nodiscard]] bool passArgs(const DefVector& argDefs, const VecT& types,
2023 CallCompileState* call) {
2024 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"
, 2024); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argDefs.length() == types.length()"
")"); do { *((volatile int*)__null) = 2024; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2025 for (uint32_t i = 0; i < argDefs.length(); i++) {
2026 MDefinition* def = argDefs[i];
2027 ValType type = types[i];
2028 if (!passArg(def, type, call)) {
2029 return false;
2030 }
2031 }
2032 return true;
2033 }
2034
2035 [[nodiscard]] bool passArg(MDefinition* argDef, MIRType type,
2036 CallCompileState* call) {
2037 if (inDeadCode()) {
2038 return true;
2039 }
2040 return passArgWorker(argDef, type, call);
2041 }
2042
2043 [[nodiscard]] bool passArg(MDefinition* argDef, ValType type,
2044 CallCompileState* call) {
2045 if (inDeadCode()) {
2046 return true;
2047 }
2048 return passArgWorker(argDef, type.toMIRType(), call);
2049 }
2050
2051 void markReturnCall(CallCompileState* call) { call->returnCall = true; }
2052
2053 // If the call returns results on the stack, prepare a stack area to receive
2054 // them, and pass the address of the stack area to the callee as an additional
2055 // argument.
2056 [[nodiscard]] bool passStackResultAreaCallArg(const ResultType& resultType,
2057 CallCompileState* call) {
2058 if (inDeadCode()) {
2059 return true;
2060 }
2061 ABIResultIter iter(resultType);
2062 while (!iter.done() && iter.cur().inRegister()) {
2063 iter.next();
2064 }
2065 if (iter.done()) {
2066 // No stack results.
2067 return true;
2068 }
2069
2070 auto* stackResultArea = MWasmStackResultArea::New(alloc());
2071 if (!stackResultArea) {
2072 return false;
2073 }
2074 if (!stackResultArea->init(alloc(), iter.remaining())) {
2075 return false;
2076 }
2077 for (uint32_t base = iter.index(); !iter.done(); iter.next()) {
2078 MWasmStackResultArea::StackResult loc(iter.cur().stackOffset(),
2079 iter.cur().type().toMIRType());
2080 stackResultArea->initResult(iter.index() - base, loc);
2081 }
2082 curBlock_->add(stackResultArea);
2083 MDefinition* def = call->returnCall ? (MDefinition*)stackResultPointer_
2084 : (MDefinition*)stackResultArea;
2085 if (!passArg(def, MIRType::Pointer, call)) {
2086 return false;
2087 }
2088 call->stackResultArea_ = stackResultArea;
2089 return true;
2090 }
2091
2092 [[nodiscard]] bool finishCall(CallCompileState* call) {
2093 if (inDeadCode()) {
2094 return true;
2095 }
2096
2097 if (!call->regArgs_.append(
2098 MWasmCallBase::Arg(AnyRegister(InstanceReg), instancePointer_))) {
2099 return false;
2100 }
2101
2102 uint32_t stackBytes = call->abi_.stackBytesConsumedSoFar();
2103
2104 maxStackArgBytes_ = std::max(maxStackArgBytes_, stackBytes);
2105 return true;
2106 }
2107
2108 // Wrappers for creating various kinds of calls.
2109
2110 [[nodiscard]] bool collectUnaryCallResult(MIRType type,
2111 MDefinition** result) {
2112 MInstruction* def;
2113 switch (type) {
2114 case MIRType::Int32:
2115 def = MWasmRegisterResult::New(alloc(), MIRType::Int32, ReturnReg);
2116 break;
2117 case MIRType::Int64:
2118 def = MWasmRegister64Result::New(alloc(), ReturnReg64);
2119 break;
2120 case MIRType::Float32:
2121 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnFloat32Reg);
2122 break;
2123 case MIRType::Double:
2124 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnDoubleReg);
2125 break;
2126#ifdef ENABLE_WASM_SIMD1
2127 case MIRType::Simd128:
2128 def = MWasmFloatRegisterResult::New(alloc(), type, ReturnSimd128Reg);
2129 break;
2130#endif
2131 case MIRType::WasmAnyRef:
2132 def = MWasmRegisterResult::New(alloc(), MIRType::WasmAnyRef, ReturnReg);
2133 break;
2134 default:
2135 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"
, 2135); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected MIRType result for builtin call"
")"); do { *((volatile int*)__null) = 2135; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2136 }
2137
2138 if (!def) {
2139 return false;
2140 }
2141
2142 curBlock_->add(def);
2143 *result = def;
2144
2145 return true;
2146 }
2147
2148 [[nodiscard]] bool collectCallResults(const ResultType& type,
2149 MWasmStackResultArea* stackResultArea,
2150 DefVector* results) {
2151 if (!results->reserve(type.length())) {
2152 return false;
2153 }
2154
2155 // The result iterator goes in the order in which results would be popped
2156 // off; we want the order in which they would be pushed.
2157 ABIResultIter iter(type);
2158 uint32_t stackResultCount = 0;
2159 while (!iter.done()) {
2160 if (iter.cur().onStack()) {
2161 stackResultCount++;
2162 }
2163 iter.next();
2164 }
2165
2166 for (iter.switchToPrev(); !iter.done(); iter.prev()) {
2167 if (!mirGen().ensureBallast()) {
2168 return false;
2169 }
2170 const ABIResult& result = iter.cur();
2171 MInstruction* def;
2172 if (result.inRegister()) {
2173 switch (result.type().kind()) {
2174 case wasm::ValType::I32:
2175 def =
2176 MWasmRegisterResult::New(alloc(), MIRType::Int32, result.gpr());
2177 break;
2178 case wasm::ValType::I64:
2179 def = MWasmRegister64Result::New(alloc(), result.gpr64());
2180 break;
2181 case wasm::ValType::F32:
2182 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Float32,
2183 result.fpr());
2184 break;
2185 case wasm::ValType::F64:
2186 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Double,
2187 result.fpr());
2188 break;
2189 case wasm::ValType::Ref:
2190 def = MWasmRegisterResult::New(alloc(), MIRType::WasmAnyRef,
2191 result.gpr());
2192 break;
2193 case wasm::ValType::V128:
2194#ifdef ENABLE_WASM_SIMD1
2195 def = MWasmFloatRegisterResult::New(alloc(), MIRType::Simd128,
2196 result.fpr());
2197#else
2198 return this->iter().fail("Ion has no SIMD support yet");
2199#endif
2200 }
2201 } else {
2202 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"
, 2202); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultArea"
")"); do { *((volatile int*)__null) = 2202; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2203 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"
, 2203); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackResultCount"
")"); do { *((volatile int*)__null) = 2203; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2204 uint32_t idx = --stackResultCount;
2205 def = MWasmStackResult::New(alloc(), stackResultArea, idx);
2206 }
2207
2208 if (!def) {
2209 return false;
2210 }
2211 curBlock_->add(def);
2212 results->infallibleAppend(def);
2213 }
2214
2215 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"
, 2215); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results->length() == type.length()"
")"); do { *((volatile int*)__null) = 2215; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2216
2217 return true;
2218 }
2219
2220 [[nodiscard]] bool catchableCall(const CallSiteDesc& desc,
2221 const CalleeDesc& callee,
2222 const MWasmCallBase::Args& args,
2223 const ArgTypeVector& argTypes,
2224 MDefinition* indexOrRef = nullptr) {
2225 MWasmCallTryDesc tryDesc;
2226 if (!beginTryCall(&tryDesc)) {
2227 return false;
2228 }
2229
2230 MInstruction* ins;
2231 if (tryDesc.inTry) {
2232 ins = MWasmCallCatchable::New(alloc(), desc, callee, args,
2233 StackArgAreaSizeUnaligned(argTypes),
2234 tryDesc, indexOrRef);
2235 } else {
2236 ins = MWasmCallUncatchable::New(alloc(), desc, callee, args,
2237 StackArgAreaSizeUnaligned(argTypes),
2238 indexOrRef);
2239 }
2240 if (!ins) {
2241 return false;
2242 }
2243 curBlock_->add(ins);
2244
2245 return finishTryCall(&tryDesc);
2246 }
2247
2248 [[nodiscard]] bool callDirect(const FuncType& funcType, uint32_t funcIndex,
2249 uint32_t lineOrBytecode,
2250 const CallCompileState& call,
2251 DefVector* results) {
2252 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"
, 2252); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2252; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2253
2254 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Func);
2255 ResultType resultType = ResultType::Vector(funcType.results());
2256 auto callee = CalleeDesc::function(funcIndex);
2257 ArgTypeVector args(funcType);
2258
2259 if (!catchableCall(desc, callee, call.regArgs_, args)) {
2260 return false;
2261 }
2262 return collectCallResults(resultType, call.stackResultArea_, results);
2263 }
2264
2265 [[nodiscard]] bool returnCallDirect(const FuncType& funcType,
2266 uint32_t funcIndex,
2267 uint32_t lineOrBytecode,
2268 const CallCompileState& call,
2269 DefVector* results) {
2270 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"
, 2270); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2270; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2271
2272 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::ReturnFunc);
2273 auto callee = CalleeDesc::function(funcIndex);
2274 ArgTypeVector args(funcType);
2275
2276 auto ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2277 StackArgAreaSizeUnaligned(args), nullptr);
2278 if (!ins) {
2279 return false;
2280 }
2281 curBlock_->end(ins);
2282 curBlock_ = nullptr;
2283 return true;
2284 }
2285
2286 [[nodiscard]] bool returnCallImport(unsigned globalDataOffset,
2287 uint32_t lineOrBytecode,
2288 const CallCompileState& call,
2289 const FuncType& funcType,
2290 DefVector* results) {
2291 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"
, 2291); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2291; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2292
2293 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Import);
2294 auto callee = CalleeDesc::import(globalDataOffset);
2295 ArgTypeVector args(funcType);
2296
2297 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2298 StackArgAreaSizeUnaligned(args), nullptr);
2299 if (!ins) {
2300 return false;
2301 }
2302 curBlock_->end(ins);
2303 curBlock_ = nullptr;
2304 return true;
2305 }
2306
2307 [[nodiscard]] bool returnCallIndirect(uint32_t funcTypeIndex,
2308 uint32_t tableIndex, MDefinition* index,
2309 uint32_t lineOrBytecode,
2310 const CallCompileState& call,
2311 DefVector* results) {
2312 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"
, 2312); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2312; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2313
2314 const FuncType& funcType = (*moduleEnv_.types)[funcTypeIndex].funcType();
2315 CallIndirectId callIndirectId =
2316 CallIndirectId::forFuncType(moduleEnv_, funcTypeIndex);
2317
2318 CalleeDesc callee;
2319 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"
, 2319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() != CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2319; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2320 const TableDesc& table = moduleEnv_.tables[tableIndex];
2321 callee =
2322 CalleeDesc::wasmTable(moduleEnv_, table, tableIndex, callIndirectId);
2323
2324 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Indirect);
2325 ArgTypeVector args(funcType);
2326
2327 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2328 StackArgAreaSizeUnaligned(args), index);
2329 if (!ins) {
2330 return false;
2331 }
2332 curBlock_->end(ins);
2333 curBlock_ = nullptr;
2334 return true;
2335 }
2336
2337 [[nodiscard]] bool callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex,
2338 MDefinition* index, uint32_t lineOrBytecode,
2339 const CallCompileState& call,
2340 DefVector* results) {
2341 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"
, 2341); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2341; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2342
2343 const FuncType& funcType = (*moduleEnv_.types)[funcTypeIndex].funcType();
2344 CallIndirectId callIndirectId =
2345 CallIndirectId::forFuncType(moduleEnv_, funcTypeIndex);
2346
2347 CalleeDesc callee;
2348 if (moduleEnv_.isAsmJS()) {
2349 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"
, 2349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "tableIndex == 0"
")"); do { *((volatile int*)__null) = 2349; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2350 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"
, 2350); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() == CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2350; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2351 uint32_t tableIndex = moduleEnv_.asmJSSigToTableIndex[funcTypeIndex];
2352 const TableDesc& table = moduleEnv_.tables[tableIndex];
2353 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"
, 2353); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsPowerOfTwo(table.initialLength)"
")"); do { *((volatile int*)__null) = 2353; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2354
2355 MDefinition* mask = constantI32(int32_t(table.initialLength - 1));
2356 MBitAnd* maskedIndex = MBitAnd::New(alloc(), index, mask, MIRType::Int32);
2357 curBlock_->add(maskedIndex);
2358
2359 index = maskedIndex;
2360 callee = CalleeDesc::asmJSTable(moduleEnv_, tableIndex);
2361 } else {
2362 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"
, 2362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callIndirectId.kind() != CallIndirectIdKind::AsmJS"
")"); do { *((volatile int*)__null) = 2362; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2363 const TableDesc& table = moduleEnv_.tables[tableIndex];
2364 callee =
2365 CalleeDesc::wasmTable(moduleEnv_, table, tableIndex, callIndirectId);
2366 }
2367
2368 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Indirect);
2369 ArgTypeVector args(funcType);
2370 ResultType resultType = ResultType::Vector(funcType.results());
2371
2372 if (!catchableCall(desc, callee, call.regArgs_, args, index)) {
2373 return false;
2374 }
2375 return collectCallResults(resultType, call.stackResultArea_, results);
2376 }
2377
2378 [[nodiscard]] bool callImport(unsigned instanceDataOffset,
2379 uint32_t lineOrBytecode,
2380 const CallCompileState& call,
2381 const FuncType& funcType, DefVector* results) {
2382 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"
, 2382); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2382; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2383
2384 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Import);
2385 auto callee = CalleeDesc::import(instanceDataOffset);
2386 ArgTypeVector args(funcType);
2387 ResultType resultType = ResultType::Vector(funcType.results());
2388
2389 if (!catchableCall(desc, callee, call.regArgs_, args)) {
2390 return false;
2391 }
2392 return collectCallResults(resultType, call.stackResultArea_, results);
2393 }
2394
2395 [[nodiscard]] bool builtinCall(const SymbolicAddressSignature& builtin,
2396 uint32_t lineOrBytecode,
2397 const CallCompileState& call,
2398 MDefinition** def) {
2399 if (inDeadCode()) {
2400 *def = nullptr;
2401 return true;
2402 }
2403
2404 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"
, 2404); AnnotateMozCrashReason("MOZ_ASSERT" "(" "builtin.failureMode == FailureMode::Infallible"
")"); do { *((volatile int*)__null) = 2404; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2405
2406 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
2407 auto callee = CalleeDesc::builtin(builtin.identity);
2408 auto* ins = MWasmCallUncatchable::New(alloc(), desc, callee, call.regArgs_,
2409 StackArgAreaSizeUnaligned(builtin));
2410 if (!ins) {
2411 return false;
2412 }
2413
2414 curBlock_->add(ins);
2415
2416 return collectUnaryCallResult(builtin.retType, def);
2417 }
2418
2419 [[nodiscard]] bool builtinInstanceMethodCall(
2420 const SymbolicAddressSignature& builtin, uint32_t lineOrBytecode,
2421 const CallCompileState& call, MDefinition** def = nullptr) {
2422 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"
, 2422); AnnotateMozCrashReason("MOZ_ASSERT" "(" "builtin.retType == MIRType::None"
")"); do { *((volatile int*)__null) = 2422; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2423 if (inDeadCode()) {
2424 if (def) {
2425 *def = nullptr;
2426 }
2427 return true;
2428 }
2429
2430 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
2431 MWasmCallTryDesc tryDesc;
2432 if (!beginTryCall(&tryDesc)) {
2433 return false;
2434 }
2435
2436 MInstruction* ins;
2437 if (tryDesc.inTry) {
2438 ins = MWasmCallCatchable::NewBuiltinInstanceMethodCall(
2439 alloc(), desc, builtin.identity, builtin.failureMode,
2440 call.instanceArg_, call.regArgs_, StackArgAreaSizeUnaligned(builtin),
2441 tryDesc);
2442 } else {
2443 ins = MWasmCallUncatchable::NewBuiltinInstanceMethodCall(
2444 alloc(), desc, builtin.identity, builtin.failureMode,
2445 call.instanceArg_, call.regArgs_, StackArgAreaSizeUnaligned(builtin));
2446 }
2447 if (!ins) {
2448 return false;
2449 }
2450 curBlock_->add(ins);
2451
2452 if (!finishTryCall(&tryDesc)) {
2453 return false;
2454 }
2455
2456 if (!def) {
2457 return true;
2458 }
2459 return collectUnaryCallResult(builtin.retType, def);
2460 }
2461
2462 [[nodiscard]] bool stackSwitch(MDefinition* suspender, MDefinition* fn,
2463 MDefinition* data, StackSwitchKind kind) {
2464 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"
, 2464); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2464; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2465
2466 MInstruction* ins;
2467 switch (kind) {
2468 case StackSwitchKind::SwitchToMain:
2469 ins = MWasmStackSwitchToMain::New(alloc(), suspender, fn, data);
2470 break;
2471 case StackSwitchKind::SwitchToSuspendable:
2472 ins = MWasmStackSwitchToSuspendable::New(alloc(), suspender, fn, data);
2473 break;
2474 case StackSwitchKind::ContinueOnSuspendable:
2475 ins = MWasmStackContinueOnSuspendable::New(alloc(), suspender);
2476 break;
2477 }
2478 if (!ins) {
2479 return false;
2480 }
2481
2482 curBlock_->add(ins);
2483
2484 return true;
2485 }
2486
2487#ifdef ENABLE_WASM_GC1
2488 [[nodiscard]] bool callRef(const FuncType& funcType, MDefinition* ref,
2489 uint32_t lineOrBytecode,
2490 const CallCompileState& call, DefVector* results) {
2491 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"
, 2491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2491; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2492
2493 CalleeDesc callee = CalleeDesc::wasmFuncRef();
2494
2495 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::FuncRef);
2496 ArgTypeVector args(funcType);
2497 ResultType resultType = ResultType::Vector(funcType.results());
2498
2499 if (!catchableCall(desc, callee, call.regArgs_, args, ref)) {
2500 return false;
2501 }
2502 return collectCallResults(resultType, call.stackResultArea_, results);
2503 }
2504
2505# ifdef ENABLE_WASM_TAIL_CALLS1
2506 [[nodiscard]] bool returnCallRef(const FuncType& funcType, MDefinition* ref,
2507 uint32_t lineOrBytecode,
2508 const CallCompileState& call,
2509 DefVector* results) {
2510 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"
, 2510); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!inDeadCode()"
")"); do { *((volatile int*)__null) = 2510; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2511
2512 CalleeDesc callee = CalleeDesc::wasmFuncRef();
2513
2514 CallSiteDesc desc(lineOrBytecode, CallSiteDesc::FuncRef);
2515 ArgTypeVector args(funcType);
2516
2517 auto* ins = MWasmReturnCall::New(alloc(), desc, callee, call.regArgs_,
2518 StackArgAreaSizeUnaligned(args), ref);
2519 if (!ins) {
2520 return false;
2521 }
2522 curBlock_->end(ins);
2523 curBlock_ = nullptr;
2524 return true;
2525 }
2526
2527# endif // ENABLE_WASM_TAIL_CALLS
2528
2529#endif // ENABLE_WASM_GC
2530
2531 /*********************************************** Control flow generation */
2532
2533 inline bool inDeadCode() const { return curBlock_ == nullptr; }
2534
2535 [[nodiscard]] bool returnValues(const DefVector& values) {
2536 if (inDeadCode()) {
2537 return true;
2538 }
2539
2540 if (values.empty()) {
2541 curBlock_->end(MWasmReturnVoid::New(alloc(), instancePointer_));
2542 } else {
2543 ResultType resultType = ResultType::Vector(funcType().results());
2544 ABIResultIter iter(resultType);
2545 // Switch to iterate in FIFO order instead of the default LIFO.
2546 while (!iter.done()) {
2547 iter.next();
2548 }
2549 iter.switchToPrev();
2550 for (uint32_t i = 0; !iter.done(); iter.prev(), i++) {
2551 if (!mirGen().ensureBallast()) {
2552 return false;
2553 }
2554 const ABIResult& result = iter.cur();
2555 if (result.onStack()) {
2556 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"
, 2556); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.remaining() > 1"
")"); do { *((volatile int*)__null) = 2556; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2557 if (result.type().isRefRepr()) {
2558 auto* store = MWasmStoreRef::New(
2559 alloc(), instancePointer_, stackResultPointer_,
2560 result.stackOffset(), values[i], AliasSet::WasmStackResult,
2561 WasmPreBarrierKind::None);
2562 curBlock_->add(store);
2563 } else {
2564 auto* store = MWasmStoreStackResult::New(
2565 alloc(), stackResultPointer_, result.stackOffset(), values[i]);
2566 curBlock_->add(store);
2567 }
2568 } else {
2569 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"
, 2569); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.remaining() == 1"
")"); do { *((volatile int*)__null) = 2569; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2570 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"
, 2570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "i + 1 == values.length()"
")"); do { *((volatile int*)__null) = 2570; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2571 curBlock_->end(
2572 MWasmReturn::New(alloc(), values[i], instancePointer_));
2573 }
2574 }
2575 }
2576 curBlock_ = nullptr;
2577 return true;
2578 }
2579
2580 void unreachableTrap() {
2581 if (inDeadCode()) {
2582 return;
2583 }
2584
2585 auto* ins =
2586 MWasmTrap::New(alloc(), wasm::Trap::Unreachable, bytecodeOffset());
2587 curBlock_->end(ins);
2588 curBlock_ = nullptr;
2589 }
2590
2591 private:
2592 static uint32_t numPushed(MBasicBlock* block) {
2593 return block->stackDepth() - block->info().firstStackSlot();
2594 }
2595
2596 public:
2597 [[nodiscard]] bool pushDefs(const DefVector& defs) {
2598 if (inDeadCode()) {
2599 return true;
2600 }
2601 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"
, 2601); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 2601; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2602 if (!curBlock_->ensureHasSlots(defs.length())) {
2603 return false;
2604 }
2605 for (MDefinition* def : defs) {
2606 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"
, 2606); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() != MIRType::None"
")"); do { *((volatile int*)__null) = 2606; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2607 curBlock_->push(def);
2608 }
2609 return true;
2610 }
2611
2612 [[nodiscard]] bool popPushedDefs(DefVector* defs) {
2613 size_t n = numPushed(curBlock_);
2614 if (!defs->resizeUninitialized(n)) {
2615 return false;
2616 }
2617 for (; n > 0; n--) {
2618 MDefinition* def = curBlock_->pop();
2619 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"
, 2619); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() != MIRType::Value"
")"); do { *((volatile int*)__null) = 2619; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2620 (*defs)[n - 1] = def;
2621 }
2622 return true;
2623 }
2624
2625 private:
2626 [[nodiscard]] bool addJoinPredecessor(const DefVector& defs,
2627 MBasicBlock** joinPred) {
2628 *joinPred = curBlock_;
2629 if (inDeadCode()) {
2630 return true;
2631 }
2632 return pushDefs(defs);
2633 }
2634
2635 public:
2636 [[nodiscard]] bool branchAndStartThen(MDefinition* cond,
2637 MBasicBlock** elseBlock) {
2638 if (inDeadCode()) {
2639 *elseBlock = nullptr;
2640 } else {
2641 MBasicBlock* thenBlock;
2642 if (!newBlock(curBlock_, &thenBlock)) {
2643 return false;
2644 }
2645 if (!newBlock(curBlock_, elseBlock)) {
2646 return false;
2647 }
2648
2649 curBlock_->end(MTest::New(alloc(), cond, thenBlock, *elseBlock));
2650
2651 curBlock_ = thenBlock;
2652 mirGraph().moveBlockToEnd(curBlock_);
2653 }
2654
2655 return startBlock();
2656 }
2657
2658 [[nodiscard]] bool switchToElse(MBasicBlock* elseBlock,
2659 MBasicBlock** thenJoinPred) {
2660 DefVector values;
2661 if (!finishBlock(&values)) {
2662 return false;
2663 }
2664
2665 if (!elseBlock) {
2666 *thenJoinPred = nullptr;
2667 } else {
2668 if (!addJoinPredecessor(values, thenJoinPred)) {
2669 return false;
2670 }
2671
2672 curBlock_ = elseBlock;
2673 mirGraph().moveBlockToEnd(curBlock_);
2674 }
2675
2676 return startBlock();
2677 }
2678
2679 [[nodiscard]] bool joinIfElse(MBasicBlock* thenJoinPred, DefVector* defs) {
2680 DefVector values;
2681 if (!finishBlock(&values)) {
2682 return false;
2683 }
2684
2685 if (!thenJoinPred && inDeadCode()) {
2686 return true;
2687 }
2688
2689 MBasicBlock* elseJoinPred;
2690 if (!addJoinPredecessor(values, &elseJoinPred)) {
2691 return false;
2692 }
2693
2694 mozilla::Array<MBasicBlock*, 2> blocks;
2695 size_t numJoinPreds = 0;
2696 if (thenJoinPred) {
2697 blocks[numJoinPreds++] = thenJoinPred;
2698 }
2699 if (elseJoinPred) {
2700 blocks[numJoinPreds++] = elseJoinPred;
2701 }
2702
2703 if (numJoinPreds == 0) {
2704 return true;
2705 }
2706
2707 MBasicBlock* join;
2708 if (!goToNewBlock(blocks[0], &join)) {
2709 return false;
2710 }
2711 for (size_t i = 1; i < numJoinPreds; ++i) {
2712 if (!goToExistingBlock(blocks[i], join)) {
2713 return false;
2714 }
2715 }
2716
2717 curBlock_ = join;
2718 return popPushedDefs(defs);
2719 }
2720
2721 [[nodiscard]] bool startBlock() {
2722 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"
, 2723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pendingBlocks_[blockDepth_].patches.empty()"
")"); do { *((volatile int*)__null) = 2723; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
2723 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"
, 2723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pendingBlocks_[blockDepth_].patches.empty()"
")"); do { *((volatile int*)__null) = 2723; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2724 blockDepth_++;
2725 return true;
2726 }
2727
2728 [[nodiscard]] bool finishBlock(DefVector* defs) {
2729 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"
, 2729); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_"
")"); do { *((volatile int*)__null) = 2729; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2730 uint32_t topLabel = --blockDepth_;
2731 return bindBranches(topLabel, defs);
2732 }
2733
2734 [[nodiscard]] bool startLoop(MBasicBlock** loopHeader, size_t paramCount) {
2735 *loopHeader = nullptr;
2736
2737 blockDepth_++;
2738 loopDepth_++;
2739
2740 if (inDeadCode()) {
2741 return true;
2742 }
2743
2744 // Create the loop header.
2745 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"
, 2745); AnnotateMozCrashReason("MOZ_ASSERT" "(" "curBlock_->loopDepth() == loopDepth_ - 1"
")"); do { *((volatile int*)__null) = 2745; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2746 *loopHeader = MBasicBlock::New(mirGraph(), info(), curBlock_,
2747 MBasicBlock::PENDING_LOOP_HEADER);
2748 if (!*loopHeader) {
2749 return false;
2750 }
2751
2752 (*loopHeader)->setLoopDepth(loopDepth_);
2753 mirGraph().addBlock(*loopHeader);
2754 curBlock_->end(MGoto::New(alloc(), *loopHeader));
2755
2756 DefVector loopParams;
2757 if (!iter().getResults(paramCount, &loopParams)) {
2758 return false;
2759 }
2760 for (size_t i = 0; i < paramCount; i++) {
2761 MPhi* phi = MPhi::New(alloc(), loopParams[i]->type());
2762 if (!phi) {
2763 return false;
2764 }
2765 if (!phi->reserveLength(2)) {
2766 return false;
2767 }
2768 (*loopHeader)->addPhi(phi);
2769 phi->addInput(loopParams[i]);
2770 loopParams[i] = phi;
2771 }
2772 iter().setResults(paramCount, loopParams);
2773
2774 MBasicBlock* body;
2775 if (!goToNewBlock(*loopHeader, &body)) {
2776 return false;
2777 }
2778 curBlock_ = body;
2779 return true;
2780 }
2781
2782 private:
2783 void fixupRedundantPhis(MBasicBlock* b) {
2784 for (size_t i = 0, depth = b->stackDepth(); i < depth; i++) {
2785 MDefinition* def = b->getSlot(i);
2786 if (def->isUnused()) {
2787 b->setSlot(i, def->toPhi()->getOperand(0));
2788 }
2789 }
2790 }
2791
2792 [[nodiscard]] bool setLoopBackedge(MBasicBlock* loopEntry,
2793 MBasicBlock* loopBody,
2794 MBasicBlock* backedge, size_t paramCount) {
2795 if (!loopEntry->setBackedgeWasm(backedge, paramCount)) {
2796 return false;
2797 }
2798
2799 // Flag all redundant phis as unused.
2800 for (MPhiIterator phi = loopEntry->phisBegin(); phi != loopEntry->phisEnd();
2801 phi++) {
2802 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"
, 2802); AnnotateMozCrashReason("MOZ_ASSERT" "(" "phi->numOperands() == 2"
")"); do { *((volatile int*)__null) = 2802; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2803 if (phi->getOperand(0) == phi->getOperand(1)) {
2804 phi->setUnused();
2805 }
2806 }
2807
2808 // Fix up phis stored in the slots Vector of pending blocks.
2809 for (PendingBlockTarget& pendingBlockTarget : pendingBlocks_) {
2810 for (ControlFlowPatch& p : pendingBlockTarget.patches) {
2811 MBasicBlock* block = p.ins->block();
2812 if (block->loopDepth() >= loopEntry->loopDepth()) {
2813 fixupRedundantPhis(block);
2814 }
2815 }
2816 }
2817
2818 // The loop body, if any, might be referencing recycled phis too.
2819 if (loopBody) {
2820 fixupRedundantPhis(loopBody);
2821 }
2822
2823 // Pending jumps to an enclosing try-catch may reference the recycled phis.
2824 // We have to search above all enclosing try blocks, as a delegate may move
2825 // patches around.
2826 for (uint32_t depth = 0; depth < iter().controlStackDepth(); depth++) {
2827 LabelKind kind = iter().controlKind(depth);
2828 if (kind != LabelKind::Try && kind != LabelKind::TryTable &&
2829 kind != LabelKind::Body) {
2830 continue;
2831 }
2832 Control& control = iter().controlItem(depth);
2833 if (!control.tryControl) {
2834 continue;
2835 }
2836 for (MControlInstruction* patch : control.tryControl->landingPadPatches) {
2837 MBasicBlock* block = patch->block();
2838 if (block->loopDepth() >= loopEntry->loopDepth()) {
2839 fixupRedundantPhis(block);
2840 }
2841 }
2842 }
2843 for (MControlInstruction* patch : bodyDelegatePadPatches_) {
2844 MBasicBlock* block = patch->block();
2845 if (block->loopDepth() >= loopEntry->loopDepth()) {
2846 fixupRedundantPhis(block);
2847 }
2848 }
2849
2850 // Discard redundant phis and add to the free list.
2851 for (MPhiIterator phi = loopEntry->phisBegin();
2852 phi != loopEntry->phisEnd();) {
2853 MPhi* entryDef = *phi++;
2854 if (!entryDef->isUnused()) {
2855 continue;
2856 }
2857
2858 entryDef->justReplaceAllUsesWith(entryDef->getOperand(0));
2859 loopEntry->discardPhi(entryDef);
2860 mirGraph().addPhiToFreeList(entryDef);
2861 }
2862
2863 return true;
2864 }
2865
2866 public:
2867 [[nodiscard]] bool closeLoop(MBasicBlock* loopHeader,
2868 DefVector* loopResults) {
2869 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"
, 2869); AnnotateMozCrashReason("MOZ_ASSERT" "(" "blockDepth_ >= 1"
")"); do { *((volatile int*)__null) = 2869; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2870 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"
, 2870); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loopDepth_"
")"); do { *((volatile int*)__null) = 2870; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2871
2872 uint32_t headerLabel = blockDepth_ - 1;
2873
2874 if (!loopHeader) {
2875 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"
, 2875); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 2875; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2876 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"
, 2877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
")"); do { *((volatile int*)__null) = 2877; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2877 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"
, 2877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "headerLabel >= pendingBlocks_.length() || pendingBlocks_[headerLabel].patches.empty()"
")"); do { *((volatile int*)__null) = 2877; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2878 blockDepth_--;
2879 loopDepth_--;
2880 return true;
2881 }
2882
2883 // Op::Loop doesn't have an implicit backedge so temporarily set
2884 // aside the end of the loop body to bind backedges.
2885 MBasicBlock* loopBody = curBlock_;
2886 curBlock_ = nullptr;
2887
2888 // As explained in bug 1253544, Ion apparently has an invariant that
2889 // there is only one backedge to loop headers. To handle wasm's ability
2890 // to have multiple backedges to the same loop header, we bind all those
2891 // branches as forward jumps to a single backward jump. This is
2892 // unfortunate but the optimizer is able to fold these into single jumps
2893 // to backedges.
2894 DefVector backedgeValues;
2895 if (!bindBranches(headerLabel, &backedgeValues)) {
2896 return false;
2897 }
2898
2899 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"
, 2899); AnnotateMozCrashReason("MOZ_ASSERT" "(" "loopHeader->loopDepth() == loopDepth_"
")"); do { *((volatile int*)__null) = 2899; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2900
2901 if (curBlock_) {
2902 // We're on the loop backedge block, created by bindBranches.
2903 for (size_t i = 0, n = numPushed(curBlock_); i != n; i++) {
2904 curBlock_->pop();
2905 }
2906
2907 if (!pushDefs(backedgeValues)) {
2908 return false;
2909 }
2910
2911 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"
, 2911); AnnotateMozCrashReason("MOZ_ASSERT" "(" "curBlock_->loopDepth() == loopDepth_"
")"); do { *((volatile int*)__null) = 2911; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2912 curBlock_->end(MGoto::New(alloc(), loopHeader));
2913 if (!setLoopBackedge(loopHeader, loopBody, curBlock_,
2914 backedgeValues.length())) {
2915 return false;
2916 }
2917 }
2918
2919 curBlock_ = loopBody;
2920
2921 loopDepth_--;
2922
2923 // If the loop depth still at the inner loop body, correct it.
2924 if (curBlock_ && curBlock_->loopDepth() != loopDepth_) {
2925 MBasicBlock* out;
2926 if (!goToNewBlock(curBlock_, &out)) {
2927 return false;
2928 }
2929 curBlock_ = out;
2930 }
2931
2932 blockDepth_ -= 1;
2933 return inDeadCode() || popPushedDefs(loopResults);
2934 }
2935
2936 [[nodiscard]] bool addControlFlowPatch(
2937 MControlInstruction* ins, uint32_t relative, uint32_t index,
2938 BranchHint branchHint = BranchHint::Invalid) {
2939 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"
, 2939); AnnotateMozCrashReason("MOZ_ASSERT" "(" "relative < blockDepth_"
")"); do { *((volatile int*)__null) = 2939; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2940 uint32_t absolute = blockDepth_ - 1 - relative;
2941
2942 if (absolute >= pendingBlocks_.length() &&
2943 !pendingBlocks_.resize(absolute + 1)) {
2944 return false;
2945 }
2946
2947 pendingBlocks_[absolute].hint = branchHint;
2948 return pendingBlocks_[absolute].patches.append(
2949 ControlFlowPatch(ins, index));
2950 }
2951
2952 [[nodiscard]] bool br(uint32_t relativeDepth, const DefVector& values) {
2953 if (inDeadCode()) {
2954 return true;
2955 }
2956
2957 MGoto* jump = MGoto::New(alloc());
2958 if (!addControlFlowPatch(jump, relativeDepth, MGoto::TargetIndex)) {
2959 return false;
2960 }
2961
2962 if (!pushDefs(values)) {
2963 return false;
2964 }
2965
2966 curBlock_->end(jump);
2967 curBlock_ = nullptr;
2968 return true;
2969 }
2970
2971 [[nodiscard]] bool brIf(uint32_t relativeDepth, const DefVector& values,
2972 MDefinition* condition, BranchHint branchHint) {
2973 if (inDeadCode()) {
2974 return true;
2975 }
2976
2977 MBasicBlock* joinBlock = nullptr;
2978 if (!newBlock(curBlock_, &joinBlock)) {
2979 return false;
2980 }
2981
2982 MTest* test = MTest::New(alloc(), condition, nullptr, joinBlock);
2983 if (!addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex,
2984 branchHint)) {
2985 return false;
2986 }
2987
2988 if (!pushDefs(values)) {
2989 return false;
2990 }
2991
2992 curBlock_->end(test);
2993 curBlock_ = joinBlock;
2994
2995 return true;
2996 }
2997
2998 [[nodiscard]] bool brTable(MDefinition* operand, uint32_t defaultDepth,
2999 const Uint32Vector& depths,
3000 const DefVector& values) {
3001 if (inDeadCode()) {
3002 return true;
3003 }
3004
3005 size_t numCases = depths.length();
3006 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"
, 3006); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numCases <= (2147483647)"
")"); do { *((volatile int*)__null) = 3006; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3007 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"
, 3007); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numCases" ")"
); do { *((volatile int*)__null) = 3007; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3008
3009 MTableSwitch* table =
3010 MTableSwitch::New(alloc(), operand, 0, int32_t(numCases - 1));
3011
3012 size_t defaultIndex;
3013 if (!table->addDefault(nullptr, &defaultIndex)) {
3014 return false;
3015 }
3016 if (!addControlFlowPatch(table, defaultDepth, defaultIndex)) {
3017 return false;
3018 }
3019
3020 using IndexToCaseMap =
3021 HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>;
3022
3023 IndexToCaseMap indexToCase;
3024 if (!indexToCase.put(defaultDepth, defaultIndex)) {
3025 return false;
3026 }
3027
3028 for (size_t i = 0; i < numCases; i++) {
3029 if (!mirGen_.ensureBallast()) {
3030 return false;
3031 }
3032
3033 uint32_t depth = depths[i];
3034
3035 size_t caseIndex;
3036 IndexToCaseMap::AddPtr p = indexToCase.lookupForAdd(depth);
3037 if (!p) {
3038 if (!table->addSuccessor(nullptr, &caseIndex)) {
3039 return false;
3040 }
3041 if (!addControlFlowPatch(table, depth, caseIndex)) {
3042 return false;
3043 }
3044 if (!indexToCase.add(p, depth, caseIndex)) {
3045 return false;
3046 }
3047 } else {
3048 caseIndex = p->value();
3049 }
3050
3051 if (!table->addCase(caseIndex)) {
3052 return false;
3053 }
3054 }
3055
3056 if (!pushDefs(values)) {
3057 return false;
3058 }
3059
3060 curBlock_->end(table);
3061 curBlock_ = nullptr;
3062
3063 return true;
3064 }
3065
3066 /********************************************************** Exceptions ***/
3067
3068 bool inTryBlockFrom(uint32_t fromRelativeDepth, uint32_t* relativeDepth) {
3069 return iter().controlFindInnermostFrom(
3070 [](LabelKind kind, const Control& control) {
3071 return control.tryControl != nullptr && control.tryControl->inBody;
3072 },
3073 fromRelativeDepth, relativeDepth);
3074 }
3075
3076 bool inTryBlock(uint32_t* relativeDepth) {
3077 return inTryBlockFrom(0, relativeDepth);
3078 }
3079
3080 bool inTryCode() {
3081 uint32_t relativeDepth;
3082 return inTryBlock(&relativeDepth);
3083 }
3084
3085 MDefinition* loadTag(uint32_t tagIndex) {
3086 MWasmLoadInstanceDataField* tag = MWasmLoadInstanceDataField::New(
3087 alloc(), MIRType::WasmAnyRef,
3088 moduleEnv_.offsetOfTagInstanceData(tagIndex), true, instancePointer_);
3089 curBlock_->add(tag);
3090 return tag;
3091 }
3092
3093 void loadPendingExceptionState(MInstruction** exception, MInstruction** tag) {
3094 *exception = MWasmLoadInstance::New(
3095 alloc(), instancePointer_, wasm::Instance::offsetOfPendingException(),
3096 MIRType::WasmAnyRef, AliasSet::Load(AliasSet::WasmPendingException));
3097 curBlock_->add(*exception);
3098
3099 *tag = MWasmLoadInstance::New(
3100 alloc(), instancePointer_,
3101 wasm::Instance::offsetOfPendingExceptionTag(), MIRType::WasmAnyRef,
3102 AliasSet::Load(AliasSet::WasmPendingException));
3103 curBlock_->add(*tag);
3104 }
3105
3106 [[nodiscard]] bool setPendingExceptionState(MDefinition* exception,
3107 MDefinition* tag) {
3108 // Set the pending exception object
3109 auto* exceptionAddr = MWasmDerivedPointer::New(
3110 alloc(), instancePointer_, Instance::offsetOfPendingException());
3111 curBlock_->add(exceptionAddr);
3112 auto* setException = MWasmStoreRef::New(
3113 alloc(), instancePointer_, exceptionAddr, /*valueOffset=*/0, exception,
3114 AliasSet::WasmPendingException, WasmPreBarrierKind::Normal);
3115 curBlock_->add(setException);
3116 if (!postBarrierPrecise(/*lineOrBytecode=*/0, exceptionAddr, exception)) {
3117 return false;
3118 }
3119
3120 // Set the pending exception tag object
3121 auto* exceptionTagAddr = MWasmDerivedPointer::New(
3122 alloc(), instancePointer_, Instance::offsetOfPendingExceptionTag());
3123 curBlock_->add(exceptionTagAddr);
3124 auto* setExceptionTag = MWasmStoreRef::New(
3125 alloc(), instancePointer_, exceptionTagAddr, /*valueOffset=*/0, tag,
3126 AliasSet::WasmPendingException, WasmPreBarrierKind::Normal);
3127 curBlock_->add(setExceptionTag);
3128 return postBarrierPrecise(/*lineOrBytecode=*/0, exceptionTagAddr, tag);
3129 }
3130
3131 [[nodiscard]] bool addPadPatch(MControlInstruction* ins,
3132 size_t relativeTryDepth) {
3133 Control& control = iter().controlItem(relativeTryDepth);
3134 return control.tryControl->landingPadPatches.emplaceBack(ins);
3135 }
3136
3137 [[nodiscard]] bool endWithPadPatch(uint32_t relativeTryDepth) {
3138 MGoto* jumpToLandingPad = MGoto::New(alloc());
3139 curBlock_->end(jumpToLandingPad);
3140 return addPadPatch(jumpToLandingPad, relativeTryDepth);
3141 }
3142
3143 [[nodiscard]] bool delegatePadPatches(const ControlInstructionVector& patches,
3144 uint32_t relativeDepth) {
3145 if (patches.empty()) {
3146 return true;
3147 }
3148
3149 // Find where we are delegating the pad patches to.
3150 ControlInstructionVector* targetPatches;
3151 uint32_t targetRelativeDepth;
3152 if (inTryBlockFrom(relativeDepth, &targetRelativeDepth)) {
3153 targetPatches = &iter()
3154 .controlItem(targetRelativeDepth)
3155 .tryControl->landingPadPatches;
3156 } else {
3157 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"
, 3157); AnnotateMozCrashReason("MOZ_ASSERT" "(" "relativeDepth <= blockDepth_ - 1"
")"); do { *((volatile int*)__null) = 3157; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3158 targetPatches = &bodyDelegatePadPatches_;
3159 }
3160
3161 // Append the delegate's pad patches to the target's.
3162 for (MControlInstruction* ins : patches) {
3163 if (!targetPatches->emplaceBack(ins)) {
3164 return false;
3165 }
3166 }
3167 return true;
3168 }
3169
3170 [[nodiscard]] bool beginTryCall(MWasmCallTryDesc* call) {
3171 call->inTry = inTryBlock(&call->relativeTryDepth);
3172 if (!call->inTry) {
3173 return true;
3174 }
3175 // Allocate a try note
3176 if (!tryNotes_.append(wasm::TryNote())) {
3177 return false;
3178 }
3179 call->tryNoteIndex = tryNotes_.length() - 1;
3180 // Allocate blocks for fallthrough and exceptions
3181 return newBlock(curBlock_, &call->fallthroughBlock) &&
3182 newBlock(curBlock_, &call->prePadBlock);
3183 }
3184
3185 [[nodiscard]] bool finishTryCall(MWasmCallTryDesc* call) {
3186 if (!call->inTry) {
3187 return true;
3188 }
3189
3190 // Switch to the prePadBlock
3191 MBasicBlock* callBlock = curBlock_;
3192 curBlock_ = call->prePadBlock;
3193
3194 // Mark this as the landing pad for the call
3195 curBlock_->add(
3196 MWasmCallLandingPrePad::New(alloc(), callBlock, call->tryNoteIndex));
3197
3198 // End with a pending jump to the landing pad
3199 if (!endWithPadPatch(call->relativeTryDepth)) {
3200 return false;
3201 }
3202
3203 // Compilation continues in the fallthroughBlock.
3204 curBlock_ = call->fallthroughBlock;
3205 return true;
3206 }
3207
3208 // Create a landing pad for a try block if there are any throwing
3209 // instructions. This is also used for the implicit rethrow landing pad used
3210 // for delegate instructions that target the outermost label.
3211 [[nodiscard]] bool createTryLandingPadIfNeeded(
3212 ControlInstructionVector& landingPadPatches, MBasicBlock** landingPad) {
3213 // If there are no pad-patches for this try control, it means there are no
3214 // instructions in the try code that could throw an exception. In this
3215 // case, all the catches are dead code, and the try code ends up equivalent
3216 // to a plain wasm block.
3217 if (landingPadPatches.empty()) {
3218 *landingPad = nullptr;
3219 return true;
3220 }
3221
3222 // Otherwise, if there are (pad-) branches from places in the try code that
3223 // may throw an exception, bind these branches to a new landing pad
3224 // block. This is done similarly to what is done in bindBranches.
3225 MControlInstruction* ins = landingPadPatches[0];
3226 MBasicBlock* pred = ins->block();
3227 if (!newBlock(pred, landingPad)) {
3228 return false;
3229 }
3230 ins->replaceSuccessor(0, *landingPad);
3231 for (size_t i = 1; i < landingPadPatches.length(); i++) {
3232 ins = landingPadPatches[i];
3233 pred = ins->block();
3234 if (!(*landingPad)->addPredecessor(alloc(), pred)) {
3235 return false;
3236 }
3237 ins->replaceSuccessor(0, *landingPad);
3238 }
3239
3240 // Set up the slots in the landing pad block.
3241 if (!setupLandingPadSlots(landingPad)) {
3242 return false;
3243 }
3244
3245 // Clear the now bound pad patches.
3246 landingPadPatches.clear();
3247 return true;
3248 }
3249
3250 [[nodiscard]] bool createTryTableLandingPad(TryControl* tryControl) {
3251 MBasicBlock* landingPad;
3252 if (!createTryLandingPadIfNeeded(tryControl->landingPadPatches,
3253 &landingPad)) {
3254 return false;
3255 }
3256
3257 // If there is no landing pad created, no exceptions were possibly thrown
3258 // and we don't need to do anything here.
3259 if (!landingPad) {
3260 return true;
3261 }
3262
3263 MBasicBlock* originalBlock = curBlock_;
3264 curBlock_ = landingPad;
3265
3266 bool hadCatchAll = false;
3267 for (const TryTableCatch& tryTableCatch : tryControl->catches) {
3268 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"
, 3268); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 2"
")"); do { *((volatile int*)__null) = 3268; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3269
3270 // Handle a catch_all by jumping to the target block
3271 if (tryTableCatch.tagIndex == CatchAllIndex) {
3272 // Get the exception from the slots we pushed when adding
3273 // control flow patches.
3274 curBlock_->pop();
3275 MDefinition* exception = curBlock_->pop();
3276
3277 // Capture the exnref value if we need to
3278 DefVector values;
3279 if (tryTableCatch.captureExnRef && !values.append(exception)) {
3280 return false;
3281 }
3282
3283 // Branch to the catch_all code
3284 if (!br(tryTableCatch.labelRelativeDepth, values)) {
3285 return false;
3286 }
3287
3288 // Break from the loop and skip the implicit rethrow that's needed
3289 // if we didn't have a catch_all
3290 hadCatchAll = true;
3291 break;
3292 }
3293
3294 // Handle a tagged catch by doing a compare and branch on the tag index,
3295 // jumping to a catch block if they match, or else to a fallthrough block
3296 // to continue the landing pad.
3297 MBasicBlock* catchBlock = nullptr;
3298 MBasicBlock* fallthroughBlock = nullptr;
3299 if (!newBlock(curBlock_, &catchBlock) ||
3300 !newBlock(curBlock_, &fallthroughBlock)) {
3301 return false;
3302 }
3303
3304 // Get the exception and its tag from the slots we pushed when adding
3305 // control flow patches.
3306 MDefinition* exceptionTag = curBlock_->pop();
3307 curBlock_->pop();
3308
3309 // Branch to the catch block if the exception's tag matches this catch
3310 // block's tag.
3311 MDefinition* catchTag = loadTag(tryTableCatch.tagIndex);
3312 MDefinition* matchesCatchTag = compare(exceptionTag, catchTag, JSOp::Eq,
3313 MCompare::Compare_WasmAnyRef);
3314 curBlock_->end(
3315 MTest::New(alloc(), matchesCatchTag, catchBlock, fallthroughBlock));
3316
3317 // Set up the catch block by extracting the values from the exception
3318 // object.
3319 curBlock_ = catchBlock;
3320
3321 // Remove the tag and exception slots from the block, they are no
3322 // longer necessary.
3323 curBlock_->pop();
3324 MDefinition* exception = curBlock_->pop();
3325 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"
, 3325); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 3325; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3326
3327 // Extract the exception values for the catch block
3328 DefVector values;
3329 if (!loadExceptionValues(exception, tryTableCatch.tagIndex, &values)) {
3330 return false;
3331 }
3332 if (tryTableCatch.captureExnRef && !values.append(exception)) {
3333 return false;
3334 }
3335
3336 if (!br(tryTableCatch.labelRelativeDepth, values)) {
3337 return false;
3338 }
3339
3340 curBlock_ = fallthroughBlock;
3341 }
3342
3343 // If there was no catch_all, we must rethrow this exception.
3344 if (!hadCatchAll) {
3345 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"
, 3345); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 2"
")"); do { *((volatile int*)__null) = 3345; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3346 MDefinition* tag = curBlock_->pop();
3347 MDefinition* exception = curBlock_->pop();
3348 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"
, 3348); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numPushed(curBlock_) == 0"
")"); do { *((volatile int*)__null) = 3348; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3349
3350 if (!throwFrom(exception, tag)) {
3351 return false;
3352 }
3353 }
3354
3355 curBlock_ = originalBlock;
3356 return true;
3357 }
3358
3359 // Consume the pending exception state from instance, and set up the slots
3360 // of the landing pad with the exception state.
3361 [[nodiscard]] bool setupLandingPadSlots(MBasicBlock** landingPad) {
3362 MBasicBlock* prevBlock = curBlock_;
3363 curBlock_ = *landingPad;
3364
3365 // Load the pending exception and tag
3366 MInstruction* exception;
3367 MInstruction* tag;
3368 loadPendingExceptionState(&exception, &tag);
3369
3370 // Clear the pending exception and tag
3371 auto* null = constantNullRef();
3372 if (!setPendingExceptionState(null, null)) {
3373 return false;
3374 }
3375
3376 // Push the exception and its tag on the stack to make them available
3377 // to the landing pad blocks.
3378 if (!curBlock_->ensureHasSlots(2)) {
3379 return false;
3380 }
3381 curBlock_->push(exception);
3382 curBlock_->push(tag);
3383 *landingPad = curBlock_;
3384
3385 curBlock_ = prevBlock;
3386 return true;
3387 }
3388
3389 [[nodiscard]] bool startTry() {
3390 Control& control = iter().controlItem();
3391 control.block = curBlock_;
3392 control.tryControl = newTryControl();
3393 if (!control.tryControl) {
3394 return false;
3395 }
3396 control.tryControl->inBody = true;
3397 return startBlock();
3398 }
3399
3400 [[nodiscard]] bool startTryTable(TryTableCatchVector&& catches) {
3401 Control& control = iter().controlItem();
3402 control.block = curBlock_;
3403 control.tryControl = newTryControl();
3404 if (!control.tryControl) {
3405 return false;
3406 }
3407 control.tryControl->inBody = true;
3408 control.tryControl->catches = std::move(catches);
3409 return startBlock();
3410 }
3411
3412 [[nodiscard]] bool joinTryOrCatchBlock(Control& control) {
3413 // If the try or catch block ended with dead code, there is no need to
3414 // do any control flow join.
3415 if (inDeadCode()) {
3416 return true;
3417 }
3418
3419 // This is a split path which we'll need to join later, using a control
3420 // flow patch.
3421 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"
, 3421); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!curBlock_->hasLastIns()"
")"); do { *((volatile int*)__null) = 3421; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3422 MGoto* jump = MGoto::New(alloc());
3423 if (!addControlFlowPatch(jump, 0, MGoto::TargetIndex)) {
3424 return false;
3425 }
3426
3427 // Finish the current block with the control flow patch instruction.
3428 curBlock_->end(jump);
3429 return true;
3430 }
3431
3432 // Finish the previous block (either a try or catch block) and then setup a
3433 // new catch block.
3434 [[nodiscard]] bool switchToCatch(Control& control, LabelKind fromKind,
3435 uint32_t tagIndex) {
3436 // Mark this control node as being no longer in the body of the try
3437 control.tryControl->inBody = false;
3438
3439 // If there is no control block, then either:
3440 // - the entry of the try block is dead code, or
3441 // - there is no landing pad for the try-catch.
3442 // In either case, any catch will be dead code.
3443 if (!control.block) {
3444 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"
, 3444); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inDeadCode()"
")"); do { *((volatile int*)__null) = 3444; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3445 return true;
3446 }
3447
3448 // Join the previous try or catch block with a patch to the future join of
3449 // the whole try-catch block.
3450 if (!joinTryOrCatchBlock(control)) {
3451 return false;
3452 }
3453
3454 // If we are switching from the try block, create the landing pad. This is
3455 // guaranteed to happen once and only once before processing catch blocks.
3456 if (fromKind == LabelKind::Try) {
3457 MBasicBlock* padBlock = nullptr;
3458 if (!createTryLandingPadIfNeeded(control.tryControl->landingPadPatches,
3459 &padBlock)) {
3460 return false;
3461 }
3462 // Set the control block for this try-catch to the landing pad.
3463 control.block = padBlock;
3464 }
3465
3466 // If there is no landing pad, then this and following catches are dead
3467 // code.
3468 if (!control.block) {
3469 curBlock_ = nullptr;
3470 return true;
3471 }
3472
3473 // Switch to the landing pad.
3474 curBlock_ = control.block;
3475
3476 // Handle a catch_all by immediately jumping to a new block. We require a
3477 // new block (as opposed to just emitting the catch_all code in the current
3478 // block) because rethrow requires the exception/tag to be present in the
3479 // landing pad's slots, while the catch_all block must not have the
3480 // exception/tag in slots.
3481 if (tagIndex == CatchAllIndex) {
3482 MBasicBlock* catchAllBlock = nullptr;
3483 if (!goToNewBlock(curBlock_, &catchAllBlock)) {
3484 return false;
3485 }
3486 // Compilation will continue in the catch_all block.
3487 curBlock_ = catchAllBlock;
3488 // Remove the tag and exception slots from the block, they are no
3489 // longer necessary.
3490 curBlock_->pop();
3491 curBlock_->pop();
3492 return true;
3493 }
3494
3495 // Handle a tagged catch by doing a compare and branch on the tag index,
3496 // jumping to a catch block if they match, or else to a fallthrough block
3497 // to continue the landing pad.
3498 MBasicBlock* catchBlock = nullptr;
3499 MBasicBlock* fallthroughBlock = nullptr;
3500 if (!newBlock(curBlock_, &catchBlock) ||
3501 !newBlock(curBlock_, &fallthroughBlock)) {
3502 return false;
3503 }
3504
3505 // Get the exception and its tag from the slots we pushed when adding
3506 // control flow patches.
3507 MDefinition* exceptionTag = curBlock_->pop();
3508 MDefinition* exception = curBlock_->pop();
Value stored to 'exception' during its initialization is never read
3509
3510 // Branch to the catch block if the exception's tag matches this catch
3511 // block's tag.
3512 MDefinition* catchTag = loadTag(tagIndex);
3513 MDefinition* matchesCatchTag =
3514 compare(exceptionTag, catchTag, JSOp::Eq, MCompare::Compare_WasmAnyRef);
3515 curBlock_->end(
3516 MTest::New(alloc(), matchesCatchTag, catchBlock, fallthroughBlock));
3517
3518 // The landing pad will continue in the fallthrough block
3519 control.block = fallthroughBlock;
3520
3521 // Set up the catch block by extracting the values from the exception
3522 // object.
3523 curBlock_ = catchBlock;
3524
3525 // Remove the tag and exception slots from the block, they are no
3526 // longer necessary.
3527 curBlock_->pop();
3528 exception = curBlock_->pop();
3529
3530 // Extract the exception values for the catch block
3531 DefVector values;
3532 if (!loadExceptionValues(exception, tagIndex, &values)) {
3533 return false;
3534 }
3535 iter().setResults(values.length(), values);
3536 return true;
3537 }
3538
3539 [[nodiscard]] bool loadExceptionValues(MDefinition* exception,
3540 uint32_t tagIndex, DefVector* values) {
3541 SharedTagType tagType = moduleEnv().tags[tagIndex].type;
3542 const ValTypeVector& params = tagType->argTypes();
3543 const TagOffsetVector& offsets = tagType->argOffsets();
3544
3545 // Get the data pointer from the exception object
3546 auto* data = MWasmLoadField::New(
3547 alloc(), exception, WasmExceptionObject::offsetOfData(),
3548 MIRType::Pointer, MWideningOp::None, AliasSet::Load(AliasSet::Any));
3549 if (!data) {
3550 return false;
3551 }
3552 curBlock_->add(data);
3553
3554 // Presize the values vector to the number of params
3555 if (!values->reserve(params.length())) {
3556 return false;
3557 }
3558
3559 // Load each value from the data pointer
3560 for (size_t i = 0; i < params.length(); i++) {
3561 if (!mirGen_.ensureBallast()) {
3562 return false;
3563 }
3564 auto* load = MWasmLoadFieldKA::New(
3565 alloc(), exception, data, offsets[i], params[i].toMIRType(),
3566 MWideningOp::None, AliasSet::Load(AliasSet::Any));
3567 if (!load || !values->append(load)) {
3568 return false;
3569 }
3570 curBlock_->add(load);
3571 }
3572 return true;
3573 }
3574
3575 [[nodiscard]] bool finishTryCatch(LabelKind kind, Control& control,
3576 DefVector* defs) {
3577 switch (kind) {
3578 case LabelKind::Try: {
3579 // This is a catchless try, we must delegate all throwing instructions
3580 // to the nearest enclosing try block if one exists, or else to the
3581 // body block which will handle it in emitBodyDelegateThrowPad. We
3582 // specify a relativeDepth of '1' to delegate outside of the still
3583 // active try block.
3584 uint32_t relativeDepth = 1;
3585 if (!delegatePadPatches(control.tryControl->landingPadPatches,
3586 relativeDepth)) {
3587 return false;
3588 }
3589 break;
3590 }
3591 case LabelKind::Catch: {
3592 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"
, 3592); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl->inBody"
")"); do { *((volatile int*)__null) = 3592; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3593 // This is a try without a catch_all, we must have a rethrow at the end
3594 // of the landing pad (if any).
3595 MBasicBlock* padBlock = control.block;
3596 if (padBlock) {
3597 MBasicBlock* prevBlock = curBlock_;
3598 curBlock_ = padBlock;
3599 MDefinition* tag = curBlock_->pop();
3600 MDefinition* exception = curBlock_->pop();
3601 if (!throwFrom(exception, tag)) {
3602 return false;
3603 }
3604 curBlock_ = prevBlock;
3605 }
3606 break;
3607 }
3608 case LabelKind::CatchAll: {
3609 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"
, 3609); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl->inBody"
")"); do { *((volatile int*)__null) = 3609; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3610 // This is a try with a catch_all, and requires no special handling.
3611 break;
3612 }
3613 default:
3614 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3614); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 3614; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
3615 }
3616
3617 // Finish the block, joining the try and catch blocks
3618 return finishBlock(defs);
3619 }
3620
3621 [[nodiscard]] bool finishTryTable(Control& control, DefVector* defs) {
3622 // Mark this control as no longer in the body of the try
3623 control.tryControl->inBody = false;
3624 // Create a landing pad for all of the catches
3625 if (!createTryTableLandingPad(control.tryControl.get())) {
3626 return false;
3627 }
3628 // Finish the block, joining the try and catch blocks
3629 return finishBlock(defs);
3630 }
3631
3632 [[nodiscard]] bool emitBodyDelegateThrowPad(Control& control) {
3633 // Create a landing pad for any throwing instructions
3634 MBasicBlock* padBlock;
3635 if (!createTryLandingPadIfNeeded(bodyDelegatePadPatches_, &padBlock)) {
3636 return false;
3637 }
3638
3639 // If no landing pad was necessary, then we don't need to do anything here
3640 if (!padBlock) {
3641 return true;
3642 }
3643
3644 // Switch to the landing pad and rethrow the exception
3645 MBasicBlock* prevBlock = curBlock_;
3646 curBlock_ = padBlock;
3647 MDefinition* tag = curBlock_->pop();
3648 MDefinition* exception = curBlock_->pop();
3649 if (!throwFrom(exception, tag)) {
3650 return false;
3651 }
3652 curBlock_ = prevBlock;
3653 return true;
3654 }
3655
3656 [[nodiscard]] bool emitNewException(MDefinition* tag,
3657 MDefinition** exception) {
3658 return emitInstanceCall1(readBytecodeOffset(), SASigExceptionNew, tag,
3659 exception);
3660 }
3661
3662 [[nodiscard]] bool emitThrow(uint32_t tagIndex, const DefVector& argValues) {
3663 if (inDeadCode()) {
3664 return true;
3665 }
3666 uint32_t bytecodeOffset = readBytecodeOffset();
3667
3668 // Load the tag
3669 MDefinition* tag = loadTag(tagIndex);
3670 if (!tag) {
3671 return false;
3672 }
3673
3674 // Allocate an exception object
3675 MDefinition* exception;
3676 if (!emitNewException(tag, &exception)) {
3677 return false;
3678 }
3679
3680 // Load the data pointer from the object
3681 auto* data = MWasmLoadField::New(
3682 alloc(), exception, WasmExceptionObject::offsetOfData(),
3683 MIRType::Pointer, MWideningOp::None, AliasSet::Load(AliasSet::Any));
3684 if (!data) {
3685 return false;
3686 }
3687 curBlock_->add(data);
3688
3689 // Store the params into the data pointer
3690 SharedTagType tagType = moduleEnv_.tags[tagIndex].type;
3691 for (size_t i = 0; i < tagType->argOffsets().length(); i++) {
3692 if (!mirGen_.ensureBallast()) {
3693 return false;
3694 }
3695 ValType type = tagType->argTypes()[i];
3696 uint32_t offset = tagType->argOffsets()[i];
3697
3698 if (!type.isRefRepr()) {
3699 auto* store = MWasmStoreFieldKA::New(alloc(), exception, data, offset,
3700 argValues[i], MNarrowingOp::None,
3701 AliasSet::Store(AliasSet::Any));
3702 if (!store) {
3703 return false;
3704 }
3705 curBlock_->add(store);
3706 continue;
3707 }
3708
3709 // Store the new value
3710 auto* store = MWasmStoreFieldRefKA::New(
3711 alloc(), instancePointer_, exception, data, offset, argValues[i],
3712 AliasSet::Store(AliasSet::Any), Nothing(), WasmPreBarrierKind::None);
3713 if (!store) {
3714 return false;
3715 }
3716 curBlock_->add(store);
3717
3718 // Call the post-write barrier
3719 if (!postBarrierImmediate(bytecodeOffset, exception, data, offset,
3720 argValues[i])) {
3721 return false;
3722 }
3723 }
3724
3725 // Throw the exception
3726 return throwFrom(exception, tag);
3727 }
3728
3729 [[nodiscard]] bool emitThrowRef(MDefinition* exnRef) {
3730 if (inDeadCode()) {
3731 return true;
3732 }
3733
3734 // The exception must be non-null
3735 if (!refAsNonNull(exnRef)) {
3736 return false;
3737 }
3738
3739 // Call Instance::throwException to perform tag unpacking and throw the
3740 // exception
3741 if (!emitInstanceCall1(readBytecodeOffset(), SASigThrowException, exnRef)) {
3742 return false;
3743 }
3744 unreachableTrap();
3745
3746 curBlock_ = nullptr;
3747 return true;
3748 }
3749
3750 [[nodiscard]] bool throwFrom(MDefinition* exn, MDefinition* tag) {
3751 if (inDeadCode()) {
3752 return true;
3753 }
3754
3755 // Check if there is a local catching try control, and if so, then add a
3756 // pad-patch to its tryPadPatches.
3757 uint32_t relativeTryDepth;
3758 if (inTryBlock(&relativeTryDepth)) {
3759 // Set the pending exception state, the landing pad will read from this
3760 if (!setPendingExceptionState(exn, tag)) {
3761 return false;
3762 }
3763
3764 // End with a pending jump to the landing pad
3765 if (!endWithPadPatch(relativeTryDepth)) {
3766 return false;
3767 }
3768 curBlock_ = nullptr;
3769 return true;
3770 }
3771
3772 // If there is no surrounding catching block, call an instance method to
3773 // throw the exception.
3774 if (!emitInstanceCall1(readBytecodeOffset(), SASigThrowException, exn)) {
3775 return false;
3776 }
3777 unreachableTrap();
3778
3779 curBlock_ = nullptr;
3780 return true;
3781 }
3782
3783 [[nodiscard]] bool emitRethrow(uint32_t relativeDepth) {
3784 if (inDeadCode()) {
3785 return true;
3786 }
3787
3788 Control& control = iter().controlItem(relativeDepth);
3789 MBasicBlock* pad = control.block;
3790 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"
, 3790); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pad" ")"); do
{ *((volatile int*)__null) = 3790; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3791 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"
, 3791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pad->nslots() > 1"
")"); do { *((volatile int*)__null) = 3791; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3792 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"
, 3793); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
")"); do { *((volatile int*)__null) = 3793; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3793 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"
, 3793); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter().controlKind(relativeDepth) == LabelKind::Catch || iter().controlKind(relativeDepth) == LabelKind::CatchAll"
")"); do { *((volatile int*)__null) = 3793; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3794
3795 // The exception will always be the last slot in the landing pad.
3796 size_t exnSlotPosition = pad->nslots() - 2;
3797 MDefinition* tag = pad->getSlot(exnSlotPosition + 1);
3798 MDefinition* exception = pad->getSlot(exnSlotPosition);
3799 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"
, 3800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 3800; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3800 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"
, 3800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "exception->type() == MIRType::WasmAnyRef && tag->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 3800; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3801 return throwFrom(exception, tag);
3802 }
3803
3804 /*********************************************** Instance call helpers ***/
3805
3806 // Do not call this function directly -- it offers no protection against
3807 // mis-counting of arguments. Instead call one of
3808 // ::emitInstanceCall{0,1,2,3,4,5,6}.
3809 //
3810 // Emits a call to the Instance function indicated by `callee`. This is
3811 // assumed to take an Instance pointer as its first argument. The remaining
3812 // args are taken from `args`, which is assumed to hold `numArgs` entries.
3813 // If `result` is non-null, the MDefinition* holding the return value is
3814 // written to `*result`.
3815 [[nodiscard]] bool emitInstanceCallN(uint32_t lineOrBytecode,
3816 const SymbolicAddressSignature& callee,
3817 MDefinition** args, size_t numArgs,
3818 MDefinition** result = nullptr) {
3819 // Check that the first formal parameter is plausibly an Instance pointer.
3820 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"
, 3820); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.numArgs > 0"
")"); do { *((volatile int*)__null) = 3820; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3821 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"
, 3821); AnnotateMozCrashReason("MOZ_ASSERT" "(" "callee.argTypes[0] == MIRType::Pointer"
")"); do { *((volatile int*)__null) = 3821; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3822 // Check we agree on the number of args.
3823 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"
, 3823); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numArgs + 1 == callee.numArgs"
")"); do { *((volatile int*)__null) = 3823; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3824 // Check we agree on whether a value is returned.
3825 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"
, 3825); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(result == nullptr) == (callee.retType == MIRType::None)"
")"); do { *((volatile int*)__null) = 3825; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3826
3827 // If we are in dead code, it can happen that some of the `args` entries
3828 // are nullptr, which will look like an OOM to the logic below. So exit
3829 // at this point. `passInstance`, `passArg`, `finishCall` and
3830 // `builtinInstanceMethodCall` all do nothing in dead code, so it's valid
3831 // to exit here.
3832 if (inDeadCode()) {
3833 if (result) {
3834 *result = nullptr;
3835 }
3836 return true;
3837 }
3838
3839 // Check all args for signs of OOMness before attempting to allocating any
3840 // more memory.
3841 for (size_t i = 0; i < numArgs; i++) {
3842 if (!args[i]) {
3843 if (result) {
3844 *result = nullptr;
3845 }
3846 return false;
3847 }
3848 }
3849
3850 // Finally, construct the call.
3851 CallCompileState ccsArgs;
3852 if (!passInstance(callee.argTypes[0], &ccsArgs)) {
3853 return false;
3854 }
3855 for (size_t i = 0; i < numArgs; i++) {
3856 if (!passArg(args[i], callee.argTypes[i + 1], &ccsArgs)) {
3857 return false;
3858 }
3859 }
3860 if (!finishCall(&ccsArgs)) {
3861 return false;
3862 }
3863 return builtinInstanceMethodCall(callee, lineOrBytecode, ccsArgs, result);
3864 }
3865
3866 [[nodiscard]] bool emitInstanceCall0(uint32_t lineOrBytecode,
3867 const SymbolicAddressSignature& callee,
3868 MDefinition** result = nullptr) {
3869 MDefinition* args[0] = {};
3870 return emitInstanceCallN(lineOrBytecode, callee, args, 0, result);
3871 }
3872 [[nodiscard]] bool emitInstanceCall1(uint32_t lineOrBytecode,
3873 const SymbolicAddressSignature& callee,
3874 MDefinition* arg1,
3875 MDefinition** result = nullptr) {
3876 MDefinition* args[1] = {arg1};
3877 return emitInstanceCallN(lineOrBytecode, callee, args, 1, result);
3878 }
3879 [[nodiscard]] bool emitInstanceCall2(uint32_t lineOrBytecode,
3880 const SymbolicAddressSignature& callee,
3881 MDefinition* arg1, MDefinition* arg2,
3882 MDefinition** result = nullptr) {
3883 MDefinition* args[2] = {arg1, arg2};
3884 return emitInstanceCallN(lineOrBytecode, callee, args, 2, result);
3885 }
3886 [[nodiscard]] bool emitInstanceCall3(uint32_t lineOrBytecode,
3887 const SymbolicAddressSignature& callee,
3888 MDefinition* arg1, MDefinition* arg2,
3889 MDefinition* arg3,
3890 MDefinition** result = nullptr) {
3891 MDefinition* args[3] = {arg1, arg2, arg3};
3892 return emitInstanceCallN(lineOrBytecode, callee, args, 3, result);
3893 }
3894 [[nodiscard]] bool emitInstanceCall4(uint32_t lineOrBytecode,
3895 const SymbolicAddressSignature& callee,
3896 MDefinition* arg1, MDefinition* arg2,
3897 MDefinition* arg3, MDefinition* arg4,
3898 MDefinition** result = nullptr) {
3899 MDefinition* args[4] = {arg1, arg2, arg3, arg4};
3900 return emitInstanceCallN(lineOrBytecode, callee, args, 4, result);
3901 }
3902 [[nodiscard]] bool emitInstanceCall5(uint32_t lineOrBytecode,
3903 const SymbolicAddressSignature& callee,
3904 MDefinition* arg1, MDefinition* arg2,
3905 MDefinition* arg3, MDefinition* arg4,
3906 MDefinition* arg5,
3907 MDefinition** result = nullptr) {
3908 MDefinition* args[5] = {arg1, arg2, arg3, arg4, arg5};
3909 return emitInstanceCallN(lineOrBytecode, callee, args, 5, result);
3910 }
3911 [[nodiscard]] bool emitInstanceCall6(uint32_t lineOrBytecode,
3912 const SymbolicAddressSignature& callee,
3913 MDefinition* arg1, MDefinition* arg2,
3914 MDefinition* arg3, MDefinition* arg4,
3915 MDefinition* arg5, MDefinition* arg6,
3916 MDefinition** result = nullptr) {
3917 MDefinition* args[6] = {arg1, arg2, arg3, arg4, arg5, arg6};
3918 return emitInstanceCallN(lineOrBytecode, callee, args, 6, result);
3919 }
3920
3921 /******************************** WasmGC: low level load/store helpers ***/
3922
3923 // Given a (StorageType, FieldExtension) pair, produce the (MIRType,
3924 // MWideningOp) pair that will give the correct operation for reading the
3925 // value from memory.
3926 static void fieldLoadInfoToMIR(StorageType type, FieldWideningOp wideningOp,
3927 MIRType* mirType, MWideningOp* mirWideningOp) {
3928 switch (type.kind()) {
3929 case StorageType::I8: {
3930 switch (wideningOp) {
3931 case FieldWideningOp::Signed:
3932 *mirType = MIRType::Int32;
3933 *mirWideningOp = MWideningOp::FromS8;
3934 return;
3935 case FieldWideningOp::Unsigned:
3936 *mirType = MIRType::Int32;
3937 *mirWideningOp = MWideningOp::FromU8;
3938 return;
3939 default:
3940 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3940); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 3940; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
3941 }
3942 }
3943 case StorageType::I16: {
3944 switch (wideningOp) {
3945 case FieldWideningOp::Signed:
3946 *mirType = MIRType::Int32;
3947 *mirWideningOp = MWideningOp::FromS16;
3948 return;
3949 case FieldWideningOp::Unsigned:
3950 *mirType = MIRType::Int32;
3951 *mirWideningOp = MWideningOp::FromU16;
3952 return;
3953 default:
3954 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3954); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 3954; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
3955 }
3956 }
3957 default: {
3958 switch (wideningOp) {
3959 case FieldWideningOp::None:
3960 *mirType = type.toMIRType();
3961 *mirWideningOp = MWideningOp::None;
3962 return;
3963 default:
3964 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 3964); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 3964; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
3965 }
3966 }
3967 }
3968 }
3969
3970 // Given a StorageType, return the Scale required when accessing array
3971 // elements of this type.
3972 static Scale scaleFromFieldType(StorageType type) {
3973 if (type.kind() == StorageType::V128) {
3974 // V128 is accessed differently, so this scale will not be used.
3975 return Scale::Invalid;
3976 }
3977 return ShiftToScale(type.indexingShift());
3978 }
3979
3980 // Given a StorageType, produce the MNarrowingOp required for writing the
3981 // value to memory.
3982 static MNarrowingOp fieldStoreInfoToMIR(StorageType type) {
3983 switch (type.kind()) {
3984 case StorageType::I8:
3985 return MNarrowingOp::To8;
3986 case StorageType::I16:
3987 return MNarrowingOp::To16;
3988 default:
3989 return MNarrowingOp::None;
3990 }
3991 }
3992
3993 // Generate a write of `value` at address `base + offset`, where `offset` is
3994 // known at JIT time. If the written value is a reftype, the previous value
3995 // at `base + offset` will be retrieved and handed off to the post-write
3996 // barrier. `keepAlive` will be referenced by the instruction so as to hold
3997 // it live (from the GC's point of view).
3998 [[nodiscard]] bool writeGcValueAtBasePlusOffset(
3999 uint32_t lineOrBytecode, StorageType type, MDefinition* keepAlive,
4000 AliasSet::Flag aliasBitset, MDefinition* value, MDefinition* base,
4001 uint32_t offset, bool needsTrapInfo, WasmPreBarrierKind preBarrierKind) {
4002 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"
, 4002); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4002; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4003 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"
, 4003); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4003; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4004 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"
, 4004); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType().toMIRType() == value->type()"
")"); do { *((volatile int*)__null) = 4004; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4005 MNarrowingOp narrowingOp = fieldStoreInfoToMIR(type);
4006
4007 if (!type.isRefRepr()) {
4008 MaybeTrapSiteInfo maybeTrap;
4009 if (needsTrapInfo) {
4010 maybeTrap.emplace(getTrapSiteInfo());
4011 }
4012 auto* store = MWasmStoreFieldKA::New(
4013 alloc(), keepAlive, base, offset, value, narrowingOp,
4014 AliasSet::Store(aliasBitset), maybeTrap);
4015 if (!store) {
4016 return false;
4017 }
4018 curBlock_->add(store);
4019 return true;
4020 }
4021
4022 // Otherwise it's a ref store. Load the previous value so we can show it
4023 // to the post-write barrier.
4024 //
4025 // Optimisation opportunity: for the case where this field write results
4026 // from struct.new, the old value is always zero. So we should synthesise
4027 // a suitable zero constant rather than reading it from the object. See
4028 // also bug 1799999.
4029 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"
, 4029); AnnotateMozCrashReason("MOZ_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 4029; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4030 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"
, 4030); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType() == type.valType()"
")"); do { *((volatile int*)__null) = 4030; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4031
4032 // Store the new value
4033 auto* store = MWasmStoreFieldRefKA::New(
4034 alloc(), instancePointer_, keepAlive, base, offset, value,
4035 AliasSet::Store(aliasBitset), mozilla::Some(getTrapSiteInfo()),
4036 preBarrierKind);
4037 if (!store) {
4038 return false;
4039 }
4040 curBlock_->add(store);
4041
4042 // Call the post-write barrier
4043 return postBarrierImmediate(lineOrBytecode, keepAlive, base, offset, value);
4044 }
4045
4046 // Generate a write of `value` at address `base + index * scale`, where
4047 // `scale` is known at JIT-time. If the written value is a reftype, the
4048 // previous value at `base + index * scale` will be retrieved and handed off
4049 // to the post-write barrier. `keepAlive` will be referenced by the
4050 // instruction so as to hold it live (from the GC's point of view).
4051 [[nodiscard]] bool writeGcValueAtBasePlusScaledIndex(
4052 uint32_t lineOrBytecode, StorageType type, MDefinition* keepAlive,
4053 AliasSet::Flag aliasBitset, MDefinition* value, MDefinition* base,
4054 uint32_t scale, MDefinition* index, WasmPreBarrierKind preBarrierKind) {
4055 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"
, 4055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4055; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4056 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"
, 4056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4056; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4057 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"
, 4057); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType().toMIRType() == value->type()"
")"); do { *((volatile int*)__null) = 4057; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4058 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"
, 4059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
")"); do { *((volatile int*)__null) = 4059; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4059 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"
, 4059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scale == 1 || scale == 2 || scale == 4 || scale == 8 || scale == 16"
")"); do { *((volatile int*)__null) = 4059; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4060
4061 MNarrowingOp narrowingOp = fieldStoreInfoToMIR(type);
4062
4063 if (!type.isRefRepr()) {
4064 MaybeTrapSiteInfo maybeTrap;
4065 Scale scale = scaleFromFieldType(type);
4066 auto* store = MWasmStoreElementKA::New(
4067 alloc(), keepAlive, base, index, value, narrowingOp, scale,
4068 AliasSet::Store(aliasBitset), maybeTrap);
4069 if (!store) {
4070 return false;
4071 }
4072 curBlock_->add(store);
4073 return true;
4074 }
4075
4076 // Otherwise it's a ref store.
4077 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"
, 4077); AnnotateMozCrashReason("MOZ_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 4077; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4078 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"
, 4078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type.widenToValType() == type.valType()"
")"); do { *((volatile int*)__null) = 4078; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4079
4080 // Store the new value
4081 auto* store = MWasmStoreElementRefKA::New(
4082 alloc(), instancePointer_, keepAlive, base, index, value,
4083 AliasSet::Store(aliasBitset), mozilla::Some(getTrapSiteInfo()),
4084 preBarrierKind);
4085 if (!store) {
4086 return false;
4087 }
4088 curBlock_->add(store);
4089
4090 return postBarrierIndex(lineOrBytecode, keepAlive, base, index,
4091 sizeof(void*), value);
4092 }
4093
4094 // Generate a read from address `base + offset`, where `offset` is known at
4095 // JIT time. The loaded value will be widened as described by `type` and
4096 // `fieldWideningOp`. `keepAlive` will be referenced by the instruction so as
4097 // to hold it live (from the GC's point of view).
4098 [[nodiscard]] MDefinition* readGcValueAtBasePlusOffset(
4099 StorageType type, FieldWideningOp fieldWideningOp, MDefinition* keepAlive,
4100 AliasSet::Flag aliasBitset, MDefinition* base, uint32_t offset,
4101 bool needsTrapInfo) {
4102 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"
, 4102); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4102; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4103 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"
, 4103); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4103; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4104 MIRType mirType;
4105 MWideningOp mirWideningOp;
4106 fieldLoadInfoToMIR(type, fieldWideningOp, &mirType, &mirWideningOp);
4107 MaybeTrapSiteInfo maybeTrap;
4108 if (needsTrapInfo) {
4109 maybeTrap.emplace(getTrapSiteInfo());
4110 }
4111 auto* load = MWasmLoadFieldKA::New(alloc(), keepAlive, base, offset,
4112 mirType, mirWideningOp,
4113 AliasSet::Load(aliasBitset), maybeTrap);
4114 if (!load) {
4115 return nullptr;
4116 }
4117 curBlock_->add(load);
4118 return load;
4119 }
4120
4121 // Generate a read from address `base + index * scale`, where `scale` is
4122 // known at JIT-time. The loaded value will be widened as described by
4123 // `type` and `fieldWideningOp`. `keepAlive` will be referenced by the
4124 // instruction so as to hold it live (from the GC's point of view).
4125 [[nodiscard]] MDefinition* readGcArrayValueAtIndex(
4126 StorageType type, FieldWideningOp fieldWideningOp, MDefinition* keepAlive,
4127 AliasSet::Flag aliasBitset, MDefinition* base, MDefinition* index) {
4128 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"
, 4128); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aliasBitset != 0"
")"); do { *((volatile int*)__null) = 4128; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4129 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"
, 4129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "keepAlive->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4129; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4130
4131 MIRType mirType;
4132 MWideningOp mirWideningOp;
4133 fieldLoadInfoToMIR(type, fieldWideningOp, &mirType, &mirWideningOp);
4134 Scale scale = scaleFromFieldType(type);
4135 auto* load = MWasmLoadElementKA::New(
4136 alloc(), keepAlive, base, index, mirType, mirWideningOp, scale,
4137 AliasSet::Load(aliasBitset), mozilla::Some(getTrapSiteInfo()));
4138 if (!load) {
4139 return nullptr;
4140 }
4141 curBlock_->add(load);
4142 return load;
4143 }
4144
4145 /************************************************ WasmGC: type helpers ***/
4146
4147 // Returns an MDefinition holding the supertype vector for `typeIndex`.
4148 [[nodiscard]] MDefinition* loadSuperTypeVector(uint32_t typeIndex) {
4149 uint32_t stvOffset = moduleEnv().offsetOfSuperTypeVector(typeIndex);
4150
4151 auto* load =
4152 MWasmLoadInstanceDataField::New(alloc(), MIRType::Pointer, stvOffset,
4153 /*isConst=*/true, instancePointer_);
4154 if (!load) {
4155 return nullptr;
4156 }
4157 curBlock_->add(load);
4158 return load;
4159 }
4160
4161 [[nodiscard]] MDefinition* loadTypeDefInstanceData(uint32_t typeIndex) {
4162 size_t offset = Instance::offsetInData(
4163 moduleEnv_.offsetOfTypeDefInstanceData(typeIndex));
4164 auto* result = MWasmDerivedPointer::New(alloc(), instancePointer_, offset);
4165 if (!result) {
4166 return nullptr;
4167 }
4168 curBlock_->add(result);
4169 return result;
4170 }
4171
4172 /********************************************** WasmGC: struct helpers ***/
4173
4174 [[nodiscard]] MDefinition* createStructObject(uint32_t typeIndex,
4175 bool zeroFields) {
4176 const TypeDef& typeDef = (*moduleEnv().types)[typeIndex];
4177 gc::AllocKind allocKind = WasmStructObject::allocKindForTypeDef(&typeDef);
4178 bool isOutline =
4179 WasmStructObject::requiresOutlineBytes(typeDef.structType().size_);
4180
4181 // Allocate an uninitialized struct. This requires the type definition
4182 // for the struct.
4183 MDefinition* typeDefData = loadTypeDefInstanceData(typeIndex);
4184 if (!typeDefData) {
4185 return nullptr;
4186 }
4187
4188 auto* structObject =
4189 MWasmNewStructObject::New(alloc(), instancePointer_, typeDefData,
4190 isOutline, zeroFields, allocKind);
4191 if (!structObject) {
4192 return nullptr;
4193 }
4194 curBlock_->add(structObject);
4195
4196 return structObject;
4197 }
4198
4199 // Helper function for EmitStruct{New,Set}: given a MIR pointer to a
4200 // WasmStructObject, a MIR pointer to a value, and a field descriptor,
4201 // generate MIR to write the value to the relevant field in the object.
4202 [[nodiscard]] bool writeValueToStructField(
4203 uint32_t lineOrBytecode, const StructField& field,
4204 MDefinition* structObject, MDefinition* value,
4205 WasmPreBarrierKind preBarrierKind) {
4206 StorageType fieldType = field.type;
4207 uint32_t fieldOffset = field.offset;
4208
4209 bool areaIsOutline;
4210 uint32_t areaOffset;
4211 WasmStructObject::fieldOffsetToAreaAndOffset(fieldType, fieldOffset,
4212 &areaIsOutline, &areaOffset);
4213
4214 // Make `base` point at the first byte of either the struct object as a
4215 // whole or of the out-of-line data area. And adjust `areaOffset`
4216 // accordingly.
4217 MDefinition* base;
4218 bool needsTrapInfo;
4219 if (areaIsOutline) {
4220 auto* load = MWasmLoadField::New(
4221 alloc(), structObject, WasmStructObject::offsetOfOutlineData(),
4222 MIRType::Pointer, MWideningOp::None,
4223 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer),
4224 mozilla::Some(getTrapSiteInfo()));
4225 if (!load) {
4226 return false;
4227 }
4228 curBlock_->add(load);
4229 base = load;
4230 needsTrapInfo = false;
4231 } else {
4232 base = structObject;
4233 needsTrapInfo = true;
4234 areaOffset += WasmStructObject::offsetOfInlineData();
4235 }
4236 // The transaction is to happen at `base + areaOffset`, so to speak.
4237 // After this point we must ignore `fieldOffset`.
4238
4239 // The alias set denoting the field's location, although lacking a
4240 // Load-vs-Store indication at this point.
4241 AliasSet::Flag fieldAliasSet = areaIsOutline
4242 ? AliasSet::WasmStructOutlineDataArea
4243 : AliasSet::WasmStructInlineDataArea;
4244
4245 return writeGcValueAtBasePlusOffset(lineOrBytecode, fieldType, structObject,
4246 fieldAliasSet, value, base, areaOffset,
4247 needsTrapInfo, preBarrierKind);
4248 }
4249
4250 // Helper function for EmitStructGet: given a MIR pointer to a
4251 // WasmStructObject, a field descriptor and a field widening operation,
4252 // generate MIR to read the value from the relevant field in the object.
4253 [[nodiscard]] MDefinition* readValueFromStructField(
4254 const StructField& field, FieldWideningOp wideningOp,
4255 MDefinition* structObject) {
4256 StorageType fieldType = field.type;
4257 uint32_t fieldOffset = field.offset;
4258
4259 bool areaIsOutline;
4260 uint32_t areaOffset;
4261 WasmStructObject::fieldOffsetToAreaAndOffset(fieldType, fieldOffset,
4262 &areaIsOutline, &areaOffset);
4263
4264 // Make `base` point at the first byte of either the struct object as a
4265 // whole or of the out-of-line data area. And adjust `areaOffset`
4266 // accordingly.
4267 MDefinition* base;
4268 bool needsTrapInfo;
4269 if (areaIsOutline) {
4270 auto* loadOOLptr = MWasmLoadField::New(
4271 alloc(), structObject, WasmStructObject::offsetOfOutlineData(),
4272 MIRType::Pointer, MWideningOp::None,
4273 AliasSet::Load(AliasSet::WasmStructOutlineDataPointer),
4274 mozilla::Some(getTrapSiteInfo()));
4275 if (!loadOOLptr) {
4276 return nullptr;
4277 }
4278 curBlock_->add(loadOOLptr);
4279 base = loadOOLptr;
4280 needsTrapInfo = false;
4281 } else {
4282 base = structObject;
4283 needsTrapInfo = true;
4284 areaOffset += WasmStructObject::offsetOfInlineData();
4285 }
4286 // The transaction is to happen at `base + areaOffset`, so to speak.
4287 // After this point we must ignore `fieldOffset`.
4288
4289 // The alias set denoting the field's location, although lacking a
4290 // Load-vs-Store indication at this point.
4291 AliasSet::Flag fieldAliasSet = areaIsOutline
4292 ? AliasSet::WasmStructOutlineDataArea
4293 : AliasSet::WasmStructInlineDataArea;
4294
4295 return readGcValueAtBasePlusOffset(fieldType, wideningOp, structObject,
4296 fieldAliasSet, base, areaOffset,
4297 needsTrapInfo);
4298 }
4299
4300 /********************************* WasmGC: address-arithmetic helpers ***/
4301
4302 inline bool targetIs64Bit() const {
4303#ifdef JS_64BIT1
4304 return true;
4305#else
4306 return false;
4307#endif
4308 }
4309
4310 // Generate MIR to unsigned widen `val` out to the target word size. If
4311 // `val` is already at the target word size, this is a no-op. The only
4312 // other allowed case is where `val` is Int32 and we're compiling for a
4313 // 64-bit target, in which case a widen is generated.
4314 [[nodiscard]] MDefinition* unsignedWidenToTargetWord(MDefinition* val) {
4315 if (targetIs64Bit()) {
4316 if (val->type() == MIRType::Int32) {
4317 auto* ext = MExtendInt32ToInt64::New(alloc(), val, /*isUnsigned=*/true);
4318 if (!ext) {
4319 return nullptr;
4320 }
4321 curBlock_->add(ext);
4322 return ext;
4323 }
4324 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"
, 4324); AnnotateMozCrashReason("MOZ_ASSERT" "(" "val->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 4324; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4325 return val;
4326 }
4327 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"
, 4327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "val->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4327; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4328 return val;
4329 }
4330
4331 /********************************************** WasmGC: array helpers ***/
4332
4333 // Given `arrayObject`, the address of a WasmArrayObject, generate MIR to
4334 // return the contents of the WasmArrayObject::numElements_ field.
4335 // Adds trap site info for the null check.
4336 [[nodiscard]] MDefinition* getWasmArrayObjectNumElements(
4337 MDefinition* arrayObject) {
4338 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"
, 4338); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4338; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4339
4340 auto* numElements = MWasmLoadField::New(
4341 alloc(), arrayObject, WasmArrayObject::offsetOfNumElements(),
4342 MIRType::Int32, MWideningOp::None,
4343 AliasSet::Load(AliasSet::WasmArrayNumElements),
4344 mozilla::Some(getTrapSiteInfo()));
4345 if (!numElements) {
4346 return nullptr;
4347 }
4348 curBlock_->add(numElements);
4349
4350 return numElements;
4351 }
4352
4353 // Given `arrayObject`, the address of a WasmArrayObject, generate MIR to
4354 // return the contents of the WasmArrayObject::data_ field.
4355 [[nodiscard]] MDefinition* getWasmArrayObjectData(MDefinition* arrayObject) {
4356 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"
, 4356); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4356; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4357
4358 auto* data = MWasmLoadField::New(
4359 alloc(), arrayObject, WasmArrayObject::offsetOfData(),
4360 MIRType::WasmArrayData, MWideningOp::None,
4361 AliasSet::Load(AliasSet::WasmArrayDataPointer),
4362 mozilla::Some(getTrapSiteInfo()));
4363 if (!data) {
4364 return nullptr;
4365 }
4366 curBlock_->add(data);
4367
4368 return data;
4369 }
4370
4371 // Given a JIT-time-known type index `typeIndex` and a run-time known number
4372 // of elements `numElements`, create MIR to allocate a new wasm array,
4373 // possibly initialized with `typeIndex`s default value.
4374 [[nodiscard]] MDefinition* createArrayObject(uint32_t lineOrBytecode,
4375 uint32_t typeIndex,
4376 MDefinition* numElements,
4377 uint32_t elemSize,
4378 bool zeroFields) {
4379 // Get the type definition for the array as a whole.
4380 MDefinition* typeDefData = loadTypeDefInstanceData(typeIndex);
4381 if (!typeDefData) {
4382 return nullptr;
4383 }
4384
4385 auto* arrayObject = MWasmNewArrayObject::New(
4386 alloc(), instancePointer_, numElements, typeDefData, elemSize,
4387 zeroFields, bytecodeOffset());
4388 if (!arrayObject) {
4389 return nullptr;
4390 }
4391 curBlock_->add(arrayObject);
4392
4393 return arrayObject;
4394 }
4395
4396 // This emits MIR to perform several actions common to array loads and
4397 // stores. Given `arrayObject`, that points to a WasmArrayObject, and an
4398 // index value `index`, it:
4399 //
4400 // * Generates a trap if the array pointer is null
4401 // * Gets the size of the array
4402 // * Emits a bounds check of `index` against the array size
4403 // * Retrieves the OOL object pointer from the array
4404 // * Includes check for null via signal handler.
4405 //
4406 // The returned value is for the OOL object pointer.
4407 [[nodiscard]] MDefinition* setupForArrayAccess(MDefinition* arrayObject,
4408 MDefinition* index) {
4409 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"
, 4409); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4409; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4410 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"
, 4410); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4410; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4411
4412 // Check for null is done in getWasmArrayObjectNumElements.
4413
4414 // Get the size value for the array.
4415 MDefinition* numElements = getWasmArrayObjectNumElements(arrayObject);
4416 if (!numElements) {
4417 return nullptr;
4418 }
4419
4420 // Create a bounds check.
4421 auto* boundsCheck =
4422 MWasmBoundsCheck::New(alloc(), index, numElements, bytecodeOffset(),
4423 MWasmBoundsCheck::Target::Unknown);
4424 if (!boundsCheck) {
4425 return nullptr;
4426 }
4427 curBlock_->add(boundsCheck);
4428
4429 // Get the address of the first byte of the (OOL) data area.
4430 return getWasmArrayObjectData(arrayObject);
4431 }
4432
4433 [[nodiscard]] bool fillArray(uint32_t lineOrBytecode,
4434 const ArrayType& arrayType,
4435 MDefinition* arrayObject, MDefinition* index,
4436 MDefinition* numElements, MDefinition* val,
4437 WasmPreBarrierKind preBarrierKind) {
4438 mozilla::DebugOnly<MIRType> valMIRType = val->type();
4439 StorageType elemType = arrayType.elementType_;
4440 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"
, 4440); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemType.widenToValType().toMIRType() == valMIRType"
")"); do { *((volatile int*)__null) = 4440; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4441
4442 uint32_t elemSize = elemType.size();
4443 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"
, 4443); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize >= 1 && elemSize <= 16"
")"); do { *((volatile int*)__null) = 4443; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4444
4445 // Make `arrayBase` point at the first byte of the (OOL) data area.
4446 MDefinition* arrayBase = getWasmArrayObjectData(arrayObject);
4447 if (!arrayBase) {
4448 return false;
4449 }
4450
4451 // We have:
4452 // arrayBase : TargetWord
4453 // index : Int32
4454 // numElements : Int32
4455 // val : <any StorageType>
4456 // $elemSize = arrayType.elementType_.size(); 1, 2, 4, 8 or 16
4457 //
4458 // Generate MIR:
4459 // <in current block>
4460 // limit : Int32 = index + numElements
4461 // if (limit == index) goto after; // skip loop if trip count == 0
4462 // loop:
4463 // indexPhi = phi(index, indexNext)
4464 // arrayBase[index * $elemSize] = val
4465 // indexNext = indexPhi + 1
4466 // if (indexNext <u limit) goto loop;
4467 // after:
4468 //
4469 // We construct the loop "manually" rather than using
4470 // FunctionCompiler::{startLoop,closeLoop} as the latter have awareness of
4471 // the wasm view of loops, whereas the loop we're building here is not a
4472 // wasm-level loop.
4473 // ==== Create the "loop" and "after" blocks ====
4474 MBasicBlock* loopBlock;
4475 if (!newBlock(curBlock_, &loopBlock, MBasicBlock::LOOP_HEADER)) {
4476 return false;
4477 }
4478 MBasicBlock* afterBlock;
4479 if (!newBlock(loopBlock, &afterBlock)) {
4480 return false;
4481 }
4482
4483 // ==== Fill in the remainder of the block preceding the loop ====
4484 MAdd* limit = MAdd::NewWasm(alloc(), index, numElements, MIRType::Int32);
4485 if (!limit) {
4486 return false;
4487 }
4488 curBlock_->add(limit);
4489
4490 // Use JSOp::StrictEq, not ::Eq, so that the comparison (and eventually
4491 // the entire initialisation loop) will be folded out in the case where
4492 // the number of elements is zero. See MCompare::tryFoldEqualOperands.
4493 MDefinition* limitEqualsBase =
4494 compare(limit, index, JSOp::StrictEq, MCompare::Compare_UInt32);
4495 if (!limitEqualsBase) {
4496 return false;
4497 }
4498 MTest* skipIfLimitEqualsBase =
4499 MTest::New(alloc(), limitEqualsBase, afterBlock, loopBlock);
4500 if (!skipIfLimitEqualsBase) {
4501 return false;
4502 }
4503 curBlock_->end(skipIfLimitEqualsBase);
4504 if (!afterBlock->addPredecessor(alloc(), curBlock_)) {
4505 return false;
4506 }
4507
4508 // ==== Fill in the loop block as best we can ====
4509 curBlock_ = loopBlock;
4510 MPhi* indexPhi = MPhi::New(alloc(), MIRType::Int32);
4511 if (!indexPhi) {
4512 return false;
4513 }
4514 if (!indexPhi->reserveLength(2)) {
4515 return false;
4516 }
4517 indexPhi->addInput(index);
4518 curBlock_->addPhi(indexPhi);
4519 curBlock_->setLoopDepth(loopDepth_ + 1);
4520
4521 if (!writeGcValueAtBasePlusScaledIndex(
4522 lineOrBytecode, elemType, arrayObject, AliasSet::WasmArrayDataArea,
4523 val, arrayBase, elemSize, indexPhi, preBarrierKind)) {
4524 return false;
4525 }
4526
4527 auto* indexNext =
4528 MAdd::NewWasm(alloc(), indexPhi, constantI32(1), MIRType::Int32);
4529 if (!indexNext) {
4530 return false;
4531 }
4532 curBlock_->add(indexNext);
4533 indexPhi->addInput(indexNext);
4534
4535 MDefinition* indexNextLtuLimit =
4536 compare(indexNext, limit, JSOp::Lt, MCompare::Compare_UInt32);
4537 if (!indexNextLtuLimit) {
4538 return false;
4539 }
4540 auto* continueIfIndexNextLtuLimit =
4541 MTest::New(alloc(), indexNextLtuLimit, loopBlock, afterBlock);
4542 if (!continueIfIndexNextLtuLimit) {
4543 return false;
4544 }
4545 curBlock_->end(continueIfIndexNextLtuLimit);
4546 if (!loopBlock->addPredecessor(alloc(), loopBlock)) {
4547 return false;
4548 }
4549 // ==== Loop block completed ====
4550
4551 curBlock_ = afterBlock;
4552 return true;
4553 }
4554
4555 // This routine generates all MIR required for `array.new`. The returned
4556 // value is for the newly created array.
4557 [[nodiscard]] MDefinition* createArrayNewCallAndLoop(uint32_t lineOrBytecode,
4558 uint32_t typeIndex,
4559 MDefinition* numElements,
4560 MDefinition* fillValue) {
4561 const ArrayType& arrayType = (*moduleEnv_.types)[typeIndex].arrayType();
4562
4563 // Create the array object, uninitialized.
4564 MDefinition* arrayObject =
4565 createArrayObject(lineOrBytecode, typeIndex, numElements,
4566 arrayType.elementType_.size(), /*zeroFields=*/false);
4567 if (!arrayObject) {
4568 return nullptr;
4569 }
4570
4571 // Optimisation opportunity: if the fill value is zero, maybe we should
4572 // likewise skip over the initialisation loop entirely (and, if the zero
4573 // value is visible at JIT time, the loop will be removed). For the
4574 // reftyped case, that would be a big win since each iteration requires a
4575 // call to the post-write barrier routine.
4576
4577 if (!fillArray(lineOrBytecode, arrayType, arrayObject, constantI32(0),
4578 numElements, fillValue, WasmPreBarrierKind::None)) {
4579 return nullptr;
4580 }
4581
4582 return arrayObject;
4583 }
4584
4585 [[nodiscard]] bool createArrayFill(uint32_t lineOrBytecode,
4586 uint32_t typeIndex,
4587 MDefinition* arrayObject,
4588 MDefinition* index, MDefinition* val,
4589 MDefinition* numElements) {
4590 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"
, 4590); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arrayObject->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4590; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4591 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"
, 4591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4591; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4592 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"
, 4592); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numElements->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4592; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4593
4594 const ArrayType& arrayType = (*moduleEnv_.types)[typeIndex].arrayType();
4595
4596 // Check for null is done in getWasmArrayObjectNumElements.
4597
4598 // Get the array's actual size.
4599 MDefinition* actualNumElements = getWasmArrayObjectNumElements(arrayObject);
4600 if (!actualNumElements) {
4601 return false;
4602 }
4603
4604 // Create a bounds check.
4605 auto* boundsCheck = MWasmBoundsCheckRange32::New(
4606 alloc(), index, numElements, actualNumElements, bytecodeOffset());
4607 if (!boundsCheck) {
4608 return false;
4609 }
4610 curBlock_->add(boundsCheck);
4611
4612 return fillArray(lineOrBytecode, arrayType, arrayObject, index, numElements,
4613 val, WasmPreBarrierKind::Normal);
4614 }
4615
4616 /*********************************************** WasmGC: other helpers ***/
4617
4618 // Generate MIR that causes a trap of kind `trapKind` if `arg` is zero.
4619 // Currently `arg` may only be a MIRType::Int32, but that requirement could
4620 // be relaxed if needed in future.
4621 [[nodiscard]] bool trapIfZero(wasm::Trap trapKind, MDefinition* arg) {
4622 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"
, 4622); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 4622; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4623
4624 MBasicBlock* trapBlock = nullptr;
4625 if (!newBlock(curBlock_, &trapBlock)) {
4626 return false;
4627 }
4628
4629 auto* trap = MWasmTrap::New(alloc(), trapKind, bytecodeOffset());
4630 if (!trap) {
4631 return false;
4632 }
4633 trapBlock->end(trap);
4634
4635 MBasicBlock* joinBlock = nullptr;
4636 if (!newBlock(curBlock_, &joinBlock)) {
4637 return false;
4638 }
4639
4640 auto* test = MTest::New(alloc(), arg, joinBlock, trapBlock);
4641 if (!test) {
4642 return false;
4643 }
4644 curBlock_->end(test);
4645 curBlock_ = joinBlock;
4646 return true;
4647 }
4648
4649 [[nodiscard]] MDefinition* isRefSubtypeOf(MDefinition* ref,
4650 RefType sourceType,
4651 RefType destType) {
4652 MInstruction* isSubTypeOf = nullptr;
4653 if (destType.isTypeRef()) {
4654 uint32_t typeIndex = moduleEnv_.types->indexOf(*destType.typeDef());
4655 MDefinition* superSTV = loadSuperTypeVector(typeIndex);
4656 isSubTypeOf = MWasmRefIsSubtypeOfConcrete::New(alloc(), ref, superSTV,
4657 sourceType, destType);
4658 } else {
4659 isSubTypeOf =
4660 MWasmRefIsSubtypeOfAbstract::New(alloc(), ref, sourceType, destType);
4661 }
4662 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"
, 4662); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSubTypeOf"
")"); do { *((volatile int*)__null) = 4662; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4663
4664 curBlock_->add(isSubTypeOf);
4665 return isSubTypeOf;
4666 }
4667
4668 // Generate MIR that attempts to downcast `ref` to `castToTypeDef`. If the
4669 // downcast fails, we trap. If it succeeds, then `ref` can be assumed to
4670 // have a type that is a subtype of (or the same as) `castToTypeDef` after
4671 // this point.
4672 [[nodiscard]] bool refCast(MDefinition* ref, RefType sourceType,
4673 RefType destType) {
4674 MDefinition* success = isRefSubtypeOf(ref, sourceType, destType);
4675 if (!success) {
4676 return false;
4677 }
4678
4679 // Trap if `success` is zero. If it's nonzero, we have established that
4680 // `ref <: castToTypeDef`.
4681 return trapIfZero(wasm::Trap::BadCast, success);
4682 }
4683
4684 // Generate MIR that computes a boolean value indicating whether or not it
4685 // is possible to downcast `ref` to `destType`.
4686 [[nodiscard]] MDefinition* refTest(MDefinition* ref, RefType sourceType,
4687 RefType destType) {
4688 return isRefSubtypeOf(ref, sourceType, destType);
4689 }
4690
4691 // Generates MIR for br_on_cast and br_on_cast_fail.
4692 [[nodiscard]] bool brOnCastCommon(bool onSuccess, uint32_t labelRelativeDepth,
4693 RefType sourceType, RefType destType,
4694 const ResultType& labelType,
4695 const DefVector& values) {
4696 if (inDeadCode()) {
4697 return true;
4698 }
4699
4700 MBasicBlock* fallthroughBlock = nullptr;
4701 if (!newBlock(curBlock_, &fallthroughBlock)) {
4702 return false;
4703 }
4704
4705 // `values` are the values in the top block-value on the stack. Since the
4706 // argument to `br_on_cast{_fail}` is at the top of the stack, it is the
4707 // last element in `values`.
4708 //
4709 // For both br_on_cast and br_on_cast_fail, the OpIter validation routines
4710 // ensure that `values` is non-empty (by rejecting the case
4711 // `labelType->length() < 1`) and that the last value in `values` is
4712 // reftyped.
4713 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"
, 4713); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "values.length() > 0"
")"); do { *((volatile int*)__null) = 4713; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4714 MDefinition* ref = values.back();
4715 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"
, 4715); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4715; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4716
4717 MDefinition* success = isRefSubtypeOf(ref, sourceType, destType);
4718 if (!success) {
4719 return false;
4720 }
4721
4722 MTest* test;
4723 if (onSuccess) {
4724 test = MTest::New(alloc(), success, nullptr, fallthroughBlock);
4725 if (!test || !addControlFlowPatch(test, labelRelativeDepth,
4726 MTest::TrueBranchIndex)) {
4727 return false;
4728 }
4729 } else {
4730 test = MTest::New(alloc(), success, fallthroughBlock, nullptr);
4731 if (!test || !addControlFlowPatch(test, labelRelativeDepth,
4732 MTest::FalseBranchIndex)) {
4733 return false;
4734 }
4735 }
4736
4737 if (!pushDefs(values)) {
4738 return false;
4739 }
4740
4741 curBlock_->end(test);
4742 curBlock_ = fallthroughBlock;
4743 return true;
4744 }
4745
4746 [[nodiscard]] bool brOnNonStruct(const DefVector& values) {
4747 if (inDeadCode()) {
4748 return true;
4749 }
4750
4751 MBasicBlock* fallthroughBlock = nullptr;
4752 if (!newBlock(curBlock_, &fallthroughBlock)) {
4753 return false;
4754 }
4755
4756 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"
, 4756); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.length() > 0"
")"); do { *((volatile int*)__null) = 4756; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4757 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"
, 4757); AnnotateMozCrashReason("MOZ_ASSERT" "(" "values.back()->type() == MIRType::WasmAnyRef"
")"); do { *((volatile int*)__null) = 4757; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4758
4759 MGoto* jump = MGoto::New(alloc(), fallthroughBlock);
4760 if (!jump) {
4761 return false;
4762 }
4763 if (!pushDefs(values)) {
4764 return false;
4765 }
4766
4767 curBlock_->end(jump);
4768 curBlock_ = fallthroughBlock;
4769 return true;
4770 }
4771
4772 /************************************************************ DECODING ***/
4773
4774 // AsmJS adds a line number to `callSiteLineNums` for certain operations that
4775 // are represented by a JS call, such as math builtins. We use these line
4776 // numbers when calling builtins. This method will read from
4777 // `callSiteLineNums` when we are using AsmJS, or else return the current
4778 // bytecode offset.
4779 //
4780 // This method MUST be called from opcodes that AsmJS will emit a call site
4781 // line number for, or else the arrays will get out of sync. Other opcodes
4782 // must use `readBytecodeOffset` below.
4783 uint32_t readCallSiteLineOrBytecode() {
4784 if (!func_.callSiteLineNums.empty()) {
4785 return func_.callSiteLineNums[lastReadCallSite_++];
4786 }
4787 return iter_.lastOpcodeOffset();
4788 }
4789
4790 // Return the current bytecode offset.
4791 uint32_t readBytecodeOffset() { return iter_.lastOpcodeOffset(); }
4792
4793 TrapSiteInfo getTrapSiteInfo() {
4794 return TrapSiteInfo(wasm::BytecodeOffset(readBytecodeOffset()));
4795 }
4796
4797#if DEBUG1
4798 bool done() const { return iter_.done(); }
4799#endif
4800
4801 /*************************************************************************/
4802 private:
4803 [[nodiscard]] bool newBlock(MBasicBlock* pred, MBasicBlock** block,
4804 MBasicBlock::Kind kind = MBasicBlock::NORMAL) {
4805 *block = MBasicBlock::New(mirGraph(), info(), pred, kind);
4806 if (!*block) {
4807 return false;
4808 }
4809 mirGraph().addBlock(*block);
4810 (*block)->setLoopDepth(loopDepth_);
4811 return true;
4812 }
4813
4814 [[nodiscard]] bool goToNewBlock(MBasicBlock* pred, MBasicBlock** block) {
4815 if (!newBlock(pred, block)) {
4816 return false;
4817 }
4818 pred->end(MGoto::New(alloc(), *block));
4819 return true;
4820 }
4821
4822 [[nodiscard]] bool goToExistingBlock(MBasicBlock* prev, MBasicBlock* next) {
4823 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"
, 4823); AnnotateMozCrashReason("MOZ_ASSERT" "(" "prev" ")");
do { *((volatile int*)__null) = 4823; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4824 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"
, 4824); AnnotateMozCrashReason("MOZ_ASSERT" "(" "next" ")");
do { *((volatile int*)__null) = 4824; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4825 prev->end(MGoto::New(alloc(), next));
4826 return next->addPredecessor(alloc(), prev);
4827 }
4828
4829 [[nodiscard]] bool bindBranches(uint32_t absolute, DefVector* defs) {
4830 if (absolute >= pendingBlocks_.length() ||
4831 pendingBlocks_[absolute].patches.empty()) {
4832 return inDeadCode() || popPushedDefs(defs);
4833 }
4834
4835 ControlFlowPatchVector& patches = pendingBlocks_[absolute].patches;
4836 MControlInstruction* ins = patches[0].ins;
4837 MBasicBlock* pred = ins->block();
4838
4839 MBasicBlock* join = nullptr;
4840 if (!newBlock(pred, &join)) {
4841 return false;
4842 }
4843
4844 // Use branch hinting information if any.
4845 if (pendingBlocks_[absolute].hint != BranchHint::Invalid) {
4846 join->setBranchHinting(pendingBlocks_[absolute].hint);
4847 }
4848
4849 pred->mark();
4850 ins->replaceSuccessor(patches[0].index, join);
4851
4852 for (size_t i = 1; i < patches.length(); i++) {
4853 ins = patches[i].ins;
4854
4855 pred = ins->block();
4856 if (!pred->isMarked()) {
4857 if (!join->addPredecessor(alloc(), pred)) {
4858 return false;
4859 }
4860 pred->mark();
4861 }
4862
4863 ins->replaceSuccessor(patches[i].index, join);
4864 }
4865
4866 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"
, 4866); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!curBlock_->isMarked()"
")"); do { *((volatile int*)__null) = 4866; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
4867 for (uint32_t i = 0; i < join->numPredecessors(); i++) {
4868 join->getPredecessor(i)->unmark();
4869 }
4870
4871 if (curBlock_ && !goToExistingBlock(curBlock_, join)) {
4872 return false;
4873 }
4874
4875 curBlock_ = join;
4876
4877 if (!popPushedDefs(defs)) {
4878 return false;
4879 }
4880
4881 patches.clear();
4882 return true;
4883 }
4884};
4885
4886template <>
4887MDefinition* FunctionCompiler::unary<MToFloat32>(MDefinition* op) {
4888 if (inDeadCode()) {
4889 return nullptr;
4890 }
4891 auto* ins = MToFloat32::New(alloc(), op, mustPreserveNaN(op->type()));
4892 curBlock_->add(ins);
4893 return ins;
4894}
4895
4896template <>
4897MDefinition* FunctionCompiler::unary<MWasmBuiltinTruncateToInt32>(
4898 MDefinition* op) {
4899 if (inDeadCode()) {
4900 return nullptr;
4901 }
4902 auto* ins = MWasmBuiltinTruncateToInt32::New(alloc(), op, instancePointer_,
4903 bytecodeOffset());
4904 curBlock_->add(ins);
4905 return ins;
4906}
4907
4908template <>
4909MDefinition* FunctionCompiler::unary<MNot>(MDefinition* op) {
4910 if (inDeadCode()) {
4911 return nullptr;
4912 }
4913 auto* ins = MNot::NewInt32(alloc(), op);
4914 curBlock_->add(ins);
4915 return ins;
4916}
4917
4918template <>
4919MDefinition* FunctionCompiler::unary<MAbs>(MDefinition* op, MIRType type) {
4920 if (inDeadCode()) {
4921 return nullptr;
4922 }
4923 auto* ins = MAbs::NewWasm(alloc(), op, type);
4924 curBlock_->add(ins);
4925 return ins;
4926}
4927
4928} // end anonymous namespace
4929
4930static bool EmitI32Const(FunctionCompiler& f) {
4931 int32_t i32;
4932 if (!f.iter().readI32Const(&i32)) {
4933 return false;
4934 }
4935
4936 f.iter().setResult(f.constantI32(i32));
4937 return true;
4938}
4939
4940static bool EmitI64Const(FunctionCompiler& f) {
4941 int64_t i64;
4942 if (!f.iter().readI64Const(&i64)) {
4943 return false;
4944 }
4945
4946 f.iter().setResult(f.constantI64(i64));
4947 return true;
4948}
4949
4950static bool EmitF32Const(FunctionCompiler& f) {
4951 float f32;
4952 if (!f.iter().readF32Const(&f32)) {
4953 return false;
4954 }
4955
4956 f.iter().setResult(f.constantF32(f32));
4957 return true;
4958}
4959
4960static bool EmitF64Const(FunctionCompiler& f) {
4961 double f64;
4962 if (!f.iter().readF64Const(&f64)) {
4963 return false;
4964 }
4965
4966 f.iter().setResult(f.constantF64(f64));
4967 return true;
4968}
4969
4970static bool EmitBlock(FunctionCompiler& f) {
4971 ResultType params;
4972 return f.iter().readBlock(&params) && f.startBlock();
4973}
4974
4975static bool EmitLoop(FunctionCompiler& f) {
4976 ResultType params;
4977 if (!f.iter().readLoop(&params)) {
4978 return false;
4979 }
4980
4981 MBasicBlock* loopHeader;
4982 if (!f.startLoop(&loopHeader, params.length())) {
4983 return false;
4984 }
4985
4986 f.addInterruptCheck();
4987
4988 f.iter().controlItem().block = loopHeader;
4989 return true;
4990}
4991
4992static bool EmitIf(FunctionCompiler& f) {
4993 BranchHint branchHint =
4994 f.iter().getBranchHint(f.funcIndex(), f.relativeBytecodeOffset());
4995
4996 ResultType params;
4997 MDefinition* condition = nullptr;
4998 if (!f.iter().readIf(&params, &condition)) {
4999 return false;
5000 }
5001
5002 MBasicBlock* elseBlock;
5003 if (!f.branchAndStartThen(condition, &elseBlock)) {
5004 return false;
5005 }
5006
5007 // Store the branch hint in the basic block.
5008 if (!f.inDeadCode() && branchHint != BranchHint::Invalid) {
5009 f.getCurBlock()->setBranchHinting(branchHint);
5010 }
5011
5012 f.iter().controlItem().block = elseBlock;
5013 return true;
5014}
5015
5016static bool EmitElse(FunctionCompiler& f) {
5017 ResultType paramType;
5018 ResultType resultType;
5019 DefVector thenValues;
5020 if (!f.iter().readElse(&paramType, &resultType, &thenValues)) {
5021 return false;
5022 }
5023
5024 if (!f.pushDefs(thenValues)) {
5025 return false;
5026 }
5027
5028 Control& control = f.iter().controlItem();
5029 return f.switchToElse(control.block, &control.block);
5030}
5031
5032static bool EmitEnd(FunctionCompiler& f) {
5033 LabelKind kind;
5034 ResultType type;
5035 DefVector preJoinDefs;
5036 DefVector resultsForEmptyElse;
5037 if (!f.iter().readEnd(&kind, &type, &preJoinDefs, &resultsForEmptyElse)) {
5038 return false;
5039 }
5040
5041 Control& control = f.iter().controlItem();
5042 MBasicBlock* block = control.block;
5043
5044 if (!f.pushDefs(preJoinDefs)) {
5045 return false;
5046 }
5047
5048 // Every label case is responsible to pop the control item at the appropriate
5049 // time for the label case
5050 DefVector postJoinDefs;
5051 switch (kind) {
5052 case LabelKind::Body:
5053 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"
, 5053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5053; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5054 if (!f.emitBodyDelegateThrowPad(control)) {
5055 return false;
5056 }
5057 if (!f.finishBlock(&postJoinDefs)) {
5058 return false;
5059 }
5060 if (!f.returnValues(postJoinDefs)) {
5061 return false;
5062 }
5063 f.iter().popEnd();
5064 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"
, 5064); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f.iter().controlStackEmpty()"
")"); do { *((volatile int*)__null) = 5064; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5065 return f.iter().endFunction(f.iter().end());
5066 case LabelKind::Block:
5067 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"
, 5067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5067; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5068 if (!f.finishBlock(&postJoinDefs)) {
5069 return false;
5070 }
5071 f.iter().popEnd();
5072 break;
5073 case LabelKind::Loop:
5074 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"
, 5074); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5074; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5075 if (!f.closeLoop(block, &postJoinDefs)) {
5076 return false;
5077 }
5078 f.iter().popEnd();
5079 break;
5080 case LabelKind::Then: {
5081 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"
, 5081); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5081; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5082 // If we didn't see an Else, create a trivial else block so that we create
5083 // a diamond anyway, to preserve Ion invariants.
5084 if (!f.switchToElse(block, &block)) {
5085 return false;
5086 }
5087
5088 if (!f.pushDefs(resultsForEmptyElse)) {
5089 return false;
5090 }
5091
5092 if (!f.joinIfElse(block, &postJoinDefs)) {
5093 return false;
5094 }
5095 f.iter().popEnd();
5096 break;
5097 }
5098 case LabelKind::Else:
5099 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"
, 5099); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!control.tryControl"
")"); do { *((volatile int*)__null) = 5099; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5100 if (!f.joinIfElse(block, &postJoinDefs)) {
5101 return false;
5102 }
5103 f.iter().popEnd();
5104 break;
5105 case LabelKind::Try:
5106 case LabelKind::Catch:
5107 case LabelKind::CatchAll:
5108 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"
, 5108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5108; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5109 if (!f.finishTryCatch(kind, control, &postJoinDefs)) {
5110 return false;
5111 }
5112 f.freeTryControl(std::move(control.tryControl));
5113 f.iter().popEnd();
5114 break;
5115 case LabelKind::TryTable:
5116 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"
, 5116); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5116; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5117 if (!f.finishTryTable(control, &postJoinDefs)) {
5118 return false;
5119 }
5120 f.freeTryControl(std::move(control.tryControl));
5121 f.iter().popEnd();
5122 break;
5123 }
5124
5125 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"
, 5125); AnnotateMozCrashReason("MOZ_ASSERT" "(" "postJoinDefs.length() == type.length()"
")"); do { *((volatile int*)__null) = 5125; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5126 f.iter().setResults(postJoinDefs.length(), postJoinDefs);
5127
5128 return true;
5129}
5130
5131static bool EmitBr(FunctionCompiler& f) {
5132 uint32_t relativeDepth;
5133 ResultType type;
5134 DefVector values;
5135 if (!f.iter().readBr(&relativeDepth, &type, &values)) {
5136 return false;
5137 }
5138
5139 return f.br(relativeDepth, values);
5140}
5141
5142static bool EmitBrIf(FunctionCompiler& f) {
5143 uint32_t relativeDepth;
5144 ResultType type;
5145 DefVector values;
5146 MDefinition* condition;
5147
5148 BranchHint branchHint =
5149 f.iter().getBranchHint(f.funcIndex(), f.relativeBytecodeOffset());
5150
5151 if (!f.iter().readBrIf(&relativeDepth, &type, &values, &condition)) {
5152 return false;
5153 }
5154
5155 return f.brIf(relativeDepth, values, condition, branchHint);
5156}
5157
5158static bool EmitBrTable(FunctionCompiler& f) {
5159 Uint32Vector depths;
5160 uint32_t defaultDepth;
5161 ResultType branchValueType;
5162 DefVector branchValues;
5163 MDefinition* index;
5164 if (!f.iter().readBrTable(&depths, &defaultDepth, &branchValueType,
5165 &branchValues, &index)) {
5166 return false;
5167 }
5168
5169 // If all the targets are the same, or there are no targets, we can just
5170 // use a goto. This is not just an optimization: MaybeFoldConditionBlock
5171 // assumes that tables have more than one successor.
5172 bool allSameDepth = true;
5173 for (uint32_t depth : depths) {
5174 if (depth != defaultDepth) {
5175 allSameDepth = false;
5176 break;
5177 }
5178 }
5179
5180 if (allSameDepth) {
5181 return f.br(defaultDepth, branchValues);
5182 }
5183
5184 return f.brTable(index, defaultDepth, depths, branchValues);
5185}
5186
5187static bool EmitReturn(FunctionCompiler& f) {
5188 DefVector values;
5189 if (!f.iter().readReturn(&values)) {
5190 return false;
5191 }
5192
5193 return f.returnValues(values);
5194}
5195
5196static bool EmitUnreachable(FunctionCompiler& f) {
5197 if (!f.iter().readUnreachable()) {
5198 return false;
5199 }
5200
5201 f.unreachableTrap();
5202 return true;
5203}
5204
5205static bool EmitTry(FunctionCompiler& f) {
5206 ResultType params;
5207 if (!f.iter().readTry(&params)) {
5208 return false;
5209 }
5210
5211 return f.startTry();
5212}
5213
5214static bool EmitCatch(FunctionCompiler& f) {
5215 LabelKind kind;
5216 uint32_t tagIndex;
5217 ResultType paramType, resultType;
5218 DefVector tryValues;
5219 if (!f.iter().readCatch(&kind, &tagIndex, &paramType, &resultType,
5220 &tryValues)) {
5221 return false;
5222 }
5223
5224 // Pushing the results of the previous block, to properly join control flow
5225 // after the try and after each handler, as well as potential control flow
5226 // patches from other instrunctions. This is similar to what is done for
5227 // if-then-else control flow and for most other control control flow joins.
5228 if (!f.pushDefs(tryValues)) {
5229 return false;
5230 }
5231
5232 return f.switchToCatch(f.iter().controlItem(), kind, tagIndex);
5233}
5234
5235static bool EmitCatchAll(FunctionCompiler& f) {
5236 LabelKind kind;
5237 ResultType paramType, resultType;
5238 DefVector tryValues;
5239 if (!f.iter().readCatchAll(&kind, &paramType, &resultType, &tryValues)) {
5240 return false;
5241 }
5242
5243 // Pushing the results of the previous block, to properly join control flow
5244 // after the try and after each handler, as well as potential control flow
5245 // patches from other instrunctions.
5246 if (!f.pushDefs(tryValues)) {
5247 return false;
5248 }
5249
5250 return f.switchToCatch(f.iter().controlItem(), kind, CatchAllIndex);
5251}
5252
5253static bool EmitTryTable(FunctionCompiler& f) {
5254 ResultType params;
5255 TryTableCatchVector catches;
5256 if (!f.iter().readTryTable(&params, &catches)) {
5257 return false;
5258 }
5259
5260 return f.startTryTable(std::move(catches));
5261}
5262
5263static bool EmitDelegate(FunctionCompiler& f) {
5264 uint32_t relativeDepth;
5265 ResultType resultType;
5266 DefVector tryValues;
5267 if (!f.iter().readDelegate(&relativeDepth, &resultType, &tryValues)) {
5268 return false;
5269 }
5270
5271 Control& control = f.iter().controlItem();
5272 MBasicBlock* block = control.block;
5273 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"
, 5273); AnnotateMozCrashReason("MOZ_ASSERT" "(" "control.tryControl"
")"); do { *((volatile int*)__null) = 5273; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5274
5275 // Unless the entire try-delegate is dead code, delegate any pad-patches from
5276 // this try to the next try-block above relativeDepth.
5277 if (block) {
5278 ControlInstructionVector& delegatePadPatches =
5279 control.tryControl->landingPadPatches;
5280 if (!f.delegatePadPatches(delegatePadPatches, relativeDepth)) {
5281 return false;
5282 }
5283 }
5284 f.freeTryControl(std::move(control.tryControl));
5285 f.iter().popDelegate();
5286
5287 // Push the results of the previous block, and join control flow with
5288 // potential control flow patches from other instrunctions in the try code.
5289 // This is similar to what is done for EmitEnd.
5290 if (!f.pushDefs(tryValues)) {
5291 return false;
5292 }
5293 DefVector postJoinDefs;
5294 if (!f.finishBlock(&postJoinDefs)) {
5295 return false;
5296 }
5297 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"
, 5297); AnnotateMozCrashReason("MOZ_ASSERT" "(" "postJoinDefs.length() == resultType.length()"
")"); do { *((volatile int*)__null) = 5297; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5298 f.iter().setResults(postJoinDefs.length(), postJoinDefs);
5299
5300 return true;
5301}
5302
5303static bool EmitThrow(FunctionCompiler& f) {
5304 uint32_t tagIndex;
5305 DefVector argValues;
5306 if (!f.iter().readThrow(&tagIndex, &argValues)) {
5307 return false;
5308 }
5309
5310 return f.emitThrow(tagIndex, argValues);
5311}
5312
5313static bool EmitThrowRef(FunctionCompiler& f) {
5314 MDefinition* exnRef;
5315 if (!f.iter().readThrowRef(&exnRef)) {
5316 return false;
5317 }
5318
5319 return f.emitThrowRef(exnRef);
5320}
5321
5322static bool EmitRethrow(FunctionCompiler& f) {
5323 uint32_t relativeDepth;
5324 if (!f.iter().readRethrow(&relativeDepth)) {
5325 return false;
5326 }
5327
5328 return f.emitRethrow(relativeDepth);
5329}
5330
5331static bool EmitCallArgs(FunctionCompiler& f, const FuncType& funcType,
5332 const DefVector& args, CallCompileState* call) {
5333 for (size_t i = 0, n = funcType.args().length(); i < n; ++i) {
5334 if (!f.mirGen().ensureBallast()) {
5335 return false;
5336 }
5337 if (!f.passArg(args[i], funcType.args()[i], call)) {
5338 return false;
5339 }
5340 }
5341
5342 ResultType resultType = ResultType::Vector(funcType.results());
5343 if (!f.passStackResultAreaCallArg(resultType, call)) {
5344 return false;
5345 }
5346
5347 return f.finishCall(call);
5348}
5349
5350static bool EmitCall(FunctionCompiler& f, bool asmJSFuncDef) {
5351 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5352
5353 uint32_t funcIndex;
5354 DefVector args;
5355 if (asmJSFuncDef) {
5356 if (!f.iter().readOldCallDirect(f.moduleEnv().numFuncImports, &funcIndex,
5357 &args)) {
5358 return false;
5359 }
5360 } else {
5361 if (!f.iter().readCall(&funcIndex, &args)) {
5362 return false;
5363 }
5364 }
5365
5366 if (f.inDeadCode()) {
5367 return true;
5368 }
5369
5370 const FuncType& funcType = *f.moduleEnv().funcs[funcIndex].type;
5371
5372 CallCompileState call;
5373 if (!EmitCallArgs(f, funcType, args, &call)) {
5374 return false;
5375 }
5376
5377 DefVector results;
5378 if (f.moduleEnv().funcIsImport(funcIndex)) {
5379 uint32_t instanceDataOffset =
5380 f.moduleEnv().offsetOfFuncImportInstanceData(funcIndex);
5381 if (!f.callImport(instanceDataOffset, lineOrBytecode, call, funcType,
5382 &results)) {
5383 return false;
5384 }
5385 } else {
5386 if (!f.callDirect(funcType, funcIndex, lineOrBytecode, call, &results)) {
5387 return false;
5388 }
5389 }
5390
5391 f.iter().setResults(results.length(), results);
5392 return true;
5393}
5394
5395static bool EmitCallIndirect(FunctionCompiler& f, bool oldStyle) {
5396 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5397
5398 uint32_t funcTypeIndex;
5399 uint32_t tableIndex;
5400 MDefinition* callee;
5401 DefVector args;
5402 if (oldStyle) {
5403 tableIndex = 0;
5404 if (!f.iter().readOldCallIndirect(&funcTypeIndex, &callee, &args)) {
5405 return false;
5406 }
5407 } else {
5408 if (!f.iter().readCallIndirect(&funcTypeIndex, &tableIndex, &callee,
5409 &args)) {
5410 return false;
5411 }
5412 }
5413
5414 if (f.inDeadCode()) {
5415 return true;
5416 }
5417
5418 const FuncType& funcType = (*f.moduleEnv().types)[funcTypeIndex].funcType();
5419
5420 CallCompileState call;
5421 if (!EmitCallArgs(f, funcType, args, &call)) {
5422 return false;
5423 }
5424
5425 DefVector results;
5426 if (!f.callIndirect(funcTypeIndex, tableIndex, callee, lineOrBytecode, call,
5427 &results)) {
5428 return false;
5429 }
5430
5431 f.iter().setResults(results.length(), results);
5432 return true;
5433}
5434
5435#ifdef ENABLE_WASM_JSPI1
5436static bool EmitStackSwitch(FunctionCompiler& f) {
5437 StackSwitchKind kind;
5438 MDefinition* suspender;
5439 MDefinition* fn;
5440 MDefinition* data;
5441 if (!f.iter().readStackSwitch(&kind, &suspender, &fn, &data)) {
5442 return false;
5443 }
5444 if (!f.stackSwitch(suspender, fn, data, kind)) {
5445 return false;
5446 }
5447 return true;
5448}
5449#endif
5450
5451#ifdef ENABLE_WASM_TAIL_CALLS1
5452static bool EmitReturnCall(FunctionCompiler& f) {
5453 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5454
5455 uint32_t funcIndex;
5456 DefVector args;
5457 if (!f.iter().readReturnCall(&funcIndex, &args)) {
5458 return false;
5459 }
5460
5461 if (f.inDeadCode()) {
5462 return true;
5463 }
5464
5465 const FuncType& funcType = *f.moduleEnv().funcs[funcIndex].type;
5466
5467 CallCompileState call;
5468 f.markReturnCall(&call);
5469 if (!EmitCallArgs(f, funcType, args, &call)) {
5470 return false;
5471 }
5472
5473 DefVector results;
5474 if (f.moduleEnv().funcIsImport(funcIndex)) {
5475 uint32_t globalDataOffset =
5476 f.moduleEnv().offsetOfFuncImportInstanceData(funcIndex);
5477 if (!f.returnCallImport(globalDataOffset, lineOrBytecode, call, funcType,
5478 &results)) {
5479 return false;
5480 }
5481 } else {
5482 if (!f.returnCallDirect(funcType, funcIndex, lineOrBytecode, call,
5483 &results)) {
5484 return false;
5485 }
5486 }
5487 return true;
5488}
5489
5490static bool EmitReturnCallIndirect(FunctionCompiler& f) {
5491 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5492
5493 uint32_t funcTypeIndex;
5494 uint32_t tableIndex;
5495 MDefinition* callee;
5496 DefVector args;
5497 if (!f.iter().readReturnCallIndirect(&funcTypeIndex, &tableIndex, &callee,
5498 &args)) {
5499 return false;
5500 }
5501
5502 if (f.inDeadCode()) {
5503 return true;
5504 }
5505
5506 const FuncType& funcType = (*f.moduleEnv().types)[funcTypeIndex].funcType();
5507
5508 CallCompileState call;
5509 f.markReturnCall(&call);
5510 if (!EmitCallArgs(f, funcType, args, &call)) {
5511 return false;
5512 }
5513
5514 DefVector results;
5515 return f.returnCallIndirect(funcTypeIndex, tableIndex, callee, lineOrBytecode,
5516 call, &results);
5517}
5518#endif
5519
5520#if defined(ENABLE_WASM_TAIL_CALLS1) && defined(ENABLE_WASM_GC1)
5521static bool EmitReturnCallRef(FunctionCompiler& f) {
5522 uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
5523
5524 const FuncType* funcType;
5525 MDefinition* callee;
5526 DefVector args;
5527
5528 if (!f.iter().readReturnCallRef(&funcType, &callee, &args)) {
5529 return false;
5530 }
5531
5532 if (f.inDeadCode()) {
5533 return true;
5534 }
5535
5536 CallCompileState call;
5537 f.markReturnCall(&call);
5538 if (!EmitCallArgs(f, *funcType, args, &call)) {
5539 return false;
5540 }
5541
5542 DefVector results;
5543 return f.returnCallRef(*funcType, callee, lineOrBytecode, call, &results);
5544}
5545#endif
5546
5547static bool EmitGetLocal(FunctionCompiler& f) {
5548 uint32_t id;
5549 if (!f.iter().readGetLocal(f.locals(), &id)) {
5550 return false;
5551 }
5552
5553 f.iter().setResult(f.getLocalDef(id));
5554 return true;
5555}
5556
5557static bool EmitSetLocal(FunctionCompiler& f) {
5558 uint32_t id;
5559 MDefinition* value;
5560 if (!f.iter().readSetLocal(f.locals(), &id, &value)) {
5561 return false;
5562 }
5563
5564 f.assign(id, value);
5565 return true;
5566}
5567
5568static bool EmitTeeLocal(FunctionCompiler& f) {
5569 uint32_t id;
5570 MDefinition* value;
5571 if (!f.iter().readTeeLocal(f.locals(), &id, &value)) {
5572 return false;
5573 }
5574
5575 f.assign(id, value);
5576 return true;
5577}
5578
5579static bool EmitGetGlobal(FunctionCompiler& f) {
5580 uint32_t id;
5581 if (!f.iter().readGetGlobal(&id)) {
5582 return false;
5583 }
5584
5585 const GlobalDesc& global = f.moduleEnv().globals[id];
5586 if (!global.isConstant()) {
5587 f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(),
5588 global.isIndirect(),
5589 global.type().toMIRType()));
5590 return true;
5591 }
5592
5593 LitVal value = global.constantValue();
5594
5595 MDefinition* result;
5596 switch (value.type().kind()) {
5597 case ValType::I32:
5598 result = f.constantI32(int32_t(value.i32()));
5599 break;
5600 case ValType::I64:
5601 result = f.constantI64(int64_t(value.i64()));
5602 break;
5603 case ValType::F32:
5604 result = f.constantF32(value.f32());
5605 break;
5606 case ValType::F64:
5607 result = f.constantF64(value.f64());
5608 break;
5609 case ValType::V128:
5610#ifdef ENABLE_WASM_SIMD1
5611 result = f.constantV128(value.v128());
5612 break;
5613#else
5614 return f.iter().fail("Ion has no SIMD support yet");
5615#endif
5616 case ValType::Ref:
5617 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"
, 5617); AnnotateMozCrashReason("MOZ_ASSERT" "(" "value.ref().isNull()"
")"); do { *((volatile int*)__null) = 5617; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5618 result = f.constantNullRef();
5619 break;
5620 default:
5621 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"
, 5621); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in EmitGetGlobal"
")"); do { *((volatile int*)__null) = 5621; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
5622 }
5623
5624 f.iter().setResult(result);
5625 return true;
5626}
5627
5628static bool EmitSetGlobal(FunctionCompiler& f) {
5629 uint32_t bytecodeOffset = f.readBytecodeOffset();
5630
5631 uint32_t id;
5632 MDefinition* value;
5633 if (!f.iter().readSetGlobal(&id, &value)) {
5634 return false;
5635 }
5636
5637 const GlobalDesc& global = f.moduleEnv().globals[id];
5638 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"
, 5638); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global.isMutable()"
")"); do { *((volatile int*)__null) = 5638; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5639 return f.storeGlobalVar(bytecodeOffset, global.offset(), global.isIndirect(),
5640 value);
5641}
5642
5643static bool EmitTeeGlobal(FunctionCompiler& f) {
5644 uint32_t bytecodeOffset = f.readBytecodeOffset();
5645
5646 uint32_t id;
5647 MDefinition* value;
5648 if (!f.iter().readTeeGlobal(&id, &value)) {
5649 return false;
5650 }
5651
5652 const GlobalDesc& global = f.moduleEnv().globals[id];
5653 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"
, 5653); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global.isMutable()"
")"); do { *((volatile int*)__null) = 5653; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5654
5655 return f.storeGlobalVar(bytecodeOffset, global.offset(), global.isIndirect(),
5656 value);
5657}
5658
5659template <typename MIRClass>
5660static bool EmitUnary(FunctionCompiler& f, ValType operandType) {
5661 MDefinition* input;
5662 if (!f.iter().readUnary(operandType, &input)) {
5663 return false;
5664 }
5665
5666 f.iter().setResult(f.unary<MIRClass>(input));
5667 return true;
5668}
5669
5670template <typename MIRClass>
5671static bool EmitConversion(FunctionCompiler& f, ValType operandType,
5672 ValType resultType) {
5673 MDefinition* input;
5674 if (!f.iter().readConversion(operandType, resultType, &input)) {
5675 return false;
5676 }
5677
5678 f.iter().setResult(f.unary<MIRClass>(input));
5679 return true;
5680}
5681
5682template <typename MIRClass>
5683static bool EmitUnaryWithType(FunctionCompiler& f, ValType operandType,
5684 MIRType mirType) {
5685 MDefinition* input;
5686 if (!f.iter().readUnary(operandType, &input)) {
5687 return false;
5688 }
5689
5690 f.iter().setResult(f.unary<MIRClass>(input, mirType));
5691 return true;
5692}
5693
5694template <typename MIRClass>
5695static bool EmitConversionWithType(FunctionCompiler& f, ValType operandType,
5696 ValType resultType, MIRType mirType) {
5697 MDefinition* input;
5698 if (!f.iter().readConversion(operandType, resultType, &input)) {
5699 return false;
5700 }
5701
5702 f.iter().setResult(f.unary<MIRClass>(input, mirType));
5703 return true;
5704}
5705
5706static bool EmitTruncate(FunctionCompiler& f, ValType operandType,
5707 ValType resultType, bool isUnsigned,
5708 bool isSaturating) {
5709 MDefinition* input = nullptr;
5710 if (!f.iter().readConversion(operandType, resultType, &input)) {
5711 return false;
5712 }
5713
5714 TruncFlags flags = 0;
5715 if (isUnsigned) {
5716 flags |= TRUNC_UNSIGNED;
5717 }
5718 if (isSaturating) {
5719 flags |= TRUNC_SATURATING;
5720 }
5721 if (resultType == ValType::I32) {
5722 if (f.moduleEnv().isAsmJS()) {
5723 if (input && (input->type() == MIRType::Double ||
5724 input->type() == MIRType::Float32)) {
5725 f.iter().setResult(f.unary<MWasmBuiltinTruncateToInt32>(input));
5726 } else {
5727 f.iter().setResult(f.unary<MTruncateToInt32>(input));
5728 }
5729 } else {
5730 f.iter().setResult(f.truncate<MWasmTruncateToInt32>(input, flags));
5731 }
5732 } else {
5733 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"
, 5733); AnnotateMozCrashReason("MOZ_ASSERT" "(" "resultType == ValType::I64"
")"); do { *((volatile int*)__null) = 5733; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5734 MOZ_ASSERT(!f.moduleEnv().isAsmJS())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!f.moduleEnv().isAsmJS())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!f.moduleEnv().isAsmJS()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!f.moduleEnv().isAsmJS()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmIonCompile.cpp"
, 5734); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!f.moduleEnv().isAsmJS()"
")"); do { *((volatile int*)__null) = 5734; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5735#if defined(JS_CODEGEN_ARM)
5736 f.iter().setResult(f.truncateWithInstance(input, flags));
5737#else
5738 f.iter().setResult(f.truncate<MWasmTruncateToInt64>(input, flags));
5739#endif
5740 }
5741 return true;
5742}
5743
5744static bool EmitSignExtend(FunctionCompiler& f, uint32_t srcSize,
5745 uint32_t targetSize) {
5746 MDefinition* input;
5747 ValType type = targetSize == 4 ? ValType::I32 : ValType::I64;
5748 if (!f.iter().readConversion(type, type, &input)) {
5749 return false;
5750 }
5751
5752 f.iter().setResult(f.signExtend(input, srcSize, targetSize));
5753 return true;
5754}
5755
5756static bool EmitExtendI32(FunctionCompiler& f, bool isUnsigned) {
5757 MDefinition* input;
5758 if (!f.iter().readConversion(ValType::I32, ValType::I64, &input)) {
5759 return false;
5760 }
5761
5762 f.iter().setResult(f.extendI32(input, isUnsigned));
5763 return true;
5764}
5765
5766static bool EmitConvertI64ToFloatingPoint(FunctionCompiler& f,
5767 ValType resultType, MIRType mirType,
5768 bool isUnsigned) {
5769 MDefinition* input;
5770 if (!f.iter().readConversion(ValType::I64, resultType, &input)) {
5771 return false;
5772 }
5773
5774 f.iter().setResult(f.convertI64ToFloatingPoint(input, mirType, isUnsigned));
5775 return true;
5776}
5777
5778static bool EmitReinterpret(FunctionCompiler& f, ValType resultType,
5779 ValType operandType, MIRType mirType) {
5780 MDefinition* input;
5781 if (!f.iter().readConversion(operandType, resultType, &input)) {
5782 return false;
5783 }
5784
5785 f.iter().setResult(f.unary<MWasmReinterpret>(input, mirType));
5786 return true;
5787}
5788
5789static bool EmitAdd(FunctionCompiler& f, ValType type, MIRType mirType) {
5790 MDefinition* lhs;
5791 MDefinition* rhs;
5792 if (!f.iter().readBinary(type, &lhs, &rhs)) {
5793 return false;
5794 }
5795
5796 f.iter().setResult(f.add(lhs, rhs, mirType));
5797 return true;
5798}
5799
5800static bool EmitSub(FunctionCompiler& f, ValType type, MIRType mirType) {
5801 MDefinition* lhs;
5802 MDefinition* rhs;
5803 if (!f.iter().readBinary(type, &lhs, &rhs)) {
5804 return false