Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit/./../../../../js/src/jit/CodeGenerator.cpp
Warning:line 13451, column 8
Value stored to 'extractObject' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_jit3.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=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src -I /root/firefox-clang/js/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -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-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 -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_js_src_jit3.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "jit/CodeGenerator.h"
6
7#include "mozilla/Assertions.h"
8#include "mozilla/CheckedArithmetic.h"
9#include "mozilla/DebugOnly.h"
10#include "mozilla/EnumeratedArray.h"
11#include "mozilla/EnumeratedRange.h"
12#include "mozilla/EnumSet.h"
13#include "mozilla/IntegerTypeTraits.h"
14#include "mozilla/Latin1.h"
15#include "mozilla/MathAlgorithms.h"
16#include "mozilla/ScopeExit.h"
17#include "mozilla/SIMD.h"
18
19#include <algorithm>
20#include <bit>
21#include <cmath>
22#include <limits>
23#include <type_traits>
24#include <utility>
25
26#include "builtin/MapObject.h"
27#include "builtin/Math.h"
28#include "builtin/Number.h"
29#include "builtin/RegExp.h"
30#include "builtin/String.h"
31#include "irregexp/RegExpTypes.h"
32#include "jit/ABIArgGenerator.h"
33#include "jit/CompileInfo.h"
34#include "jit/InlineScriptTree.h"
35#include "jit/Invalidation.h"
36#include "jit/IonGenericCallStub.h"
37#include "jit/IonIC.h"
38#include "jit/IonScript.h"
39#include "jit/JitcodeMap.h"
40#include "jit/JitFrames.h"
41#include "jit/JitRuntime.h"
42#include "jit/JitSpewer.h"
43#include "jit/JitZone.h"
44#include "jit/Linker.h"
45#include "jit/MIRGenerator.h"
46#include "jit/MoveEmitter.h"
47#include "jit/RangeAnalysis.h"
48#include "jit/RegExpStubConstants.h"
49#include "jit/SafepointIndex.h"
50#include "jit/SharedICHelpers.h"
51#include "jit/SharedICRegisters.h"
52#include "jit/VMFunctions.h"
53#include "jit/WarpSnapshot.h"
54#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
55#include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}CallArgs, JSJitMethodCallArgsTraits, JSJitInfo
56#include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
57#include "js/RegExpFlags.h" // JS::RegExpFlag
58#include "js/ScalarType.h" // js::Scalar::Type
59#include "proxy/DOMProxy.h"
60#include "proxy/ScriptedProxyHandler.h"
61#include "util/DifferentialTesting.h"
62#include "util/PortableMath.h"
63#include "util/Unicode.h"
64#include "vm/ArrayBufferViewObject.h"
65#include "vm/AsyncFunction.h"
66#include "vm/AsyncIteration.h"
67#include "vm/BuiltinObjectKind.h"
68#include "vm/DateObject.h"
69#include "vm/FunctionFlags.h" // js::FunctionFlags
70#include "vm/Interpreter.h"
71#include "vm/JSAtomUtils.h" // AtomizeString
72#include "vm/MatchPairs.h"
73#include "vm/RegExpObject.h"
74#include "vm/RegExpStatics.h"
75#include "vm/RuntimeFuses.h"
76#include "vm/StaticStrings.h"
77#include "vm/StringObject.h"
78#include "vm/StringType.h"
79#include "vm/TypedArrayObject.h"
80#include "wasm/WasmCodegenConstants.h"
81#include "wasm/WasmPI.h"
82#include "wasm/WasmStacks.h"
83#include "wasm/WasmValType.h"
84#ifdef MOZ_VTUNE1
85# include "vtune/VTuneWrapper.h"
86#endif
87#include "wasm/WasmBinary.h"
88#include "wasm/WasmGC.h"
89#include "wasm/WasmGcObject.h"
90#include "wasm/WasmStubs.h"
91
92#include "builtin/Boolean-inl.h"
93#include "jit/MacroAssembler-inl.h"
94#include "jit/shared/CodeGenerator-shared-inl.h"
95#include "jit/TemplateObject-inl.h"
96#include "jit/VMFunctionList-inl.h"
97#include "vm/BytecodeUtil-inl.h"
98#include "vm/JSScript-inl.h"
99#include "wasm/WasmInstance-inl.h"
100
101using namespace js;
102using namespace js::jit;
103
104using mozilla::CheckedUint32;
105using mozilla::DebugOnly;
106using mozilla::FloatingPoint;
107using mozilla::NegativeInfinity;
108using mozilla::PositiveInfinity;
109
110using JS::ExpandoAndGeneration;
111
112namespace js {
113namespace jit {
114
115#ifdef CHECK_OSIPOINT_REGISTERS1
116template <class Op>
117static void HandleRegisterDump(Op op, MacroAssembler& masm,
118 LiveRegisterSet liveRegs, Register activation,
119 Register scratch) {
120 const size_t baseOffset = JitActivation::offsetOfRegs();
121
122 // Handle live GPRs.
123 for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) {
124 Register reg = *iter;
125 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
126
127 if (reg == activation) {
128 // To use the original value of the activation register (that's
129 // now on top of the stack), we need the scratch register.
130 masm.push(scratch);
131 masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch);
132 op(scratch, dump);
133 masm.pop(scratch);
134 } else {
135 op(reg, dump);
136 }
137 }
138
139 // Handle live FPRs.
140 for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) {
141 FloatRegister reg = *iter;
142 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
143 op(reg, dump);
144 }
145}
146
147class StoreOp {
148 MacroAssembler& masm;
149
150 public:
151 explicit StoreOp(MacroAssembler& masm) : masm(masm) {}
152
153 void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); }
154 void operator()(FloatRegister reg, Address dump) {
155 if (reg.isDouble()) {
156 masm.storeDouble(reg, dump);
157 } else if (reg.isSingle()) {
158 masm.storeFloat32(reg, dump);
159 } else if (reg.isSimd128()) {
160 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "./../../../../js/src/jit/CodeGenerator.cpp", 160); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { MOZ_CrashSequence
(__null, 160); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
161 } else {
162 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "./../../../../js/src/jit/CodeGenerator.cpp", 162); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected register type." ")"); do { MOZ_CrashSequence
(__null, 162); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
163 }
164 }
165};
166
167class VerifyOp {
168 MacroAssembler& masm;
169 Label* failure_;
170
171 public:
172 VerifyOp(MacroAssembler& masm, Label* failure)
173 : masm(masm), failure_(failure) {}
174
175 void operator()(Register reg, Address dump) {
176 masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
177 }
178 void operator()(FloatRegister reg, Address dump) {
179 if (reg.isDouble()) {
180 ScratchDoubleScope scratch(masm);
181 masm.loadDouble(dump, scratch);
182 masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_);
183 } else if (reg.isSingle()) {
184 ScratchFloat32Scope scratch(masm);
185 masm.loadFloat32(dump, scratch);
186 masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_);
187 } else if (reg.isSimd128()) {
188 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "./../../../../js/src/jit/CodeGenerator.cpp", 188); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { MOZ_CrashSequence
(__null, 188); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
189 } else {
190 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "./../../../../js/src/jit/CodeGenerator.cpp", 190); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected register type." ")"); do { MOZ_CrashSequence
(__null, 190); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
191 }
192 }
193};
194
195void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) {
196 // Ensure the live registers stored by callVM did not change between
197 // the call and this OsiPoint. Try-catch relies on this invariant.
198
199 // Load pointer to the JitActivation in a scratch register.
200 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
201 Register scratch = allRegs.takeAny();
202 masm.push(scratch);
203 masm.loadJitActivation(scratch);
204
205 // If we should not check registers (because the instruction did not call
206 // into the VM, or a GC happened), we're done.
207 Label failure, done;
208 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
209 masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
210
211 // Having more than one VM function call made in one visit function at
212 // runtime is a sec-ciritcal error, because if we conservatively assume that
213 // one of the function call can re-enter Ion, then the invalidation process
214 // will potentially add a call at a random location, by patching the code
215 // before the return address.
216 masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
217
218 // Set checkRegs to 0, so that we don't try to verify registers after we
219 // return from this script to the caller.
220 masm.store32(Imm32(0), checkRegs);
221
222 // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
223 // temps after calling into the VM. This is fine because no other
224 // instructions (including this OsiPoint) will depend on them. Also
225 // backtracking can also use the same register for an input and an output.
226 // These are marked as clobbered and shouldn't get checked.
227 LiveRegisterSet liveRegs;
228 liveRegs.set() = RegisterSet::Intersect(
229 safepoint->liveRegs().set(),
230 RegisterSet::Not(safepoint->clobberedRegs().set()));
231
232 VerifyOp op(masm, &failure);
233 HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
234
235 masm.jump(&done);
236
237 // Do not profile the callWithABI that occurs below. This is to avoid a
238 // rare corner case that occurs when profiling interacts with itself:
239 //
240 // When slow profiling assertions are turned on, FunctionBoundary ops
241 // (which update the profiler pseudo-stack) may emit a callVM, which
242 // forces them to have an osi point associated with them. The
243 // FunctionBoundary for inline function entry is added to the caller's
244 // graph with a PC from the caller's code, but during codegen it modifies
245 // Gecko Profiler instrumentation to add the callee as the current top-most
246 // script. When codegen gets to the OSIPoint, and the callWithABI below is
247 // emitted, the codegen thinks that the current frame is the callee, but
248 // the PC it's using from the OSIPoint refers to the caller. This causes
249 // the profiler instrumentation of the callWithABI below to ASSERT, since
250 // the script and pc are mismatched. To avoid this, we simply omit
251 // instrumentation for these callWithABIs.
252
253 // Any live register captured by a safepoint (other than temp registers)
254 // must remain unchanged between the call and the OsiPoint instruction.
255 masm.bind(&failure);
256 masm.assumeUnreachable("Modified registers between VM call and OsiPoint");
257
258 masm.bind(&done);
259 masm.pop(scratch);
260}
261
262bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) {
263 if (!checkOsiPointRegisters) {
264 return false;
265 }
266
267 if (safepoint->liveRegs().emptyGeneral() &&
268 safepoint->liveRegs().emptyFloat()) {
269 return false; // No registers to check.
270 }
271
272 return true;
273}
274
275void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) {
276 if (!shouldVerifyOsiPointRegs(safepoint)) {
277 return;
278 }
279
280 // Set checkRegs to 0. If we perform a VM call, the instruction
281 // will set it to 1.
282 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
283 Register scratch = allRegs.takeAny();
284 masm.push(scratch);
285 masm.loadJitActivation(scratch);
286 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
287 masm.store32(Imm32(0), checkRegs);
288 masm.pop(scratch);
289}
290
291static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) {
292 // Store a copy of all live registers before performing the call.
293 // When we reach the OsiPoint, we can use this to check nothing
294 // modified them in the meantime.
295
296 // Load pointer to the JitActivation in a scratch register.
297 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
298 Register scratch = allRegs.takeAny();
299 masm.push(scratch);
300 masm.loadJitActivation(scratch);
301
302 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
303 masm.add32(Imm32(1), checkRegs);
304
305 StoreOp op(masm);
306 HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
307
308 masm.pop(scratch);
309}
310#endif // CHECK_OSIPOINT_REGISTERS
311
312// Before doing any call to Cpp, you should ensure that volatile
313// registers are evicted by the register allocator.
314void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) {
315 TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id);
316 const VMFunctionData& fun = GetVMFunction(id);
317
318 // Stack is:
319 // ... frame ...
320 // [args]
321#ifdef DEBUG1
322 MOZ_ASSERT(pushedArgs_ == fun.explicitArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pushedArgs_ == fun.explicitArgs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pushedArgs_ == fun.explicitArgs
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pushedArgs_ == fun.explicitArgs", "./../../../../js/src/jit/CodeGenerator.cpp"
, 322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs"
")"); do { MOZ_CrashSequence(__null, 322); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
323 pushedArgs_ = 0;
324#endif
325
326#ifdef CHECK_OSIPOINT_REGISTERS1
327 if (shouldVerifyOsiPointRegs(ins->safepoint())) {
328 StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
329 }
330#endif
331
332#ifdef DEBUG1
333 if (ins->mirRaw()) {
334 MOZ_ASSERT(ins->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mirRaw()->isInstruction())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mirRaw()->isInstruction
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ins->mirRaw()->isInstruction()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()"
")"); do { MOZ_CrashSequence(__null, 334); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
335 MInstruction* mir = ins->mirRaw()->toInstruction();
336 MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint())do { if (mir->needsResumePoint()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(mir->resumePoint
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mir->resumePoint()))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("mir->resumePoint()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 336); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()"
")"); do { MOZ_CrashSequence(__null, 336); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
337
338 // If this MIR instruction has an overridden AliasSet, set the JitRuntime's
339 // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call
340 // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because
341 // interrupt callbacks can call JS (chrome JS or shell testing functions).
342 // MCheckOverRecursed for a generator resume doesn't check for interrupts.
343 bool isWhitelisted = mir->isInterruptCheck() ||
344 (mir->isCheckOverRecursed() &&
345 !mir->toCheckOverRecursed()->isResumingGenerator());
346 if (!mir->hasDefaultAliasSet() && !isWhitelisted) {
347 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
348 masm.move32(Imm32(1), ReturnReg);
349 masm.store32(ReturnReg, AbsoluteAddress(addr));
350 }
351 }
352#endif
353
354 // Push an exit frame descriptor.
355 masm.Push(FrameDescriptor(FrameType::IonJS));
356
357 // Call the wrapper function. The wrapper is in charge to unwind the stack
358 // when returning from the call. Failures are handled with exceptions based
359 // on the return value of the C functions. To guard the outcome of the
360 // returned value, use another LIR instruction.
361 ensureOsiSpace();
362 uint32_t callOffset = masm.callJit(code);
363 markSafepointAt(callOffset, ins);
364
365#ifdef DEBUG1
366 // Reset the disallowArbitraryCode flag after the call.
367 {
368 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
369 masm.push(ReturnReg);
370 masm.move32(Imm32(0), ReturnReg);
371 masm.store32(ReturnReg, AbsoluteAddress(addr));
372 masm.pop(ReturnReg);
373 }
374#endif
375
376 // Pop rest of the exit frame and the arguments left on the stack.
377 int framePop =
378 sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall();
379 masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop);
380
381 // Stack is:
382 // ... frame ...
383}
384
385template <typename Fn, Fn fn>
386void CodeGenerator::callVM(LInstruction* ins) {
387 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
388 callVMInternal(id, ins);
389}
390
391// ArgSeq store arguments for OutOfLineCallVM.
392//
393// OutOfLineCallVM are created with "oolCallVM" function. The third argument of
394// this function is an instance of a class which provides a "generate" in charge
395// of pushing the argument, with "pushArg", for a VMFunction.
396//
397// Such list of arguments can be created by using the "ArgList" function which
398// creates one instance of "ArgSeq", where the type of the arguments are
399// inferred from the type of the arguments.
400//
401// The list of arguments must be written in the same order as if you were
402// calling the function in C++.
403//
404// Example:
405// ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs()))
406
407template <typename... ArgTypes>
408class ArgSeq {
409 std::tuple<std::remove_reference_t<ArgTypes>...> args_;
410
411 template <std::size_t... ISeq>
412 inline void generate(CodeGenerator* codegen,
413 std::index_sequence<ISeq...>) const {
414 // Arguments are pushed in reverse order, from last argument to first
415 // argument.
416 (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...);
417 }
418
419 public:
420 explicit ArgSeq(ArgTypes&&... args)
421 : args_(std::forward<ArgTypes>(args)...) {}
422
423 inline void generate(CodeGenerator* codegen) const {
424 generate(codegen, std::index_sequence_for<ArgTypes...>{});
425 }
426
427#ifdef DEBUG1
428 static constexpr size_t numArgs = sizeof...(ArgTypes);
429#endif
430};
431
432template <typename... ArgTypes>
433inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) {
434 return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...);
435}
436
437// Store wrappers, to generate the right move of data after the VM call.
438
439struct StoreNothing {
440 inline void generate(CodeGenerator* codegen) const {}
441 inline LiveRegisterSet clobbered() const {
442 return LiveRegisterSet(); // No register gets clobbered
443 }
444};
445
446class StoreRegisterTo {
447 private:
448 Register out_;
449
450 public:
451 explicit StoreRegisterTo(Register out) : out_(out) {}
452
453 inline void generate(CodeGenerator* codegen) const {
454 // It's okay to use storePointerResultTo here - the VMFunction wrapper
455 // ensures the upper bytes are zero for bool/int32 return values.
456 codegen->storePointerResultTo(out_);
457 }
458 inline LiveRegisterSet clobbered() const {
459 LiveRegisterSet set;
460 set.add(out_);
461 return set;
462 }
463};
464
465class StoreFloatRegisterTo {
466 private:
467 FloatRegister out_;
468
469 public:
470 explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {}
471
472 inline void generate(CodeGenerator* codegen) const {
473 codegen->storeFloatResultTo(out_);
474 }
475 inline LiveRegisterSet clobbered() const {
476 LiveRegisterSet set;
477 set.add(out_);
478 return set;
479 }
480};
481
482template <typename Output>
483class StoreValueTo_ {
484 private:
485 Output out_;
486
487 public:
488 explicit StoreValueTo_(const Output& out) : out_(out) {}
489
490 inline void generate(CodeGenerator* codegen) const {
491 codegen->storeResultValueTo(out_);
492 }
493 inline LiveRegisterSet clobbered() const {
494 LiveRegisterSet set;
495 set.add(out_);
496 return set;
497 }
498};
499
500template <typename Output>
501StoreValueTo_<Output> StoreValueTo(const Output& out) {
502 return StoreValueTo_<Output>(out);
503}
504
505template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
506class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> {
507 private:
508 LInstruction* lir_;
509 ArgSeq args_;
510 StoreOutputTo out_;
511
512 public:
513 OutOfLineCallVM(LInstruction* lir, const ArgSeq& args,
514 const StoreOutputTo& out)
515 : lir_(lir), args_(args), out_(out) {}
516
517 void accept(CodeGenerator* codegen) override {
518 codegen->visitOutOfLineCallVM(this);
519 }
520
521 LInstruction* lir() const { return lir_; }
522 const ArgSeq& args() const { return args_; }
523 const StoreOutputTo& out() const { return out_; }
524};
525
526template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
527OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args,
528 const StoreOutputTo& out) {
529 MOZ_ASSERT(lir->mirRaw())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mirRaw())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mirRaw()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("lir->mirRaw()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 529); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mirRaw()" ")"); do { MOZ_CrashSequence
(__null, 529); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
530 MOZ_ASSERT(lir->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mirRaw()->isInstruction())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mirRaw()->isInstruction
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->mirRaw()->isInstruction()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 530); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()"
")"); do { MOZ_CrashSequence(__null, 530); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
531
532#ifdef DEBUG1
533 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
534 const VMFunctionData& fun = GetVMFunction(id);
535 MOZ_ASSERT(fun.explicitArgs == args.numArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun.explicitArgs == args.numArgs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun.explicitArgs == args.numArgs
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"fun.explicitArgs == args.numArgs", "./../../../../js/src/jit/CodeGenerator.cpp"
, 535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs"
")"); do { MOZ_CrashSequence(__null, 535); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
536 MOZ_ASSERT(fun.returnsData() !=do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo
, StoreNothing>))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v
<StoreOutputTo, StoreNothing>)))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 537); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { MOZ_CrashSequence(__null, 537); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
537 (std::is_same_v<StoreOutputTo, StoreNothing>))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo
, StoreNothing>))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v
<StoreOutputTo, StoreNothing>)))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 537); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { MOZ_CrashSequence(__null, 537); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
538#endif
539
540 OutOfLineCode* ool = new (alloc())
541 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out);
542 addOutOfLineCode(ool, lir->mirRaw()->toInstruction());
543 return ool;
544}
545
546template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
547void CodeGenerator::visitOutOfLineCallVM(
548 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) {
549 LInstruction* lir = ool->lir();
550
551#ifdef JS_JITSPEW1
552 {
553 AutoJitSpewMessage msg(JitSpew_Codegen,
554 " # LIR=%s",
555 lir->opName());
556 if (const char* extra = lir->getExtraName()) {
557 msg.append(":%s", extra);
558 }
559 }
560#endif
561 perfSpewer().recordInstruction(masm, lir);
562 if (!lir->isCall()) {
563 saveLive(lir);
564 }
565 ool->args().generate(this);
566 callVM<Fn, fn>(lir);
567 ool->out().generate(this);
568 if (!lir->isCall()) {
569 restoreLiveIgnore(lir, ool->out().clobbered());
570 }
571 masm.jump(ool->rejoin());
572}
573
574class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> {
575 private:
576 LInstruction* lir_;
577 size_t cacheIndex_;
578 size_t cacheInfoIndex_;
579
580 public:
581 OutOfLineICFallback(LInstruction* lir, size_t cacheIndex,
582 size_t cacheInfoIndex)
583 : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {}
584
585 void bind(MacroAssembler* masm) override {
586 // The binding of the initial jump is done in
587 // CodeGenerator::visitOutOfLineICFallback.
588 }
589
590 size_t cacheIndex() const { return cacheIndex_; }
591 size_t cacheInfoIndex() const { return cacheInfoIndex_; }
592 LInstruction* lir() const { return lir_; }
593
594 void accept(CodeGenerator* codegen) override {
595 codegen->visitOutOfLineICFallback(this);
596 }
597};
598
599void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) {
600 if (cacheIndex == SIZE_MAX(18446744073709551615UL)) {
601 masm.setOOM();
602 return;
603 }
604
605 DataPtr<IonIC> cache(this, cacheIndex);
606 MInstruction* mir = lir->mirRaw()->toInstruction();
607 cache->setScriptedLocation(mir->block()->info().script(),
608 mir->resumePoint()->pc());
609
610 Register temp = cache->scratchRegisterForEntryJump();
611 icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp);
612 masm.jump(Address(temp, 0));
613
614 MOZ_ASSERT(!icInfo_.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!icInfo_.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!icInfo_.empty()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!icInfo_.empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 614); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!icInfo_.empty()" ")"); do { MOZ_CrashSequence
(__null, 614); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
615
616 OutOfLineICFallback* ool =
617 new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1);
618 addOutOfLineCode(ool, mir);
619
620 masm.bind(ool->rejoin());
621 cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset()));
622}
623
624void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) {
625 LInstruction* lir = ool->lir();
626 size_t cacheIndex = ool->cacheIndex();
627 size_t cacheInfoIndex = ool->cacheInfoIndex();
628
629 DataPtr<IonIC> ic(this, cacheIndex);
630
631 // Register the location of the OOL path in the IC.
632 ic->setFallbackOffset(CodeOffset(masm.currentOffset()));
633
634 switch (ic->kind()) {
635 case CacheKind::GetProp:
636 case CacheKind::GetElem: {
637 IonGetPropertyIC* getPropIC = ic->asGetPropertyIC();
638
639 saveLive(lir);
640
641 pushArg(getPropIC->id());
642 pushArg(getPropIC->value());
643 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
644 pushArg(ImmGCPtr(gen->outerInfo().script()));
645
646 using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*,
647 HandleValue, HandleValue, MutableHandleValue);
648 callVM<Fn, IonGetPropertyIC::update>(lir);
649
650 StoreValueTo(getPropIC->output()).generate(this);
651 restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
652
653 masm.jump(ool->rejoin());
654 return;
655 }
656 case CacheKind::GetPropSuper:
657 case CacheKind::GetElemSuper: {
658 IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC();
659
660 saveLive(lir);
661
662 pushArg(getPropSuperIC->id());
663 pushArg(getPropSuperIC->receiver());
664 pushArg(getPropSuperIC->object());
665 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
666 pushArg(ImmGCPtr(gen->outerInfo().script()));
667
668 using Fn =
669 bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject,
670 HandleValue, HandleValue, MutableHandleValue);
671 callVM<Fn, IonGetPropSuperIC::update>(lir);
672
673 StoreValueTo(getPropSuperIC->output()).generate(this);
674 restoreLiveIgnore(lir,
675 StoreValueTo(getPropSuperIC->output()).clobbered());
676
677 masm.jump(ool->rejoin());
678 return;
679 }
680 case CacheKind::SetProp:
681 case CacheKind::SetElem: {
682 IonSetPropertyIC* setPropIC = ic->asSetPropertyIC();
683
684 saveLive(lir);
685
686 pushArg(setPropIC->rhs());
687 pushArg(setPropIC->id());
688 pushArg(setPropIC->object());
689 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
690 pushArg(ImmGCPtr(gen->outerInfo().script()));
691
692 using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*,
693 HandleObject, HandleValue, HandleValue);
694 callVM<Fn, IonSetPropertyIC::update>(lir);
695
696 restoreLive(lir);
697
698 masm.jump(ool->rejoin());
699 return;
700 }
701 case CacheKind::GetName: {
702 IonGetNameIC* getNameIC = ic->asGetNameIC();
703
704 saveLive(lir);
705
706 pushArg(getNameIC->environment());
707 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
708 pushArg(ImmGCPtr(gen->outerInfo().script()));
709
710 using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
711 MutableHandleValue);
712 callVM<Fn, IonGetNameIC::update>(lir);
713
714 StoreValueTo(getNameIC->output()).generate(this);
715 restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
716
717 masm.jump(ool->rejoin());
718 return;
719 }
720 case CacheKind::BindName: {
721 IonBindNameIC* bindNameIC = ic->asBindNameIC();
722
723 saveLive(lir);
724
725 pushArg(bindNameIC->environment());
726 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
727 pushArg(ImmGCPtr(gen->outerInfo().script()));
728
729 using Fn =
730 JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject);
731 callVM<Fn, IonBindNameIC::update>(lir);
732
733 StoreRegisterTo(bindNameIC->output()).generate(this);
734 restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered());
735
736 masm.jump(ool->rejoin());
737 return;
738 }
739 case CacheKind::GetIterator: {
740 IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC();
741
742 saveLive(lir);
743
744 pushArg(getIteratorIC->value());
745 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
746 pushArg(ImmGCPtr(gen->outerInfo().script()));
747
748 using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*,
749 HandleValue);
750 callVM<Fn, IonGetIteratorIC::update>(lir);
751
752 StoreRegisterTo(getIteratorIC->output()).generate(this);
753 restoreLiveIgnore(lir,
754 StoreRegisterTo(getIteratorIC->output()).clobbered());
755
756 masm.jump(ool->rejoin());
757 return;
758 }
759 case CacheKind::OptimizeSpreadCall: {
760 auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC();
761
762 saveLive(lir);
763
764 pushArg(optimizeSpreadCallIC->value());
765 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
766 pushArg(ImmGCPtr(gen->outerInfo().script()));
767
768 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*,
769 HandleValue, MutableHandleValue);
770 callVM<Fn, IonOptimizeSpreadCallIC::update>(lir);
771
772 StoreValueTo(optimizeSpreadCallIC->output()).generate(this);
773 restoreLiveIgnore(
774 lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered());
775
776 masm.jump(ool->rejoin());
777 return;
778 }
779 case CacheKind::In: {
780 IonInIC* inIC = ic->asInIC();
781
782 saveLive(lir);
783
784 pushArg(inIC->object());
785 pushArg(inIC->key());
786 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
787 pushArg(ImmGCPtr(gen->outerInfo().script()));
788
789 using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue,
790 HandleObject, bool*);
791 callVM<Fn, IonInIC::update>(lir);
792
793 StoreRegisterTo(inIC->output()).generate(this);
794 restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered());
795
796 masm.jump(ool->rejoin());
797 return;
798 }
799 case CacheKind::HasOwn: {
800 IonHasOwnIC* hasOwnIC = ic->asHasOwnIC();
801
802 saveLive(lir);
803
804 pushArg(hasOwnIC->id());
805 pushArg(hasOwnIC->value());
806 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
807 pushArg(ImmGCPtr(gen->outerInfo().script()));
808
809 using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue,
810 HandleValue, int32_t*);
811 callVM<Fn, IonHasOwnIC::update>(lir);
812
813 StoreRegisterTo(hasOwnIC->output()).generate(this);
814 restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
815
816 masm.jump(ool->rejoin());
817 return;
818 }
819 case CacheKind::CheckPrivateField: {
820 IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC();
821
822 saveLive(lir);
823
824 pushArg(checkPrivateFieldIC->id());
825 pushArg(checkPrivateFieldIC->value());
826
827 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
828 pushArg(ImmGCPtr(gen->outerInfo().script()));
829
830 using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*,
831 HandleValue, HandleValue, bool*);
832 callVM<Fn, IonCheckPrivateFieldIC::update>(lir);
833
834 StoreRegisterTo(checkPrivateFieldIC->output()).generate(this);
835 restoreLiveIgnore(
836 lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered());
837
838 masm.jump(ool->rejoin());
839 return;
840 }
841 case CacheKind::InstanceOf: {
842 IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC();
843
844 saveLive(lir);
845
846 pushArg(hasInstanceOfIC->rhs());
847 pushArg(hasInstanceOfIC->lhs());
848 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
849 pushArg(ImmGCPtr(gen->outerInfo().script()));
850
851 using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*,
852 HandleValue lhs, HandleObject rhs, bool* res);
853 callVM<Fn, IonInstanceOfIC::update>(lir);
854
855 StoreRegisterTo(hasInstanceOfIC->output()).generate(this);
856 restoreLiveIgnore(lir,
857 StoreRegisterTo(hasInstanceOfIC->output()).clobbered());
858
859 masm.jump(ool->rejoin());
860 return;
861 }
862 case CacheKind::UnaryArith: {
863 IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC();
864
865 saveLive(lir);
866
867 pushArg(unaryArithIC->input());
868 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
869 pushArg(ImmGCPtr(gen->outerInfo().script()));
870
871 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
872 IonUnaryArithIC* stub, HandleValue val,
873 MutableHandleValue res);
874 callVM<Fn, IonUnaryArithIC::update>(lir);
875
876 StoreValueTo(unaryArithIC->output()).generate(this);
877 restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered());
878
879 masm.jump(ool->rejoin());
880 return;
881 }
882 case CacheKind::ToPropertyKey: {
883 IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC();
884
885 saveLive(lir);
886
887 pushArg(toPropertyKeyIC->input());
888 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
889 pushArg(ImmGCPtr(gen->outerInfo().script()));
890
891 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
892 IonToPropertyKeyIC* ic, HandleValue val,
893 MutableHandleValue res);
894 callVM<Fn, IonToPropertyKeyIC::update>(lir);
895
896 StoreValueTo(toPropertyKeyIC->output()).generate(this);
897 restoreLiveIgnore(lir,
898 StoreValueTo(toPropertyKeyIC->output()).clobbered());
899
900 masm.jump(ool->rejoin());
901 return;
902 }
903 case CacheKind::BinaryArith: {
904 IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC();
905
906 saveLive(lir);
907
908 pushArg(binaryArithIC->rhs());
909 pushArg(binaryArithIC->lhs());
910 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
911 pushArg(ImmGCPtr(gen->outerInfo().script()));
912
913 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
914 IonBinaryArithIC* stub, HandleValue lhs,
915 HandleValue rhs, MutableHandleValue res);
916 callVM<Fn, IonBinaryArithIC::update>(lir);
917
918 StoreValueTo(binaryArithIC->output()).generate(this);
919 restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered());
920
921 masm.jump(ool->rejoin());
922 return;
923 }
924 case CacheKind::Compare: {
925 IonCompareIC* compareIC = ic->asCompareIC();
926
927 saveLive(lir);
928
929 pushArg(compareIC->rhs());
930 pushArg(compareIC->lhs());
931 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
932 pushArg(ImmGCPtr(gen->outerInfo().script()));
933
934 using Fn =
935 bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
936 HandleValue lhs, HandleValue rhs, bool* res);
937 callVM<Fn, IonCompareIC::update>(lir);
938
939 StoreRegisterTo(compareIC->output()).generate(this);
940 restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered());
941
942 masm.jump(ool->rejoin());
943 return;
944 }
945 case CacheKind::CloseIter: {
946 IonCloseIterIC* closeIterIC = ic->asCloseIterIC();
947
948 saveLive(lir);
949
950 pushArg(closeIterIC->iter());
951 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
952 pushArg(ImmGCPtr(gen->outerInfo().script()));
953
954 using Fn =
955 bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject);
956 callVM<Fn, IonCloseIterIC::update>(lir);
957
958 restoreLive(lir);
959
960 masm.jump(ool->rejoin());
961 return;
962 }
963 case CacheKind::OptimizeGetIterator: {
964 auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC();
965
966 saveLive(lir);
967
968 pushArg(optimizeGetIteratorIC->value());
969 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
970 pushArg(ImmGCPtr(gen->outerInfo().script()));
971
972 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*,
973 HandleValue, bool* res);
974 callVM<Fn, IonOptimizeGetIteratorIC::update>(lir);
975
976 StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this);
977 restoreLiveIgnore(
978 lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered());
979
980 masm.jump(ool->rejoin());
981 return;
982 }
983 case CacheKind::Call:
984 case CacheKind::TypeOf:
985 case CacheKind::TypeOfEq:
986 case CacheKind::ToBool:
987 case CacheKind::LazyConstant:
988 case CacheKind::NewArray:
989 case CacheKind::NewObject:
990 case CacheKind::Lambda:
991 case CacheKind::GetImport:
992 MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC"
, "./../../../../js/src/jit/CodeGenerator.cpp", 992); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported IC" ")"); do { MOZ_CrashSequence(__null
, 992); __attribute__((nomerge)) ::abort(); } while (false); }
while (false)
;
993 }
994 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 994); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 994); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
995}
996
997StringObject* MNewStringObject::templateObj() const {
998 return &templateObj_->as<StringObject>();
999}
1000
1001CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph,
1002 MacroAssembler* masm,
1003 const wasm::CodeMetadata* wasmCodeMeta)
1004 : CodeGeneratorSpecific(gen, graph, masm, wasmCodeMeta),
1005 ionScriptLabels_(gen->alloc()),
1006 nurseryObjectLabels_(gen->alloc()),
1007 nurseryValueLabels_(gen->alloc()),
1008 scriptCounts_(nullptr) {}
1009
1010CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); }
1011
1012void CodeGenerator::visitValueToNumberInt32(LValueToNumberInt32* lir) {
1013 ValueOperand operand = ToValue(lir->input());
1014 Register output = ToRegister(lir->output());
1015 FloatRegister temp = ToFloatRegister(lir->temp0());
1016
1017 Label fails;
1018 masm.convertValueToInt32(operand, temp, output, &fails,
1019 lir->mir()->needsNegativeZeroCheck(),
1020 lir->mir()->conversion());
1021
1022 bailoutFrom(&fails, lir->snapshot());
1023}
1024
1025void CodeGenerator::visitValueTruncateToInt32(LValueTruncateToInt32* lir) {
1026 ValueOperand operand = ToValue(lir->input());
1027 Register output = ToRegister(lir->output());
1028 FloatRegister temp = ToFloatRegister(lir->temp0());
1029 Register stringReg = ToRegister(lir->temp1());
1030
1031 auto* oolDouble = oolTruncateDouble(temp, output, lir->mir());
1032
1033 using Fn = bool (*)(JSContext*, JSString*, double*);
1034 auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg),
1035 StoreFloatRegisterTo(temp));
1036 Label* stringEntry = oolString->entry();
1037 Label* stringRejoin = oolString->rejoin();
1038
1039 Label fails;
1040 masm.truncateValueToInt32(operand, stringEntry, stringRejoin,
1041 oolDouble->entry(), stringReg, temp, output,
1042 &fails);
1043 masm.bind(oolDouble->rejoin());
1044
1045 bailoutFrom(&fails, lir->snapshot());
1046}
1047
1048void CodeGenerator::visitValueToDouble(LValueToDouble* lir) {
1049 ValueOperand operand = ToValue(lir->input());
1050 FloatRegister output = ToFloatRegister(lir->output());
1051
1052 Label fail;
1053 masm.convertValueToDouble(operand, output, &fail);
1054 bailoutFrom(&fail, lir->snapshot());
1055}
1056
1057void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) {
1058 ValueOperand operand = ToValue(lir->input());
1059 FloatRegister output = ToFloatRegister(lir->output());
1060
1061 Label fail;
1062 masm.convertValueToFloat32(operand, output, &fail);
1063 bailoutFrom(&fail, lir->snapshot());
1064}
1065
1066void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) {
1067 ValueOperand operand = ToValue(lir->input());
1068 Register temp = ToTempRegisterOrInvalid(lir->temp0());
1069 FloatRegister output = ToFloatRegister(lir->output());
1070
1071 LiveRegisterSet volatileRegs;
1072 if (!MacroAssembler::SupportsFloat64To16()) {
1073 volatileRegs = liveVolatileRegs(lir);
1074 }
1075
1076 Label fail;
1077 masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail);
1078 bailoutFrom(&fail, lir->snapshot());
1079}
1080
1081void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) {
1082 ValueOperand operand = ToValue(lir->input());
1083 Register output = ToRegister(lir->output());
1084
1085 using Fn = BigInt* (*)(JSContext*, HandleValue);
1086 auto* ool =
1087 oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output));
1088
1089 Register tag = masm.extractTag(operand, output);
1090
1091 Label notBigInt, done;
1092 masm.branchTestBigInt(Assembler::NotEqual, tag, &notBigInt);
1093 masm.unboxBigInt(operand, output);
1094 masm.jump(&done);
1095 masm.bind(&notBigInt);
1096
1097 masm.branchTestBoolean(Assembler::Equal, tag, ool->entry());
1098 masm.branchTestString(Assembler::Equal, tag, ool->entry());
1099
1100 // ToBigInt(object) can have side-effects; all other types throw a TypeError.
1101 bailout(lir->snapshot());
1102
1103 masm.bind(ool->rejoin());
1104 masm.bind(&done);
1105}
1106
1107void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) {
1108 masm.convertInt32ToDouble(ToRegister(lir->input()),
1109 ToFloatRegister(lir->output()));
1110}
1111
1112void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) {
1113 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()),
1114 ToFloatRegister(lir->output()));
1115}
1116
1117void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) {
1118 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()),
1119 ToFloatRegister(lir->output()));
1120}
1121
1122void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) {
1123 masm.convertInt32ToFloat32(ToRegister(lir->input()),
1124 ToFloatRegister(lir->output()));
1125}
1126
1127void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) {
1128 LiveRegisterSet volatileRegs;
1129 if (!MacroAssembler::SupportsFloat64To16()) {
1130 volatileRegs = liveVolatileRegs(lir);
1131 }
1132 masm.convertDoubleToFloat16(
1133 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1134 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1135}
1136
1137void CodeGenerator::visitDoubleToFloat32ToFloat16(
1138 LDoubleToFloat32ToFloat16* lir) {
1139 masm.convertDoubleToFloat16(
1140 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1141 ToRegister(lir->temp0()), ToRegister(lir->temp1()));
1142}
1143
1144void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) {
1145 LiveRegisterSet volatileRegs;
1146 if (!MacroAssembler::SupportsFloat32To16()) {
1147 volatileRegs = liveVolatileRegs(lir);
1148 }
1149 masm.convertFloat32ToFloat16(
1150 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1151 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1152}
1153
1154void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) {
1155 LiveRegisterSet volatileRegs;
1156 if (!MacroAssembler::SupportsFloat32To16()) {
1157 volatileRegs = liveVolatileRegs(lir);
1158 }
1159 masm.convertInt32ToFloat16(
1160 ToRegister(lir->input()), ToFloatRegister(lir->output()),
1161 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1162}
1163
1164void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) {
1165 Label fail;
1166 FloatRegister input = ToFloatRegister(lir->input());
1167 Register output = ToRegister(lir->output());
1168 masm.convertDoubleToInt32(input, output, &fail,
1169 lir->mir()->needsNegativeZeroCheck());
1170 bailoutFrom(&fail, lir->snapshot());
1171}
1172
1173void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) {
1174 Label fail;
1175 FloatRegister input = ToFloatRegister(lir->input());
1176 Register output = ToRegister(lir->output());
1177 masm.convertFloat32ToInt32(input, output, &fail,
1178 lir->mir()->needsNegativeZeroCheck());
1179 bailoutFrom(&fail, lir->snapshot());
1180}
1181
1182void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) {
1183#ifdef JS_64BIT1
1184 Register output = ToRegister(lir->output());
1185 const LAllocation* input = lir->input();
1186 if (input->isGeneralReg()) {
1187 masm.move32SignExtendToPtr(ToRegister(input), output);
1188 } else {
1189 masm.load32SignExtendToPtr(ToAddress(input), output);
1190 }
1191#else
1192 MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1192); AnnotateMozCrashReason
("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { MOZ_CrashSequence
(__null, 1192); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1193#endif
1194}
1195
1196void CodeGenerator::visitNonNegativeIntPtrToInt32(
1197 LNonNegativeIntPtrToInt32* lir) {
1198#ifdef JS_64BIT1
1199 Register output = ToRegister(lir->output());
1200 MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->input()) == output)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(ToRegister(lir->input()) == output))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1200); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"
); do { MOZ_CrashSequence(__null, 1200); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1201
1202 Label bail;
1203 masm.guardNonNegativeIntPtrToInt32(output, &bail);
1204 bailoutFrom(&bail, lir->snapshot());
1205#else
1206 MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1206); AnnotateMozCrashReason
("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { MOZ_CrashSequence
(__null, 1206); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1207#endif
1208}
1209
1210void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) {
1211 Register input = ToRegister(lir->input());
1212 FloatRegister output = ToFloatRegister(lir->output());
1213 masm.convertIntPtrToDouble(input, output);
1214}
1215
1216void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) {
1217 Register output = ToRegister(lir->output());
1218 MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->input()) == output)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(ToRegister(lir->input()) == output))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1218); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"
); do { MOZ_CrashSequence(__null, 1218); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1219
1220 uint32_t byteSize = lir->mir()->byteSize();
1221
1222#ifdef DEBUG1
1223 Label ok;
1224 masm.branchTestPtr(Assembler::NotSigned, output, output, &ok);
1225 masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength");
1226 masm.bind(&ok);
1227#endif
1228
1229 Label bail;
1230 masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail);
1231 bailoutFrom(&bail, lir->snapshot());
1232}
1233
1234void CodeGenerator::emitOOLTestObject(Register objreg,
1235 Label* ifEmulatesUndefined,
1236 Label* ifDoesntEmulateUndefined,
1237 Register scratch) {
1238 saveVolatile(scratch);
1239#if defined(DEBUG1) || defined(FUZZING)
1240 masm.loadRuntimeFuse(
1241 RuntimeFuses::FuseIndex::HasSeenObjectEmulateUndefinedFuse, scratch);
1242 using Fn = bool (*)(JSObject* obj, size_t fuseValue);
1243 masm.setupAlignedABICall();
1244 masm.passABIArg(objreg);
1245 masm.passABIArg(scratch);
1246 masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>();
1247#else
1248 using Fn = bool (*)(JSObject* obj);
1249 masm.setupAlignedABICall();
1250 masm.passABIArg(objreg);
1251 masm.callWithABI<Fn, js::EmulatesUndefined>();
1252#endif
1253 masm.storeCallPointerResult(scratch);
1254 restoreVolatile(scratch);
1255
1256 masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
1257 masm.jump(ifDoesntEmulateUndefined);
1258}
1259
1260// Base out-of-line code generator for all tests of the truthiness of an
1261// object, where the object might not be truthy. (Recall that per spec all
1262// objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class
1263// flag to permit objects to look like |undefined| in certain contexts,
1264// including in object truthiness testing.) We check truthiness inline except
1265// when we're testing it on a proxy, in which case out-of-line code will call
1266// EmulatesUndefined for a conclusive answer.
1267class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> {
1268 Register objreg_;
1269 Register scratch_;
1270
1271 Label* ifEmulatesUndefined_;
1272 Label* ifDoesntEmulateUndefined_;
1273
1274#ifdef DEBUG1
1275 bool initialized() { return ifEmulatesUndefined_ != nullptr; }
1276#endif
1277
1278 public:
1279 OutOfLineTestObject()
1280 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {}
1281
1282 void accept(CodeGenerator* codegen) final {
1283 MOZ_ASSERT(initialized())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(initialized())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(initialized()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("initialized()",
"./../../../../js/src/jit/CodeGenerator.cpp", 1283); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "initialized()" ")"); do { MOZ_CrashSequence
(__null, 1283); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1284 codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_,
1285 ifDoesntEmulateUndefined_, scratch_);
1286 }
1287
1288 // Specify the register where the object to be tested is found, labels to
1289 // jump to if the object is truthy or falsy, and a scratch register for
1290 // use in the out-of-line path.
1291 void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined,
1292 Label* ifDoesntEmulateUndefined, Register scratch) {
1293 MOZ_ASSERT(!initialized())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!initialized())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!initialized()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!initialized()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1293); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!initialized()" ")"); do { MOZ_CrashSequence
(__null, 1293); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1294 MOZ_ASSERT(ifEmulatesUndefined)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ifEmulatesUndefined)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ifEmulatesUndefined))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("ifEmulatesUndefined"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1294); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ifEmulatesUndefined" ")"); do { MOZ_CrashSequence
(__null, 1294); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1295 objreg_ = objreg;
1296 scratch_ = scratch;
1297 ifEmulatesUndefined_ = ifEmulatesUndefined;
1298 ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined;
1299 }
1300};
1301
1302// A subclass of OutOfLineTestObject containing two extra labels, for use when
1303// the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line
1304// code. The user should bind these labels in inline code, and specify them as
1305// targets via setInputAndTargets, as appropriate.
1306class OutOfLineTestObjectWithLabels : public OutOfLineTestObject {
1307 Label label1_;
1308 Label label2_;
1309
1310 public:
1311 OutOfLineTestObjectWithLabels() = default;
1312
1313 Label* label1() { return &label1_; }
1314 Label* label2() { return &label2_; }
1315};
1316
1317void CodeGenerator::testObjectEmulatesUndefinedKernel(
1318 Register objreg, Label* ifEmulatesUndefined,
1319 Label* ifDoesntEmulateUndefined, Register scratch,
1320 OutOfLineTestObject* ool) {
1321 ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
1322 scratch);
1323
1324 // Perform a fast-path check of the object's class flags if the object's
1325 // not a proxy. Let out-of-line code handle the slow cases that require
1326 // saving registers, making a function call, and restoring registers.
1327 masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(),
1328 ifEmulatesUndefined);
1329}
1330
1331void CodeGenerator::branchTestObjectEmulatesUndefined(
1332 Register objreg, Label* ifEmulatesUndefined,
1333 Label* ifDoesntEmulateUndefined, Register scratch,
1334 OutOfLineTestObject* ool) {
1335 MOZ_ASSERT(!ifDoesntEmulateUndefined->bound(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ifDoesntEmulateUndefined->bound())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()"
" (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 1336); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") ("
"ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { MOZ_CrashSequence(__null, 1336); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1336 "ifDoesntEmulateUndefined will be bound to the fallthrough path")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ifDoesntEmulateUndefined->bound())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()"
" (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 1336); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") ("
"ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { MOZ_CrashSequence(__null, 1336); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1337
1338 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1339 ifDoesntEmulateUndefined, scratch, ool);
1340 masm.bind(ifDoesntEmulateUndefined);
1341}
1342
1343void CodeGenerator::testObjectEmulatesUndefined(Register objreg,
1344 Label* ifEmulatesUndefined,
1345 Label* ifDoesntEmulateUndefined,
1346 Register scratch,
1347 OutOfLineTestObject* ool) {
1348 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1349 ifDoesntEmulateUndefined, scratch, ool);
1350 masm.jump(ifDoesntEmulateUndefined);
1351}
1352
1353void CodeGenerator::testValueTruthyForType(
1354 JSValueType type, ScratchTagScope& tag, const ValueOperand& value,
1355 Register tempToUnbox, Register temp, FloatRegister floatTemp,
1356 Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool,
1357 bool skipTypeTest) {
1358#ifdef DEBUG1
1359 if (skipTypeTest) {
1360 Label expected;
1361 masm.branchTestType(Assembler::Equal, tag, type, &expected);
1362 masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType");
1363 masm.bind(&expected);
1364 }
1365#endif
1366
1367 // Handle irregular types first.
1368 switch (type) {
1369 case JSVAL_TYPE_UNDEFINED:
1370 case JSVAL_TYPE_NULL:
1371 // Undefined and null are falsy.
1372 if (!skipTypeTest) {
1373 masm.branchTestType(Assembler::Equal, tag, type, ifFalsy);
1374 } else {
1375 masm.jump(ifFalsy);
1376 }
1377 return;
1378 case JSVAL_TYPE_SYMBOL:
1379 // Symbols are truthy.
1380 if (!skipTypeTest) {
1381 masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy);
1382 } else {
1383 masm.jump(ifTruthy);
1384 }
1385 return;
1386 case JSVAL_TYPE_OBJECT: {
1387 if (!ool) {
1388 // If we have no ool path, then the hasSeenObjectEmulateUndefined fuse
1389 // is intact, and all objects are truthy.
1390 if (!skipTypeTest) {
1391 masm.branchTestObject(Assembler::Equal, tag, ifTruthy);
1392 } else {
1393 masm.jump(ifTruthy);
1394 }
1395 return;
1396 }
1397 Label notObject;
1398 if (!skipTypeTest) {
1399 masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
1400 }
1401 ScratchTagScopeRelease _(&tag);
1402 Register objreg = masm.extractObject(value, tempToUnbox);
1403 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool);
1404 masm.bind(&notObject);
1405 return;
1406 }
1407 default:
1408 break;
1409 }
1410
1411 // Check the type of the value (unless this is the last possible type).
1412 Label differentType;
1413 if (!skipTypeTest) {
1414 masm.branchTestType(Assembler::NotEqual, tag, type, &differentType);
1415 }
1416
1417 // Branch if the value is falsy.
1418 ScratchTagScopeRelease _(&tag);
1419 switch (type) {
1420 case JSVAL_TYPE_BOOLEAN: {
1421 masm.branchTestBooleanTruthy(false, value, ifFalsy);
1422 break;
1423 }
1424 case JSVAL_TYPE_INT32: {
1425 masm.branchTestInt32Truthy(false, value, ifFalsy);
1426 break;
1427 }
1428 case JSVAL_TYPE_STRING: {
1429 masm.branchTestStringTruthy(false, value, ifFalsy);
1430 break;
1431 }
1432 case JSVAL_TYPE_BIGINT: {
1433 masm.branchTestBigIntTruthy(false, value, ifFalsy);
1434 break;
1435 }
1436 case JSVAL_TYPE_DOUBLE: {
1437 masm.unboxDouble(value, floatTemp);
1438 masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy);
1439 break;
1440 }
1441 default:
1442 MOZ_CRASH("Unexpected value type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected value type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1442); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected value type" ")"); do { MOZ_CrashSequence
(__null, 1442); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1443 }
1444
1445 // If we reach this point, the value is truthy. We fall through for
1446 // truthy on the last test; otherwise, branch.
1447 if (!skipTypeTest) {
1448 masm.jump(ifTruthy);
1449 }
1450
1451 masm.bind(&differentType);
1452}
1453
1454void CodeGenerator::testValueTruthy(const ValueOperand& value,
1455 Register tempToUnbox, Register temp,
1456 FloatRegister floatTemp,
1457 const TypeDataList& observedTypes,
1458 Label* ifTruthy, Label* ifFalsy,
1459 OutOfLineTestObject* ool) {
1460 ScratchTagScope tag(masm, value);
1461 masm.splitTagForTest(value, tag);
1462
1463 const std::initializer_list<JSValueType> defaultOrder = {
1464 JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN,
1465 JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING,
1466 JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
1467
1468 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
1469
1470 // Generate tests for previously observed types first.
1471 // The TypeDataList is sorted by descending frequency.
1472 for (auto& observed : observedTypes) {
1473 JSValueType type = observed.type();
1474 remaining -= type;
1475
1476 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1477 ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false);
1478 }
1479
1480 // Generate tests for remaining types.
1481 for (auto type : defaultOrder) {
1482 if (!remaining.contains(type)) {
1483 continue;
1484 }
1485 remaining -= type;
1486
1487 // We don't need a type test for the last possible type.
1488 bool skipTypeTest = remaining.isEmpty();
1489 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1490 ifTruthy, ifFalsy, ool, skipTypeTest);
1491 }
1492 MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(remaining.isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1492); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { MOZ_CrashSequence
(__null, 1492); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1493
1494 // We fall through if the final test is truthy.
1495}
1496
1497void CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) {
1498 Register input = ToRegister(test->input());
1499 MBasicBlock* ifTrue = test->ifTrue();
1500 MBasicBlock* ifFalse = test->ifFalse();
1501
1502 if (isNextBlock(ifFalse->lir())) {
1503 masm.branchTest32(Assembler::NonZero, input, input,
1504 getJumpLabelForBranch(ifTrue));
1505 } else {
1506 masm.branchTest32(Assembler::Zero, input, input,
1507 getJumpLabelForBranch(ifFalse));
1508 jumpToBlock(ifTrue);
1509 }
1510}
1511
1512void CodeGenerator::visitTestIPtrAndBranch(LTestIPtrAndBranch* test) {
1513 Register input = ToRegister(test->input());
1514 MBasicBlock* ifTrue = test->ifTrue();
1515 MBasicBlock* ifFalse = test->ifFalse();
1516
1517 if (isNextBlock(ifFalse->lir())) {
1518 masm.branchTestPtr(Assembler::NonZero, input, input,
1519 getJumpLabelForBranch(ifTrue));
1520 } else {
1521 masm.branchTestPtr(Assembler::Zero, input, input,
1522 getJumpLabelForBranch(ifFalse));
1523 jumpToBlock(ifTrue);
1524 }
1525}
1526
1527void CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* test) {
1528 Register64 input = ToRegister64(test->input());
1529 MBasicBlock* ifTrue = test->ifTrue();
1530 MBasicBlock* ifFalse = test->ifFalse();
1531
1532 if (isNextBlock(ifFalse->lir())) {
1533 masm.branchTest64(Assembler::NonZero, input, input,
1534 getJumpLabelForBranch(ifTrue));
1535 } else if (isNextBlock(ifTrue->lir())) {
1536 masm.branchTest64(Assembler::Zero, input, input,
1537 getJumpLabelForBranch(ifFalse));
1538 } else {
1539 masm.branchTest64(Assembler::NonZero, input, input,
1540 getJumpLabelForBranch(ifTrue),
1541 getJumpLabelForBranch(ifFalse));
1542 }
1543}
1544
1545void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) {
1546 Register input = ToRegister(lir->input());
1547 MBasicBlock* ifTrue = lir->ifTrue();
1548 MBasicBlock* ifFalse = lir->ifFalse();
1549
1550 if (isNextBlock(ifFalse->lir())) {
1551 masm.branchIfBigIntIsNonZero(input, getJumpLabelForBranch(ifTrue));
1552 } else {
1553 masm.branchIfBigIntIsZero(input, getJumpLabelForBranch(ifFalse));
1554 jumpToBlock(ifTrue);
1555 }
1556}
1557
1558static Assembler::Condition ReverseCondition(Assembler::Condition condition) {
1559 switch (condition) {
1560 case Assembler::Equal:
1561 case Assembler::NotEqual:
1562 return condition;
1563 case Assembler::Above:
1564 return Assembler::Below;
1565 case Assembler::AboveOrEqual:
1566 return Assembler::BelowOrEqual;
1567 case Assembler::Below:
1568 return Assembler::Above;
1569 case Assembler::BelowOrEqual:
1570 return Assembler::AboveOrEqual;
1571 case Assembler::GreaterThan:
1572 return Assembler::LessThan;
1573 case Assembler::GreaterThanOrEqual:
1574 return Assembler::LessThanOrEqual;
1575 case Assembler::LessThan:
1576 return Assembler::GreaterThan;
1577 case Assembler::LessThanOrEqual:
1578 return Assembler::GreaterThanOrEqual;
1579 default:
1580 break;
1581 }
1582 MOZ_CRASH("unhandled condition")do { do { } while (false); MOZ_ReportCrash("" "unhandled condition"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1582); AnnotateMozCrashReason
("MOZ_CRASH(" "unhandled condition" ")"); do { MOZ_CrashSequence
(__null, 1582); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1583}
1584
1585void CodeGenerator::visitCompare(LCompare* comp) {
1586 MCompare::CompareType compareType = comp->mir()->compareType();
1587 Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop());
1588 Register left = ToRegister(comp->left());
1589 const LAllocation* right = comp->right();
1590 Register output = ToRegister(comp->output());
1591
1592 if (compareType == MCompare::Compare_Object ||
1593 compareType == MCompare::Compare_Symbol ||
1594 compareType == MCompare::Compare_IntPtr ||
1595 compareType == MCompare::Compare_UIntPtr ||
1596 compareType == MCompare::Compare_WasmAnyRef) {
1597 if (right->isConstant()) {
1598 MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1599); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1599); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1599 compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1599); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1599); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1600 masm.cmpPtrSet(cond, left, ImmWord(ToInt32(right)), output);
1601 } else if (right->isGeneralReg()) {
1602 masm.cmpPtrSet(cond, left, ToRegister(right), output);
1603 } else {
1604 masm.cmpPtrSet(ReverseCondition(cond), ToAddress(right), left, output);
1605 }
1606 return;
1607 }
1608
1609 MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1610); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1610); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1610 compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1610); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1610); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1611
1612 if (right->isConstant()) {
1613 masm.cmp32Set(cond, left, Imm32(ToInt32(right)), output);
1614 } else if (right->isGeneralReg()) {
1615 masm.cmp32Set(cond, left, ToRegister(right), output);
1616 } else {
1617 masm.cmp32Set(ReverseCondition(cond), ToAddress(right), left, output);
1618 }
1619}
1620
1621void CodeGenerator::visitStrictConstantCompareInt32(
1622 LStrictConstantCompareInt32* comp) {
1623 ValueOperand value = ToValue(comp->value());
1624 Register temp = ToRegister(comp->temp0());
1625 Register output = ToRegister(comp->output());
1626
1627 int32_t constantVal = comp->mir()->constant();
1628 JSOp op = comp->mir()->jsop();
1629 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1629); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1629); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1630
1631 masm.testValueSet(JSOpToCondition(op, false), value, Int32Value(constantVal),
1632 output);
1633 masm.testValueSet(JSOpToCondition(op, false), value, DoubleValue(constantVal),
1634 temp);
1635
1636 if (op == JSOp::StrictEq) {
1637 masm.or32(temp, output);
1638 } else {
1639 masm.and32(temp, output);
1640 }
1641
1642 if (constantVal == 0) {
1643 masm.testValueSet(JSOpToCondition(op, false), value, DoubleValue(-0.0),
1644 temp);
1645
1646 if (op == JSOp::StrictEq) {
1647 masm.or32(temp, output);
1648 } else {
1649 masm.and32(temp, output);
1650 }
1651 }
1652}
1653
1654void CodeGenerator::visitStrictConstantCompareInt32AndBranch(
1655 LStrictConstantCompareInt32AndBranch* comp) {
1656 ValueOperand value = ToValue(comp->value());
1657
1658 int32_t constantVal = comp->cmpMir()->constant();
1659 JSOp op = comp->cmpMir()->jsop();
1660 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1660); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1660); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1661 Assembler::Condition cond = JSOpToCondition(op, false);
1662
1663 MBasicBlock* ifTrue = comp->ifTrue();
1664 MBasicBlock* ifFalse = comp->ifFalse();
1665
1666 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1667 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1668
1669 Label* onEqual = op == JSOp::StrictEq ? trueLabel : falseLabel;
1670
1671 // If the next block is the true case, invert the condition to fall through.
1672 if (isNextBlock(ifTrue->lir())) {
1673 cond = Assembler::InvertCondition(cond);
1674 trueLabel = falseLabel;
1675 falseLabel = nullptr;
1676 } else if (isNextBlock(ifFalse->lir())) {
1677 falseLabel = nullptr;
1678 }
1679
1680 masm.branch64(Assembler::Equal, value.toRegister64(),
1681 Imm64(Int32Value(constantVal).asRawBits()), onEqual);
1682 if (constantVal == 0) {
1683 masm.branch64(Assembler::Equal, value.toRegister64(),
1684 Imm64(DoubleValue(0.0).asRawBits()), onEqual);
1685 masm.branch64(cond, value.toRegister64(),
1686 Imm64(DoubleValue(-0.0).asRawBits()), trueLabel, falseLabel);
1687 } else {
1688 masm.branch64(cond, value.toRegister64(),
1689 Imm64(DoubleValue(constantVal).asRawBits()), trueLabel,
1690 falseLabel);
1691 }
1692}
1693
1694void CodeGenerator::visitStrictConstantCompareBoolean(
1695 LStrictConstantCompareBoolean* comp) {
1696 ValueOperand value = ToValue(comp->value());
1697 Register output = ToRegister(comp->output());
1698
1699 bool constantVal = comp->mir()->constant();
1700 JSOp op = comp->mir()->jsop();
1701 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1701); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1701); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1702
1703 masm.testValueSet(JSOpToCondition(op, false), value,
1704 BooleanValue(constantVal), output);
1705}
1706
1707void CodeGenerator::visitStrictConstantCompareBooleanAndBranch(
1708 LStrictConstantCompareBooleanAndBranch* comp) {
1709 ValueOperand value = ToValue(comp->value());
1710
1711 bool constantVal = comp->cmpMir()->constant();
1712 JSOp op = comp->cmpMir()->jsop();
1713 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1713); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1713); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1714 Assembler::Condition cond = JSOpToCondition(op, false);
1715
1716 MBasicBlock* ifTrue = comp->ifTrue();
1717 MBasicBlock* ifFalse = comp->ifFalse();
1718
1719 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1720 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1721
1722 // If the next block is the true case, invert the condition to fall through.
1723 if (isNextBlock(ifTrue->lir())) {
1724 cond = Assembler::InvertCondition(cond);
1725 trueLabel = falseLabel;
1726 falseLabel = nullptr;
1727 } else if (isNextBlock(ifFalse->lir())) {
1728 falseLabel = nullptr;
1729 }
1730
1731 masm.branch64(cond, value.toRegister64(),
1732 Imm64(BooleanValue(constantVal).asRawBits()), trueLabel,
1733 falseLabel);
1734}
1735
1736void CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) {
1737 MCompare::CompareType compareType = comp->cmpMir()->compareType();
1738 Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop());
1739 Register left = ToRegister(comp->left());
1740 const LAllocation* right = comp->right();
1741
1742 MBasicBlock* ifTrue = comp->ifTrue();
1743 MBasicBlock* ifFalse = comp->ifFalse();
1744
1745 // If the next block is the true case, invert the condition to fall through.
1746 Label* label;
1747 if (isNextBlock(ifTrue->lir())) {
1748 cond = Assembler::InvertCondition(cond);
1749 label = getJumpLabelForBranch(ifFalse);
1750 } else {
1751 label = getJumpLabelForBranch(ifTrue);
1752 }
1753
1754 if (compareType == MCompare::Compare_Object ||
1755 compareType == MCompare::Compare_Symbol ||
1756 compareType == MCompare::Compare_IntPtr ||
1757 compareType == MCompare::Compare_UIntPtr ||
1758 compareType == MCompare::Compare_WasmAnyRef) {
1759 if (right->isConstant()) {
1760 MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1761); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1761); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1761 compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1761); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1761); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1762 masm.branchPtr(cond, left, ImmWord(ToInt32(right)), label);
1763 } else if (right->isGeneralReg()) {
1764 masm.branchPtr(cond, left, ToRegister(right), label);
1765 } else {
1766 masm.branchPtr(ReverseCondition(cond), ToAddress(right), left, label);
1767 }
1768 } else {
1769 MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1770); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1770); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1770 compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1770); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1770); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1771
1772 if (right->isConstant()) {
1773 masm.branch32(cond, left, Imm32(ToInt32(right)), label);
1774 } else if (right->isGeneralReg()) {
1775 masm.branch32(cond, left, ToRegister(right), label);
1776 } else {
1777 masm.branch32(ReverseCondition(cond), ToAddress(right), left, label);
1778 }
1779 }
1780
1781 if (!isNextBlock(ifTrue->lir())) {
1782 jumpToBlock(ifFalse);
1783 }
1784}
1785
1786void CodeGenerator::visitCompareI64(LCompareI64* lir) {
1787 MCompare::CompareType compareType = lir->mir()->compareType();
1788 MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1789); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1789); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1789 compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1789); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1789); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1790 bool isSigned = compareType == MCompare::Compare_Int64;
1791 Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
1792 Register64 left = ToRegister64(lir->left());
1793 LInt64Allocation right = lir->right();
1794 Register output = ToRegister(lir->output());
1795
1796 if (IsConstant(right)) {
1797 masm.cmp64Set(cond, left, Imm64(ToInt64(right)), output);
1798 } else if (IsRegister64(right)) {
1799 masm.cmp64Set(cond, left, ToRegister64(right), output);
1800 } else {
1801 masm.cmp64Set(ReverseCondition(cond), ToAddress(right), left, output);
1802 }
1803}
1804
1805void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) {
1806 MCompare::CompareType compareType = lir->cmpMir()->compareType();
1807 MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1808); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1808); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1808 compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1808); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1808); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1809 bool isSigned = compareType == MCompare::Compare_Int64;
1810 Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
1811 Register64 left = ToRegister64(lir->left());
1812 LInt64Allocation right = lir->right();
1813
1814 MBasicBlock* ifTrue = lir->ifTrue();
1815 MBasicBlock* ifFalse = lir->ifFalse();
1816
1817 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1818 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1819
1820 // If the next block is the true case, invert the condition to fall through.
1821 if (isNextBlock(ifTrue->lir())) {
1822 cond = Assembler::InvertCondition(cond);
1823 trueLabel = falseLabel;
1824 falseLabel = nullptr;
1825 } else if (isNextBlock(ifFalse->lir())) {
1826 falseLabel = nullptr;
1827 }
1828
1829 if (IsConstant(right)) {
1830 masm.branch64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel);
1831 } else if (IsRegister64(right)) {
1832 masm.branch64(cond, left, ToRegister64(right), trueLabel, falseLabel);
1833 } else {
1834 masm.branch64(ReverseCondition(cond), ToAddress(right), left, trueLabel,
1835 falseLabel);
1836 }
1837}
1838
1839void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) {
1840 Assembler::Condition cond = baab->cond();
1841 MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cond == Assembler::Zero || cond == Assembler::NonZero
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(cond == Assembler::Zero || cond == Assembler::NonZero
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"cond == Assembler::Zero || cond == Assembler::NonZero", "./../../../../js/src/jit/CodeGenerator.cpp"
, 1841); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cond == Assembler::Zero || cond == Assembler::NonZero"
")"); do { MOZ_CrashSequence(__null, 1841); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1842
1843 Register left = ToRegister(baab->left());
1844 const LAllocation* right = baab->right();
1845
1846 MBasicBlock* ifTrue = baab->ifTrue();
1847 MBasicBlock* ifFalse = baab->ifFalse();
1848
1849 // If the next block is the true case, invert the condition to fall through.
1850 Label* label;
1851 if (isNextBlock(ifTrue->lir())) {
1852 cond = Assembler::InvertCondition(cond);
1853 label = getJumpLabelForBranch(ifFalse);
1854 } else {
1855 label = getJumpLabelForBranch(ifTrue);
1856 }
1857
1858 if (right->isConstant()) {
1859 masm.branchTest32(cond, left, Imm32(ToInt32(right)), label);
1860 } else {
1861 masm.branchTest32(cond, left, ToRegister(right), label);
1862 }
1863
1864 if (!isNextBlock(ifTrue->lir())) {
1865 jumpToBlock(ifFalse);
1866 }
1867}
1868
1869void CodeGenerator::visitBitAnd64AndBranch(LBitAnd64AndBranch* baab) {
1870 Assembler::Condition cond = baab->cond();
1871 MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cond == Assembler::Zero || cond == Assembler::NonZero
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(cond == Assembler::Zero || cond == Assembler::NonZero
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"cond == Assembler::Zero || cond == Assembler::NonZero", "./../../../../js/src/jit/CodeGenerator.cpp"
, 1871); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cond == Assembler::Zero || cond == Assembler::NonZero"
")"); do { MOZ_CrashSequence(__null, 1871); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1872
1873 Register64 left = ToRegister64(baab->left());
1874 LInt64Allocation right = baab->right();
1875
1876 MBasicBlock* ifTrue = baab->ifTrue();
1877 MBasicBlock* ifFalse = baab->ifFalse();
1878
1879 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1880 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1881
1882 // If the next block is the true case, invert the condition to fall through.
1883 if (isNextBlock(ifTrue->lir())) {
1884 cond = Assembler::InvertCondition(cond);
1885 trueLabel = falseLabel;
1886 falseLabel = nullptr;
1887 } else if (isNextBlock(ifFalse->lir())) {
1888 falseLabel = nullptr;
1889 }
1890
1891 if (IsConstant(right)) {
1892 masm.branchTest64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel);
1893 } else {
1894 masm.branchTest64(cond, left, ToRegister64(right), trueLabel, falseLabel);
1895 }
1896}
1897
1898void CodeGenerator::assertObjectDoesNotEmulateUndefined(
1899 Register input, Register temp, const MInstruction* mir) {
1900#if defined(DEBUG1) || defined(FUZZING)
1901 // Validate that the object indeed doesn't have the emulates undefined flag.
1902 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
1903 addOutOfLineCode(ool, mir);
1904
1905 Label* doesNotEmulateUndefined = ool->label1();
1906 Label* emulatesUndefined = ool->label2();
1907
1908 testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined,
1909 temp, ool);
1910 masm.bind(emulatesUndefined);
1911 masm.assumeUnreachable(
1912 "Found an object emulating undefined while the fuse is intact");
1913 masm.bind(doesNotEmulateUndefined);
1914#endif
1915}
1916
1917void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) {
1918 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1919 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1920 Register input = ToRegister(lir->input());
1921 Register temp = ToRegister(lir->temp0());
1922
1923 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
1924 if (intact) {
1925 assertObjectDoesNotEmulateUndefined(input, temp, lir->mir());
1926 // Bug 1874905: It would be fantastic if this could be optimized out
1927 masm.jump(truthy);
1928 } else {
1929 auto* ool = new (alloc()) OutOfLineTestObject();
1930 addOutOfLineCode(ool, lir->mir());
1931
1932 testObjectEmulatesUndefined(input, falsy, truthy, temp, ool);
1933 }
1934}
1935
1936void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) {
1937 OutOfLineTestObject* ool = nullptr;
1938 if (!hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted()) {
1939 ool = new (alloc()) OutOfLineTestObject();
1940 addOutOfLineCode(ool, lir->mir());
1941 }
1942
1943 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1944 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1945
1946 ValueOperand input = ToValue(lir->input());
1947 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
1948 Register temp = ToRegister(lir->temp2());
1949 FloatRegister floatTemp = ToFloatRegister(lir->temp0());
1950 const TypeDataList& observedTypes = lir->mir()->observedTypes();
1951
1952 testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy,
1953 falsy, ool);
1954 masm.jump(truthy);
1955}
1956
1957void CodeGenerator::visitBooleanToString(LBooleanToString* lir) {
1958 Register input = ToRegister(lir->input());
1959 Register output = ToRegister(lir->output());
1960 const JSAtomState& names = gen->runtime->names();
1961 Label true_, done;
1962
1963 masm.branchTest32(Assembler::NonZero, input, input, &true_);
1964 masm.movePtr(ImmGCPtr(names.false_), output);
1965 masm.jump(&done);
1966
1967 masm.bind(&true_);
1968 masm.movePtr(ImmGCPtr(names.true_), output);
1969
1970 masm.bind(&done);
1971}
1972
1973void CodeGenerator::visitIntToString(LIntToString* lir) {
1974 Register input = ToRegister(lir->input());
1975 Register output = ToRegister(lir->output());
1976
1977 using Fn = JSLinearString* (*)(JSContext*, int);
1978 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
1979 lir, ArgList(input), StoreRegisterTo(output));
1980
1981 masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(),
1982 ool->entry());
1983
1984 masm.bind(ool->rejoin());
1985}
1986
1987void CodeGenerator::visitDoubleToString(LDoubleToString* lir) {
1988 FloatRegister input = ToFloatRegister(lir->input());
1989 Register temp = ToRegister(lir->temp0());
1990 Register output = ToRegister(lir->output());
1991
1992 using Fn = JSString* (*)(JSContext*, double);
1993 OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>(
1994 lir, ArgList(input), StoreRegisterTo(output));
1995
1996 // Try double to integer conversion and run integer to string code.
1997 masm.convertDoubleToInt32(input, temp, ool->entry(), false);
1998 masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(),
1999 ool->entry());
2000
2001 masm.bind(ool->rejoin());
2002}
2003
2004void CodeGenerator::visitValueToString(LValueToString* lir) {
2005 ValueOperand input = ToValue(lir->input());
2006 Register output = ToRegister(lir->output());
2007
2008 using Fn = JSString* (*)(JSContext*, HandleValue);
2009 OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>(
2010 lir, ArgList(input), StoreRegisterTo(output));
2011
2012 Label done;
2013 Register tag = masm.extractTag(input, output);
2014 const JSAtomState& names = gen->runtime->names();
2015
2016 // String
2017 {
2018 Label notString;
2019 masm.branchTestString(Assembler::NotEqual, tag, &notString);
2020 masm.unboxString(input, output);
2021 masm.jump(&done);
2022 masm.bind(&notString);
2023 }
2024
2025 // Integer
2026 {
2027 Label notInteger;
2028 masm.branchTestInt32(Assembler::NotEqual, tag, &notInteger);
2029 Register unboxed = ToTempUnboxRegister(lir->temp0());
2030 unboxed = masm.extractInt32(input, unboxed);
2031 masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(),
2032 ool->entry());
2033 masm.jump(&done);
2034 masm.bind(&notInteger);
2035 }
2036
2037 // Double
2038 {
2039 // Note: no fastpath. Need two extra registers and can only convert doubles
2040 // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT.
2041 masm.branchTestDouble(Assembler::Equal, tag, ool->entry());
2042 }
2043
2044 // Undefined
2045 {
2046 Label notUndefined;
2047 masm.branchTestUndefined(Assembler::NotEqual, tag, &notUndefined);
2048 masm.movePtr(ImmGCPtr(names.undefined), output);
2049 masm.jump(&done);
2050 masm.bind(&notUndefined);
2051 }
2052
2053 // Null
2054 {
2055 Label notNull;
2056 masm.branchTestNull(Assembler::NotEqual, tag, &notNull);
2057 masm.movePtr(ImmGCPtr(names.null), output);
2058 masm.jump(&done);
2059 masm.bind(&notNull);
2060 }
2061
2062 // Boolean
2063 {
2064 Label notBoolean, true_;
2065 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
2066 masm.branchTestBooleanTruthy(true, input, &true_);
2067 masm.movePtr(ImmGCPtr(names.false_), output);
2068 masm.jump(&done);
2069 masm.bind(&true_);
2070 masm.movePtr(ImmGCPtr(names.true_), output);
2071 masm.jump(&done);
2072 masm.bind(&notBoolean);
2073 }
2074
2075 // Objects/symbols are only possible when |mir->mightHaveSideEffects()|.
2076 if (lir->mir()->mightHaveSideEffects()) {
2077 // Object
2078 if (lir->mir()->supportSideEffects()) {
2079 masm.branchTestObject(Assembler::Equal, tag, ool->entry());
2080 } else {
2081 // Bail.
2082 MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->mir()->needsSnapshot()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 2082); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { MOZ_CrashSequence(__null, 2082); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2083 Label bail;
2084 masm.branchTestObject(Assembler::Equal, tag, &bail);
2085 bailoutFrom(&bail, lir->snapshot());
2086 }
2087
2088 // Symbol
2089 if (lir->mir()->supportSideEffects()) {
2090 masm.branchTestSymbol(Assembler::Equal, tag, ool->entry());
2091 } else {
2092 // Bail.
2093 MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->mir()->needsSnapshot()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 2093); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { MOZ_CrashSequence(__null, 2093); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2094 Label bail;
2095 masm.branchTestSymbol(Assembler::Equal, tag, &bail);
2096 bailoutFrom(&bail, lir->snapshot());
2097 }
2098 }
2099
2100 // BigInt
2101 {
2102 // No fastpath currently implemented.
2103 masm.branchTestBigInt(Assembler::Equal, tag, ool->entry());
2104 }
2105
2106 masm.assumeUnreachable("Unexpected type for LValueToString.");
2107
2108 masm.bind(&done);
2109 masm.bind(ool->rejoin());
2110}
2111
2112using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**);
2113
2114static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder,
2115 size_t offset, Register buffer,
2116 LiveGeneralRegisterSet& liveVolatiles,
2117 StoreBufferMutationFn fun) {
2118 Label callVM;
2119 Label exit;
2120
2121 // Call into the VM to barrier the write. The only registers that need to
2122 // be preserved are those in liveVolatiles, so once they are saved on the
2123 // stack all volatile registers are available for use.
2124 masm.bind(&callVM);
2125 masm.PushRegsInMask(liveVolatiles);
2126
2127 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
2128 regs.takeUnchecked(buffer);
2129 regs.takeUnchecked(holder);
2130 Register addrReg = regs.takeAny();
2131
2132 masm.computeEffectiveAddress(Address(holder, offset), addrReg);
2133
2134 bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>();
2135 if (needExtraReg) {
2136 masm.push(holder);
2137 masm.setupUnalignedABICall(holder);
2138 } else {
2139 masm.setupUnalignedABICall(regs.takeAny());
2140 }
2141 masm.passABIArg(buffer);
2142 masm.passABIArg(addrReg);
2143 masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun),
2144 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
2145
2146 if (needExtraReg) {
2147 masm.pop(holder);
2148 }
2149 masm.PopRegsInMask(liveVolatiles);
2150 masm.bind(&exit);
2151}
2152
2153// Warning: this function modifies prev and next.
2154static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder,
2155 size_t offset, Register prev, Register next,
2156 LiveGeneralRegisterSet& liveVolatiles) {
2157 Label exit;
2158 Label checkRemove, putCell;
2159
2160 // if (next && (buffer = next->storeBuffer()))
2161 // but we never pass in nullptr for next.
2162 Register storebuffer = next;
2163 masm.loadStoreBuffer(next, storebuffer);
2164 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove);
2165
2166 // if (prev && prev->storeBuffer())
2167 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell);
2168 masm.loadStoreBuffer(prev, prev);
2169 masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit);
2170
2171 // buffer->putCell(cellp)
2172 masm.bind(&putCell);
2173 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
2174 JSString::addCellAddressToStoreBuffer);
2175 masm.jump(&exit);
2176
2177 // if (prev && (buffer = prev->storeBuffer()))
2178 masm.bind(&checkRemove);
2179 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit);
2180 masm.loadStoreBuffer(prev, storebuffer);
2181 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit);
2182 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
2183 JSString::removeCellAddressFromStoreBuffer);
2184
2185 masm.bind(&exit);
2186}
2187
2188void CodeGenerator::visitRegExp(LRegExp* lir) {
2189 Register output = ToRegister(lir->output());
2190 Register temp = ToRegister(lir->temp0());
2191 JSObject* source = lir->mir()->source();
2192
2193 using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
2194 OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>(
2195 lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output));
2196 if (lir->mir()->hasShared()) {
2197 TemplateObject templateObject(source);
2198 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
2199 ool->entry());
2200 } else {
2201 masm.jump(ool->entry());
2202 }
2203 masm.bind(ool->rejoin());
2204}
2205
2206/*
2207 * [SMDOC] RegExp stubs
2208 *
2209 * The RegExp stubs are a set of lazily generated per-zone stubs
2210 * providing fast paths for regexp execution in baseline and Ion.
2211 * In general, they are invoked from self-hosted code.
2212 *
2213 * There are four stubs:
2214 * - RegExpMatcher: Given a regular expression, an input string,
2215 * and the current lastIndex, return the match result object.
2216 * - RegExpExecMatch: The same as RegExpMatcher, but lastIndex is
2217 * not an argument. Instead, for sticky/global regexps, it is
2218 * loaded from the regexp, and the new value is stored back to
2219 * the regexp after execution. Otherwise, it is hardcoded to 0.
2220 * - RegExpSearcher: Given a regular expression, an input string,
2221 * and the current lastIndex, return the index of the next match.
2222 * - RegExpExecTest: Given a regular expression and an input string,
2223 * return a boolean indicating whether a match was found. This
2224 * stub has the same behaviour as RegExpExecMatch with respect to
2225 * lastIndex.
2226 */
2227
2228// Offset of the InputOutputData relative to the frame pointer in regexp stubs.
2229// The InputOutputData is allocated by the caller, so it is placed above the
2230// frame pointer and return address on the stack.
2231static constexpr size_t RegExpInputOutputDataOffset = 2 * sizeof(void*);
2232
2233static constexpr size_t RegExpPairsVectorStartOffset =
2234 RegExpInputOutputDataOffset + InputOutputDataSize + sizeof(MatchPairs);
2235
2236static Address RegExpPairCountAddress() {
2237 return Address(FramePointer, RegExpInputOutputDataOffset +
2238 int32_t(InputOutputDataSize) +
2239 MatchPairs::offsetOfPairCount());
2240}
2241
2242static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp,
2243 Register input, Register lastIndex,
2244 Register staticsReg, Register temp1,
2245 Register temp2, gc::Heap initialStringHeap,
2246 LiveGeneralRegisterSet& volatileRegs) {
2247 Address pendingInputAddress(staticsReg,
2248 RegExpStatics::offsetOfPendingInput());
2249 Address matchesInputAddress(staticsReg,
2250 RegExpStatics::offsetOfMatchesInput());
2251 Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource());
2252 Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex());
2253 Label legacyFeaturesEnabled, done;
2254 if (JS::Prefs::experimental_legacy_regexp()) {
2255 Address invalidatedAddress(staticsReg,
2256 RegExpStatics::offsetOfInvalidated());
2257
2258 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
2259 RegExpObject::flagsSlot())),
2260 temp1, JSVAL_TYPE_INT32);
2261 masm.branchTest32(Assembler::NonZero, temp1,
2262 Imm32(RegExpObject::LegacyFeaturesEnabledBit),
2263 &legacyFeaturesEnabled);
2264 masm.store8(Imm32(1), invalidatedAddress);
2265 masm.jump(&done);
2266 masm.bind(&legacyFeaturesEnabled);
2267 masm.store8(Imm32(0), invalidatedAddress);
2268 }
2269
2270 masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String);
2271 masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String);
2272 masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String);
2273
2274 if (initialStringHeap == gc::Heap::Default) {
2275 // Writing into RegExpStatics tenured memory; must post-barrier.
2276 if (staticsReg.volatile_()) {
2277 volatileRegs.add(staticsReg);
2278 }
2279
2280 masm.loadPtr(pendingInputAddress, temp1);
2281 masm.storePtr(input, pendingInputAddress);
2282 masm.movePtr(input, temp2);
2283 EmitPostWriteBarrierS(masm, staticsReg,
2284 RegExpStatics::offsetOfPendingInput(),
2285 temp1 /* prev */, temp2 /* next */, volatileRegs);
2286
2287 masm.loadPtr(matchesInputAddress, temp1);
2288 masm.storePtr(input, matchesInputAddress);
2289 masm.movePtr(input, temp2);
2290 EmitPostWriteBarrierS(masm, staticsReg,
2291 RegExpStatics::offsetOfMatchesInput(),
2292 temp1 /* prev */, temp2 /* next */, volatileRegs);
2293 } else {
2294 masm.debugAssertGCThingIsTenured(input, temp1);
2295 masm.storePtr(input, pendingInputAddress);
2296 masm.storePtr(input, matchesInputAddress);
2297 }
2298
2299 masm.storePtr(lastIndex,
2300 Address(staticsReg, RegExpStatics::offsetOfLazyIndex()));
2301 masm.store32(
2302 Imm32(1),
2303 Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation()));
2304
2305 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffsetTyped(
2306 RegExpObject::SHARED_SLOT)),
2307 temp1, JSVAL_TYPE_PRIVATE_GCTHING);
2308 masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2);
2309 masm.storePtr(temp2, lazySourceAddress);
2310 static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size");
2311 masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2);
2312 masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags()));
2313 masm.bind(&done);
2314}
2315
2316// Prepare an InputOutputData and optional MatchPairs which space has been
2317// allocated for on the stack, and try to execute a RegExp on a string input.
2318// If the RegExp was successfully executed and matched the input, fallthrough.
2319// Otherwise, jump to notFound or failure.
2320static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp,
2321 Register input, Register lastIndex,
2322 Register temp1, Register temp2,
2323 Register temp3, gc::Heap initialStringHeap,
2324 Label* notFound, Label* failure,
2325 JitZone::StubKind kind) {
2326 JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp");
2327
2328 using irregexp::InputOutputData;
2329
2330 /*
2331 * [SMDOC] Stack layout for PrepareAndExecuteRegExp
2332 *
2333 * Before this function is called, the caller is responsible for
2334 * allocating enough stack space for the result data. This code
2335 * will fill in that data. This means that the match pairs will
2336 * not be freed when we return from a match stub, which allows us
2337 * to reuse them if we have to call into the VM to allocate results,
2338 * instead of executing the regexp from scratch. For consistency,
2339 * we use the same approach for stubs that don't use match pairs.
2340 *
2341 * +---------------+
2342 * | Saved frameptr|
2343 * | Return address|
2344 * Current frame +---------------+
2345 *------------------------------------------------------------
2346 * Caller's frame +---------------+
2347 * |InputOutputData|
2348 * inputStartAddress +----------> inputStart|
2349 * inputEndAddress +----------> inputEnd|
2350 * startIndexAddress +----------> startIndex|
2351 * matchesAddress +----------> matches|-----+
2352 * +---------------+ |
2353 * matchPairs(Address|Offset) +-----> +---------------+ <--+
2354 * | MatchPairs |
2355 * pairCountAddress +----------> count |
2356 * pairsPointerAddress +----------> pairs |-----+
2357 * +---------------+ |
2358 * pairsArray(Address|Offset) +-----> +---------------+ <--+
2359 * | MatchPair |
2360 * firstMatchStartAddress +----------> start | <--+
2361 * | limit | |
2362 * +---------------+ |
2363 * . |
2364 * . Reserved space for
2365 * . RegExpObject::MaxPairCount
2366 * . MatchPair objects
2367 * . |
2368 * +---------------+ |
2369 * | MatchPair | |
2370 * | start | |
2371 * | limit | <--+
2372 * +---------------+
2373 */
2374
2375 int32_t ioOffset = RegExpInputOutputDataOffset;
2376 int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData));
2377 int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs));
2378
2379 Address inputStartAddress(FramePointer,
2380 ioOffset + InputOutputData::offsetOfInputStart());
2381 Address inputEndAddress(FramePointer,
2382 ioOffset + InputOutputData::offsetOfInputEnd());
2383 Address startIndexAddress(FramePointer,
2384 ioOffset + InputOutputData::offsetOfStartIndex());
2385 Address matchesAddress(FramePointer,
2386 ioOffset + InputOutputData::offsetOfMatches());
2387
2388 Address matchPairsAddress(FramePointer, matchPairsOffset);
2389 Address pairCountAddress(FramePointer,
2390 matchPairsOffset + MatchPairs::offsetOfPairCount());
2391 Address pairsPointerAddress(FramePointer,
2392 matchPairsOffset + MatchPairs::offsetOfPairs());
2393
2394 Address pairsArrayAddress(FramePointer, pairsArrayOffset);
2395 Address firstMatchStartAddress(FramePointer,
2396 pairsArrayOffset + MatchPair::offsetOfStart());
2397
2398 // First, fill in a skeletal MatchPairs instance on the stack. This will be
2399 // passed to the OOL stub in the caller if we aren't able to execute the
2400 // RegExp inline, and that stub needs to be able to determine whether the
2401 // execution finished successfully.
2402
2403 // Initialize MatchPairs::pairCount to 1. The correct value can only
2404 // be determined after loading the RegExpShared. If the RegExpShared
2405 // has Kind::Atom, this is the correct pairCount.
2406 masm.store32(Imm32(1), pairCountAddress);
2407
2408 // Initialize MatchPairs::pairs pointer
2409 masm.computeEffectiveAddress(pairsArrayAddress, temp1);
2410 masm.storePtr(temp1, pairsPointerAddress);
2411
2412 // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch
2413 masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress);
2414
2415 // Determine the set of volatile inputs to save when calling into C++ or
2416 // regexp code.
2417 LiveGeneralRegisterSet volatileRegs;
2418 if (lastIndex.volatile_()) {
2419 volatileRegs.add(lastIndex);
2420 }
2421 if (input.volatile_()) {
2422 volatileRegs.add(input);
2423 }
2424 if (regexp.volatile_()) {
2425 volatileRegs.add(regexp);
2426 }
2427
2428 // Ensure the input string is not a rope.
2429 Label isLinear;
2430 masm.branchIfNotRope(input, &isLinear);
2431 {
2432 masm.PushRegsInMask(volatileRegs);
2433
2434 using Fn = JSLinearString* (*)(JSString*);
2435 masm.setupUnalignedABICall(temp1);
2436 masm.passABIArg(input);
2437 masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>();
2438
2439 MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2439); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { MOZ_CrashSequence
(__null, 2439); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2440 masm.storeCallPointerResult(temp1);
2441 masm.PopRegsInMask(volatileRegs);
2442
2443 masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure);
2444 }
2445 masm.bind(&isLinear);
2446
2447 // Load the RegExpShared.
2448 Register regexpReg = temp1;
2449 Address sharedSlot = Address(
2450 regexp, NativeObject::getFixedSlotOffsetTyped(RegExpObject::SHARED_SLOT));
2451 masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure);
2452 masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING);
2453
2454 // Handle Atom matches
2455 Label notAtom, checkSuccess;
2456 masm.branchPtr(Assembler::Equal,
2457 Address(regexpReg, RegExpShared::offsetOfPatternAtom()),
2458 ImmWord(0), &notAtom);
2459 {
2460 masm.computeEffectiveAddress(matchPairsAddress, temp3);
2461
2462 masm.PushRegsInMask(volatileRegs);
2463 using Fn =
2464 RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input,
2465 size_t start, MatchPairs* matchPairs);
2466 masm.setupUnalignedABICall(temp2);
2467 masm.passABIArg(regexpReg);
2468 masm.passABIArg(input);
2469 masm.passABIArg(lastIndex);
2470 masm.passABIArg(temp3);
2471 masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>();
2472
2473 MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2473); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { MOZ_CrashSequence
(__null, 2473); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2474 masm.storeCallInt32Result(temp1);
2475 masm.PopRegsInMask(volatileRegs);
2476
2477 masm.jump(&checkSuccess);
2478 }
2479 masm.bind(&notAtom);
2480
2481 // If we don't need to look at the capture groups, we can leave pairCount at 1
2482 // (set above). The regexp code is special-cased to skip copying capture
2483 // groups if the pair count is 1, which also lets us avoid having to allocate
2484 // memory to store them.
2485 bool skipMatchPairs = kind == JitZone::StubKind::RegExpSearcher ||
2486 kind == JitZone::StubKind::RegExpExecTest;
2487 if (!skipMatchPairs) {
2488 // Don't handle regexps with too many capture pairs.
2489 masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2);
2490 masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount),
2491 failure);
2492
2493 // Fill in the pair count in the MatchPairs on the stack.
2494 masm.store32(temp2, pairCountAddress);
2495 }
2496
2497 // Load code pointer and length of input (in bytes).
2498 // Store the input start in the InputOutputData.
2499 Register codePointer = temp1; // Note: temp1 was previously regexpReg.
2500 Register byteLength = temp3;
2501 {
2502 Label isLatin1, done;
2503 masm.loadStringLength(input, byteLength);
2504
2505 masm.branchLatin1String(input, &isLatin1);
2506
2507 // Two-byte input
2508 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
2509 masm.storePtr(temp2, inputStartAddress);
2510 masm.loadPtr(
2511 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)),
2512 codePointer);
2513 masm.lshiftPtr(Imm32(1), byteLength);
2514 masm.jump(&done);
2515
2516 // Latin1 input
2517 masm.bind(&isLatin1);
2518 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
2519 masm.storePtr(temp2, inputStartAddress);
2520 masm.loadPtr(
2521 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)),
2522 codePointer);
2523
2524 masm.bind(&done);
2525
2526 // Store end pointer
2527 masm.addPtr(byteLength, temp2);
2528 masm.storePtr(temp2, inputEndAddress);
2529 }
2530
2531 // Guard that the RegExpShared has been compiled for this type of input.
2532 // If it has not been compiled, we fall back to the OOL case, which will
2533 // do a VM call into the interpreter.
2534 // TODO: add an interpreter trampoline?
2535 masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
2536 masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
2537
2538 // Finish filling in the InputOutputData instance on the stack
2539 masm.computeEffectiveAddress(matchPairsAddress, temp2);
2540 masm.storePtr(temp2, matchesAddress);
2541 masm.storePtr(lastIndex, startIndexAddress);
2542
2543 // Execute the RegExp.
2544 masm.computeEffectiveAddress(Address(FramePointer, ioOffset), temp2);
2545 masm.PushRegsInMask(volatileRegs);
2546 masm.setupUnalignedABICall(temp3);
2547 masm.passABIArg(temp2);
2548 masm.callWithABI(codePointer);
2549 masm.storeCallInt32Result(temp1);
2550 masm.PopRegsInMask(volatileRegs);
2551
2552 masm.bind(&checkSuccess);
2553 masm.branch32(Assembler::Equal, temp1,
2554 Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound);
2555 masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)),
2556 failure);
2557
2558 // Lazily update the RegExpStatics.
2559 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2560 RegExpRealm::offsetOfRegExpStatics();
2561 masm.loadGlobalObjectData(temp1);
2562 masm.loadPtr(Address(temp1, offset), temp1);
2563 UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3,
2564 initialStringHeap, volatileRegs);
2565
2566 return true;
2567}
2568
2569// Shift a bit within a 32-bit word from one bit position to another.
2570// Both FromBitMask and ToBitMask must have a single bit set.
2571template <uint32_t FromBitMask, uint32_t ToBitMask>
2572static void ShiftFlag32(MacroAssembler& masm, Register reg) {
2573 static_assert(std::has_single_bit(FromBitMask));
2574 static_assert(std::has_single_bit(ToBitMask));
2575 static_assert(FromBitMask != ToBitMask);
2576 constexpr uint32_t fromShift = std::countr_zero(FromBitMask);
2577 constexpr uint32_t toShift = std::countr_zero(ToBitMask);
2578 if (fromShift < toShift) {
2579 masm.lshift32(Imm32(toShift - fromShift), reg);
2580 } else {
2581 masm.rshift32(Imm32(fromShift - toShift), reg);
2582 }
2583}
2584
2585static void EmitInitDependentStringBase(MacroAssembler& masm,
2586 Register dependent, Register base,
2587 Register temp1, Register temp2,
2588 bool needsPostBarrier) {
2589 // Determine the base string to use and store it in temp2.
2590 Label notDependent, markedDependedOn;
2591 masm.load32(Address(base, JSString::offsetOfFlags()), temp1);
2592 masm.branchTest32(Assembler::Zero, temp1, Imm32(StringFlags::DEPENDENT_BIT),
2593 &notDependent);
2594 {
2595 // The base is also a dependent string. Load its base to prevent chains of
2596 // dependent strings in most cases. This must either be an atom or already
2597 // have the DEPENDED_ON_BIT set.
2598 masm.loadDependentStringBase(base, temp2);
2599 masm.jump(&markedDependedOn);
2600 }
2601 masm.bind(&notDependent);
2602 {
2603 // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not
2604 // an atom (ATOM_BIT is not set). Roughly:
2605 //
2606 // flags |= ((~flags) & ATOM_BIT) << (DEPENDED_ON_BIT - ATOM_BIT))
2607 //
2608 // but further modified to combine the initial move with an OR:
2609 //
2610 // flags |= ~(flags | ~ATOM_BIT) << (DEPENDED_ON_BIT - ATOM_BIT)
2611 //
2612 masm.nor32(Imm32(~StringFlags::ATOM_BIT), temp1, temp2);
2613 ShiftFlag32<StringFlags::ATOM_BIT, StringFlags::DEPENDED_ON_BIT>(masm,
2614 temp2);
2615 masm.or32(temp2, temp1);
2616 masm.movePtr(base, temp2);
2617 masm.store32(temp1, Address(temp2, JSString::offsetOfFlags()));
2618 }
2619 masm.bind(&markedDependedOn);
2620
2621#ifdef DEBUG1
2622 // Assert the base has the DEPENDED_ON_BIT set or is an atom.
2623 Label isAppropriatelyMarked;
2624 masm.branchTest32(Assembler::NonZero,
2625 Address(temp2, JSString::offsetOfFlags()),
2626 Imm32(StringFlags::ATOM_BIT | StringFlags::DEPENDED_ON_BIT),
2627 &isAppropriatelyMarked);
2628 masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT");
2629 masm.bind(&isAppropriatelyMarked);
2630#endif
2631 masm.storeDependentStringBase(temp2, dependent);
2632
2633 // Post-barrier the base store. The base is still in temp2.
2634 if (needsPostBarrier) {
2635 Label done;
2636 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2637 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2638
2639 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2640 regsToSave.takeUnchecked(temp1);
2641 regsToSave.takeUnchecked(temp2);
2642
2643 masm.PushRegsInMask(regsToSave);
2644
2645 masm.mov(ImmPtr(masm.runtime()), temp1);
2646
2647 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell);
2648 masm.setupUnalignedABICall(temp2);
2649 masm.passABIArg(temp1);
2650 masm.passABIArg(dependent);
2651 masm.callWithABI<Fn, PostWriteBarrier>();
2652
2653 masm.PopRegsInMask(regsToSave);
2654
2655 masm.bind(&done);
2656 } else {
2657#ifdef DEBUG1
2658 Label done;
2659 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2660 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2661 masm.assumeUnreachable("Missing post barrier for dependent string base");
2662 masm.bind(&done);
2663#endif
2664 }
2665}
2666
2667static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
2668 Register len, Register byteOpScratch,
2669 CharEncoding encoding,
2670 size_t maximumLength = SIZE_MAX(18446744073709551615UL));
2671
2672class CreateDependentString {
2673 CharEncoding encoding_;
2674 Register string_;
2675 Register temp1_;
2676 Register temp2_;
2677 Label* failure_;
2678
2679 enum class FallbackKind : uint8_t {
2680 InlineString,
2681 FatInlineString,
2682 NotInlineString,
2683 Count
2684 };
2685 mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)>
2686 fallbacks_, joins_;
2687
2688 public:
2689 CreateDependentString(CharEncoding encoding, Register string, Register temp1,
2690 Register temp2, Label* failure)
2691 : encoding_(encoding),
2692 string_(string),
2693 temp1_(temp1),
2694 temp2_(temp2),
2695 failure_(failure) {}
2696
2697 Register string() const { return string_; }
2698 CharEncoding encoding() const { return encoding_; }
2699
2700 // Generate code that creates DependentString.
2701 // Caller should call generateFallback after masm.ret(), to generate
2702 // fallback path.
2703 void generate(MacroAssembler& masm, const JSAtomState& names,
2704 CompileRuntime* runtime, Register base,
2705 BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
2706 gc::Heap initialStringHeap);
2707
2708 // Generate fallback path for creating DependentString.
2709 void generateFallback(MacroAssembler& masm);
2710};
2711
2712void CreateDependentString::generate(MacroAssembler& masm,
2713 const JSAtomState& names,
2714 CompileRuntime* runtime, Register base,
2715 BaseIndex startIndexAddress,
2716 BaseIndex limitIndexAddress,
2717 gc::Heap initialStringHeap) {
2718 JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)",
2719 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2720
2721 auto newGCString = [&](FallbackKind kind) {
2722 uint32_t flags;
2723 switch (kind) {
2724 case FallbackKind::InlineString:
2725 flags = StringFlags::thinInlineStringFlags(encoding_);
2726 break;
2727 case FallbackKind::FatInlineString:
2728 flags = StringFlags::fatInlineStringFlags(encoding_);
2729 break;
2730 case FallbackKind::NotInlineString:
2731 flags = StringFlags::dependentStringFlags(encoding_);
2732 break;
2733 default:
2734 MOZ_CRASH("Unexpected FallbackKind")do { do { } while (false); MOZ_ReportCrash("" "Unexpected FallbackKind"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2734); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected FallbackKind" ")"); do { MOZ_CrashSequence
(__null, 2734); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
2735 }
2736
2737 if (kind != FallbackKind::FatInlineString) {
2738 masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]);
2739 } else {
2740 masm.newGCFatInlineString(string_, temp2_, initialStringHeap,
2741 &fallbacks_[kind]);
2742 }
2743 masm.bind(&joins_[kind]);
2744 masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags()));
2745 };
2746
2747 // Compute the string length.
2748 masm.load32(startIndexAddress, temp2_);
2749 masm.load32(limitIndexAddress, temp1_);
2750 masm.sub32(temp2_, temp1_);
2751
2752 Label done, nonEmpty;
2753
2754 // Zero length matches use the empty string.
2755 masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty);
2756 masm.movePtr(ImmGCPtr(names.empty_), string_);
2757 masm.jump(&done);
2758
2759 masm.bind(&nonEmpty);
2760
2761 // Complete matches use the base string.
2762 Label nonBaseStringMatch;
2763 masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch);
2764 masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()),
2765 temp1_, &nonBaseStringMatch);
2766 masm.movePtr(base, string_);
2767 masm.jump(&done);
2768
2769 masm.bind(&nonBaseStringMatch);
2770
2771 Label notInline;
2772
2773 int32_t maxInlineLength = encoding_ == CharEncoding::Latin1
2774 ? JSFatInlineString::MAX_LENGTH_LATIN1
2775 : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
2776 masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), &notInline);
2777 {
2778 // Make a thin or fat inline string.
2779 Label stringAllocated, fatInline;
2780
2781 int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1
2782 ? JSThinInlineString::MAX_LENGTH_LATIN1
2783 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
2784 masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength),
2785 &fatInline);
2786 if (encoding_ == CharEncoding::Latin1) {
2787 // One character Latin-1 strings can be loaded directly from the
2788 // static strings table.
2789 Label thinInline;
2790 masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline);
2791 {
2792 static_assert(
2793 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
2794 "Latin-1 strings can be loaded from static strings");
2795
2796 masm.loadStringChars(base, temp1_, encoding_);
2797 masm.loadChar(temp1_, temp2_, temp1_, encoding_);
2798
2799 masm.lookupStaticString(temp1_, string_, runtime->staticStrings());
2800
2801 masm.jump(&done);
2802 }
2803 masm.bind(&thinInline);
2804 }
2805 {
2806 newGCString(FallbackKind::InlineString);
2807 masm.jump(&stringAllocated);
2808 }
2809 masm.bind(&fatInline);
2810 {
2811 newGCString(FallbackKind::FatInlineString);
2812 }
2813 masm.bind(&stringAllocated);
2814
2815 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2816
2817 masm.push(string_);
2818 masm.push(base);
2819
2820 MOZ_ASSERT(startIndexAddress.base == FramePointer,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startIndexAddress.base == FramePointer)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(startIndexAddress.base == FramePointer))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer"
" (" "startIndexAddress is still valid after stack pushes" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2821); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") ("
"startIndexAddress is still valid after stack pushes" ")"); do
{ MOZ_CrashSequence(__null, 2821); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
2821 "startIndexAddress is still valid after stack pushes")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startIndexAddress.base == FramePointer)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(startIndexAddress.base == FramePointer))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer"
" (" "startIndexAddress is still valid after stack pushes" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2821); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") ("
"startIndexAddress is still valid after stack pushes" ")"); do
{ MOZ_CrashSequence(__null, 2821); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2822
2823 // Load chars pointer for the new string.
2824 masm.loadInlineStringCharsForStore(string_, string_);
2825
2826 // Load the source characters pointer.
2827 masm.loadStringChars(base, temp2_, encoding_);
2828 masm.load32(startIndexAddress, base);
2829 masm.addToCharPtr(temp2_, base, encoding_);
2830
2831 CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_);
2832
2833 masm.pop(base);
2834 masm.pop(string_);
2835
2836 masm.jump(&done);
2837 }
2838
2839 masm.bind(&notInline);
2840
2841 {
2842 // Make a dependent string.
2843 // Warning: string may be tenured (if the fallback case is hit), so
2844 // stores into it must be post barriered.
2845 newGCString(FallbackKind::NotInlineString);
2846
2847 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2848
2849 masm.loadNonInlineStringChars(base, temp1_, encoding_);
2850 masm.load32(startIndexAddress, temp2_);
2851 masm.addToCharPtr(temp1_, temp2_, encoding_);
2852 masm.storeNonInlineStringChars(temp1_, string_);
2853
2854 EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_,
2855 /* needsPostBarrier = */ true);
2856 }
2857
2858 masm.bind(&done);
2859}
2860
2861void CreateDependentString::generateFallback(MacroAssembler& masm) {
2862 JitSpew(JitSpew_Codegen,
2863 "# Emitting CreateDependentString fallback (encoding=%s)",
2864 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2865
2866 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2867 regsToSave.takeUnchecked(string_);
2868 regsToSave.takeUnchecked(temp2_);
2869
2870 for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) {
2871 masm.bind(&fallbacks_[kind]);
2872
2873 masm.PushRegsInMask(regsToSave);
2874
2875 using Fn = void* (*)(JSContext * cx);
2876 masm.setupUnalignedABICall(string_);
2877 masm.loadJSContext(string_);
2878 masm.passABIArg(string_);
2879 if (kind == FallbackKind::FatInlineString) {
2880 masm.callWithABI<Fn, AllocateFatInlineString>();
2881 } else {
2882 masm.callWithABI<Fn, AllocateDependentString>();
2883 }
2884 masm.storeCallPointerResult(string_);
2885
2886 masm.PopRegsInMask(regsToSave);
2887
2888 masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_);
2889
2890 masm.jump(&joins_[kind]);
2891 }
2892}
2893
2894// Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar,
2895// but RegExpExecMatch also has to load and update .lastIndex for global/sticky
2896// regular expressions.
2897static JitCode* GenerateRegExpMatchStubShared(JSContext* cx,
2898 gc::Heap initialStringHeap,
2899 JitZone::StubKind kind) {
2900 bool isExecMatch = kind == JitZone::StubKind::RegExpExecMatch;
2901 MOZ_ASSERT_IF(!isExecMatch, kind == JitZone::StubKind::RegExpMatcher)do { if (!isExecMatch) { do { static_assert( mozilla::detail::
AssertionConditionType<decltype(kind == JitZone::StubKind::
RegExpMatcher)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(kind == JitZone::StubKind::RegExpMatcher
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"kind == JitZone::StubKind::RegExpMatcher", "./../../../../js/src/jit/CodeGenerator.cpp"
, 2901); AnnotateMozCrashReason("MOZ_ASSERT" "(" "kind == JitZone::StubKind::RegExpMatcher"
")"); do { MOZ_CrashSequence(__null, 2901); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2902
2903 if (isExecMatch) {
2904 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub");
2905 } else {
2906 JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub");
2907 }
2908
2909 // |initialStringHeap| could be stale after a GC.
2910 JS::AutoCheckCannotGC nogc(cx);
2911
2912 Register regexp = RegExpMatcherRegExpReg;
2913 Register input = RegExpMatcherStringReg;
2914 Register lastIndex = RegExpMatcherLastIndexReg;
2915 ValueOperand result = JSReturnOperand;
2916
2917 // We are free to clobber all registers, as LRegExpMatcher is a call
2918 // instruction.
2919 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2920 regs.take(input);
2921 regs.take(regexp);
2922 regs.take(lastIndex);
2923
2924 Register temp1 = regs.takeAny();
2925 Register temp2 = regs.takeAny();
2926 Register temp3 = regs.takeAny();
2927 Register maybeTemp4 = InvalidReg;
2928 if (!regs.empty()) {
2929 // There are not enough registers on x86.
2930 maybeTemp4 = regs.takeAny();
2931 }
2932 Register maybeTemp5 = InvalidReg;
2933 if (!regs.empty()) {
2934 // There are not enough registers on x86.
2935 maybeTemp5 = regs.takeAny();
2936 }
2937
2938 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
2939 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
2940
2941 TempAllocator temp(&cx->tempLifoAlloc());
2942 JitContext jcx(cx);
2943 StackMacroAssembler masm(cx, temp);
2944 AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared");
2945
2946#ifdef JS_USE_LINK_REGISTER
2947 masm.pushReturnAddress();
2948#endif
2949 masm.push(FramePointer);
2950 masm.moveStackPtrTo(FramePointer);
2951
2952 Label notFoundZeroLastIndex;
2953 if (isExecMatch) {
2954 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
2955 }
2956
2957 Label notFound, oolEntry;
2958 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
2959 temp3, initialStringHeap, &notFound, &oolEntry,
2960 kind)) {
2961 return nullptr;
2962 }
2963
2964 // If a regexp has named captures, fall back to the OOL stub, which
2965 // will end up calling CreateRegExpMatchResults.
2966 Register shared = temp2;
2967 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffsetTyped(
2968 RegExpObject::SHARED_SLOT)),
2969 shared, JSVAL_TYPE_PRIVATE_GCTHING);
2970 masm.branchPtr(Assembler::NotEqual,
2971 Address(shared, RegExpShared::offsetOfGroupsTemplate()),
2972 ImmWord(0), &oolEntry);
2973
2974 // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub.
2975 masm.branchTest32(Assembler::NonZero,
2976 Address(shared, RegExpShared::offsetOfFlags()),
2977 Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry);
2978
2979 Address pairCountAddress = RegExpPairCountAddress();
2980
2981 // Construct the result.
2982 Register object = temp1;
2983 {
2984 // In most cases, the array will have just 1-2 elements, so we optimize for
2985 // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots
2986 // because two slots are used for the elements header).
2987
2988 // Load the array length in temp2 and the shape in temp3.
2989 Label allocated;
2990 masm.load32(pairCountAddress, temp2);
2991 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2992 RegExpRealm::offsetOfNormalMatchResultShape();
2993 masm.loadGlobalObjectData(temp3);
2994 masm.loadPtr(Address(temp3, offset), temp3);
2995
2996 auto emitAllocObject = [&](size_t elementCapacity) {
2997 gc::AllocKind kind = GuessArrayGCKind(elementCapacity);
2998 MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==
gc::FinalizeKind::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(&
ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2999); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 2999); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2999 gc::FinalizeKind::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==
gc::FinalizeKind::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(&
ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2999); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 2999); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3000 MOZ_ASSERT(!IsFinalizedKind(kind))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsFinalizedKind(kind))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsFinalizedKind(kind)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!IsFinalizedKind(kind)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3000); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFinalizedKind(kind)" ")"); do { MOZ_CrashSequence
(__null, 3000); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3001
3002#ifdef DEBUG1
3003 // Assert all of the available slots are used for |elementCapacity|
3004 // elements.
3005 size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity;
3006 MOZ_ASSERT(usedSlots == GetGCKindSlots(kind))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(usedSlots == GetGCKindSlots(kind))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(usedSlots == GetGCKindSlots(
kind)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("usedSlots == GetGCKindSlots(kind)", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3006); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)"
")"); do { MOZ_CrashSequence(__null, 3006); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3007#endif
3008
3009 constexpr size_t numUsedDynamicSlots =
3010 RegExpRealm::MatchResultObjectSlotSpan;
3011 constexpr size_t numDynamicSlots =
3012 RegExpRealm::MatchResultObjectNumDynamicSlots;
3013 constexpr size_t arrayLength = 1;
3014 masm.createArrayWithFixedElements(object, temp3, temp2, temp3,
3015 arrayLength, elementCapacity,
3016 numUsedDynamicSlots, numDynamicSlots,
3017 kind, gc::Heap::Default, &oolEntry);
3018 };
3019
3020 Label moreThan2;
3021 masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2);
3022 emitAllocObject(2);
3023 masm.jump(&allocated);
3024
3025 Label moreThan6;
3026 masm.bind(&moreThan2);
3027 masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6);
3028 emitAllocObject(6);
3029 masm.jump(&allocated);
3030
3031 masm.bind(&moreThan6);
3032 static_assert(RegExpObject::MaxPairCount == 14);
3033 emitAllocObject(RegExpObject::MaxPairCount);
3034
3035 masm.bind(&allocated);
3036 }
3037
3038 static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t),
3039 "MatchPair consists of two int32 values representing the start"
3040 "and the end offset of the match");
3041
3042 int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset;
3043
3044 // Incremented by one below for each match pair.
3045 Register matchIndex = temp2;
3046 masm.move32(Imm32(0), matchIndex);
3047
3048 // The element in which to store the result of the current match.
3049 size_t elementsOffset = NativeObject::offsetOfFixedElements();
3050 BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset);
3051
3052 // The current match pair's "start" and "limit" member.
3053 BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight,
3054 pairsVectorStartOffset + MatchPair::offsetOfStart());
3055 BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight,
3056 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3057
3058 Label* depStrFailure = &oolEntry;
3059 Label restoreRegExpAndLastIndex;
3060
3061 Register temp4;
3062 if (maybeTemp4 == InvalidReg) {
3063 depStrFailure = &restoreRegExpAndLastIndex;
3064
3065 // We don't have enough registers for a fourth temporary. Reuse |regexp|
3066 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
3067 masm.push(regexp);
3068 temp4 = regexp;
3069 } else {
3070 temp4 = maybeTemp4;
3071 }
3072
3073 Register temp5;
3074 if (maybeTemp5 == InvalidReg) {
3075 depStrFailure = &restoreRegExpAndLastIndex;
3076
3077 // We don't have enough registers for a fifth temporary. Reuse |lastIndex|
3078 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
3079 masm.push(lastIndex);
3080 temp5 = lastIndex;
3081 } else {
3082 temp5 = maybeTemp5;
3083 }
3084
3085 auto maybeRestoreRegExpAndLastIndex = [&]() {
3086 if (maybeTemp5 == InvalidReg) {
3087 masm.pop(lastIndex);
3088 }
3089 if (maybeTemp4 == InvalidReg) {
3090 masm.pop(regexp);
3091 }
3092 };
3093
3094 // Loop to construct the match strings. There are two different loops,
3095 // depending on whether the input is a Two-Byte or a Latin-1 string.
3096 CreateDependentString depStrs[]{
3097 {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure},
3098 {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure},
3099 };
3100
3101 {
3102 Label isLatin1, done;
3103 masm.branchLatin1String(input, &isLatin1);
3104
3105 for (auto& depStr : depStrs) {
3106 if (depStr.encoding() == CharEncoding::Latin1) {
3107 masm.bind(&isLatin1);
3108 }
3109
3110 Label matchLoop;
3111 masm.bind(&matchLoop);
3112
3113 static_assert(MatchPair::NoMatch == -1,
3114 "MatchPair::start is negative if no match was found");
3115
3116 Label isUndefined, storeDone;
3117 masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0),
3118 &isUndefined);
3119 {
3120 depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()),
3121 input, matchPairStart, matchPairLimit,
3122 initialStringHeap);
3123
3124 // Storing into nursery-allocated results object's elements; no post
3125 // barrier.
3126 masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement);
3127 masm.jump(&storeDone);
3128 }
3129 masm.bind(&isUndefined);
3130 {
3131 masm.storeValue(UndefinedValue(), objectMatchElement);
3132 }
3133 masm.bind(&storeDone);
3134
3135 masm.add32(Imm32(1), matchIndex);
3136 masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex,
3137 &done);
3138 masm.jump(&matchLoop);
3139 }
3140
3141#ifdef DEBUG1
3142 masm.assumeUnreachable("The match string loop doesn't fall through.");
3143#endif
3144
3145 masm.bind(&done);
3146 }
3147
3148 maybeRestoreRegExpAndLastIndex();
3149
3150 // Fill in the rest of the output object.
3151 masm.store32(
3152 matchIndex,
3153 Address(object,
3154 elementsOffset + ObjectElements::offsetOfInitializedLength()));
3155 masm.store32(
3156 matchIndex,
3157 Address(object, elementsOffset + ObjectElements::offsetOfLength()));
3158
3159 Address firstMatchPairStartAddress(
3160 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart());
3161 Address firstMatchPairLimitAddress(
3162 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit());
3163
3164 static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0,
3165 "First slot holds the 'index' property");
3166 static_assert(RegExpRealm::MatchResultObjectInputSlot == 1,
3167 "Second slot holds the 'input' property");
3168
3169 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
3170
3171 masm.load32(firstMatchPairStartAddress, temp3);
3172 masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
3173
3174 // No post barrier needed (address is within nursery object.)
3175 masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
3176
3177 // For the ExecMatch stub, if the regular expression is global or sticky, we
3178 // have to update its .lastIndex slot.
3179 if (isExecMatch) {
3180 MOZ_ASSERT(object != lastIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(object != lastIndex)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(object != lastIndex))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("object != lastIndex"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3180); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "object != lastIndex" ")"); do { MOZ_CrashSequence
(__null, 3180); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3181 Label notGlobalOrSticky;
3182 masm.branchTest32(Assembler::Zero, flagsSlot,
3183 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3184 &notGlobalOrSticky);
3185 masm.load32(firstMatchPairLimitAddress, lastIndex);
3186 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3187 masm.bind(&notGlobalOrSticky);
3188 }
3189
3190 // All done!
3191 masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
3192 masm.pop(FramePointer);
3193 masm.ret();
3194
3195 masm.bind(&notFound);
3196 if (isExecMatch) {
3197 Label notGlobalOrSticky;
3198 masm.branchTest32(Assembler::Zero, flagsSlot,
3199 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3200 &notGlobalOrSticky);
3201 masm.bind(&notFoundZeroLastIndex);
3202 masm.storeValue(Int32Value(0), lastIndexSlot);
3203 masm.bind(&notGlobalOrSticky);
3204 }
3205 masm.moveValue(NullValue(), result);
3206 masm.pop(FramePointer);
3207 masm.ret();
3208
3209 // Fallback paths for CreateDependentString.
3210 for (auto& depStr : depStrs) {
3211 depStr.generateFallback(masm);
3212 }
3213
3214 // Fall-through to the ool entry after restoring the registers.
3215 masm.bind(&restoreRegExpAndLastIndex);
3216 maybeRestoreRegExpAndLastIndex();
3217
3218 // Use an undefined value to signal to the caller that the OOL stub needs to
3219 // be called.
3220 masm.bind(&oolEntry);
3221 masm.moveValue(UndefinedValue(), result);
3222 masm.pop(FramePointer);
3223 masm.ret();
3224
3225 Linker linker(masm);
3226 JitCode* code = linker.newCode(cx, CodeKind::Other);
3227 if (!code) {
3228 return nullptr;
3229 }
3230
3231 const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub";
3232 CollectPerfSpewerJitCodeProfile(code, name);
3233#ifdef MOZ_VTUNE1
3234 vtune::MarkStub(code, name);
3235#endif
3236
3237 return code;
3238}
3239
3240JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) {
3241 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
3242 JitZone::StubKind::RegExpMatcher);
3243}
3244
3245JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) {
3246 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
3247 JitZone::StubKind::RegExpExecMatch);
3248}
3249
3250void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) {
3251 MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3251); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3251); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3252 MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->string()) == RegExpMatcherStringReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->string()) == RegExpMatcherStringReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3252); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { MOZ_CrashSequence(__null, 3252); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3253 MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3253); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg"
")"); do { MOZ_CrashSequence(__null, 3253); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3254 MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToOutValue(lir) == JSReturnOperand", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { MOZ_CrashSequence(__null, 3254); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3255
3256#if defined(JS_NUNBOX32)
3257 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
3258 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
3259 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
3260 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
3261 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type);
3262 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data);
3263#elif defined(JS_PUNBOX641)
3264 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
3265 static_assert(RegExpMatcherStringReg != JSReturnReg);
3266 static_assert(RegExpMatcherLastIndexReg != JSReturnReg);
3267#endif
3268
3269 masm.reserveStack(RegExpReservedStack);
3270
3271 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3272 Register lastIndex = ToRegister(lir->lastIndex());
3273 Register input = ToRegister(lir->string());
3274 Register regexp = ToRegister(lir->regexp());
3275
3276 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3277 regs.take(lastIndex);
3278 regs.take(input);
3279 regs.take(regexp);
3280 Register temp = regs.takeAny();
3281
3282 masm.computeEffectiveAddress(
3283 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3284
3285 pushArg(temp);
3286 pushArg(lastIndex);
3287 pushArg(input);
3288 pushArg(regexp);
3289
3290 // We are not using oolCallVM because we are in a Call, and that live
3291 // registers are already saved by the the register allocator.
3292 using Fn = bool (*)(JSContext*, HandleObject regexp, HandleString input,
3293 int32_t lastIndex, MatchPairs* pairs,
3294 MutableHandleValue output);
3295 callVM<Fn, RegExpMatcherRaw>(lir);
3296
3297 masm.jump(ool.rejoin());
3298 });
3299 addOutOfLineCode(ool, lir->mir());
3300
3301 JitCode* regExpMatcherStub =
3302 snapshot_->getZoneStub(JitZone::StubKind::RegExpMatcher);
3303 masm.call(regExpMatcherStub);
3304 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
3305 masm.bind(ool->rejoin());
3306
3307 masm.freeStack(RegExpReservedStack);
3308}
3309
3310void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) {
3311 MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3311); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3311); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3312 MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->string()) == RegExpMatcherStringReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->string()) == RegExpMatcherStringReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3312); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { MOZ_CrashSequence(__null, 3312); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3313 MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToOutValue(lir) == JSReturnOperand", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { MOZ_CrashSequence(__null, 3313); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3314
3315#if defined(JS_NUNBOX32)
3316 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
3317 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
3318 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
3319 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
3320#elif defined(JS_PUNBOX641)
3321 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
3322 static_assert(RegExpMatcherStringReg != JSReturnReg);
3323#endif
3324
3325 masm.reserveStack(RegExpReservedStack);
3326
3327 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3328 Register input = ToRegister(lir->string());
3329 Register regexp = ToRegister(lir->regexp());
3330
3331 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3332 regs.take(input);
3333 regs.take(regexp);
3334 Register temp = regs.takeAny();
3335
3336 masm.computeEffectiveAddress(
3337 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3338
3339 pushArg(temp);
3340 pushArg(input);
3341 pushArg(regexp);
3342
3343 // We are not using oolCallVM because we are in a Call and live registers
3344 // have already been saved by the register allocator.
3345 using Fn =
3346 bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input,
3347 MatchPairs* pairs, MutableHandleValue output);
3348 callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir);
3349 masm.jump(ool.rejoin());
3350 });
3351 addOutOfLineCode(ool, lir->mir());
3352
3353 JitCode* regExpExecMatchStub =
3354 snapshot_->getZoneStub(JitZone::StubKind::RegExpExecMatch);
3355 masm.call(regExpExecMatchStub);
3356 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
3357
3358 masm.bind(ool->rejoin());
3359 masm.freeStack(RegExpReservedStack);
3360}
3361
3362JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) {
3363 JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub");
3364
3365 Register regexp = RegExpSearcherRegExpReg;
3366 Register input = RegExpSearcherStringReg;
3367 Register lastIndex = RegExpSearcherLastIndexReg;
3368 Register result = ReturnReg;
3369
3370 // We are free to clobber all registers, as LRegExpSearcher is a call
3371 // instruction.
3372 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3373 regs.take(input);
3374 regs.take(regexp);
3375 regs.take(lastIndex);
3376
3377 Register temp1 = regs.takeAny();
3378 Register temp2 = regs.takeAny();
3379 Register temp3 = regs.takeAny();
3380
3381 TempAllocator temp(&cx->tempLifoAlloc());
3382 JitContext jcx(cx);
3383 StackMacroAssembler masm(cx, temp);
3384 AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub");
3385
3386#ifdef JS_USE_LINK_REGISTER
3387 masm.pushReturnAddress();
3388#endif
3389 masm.push(FramePointer);
3390 masm.moveStackPtrTo(FramePointer);
3391
3392#ifdef DEBUG1
3393 // Store sentinel value to cx->regExpSearcherLastLimit.
3394 // See comment in RegExpSearcherImpl.
3395 masm.loadJSContext(temp1);
3396 masm.store32(Imm32(RegExpSearcherLastLimitSentinel),
3397 Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit()));
3398#endif
3399
3400 Label notFound, oolEntry;
3401 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3402 temp3, initialStringHeap, &notFound, &oolEntry,
3403 JitZone::StubKind::RegExpSearcher)) {
3404 return nullptr;
3405 }
3406
3407 int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset;
3408 Address matchPairStart(FramePointer,
3409 pairsVectorStartOffset + MatchPair::offsetOfStart());
3410 Address matchPairLimit(FramePointer,
3411 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3412
3413 // Store match limit to cx->regExpSearcherLastLimit and return the index.
3414 masm.load32(matchPairLimit, result);
3415 masm.loadJSContext(input);
3416 masm.store32(result,
3417 Address(input, JSContext::offsetOfRegExpSearcherLastLimit()));
3418 masm.load32(matchPairStart, result);
3419 masm.pop(FramePointer);
3420 masm.ret();
3421
3422 masm.bind(&notFound);
3423 masm.move32(Imm32(RegExpSearcherResultNotFound), result);
3424 masm.pop(FramePointer);
3425 masm.ret();
3426
3427 masm.bind(&oolEntry);
3428 masm.move32(Imm32(RegExpSearcherResultFailed), result);
3429 masm.pop(FramePointer);
3430 masm.ret();
3431
3432 Linker linker(masm);
3433 JitCode* code = linker.newCode(cx, CodeKind::Other);
3434 if (!code) {
3435 return nullptr;
3436 }
3437
3438 CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub");
3439#ifdef MOZ_VTUNE1
3440 vtune::MarkStub(code, "RegExpSearcherStub");
3441#endif
3442
3443 return code;
3444}
3445
3446void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) {
3447 MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->regexp()) == RegExpSearcherRegExpReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3447); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3447); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3448 MOZ_ASSERT(ToRegister(lir->string()) == RegExpSearcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->string()) == RegExpSearcherStringReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->string()) == RegExpSearcherStringReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->string()) == RegExpSearcherStringReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg"
")"); do { MOZ_CrashSequence(__null, 3448); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3449 MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3449); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg"
")"); do { MOZ_CrashSequence(__null, 3449); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3450 MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->output()) == ReturnReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3450); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { MOZ_CrashSequence(__null, 3450); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3451
3452 static_assert(RegExpSearcherRegExpReg != ReturnReg);
3453 static_assert(RegExpSearcherStringReg != ReturnReg);
3454 static_assert(RegExpSearcherLastIndexReg != ReturnReg);
3455
3456 masm.reserveStack(RegExpReservedStack);
3457
3458 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3459 Register lastIndex = ToRegister(lir->lastIndex());
3460 Register input = ToRegister(lir->string());
3461 Register regexp = ToRegister(lir->regexp());
3462
3463 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3464 regs.take(lastIndex);
3465 regs.take(input);
3466 regs.take(regexp);
3467 Register temp = regs.takeAny();
3468
3469 masm.computeEffectiveAddress(
3470 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3471
3472 pushArg(temp);
3473 pushArg(lastIndex);
3474 pushArg(input);
3475 pushArg(regexp);
3476
3477 // We are not using oolCallVM because we are in a Call, and that live
3478 // registers are already saved by the the register allocator.
3479 using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input,
3480 int32_t lastIndex, MatchPairs* pairs, int32_t* result);
3481 callVM<Fn, RegExpSearcherRaw>(lir);
3482
3483 masm.jump(ool.rejoin());
3484 });
3485 addOutOfLineCode(ool, lir->mir());
3486
3487 JitCode* regExpSearcherStub =
3488 snapshot_->getZoneStub(JitZone::StubKind::RegExpSearcher);
3489 masm.call(regExpSearcherStub);
3490 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed),
3491 ool->entry());
3492 masm.bind(ool->rejoin());
3493
3494 masm.freeStack(RegExpReservedStack);
3495}
3496
3497void CodeGenerator::visitRegExpSearcherLastLimit(
3498 LRegExpSearcherLastLimit* lir) {
3499 Register result = ToRegister(lir->output());
3500 Register scratch = ToRegister(lir->temp0());
3501
3502 masm.loadAndClearRegExpSearcherLastLimit(result, scratch);
3503}
3504
3505JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) {
3506 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub");
3507
3508 Register regexp = RegExpExecTestRegExpReg;
3509 Register input = RegExpExecTestStringReg;
3510 Register result = ReturnReg;
3511
3512 TempAllocator temp(&cx->tempLifoAlloc());
3513 JitContext jcx(cx);
3514 StackMacroAssembler masm(cx, temp);
3515 AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub");
3516
3517#ifdef JS_USE_LINK_REGISTER
3518 masm.pushReturnAddress();
3519#endif
3520 masm.push(FramePointer);
3521 masm.moveStackPtrTo(FramePointer);
3522
3523 // We are free to clobber all registers, as LRegExpExecTest is a call
3524 // instruction.
3525 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3526 regs.take(input);
3527 regs.take(regexp);
3528
3529 // Ensure lastIndex != result.
3530 regs.take(result);
3531 Register lastIndex = regs.takeAny();
3532 regs.add(result);
3533 Register temp1 = regs.takeAny();
3534 Register temp2 = regs.takeAny();
3535 Register temp3 = regs.takeAny();
3536
3537 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
3538 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
3539
3540 // Load lastIndex and skip RegExp execution if needed.
3541 Label notFoundZeroLastIndex;
3542 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
3543
3544 Label notFound, oolEntry;
3545 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3546 temp3, initialStringHeap, &notFound, &oolEntry,
3547 JitZone::StubKind::RegExpExecTest)) {
3548 return nullptr;
3549 }
3550
3551 // Set `result` to true/false to indicate found/not-found, or to
3552 // RegExpExecTestResultFailed if we have to retry in C++. If the regular
3553 // expression is global or sticky, we also have to update its .lastIndex slot.
3554
3555 Label done;
3556 int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset;
3557 Address matchPairLimit(FramePointer,
3558 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3559
3560 masm.move32(Imm32(1), result);
3561 masm.branchTest32(Assembler::Zero, flagsSlot,
3562 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3563 &done);
3564 masm.load32(matchPairLimit, lastIndex);
3565 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3566 masm.jump(&done);
3567
3568 masm.bind(&notFound);
3569 masm.move32(Imm32(0), result);
3570 masm.branchTest32(Assembler::Zero, flagsSlot,
3571 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3572 &done);
3573 masm.storeValue(Int32Value(0), lastIndexSlot);
3574 masm.jump(&done);
3575
3576 masm.bind(&notFoundZeroLastIndex);
3577 masm.move32(Imm32(0), result);
3578 masm.storeValue(Int32Value(0), lastIndexSlot);
3579 masm.jump(&done);
3580
3581 masm.bind(&oolEntry);
3582 masm.move32(Imm32(RegExpExecTestResultFailed), result);
3583
3584 masm.bind(&done);
3585 masm.pop(FramePointer);
3586 masm.ret();
3587
3588 Linker linker(masm);
3589 JitCode* code = linker.newCode(cx, CodeKind::Other);
3590 if (!code) {
3591 return nullptr;
3592 }
3593
3594 CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub");
3595#ifdef MOZ_VTUNE1
3596 vtune::MarkStub(code, "RegExpExecTestStub");
3597#endif
3598
3599 return code;
3600}
3601
3602void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) {
3603 MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->regexp()) == RegExpExecTestRegExpReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3603); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3604 MOZ_ASSERT(ToRegister(lir->string()) == RegExpExecTestStringReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->string()) == RegExpExecTestStringReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->string()) == RegExpExecTestStringReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(lir->string()) == RegExpExecTestStringReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3604); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg"
")"); do { MOZ_CrashSequence(__null, 3604); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3605 MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->output()) == ReturnReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3605); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { MOZ_CrashSequence(__null, 3605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3606
3607 static_assert(RegExpExecTestRegExpReg != ReturnReg);
3608 static_assert(RegExpExecTestStringReg != ReturnReg);
3609
3610 masm.reserveStack(RegExpReservedStack);
3611
3612 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3613 Register input = ToRegister(lir->string());
3614 Register regexp = ToRegister(lir->regexp());
3615
3616 pushArg(input);
3617 pushArg(regexp);
3618
3619 // We are not using oolCallVM because we are in a Call and live registers
3620 // have already been saved by the register allocator.
3621 using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp,
3622 HandleString input, bool* result);
3623 callVM<Fn, RegExpBuiltinExecTestFromJit>(lir);
3624
3625 masm.jump(ool.rejoin());
3626 });
3627 addOutOfLineCode(ool, lir->mir());
3628
3629 JitCode* regExpExecTestStub =
3630 snapshot_->getZoneStub(JitZone::StubKind::RegExpExecTest);
3631 masm.call(regExpExecTestStub);
3632
3633 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed),
3634 ool->entry());
3635
3636 masm.bind(ool->rejoin());
3637
3638 masm.freeStack(RegExpReservedStack);
3639}
3640
3641void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) {
3642 Register regexp = ToRegister(ins->regexp());
3643 Register input = ToRegister(ins->input());
3644 Register output = ToRegister(ins->output());
3645
3646 using Fn =
3647 bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*);
3648 auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>(
3649 ins, ArgList(regexp, input), StoreRegisterTo(output));
3650
3651 // Load RegExpShared in |output|.
3652 Label vmCall;
3653 masm.loadParsedRegExpShared(regexp, output, ool->entry());
3654
3655 // Return true iff pairCount > 1.
3656 Label returnTrue;
3657 masm.branch32(Assembler::Above,
3658 Address(output, RegExpShared::offsetOfPairCount()), Imm32(1),
3659 &returnTrue);
3660 masm.move32(Imm32(0), output);
3661 masm.jump(ool->rejoin());
3662
3663 masm.bind(&returnTrue);
3664 masm.move32(Imm32(1), output);
3665
3666 masm.bind(ool->rejoin());
3667}
3668
3669static void FindFirstDollarIndex(MacroAssembler& masm, Register str,
3670 Register len, Register temp0, Register temp1,
3671 Register output, CharEncoding encoding) {
3672#ifdef DEBUG1
3673 Label ok;
3674 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
3675 masm.assumeUnreachable("Length should be greater than 0.");
3676 masm.bind(&ok);
3677#endif
3678
3679 Register chars = temp0;
3680 masm.loadStringChars(str, chars, encoding);
3681
3682 masm.move32(Imm32(0), output);
3683
3684 Label start, done;
3685 masm.bind(&start);
3686
3687 Register currentChar = temp1;
3688 masm.loadChar(chars, output, currentChar, encoding);
3689 masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done);
3690
3691 masm.add32(Imm32(1), output);
3692 masm.branch32(Assembler::NotEqual, output, len, &start);
3693
3694 masm.move32(Imm32(-1), output);
3695
3696 masm.bind(&done);
3697}
3698
3699void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) {
3700 Register str = ToRegister(ins->str());
3701 Register output = ToRegister(ins->output());
3702 Register temp0 = ToRegister(ins->temp0());
3703 Register temp1 = ToRegister(ins->temp1());
3704 Register len = ToRegister(ins->temp2());
3705
3706 using Fn = bool (*)(JSContext*, JSString*, int32_t*);
3707 OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>(
3708 ins, ArgList(str), StoreRegisterTo(output));
3709
3710 masm.branchIfRope(str, ool->entry());
3711 masm.loadStringLength(str, len);
3712
3713 Label isLatin1, done;
3714 masm.branchLatin1String(str, &isLatin1);
3715 {
3716 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3717 CharEncoding::TwoByte);
3718 masm.jump(&done);
3719 }
3720 masm.bind(&isLatin1);
3721 {
3722 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3723 CharEncoding::Latin1);
3724 }
3725 masm.bind(&done);
3726 masm.bind(ool->rejoin());
3727}
3728
3729void CodeGenerator::visitStringReplace(LStringReplace* lir) {
3730 if (lir->replacement()->isConstant()) {
3731 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
3732 } else {
3733 pushArg(ToRegister(lir->replacement()));
3734 }
3735
3736 if (lir->pattern()->isConstant()) {
3737 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString()));
3738 } else {
3739 pushArg(ToRegister(lir->pattern()));
3740 }
3741
3742 if (lir->string()->isConstant()) {
3743 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
3744 } else {
3745 pushArg(ToRegister(lir->string()));
3746 }
3747
3748 using Fn =
3749 JSString* (*)(JSContext*, HandleString, HandleString, HandleString);
3750 if (lir->mir()->isFlatReplacement()) {
3751 callVM<Fn, StringFlatReplaceString>(lir);
3752 } else {
3753 callVM<Fn, StringReplace>(lir);
3754 }
3755}
3756
3757void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) {
3758 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3759 TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir->lhs()));
3760 TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir->rhs()));
3761 ValueOperand output = ToOutValue(lir);
3762
3763 JSOp jsop = lir->mir()->jsop();
3764
3765 switch (jsop) {
3766 case JSOp::Add:
3767 case JSOp::Sub:
3768 case JSOp::Mul:
3769 case JSOp::Div:
3770 case JSOp::Mod:
3771 case JSOp::Pow:
3772 case JSOp::BitAnd:
3773 case JSOp::BitOr:
3774 case JSOp::BitXor:
3775 case JSOp::Lsh:
3776 case JSOp::Rsh:
3777 case JSOp::Ursh: {
3778 IonBinaryArithIC ic(liveRegs, lhs, rhs, output);
3779 addIC(lir, allocateIC(ic));
3780 return;
3781 }
3782 default:
3783 MOZ_CRASH("Unsupported jsop in MBinaryValueCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryValueCache"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3783); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache" ")"); do
{ MOZ_CrashSequence(__null, 3783); __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
3784 }
3785}
3786
3787void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) {
3788 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3789 TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir->lhs()));
3790 TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir->rhs()));
3791 Register output = ToRegister(lir->output());
3792
3793 JSOp jsop = lir->mir()->jsop();
3794
3795 switch (jsop) {
3796 case JSOp::Lt:
3797 case JSOp::Le:
3798 case JSOp::Gt:
3799 case JSOp::Ge:
3800 case JSOp::Eq:
3801 case JSOp::Ne:
3802 case JSOp::StrictEq:
3803 case JSOp::StrictNe: {
3804 IonCompareIC ic(liveRegs, lhs, rhs, output);
3805 addIC(lir, allocateIC(ic));
3806 return;
3807 }
3808 default:
3809 MOZ_CRASH("Unsupported jsop in MBinaryBoolCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryBoolCache"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3809); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache" ")"); do
{ MOZ_CrashSequence(__null, 3809); __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
3810 }
3811}
3812
3813void CodeGenerator::visitUnaryCache(LUnaryCache* lir) {
3814 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3815 TypedOrValueRegister input = TypedOrValueRegister(ToValue(lir->input()));
3816 ValueOperand output = ToOutValue(lir);
3817
3818 IonUnaryArithIC ic(liveRegs, input, output);
3819 addIC(lir, allocateIC(ic));
3820}
3821
3822void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) {
3823 pushArg(ImmGCPtr(lir->mir()->module()));
3824
3825 using Fn = JSObject* (*)(JSContext*, HandleObject);
3826 callVM<Fn, js::GetOrCreateModuleMetaObject>(lir);
3827}
3828
3829void CodeGenerator::visitDynamicImport(LDynamicImport* lir) {
3830 pushArg(Imm32(uint8_t(lir->mir()->phase())));
3831 pushArg(ToValue(lir->options()));
3832 pushArg(ToValue(lir->specifier()));
3833 pushArg(ImmGCPtr(current->mir()->info().script()));
3834
3835 using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue,
3836 ImportPhase);
3837 callVM<Fn, js::StartDynamicModuleImport>(lir);
3838}
3839
3840void CodeGenerator::visitLambda(LLambda* lir) {
3841 Register envChain = ToRegister(lir->environmentChain());
3842 Register output = ToRegister(lir->output());
3843 Register tempReg = ToRegister(lir->temp0());
3844 gc::Heap heap = lir->mir()->initialHeap();
3845
3846 JSFunction* fun = lir->mir()->templateFunction();
3847 MOZ_ASSERT(fun->isTenured())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun->isTenured())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun->isTenured()))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("fun->isTenured()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3847); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun->isTenured()" ")"); do { MOZ_CrashSequence
(__null, 3847); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3848
3849 using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject, gc::Heap);
3850 OutOfLineCode* ool = oolCallVM<Fn, js::LambdaOptimizedFallback>(
3851 lir, ArgList(ImmGCPtr(fun), envChain, Imm32(uint32_t(heap))),
3852 StoreRegisterTo(output));
3853
3854 TemplateObject templateObject(fun);
3855 masm.createGCObject(output, tempReg, templateObject, heap, ool->entry(),
3856 /* initContents = */ true,
3857 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
3858
3859 masm.storeValue(JSVAL_TYPE_OBJECT, envChain,
3860 Address(output, JSFunction::offsetOfEnvironment()));
3861
3862 // If we specified the tenured heap then we need a post barrier. Otherwise no
3863 // post barrier needed as the output is guaranteed to be allocated in the
3864 // nursery.
3865 if (heap == gc::Heap::Tenured) {
3866 Label skipBarrier;
3867 masm.branchPtrInNurseryChunk(Assembler::NotEqual, envChain, tempReg,
3868 &skipBarrier);
3869 saveVolatile(tempReg);
3870 emitPostWriteBarrier(output);
3871 restoreVolatile(tempReg);
3872 masm.bind(&skipBarrier);
3873 }
3874
3875 masm.bind(ool->rejoin());
3876}
3877
3878void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) {
3879 Register envChain = ToRegister(lir->envChain());
3880 Register prototype = ToRegister(lir->prototype());
3881
3882 pushArg(prototype);
3883 pushArg(envChain);
3884 pushArg(ImmGCPtr(lir->mir()->function()));
3885
3886 using Fn =
3887 JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject);
3888 callVM<Fn, js::FunWithProtoOperation>(lir);
3889}
3890
3891void CodeGenerator::visitSetFunName(LSetFunName* lir) {
3892 pushArg(Imm32(lir->mir()->prefixKind()));
3893 pushArg(ToValue(lir->name()));
3894 pushArg(ToRegister(lir->fun()));
3895
3896 using Fn =
3897 bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
3898 callVM<Fn, js::SetFunctionName>(lir);
3899}
3900
3901void CodeGenerator::visitOsiPoint(LOsiPoint* lir) {
3902 // Note: markOsiPoint ensures enough space exists between the last
3903 // LOsiPoint and this one to patch adjacent call instructions.
3904
3905 MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("masm.framePushed() == frameSize()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 3905); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3906
3907 uint32_t osiCallPointOffset = markOsiPoint(lir);
3908
3909 LSafepoint* safepoint = lir->associatedSafepoint();
3910 MOZ_ASSERT(!safepoint->osiCallPointOffset())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!safepoint->osiCallPointOffset())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!safepoint->osiCallPointOffset
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!safepoint->osiCallPointOffset()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()"
")"); do { MOZ_CrashSequence(__null, 3910); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3911 safepoint->setOsiCallPointOffset(osiCallPointOffset);
3912
3913#ifdef DEBUG1
3914 // There should be no movegroups or other instructions between
3915 // an instruction and its OsiPoint. This is necessary because
3916 // we use the OsiPoint's snapshot from within VM calls.
3917 for (LInstructionReverseIterator iter(current->rbegin(lir));
3918 iter != current->rend(); iter++) {
3919 if (*iter == lir) {
3920 continue;
3921 }
3922 MOZ_ASSERT(!iter->isMoveGroup())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!iter->isMoveGroup())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!iter->isMoveGroup()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!iter->isMoveGroup()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3922); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!iter->isMoveGroup()" ")"); do { MOZ_CrashSequence
(__null, 3922); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3923 MOZ_ASSERT(iter->safepoint() == safepoint)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter->safepoint() == safepoint)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter->safepoint() == safepoint
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"iter->safepoint() == safepoint", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint"
")"); do { MOZ_CrashSequence(__null, 3923); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3924 break;
3925 }
3926#endif
3927
3928#ifdef CHECK_OSIPOINT_REGISTERS1
3929 if (shouldVerifyOsiPointRegs(safepoint)) {
3930 verifyOsiPointRegs(safepoint);
3931 }
3932#endif
3933}
3934
3935void CodeGenerator::visitPhi(LPhi* lir) {
3936 MOZ_CRASH("Unexpected LPhi in CodeGenerator")do { do { } while (false); MOZ_ReportCrash("" "Unexpected LPhi in CodeGenerator"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3936); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator" ")"); do { MOZ_CrashSequence
(__null, 3936); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
3937}
3938
3939void CodeGenerator::visitGoto(LGoto* lir) {
3940 // It would be valid to do simply `jumpToBlock(lir->target()); return;`.
3941 // That shorts out chains of completely empty (apart from the final Goto)
3942 // blocks. However, we try to do a bit better by shorting out chains of
3943 // blocks which are either completely empty or contain only MoveGroups, by
3944 // emitting the MoveGroups at this point. Hence this is a very limited form
3945 // of tail duplication, in which the duplicated tail(s) consist entirely of
3946 // MoveGroups.
3947 //
3948 // Ideally this logic should be in CodeGeneratorShared::jumpToBlock as it
3949 // would cover more use cases. That unfortunately creates a circular
3950 // dependency between the classes CodeGeneratorShared, CodeGenerator{Arch}
3951 // and CodeGenerator, which is not easy to resolve; specifically,
3952 // CodeGeneratorShared would need to call CodeGenerator::visitMoveGroup, but
3953 // CodeGenerator is (indirectly) a child class of CodeGeneratorShared.
3954 //
3955 // See CodeGeneratorShared::jumpToBlock(MBasicBlock*) as reference.
3956
3957 // If we can fall through to the target, don't bother cloning MoveGroups
3958 // because this would turn the fallthrough into an explicit jump.
3959 MBasicBlock* target = lir->target();
3960 if (isNextBlock(target->lir())) {
3961 return;
3962 }
3963
3964 uint32_t numMoveGroupsCloned = 0;
3965 while (true) {
3966 LBlock* targetLBlock = target->lir();
3967 LBlock* nextLBlock = targetLBlock->isMoveGroupsThenGoto();
3968 if (!nextLBlock) {
3969 break;
3970 }
3971 // This block is merely zero-or-more MoveGroups followed by a Goto. Emit
3972 // the MoveGroups and keep following the chain.
3973 auto iter = targetLBlock->begin();
3974 while (true) {
3975 LInstruction* ins = *iter;
3976 if (!ins->isMoveGroup()) {
3977 break;
3978 }
3979 visitMoveGroup(ins->toMoveGroup());
3980 iter++;
3981 numMoveGroupsCloned++;
3982 }
3983 // Ensured by LBlock::isMoveGroupsThenGoto
3984 MOZ_ASSERT((*iter)->isGoto())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((*iter)->isGoto())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((*iter)->isGoto()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("(*iter)->isGoto()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3984); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(*iter)->isGoto()" ")"); do { MOZ_CrashSequence
(__null, 3984); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3985 MOZ_ASSERT((*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((*iter)->toGoto()->getSuccessor(0)->lir() ==
nextLBlock)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!((*iter)->toGoto()->getSuccessor
(0)->lir() == nextLBlock))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3985); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock"
")"); do { MOZ_CrashSequence(__null, 3985); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3986 iter++;
3987 MOZ_RELEASE_ASSERT(iter == targetLBlock->end())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter == targetLBlock->end())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter == targetLBlock->end
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("iter == targetLBlock->end()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 3987); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "iter == targetLBlock->end()"
")"); do { MOZ_CrashSequence(__null, 3987); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3988 target = nextLBlock->mir();
3989 if (numMoveGroupsCloned >= 1) {
3990 // Be very conservative about cloning. Higher numbers give more
3991 // aggressive chasing but seem to sometimes cause a slight cycle count
3992 // regression. In practice, cloning one happens occasionally, cloning of
3993 // two groups happens very rarely, and cloning of more than 2 groups has
3994 // only been seen in artificially constructed test cases.
3995 break;
3996 }
3997 }
3998
3999 // If the above loop exited due to hitting the MoveGroup clone limit, we
4000 // still need to skip past any "trivial" blocks, to avoid asserting in
4001 // `target->lir()->label()` below.
4002 target = skipTrivialBlocks(target);
4003
4004 // No jump necessary if we can fall through to the next block.
4005 if (isNextBlock(target->lir())) {
4006 return;
4007 }
4008
4009 masm.jump(target->lir()->label());
4010}
4011
4012void CodeGenerator::visitTableSwitch(LTableSwitch* ins) {
4013 MTableSwitch* mir = ins->mir();
4014 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
4015
4016 Register intIndex;
4017 if (mir->getOperand(0)->type() != MIRType::Int32) {
4018 intIndex = ToRegister(ins->temp0());
4019
4020 // The input is a double, so try and convert it to an integer.
4021 // If it does not fit in an integer, take the default case.
4022 masm.convertDoubleToInt32(ToFloatRegister(ins->index()), intIndex,
4023 defaultcase, false);
4024 } else {
4025 intIndex = ToRegister(ins->index());
4026 }
4027
4028 emitTableSwitchDispatch(mir, intIndex, ToTempRegisterOrInvalid(ins->temp1()));
4029}
4030
4031void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) {
4032 MTableSwitch* mir = ins->mir();
4033 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
4034
4035 Register index = ToRegister(ins->temp0());
4036 ValueOperand value = ToValue(ins->input());
4037 Register tag = masm.extractTag(value, index);
4038 masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
4039
4040 Label unboxInt, isInt;
4041 masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
4042 {
4043 FloatRegister floatIndex = ToFloatRegister(ins->temp1());
4044 masm.unboxDouble(value, floatIndex);
4045 masm.convertDoubleToInt32(floatIndex, index, defaultcase, false);
4046 masm.jump(&isInt);
4047 }
4048
4049 masm.bind(&unboxInt);
4050 masm.unboxInt32(value, index);
4051
4052 masm.bind(&isInt);
4053
4054 emitTableSwitchDispatch(mir, index, ToTempRegisterOrInvalid(ins->temp2()));
4055}
4056
4057void CodeGenerator::visitParameter(LParameter* lir) {}
4058
4059void CodeGenerator::visitCallee(LCallee* lir) {
4060 Register callee = ToRegister(lir->output());
4061 Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken());
4062
4063 masm.loadFunctionFromCalleeToken(ptr, callee);
4064}
4065
4066void CodeGenerator::visitIsConstructing(LIsConstructing* lir) {
4067 Register output = ToRegister(lir->output());
4068 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
4069 masm.loadPtr(calleeToken, output);
4070
4071 // We must be inside a function.
4072 MOZ_ASSERT(current->mir()->info().script()->function())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(current->mir()->info().script()->function()
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(current->mir()->info().script()->function()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"current->mir()->info().script()->function()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4072); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()"
")"); do { MOZ_CrashSequence(__null, 4072); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4073
4074 // The low bit indicates whether this call is constructing, just clear the
4075 // other bits.
4076 static_assert(CalleeToken_Function == 0x0,
4077 "CalleeTokenTag value should match");
4078 static_assert(CalleeToken_FunctionConstructing == 0x1,
4079 "CalleeTokenTag value should match");
4080 masm.andPtr(Imm32(0x1), output);
4081}
4082
4083void CodeGenerator::visitReturn(LReturn* lir) {
4084#if defined(JS_NUNBOX32)
4085 DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX);
4086 DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX);
4087 MOZ_ASSERT(ToRegister(type) == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(type) == JSReturnReg_Type)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ToRegister(type) == JSReturnReg_Type
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(type) == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4087); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 4087); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4088 MOZ_ASSERT(ToRegister(payload) == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(payload) == JSReturnReg_Data)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(payload) == JSReturnReg_Data))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("ToRegister(payload) == JSReturnReg_Data"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4088); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 4088); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4089#elif defined(JS_PUNBOX641)
4090 DebugOnly<LAllocation*> result = lir->getOperand(0);
4091 MOZ_ASSERT(ToRegister(result) == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(result) == JSReturnReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ToRegister(result) == JSReturnReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(result) == JSReturnReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 4091); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4092#endif
4093 // Don't emit a jump to the return label if this is the last block, as
4094 // it'll fall through to the epilogue.
4095 if (current->mir() != *gen->graph().poBegin()) {
4096 masm.jump(&returnLabel_);
4097 }
4098}
4099
4100void CodeGenerator::visitOsrEntry(LOsrEntry* lir) {
4101 Register temp = ToRegister(lir->temp());
4102
4103 // Remember the OSR entry offset into the code buffer.
4104 masm.flushBuffer();
4105 setOsrEntryOffset(masm.size());
4106
4107 // Allocate the full frame for this function
4108 // Note we have a new entry here. So we reset MacroAssembler::framePushed()
4109 // to 0, before reserving the stack.
4110 MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("masm.framePushed() == frameSize()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4110); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 4110); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4111 masm.setFramePushed(0);
4112
4113 // The Baseline code ensured both the frame pointer and stack pointer point to
4114 // the JitFrameLayout on the stack.
4115
4116 // If profiling, save the current frame pointer to a per-thread global field.
4117 if (isProfilerInstrumentationEnabled()) {
4118 masm.profilerEnterFrame(FramePointer, temp);
4119 }
4120
4121 masm.reserveStack(frameSize());
4122 MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("masm.framePushed() == frameSize()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4122); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 4122); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4123
4124 // Ensure that the Ion frames is properly aligned.
4125 masm.assertStackAlignment(JitStackAlignment, 0);
4126}
4127
4128void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) {
4129 const LAllocation* frame = lir->entry();
4130 const LDefinition* object = lir->output();
4131
4132 const ptrdiff_t frameOffset =
4133 BaselineFrame::reverseOffsetOfEnvironmentChain();
4134
4135 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
4136}
4137
4138void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) {
4139 const LAllocation* frame = lir->entry();
4140 const LDefinition* object = lir->output();
4141
4142 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj();
4143
4144 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
4145}
4146
4147void CodeGenerator::visitOsrValue(LOsrValue* value) {
4148 const LAllocation* frame = value->entry();
4149 const ValueOperand out = ToOutValue(value);
4150
4151 const ptrdiff_t frameOffset = value->mir()->frameOffset();
4152
4153 masm.loadValue(Address(ToRegister(frame), frameOffset), out);
4154}
4155
4156void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) {
4157 const LAllocation* frame = lir->entry();
4158 const ValueOperand out = ToOutValue(lir);
4159
4160 Address flags =
4161 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags());
4162 Address retval =
4163 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue());
4164
4165 masm.moveValue(UndefinedValue(), out);
4166
4167 Label done;
4168 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL),
4169 &done);
4170 masm.loadValue(retval, out);
4171 masm.bind(&done);
4172}
4173
4174void CodeGenerator::visitStackArgT(LStackArgT* lir) {
4175 const LAllocation* arg = lir->arg();
4176 MIRType argType = lir->type();
4177 uint32_t argslot = lir->argslot();
4178 MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argslot - 1u < graph.argumentSlotCount())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4178); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { MOZ_CrashSequence(__null, 4178); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4179
4180 Address dest = AddressOfPassedArg(argslot);
4181
4182 if (arg->isFloatReg()) {
4183 masm.boxDouble(ToFloatRegister(arg), dest);
4184 } else if (arg->isGeneralReg()) {
4185 masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
4186 } else {
4187 masm.storeValue(arg->toConstant()->toJSValue(), dest);
4188 }
4189}
4190
4191void CodeGenerator::visitStackArgV(LStackArgV* lir) {
4192 ValueOperand val = ToValue(lir->value());
4193 uint32_t argslot = lir->argslot();
4194 MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argslot - 1u < graph.argumentSlotCount())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4194); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { MOZ_CrashSequence(__null, 4194); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4195
4196 masm.storeValue(val, AddressOfPassedArg(argslot));
4197}
4198
4199void CodeGenerator::visitMoveGroup(LMoveGroup* group) {
4200 if (!group->numMoves()) {
4201 return;
4202 }
4203
4204 MoveResolver& resolver = masm.moveResolver();
4205
4206 for (size_t i = 0; i < group->numMoves(); i++) {
4207 const LMove& move = group->getMove(i);
4208
4209 LAllocation from = move.from();
4210 LAllocation to = move.to();
4211 LDefinition::Type type = move.type();
4212
4213 // No bogus moves.
4214 MOZ_ASSERT(from != to)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(from != to)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(from != to))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("from != to", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4214); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to"
")"); do { MOZ_CrashSequence(__null, 4214); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4215 MOZ_ASSERT(!from.isConstant())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!from.isConstant())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!from.isConstant()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!from.isConstant()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4215); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!from.isConstant()" ")"); do { MOZ_CrashSequence
(__null, 4215); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4216 MoveOp::Type moveType;
4217 switch (type) {
4218 case LDefinition::OBJECT:
4219 case LDefinition::SLOTS:
4220 case LDefinition::WASM_ANYREF:
4221 case LDefinition::WASM_STRUCT_DATA:
4222 case LDefinition::WASM_ARRAY_DATA:
4223#ifdef JS_NUNBOX32
4224 case LDefinition::TYPE:
4225 case LDefinition::PAYLOAD:
4226#else
4227 case LDefinition::BOX:
4228#endif
4229 case LDefinition::GENERAL:
4230 case LDefinition::STACKRESULTS:
4231 moveType = MoveOp::GENERAL;
4232 break;
4233 case LDefinition::INT32:
4234 moveType = MoveOp::INT32;
4235 break;
4236 case LDefinition::FLOAT32:
4237 moveType = MoveOp::FLOAT32;
4238 break;
4239 case LDefinition::DOUBLE:
4240 moveType = MoveOp::DOUBLE;
4241 break;
4242 case LDefinition::SIMD128:
4243 moveType = MoveOp::SIMD128;
4244 break;
4245 default:
4246 MOZ_CRASH("Unexpected move type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected move type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4246); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected move type" ")"); do { MOZ_CrashSequence
(__null, 4246); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
4247 }
4248
4249 masm.propagateOOM(
4250 resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType));
4251 }
4252
4253 masm.propagateOOM(resolver.resolve());
4254 if (masm.oom()) {
4255 return;
4256 }
4257
4258 MoveEmitter emitter(masm);
4259
4260#ifdef JS_CODEGEN_X86
4261 if (group->maybeScratchRegister().isGeneralReg()) {
4262 emitter.setScratchRegister(
4263 group->maybeScratchRegister().toGeneralReg()->reg());
4264 } else {
4265 resolver.sortMemoryToMemoryMoves();
4266 }
4267#endif
4268
4269 emitter.emit(resolver);
4270 emitter.finish();
4271}
4272
4273void CodeGenerator::visitInteger(LInteger* lir) {
4274 masm.move32(Imm32(lir->i32()), ToRegister(lir->output()));
4275}
4276
4277void CodeGenerator::visitInteger64(LInteger64* lir) {
4278 masm.move64(Imm64(lir->i64()), ToOutRegister64(lir));
4279}
4280
4281void CodeGenerator::visitPointer(LPointer* lir) {
4282 masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
4283}
4284
4285void CodeGenerator::visitDouble(LDouble* ins) {
4286 masm.loadConstantDouble(ins->value(), ToFloatRegister(ins->output()));
4287}
4288
4289void CodeGenerator::visitFloat32(LFloat32* ins) {
4290 masm.loadConstantFloat32(ins->value(), ToFloatRegister(ins->output()));
4291}
4292
4293void CodeGenerator::visitValue(LValue* value) {
4294 ValueOperand result = ToOutValue(value);
4295 masm.moveValue(value->value(), result);
4296}
4297
4298void CodeGenerator::visitNurseryObject(LNurseryObject* lir) {
4299 Register output = ToRegister(lir->output());
4300 uint32_t nurseryIndex = lir->mir()->nurseryObjectIndex();
4301
4302 // Load a pointer to the entry in IonScript's nursery objects list.
4303 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output);
4304 masm.propagateOOM(nurseryObjectLabels_.emplaceBack(label, nurseryIndex));
4305
4306 // Load the JSObject*.
4307 masm.loadPtr(Address(output, 0), output);
4308}
4309
4310void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) {
4311 // No-op.
4312}
4313
4314void CodeGenerator::visitDebugEnterGCUnsafeRegion(
4315 LDebugEnterGCUnsafeRegion* lir) {
4316 Register temp = ToRegister(lir->temp0());
4317
4318 masm.loadJSContext(temp);
4319
4320 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4321 masm.add32(Imm32(1), inUnsafeRegion);
4322
4323 Label ok;
4324 masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok);
4325 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4326 masm.bind(&ok);
4327}
4328
4329void CodeGenerator::visitDebugLeaveGCUnsafeRegion(
4330 LDebugLeaveGCUnsafeRegion* lir) {
4331 Register temp = ToRegister(lir->temp0());
4332
4333 masm.loadJSContext(temp);
4334
4335 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4336 masm.add32(Imm32(-1), inUnsafeRegion);
4337
4338 Label ok;
4339 masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok);
4340 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4341 masm.bind(&ok);
4342}
4343
4344void CodeGenerator::visitSlots(LSlots* lir) {
4345 Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots());
4346 masm.loadPtr(slots, ToRegister(lir->output()));
4347}
4348
4349void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) {
4350 ValueOperand dest = ToOutValue(lir);
4351 Register base = ToRegister(lir->input());
4352 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4353
4354 masm.loadValue(Address(base, offset), dest);
4355}
4356
4357void CodeGenerator::visitLoadDynamicSlotFromOffset(
4358 LLoadDynamicSlotFromOffset* lir) {
4359 ValueOperand dest = ToOutValue(lir);
4360 Register slots = ToRegister(lir->slots());
4361 Register offset = ToRegister(lir->offset());
4362
4363 // slots[offset]
4364 masm.loadValue(BaseIndex(slots, offset, TimesOne), dest);
4365}
4366
4367static ConstantOrRegister ToConstantOrRegister(const LAllocation* value,
4368 MIRType valueType) {
4369 if (value->isConstant()) {
4370 return ConstantOrRegister(value->toConstant()->toJSValue());
4371 }
4372 return TypedOrValueRegister(valueType, ToAnyRegister(value));
4373}
4374
4375void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) {
4376 Register base = ToRegister(lir->slots());
4377 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4378 Address dest(base, offset);
4379
4380 if (lir->mir()->needsBarrier()) {
4381 emitPreBarrier(dest);
4382 }
4383
4384 MIRType valueType = lir->mir()->value()->type();
4385 ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType);
4386 masm.storeUnboxedValue(value, valueType, dest);
4387}
4388
4389void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) {
4390 Register base = ToRegister(lir->slots());
4391 int32_t offset = lir->mir()->slot() * sizeof(Value);
4392
4393 ValueOperand value = ToValue(lir->value());
4394
4395 if (lir->mir()->needsBarrier()) {
4396 emitPreBarrier(Address(base, offset));
4397 }
4398
4399 masm.storeValue(value, Address(base, offset));
4400}
4401
4402void CodeGenerator::visitStoreDynamicSlotFromOffsetV(
4403 LStoreDynamicSlotFromOffsetV* lir) {
4404 Register slots = ToRegister(lir->slots());
4405 Register offset = ToRegister(lir->offset());
4406 ValueOperand value = ToValue(lir->value());
4407 Register temp = ToRegister(lir->temp0());
4408
4409 BaseIndex baseIndex(slots, offset, TimesOne);
4410 masm.computeEffectiveAddress(baseIndex, temp);
4411
4412 Address address(temp, 0);
4413
4414 emitPreBarrier(address);
4415
4416 // obj->slots[offset]
4417 masm.storeValue(value, address);
4418}
4419
4420void CodeGenerator::visitStoreDynamicSlotFromOffsetT(
4421 LStoreDynamicSlotFromOffsetT* lir) {
4422 Register slots = ToRegister(lir->slots());
4423 Register offset = ToRegister(lir->offset());
4424 const LAllocation* value = lir->value();
4425 MIRType valueType = lir->mir()->value()->type();
4426 Register temp = ToRegister(lir->temp0());
4427
4428 BaseIndex baseIndex(slots, offset, TimesOne);
4429 masm.computeEffectiveAddress(baseIndex, temp);
4430
4431 Address address(temp, 0);
4432
4433 emitPreBarrier(address);
4434
4435 // obj->slots[offset]
4436 ConstantOrRegister nvalue =
4437 value->isConstant()
4438 ? ConstantOrRegister(value->toConstant()->toJSValue())
4439 : TypedOrValueRegister(valueType, ToAnyRegister(value));
4440 masm.storeConstantOrRegister(nvalue, address);
4441}
4442
4443void CodeGenerator::visitElements(LElements* lir) {
4444 Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
4445 masm.loadPtr(elements, ToRegister(lir->output()));
4446}
4447
4448void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) {
4449 Address environment(ToRegister(lir->function()),
4450 JSFunction::offsetOfEnvironment());
4451 masm.unboxObject(environment, ToRegister(lir->output()));
4452}
4453
4454void CodeGenerator::visitHomeObject(LHomeObject* lir) {
4455 Register func = ToRegister(lir->function());
4456 Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
4457
4458 masm.assertFunctionIsExtended(func);
4459#ifdef DEBUG1
4460 Label isObject;
4461 masm.branchTestObject(Assembler::Equal, homeObject, &isObject);
4462 masm.assumeUnreachable("[[HomeObject]] must be Object");
4463 masm.bind(&isObject);
4464#endif
4465
4466 masm.unboxObject(homeObject, ToRegister(lir->output()));
4467}
4468
4469void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) {
4470 Register homeObject = ToRegister(lir->homeObject());
4471 ValueOperand output = ToOutValue(lir);
4472 Register temp = output.scratchReg();
4473
4474 masm.loadObjProto(homeObject, temp);
4475
4476#ifdef DEBUG1
4477 // We won't encounter a lazy proto, because the prototype is guaranteed to
4478 // either be a JSFunction or a PlainObject, and only proxy objects can have a
4479 // lazy proto.
4480 MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4480); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 4480); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4481
4482 Label proxyCheckDone;
4483 masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone);
4484 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase");
4485 masm.bind(&proxyCheckDone);
4486#endif
4487
4488 Label nullProto, done;
4489 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
4490
4491 // Box prototype and return
4492 masm.tagValue(JSVAL_TYPE_OBJECT, temp, output);
4493 masm.jump(&done);
4494
4495 masm.bind(&nullProto);
4496 masm.moveValue(NullValue(), output);
4497
4498 masm.bind(&done);
4499}
4500
4501template <class T>
4502static T* ToConstantObject(MDefinition* def) {
4503 MOZ_ASSERT(def->isConstant())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(def->isConstant())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(def->isConstant()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("def->isConstant()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4503); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "def->isConstant()" ")"); do { MOZ_CrashSequence
(__null, 4503); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4504 return &def->toConstant()->toObject().as<T>();
4505}
4506
4507void CodeGenerator::visitNewLexicalEnvironmentObject(
4508 LNewLexicalEnvironmentObject* lir) {
4509 Register output = ToRegister(lir->output());
4510 Register temp = ToRegister(lir->temp0());
4511
4512 auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>(
4513 lir->mir()->templateObj());
4514 auto* scope = &templateObj->scope();
4515 gc::Heap initialHeap = gc::Heap::Default;
4516
4517 using Fn =
4518 BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>);
4519 auto* ool =
4520 oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>(
4521 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4522
4523 TemplateObject templateObject(templateObj);
4524 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4525
4526 masm.bind(ool->rejoin());
4527}
4528
4529void CodeGenerator::visitNewClassBodyEnvironmentObject(
4530 LNewClassBodyEnvironmentObject* lir) {
4531 Register output = ToRegister(lir->output());
4532 Register temp = ToRegister(lir->temp0());
4533
4534 auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>(
4535 lir->mir()->templateObj());
4536 auto* scope = &templateObj->scope();
4537 gc::Heap initialHeap = gc::Heap::Default;
4538
4539 using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*,
4540 Handle<ClassBodyScope*>);
4541 auto* ool =
4542 oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>(
4543 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4544
4545 TemplateObject templateObject(templateObj);
4546 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4547
4548 masm.bind(ool->rejoin());
4549}
4550
4551void CodeGenerator::visitNewVarEnvironmentObject(
4552 LNewVarEnvironmentObject* lir) {
4553 Register output = ToRegister(lir->output());
4554 Register temp = ToRegister(lir->temp0());
4555
4556 auto* templateObj =
4557 ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj());
4558 auto* scope = &templateObj->scope().as<VarScope>();
4559 gc::Heap initialHeap = gc::Heap::Default;
4560
4561 using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>);
4562 auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>(
4563 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4564
4565 TemplateObject templateObject(templateObj);
4566 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4567
4568 masm.bind(ool->rejoin());
4569}
4570
4571void CodeGenerator::visitGuardShape(LGuardShape* guard) {
4572 Register obj = ToRegister(guard->object());
4573 Register temp = ToTempRegisterOrInvalid(guard->temp0());
4574 Label bail;
4575 masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp,
4576 obj, &bail);
4577 bailoutFrom(&bail, guard->snapshot());
4578}
4579
4580void CodeGenerator::visitGuardFuse(LGuardFuse* guard) {
4581 auto fuseIndex = guard->mir()->fuseIndex();
4582
4583 Label bail;
4584
4585 // Bake specific fuse address for Ion code, because we won't share this code
4586 // across realms.
4587 GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex);
4588 masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(fuse->fuseRef()),
4589 ImmWord(0), &bail);
4590
4591 bailoutFrom(&bail, guard->snapshot());
4592}
4593
4594void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) {
4595 Register obj = ToRegister(guard->object());
4596 Register shapeList = ToRegister(guard->shapeList());
4597 Register temp = ToRegister(guard->temp0());
4598 Register temp2 = ToRegister(guard->temp1());
4599 Register temp3 = ToRegister(guard->temp2());
4600 Register spectre = ToTempRegisterOrInvalid(guard->temp3());
4601
4602 Label bail;
4603 masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp);
4604 masm.branchTestObjShapeList(obj, temp, temp2, temp3, spectre, &bail);
4605 bailoutFrom(&bail, guard->snapshot());
4606}
4607
4608void CodeGenerator::visitGuardShapeList(LGuardShapeList* guard) {
4609 Register obj = ToRegister(guard->object());
4610 Register temp = ToRegister(guard->temp0());
4611 Register spectre = ToTempRegisterOrInvalid(guard->temp1());
4612
4613 Label done, bail;
4614 masm.loadObjShapeUnsafe(obj, temp);
4615
4616 // Count the number of branches to emit.
4617 const auto& shapes = guard->mir()->shapeList()->shapes();
4618 size_t branchesLeft = std::count_if(shapes.begin(), shapes.end(),
4619 [](Shape* s) { return s != nullptr; });
4620 MOZ_RELEASE_ASSERT(branchesLeft > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(branchesLeft > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(branchesLeft > 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("branchesLeft > 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4620); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "branchesLeft > 0" ")"); do { MOZ_CrashSequence
(__null, 4620); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4621
4622 for (Shape* shape : shapes) {
4623 if (!shape) {
4624 continue;
4625 }
4626 if (branchesLeft > 1) {
4627 masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done);
4628 if (spectre != InvalidReg) {
4629 masm.spectreMovePtr(Assembler::Equal, spectre, obj);
4630 }
4631 } else {
4632 // This is the last branch so invert the condition and jump to |bail|.
4633 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &bail);
4634 if (spectre != InvalidReg) {
4635 masm.spectreMovePtr(Assembler::NotEqual, spectre, obj);
4636 }
4637 }
4638 branchesLeft--;
4639 }
4640 MOZ_ASSERT(branchesLeft == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(branchesLeft == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(branchesLeft == 0))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("branchesLeft == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4640); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "branchesLeft == 0" ")"); do { MOZ_CrashSequence
(__null, 4640); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4641
4642 masm.bind(&done);
4643 bailoutFrom(&bail, guard->snapshot());
4644}
4645
4646void CodeGenerator::visitGuardShapeListToOffset(
4647 LGuardShapeListToOffset* guard) {
4648 Register obj = ToRegister(guard->object());
4649 Register temp = ToRegister(guard->temp0());
4650 Register spectre = ToTempRegisterOrInvalid(guard->temp1());
4651 Register offset = ToRegister(guard->output());
4652
4653 Label done, bail;
4654 masm.loadObjShapeUnsafe(obj, temp);
4655
4656 // Count the number of branches to emit.
4657 const auto& shapes = guard->mir()->shapeList()->shapes();
4658 const auto& offsets = guard->mir()->shapeList()->offsets();
4659 size_t branchesLeft = std::count_if(shapes.begin(), shapes.end(),
4660 [](Shape* s) { return s != nullptr; });
4661 MOZ_RELEASE_ASSERT(branchesLeft > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(branchesLeft > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(branchesLeft > 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("branchesLeft > 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4661); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "branchesLeft > 0" ")"); do { MOZ_CrashSequence
(__null, 4661); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4662
4663 size_t index = 0;
4664 for (Shape* shape : shapes) {
4665 if (!shape) {
4666 index++;
4667 continue;
4668 }
4669
4670 if (branchesLeft > 1) {
4671 Label next;
4672 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &next);
4673 if (spectre != InvalidReg) {
4674 masm.spectreMovePtr(Assembler::NotEqual, spectre, obj);
4675 }
4676 masm.move32(Imm32(offsets[index]), offset);
4677 masm.jump(&done);
4678 masm.bind(&next);
4679 } else {
4680 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &bail);
4681 if (spectre != InvalidReg) {
4682 masm.spectreMovePtr(Assembler::NotEqual, spectre, obj);
4683 }
4684 masm.move32(Imm32(offsets[index]), offset);
4685 }
4686
4687 branchesLeft--;
4688 index++;
4689 }
4690 MOZ_ASSERT(branchesLeft == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(branchesLeft == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(branchesLeft == 0))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("branchesLeft == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4690); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "branchesLeft == 0" ")"); do { MOZ_CrashSequence
(__null, 4690); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4691
4692 masm.bind(&done);
4693 bailoutFrom(&bail, guard->snapshot());
4694}
4695
4696void CodeGenerator::visitGuardMultipleShapesToOffset(
4697 LGuardMultipleShapesToOffset* guard) {
4698 Register obj = ToRegister(guard->object());
4699 Register shapeList = ToRegister(guard->shapeList());
4700 Register temp = ToRegister(guard->temp0());
4701 Register temp1 = ToRegister(guard->temp1());
4702 Register temp2 = ToRegister(guard->temp2());
4703 Register offset = ToRegister(guard->output());
4704 Register spectre = JitOptions.spectreObjectMitigations ? offset : InvalidReg;
4705
4706 Label bail;
4707 masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp);
4708 masm.branchTestObjShapeListSetOffset(obj, temp, offset, temp1, temp2, spectre,
4709 &bail);
4710 bailoutFrom(&bail, guard->snapshot());
4711}
4712
4713void CodeGenerator::visitGuardProto(LGuardProto* guard) {
4714 Register obj = ToRegister(guard->object());
4715 Register expected = ToRegister(guard->expected());
4716 Register temp = ToRegister(guard->temp0());
4717
4718 masm.loadObjProto(obj, temp);
4719
4720 Label bail;
4721 masm.branchPtr(Assembler::NotEqual, temp, expected, &bail);
4722 bailoutFrom(&bail, guard->snapshot());
4723}
4724
4725void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) {
4726 Register obj = ToRegister(guard->object());
4727 Register temp = ToRegister(guard->temp0());
4728
4729 masm.loadObjProto(obj, temp);
4730
4731 Label bail;
4732 masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail);
4733 bailoutFrom(&bail, guard->snapshot());
4734}
4735
4736void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) {
4737 Register obj = ToRegister(guard->object());
4738 Register temp = ToRegister(guard->temp0());
4739
4740 Label bail;
4741 masm.branchIfNonNativeObj(obj, temp, &bail);
4742 bailoutFrom(&bail, guard->snapshot());
4743}
4744
4745void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) {
4746 Register temp = ToRegister(guard->temp0());
4747 Label bail;
4748
4749 masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp);
4750 masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()),
4751 &bail);
4752 bailoutFrom(&bail, guard->snapshot());
4753}
4754
4755void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) {
4756 Register obj = ToRegister(guard->object());
4757 Register temp = ToRegister(guard->temp0());
4758
4759 Label bail;
4760 masm.branchTestObjectIsProxy(false, obj, temp, &bail);
4761 bailoutFrom(&bail, guard->snapshot());
4762}
4763
4764void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) {
4765 Register obj = ToRegister(guard->object());
4766 Register temp = ToRegister(guard->temp0());
4767
4768 Label bail;
4769 masm.branchTestObjectIsProxy(true, obj, temp, &bail);
4770 bailoutFrom(&bail, guard->snapshot());
4771}
4772
4773void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) {
4774 Register proxy = ToRegister(guard->proxy());
4775 Register temp = ToRegister(guard->temp0());
4776
4777 Label bail;
4778 masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp,
4779 GetDOMProxyHandlerFamily(), &bail);
4780 bailoutFrom(&bail, guard->snapshot());
4781}
4782
4783void CodeGenerator::visitProxyGet(LProxyGet* lir) {
4784 Register proxy = ToRegister(lir->proxy());
4785 Register temp = ToRegister(lir->temp0());
4786
4787 pushArg(lir->mir()->id(), temp);
4788 pushArg(proxy);
4789
4790 using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue);
4791 callVM<Fn, ProxyGetProperty>(lir);
4792}
4793
4794void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) {
4795 Register proxy = ToRegister(lir->proxy());
4796 ValueOperand idVal = ToValue(lir->idVal());
4797
4798 pushArg(idVal);
4799 pushArg(proxy);
4800
4801 using Fn =
4802 bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
4803 callVM<Fn, ProxyGetPropertyByValue>(lir);
4804}
4805
4806void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) {
4807 Register proxy = ToRegister(lir->proxy());
4808 ValueOperand idVal = ToValue(lir->id());
4809
4810 pushArg(idVal);
4811 pushArg(proxy);
4812
4813 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*);
4814 if (lir->mir()->hasOwn()) {
4815 callVM<Fn, ProxyHasOwn>(lir);
4816 } else {
4817 callVM<Fn, ProxyHas>(lir);
4818 }
4819}
4820
4821void CodeGenerator::visitProxySet(LProxySet* lir) {
4822 Register proxy = ToRegister(lir->proxy());
4823 ValueOperand rhs = ToValue(lir->rhs());
4824 Register temp = ToRegister(lir->temp0());
4825
4826 pushArg(Imm32(lir->mir()->strict()));
4827 pushArg(rhs);
4828 pushArg(lir->mir()->id(), temp);
4829 pushArg(proxy);
4830
4831 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4832 callVM<Fn, ProxySetProperty>(lir);
4833}
4834
4835void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) {
4836 Register proxy = ToRegister(lir->proxy());
4837 ValueOperand idVal = ToValue(lir->idVal());
4838 ValueOperand rhs = ToValue(lir->rhs());
4839
4840 pushArg(Imm32(lir->mir()->strict()));
4841 pushArg(rhs);
4842 pushArg(idVal);
4843 pushArg(proxy);
4844
4845 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
4846 callVM<Fn, ProxySetPropertyByValue>(lir);
4847}
4848
4849void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) {
4850 Register obj = ToRegister(lir->obj());
4851 ValueOperand rhs = ToValue(lir->rhs());
4852
4853 pushArg(Imm32(lir->mir()->strict()));
4854 pushArg(rhs);
4855 pushArg(obj);
4856
4857 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool);
4858 callVM<Fn, jit::SetArrayLength>(lir);
4859}
4860
4861void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) {
4862 Register obj = ToRegister(lir->object());
4863 Register temp0 = ToRegister(lir->temp0());
4864 Register temp1 = ToRegister(lir->temp1());
4865 Register temp2 = ToRegister(lir->temp2());
4866 Register temp3 = ToRegister(lir->temp3());
4867 ValueOperand output = ToOutValue(lir);
4868
4869 Label done;
4870 PropertyKey id = lir->mir()->name();
4871 masm.movePropertyKey(id, temp0);
4872 masm.move32(Imm32(HashPropertyKeyThreadSafe(id)), temp1);
4873
4874 MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4874); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 4874); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4875 MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4875); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 4875); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4876 MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4876); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 4876); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4877 MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4877); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 4877); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4878#if defined(JS_NUNBOX32)
4879 MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4879); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 4879); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4880 MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadReg() == JSReturnReg_Data)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4880); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 4880); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4881#else
4882 MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4882); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 4882); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4883#endif
4884 TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub();
4885 masm.call(megamorphicLoadStub);
4886 masm.branchPtr(Assembler::Equal, temp2,
4887 Imm32(JitRuntime::MegamorphicLoadStubCacheHit), &done);
4888
4889 Label bail;
4890 masm.branchIfNonNativeObj(obj, temp0, &bail);
4891
4892 masm.Push(UndefinedValue());
4893 masm.moveStackPtrTo(temp3);
4894
4895 using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id,
4896 MegamorphicCache::Entry* cacheEntry, Value* vp);
4897 masm.setupAlignedABICall();
4898 masm.loadJSContext(temp0);
4899 masm.passABIArg(temp0);
4900 masm.passABIArg(obj);
4901 masm.movePropertyKey(lir->mir()->name(), temp1);
4902 masm.passABIArg(temp1);
4903 masm.passABIArg(temp2);
4904 masm.passABIArg(temp3);
4905
4906 masm.callWithABI<Fn, GetNativeDataPropertyPure>();
4907
4908 MOZ_ASSERT(!output.aliases(ReturnReg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!output.aliases(ReturnReg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!output.aliases(ReturnReg)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(ReturnReg)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4908); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)" ")"); do { MOZ_CrashSequence
(__null, 4908); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4909 masm.Pop(output);
4910
4911 masm.branchIfFalseBool(ReturnReg, &bail);
4912 masm.bind(&done);
4913
4914 bailoutFrom(&bail, lir->snapshot());
4915}
4916
4917void CodeGenerator::visitMegamorphicLoadSlotPermissive(
4918 LMegamorphicLoadSlotPermissive* lir) {
4919 Register obj = ToRegister(lir->object());
4920 Register temp0 = ToRegister(lir->temp0());
4921 Register temp1 = ToRegister(lir->temp1());
4922 Register temp2 = ToRegister(lir->temp2());
4923 Register temp3 = ToRegister(lir->temp3());
4924 ValueOperand output = ToOutValue(lir);
4925
4926 masm.movePtr(obj, temp3);
4927
4928 Label done, getter, nullGetter;
4929 PropertyKey id = lir->mir()->name();
4930 masm.movePropertyKey(id, temp0);
4931 masm.move32(Imm32(HashPropertyKeyThreadSafe(id)), temp1);
4932
4933 MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4933); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 4933); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4934 MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4934); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 4934); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4935 MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4935); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 4935); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4936 MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4936); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 4936); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4937#if defined(JS_NUNBOX32)
4938 MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4938); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 4938); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4939 MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadReg() == JSReturnReg_Data)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4939); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 4939); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4940#else
4941 MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4941); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 4941); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4942#endif
4943 MOZ_ASSERT(!output.aliases(temp3))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!output.aliases(temp3))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!output.aliases(temp3)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(temp3)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4943); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!output.aliases(temp3)" ")"); do { MOZ_CrashSequence
(__null, 4943); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4944 TrampolinePtr megamorphicLoadStub =
4945 gen->jitRuntime()->megamorphicLoadStubPermissive();
4946 masm.call(megamorphicLoadStub);
4947 masm.branchPtr(Assembler::Equal, temp2,
4948 Imm32(JitRuntime::MegamorphicLoadStubCacheHit), &done);
4949 masm.branchPtr(Assembler::Equal, temp2,
4950 Imm32(JitRuntime::MegamorphicLoadStubCacheHitGetter), &getter);
4951
4952 masm.movePropertyKey(lir->mir()->name(), temp1);
4953 pushArg(temp2);
4954 pushArg(temp1);
4955 pushArg(obj);
4956
4957 using Fn = bool (*)(JSContext*, HandleObject, HandleId,
4958 MegamorphicCacheEntry*, MutableHandleValue);
4959 callVM<Fn, GetPropMaybeCached>(lir);
4960
4961 masm.jump(&done);
4962
4963 masm.bind(&getter);
4964
4965 emitCallMegamorphicGetter(lir, output, temp3, temp1, temp2, &nullGetter);
4966 masm.jump(&done);
4967
4968 masm.bind(&nullGetter);
4969 masm.moveValue(UndefinedValue(), output);
4970 masm.bind(&done);
4971}
4972
4973void CodeGenerator::visitMegamorphicLoadSlotByValue(
4974 LMegamorphicLoadSlotByValue* lir) {
4975 Register obj = ToRegister(lir->object());
4976 ValueOperand idVal = ToValue(lir->idVal());
4977 Register temp0 = ToRegister(lir->temp0());
4978 Register temp1 = ToRegister(lir->temp1());
4979 Register temp2 = ToRegister(lir->temp2());
4980 ValueOperand output = ToOutValue(lir);
4981
4982 Label done, bail, atomizeMiss;
4983 masm.xorPtr(temp2, temp2);
4984 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
4985
4986 MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4986); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 4986); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4987 MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4987); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 4987); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4988 MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4988); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 4988); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4989 MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4989); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 4989); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4990#if defined(JS_NUNBOX32)
4991 MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp"
, 4991); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 4991); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4992 MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadReg() == JSReturnReg_Data)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4992); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 4992); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4993#else
4994 MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4994); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 4994); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4995#endif
4996 TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub();
4997 masm.call(megamorphicLoadStub);
4998 masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done);
4999
5000 masm.bind(&atomizeMiss);
5001 masm.branchIfNonNativeObj(obj, temp0, &bail);
5002
5003 // idVal will be in vp[0], result will be stored in vp[1].
5004 masm.reserveStack(sizeof(Value));
5005 masm.Push(idVal);
5006 masm.moveStackPtrTo(temp0);
5007
5008 using Fn = bool (*)(JSContext* cx, JSObject* obj,
5009 MegamorphicCache::Entry* cacheEntry, Value* vp);
5010 masm.setupAlignedABICall();
5011 masm.loadJSContext(temp1);
5012 masm.passABIArg(temp1);
5013 masm.passABIArg(obj);
5014 masm.passABIArg(temp2);
5015 masm.passABIArg(temp0);
5016 masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>();
5017
5018 MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5018); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { MOZ_CrashSequence
(__null, 5018); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5019 masm.storeCallPointerResult(temp0);
5020 masm.Pop(idVal);
5021
5022 uint32_t framePushed = masm.framePushed();
5023 Label ok;
5024 masm.branchIfTrueBool(temp0, &ok);
5025 masm.freeStack(sizeof(Value)); // Discard result Value.
5026 masm.jump(&bail);
5027
5028 masm.bind(&ok);
5029 masm.setFramePushed(framePushed);
5030 masm.Pop(output);
5031
5032 masm.bind(&done);
5033
5034 bailoutFrom(&bail, lir->snapshot());
5035}
5036
5037void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive(
5038 LMegamorphicLoadSlotByValuePermissive* lir) {
5039 Register obj = ToRegister(lir->object());
5040 ValueOperand idVal = ToValue(lir->idVal());
5041 Register temp0 = ToRegister(lir->temp0());
5042 Register temp1 = ToRegister(lir->temp1());
5043 Register temp2 = ToRegister(lir->temp2());
5044
5045 Label done, atomizeMiss;
5046
5047 // If we have enough registers available, we can call getters directly from
5048 // jitcode. On x86, we have to call into the VM.
5049#ifndef JS_CODEGEN_X86
5050 ValueOperand output = ToOutValue(lir);
5051 Label getter, nullGetter;
5052 Register temp3 = ToRegister(lir->temp3());
5053 masm.movePtr(obj, temp3);
5054 masm.xorPtr(temp2, temp2);
5055 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
5056
5057 MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5057); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 5057); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5058 MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5058); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 5058); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5059 MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5059); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 5059); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5060 MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5060); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 5060); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5061# if defined(JS_NUNBOX32)
5062 MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp"
, 5062); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 5062); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5063 MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadReg() == JSReturnReg_Data)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5063); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 5063); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5064# else
5065 MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5065); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 5065); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5066# endif
5067 MOZ_ASSERT(!output.aliases(temp3))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!output.aliases(temp3))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!output.aliases(temp3)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(temp3)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5067); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!output.aliases(temp3)" ")"); do { MOZ_CrashSequence
(__null, 5067); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5068 TrampolinePtr megamorphicLoadStub =
5069 gen->jitRuntime()->megamorphicLoadStubPermissive();
5070 masm.call(megamorphicLoadStub);
5071 masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done);
5072 masm.branchTest32(Assembler::NonZero, temp2, Imm32(2), &getter);
5073#else
5074 masm.xorPtr(temp2, temp2);
5075 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
5076
5077 MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5077); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 5077); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5078 MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5078); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 5078); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5079 MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5079); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 5079); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5080 MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5080); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 5080); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5081# if defined(JS_NUNBOX32)
5082 MOZ_ASSERT(ToOutValue(lir).typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToOutValue(lir).typeReg() == JSReturnReg_Type)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToOutValue(lir).typeReg() == JSReturnReg_Type))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("ToOutValue(lir).typeReg() == JSReturnReg_Type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5082); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToOutValue(lir).typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 5082); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5083 MOZ_ASSERT(ToOutValue(lir).payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToOutValue(lir).payloadReg() == JSReturnReg_Data)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToOutValue(lir).payloadReg() == JSReturnReg_Data))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("ToOutValue(lir).payloadReg() == JSReturnReg_Data"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5083); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToOutValue(lir).payloadReg() == JSReturnReg_Data"
")"); do { MOZ_CrashSequence(__null, 5083); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5084# else
5085 MOZ_ASSERT(ToOutValue(lir).payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToOutValue(lir).payloadOrValueReg() == JSReturnReg)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToOutValue(lir).payloadOrValueReg() == JSReturnReg))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToOutValue(lir).payloadOrValueReg() == JSReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5085); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToOutValue(lir).payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 5085); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5086# endif
5087 TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub();
5088 masm.call(megamorphicLoadStub);
5089 masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done);
5090#endif
5091
5092 masm.bind(&atomizeMiss);
5093
5094 pushArg(temp2);
5095 pushArg(idVal);
5096 pushArg(obj);
5097
5098 using Fn = bool (*)(JSContext*, HandleObject, HandleValue,
5099 MegamorphicCacheEntry*, MutableHandleValue);
5100 callVM<Fn, GetElemMaybeCached>(lir);
5101
5102#ifndef JS_CODEGEN_X86
5103 masm.jump(&done);
5104 masm.bind(&getter);
5105
5106 emitCallMegamorphicGetter(lir, output, temp3, temp1, temp2, &nullGetter);
5107 masm.jump(&done);
5108
5109 masm.bind(&nullGetter);
5110 masm.moveValue(UndefinedValue(), output);
5111#endif
5112
5113 masm.bind(&done);
5114}
5115
5116void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) {
5117 Register obj = ToRegister(lir->object());
5118 ValueOperand value = ToValue(lir->rhs());
5119
5120 Register temp0 = ToRegister(lir->temp0());
5121#ifndef JS_CODEGEN_X86
5122 Register temp1 = ToRegister(lir->temp1());
5123 Register temp2 = ToRegister(lir->temp2());
5124#endif
5125
5126 // The instruction is marked as call-instruction so only these registers are
5127 // live.
5128 LiveRegisterSet liveRegs;
5129 liveRegs.addUnchecked(obj);
5130 liveRegs.addUnchecked(value);
5131 liveRegs.addUnchecked(temp0);
5132#ifndef JS_CODEGEN_X86
5133 liveRegs.addUnchecked(temp1);
5134 liveRegs.addUnchecked(temp2);
5135#endif
5136
5137 Label cacheHit, done;
5138#ifdef JS_CODEGEN_X86
5139 masm.emitMegamorphicCachedSetSlot(
5140 lir->mir()->name(), obj, temp0, value, liveRegs, &cacheHit,
5141 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
5142 EmitPreBarrier(masm, addr, mirType);
5143 });
5144#else
5145 masm.emitMegamorphicCachedSetSlot(
5146 lir->mir()->name(), obj, temp0, temp1, temp2, value, liveRegs, &cacheHit,
5147 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
5148 EmitPreBarrier(masm, addr, mirType);
5149 });
5150#endif
5151
5152 pushArg(Imm32(lir->mir()->strict()));
5153 pushArg(value);
5154 pushArg(lir->mir()->name(), temp0);
5155 pushArg(obj);
5156
5157 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
5158 callVM<Fn, SetPropertyMegamorphic<true>>(lir);
5159
5160 masm.jump(&done);
5161 masm.bind(&cacheHit);
5162
5163 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
5164 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
5165
5166 // Note: because this is a call-instruction, no registers need to be saved.
5167 MOZ_ASSERT(lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->isCall())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->isCall()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("lir->isCall()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5167); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 5167); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5168 emitPostWriteBarrier(obj);
5169
5170 masm.bind(&done);
5171}
5172
5173void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) {
5174 Register obj = ToRegister(lir->object());
5175 ValueOperand idVal = ToValue(lir->idVal());
5176 Register temp0 = ToRegister(lir->temp0());
5177 Register temp1 = ToRegister(lir->temp1());
5178 Register temp2 = ToRegister(lir->temp2());
5179 Register output = ToRegister(lir->output());
5180
5181 Label bail, cacheHit, atomizeMiss;
5182 masm.xorPtr(temp2, temp2);
5183 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
5184 masm.emitMegamorphicCacheLookupExists(obj, temp0, temp1, temp2, output,
5185 &cacheHit, lir->mir()->hasOwn());
5186
5187 masm.bind(&atomizeMiss);
5188 masm.branchIfNonNativeObj(obj, temp0, &bail);
5189
5190 // idVal will be in vp[0], result will be stored in vp[1].
5191 masm.reserveStack(sizeof(Value));
5192 masm.Push(idVal);
5193 masm.moveStackPtrTo(temp0);
5194
5195 using Fn = bool (*)(JSContext* cx, JSObject* obj,
5196 MegamorphicCache::Entry* cacheEntry, Value* vp);
5197 masm.setupAlignedABICall();
5198 masm.loadJSContext(temp1);
5199 masm.passABIArg(temp1);
5200 masm.passABIArg(obj);
5201 masm.passABIArg(temp2);
5202 masm.passABIArg(temp0);
5203 if (lir->mir()->hasOwn()) {
5204 masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>();
5205 } else {
5206 masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>();
5207 }
5208
5209 MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5209); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { MOZ_CrashSequence
(__null, 5209); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5210 masm.storeCallPointerResult(temp0);
5211 masm.Pop(idVal);
5212
5213 uint32_t framePushed = masm.framePushed();
5214 Label ok;
5215 masm.branchIfTrueBool(temp0, &ok);
5216 masm.freeStack(sizeof(Value)); // Discard result Value.
5217 masm.jump(&bail);
5218
5219 masm.bind(&ok);
5220 masm.setFramePushed(framePushed);
5221 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
5222 masm.freeStack(sizeof(Value));
5223 masm.bind(&cacheHit);
5224
5225 bailoutFrom(&bail, lir->snapshot());
5226}
5227
5228void CodeGenerator::visitSmallObjectVariableKeyHasProp(
5229 LSmallObjectVariableKeyHasProp* lir) {
5230 Register id = ToRegister(lir->idStr());
5231 Register output = ToRegister(lir->output());
5232
5233#ifdef DEBUG1
5234 Label isAtom;
5235 masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()),
5236 Imm32(StringFlags::ATOM_BIT), &isAtom);
5237 masm.assumeUnreachable("Expected atom input");
5238 masm.bind(&isAtom);
5239#endif
5240
5241 SharedShape* shape = &lir->mir()->shape()->asShared();
5242
5243 Label done, success;
5244 for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) {
5245 masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()),
5246 &success);
5247 }
5248 masm.move32(Imm32(0), output);
5249 masm.jump(&done);
5250 masm.bind(&success);
5251 masm.move32(Imm32(1), output);
5252 masm.bind(&done);
5253}
5254
5255void CodeGenerator::visitGuardToArrayBuffer(LGuardToArrayBuffer* guard) {
5256 Register obj = ToRegister(guard->object());
5257 Register temp = ToRegister(guard->temp0());
5258
5259 // branchIfIsNotArrayBuffer may zero the object register on speculative paths
5260 // (we should have a defineReuseInput allocation in this case).
5261
5262 Label bail;
5263 masm.branchIfIsNotArrayBuffer(obj, temp, &bail);
5264 bailoutFrom(&bail, guard->snapshot());
5265}
5266
5267void CodeGenerator::visitGuardToSharedArrayBuffer(
5268 LGuardToSharedArrayBuffer* guard) {
5269 Register obj = ToRegister(guard->object());
5270 Register temp = ToRegister(guard->temp0());
5271
5272 // branchIfIsNotSharedArrayBuffer may zero the object register on speculative
5273 // paths (we should have a defineReuseInput allocation in this case).
5274
5275 Label bail;
5276 masm.branchIfIsNotSharedArrayBuffer(obj, temp, &bail);
5277 bailoutFrom(&bail, guard->snapshot());
5278}
5279
5280void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared(
5281 LGuardIsNotArrayBufferMaybeShared* guard) {
5282 Register obj = ToRegister(guard->object());
5283 Register temp = ToRegister(guard->temp0());
5284
5285 Label bail;
5286 masm.branchIfIsArrayBufferMaybeShared(obj, temp, &bail);
5287 bailoutFrom(&bail, guard->snapshot());
5288}
5289
5290void CodeGenerator::visitGuardIsNonResizableTypedArray(
5291 LGuardIsNonResizableTypedArray* guard) {
5292 Register obj = ToRegister(guard->object());
5293 Register temp = ToRegister(guard->temp0());
5294
5295 Label bail;
5296 masm.loadObjClassUnsafe(obj, temp);
5297 masm.branchIfClassIsNotNonResizableTypedArray(temp, &bail);
5298 bailoutFrom(&bail, guard->snapshot());
5299}
5300
5301void CodeGenerator::visitGuardIsResizableTypedArray(
5302 LGuardIsResizableTypedArray* guard) {
5303 Register obj = ToRegister(guard->object());
5304 Register temp = ToRegister(guard->temp0());
5305
5306 Label bail;
5307 masm.loadObjClassUnsafe(obj, temp);
5308 masm.branchIfClassIsNotResizableTypedArray(temp, &bail);
5309 bailoutFrom(&bail, guard->snapshot());
5310}
5311
5312void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) {
5313 Register obj = ToRegister(guard->object());
5314
5315 Label bail;
5316
5317 Address handlerAddr(obj, ProxyObject::offsetOfHandler());
5318 masm.branchPtr(Assembler::NotEqual, handlerAddr,
5319 ImmPtr(guard->mir()->handler()), &bail);
5320
5321 bailoutFrom(&bail, guard->snapshot());
5322}
5323
5324void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) {
5325 Register input = ToRegister(guard->input());
5326 Register expected = ToRegister(guard->expected());
5327
5328 Assembler::Condition cond =
5329 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5330 bailoutCmpPtr(cond, input, expected, guard->snapshot());
5331}
5332
5333void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) {
5334 Register input = ToRegister(guard->input());
5335 Register expected = ToRegister(guard->expected());
5336
5337 bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot());
5338}
5339
5340void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) {
5341 Register str = ToRegister(guard->str());
5342 Register scratch = ToRegister(guard->temp0());
5343
5344 LiveRegisterSet volatileRegs = liveVolatileRegs(guard);
5345 volatileRegs.takeUnchecked(scratch);
5346
5347 Label bail;
5348 masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs,
5349 &bail);
5350 bailoutFrom(&bail, guard->snapshot());
5351}
5352
5353void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) {
5354 Register symbol = ToRegister(guard->symbol());
5355
5356 bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()),
5357 guard->snapshot());
5358}
5359
5360void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) {
5361 Register num = ToRegister(guard->num());
5362
5363 bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()),
5364 guard->snapshot());
5365}
5366
5367void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) {
5368 Register str = ToRegister(lir->string());
5369 Register output = ToRegister(lir->output());
5370
5371 Label vmCall, done;
5372 masm.loadStringIndexValue(str, output, &vmCall);
5373 masm.jump(&done);
5374
5375 {
5376 masm.bind(&vmCall);
5377
5378 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5379 volatileRegs.takeUnchecked(output);
5380 masm.PushRegsInMask(volatileRegs);
5381
5382 using Fn = int32_t (*)(JSString* str);
5383 masm.setupAlignedABICall();
5384 masm.passABIArg(str);
5385 masm.callWithABI<Fn, GetIndexFromString>();
5386 masm.storeCallInt32Result(output);
5387
5388 masm.PopRegsInMask(volatileRegs);
5389
5390 // GetIndexFromString returns a negative value on failure.
5391 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
5392 }
5393
5394 masm.bind(&done);
5395}
5396
5397void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) {
5398 Register str = ToRegister(lir->string());
5399 Register output = ToRegister(lir->output());
5400 Register temp = ToRegister(lir->temp0());
5401
5402 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5403
5404 Label bail;
5405 masm.guardStringToInt32(str, output, temp, volatileRegs, &bail);
5406 bailoutFrom(&bail, lir->snapshot());
5407}
5408
5409void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) {
5410 Register str = ToRegister(lir->string());
5411 FloatRegister output = ToFloatRegister(lir->output());
5412 Register temp0 = ToRegister(lir->temp0());
5413 Register temp1 = ToRegister(lir->temp1());
5414
5415 Label vmCall, done;
5416 // Use indexed value as fast path if possible.
5417 masm.loadStringIndexValue(str, temp0, &vmCall);
5418 masm.convertInt32ToDouble(temp0, output);
5419 masm.jump(&done);
5420 {
5421 masm.bind(&vmCall);
5422
5423 // Reserve stack for holding the result value of the call.
5424 masm.reserveStack(sizeof(double));
5425 masm.moveStackPtrTo(temp0);
5426
5427 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5428 volatileRegs.takeUnchecked(temp0);
5429 volatileRegs.takeUnchecked(temp1);
5430 masm.PushRegsInMask(volatileRegs);
5431
5432 using Fn = bool (*)(JSContext* cx, JSString* str, double* result);
5433 masm.setupAlignedABICall();
5434 masm.loadJSContext(temp1);
5435 masm.passABIArg(temp1);
5436 masm.passABIArg(str);
5437 masm.passABIArg(temp0);
5438 masm.callWithABI<Fn, StringToNumberPure>();
5439 masm.storeCallPointerResult(temp0);
5440
5441 masm.PopRegsInMask(volatileRegs);
5442
5443 Label ok;
5444 masm.branchIfTrueBool(temp0, &ok);
5445 {
5446 // OOM path, recovered by StringToNumberPure.
5447 //
5448 // Use addToStackPtr instead of freeStack as freeStack tracks stack height
5449 // flow-insensitively, and using it here would confuse the stack height
5450 // tracking.
5451 masm.addToStackPtr(Imm32(sizeof(double)));
5452 bailout(lir->snapshot());
5453 }
5454 masm.bind(&ok);
5455 masm.Pop(output);
5456 }
5457 masm.bind(&done);
5458}
5459
5460void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) {
5461 Register obj = ToRegister(guard->object());
5462 Register temp = ToRegister(guard->temp0());
5463
5464 // Load obj->elements.
5465 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
5466
5467 // Make sure there are no dense elements.
5468 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
5469 bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot());
5470}
5471
5472void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) {
5473 Register input = ToRegister(lir->input());
5474 Register64 output = ToOutRegister64(lir);
5475
5476 masm.move32To64ZeroExtend(input, output);
5477}
5478
5479void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input,
5480 Register64 output) {
5481 Register temp = output.scratchReg();
5482
5483 saveLive(lir);
5484
5485 masm.reserveStack(sizeof(uint64_t));
5486 masm.moveStackPtrTo(temp);
5487 pushArg(temp);
5488 pushArg(input);
5489
5490 using Fn = bool (*)(JSContext*, HandleString, uint64_t*);
5491 callVM<Fn, DoStringToInt64>(lir);
5492
5493 masm.load64(Address(masm.getStackPointer(), 0), output);
5494 masm.freeStack(sizeof(uint64_t));
5495
5496 restoreLiveIgnore(lir, StoreValueTo(output).clobbered());
5497}
5498
5499void CodeGenerator::visitStringToInt64(LStringToInt64* lir) {
5500 Register input = ToRegister(lir->input());
5501 Register64 output = ToOutRegister64(lir);
5502
5503 emitStringToInt64(lir, input, output);
5504}
5505
5506void CodeGenerator::visitValueToInt64(LValueToInt64* lir) {
5507 ValueOperand input = ToValue(lir->input());
5508 Register temp = ToRegister(lir->temp0());
5509 Register64 output = ToOutRegister64(lir);
5510
5511 int checks = 3;
5512
5513 Label fail, done;
5514 // Jump to fail if this is the last check and we fail it,
5515 // otherwise to the next test.
5516 auto emitTestAndUnbox = [&](auto testAndUnbox) {
5517 MOZ_ASSERT(checks > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(checks > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(checks > 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("checks > 0",
"./../../../../js/src/jit/CodeGenerator.cpp", 5517); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "checks > 0" ")"); do { MOZ_CrashSequence
(__null, 5517); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5518
5519 checks--;
5520 Label notType;
5521 Label* target = checks ? &notType : &fail;
5522
5523 testAndUnbox(target);
5524
5525 if (checks) {
5526 masm.jump(&done);
5527 masm.bind(&notType);
5528 }
5529 };
5530
5531 Register tag = masm.extractTag(input, temp);
5532
5533 // BigInt.
5534 emitTestAndUnbox([&](Label* target) {
5535 masm.branchTestBigInt(Assembler::NotEqual, tag, target);
5536 masm.unboxBigInt(input, temp);
5537 masm.loadBigInt64(temp, output);
5538 });
5539
5540 // Boolean
5541 emitTestAndUnbox([&](Label* target) {
5542 masm.branchTestBoolean(Assembler::NotEqual, tag, target);
5543 masm.unboxBoolean(input, temp);
5544 masm.move32To64ZeroExtend(temp, output);
5545 });
5546
5547 // String
5548 emitTestAndUnbox([&](Label* target) {
5549 masm.branchTestString(Assembler::NotEqual, tag, target);
5550 masm.unboxString(input, temp);
5551 emitStringToInt64(lir, temp, output);
5552 });
5553
5554 MOZ_ASSERT(checks == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(checks == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(checks == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("checks == 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 5554); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0"
")"); do { MOZ_CrashSequence(__null, 5554); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5555
5556 bailoutFrom(&fail, lir->snapshot());
5557 masm.bind(&done);
5558}
5559
5560void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) {
5561 Register operand = ToRegister(lir->input());
5562 Register64 output = ToOutRegister64(lir);
5563
5564 masm.loadBigInt64(operand, output);
5565}
5566
5567OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir,
5568 Scalar::Type type,
5569 Register64 input,
5570 Register output) {
5571#if JS_BITS_PER_WORD64 == 32
5572 using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t);
5573 auto args = ArgList(input.low, input.high);
5574#else
5575 using Fn = BigInt* (*)(JSContext*, uint64_t);
5576 auto args = ArgList(input);
5577#endif
5578
5579 if (type == Scalar::BigInt64) {
5580 return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args,
5581 StoreRegisterTo(output));
5582 }
5583 MOZ_ASSERT(type == Scalar::BigUint64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == Scalar::BigUint64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == Scalar::BigUint64)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == Scalar::BigUint64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5583); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == Scalar::BigUint64" ")"); do { MOZ_CrashSequence
(__null, 5583); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5584 return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args,
5585 StoreRegisterTo(output));
5586}
5587
5588void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type,
5589 Register64 input, Register output,
5590 Register maybeTemp,
5591 Register64 maybeTemp64) {
5592 OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output);
5593
5594 if (maybeTemp != InvalidReg) {
5595 masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry());
5596 } else {
5597 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
5598 regs.take(input);
5599 regs.take(output);
5600
5601 Register temp = regs.takeAny();
5602
5603 masm.push(temp);
5604
5605 Label fail, ok;
5606 masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail);
5607 masm.pop(temp);
5608 masm.jump(&ok);
5609 masm.bind(&fail);
5610 masm.pop(temp);
5611 masm.jump(ool->entry());
5612 masm.bind(&ok);
5613 }
5614 masm.initializeBigInt64(type, output, input, maybeTemp64);
5615 masm.bind(ool->rejoin());
5616}
5617
5618void CodeGenerator::emitCallMegamorphicGetter(
5619 LInstruction* lir, ValueOperand accessorAndOutput, Register obj,
5620 Register calleeScratch, Register argcScratch, Label* nullGetter) {
5621 MOZ_ASSERT(calleeScratch == IonGenericCallCalleeReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(calleeScratch == IonGenericCallCalleeReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(calleeScratch == IonGenericCallCalleeReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("calleeScratch == IonGenericCallCalleeReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5621); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "calleeScratch == IonGenericCallCalleeReg" ")"
); do { MOZ_CrashSequence(__null, 5621); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5622 MOZ_ASSERT(argcScratch == IonGenericCallArgcReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argcScratch == IonGenericCallArgcReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(argcScratch == IonGenericCallArgcReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"argcScratch == IonGenericCallArgcReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 5622); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argcScratch == IonGenericCallArgcReg"
")"); do { MOZ_CrashSequence(__null, 5622); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5623
5624 masm.unboxNonDouble(accessorAndOutput, calleeScratch,
5625 JSVAL_TYPE_PRIVATE_GCTHING);
5626
5627 masm.loadPtr(Address(calleeScratch, GetterSetter::offsetOfGetter()),
5628 calleeScratch);
5629 masm.branchTestPtr(Assembler::Zero, calleeScratch, calleeScratch, nullGetter);
5630
5631 if (JitStackValueAlignment > 1) {
5632 masm.reserveStack(sizeof(Value) * (JitStackValueAlignment - 1));
5633 }
5634 masm.pushValue(JSVAL_TYPE_OBJECT, obj);
5635
5636 masm.checkStackAlignment();
5637
5638 masm.move32(Imm32(0), argcScratch);
5639 ensureOsiSpace();
5640
5641 TrampolinePtr genericCallStub =
5642 gen->jitRuntime()->getIonGenericCallStub(IonGenericCallKind::Call);
5643 uint32_t callOffset = masm.callJit(genericCallStub);
5644 markSafepointAt(callOffset, lir);
5645
5646 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5647
5648 masm.moveValue(JSReturnOperand, accessorAndOutput);
5649
5650 masm.setFramePushed(frameSize());
5651 emitRestoreStackPointerFromFP();
5652}
5653
5654void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) {
5655 Register64 input = ToRegister64(lir->input());
5656 Register64 temp = ToRegister64(lir->temp0());
5657 Register output = ToRegister(lir->output());
5658
5659 emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp.scratchReg(),
5660 temp);
5661}
5662
5663void CodeGenerator::visitUint64ToBigInt(LUint64ToBigInt* lir) {
5664 Register64 input = ToRegister64(lir->input());
5665 Register temp = ToRegister(lir->temp0());
5666 Register output = ToRegister(lir->output());
5667
5668 emitCreateBigInt(lir, Scalar::BigUint64, input, output, temp);
5669}
5670
5671void CodeGenerator::visitInt64ToIntPtr(LInt64ToIntPtr* lir) {
5672 Register64 input = ToRegister64(lir->input());
5673#ifdef JS_64BIT1
5674 MOZ_ASSERT(input.reg == ToRegister(lir->output()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input.reg == ToRegister(lir->output()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(input.reg == ToRegister(lir->output())))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("input.reg == ToRegister(lir->output())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5674); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input.reg == ToRegister(lir->output())"
")"); do { MOZ_CrashSequence(__null, 5674); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5675#else
5676 Register output = ToRegister(lir->output());
5677#endif
5678
5679 Label bail;
5680 if (lir->mir()->isSigned()) {
5681 masm.branchInt64NotInPtrRange(input, &bail);
5682 } else {
5683 masm.branchUInt64NotInPtrRange(input, &bail);
5684 }
5685 bailoutFrom(&bail, lir->snapshot());
5686
5687#ifndef JS_64BIT1
5688 masm.move64To32(input, output);
5689#endif
5690}
5691
5692void CodeGenerator::visitIntPtrToInt64(LIntPtrToInt64* lir) {
5693#ifdef JS_64BIT1
5694 MOZ_CRASH("Not used on 64-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 64-bit platforms"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5694); AnnotateMozCrashReason
("MOZ_CRASH(" "Not used on 64-bit platforms" ")"); do { MOZ_CrashSequence
(__null, 5694); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
5695#else
5696 Register input = ToRegister(lir->input());
5697 Register64 output = ToOutRegister64(lir);
5698
5699 masm.move32To64SignExtend(input, output);
5700#endif
5701}
5702
5703Address CodeGenerator::getNurseryValueAddress(ValueOrNurseryValueIndex val,
5704 Register reg) {
5705 // Move the address of the Value stored in the IonScript into |reg|.
5706 uint32_t nurseryIndex = val.toNurseryValueIndex();
5707 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), reg);
5708 masm.propagateOOM(nurseryValueLabels_.emplaceBack(label, nurseryIndex));
5709 return Address(reg, 0);
5710}
5711
5712void CodeGenerator::visitGuardValue(LGuardValue* lir) {
5713 ValueOperand input = ToValue(lir->input());
5714 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5715 ValueOrNurseryValueIndex expected = lir->mir()->expected();
5716
5717 Label bail;
5718 if (expected.isValue()) {
5719 Value expectedVal = expected.toValue();
5720 if (expectedVal.isNaN()) {
5721 MOZ_ASSERT(temp != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp != InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp != InvalidReg))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("temp != InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5721); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 5721); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5722 masm.branchTestNaNValue(Assembler::NotEqual, input, temp, &bail);
5723 } else {
5724 MOZ_ASSERT(temp == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp == InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp == InvalidReg))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("temp == InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5724); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp == InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 5724); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5725 masm.branchTestValue(Assembler::NotEqual, input, expectedVal, &bail);
5726 }
5727 } else {
5728 // Compare to the Value stored in IonScript's nursery values list.
5729 MOZ_ASSERT(temp != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp != InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp != InvalidReg))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("temp != InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5729); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 5729); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5730 Address valueAddr = getNurseryValueAddress(expected, temp);
5731 masm.branchTestValue(Assembler::NotEqual, valueAddr, input, &bail);
5732 }
5733
5734 bailoutFrom(&bail, lir->snapshot());
5735}
5736
5737void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) {
5738 ValueOperand input = ToValue(lir->value());
5739
5740 ScratchTagScope tag(masm, input);
5741 masm.splitTagForTest(input, tag);
5742
5743 Label done;
5744 masm.branchTestNull(Assembler::Equal, tag, &done);
5745
5746 Label bail;
5747 masm.branchTestUndefined(Assembler::NotEqual, tag, &bail);
5748 bailoutFrom(&bail, lir->snapshot());
5749
5750 masm.bind(&done);
5751}
5752
5753void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) {
5754 ValueOperand input = ToValue(lir->value());
5755
5756 Label bail;
5757 masm.branchTestObject(Assembler::Equal, input, &bail);
5758 bailoutFrom(&bail, lir->snapshot());
5759}
5760
5761void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) {
5762 Register function = ToRegister(lir->function());
5763
5764 Label bail;
5765 if (uint16_t flags = lir->mir()->expectedFlags()) {
5766 masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail);
5767 }
5768 if (uint16_t flags = lir->mir()->unexpectedFlags()) {
5769 masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail);
5770 }
5771 bailoutFrom(&bail, lir->snapshot());
5772}
5773
5774void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor(
5775 LGuardFunctionIsNonBuiltinCtor* lir) {
5776 Register function = ToRegister(lir->function());
5777 Register temp = ToRegister(lir->temp0());
5778
5779 Label bail;
5780 masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail);
5781 bailoutFrom(&bail, lir->snapshot());
5782}
5783
5784void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) {
5785 Register function = ToRegister(lir->function());
5786 Register temp = ToRegister(lir->temp0());
5787
5788 Assembler::Condition cond =
5789 lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5790
5791 Label bail;
5792 masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail);
5793 bailoutFrom(&bail, lir->snapshot());
5794}
5795
5796void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) {
5797 Register function = ToRegister(lir->function());
5798
5799 Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript());
5800 bailoutCmpPtr(Assembler::NotEqual, scriptAddr,
5801 ImmGCPtr(lir->mir()->expected()), lir->snapshot());
5802}
5803
5804// Out-of-line path to update the store buffer.
5805class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> {
5806 LInstruction* lir_;
5807 const LAllocation* object_;
5808
5809 public:
5810 OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object)
5811 : lir_(lir), object_(object) {}
5812
5813 void accept(CodeGenerator* codegen) override {
5814 codegen->visitOutOfLineCallPostWriteBarrier(this);
5815 }
5816
5817 LInstruction* lir() const { return lir_; }
5818 const LAllocation* object() const { return object_; }
5819};
5820
5821static void EmitStoreBufferCheckForConstant(MacroAssembler& masm,
5822 const gc::TenuredCell* cell,
5823 AllocatableGeneralRegisterSet& regs,
5824 Label* exit, Label* callVM) {
5825 Register temp = regs.takeAny();
5826
5827 gc::Arena* arena = cell->arena();
5828
5829 Register cells = temp;
5830 masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells);
5831
5832 size_t index = gc::ArenaCellSet::getCellIndex(cell);
5833 auto [word, mask] = gc::ArenaCellSet::getWordIndexAndMask(index);
5834 size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t);
5835
5836 masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask),
5837 exit);
5838
5839 // Check whether this is the sentinel set and if so call the VM to allocate
5840 // one for this arena.
5841 masm.branchPtr(Assembler::Equal,
5842 Address(cells, gc::ArenaCellSet::offsetOfArena()),
5843 ImmPtr(nullptr), callVM);
5844
5845 // Add the cell to the set.
5846 masm.or32(Imm32(mask), Address(cells, offset));
5847 masm.jump(exit);
5848
5849 regs.add(temp);
5850}
5851
5852static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime,
5853 Register objreg, JSObject* maybeConstant,
5854 bool isGlobal,
5855 AllocatableGeneralRegisterSet& regs) {
5856 MOZ_ASSERT_IF(isGlobal, maybeConstant)do { if (isGlobal) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(maybeConstant)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(maybeConstant))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("maybeConstant",
"./../../../../js/src/jit/CodeGenerator.cpp", 5856); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maybeConstant" ")"); do { MOZ_CrashSequence
(__null, 5856); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
5857
5858 Label callVM;
5859 Label exit;
5860
5861 Register temp = regs.takeAny();
5862
5863 // We already have a fast path to check whether a global is in the store
5864 // buffer.
5865 if (!isGlobal) {
5866 if (maybeConstant) {
5867 // Check store buffer bitmap directly for known object.
5868 EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs,
5869 &exit, &callVM);
5870 } else {
5871 // Check one element cache to avoid VM call.
5872 masm.branchPtr(Assembler::Equal,
5873 AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()),
5874 objreg, &exit);
5875 }
5876 }
5877
5878 // Call into the VM to barrier the write.
5879 masm.bind(&callVM);
5880
5881 Register runtimereg = temp;
5882 masm.mov(ImmPtr(runtime), runtimereg);
5883
5884 masm.setupAlignedABICall();
5885 masm.passABIArg(runtimereg);
5886 masm.passABIArg(objreg);
5887 if (isGlobal) {
5888 using Fn = void (*)(JSRuntime* rt, GlobalObject* obj);
5889 masm.callWithABI<Fn, PostGlobalWriteBarrier>();
5890 } else {
5891 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj);
5892 masm.callWithABI<Fn, PostWriteBarrier>();
5893 }
5894
5895 masm.bind(&exit);
5896}
5897
5898void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) {
5899 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5900
5901 Register objreg;
5902 JSObject* object = nullptr;
5903 bool isGlobal = false;
5904 if (obj->isConstant()) {
5905 object = &obj->toConstant()->toObject();
5906 isGlobal = isGlobalObject(object);
5907 objreg = regs.takeAny();
5908 masm.movePtr(ImmGCPtr(object), objreg);
5909 } else {
5910 objreg = ToRegister(obj);
5911 regs.takeUnchecked(objreg);
5912 }
5913
5914 EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs);
5915}
5916
5917// Returns true if `def` might be allocated in the nursery.
5918static bool ValueNeedsPostBarrier(MDefinition* def) {
5919 if (def->isBox()) {
5920 def = def->toBox()->input();
5921 }
5922 if (def->type() == MIRType::Value) {
5923 return true;
5924 }
5925 return NeedsPostBarrier(def->type());
5926}
5927
5928void CodeGenerator::emitElementPostWriteBarrier(
5929 MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj,
5930 Register index, Register scratch, const ConstantOrRegister& val,
5931 int32_t indexDiff) {
5932 if (val.constant()) {
5933 MOZ_ASSERT_IF(val.value().isGCThing(),do { if (val.value().isGCThing()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(!IsInsideNursery
(val.value().toGCThing()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value()
.toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsInsideNursery(val.value().toGCThing())", "./../../../../js/src/jit/CodeGenerator.cpp"
, 5934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { MOZ_CrashSequence(__null, 5934); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5934 !IsInsideNursery(val.value().toGCThing()))do { if (val.value().isGCThing()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(!IsInsideNursery
(val.value().toGCThing()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value()
.toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsInsideNursery(val.value().toGCThing())", "./../../../../js/src/jit/CodeGenerator.cpp"
, 5934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { MOZ_CrashSequence(__null, 5934); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5935 return;
5936 }
5937
5938 TypedOrValueRegister reg = val.reg();
5939 if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) {
5940 return;
5941 }
5942
5943 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
5944 masm.PushRegsInMask(liveVolatileRegs);
5945
5946 if (indexDiff != 0) {
5947 masm.add32(Imm32(indexDiff), index);
5948 }
5949
5950 masm.setupUnalignedABICall(scratch);
5951 masm.movePtr(ImmPtr(gen->runtime), scratch);
5952 masm.passABIArg(scratch);
5953 masm.passABIArg(obj);
5954 masm.passABIArg(index);
5955 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5956 masm.callWithABI<Fn, PostWriteElementBarrier>();
5957
5958 // We don't need a sub32 here because index must be in liveVolatileRegs
5959 // if indexDiff is not zero, so it will be restored below.
5960 MOZ_ASSERT_IF(indexDiff != 0, liveVolatileRegs.has(index))do { if (indexDiff != 0) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(liveVolatileRegs.has(index
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(liveVolatileRegs.has(index)))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("liveVolatileRegs.has(index)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 5960); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "liveVolatileRegs.has(index)" ")"); do { MOZ_CrashSequence
(__null, 5960); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
5961
5962 masm.PopRegsInMask(liveVolatileRegs);
5963
5964 masm.jump(ool.rejoin());
5965 });
5966 addOutOfLineCode(ool, mir);
5967
5968 if (reg.hasValue()) {
5969 masm.branchValueIsNurseryCell(Assembler::NotEqual, reg.valueReg(), scratch,
5970 ool->rejoin());
5971 } else {
5972 masm.branchPtrInNurseryChunk(Assembler::NotEqual, reg.typedReg().gpr(),
5973 scratch, ool->rejoin());
5974 }
5975 masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, scratch, ool->entry());
5976
5977 masm.bind(ool->rejoin());
5978}
5979
5980void CodeGenerator::emitPostWriteBarrier(Register objreg) {
5981 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5982 regs.takeUnchecked(objreg);
5983 EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs);
5984}
5985
5986void CodeGenerator::visitOutOfLineCallPostWriteBarrier(
5987 OutOfLineCallPostWriteBarrier* ool) {
5988 saveLiveVolatile(ool->lir());
5989 const LAllocation* obj = ool->object();
5990 emitPostWriteBarrier(obj);
5991 restoreLiveVolatile(ool->lir());
5992
5993 masm.jump(ool->rejoin());
5994}
5995
5996void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal,
5997 OutOfLineCode* ool) {
5998 // Check whether an object is a global that we have already barriered before
5999 // calling into the VM.
6000 //
6001 // We only check for the script's global, not other globals within the same
6002 // compartment, because we bake in a pointer to realm->globalWriteBarriered
6003 // and doing that would be invalid for other realms because they could be
6004 // collected before the Ion code is discarded.
6005
6006 if (!maybeGlobal->isConstant()) {
6007 return;
6008 }
6009
6010 JSObject* obj = &maybeGlobal->toConstant()->toObject();
6011 if (gen->realm->maybeGlobal() != obj) {
6012 return;
6013 }
6014
6015 const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered();
6016 masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0),
6017 ool->rejoin());
6018}
6019
6020template <class LPostBarrierType, MIRType nurseryType>
6021void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir,
6022 OutOfLineCode* ool) {
6023 static_assert(NeedsPostBarrier(nurseryType));
6024
6025 addOutOfLineCode(ool, lir->mir());
6026
6027 Register temp = ToTempRegisterOrInvalid(lir->temp0());
6028
6029 if (lir->object()->isConstant()) {
6030 // The object must be tenured because MIR and LIR can't contain nursery
6031 // pointers.
6032 MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsInsideNursery(&lir->object()->toConstant
()->toObject()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir->
object()->toConstant()->toObject())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 6032); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { MOZ_CrashSequence(__null, 6032); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6033 } else {
6034 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
6035 temp, ool->rejoin());
6036 }
6037
6038 maybeEmitGlobalBarrierCheck(lir->object(), ool);
6039
6040 Register value = ToRegister(lir->value());
6041 if constexpr (nurseryType == MIRType::Object) {
6042 MOZ_ASSERT(lir->mir()->value()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->value()->type() == MIRType::Object
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->value()->type() == MIRType::Object
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->value()->type() == MIRType::Object", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6042); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 6042); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6043 } else if constexpr (nurseryType == MIRType::String) {
6044 MOZ_ASSERT(lir->mir()->value()->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->value()->type() == MIRType::String
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->value()->type() == MIRType::String
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->value()->type() == MIRType::String", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 6044); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6045 } else {
6046 static_assert(nurseryType == MIRType::BigInt);
6047 MOZ_ASSERT(lir->mir()->value()->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->value()->type() == MIRType::BigInt
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->value()->type() == MIRType::BigInt
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->value()->type() == MIRType::BigInt", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 6047); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6048 }
6049 masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry());
6050
6051 masm.bind(ool->rejoin());
6052}
6053
6054template <class LPostBarrierType>
6055void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir,
6056 OutOfLineCode* ool) {
6057 addOutOfLineCode(ool, lir->mir());
6058
6059 Register temp = ToTempRegisterOrInvalid(lir->temp0());
6060
6061 maybeEmitGlobalBarrierCheck(lir->object(), ool);
6062
6063 ValueOperand value = ToValue(lir->value());
6064 if (lir->object()->isConstant()) {
6065 // The object must be tenured because MIR and LIR can't contain nursery
6066 // pointers.
6067 MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsInsideNursery(&lir->object()->toConstant
()->toObject()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir->
object()->toConstant()->toObject())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 6067); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { MOZ_CrashSequence(__null, 6067); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6068 masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry());
6069 } else {
6070 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp,
6071 ool->rejoin());
6072 masm.branchPtrInNurseryChunk(Assembler::NotEqual, ToRegister(lir->object()),
6073 temp, ool->entry());
6074 }
6075
6076 masm.bind(ool->rejoin());
6077}
6078
6079void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) {
6080 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6081 visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool);
6082}
6083
6084void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) {
6085 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6086 visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool);
6087}
6088
6089void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) {
6090 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6091 visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool);
6092}
6093
6094void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) {
6095 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6096 visitPostWriteBarrierCommonV(lir, ool);
6097}
6098
6099// Out-of-line path to update the store buffer.
6100class OutOfLineCallPostWriteElementBarrier
6101 : public OutOfLineCodeBase<CodeGenerator> {
6102 LInstruction* lir_;
6103 const LAllocation* object_;
6104 const LAllocation* index_;
6105
6106 public:
6107 OutOfLineCallPostWriteElementBarrier(LInstruction* lir,
6108 const LAllocation* object,
6109 const LAllocation* index)
6110 : lir_(lir), object_(object), index_(index) {}
6111
6112 void accept(CodeGenerator* codegen) override {
6113 codegen->visitOutOfLineCallPostWriteElementBarrier(this);
6114 }
6115
6116 LInstruction* lir() const { return lir_; }
6117
6118 const LAllocation* object() const { return object_; }
6119
6120 const LAllocation* index() const { return index_; }
6121};
6122
6123void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier(
6124 OutOfLineCallPostWriteElementBarrier* ool) {
6125 saveLiveVolatile(ool->lir());
6126
6127 const LAllocation* obj = ool->object();
6128 const LAllocation* index = ool->index();
6129
6130 Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj);
6131 Register indexreg = ToRegister(index);
6132
6133 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
6134 regs.takeUnchecked(indexreg);
6135
6136 if (obj->isConstant()) {
6137 objreg = regs.takeAny();
6138 masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg);
6139 } else {
6140 regs.takeUnchecked(objreg);
6141 }
6142
6143 Register runtimereg = regs.takeAny();
6144 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
6145 masm.setupAlignedABICall();
6146 masm.mov(ImmPtr(gen->runtime), runtimereg);
6147 masm.passABIArg(runtimereg);
6148 masm.passABIArg(objreg);
6149 masm.passABIArg(indexreg);
6150 masm.callWithABI<Fn, PostWriteElementBarrier>();
6151
6152 restoreLiveVolatile(ool->lir());
6153
6154 masm.jump(ool->rejoin());
6155}
6156
6157void CodeGenerator::visitPostWriteElementBarrierO(
6158 LPostWriteElementBarrierO* lir) {
6159 auto ool = new (alloc())
6160 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6161 visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir,
6162 ool);
6163}
6164
6165void CodeGenerator::visitPostWriteElementBarrierS(
6166 LPostWriteElementBarrierS* lir) {
6167 auto ool = new (alloc())
6168 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6169 visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir,
6170 ool);
6171}
6172
6173void CodeGenerator::visitPostWriteElementBarrierBI(
6174 LPostWriteElementBarrierBI* lir) {
6175 auto ool = new (alloc())
6176 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6177 visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir,
6178 ool);
6179}
6180
6181void CodeGenerator::visitPostWriteElementBarrierV(
6182 LPostWriteElementBarrierV* lir) {
6183 auto ool = new (alloc())
6184 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6185 visitPostWriteBarrierCommonV(lir, ool);
6186}
6187
6188void CodeGenerator::visitAssertCanElidePostWriteBarrier(
6189 LAssertCanElidePostWriteBarrier* lir) {
6190 Register object = ToRegister(lir->object());
6191 ValueOperand value = ToValue(lir->value());
6192 Register temp = ToRegister(lir->temp0());
6193
6194 Label ok;
6195 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok);
6196 masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok);
6197
6198 masm.assumeUnreachable("Unexpected missing post write barrier");
6199
6200 masm.bind(&ok);
6201}
6202
6203template <typename LCallIns>
6204void CodeGenerator::emitCallNative(LCallIns* call, JSNative native,
6205 Register argContextReg, Register argUintNReg,
6206 Register argVpReg, Register tempReg,
6207 uint32_t unusedStack) {
6208 masm.checkStackAlignment();
6209
6210 // Native functions have the signature:
6211 // bool (*)(JSContext*, unsigned, Value* vp)
6212 // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
6213 // are the function arguments.
6214
6215 // Allocate space for the outparam, moving the StackPointer to what will be
6216 // &vp[1].
6217 masm.adjustStack(unusedStack);
6218
6219 // Push a Value containing the callee object: natives are allowed to access
6220 // their callee before setting the return value. The StackPointer is moved
6221 // to &vp[0].
6222 //
6223 // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|.
6224 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
6225 Register calleeReg = ToRegister(call->getCallee());
6226 masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg)));
6227
6228 // Enter the callee realm.
6229 if (call->mir()->maybeCrossRealm()) {
6230 masm.switchToObjectRealm(calleeReg, tempReg);
6231 }
6232 } else {
6233 WrappedFunction* target = call->mir()->getSingleTarget();
6234 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
6235
6236 // Enter the callee realm.
6237 if (call->mir()->maybeCrossRealm()) {
6238 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg);
6239 masm.switchToObjectRealm(tempReg, tempReg);
6240 }
6241 }
6242
6243 // Preload arguments into registers.
6244 masm.loadJSContext(argContextReg);
6245 masm.moveStackPtrTo(argVpReg);
6246
6247 // Initialize |NativeExitFrameLayout::argc_|.
6248 masm.Push(argUintNReg);
6249
6250 // Construct native exit frame.
6251 //
6252 // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and
6253 // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|.
6254 //
6255 // The NativeExitFrameLayout is now fully initialized.
6256 uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
6257 masm.enterFakeExitFrameForNative(argContextReg, tempReg,
6258 call->mir()->isConstructing());
6259
6260 markSafepointAt(safepointOffset, call);
6261
6262 // Construct and execute call.
6263 masm.setupAlignedABICall();
6264 masm.passABIArg(argContextReg);
6265 masm.passABIArg(argUintNReg);
6266 masm.passABIArg(argVpReg);
6267
6268 ensureOsiSpace();
6269 // If we're using a simulator build, `native` will already point to the
6270 // simulator's call-redirection code for LCallClassHook. Load the address in
6271 // a register first so that we don't try to redirect it a second time.
6272 bool emittedCall = false;
6273#ifdef JS_SIMULATOR
6274 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
6275 masm.movePtr(ImmPtr(native), tempReg);
6276 masm.callWithABI(tempReg);
6277 emittedCall = true;
6278 }
6279#endif
6280 if (!emittedCall) {
6281 masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General,
6282 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
6283 }
6284
6285 // Test for failure.
6286 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
6287
6288 // Exit the callee realm.
6289 if (call->mir()->maybeCrossRealm()) {
6290 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6291 }
6292
6293 // Load the outparam vp[0] into output register(s).
6294 masm.loadValue(
6295 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
6296 JSReturnOperand);
6297
6298 // Until C++ code is instrumented against Spectre, prevent speculative
6299 // execution from returning any private data.
6300 if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() &&
6301 call->mir()->hasLiveDefUses()) {
6302 masm.speculationBarrier();
6303 }
6304
6305#ifdef DEBUG1
6306 // Native constructors are guaranteed to return an Object value.
6307 if (call->mir()->isConstructing()) {
6308 Label notPrimitive;
6309 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6310 &notPrimitive);
6311 masm.assumeUnreachable("native constructors don't return primitives");
6312 masm.bind(&notPrimitive);
6313 }
6314#endif
6315}
6316
6317template <typename LCallIns>
6318void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) {
6319 uint32_t unusedStack =
6320 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6321
6322 // Registers used for callWithABI() argument-passing.
6323 const Register argContextReg = ToRegister(call->getArgContextReg());
6324 const Register argUintNReg = ToRegister(call->getArgUintNReg());
6325 const Register argVpReg = ToRegister(call->getArgVpReg());
6326
6327 // Misc. temporary registers.
6328 const Register tempReg = ToRegister(call->getTempReg());
6329
6330 DebugOnly<uint32_t> initialStack = masm.framePushed();
6331
6332 // Initialize the argc register.
6333 masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg);
6334
6335 // Create the exit frame and call the native.
6336 emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg,
6337 unusedStack);
6338
6339 // The next instruction is removing the footer of the exit frame, so there
6340 // is no need for leaveFakeExitFrame.
6341
6342 // Move the StackPointer back to its original location, unwinding the native
6343 // exit frame.
6344 masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack);
6345 MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"masm.framePushed() == initialStack", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6345); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 6345); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6346}
6347
6348void CodeGenerator::visitCallNative(LCallNative* call) {
6349 WrappedFunction* target = call->getSingleTarget();
6350 MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("target", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6350); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 6350); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6351 MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("target->isNativeWithoutJitEntry()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6351); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 6351); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6352
6353 JSNative native = target->native();
6354 if (call->ignoresReturnValue() && target->hasJitInfo()) {
6355 const JSJitInfo* jitInfo = target->jitInfo();
6356 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
6357 native = jitInfo->ignoresReturnValueMethod;
6358 }
6359 }
6360 emitCallNative(call, native);
6361}
6362
6363void CodeGenerator::visitCallClassHook(LCallClassHook* call) {
6364 emitCallNative(call, call->mir()->target());
6365}
6366
6367static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv,
6368 DOMObjectKind kind) {
6369 // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This
6370 // will be in the first slot but may be fixed or non-fixed.
6371 MOZ_ASSERT(obj != priv)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj != priv)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(obj != priv))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("obj != priv", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6371); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv"
")"); do { MOZ_CrashSequence(__null, 6371); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6372
6373 switch (kind) {
6374 case DOMObjectKind::Native:
6375 // If it's a native object, the value must be in a fixed slot.
6376 // See CanAttachDOMCall in CacheIR.cpp.
6377 masm.debugAssertObjHasFixedSlots(obj, priv);
6378 masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
6379 break;
6380 case DOMObjectKind::Proxy: {
6381#ifdef DEBUG1
6382 // Sanity check: it must be a DOM proxy.
6383 Label isDOMProxy;
6384 masm.branchTestProxyHandlerFamily(
6385 Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy);
6386 masm.assumeUnreachable("Expected a DOM proxy");
6387 masm.bind(&isDOMProxy);
6388#endif
6389 masm.loadPrivate(Address(obj, ProxyObject::offsetOfReservedSlot(0)),
6390 priv);
6391 break;
6392 }
6393 }
6394}
6395
6396void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) {
6397 WrappedFunction* target = call->getSingleTarget();
6398 MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("target", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 6398); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6399 MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("target->isNativeWithoutJitEntry()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 6399); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6400 MOZ_ASSERT(target->hasJitInfo())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target->hasJitInfo())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(target->hasJitInfo()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitInfo()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 6400); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->hasJitInfo()" ")"); do { MOZ_CrashSequence
(__null, 6400); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6401 MOZ_ASSERT(call->mir()->isCallDOMNative())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(call->mir()->isCallDOMNative())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(call->mir()->isCallDOMNative
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("call->mir()->isCallDOMNative()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6401); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()"
")"); do { MOZ_CrashSequence(__null, 6401); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6402
6403 int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6404
6405 // Registers used for callWithABI() argument-passing.
6406 const Register argJSContext = ToRegister(call->getArgJSContext());
6407 const Register argObj = ToRegister(call->getArgObj());
6408 const Register argPrivate = ToRegister(call->getArgPrivate());
6409 const Register argArgs = ToRegister(call->getArgArgs());
6410
6411 DebugOnly<uint32_t> initialStack = masm.framePushed();
6412
6413 masm.checkStackAlignment();
6414
6415 // DOM methods have the signature:
6416 // bool (*)(JSContext*, HandleObject, void* private, const
6417 // JSJitMethodCallArgs& args)
6418 // Where args is initialized from an argc and a vp, vp[0] is space for an
6419 // outparam and the callee, vp[1] is |this|, and vp[2] onward are the
6420 // function arguments. Note that args stores the argv, not the vp, and
6421 // argv == vp + 2.
6422
6423 // Nestle the stack up against the pushed arguments, leaving StackPointer at
6424 // &vp[1]
6425 masm.adjustStack(unusedStack);
6426 // argObj is filled with the extracted object, then returned.
6427 Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj);
6428 MOZ_ASSERT(obj == argObj)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj == argObj)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(obj == argObj))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("obj == argObj",
"./../../../../js/src/jit/CodeGenerator.cpp", 6428); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == argObj" ")"); do { MOZ_CrashSequence
(__null, 6428); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6429
6430 // Push a Value containing the callee object: natives are allowed to access
6431 // their callee before setting the return value. After this the StackPointer
6432 // points to &vp[0].
6433 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
6434
6435 // Now compute the argv value. Since StackPointer is pointing to &vp[0] and
6436 // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
6437 // StackPointer.
6438 static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
6439 static_assert(JSJitMethodCallArgsTraits::offsetOfArgc ==
6440 IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
6441 masm.computeEffectiveAddress(
6442 Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs);
6443
6444 LoadDOMPrivate(masm, obj, argPrivate,
6445 static_cast<MCallDOMNative*>(call->mir())->objectKind());
6446
6447 // Push argc from the call instruction into what will become the IonExitFrame
6448 masm.Push(Imm32(call->numActualArgs()));
6449
6450 // Push our argv onto the stack
6451 masm.Push(argArgs);
6452 // And store our JSJitMethodCallArgs* in argArgs.
6453 masm.moveStackPtrTo(argArgs);
6454
6455 // Push |this| object for passing HandleObject. We push after argc to
6456 // maintain the same sp-relative location of the object pointer with other
6457 // DOMExitFrames.
6458 masm.Push(argObj);
6459 masm.moveStackPtrTo(argObj);
6460
6461 if (call->mir()->maybeCrossRealm()) {
6462 // We use argJSContext as scratch register here.
6463 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext);
6464 masm.switchToObjectRealm(argJSContext, argJSContext);
6465 }
6466
6467 bool preTenureWrapperAllocation =
6468 call->mir()->to<MCallDOMNative>()->initialHeap() == gc::Heap::Tenured;
6469 if (preTenureWrapperAllocation) {
6470 auto ptr = ImmPtr(mirGen().realm->zone()->tenuringAllocSite());
6471 masm.storeLocalAllocSite(ptr, argJSContext);
6472 }
6473
6474 // Construct native exit frame.
6475 uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
6476
6477 masm.loadJSContext(argJSContext);
6478 masm.enterFakeExitFrame(argJSContext, argJSContext,
6479 ExitFrameType::IonDOMMethod);
6480
6481 markSafepointAt(safepointOffset, call);
6482
6483 // Construct and execute call.
6484 masm.setupAlignedABICall();
6485 masm.loadJSContext(argJSContext);
6486 masm.passABIArg(argJSContext);
6487 masm.passABIArg(argObj);
6488 masm.passABIArg(argPrivate);
6489 masm.passABIArg(argArgs);
6490 ensureOsiSpace();
6491 masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method),
6492 ABIType::General,
6493 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
6494
6495 if (target->jitInfo()->isInfallible) {
6496 masm.loadValue(Address(masm.getStackPointer(),
6497 IonDOMMethodExitFrameLayout::offsetOfResult()),
6498 JSReturnOperand);
6499 } else {
6500 // Test for failure.
6501 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
6502
6503 // Load the outparam vp[0] into output register(s).
6504 masm.loadValue(Address(masm.getStackPointer(),
6505 IonDOMMethodExitFrameLayout::offsetOfResult()),
6506 JSReturnOperand);
6507 }
6508
6509 static_assert(!JSReturnOperand.aliases(ReturnReg),
6510 "Clobbering ReturnReg should not affect the return value");
6511
6512 // Switch back to the current realm if needed. Note: if the DOM method threw
6513 // an exception, the exception handler will do this.
6514 if (call->mir()->maybeCrossRealm()) {
6515 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6516 }
6517
6518 // Wipe out the preTenuring bit from the local alloc site
6519 // On exception we handle this in C++
6520 if (preTenureWrapperAllocation) {
6521 masm.storeLocalAllocSite(ImmPtr(nullptr), ReturnReg);
6522 }
6523
6524 // Until C++ code is instrumented against Spectre, prevent speculative
6525 // execution from returning any private data.
6526 if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) {
6527 masm.speculationBarrier();
6528 }
6529
6530 // The next instruction is removing the footer of the exit frame, so there
6531 // is no need for leaveFakeExitFrame.
6532
6533 // Move the StackPointer back to its original location, unwinding the native
6534 // exit frame.
6535 masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
6536 MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"masm.framePushed() == initialStack", "./../../../../js/src/jit/CodeGenerator.cpp"
, 6536); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 6536); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6537}
6538
6539void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) {
6540 pushArg(ImmGCPtr(lir->mir()->name()));
6541
6542 using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue);
6543 callVM<Fn, GetIntrinsicValue>(lir);
6544}
6545
6546void CodeGenerator::emitCallInvokeFunction(
6547 LInstruction* call, Register calleereg, bool constructing,
6548 bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) {
6549 // Nestle %esp up to the argument vector.
6550 // Each path must account for framePushed_ separately, for callVM to be valid.
6551 masm.freeStack(unusedStack);
6552
6553 pushArg(masm.getStackPointer()); // argv.
6554 pushArg(Imm32(argc)); // argc.
6555 pushArg(Imm32(ignoresReturnValue));
6556 pushArg(Imm32(constructing)); // constructing.
6557 pushArg(calleereg); // JSFunction*.
6558
6559 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6560 MutableHandleValue);
6561 callVM<Fn, jit::InvokeFunction>(call);
6562
6563 // Un-nestle %esp from the argument vector. No prefix was pushed.
6564 masm.reserveStack(unusedStack);
6565}
6566
6567void CodeGenerator::visitCallGeneric(LCallGeneric* call) {
6568 // The callee is passed straight through to the trampoline.
6569 MOZ_ASSERT(ToRegister(call->getCallee()) == IonGenericCallCalleeReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(call->getCallee()) == IonGenericCallCalleeReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(call->getCallee()) == IonGenericCallCalleeReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToRegister(call->getCallee()) == IonGenericCallCalleeReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 6569); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg"
")"); do { MOZ_CrashSequence(__null, 6569); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6570
6571 Register argcReg = ToRegister(call->getArgc());
6572 uint32_t unusedStack =
6573 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6574
6575 // Known-target case is handled by LCallKnown.
6576 MOZ_ASSERT(!call->hasSingleTarget())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!call->hasSingleTarget())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!call->hasSingleTarget())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!call->hasSingleTarget()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 6576); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!call->hasSingleTarget()" ")"); do { MOZ_CrashSequence
(__null, 6576); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6577
6578 masm.checkStackAlignment();
6579
6580 masm.move32(Imm32(call->numActualArgs()), argcReg);
6581
6582 // Nestle the StackPointer up to the argument vector.
6583 masm.freeStack(unusedStack);
6584 ensureOsiSpace();
6585
6586 auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct
6587 : IonGenericCallKind::Call;
6588
6589 TrampolinePtr genericCallStub =
6590 gen->jitRuntime()->getIonGenericCallStub(kind);
6591 uint32_t callOffset = masm.callJit(genericCallStub);
6592 markSafepointAt(callOffset, call);
6593
6594 if (call->mir()->maybeCrossRealm()) {
6595 static_assert(!JSReturnOperand.aliases(ReturnReg),
6596 "ReturnReg available as scratch after scripted calls");
6597 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6598 }
6599
6600 // If the return value of the constructing function is Primitive,
6601 // replace the return value with the Object from CreateThis.
6602 if (call->mir()->isConstructing()) {
6603 Label notPrimitive;
6604 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6605 &notPrimitive);
6606 size_t thisvOffset =
6607 JitFrameLayout::offsetOfThis() - JitFrameLayout::bytesPoppedAfterCall();
6608 masm.loadValue(Address(masm.getStackPointer(), thisvOffset),
6609 JSReturnOperand);
6610#ifdef DEBUG1
6611 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6612 &notPrimitive);
6613 masm.assumeUnreachable("CreateThis creates an object");
6614#endif
6615 masm.bind(&notPrimitive);
6616 }
6617
6618 // Restore stack pointer.
6619 masm.setFramePushed(frameSize());
6620 emitRestoreStackPointerFromFP();
6621}
6622
6623void JitRuntime::generateIonGenericCallArgumentsShift(
6624 MacroAssembler& masm, Register argc, Register curr, Register end,
6625 Register scratch, Label* done) {
6626 static_assert(sizeof(Value) == 8);
6627 // There are |argc| Values on the stack. Shift them all down by 8 bytes,
6628 // overwriting the first value.
6629
6630 // Initialize `curr` to the destination of the first copy, and `end` to the
6631 // final value of curr.
6632 masm.moveStackPtrTo(curr);
6633 masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end);
6634
6635 Label loop;
6636 masm.bind(&loop);
6637 masm.branchPtr(Assembler::Equal, curr, end, done);
6638 masm.loadPtr(Address(curr, 8), scratch);
6639 masm.storePtr(scratch, Address(curr, 0));
6640 masm.addPtr(Imm32(sizeof(uintptr_t)), curr);
6641 masm.jump(&loop);
6642}
6643
6644void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm,
6645 IonGenericCallKind kind) {
6646 AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub");
6647 ionGenericCallStubOffset_[kind] = startTrampolineCode(masm);
6648
6649 // This code is tightly coupled with visitCallGeneric.
6650 //
6651 // Upon entry:
6652 // IonGenericCallCalleeReg contains a pointer to the callee object.
6653 // IonGenericCallArgcReg contains the number of actual args.
6654 // The arguments have been pushed onto the stack:
6655 // [newTarget] (iff isConstructing)
6656 // [argN]
6657 // ...
6658 // [arg1]
6659 // [arg0]
6660 // [this]
6661 // <return address> (if not JS_USE_LINK_REGISTER)
6662 //
6663 // This trampoline is responsible for entering the callee's realm,
6664 // massaging the stack into the right shape, and then performing a
6665 // tail call. We will return directly to the Ion code from the
6666 // callee.
6667 //
6668 // To do a tail call, we keep the return address in a register, even
6669 // on platforms that don't normally use a link register, and push it
6670 // just before jumping to the callee, after we are done setting up
6671 // the stack.
6672 //
6673 // The caller is responsible for switching back to the caller's
6674 // realm and cleaning up the stack.
6675
6676 Register calleeReg = IonGenericCallCalleeReg;
6677 Register argcReg = IonGenericCallArgcReg;
6678 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
6679 Register scratch = regs.takeAny();
6680 Register scratch2 = regs.takeAny();
6681
6682#ifndef JS_USE_LINK_REGISTER
6683 Register returnAddrReg = IonGenericCallReturnAddrReg;
6684 masm.pop(returnAddrReg);
6685#endif
6686
6687#ifdef JS_CODEGEN_ARM
6688 // The default second scratch register on arm is lr, which we need
6689 // preserved for tail calls.
6690 AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg);
6691#endif
6692
6693 bool isConstructing = kind == IonGenericCallKind::Construct;
6694
6695 Label entry, notFunction, noJitEntry, vmCall;
6696 masm.bind(&entry);
6697
6698 // Guard that the callee is actually a function.
6699 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
6700 calleeReg, &notFunction);
6701
6702 // Guard that the callee supports the [[Call]] or [[Construct]] operation.
6703 // If these tests fail, we will call into the VM to throw an exception.
6704 if (isConstructing) {
6705 masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR,
6706 Assembler::Zero, &vmCall);
6707 } else {
6708 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
6709 calleeReg, scratch, &vmCall);
6710 }
6711
6712 if (isConstructing) {
6713 // Use the slow path if CreateThis was unable to create the |this| object.
6714 Address thisAddr(masm.getStackPointer(), 0);
6715 masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall);
6716 }
6717
6718 masm.switchToObjectRealm(calleeReg, scratch);
6719
6720 // Load jitCodeRaw for callee if it exists.
6721 masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry);
6722
6723 // ****************************
6724 // * Functions with jit entry *
6725 // ****************************
6726
6727 generateIonGenericHandleUnderflow(masm, isConstructing, &vmCall);
6728
6729 masm.loadJitCodeRaw(calleeReg, scratch2);
6730
6731 // Construct the JitFrameLayout.
6732 masm.PushCalleeToken(calleeReg, isConstructing);
6733 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch);
6734#ifndef JS_USE_LINK_REGISTER
6735 masm.push(returnAddrReg);
6736#endif
6737
6738 // Tail call the jit entry.
6739 masm.jump(scratch2);
6740
6741 // ********************
6742 // * Native functions *
6743 // ********************
6744 masm.bind(&noJitEntry);
6745 if (!isConstructing) {
6746 generateIonGenericCallFunCall(masm, &entry, &vmCall);
6747 }
6748 generateIonGenericCallNativeFunction(masm, isConstructing);
6749
6750 // *******************
6751 // * Bound functions *
6752 // *******************
6753 // TODO: support class hooks?
6754 masm.bind(&notFunction);
6755 if (!isConstructing) {
6756 // TODO: support generic bound constructors?
6757 generateIonGenericCallBoundFunction(masm, &entry, &vmCall);
6758 }
6759
6760 // ********************
6761 // * Fallback VM call *
6762 // ********************
6763 masm.bind(&vmCall);
6764
6765 masm.push(masm.getStackPointer()); // argv
6766 masm.push(argcReg); // argc
6767 masm.push(Imm32(false)); // ignores return value
6768 masm.push(Imm32(isConstructing)); // constructing
6769 masm.push(calleeReg); // callee
6770
6771 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6772 MutableHandleValue);
6773 VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id;
6774 uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)];
6775 Label invokeFunctionVMEntry;
6776 bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset);
6777
6778 masm.push(FrameDescriptor(FrameType::IonJS));
6779#ifndef JS_USE_LINK_REGISTER
6780 masm.push(returnAddrReg);
6781#endif
6782 masm.jump(&invokeFunctionVMEntry);
6783}
6784
6785void JitRuntime::generateMegamorphicLoadStub(MacroAssembler& masm) {
6786 AutoCreatedBy acb(masm, "JitRuntime::generateMegamorphicLoadStub");
6787 megamorphicLoadStubOffset_ = startTrampolineCode(masm);
6788
6789 Register obj = CallTempReg3;
6790 Register id = CallTempReg0;
6791 Register idHash = CallTempReg1;
6792 Register outEntryPtr = CallTempReg2;
6793
6794#if defined(JS_NUNBOX32)
6795 auto output = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
6796 static_assert(!JSReturnReg_Type.aliases(CallTempReg2));
6797 static_assert(!JSReturnReg_Data.aliases(CallTempReg2));
6798#else
6799 auto output = ValueOperand(JSReturnReg);
6800 static_assert(!JSReturnReg.aliases(CallTempReg2));
6801#endif
6802
6803 Label cacheHit;
6804 masm.emitMegamorphicCacheLookupByValue(obj, id, idHash, outEntryPtr, output,
6805 &cacheHit);
6806
6807 masm.abiret();
6808
6809 // Given we don't need the entry if we got a cache hit, and the entry pointer
6810 // will never point into the first code page, we can use the low bits of the
6811 // outEntryPtr to indicate success
6812 masm.bind(&cacheHit);
6813 masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHit)), outEntryPtr);
6814 masm.abiret();
6815}
6816
6817void JitRuntime::generateMegamorphicLoadStubPermissive(MacroAssembler& masm) {
6818 AutoCreatedBy acb(masm, "JitRuntime::generateMegamorphicLoadStubPermissive");
6819 megamorphicLoadStubPermissiveOffset_ = startTrampolineCode(masm);
6820
6821 Register obj = CallTempReg3;
6822 Register id = CallTempReg0;
6823 Register idHash = CallTempReg1;
6824 Register outEntryPtr = CallTempReg2;
6825
6826#if defined(JS_NUNBOX32)
6827 auto output = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
6828 static_assert(!JSReturnReg_Type.aliases(CallTempReg2));
6829 static_assert(!JSReturnReg_Data.aliases(CallTempReg2));
6830#else
6831 auto output = ValueOperand(JSReturnReg);
6832 static_assert(!JSReturnReg.aliases(CallTempReg2));
6833#endif
6834
6835 Label cacheHit, cacheHitGetter;
6836 masm.emitMegamorphicCacheLookupByValue(obj, id, idHash, outEntryPtr, output,
6837 &cacheHit, &cacheHitGetter);
6838
6839 masm.abiret();
6840
6841 // Given we don't need the entry if we got a cache hit, and the entry pointer
6842 // will never point into the first code page, we can use the low bits of the
6843 // outEntryPtr to indicate success or that output holds a getter.
6844 masm.bind(&cacheHit);
6845 masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHit)), outEntryPtr);
6846 masm.abiret();
6847
6848 masm.bind(&cacheHitGetter);
6849 masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHitGetter)), outEntryPtr);
6850 masm.abiret();
6851}
6852
6853void JitRuntime::generateIonGenericHandleUnderflow(MacroAssembler& masm,
6854 bool isConstructing,
6855 Label* vmCall) {
6856 Register calleeReg = IonGenericCallCalleeReg;
6857 Register argcReg = IonGenericCallArgcReg;
6858 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
6859 Register numMissing = regs.takeAny();
6860 Register src = regs.takeAny();
6861 Register dest = regs.takeAny();
6862
6863 // On x86 we have fewer registers than we'd like, so we generate
6864 // slightly less efficient code.
6865 Register srcEnd, scratch;
6866 bool mustSpill = false;
6867 if (regs.empty()) {
6868 srcEnd = numMissing;
6869 scratch = calleeReg;
6870 mustSpill = true;
6871 } else {
6872 srcEnd = regs.takeAny();
6873 scratch = regs.takeAny();
6874 }
6875
6876 // Compute fun->nargs - argc. If it's positive, it's the number of
6877 // undefined args we must push.
6878 Label noUnderflow;
6879 masm.loadFunctionArgCount(calleeReg, numMissing);
6880 masm.sub32(argcReg, numMissing);
6881 masm.branch32(Assembler::LessThanOrEqual, numMissing, Imm32(0), &noUnderflow);
6882
6883 // Ensure that we don't adjust the stack pointer by more than a page.
6884 masm.branch32(Assembler::Above, numMissing, Imm32(JIT_ARGS_LENGTH_MAX),
6885 vmCall);
6886
6887 // If numMissing is even, we want to make the following transformation:
6888 //
6889 // INITIAL FINAL
6890 // [newTarget] (iff isConstructing) [newTarget] (iff isConstructing)
6891 // [argN] [undefined]
6892 // ... [undefined] (...)
6893 // [arg1] [argN]
6894 // [arg0] ...
6895 // [this] <- sp aligned [arg1]
6896 // [arg0]
6897 // [this] -> moved down numMissing
6898 // slots
6899 //
6900 // If numMissing is odd, we must also insert padding:
6901 // [newTarget] (iff isConstructing) (padding)
6902 // [argN] [newTarget] (iff isConstructing)
6903 // ... [undefined]
6904 // [arg1] [argN]
6905 // [arg0] ...
6906 // [this] <- sp aligned [arg1]
6907 // [arg0]
6908 // [this] -> moved down numMissing+1
6909 // slots
6910 //
6911 // Note that |newTarget|, if it exists, must be between the padding and the
6912 // undefined args. It does not move down along with the actual args.
6913
6914 // The first step is to copy the memory from [this] through [argN] into the
6915 // correct position. The source of the copy is the current stack pointer.
6916 masm.moveStackPtrTo(src);
6917
6918 // Compute how far the args must be moved and adjust the stack pointer.
6919 // If numMissing is even, this is numMissing slots. If numMissing is odd,
6920 // this is numMissing+1 slots. We can compute this as (numMissing + 1) & ~1.
6921 masm.add32(Imm32(1), numMissing, dest);
6922 masm.and32(Imm32(~1), dest);
6923 masm.lshift32(Imm32(3), dest);
6924 masm.subFromStackPtr(dest);
6925 masm.moveStackPtrTo(dest);
6926
6927 // We also set up a register pointing to the last copied argument. On x86
6928 // we don't have enough registers, so we spill the calleeReg and numMissing.
6929 if (mustSpill) {
6930 masm.push(calleeReg);
6931 masm.push(numMissing);
6932 }
6933 masm.computeEffectiveAddress(BaseValueIndex(src, argcReg), srcEnd);
6934
6935 // The stack currently looks like this:
6936 //
6937 // [newTarget]
6938 // [argN] <-- srcEnd
6939 // ...
6940 // [arg0]
6941 // [this] <-- src
6942 // ...
6943 // ... <-- dest
6944 // [spill?]
6945 // [spill?]
6946
6947 // Loop to move the arguments.
6948 Label argLoop;
6949 masm.bind(&argLoop);
6950 masm.copy64(Address(src, 0), Address(dest, 0), scratch);
6951 masm.addPtr(Imm32(sizeof(Value)), src);
6952 masm.addPtr(Imm32(sizeof(Value)), dest);
6953 masm.branchPtr(Assembler::BelowOrEqual, src, srcEnd, &argLoop);
6954
6955 if (mustSpill) {
6956 // We must restore numMissing now, so that we can test if it's odd.
6957 // The copy64 below still needs calleeReg as a scratch register.
6958 masm.pop(numMissing);
6959 }
6960
6961 if (isConstructing) {
6962 // If numMissing is odd, we must move newTarget down by one slot.
6963 Label skip;
6964 masm.branchTest32(Assembler::Zero, numMissing, Imm32(1), &skip);
6965 Address newTargetSrc(src, 0);
6966 Address newTargetDest(src, -int32_t(sizeof(Value)));
6967 masm.copy64(newTargetSrc, newTargetDest, scratch);
6968 masm.bind(&skip);
6969 }
6970
6971 if (mustSpill) {
6972 masm.pop(calleeReg);
6973 }
6974
6975 // Loop to fill the remaining numMissing slots with UndefinedValue.
6976 // We do this last so that we can safely clobber numMissing.
6977 Label undefLoop;
6978 masm.bind(&undefLoop);
6979 BaseValueIndex undefSlot(dest, numMissing, -int32_t(sizeof(Value)));
6980 masm.storeValue(UndefinedValue(), undefSlot);
6981 masm.branchSub32(Assembler::NonZero, Imm32(1), numMissing, &undefLoop);
6982
6983 masm.bind(&noUnderflow);
6984}
6985
6986void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm,
6987 bool isConstructing) {
6988 Register calleeReg = IonGenericCallCalleeReg;
6989 Register argcReg = IonGenericCallArgcReg;
6990 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
6991 Register scratch = regs.takeAny();
6992 Register scratch2 = regs.takeAny();
6993 Register contextReg = regs.takeAny();
6994#ifndef JS_USE_LINK_REGISTER
6995 Register returnAddrReg = IonGenericCallReturnAddrReg;
6996#endif
6997
6998 // Push a value containing the callee, which will become argv[0].
6999 masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg);
7000
7001 // Load the callee address into calleeReg.
7002#ifdef JS_SIMULATOR
7003 masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg);
7004#else
7005 masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
7006 calleeReg);
7007#endif
7008
7009 // Load argv into scratch2.
7010 masm.moveStackPtrTo(scratch2);
7011
7012 // Push argc.
7013 masm.push(argcReg);
7014
7015 masm.loadJSContext(contextReg);
7016
7017 // Construct native exit frame. Note that unlike other cases in this
7018 // trampoline, this code does not use a tail call.
7019 masm.push(FrameDescriptor(FrameType::IonJS));
7020#ifdef JS_USE_LINK_REGISTER
7021 masm.pushReturnAddress();
7022#else
7023 masm.push(returnAddrReg);
7024#endif
7025
7026 masm.push(FramePointer);
7027 masm.moveStackPtrTo(FramePointer);
7028 masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing);
7029
7030 masm.setupUnalignedABICall(scratch);
7031 masm.passABIArg(contextReg); // cx
7032 masm.passABIArg(argcReg); // argc
7033 masm.passABIArg(scratch2); // argv
7034
7035 masm.callWithABI(calleeReg);
7036
7037 // Test for failure.
7038 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
7039
7040 masm.loadValue(
7041 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
7042 JSReturnOperand);
7043
7044 // Leave the exit frame.
7045 masm.moveToStackPtr(FramePointer);
7046 masm.pop(FramePointer);
7047
7048 // Return.
7049 masm.ret();
7050}
7051
7052void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm,
7053 Label* entry, Label* vmCall) {
7054 Register calleeReg = IonGenericCallCalleeReg;
7055 Register argcReg = IonGenericCallArgcReg;
7056 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
7057 Register scratch = regs.takeAny();
7058 Register scratch2 = regs.takeAny();
7059 Register scratch3 = regs.takeAny();
7060
7061 Label notFunCall;
7062 masm.branchPtr(Assembler::NotEqual,
7063 Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
7064 ImmPtr(js::fun_call), &notFunCall);
7065
7066 // In general, we can implement fun_call by replacing calleeReg with
7067 // |this|, sliding all the other arguments down, and decrementing argc.
7068 //
7069 // *BEFORE* *AFTER*
7070 // [argN] argc = N+1 <padding>
7071 // ... [argN] argc = N
7072 // [arg1] ...
7073 // [arg0] [arg1] <- now arg0
7074 // [this] <- top of stack (aligned) [arg0] <- now this
7075 //
7076 // The only exception is when argc is already 0, in which case instead
7077 // of shifting arguments down we replace [this] with UndefinedValue():
7078 //
7079 // *BEFORE* *AFTER*
7080 // [this] argc = 0 [undef] argc = 0
7081 //
7082 // After making this transformation, we can jump back to the beginning
7083 // of this trampoline to handle the inner call.
7084
7085 // Guard that |this| is an object. If it is, replace calleeReg.
7086 masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall);
7087 masm.movePtr(scratch, calleeReg);
7088
7089 Label hasArgs;
7090 masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs);
7091
7092 // No arguments. Replace |this| with |undefined| and start from the top.
7093 masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0));
7094 masm.jump(entry);
7095
7096 masm.bind(&hasArgs);
7097
7098 Label doneSliding;
7099 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
7100 scratch3, &doneSliding);
7101 masm.bind(&doneSliding);
7102 masm.sub32(Imm32(1), argcReg);
7103
7104 masm.jump(entry);
7105
7106 masm.bind(&notFunCall);
7107}
7108
7109void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm,
7110 Label* entry,
7111 Label* vmCall) {
7112 Register calleeReg = IonGenericCallCalleeReg;
7113 Register argcReg = IonGenericCallArgcReg;
7114 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
7115 Register scratch = regs.takeAny();
7116 Register scratch2 = regs.takeAny();
7117 Register scratch3 = regs.takeAny();
7118
7119 masm.branchTestObjClass(Assembler::NotEqual, calleeReg,
7120 &BoundFunctionObject::class_, scratch, calleeReg,
7121 vmCall);
7122
7123 Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
7124 Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot());
7125 Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot());
7126 Address firstInlineArgSlot(
7127 calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg());
7128
7129 // Check that we won't be pushing too many arguments.
7130 masm.load32(flagsSlot, scratch);
7131 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
7132 masm.add32(argcReg, scratch);
7133 masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall);
7134
7135 // The stack is currently correctly aligned for a jit call. We will
7136 // be updating the `this` value and potentially adding additional
7137 // arguments. On platforms with 16-byte alignment, if the number of
7138 // bound arguments is odd, we have to move the arguments that are
7139 // currently on the stack. For example, with one bound argument:
7140 //
7141 // *BEFORE* *AFTER*
7142 // [argN] <padding>
7143 // ... [argN] |
7144 // [arg1] ... | These arguments have been
7145 // [arg0] [arg1] | shifted down 8 bytes.
7146 // [this] <- top of stack (aligned) [arg0] v
7147 // [bound0] <- one bound argument (odd)
7148 // [boundThis] <- top of stack (aligned)
7149 //
7150 Label poppedThis;
7151 if (JitStackValueAlignment > 1) {
7152 Label alreadyAligned;
7153 masm.branchTest32(Assembler::Zero, flagsSlot,
7154 Imm32(1 << BoundFunctionObject::NumBoundArgsShift),
7155 &alreadyAligned);
7156
7157 // We have an odd number of bound arguments. Shift the existing arguments
7158 // down by 8 bytes.
7159 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
7160 scratch3, &poppedThis);
7161 masm.bind(&alreadyAligned);
7162 }
7163
7164 // Pop the current `this`. It will be replaced with the bound `this`.
7165 masm.freeStack(sizeof(Value));
7166 masm.bind(&poppedThis);
7167
7168 // Load the number of bound arguments in scratch
7169 masm.load32(flagsSlot, scratch);
7170 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
7171
7172 Label donePushingBoundArguments;
7173 masm.branch32(Assembler::Equal, scratch, Imm32(0),
7174 &donePushingBoundArguments);
7175
7176 // Update argc to include bound arguments.
7177 masm.add32(scratch, argcReg);
7178
7179 // Load &boundArgs[0] in scratch2.
7180 Label outOfLineBoundArguments, haveBoundArguments;
7181 masm.branch32(Assembler::Above, scratch,
7182 Imm32(BoundFunctionObject::MaxInlineBoundArgs),
7183 &outOfLineBoundArguments);
7184 masm.computeEffectiveAddress(firstInlineArgSlot, scratch2);
7185 masm.jump(&haveBoundArguments);
7186
7187 masm.bind(&outOfLineBoundArguments);
7188 masm.unboxObject(firstInlineArgSlot, scratch2);
7189 masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
7190
7191 masm.bind(&haveBoundArguments);
7192
7193 // Load &boundArgs[numBoundArgs] in scratch.
7194 BaseObjectElementIndex lastBoundArg(scratch2, scratch);
7195 masm.computeEffectiveAddress(lastBoundArg, scratch);
7196
7197 // Push the bound arguments, starting with the last one.
7198 // Copying pre-decrements scratch until scratch2 is reached.
7199 Label boundArgumentsLoop;
7200 masm.bind(&boundArgumentsLoop);
7201 masm.subPtr(Imm32(sizeof(Value)), scratch);
7202 masm.pushValue(Address(scratch, 0));
7203 masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop);
7204 masm.bind(&donePushingBoundArguments);
7205
7206 // Push the bound `this`.
7207 masm.pushValue(thisSlot);
7208
7209 // Load the target in calleeReg.
7210 masm.unboxObject(targetSlot, calleeReg);
7211
7212 // At this point, all preconditions for entering the trampoline are met:
7213 // - calleeReg contains a pointer to the callee object
7214 // - argcReg contains the number of actual args (now including bound args)
7215 // - the arguments are on the stack with the correct alignment.
7216 // Instead of generating more code, we can jump back to the entry point
7217 // of the trampoline to call the bound target.
7218 masm.jump(entry);
7219}
7220
7221void CodeGenerator::visitCallKnown(LCallKnown* call) {
7222 Register calleereg = ToRegister(call->getFunction());
7223 Register objreg = ToRegister(call->getTempObject());
7224 uint32_t unusedStack =
7225 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
7226 WrappedFunction* target = call->getSingleTarget();
7227
7228 // Native single targets (except Wasm and TrampolineNative functions) are
7229 // handled by LCallNative.
7230 MOZ_ASSERT(target->hasJitEntry())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target->hasJitEntry())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(target->hasJitEntry()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitEntry()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7230); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->hasJitEntry()" ")"); do { MOZ_CrashSequence
(__null, 7230); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7231
7232 // Missing arguments must have been explicitly appended by WarpBuilder.
7233 DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
7234 MOZ_ASSERT(target->nargs() <=do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target->nargs() <= call->mir()->numStackArgs
() - numNonArgsOnStack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(target->nargs() <= call
->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7235); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { MOZ_CrashSequence(__null, 7235); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7235 call->mir()->numStackArgs() - numNonArgsOnStack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target->nargs() <= call->mir()->numStackArgs
() - numNonArgsOnStack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(target->nargs() <= call
->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7235); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { MOZ_CrashSequence(__null, 7235); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7236
7237 MOZ_ASSERT_IF(call->isConstructing(), target->isConstructor())do { if (call->isConstructing()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(target->isConstructor
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(target->isConstructor()))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("target->isConstructor()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7237); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->isConstructor()" ")"); do { MOZ_CrashSequence
(__null, 7237); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
7238
7239 masm.checkStackAlignment();
7240
7241 if (target->isClassConstructor() && !call->isConstructing()) {
7242 emitCallInvokeFunction(call, calleereg, call->isConstructing(),
7243 call->ignoresReturnValue(), call->numActualArgs(),
7244 unusedStack);
7245 return;
7246 }
7247
7248 MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing())do { if (target->isClassConstructor()) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(call->
isConstructing())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(call->isConstructing())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("call->isConstructing()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7248); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "call->isConstructing()" ")"); do { MOZ_CrashSequence
(__null, 7248); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
7249
7250 MOZ_ASSERT(!call->mir()->needsThisCheck())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!call->mir()->needsThisCheck())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!call->mir()->needsThisCheck
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!call->mir()->needsThisCheck()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 7250); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()"
")"); do { MOZ_CrashSequence(__null, 7250); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7251
7252 if (call->mir()->maybeCrossRealm()) {
7253 masm.switchToObjectRealm(calleereg, objreg);
7254 }
7255
7256 masm.loadJitCodeRaw(calleereg, objreg);
7257
7258 // Nestle the StackPointer up to the argument vector.
7259 masm.freeStack(unusedStack);
7260
7261 // Construct the JitFrameLayout.
7262 masm.PushCalleeToken(calleereg, call->mir()->isConstructing());
7263 masm.Push(FrameDescriptor(FrameType::IonJS, call->numActualArgs()));
7264
7265 // Finally call the function in objreg.
7266 ensureOsiSpace();
7267 uint32_t callOffset = masm.callJit(objreg);
7268 markSafepointAt(callOffset, call);
7269
7270 if (call->mir()->maybeCrossRealm()) {
7271 static_assert(!JSReturnOperand.aliases(ReturnReg),
7272 "ReturnReg available as scratch after scripted calls");
7273 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
7274 }
7275
7276 // Restore stack pointer: pop JitFrameLayout fields still left on the stack
7277 // and undo the earlier |freeStack(unusedStack)|.
7278 int prefixGarbage =
7279 sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall();
7280 masm.adjustStack(prefixGarbage - unusedStack);
7281
7282 // If the return value of the constructing function is Primitive,
7283 // replace the return value with the Object from CreateThis.
7284 if (call->mir()->isConstructing()) {
7285 Label notPrimitive;
7286 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7287 &notPrimitive);
7288 masm.loadValue(Address(masm.getStackPointer(), unusedStack),
7289 JSReturnOperand);
7290#ifdef DEBUG1
7291 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7292 &notPrimitive);
7293 masm.assumeUnreachable("CreateThis creates an object");
7294#endif
7295 masm.bind(&notPrimitive);
7296 }
7297}
7298
7299template <typename T>
7300void CodeGenerator::emitCallInvokeFunction(T* apply) {
7301 pushArg(masm.getStackPointer()); // argv.
7302 pushArg(ToRegister(apply->getArgc())); // argc.
7303 pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue.
7304 pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing.
7305 pushArg(ToRegister(apply->getFunction())); // JSFunction*.
7306
7307 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
7308 MutableHandleValue);
7309 callVM<Fn, jit::InvokeFunction>(apply);
7310}
7311
7312// Do not bailout after the execution of this function since the stack no longer
7313// corresponds to what is expected by the snapshots.
7314template <typename T>
7315void CodeGenerator::emitAllocateSpaceForApply(T* apply, Register calleeReg,
7316 Register argcreg,
7317 Register scratch) {
7318 Label* oolRejoin = nullptr;
7319 bool canUnderflow =
7320 !apply->hasSingleTarget() || apply->getSingleTarget()->nargs() > 0;
7321
7322 if (canUnderflow) {
7323 auto* ool =
7324 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
7325 // Align the JitFrameLayout on the JitStackAlignment by allocating
7326 // callee->nargs() slots, possibly rounded up to the nearest odd
7327 // number (see below). Leave callee->nargs() in `scratch` for the
7328 // undef loop.
7329 if (apply->hasSingleTarget()) {
7330 uint32_t nargs = apply->getSingleTarget()->nargs();
7331 uint32_t numSlots = JitStackValueAlignment == 1 ? nargs : nargs | 1;
7332 masm.subFromStackPtr(Imm32((numSlots) * sizeof(Value)));
7333 masm.move32(Imm32(nargs), scratch);
7334 } else {
7335 // `scratch` contains callee->nargs()
7336 if (JitStackValueAlignment > 1) {
7337 masm.orPtr(Imm32(1), scratch);
7338 }
7339 masm.lshiftPtr(Imm32(ValueShift), scratch);
7340 masm.subFromStackPtr(scratch);
7341
7342 // We need callee->nargs in `scratch`. If we rounded it up
7343 // above, we need to reload it. If we only shifted it, we can
7344 // simply shift it back.
7345 if (JitStackValueAlignment > 1) {
7346 masm.loadFunctionArgCount(calleeReg, scratch);
7347 } else {
7348 masm.rshiftPtr(Imm32(ValueShift), scratch);
7349 }
7350 }
7351
7352 // Count from callee->nargs() down to argc, storing undefined values.
7353 Label loop;
7354 masm.bind(&loop);
7355 masm.sub32(Imm32(1), scratch);
7356 masm.storeValue(UndefinedValue(),
7357 BaseValueIndex(masm.getStackPointer(), scratch));
7358 masm.branch32(Assembler::Above, scratch, argcreg, &loop);
7359 masm.jump(ool.rejoin());
7360 });
7361 addOutOfLineCode(ool, apply->mir());
7362 oolRejoin = ool->rejoin();
7363
7364 Label noUnderflow;
7365 if (apply->hasSingleTarget()) {
7366 masm.branch32(Assembler::AboveOrEqual, argcreg,
7367 Imm32(apply->getSingleTarget()->nargs()), &noUnderflow);
7368 } else {
7369 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
7370 calleeReg, &noUnderflow);
7371 masm.loadFunctionArgCount(calleeReg, scratch);
7372 masm.branch32(Assembler::AboveOrEqual, argcreg, scratch, &noUnderflow);
7373 }
7374 masm.branchIfFunctionHasJitEntry(calleeReg, ool->entry());
7375 masm.bind(&noUnderflow);
7376 }
7377
7378 // Use scratch register to calculate stack space (including padding).
7379 masm.movePtr(argcreg, scratch);
7380
7381 // Align the JitFrameLayout on the JitStackAlignment.
7382 if (JitStackValueAlignment > 1) {
7383 MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 7384); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7384); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
7384 "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 7384); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7384); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7385 MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7385); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence
(__null, 7385); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7386 // If the number of arguments is odd, then we do not need any padding.
7387 //
7388 // Note: The |JitStackValueAlignment == 2| condition requires that the
7389 // overall number of values on the stack is even. When we have an odd number
7390 // of arguments, we don't need any padding, because the |thisValue| is
7391 // pushed after the arguments, so the overall number of values on the stack
7392 // is even.
7393 //
7394 // We can align by unconditionally setting the low bit. If the number of
7395 // arguments is odd, the low bit was already set, so this adds no padding.
7396 // If the number of arguments is even, the low bit was not set, so this adds
7397 // 1, as we require.
7398 masm.orPtr(Imm32(1), scratch);
7399 }
7400
7401 // Reserve space for copying the arguments.
7402 NativeObject::elementsSizeMustNotOverflow();
7403 masm.lshiftPtr(Imm32(ValueShift), scratch);
7404 masm.subFromStackPtr(scratch);
7405
7406#ifdef DEBUG1
7407 // Put a magic value in the space reserved for padding. Note, this code cannot
7408 // be merged with the previous test, as not all architectures can write below
7409 // their stack pointers.
7410 if (JitStackValueAlignment > 1) {
7411 MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7411); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence
(__null, 7411); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7412 Label noPaddingNeeded;
7413 // If the number of arguments is odd, then we do not need any padding.
7414 masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
7415 BaseValueIndex dstPtr(masm.getStackPointer(), argcreg);
7416 masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr);
7417 masm.bind(&noPaddingNeeded);
7418 }
7419#endif
7420
7421 if (canUnderflow) {
7422 masm.bind(oolRejoin);
7423 }
7424}
7425
7426// Do not bailout after the execution of this function since the stack no longer
7427// corresponds to what is expected by the snapshots.
7428template <typename T>
7429void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget(
7430 T* construct, Register calleeReg, Register argcreg,
7431 Register newTargetAndScratch) {
7432 // Push newTarget.
7433 masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch);
7434 if (JitStackValueAlignment > 1) {
7435 // x86 is short on registers. To free up newTarget for use as a scratch
7436 // register before we know if we need padding, we push newTarget twice.
7437 // If the first copy pushed is correctly aligned, we will overwrite the
7438 // second. If the second copy is correctly aligned, the first is padding.
7439 masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch);
7440 }
7441 Register scratch = newTargetAndScratch;
7442
7443 Label* oolRejoin = nullptr;
7444 bool canUnderflow = !construct->hasSingleTarget() ||
7445 construct->getSingleTarget()->nargs() > 0;
7446 if (canUnderflow) {
7447 auto* ool =
7448 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
7449 // Align the JitFrameLayout on the JitStackAlignment by allocating
7450 // callee->nargs() slots, rounded down to the nearest odd number (see
7451 // below). Leave callee->nargs() in `scratch` for the undef loop.
7452 if (construct->hasSingleTarget()) {
7453 uint32_t nargs = construct->getSingleTarget()->nargs();
7454 uint32_t numSlots =
7455 JitStackValueAlignment == 1 ? nargs : ((nargs + 1) & ~1) - 1;
7456 masm.subFromStackPtr(Imm32((numSlots) * sizeof(Value)));
7457 masm.move32(Imm32(nargs), scratch);
7458 } else {
7459 // `scratch` contains callee->nargs()
7460 if (JitStackValueAlignment > 1) {
7461 // Round down to nearest odd number.
7462 masm.addPtr(Imm32(1), scratch);
7463 masm.andPtr(Imm32(~1), scratch);
7464 masm.subPtr(Imm32(1), scratch);
7465 }
7466 masm.lshiftPtr(Imm32(ValueShift), scratch);
7467 masm.subFromStackPtr(scratch);
7468
7469 // We need callee->nargs in `scratch`. If we rounded it down
7470 // above, we need to reload it. If we only shifted it, we can
7471 // simply shift it back.
7472 if (JitStackValueAlignment > 1) {
7473 masm.loadFunctionArgCount(calleeReg, scratch);
7474 } else {
7475 masm.rshiftPtr(Imm32(ValueShift), scratch);
7476 }
7477 }
7478
7479 // Count from callee->nargs() down to argc, storing undefined values.
7480 Label loop;
7481 masm.bind(&loop);
7482 masm.sub32(Imm32(1), scratch);
7483 masm.storeValue(UndefinedValue(),
7484 BaseValueIndex(masm.getStackPointer(), scratch));
7485 masm.branch32(Assembler::Above, scratch, argcreg, &loop);
7486 masm.jump(ool.rejoin());
7487 });
7488 addOutOfLineCode(ool, construct->mir());
7489 oolRejoin = ool->rejoin();
7490
7491 Label noUnderflow;
7492 if (construct->hasSingleTarget()) {
7493 masm.branch32(Assembler::AboveOrEqual, argcreg,
7494 Imm32(construct->getSingleTarget()->nargs()), &noUnderflow);
7495 } else {
7496 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
7497 calleeReg, &noUnderflow);
7498 masm.loadFunctionArgCount(calleeReg, scratch);
7499 masm.branch32(Assembler::AboveOrEqual, argcreg, scratch, &noUnderflow);
7500 }
7501 masm.branchIfFunctionHasJitEntry(calleeReg, ool->entry());
7502 masm.bind(&noUnderflow);
7503 }
7504
7505 // Use newTargetAndScratch to calculate stack space (including padding).
7506 masm.movePtr(argcreg, newTargetAndScratch);
7507
7508 // Align the JitFrameLayout on the JitStackAlignment.
7509 if (JitStackValueAlignment > 1) {
7510 MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 7511); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7511); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
7511 "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 7511); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7511); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7512 MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7512); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence
(__null, 7512); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7513 // Note: The |JitStackValueAlignment == 2| condition requires that the
7514 // overall number of values on the stack is even. We must push `newTarget`,
7515 // the args, and `this`. We've already pushed newTarget twice. Rounding
7516 // argc down to the closest odd number will give us the correct alignment:
7517 //
7518 // argc: *0* *1* *2* *3*
7519 // rounds to: -1 1 1 3
7520 // *newTarget (newTarget) newTarget (newTarget)
7521 // curr sp --> this newTarget arg1 newTarget
7522 // *arg0 *arg0 arg2
7523 // this this arg1
7524 // *arg0
7525 // this
7526 // The asterisk in each column marks the stack pointer after adding
7527 // the rounded value. In each case, pushing `this` will result in an
7528 // even number of total slots.
7529 masm.addPtr(Imm32(1), scratch);
7530 masm.andPtr(Imm32(~1), scratch);
7531 masm.subPtr(Imm32(1), scratch);
7532 }
7533
7534 // Reserve space for copying the arguments.
7535 NativeObject::elementsSizeMustNotOverflow();
7536 masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch);
7537 masm.subFromStackPtr(newTargetAndScratch);
7538
7539 if (canUnderflow) {
7540 masm.bind(oolRejoin);
7541 }
7542}
7543
7544// Destroys argvIndex and copyreg.
7545void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase,
7546 Register argvIndex, Register copyreg,
7547 size_t argvSrcOffset,
7548 size_t argvDstOffset) {
7549 Label loop;
7550 masm.bind(&loop);
7551
7552 // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop
7553 // back, we have to substract the size of the word which are copied.
7554 BaseValueIndex srcPtr(argvSrcBase, argvIndex,
7555 int32_t(argvSrcOffset) - sizeof(void*));
7556 BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex,
7557 int32_t(argvDstOffset) - sizeof(void*));
7558 masm.loadPtr(srcPtr, copyreg);
7559 masm.storePtr(copyreg, dstPtr);
7560
7561 // Handle 32 bits architectures.
7562 if (sizeof(Value) == 2 * sizeof(void*)) {
7563 BaseValueIndex srcPtrLow(argvSrcBase, argvIndex,
7564 int32_t(argvSrcOffset) - 2 * sizeof(void*));
7565 BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex,
7566 int32_t(argvDstOffset) - 2 * sizeof(void*));
7567 masm.loadPtr(srcPtrLow, copyreg);
7568 masm.storePtr(copyreg, dstPtrLow);
7569 }
7570
7571 masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop);
7572}
7573
7574void CodeGenerator::emitRestoreStackPointerFromFP() {
7575 // This is used to restore the stack pointer after a call with a dynamic
7576 // number of arguments.
7577
7578 MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("masm.framePushed() == frameSize()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 7578); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 7578); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7579
7580 int32_t offset = -int32_t(frameSize());
7581 masm.computeEffectiveAddress(Address(FramePointer, offset),
7582 masm.getStackPointer());
7583#if JS_CODEGEN_ARM64
7584 masm.syncStackPtr();
7585#endif
7586}
7587
7588void CodeGenerator::emitPushArguments(Register argcreg, Register scratch,
7589 Register copyreg, uint32_t extraFormals) {
7590 Label end;
7591
7592 // Skip the copy of arguments if there are none.
7593 masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
7594
7595 // clang-format off
7596 //
7597 // We are making a copy of the arguments which are above the JitFrameLayout
7598 // of the current Ion frame.
7599 //
7600 // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst
7601 //
7602 // clang-format on
7603
7604 // Compute the source and destination offsets into the stack.
7605 //
7606 // The |extraFormals| parameter is used when copying rest-parameters and
7607 // allows to skip the initial parameters before the actual rest-parameters.
7608 Register argvSrcBase = FramePointer;
7609 size_t argvSrcOffset =
7610 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
7611 size_t argvDstOffset = 0;
7612
7613 Register argvIndex = scratch;
7614 masm.move32(argcreg, argvIndex);
7615
7616 // Copy arguments.
7617 emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset,
7618 argvDstOffset);
7619
7620 // Join with all arguments copied.
7621 masm.bind(&end);
7622}
7623
7624void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) {
7625 // Holds the function nargs.
7626 Register funcreg = ToRegister(apply->getFunction());
7627 Register argcreg = ToRegister(apply->getArgc());
7628 Register copyreg = ToRegister(apply->getTempObject());
7629 Register scratch = ToRegister(apply->getTempForArgCopy());
7630 uint32_t extraFormals = apply->numExtraFormals();
7631
7632 // Allocate space on the stack for arguments.
7633 emitAllocateSpaceForApply(apply, funcreg, argcreg, scratch);
7634
7635 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
7636
7637 // Push |this|.
7638 masm.pushValue(ToValue(apply->thisValue()));
7639}
7640
7641void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) {
7642 Register function = ToRegister(apply->getFunction());
7643 Register argsObj = ToRegister(apply->getArgsObj());
7644 Register tmpArgc = ToRegister(apply->getTempObject());
7645 Register scratch = ToRegister(apply->getTempForArgCopy());
7646
7647 // argc and argsObj are mapped to the same calltemp register.
7648 MOZ_ASSERT(argsObj == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argsObj == ToRegister(apply->getArgc()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argsObj == ToRegister(apply->getArgc())))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argsObj == ToRegister(apply->getArgc())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7648); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())"
")"); do { MOZ_CrashSequence(__null, 7648); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7649
7650 // Load argc into tmpArgc.
7651 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
7652
7653 // Allocate space on the stack for arguments.
7654 emitAllocateSpaceForApply(apply, function, tmpArgc, scratch);
7655
7656 // Load arguments data.
7657 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
7658 argsObj);
7659 size_t argsSrcOffset = ArgumentsData::offsetOfArgs();
7660
7661 // This is the end of the lifetime of argsObj.
7662 // After this call, the argsObj register holds the argument count instead.
7663 emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset);
7664
7665 // Push |this|.
7666 masm.pushValue(ToValue(apply->thisValue()));
7667}
7668
7669void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc,
7670 Register srcBaseAndArgc,
7671 Register scratch,
7672 size_t argvSrcOffset) {
7673 // Preconditions:
7674 // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of
7675 // the stack to hold arguments.
7676 // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values.
7677 //
7678 // Postconditions:
7679 // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into
7680 // the allocated space.
7681 // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|.
7682 //
7683 // |scratch| is used as a temp register within this function and clobbered.
7684
7685 Label noCopy, epilogue;
7686
7687 // Skip the copy of arguments if there are none.
7688 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
7689 {
7690 // Copy the values. This code is skipped entirely if there are no values.
7691 size_t argvDstOffset = 0;
7692
7693 Register argvSrcBase = srcBaseAndArgc;
7694
7695 // Stash away |tmpArgc| and adjust argvDstOffset accordingly.
7696 masm.push(tmpArgc);
7697 Register argvIndex = tmpArgc;
7698 argvDstOffset += sizeof(void*);
7699
7700 // Copy
7701 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
7702 argvDstOffset);
7703
7704 // Restore.
7705 masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc.
7706 masm.jump(&epilogue);
7707 }
7708 masm.bind(&noCopy);
7709 {
7710 // Clear argc if we skipped the copy step.
7711 masm.movePtr(ImmWord(0), srcBaseAndArgc);
7712 }
7713
7714 // Join with all arguments copied.
7715 // Note, "srcBase" has become "argc".
7716 masm.bind(&epilogue);
7717}
7718
7719void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) {
7720 Register function = ToRegister(apply->getFunction());
7721 Register elements = ToRegister(apply->getElements());
7722 Register tmpArgc = ToRegister(apply->getTempObject());
7723 Register scratch = ToRegister(apply->getTempForArgCopy());
7724
7725 // argc and elements are mapped to the same calltemp register.
7726 MOZ_ASSERT(elements == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(elements == ToRegister(apply->getArgc()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(elements == ToRegister(apply->getArgc())))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("elements == ToRegister(apply->getArgc())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7726); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())"
")"); do { MOZ_CrashSequence(__null, 7726); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7727
7728 // Invariants guarded in the caller:
7729 // - the array is not too long
7730 // - the array length equals its initialized length
7731
7732 // The array length is our argc for the purposes of allocating space.
7733 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7734
7735 // Allocate space for the values.
7736 emitAllocateSpaceForApply(apply, function, tmpArgc, scratch);
7737
7738 // After this call "elements" has become "argc".
7739 size_t elementsOffset = 0;
7740 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
7741
7742 // Push |this|.
7743 masm.pushValue(ToValue(apply->thisValue()));
7744}
7745
7746void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) {
7747 // Holds the function nargs.
7748 Register argcreg = ToRegister(construct->getArgc());
7749 Register function = ToRegister(construct->getFunction());
7750 Register copyreg = ToRegister(construct->getTempObject());
7751 Register scratch = ToRegister(construct->getTempForArgCopy());
7752 uint32_t extraFormals = construct->numExtraFormals();
7753
7754 // newTarget and scratch are mapped to the same calltemp register.
7755 MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scratch == ToRegister(construct->getNewTarget()))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(scratch == ToRegister(construct->getNewTarget()))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7755); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { MOZ_CrashSequence(__null, 7755); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7756
7757 // Allocate space for the values.
7758 // After this call "newTarget" has become "scratch".
7759 emitAllocateSpaceForConstructAndPushNewTarget(construct, function, argcreg,
7760 scratch);
7761
7762 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
7763
7764 // Push |this|.
7765 masm.pushValue(ToValue(construct->thisValue()));
7766}
7767
7768void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) {
7769 Register function = ToRegister(construct->getFunction());
7770 Register elements = ToRegister(construct->getElements());
7771 Register tmpArgc = ToRegister(construct->getTempObject());
7772 Register scratch = ToRegister(construct->getTempForArgCopy());
7773
7774 // argc and elements are mapped to the same calltemp register.
7775 MOZ_ASSERT(elements == ToRegister(construct->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(elements == ToRegister(construct->getArgc()))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(elements == ToRegister(construct->getArgc())))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("elements == ToRegister(construct->getArgc())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7775); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())"
")"); do { MOZ_CrashSequence(__null, 7775); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7776
7777 // newTarget and scratch are mapped to the same calltemp register.
7778 MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scratch == ToRegister(construct->getNewTarget()))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(scratch == ToRegister(construct->getNewTarget()))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7778); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { MOZ_CrashSequence(__null, 7778); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7779
7780 // Invariants guarded in the caller:
7781 // - the array is not too long
7782 // - the array length equals its initialized length
7783
7784 // The array length is our argc for the purposes of allocating space.
7785 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7786
7787 // Allocate space for the values.
7788 // After this call "newTarget" has become "scratch".
7789 emitAllocateSpaceForConstructAndPushNewTarget(construct, function, tmpArgc,
7790 scratch);
7791
7792 // After this call "elements" has become "argc".
7793 size_t elementsOffset = 0;
7794 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
7795
7796 // Push |this|.
7797 masm.pushValue(ToValue(construct->thisValue()));
7798}
7799
7800template <typename T>
7801void CodeGenerator::emitApplyGeneric(T* apply) {
7802 // Holds the function object.
7803 Register calleereg = ToRegister(apply->getFunction());
7804
7805 // Temporary register for modifying the function object.
7806 Register objreg = ToRegister(apply->getTempObject());
7807 Register scratch = ToRegister(apply->getTempForArgCopy());
7808
7809 // Holds the function nargs, computed in the invoker or (for ApplyArray,
7810 // ConstructArray, or ApplyArgsObj) in the argument pusher.
7811 Register argcreg = ToRegister(apply->getArgc());
7812
7813 // Copy the arguments of the current function.
7814 //
7815 // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute
7816 // argc. The argc register and the elements/argsObj register are the same;
7817 // argc must not be referenced before the call to emitPushArguments() and
7818 // elements/argsObj must not be referenced after it returns.
7819 //
7820 // In the case of ConstructArray or ConstructArgs, also overwrite newTarget;
7821 // newTarget must not be referenced after this point.
7822 //
7823 // objreg is dead across this call.
7824 emitPushArguments(apply);
7825
7826 masm.checkStackAlignment();
7827
7828 bool constructing = apply->mir()->isConstructing();
7829
7830 // If the function is native, the call is compiled through emitApplyNative.
7831 MOZ_ASSERT_IF(apply->hasSingleTarget(),do { if (apply->hasSingleTarget()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(!apply->getSingleTarget
()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget()
->isNativeWithoutJitEntry()))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7832); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 7832); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
7832 !apply->getSingleTarget()->isNativeWithoutJitEntry())do { if (apply->hasSingleTarget()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(!apply->getSingleTarget
()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget()
->isNativeWithoutJitEntry()))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7832); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 7832); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
7833
7834 Label end, invoke;
7835
7836 // Unless already known, guard that calleereg is actually a function object.
7837 if (!apply->hasSingleTarget()) {
7838 masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg,
7839 calleereg, &invoke);
7840 }
7841
7842 // Guard that calleereg is an interpreted function with a JSScript.
7843 masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke);
7844
7845 // Guard that callee allows the [[Call]] or [[Construct]] operation required.
7846 if (constructing) {
7847 masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR,
7848 Assembler::Zero, &invoke);
7849 } else {
7850 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
7851 calleereg, objreg, &invoke);
7852 }
7853
7854 // Use the slow path if CreateThis was unable to create the |this| object.
7855 if (constructing) {
7856 Address thisAddr(masm.getStackPointer(), 0);
7857 masm.branchTestNull(Assembler::Equal, thisAddr, &invoke);
7858 }
7859
7860 // Call with an Ion frame
7861 {
7862 if (apply->mir()->maybeCrossRealm()) {
7863 masm.switchToObjectRealm(calleereg, objreg);
7864 }
7865
7866 // Knowing that calleereg is a non-native function, load jitcode.
7867 masm.loadJitCodeRaw(calleereg, objreg);
7868
7869 masm.PushCalleeToken(calleereg, constructing);
7870 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch);
7871
7872 // Call the function.
7873 ensureOsiSpace();
7874 uint32_t callOffset = masm.callJit(objreg);
7875 markSafepointAt(callOffset, apply);
7876
7877 if (apply->mir()->maybeCrossRealm()) {
7878 static_assert(!JSReturnOperand.aliases(ReturnReg),
7879 "ReturnReg available as scratch after scripted calls");
7880 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
7881 }
7882
7883 // Discard JitFrameLayout fields still left on the stack.
7884 masm.freeStack(sizeof(JitFrameLayout) -
7885 JitFrameLayout::bytesPoppedAfterCall());
7886 masm.jump(&end);
7887 }
7888
7889 // Handle uncompiled or native functions.
7890 {
7891 masm.bind(&invoke);
7892 emitCallInvokeFunction(apply);
7893 }
7894
7895 masm.bind(&end);
7896
7897 // If the return value of the constructing function is Primitive, replace the
7898 // return value with the Object from CreateThis.
7899 if (constructing) {
7900 Label notPrimitive;
7901 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7902 &notPrimitive);
7903 masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand);
7904
7905#ifdef DEBUG1
7906 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7907 &notPrimitive);
7908 masm.assumeUnreachable("CreateThis creates an object");
7909#endif
7910
7911 masm.bind(&notPrimitive);
7912 }
7913
7914 // Pop arguments and continue.
7915 emitRestoreStackPointerFromFP();
7916}
7917
7918template <typename T>
7919void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) {
7920 static_assert(JitStackAlignment % ABIStackAlignment == 0,
7921 "aligning on JIT stack subsumes ABI alignment");
7922
7923 // Align the arguments on the JitStackAlignment.
7924 if (JitStackValueAlignment > 1) {
7925 MOZ_ASSERT(JitStackValueAlignment == 2,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2"
" (" "Stack padding adds exactly one Value" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 7926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { MOZ_CrashSequence
(__null, 7926); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
7926 "Stack padding adds exactly one Value")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2"
" (" "Stack padding adds exactly one Value" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 7926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { MOZ_CrashSequence
(__null, 7926); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7927 MOZ_ASSERT(frameSize() % JitStackValueAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameSize() % JitStackValueAlignment == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0"
" (" "Stack padding assumes that the frameSize is correct" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7928); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { MOZ_CrashSequence(__null, 7928); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7928 "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameSize() % JitStackValueAlignment == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0"
" (" "Stack padding assumes that the frameSize is correct" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 7928); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { MOZ_CrashSequence(__null, 7928); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7929
7930 Assembler::Condition cond;
7931 if constexpr (T::isConstructing()) {
7932 // If the number of arguments is even, then we do not need any padding.
7933 //
7934 // Also see emitAllocateSpaceForApply().
7935 cond = Assembler::Zero;
7936 } else {
7937 // If the number of arguments is odd, then we do not need any padding.
7938 //
7939 // Also see emitAllocateSpaceForConstructAndPushNewTarget().
7940 cond = Assembler::NonZero;
7941 }
7942
7943 Label noPaddingNeeded;
7944 masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded);
7945 masm.pushValue(MagicValue(JS_ARG_POISON));
7946 masm.bind(&noPaddingNeeded);
7947 }
7948}
7949
7950template <typename T>
7951void CodeGenerator::emitPushNativeArguments(T* apply) {
7952 Register argc = ToRegister(apply->getArgc());
7953 Register tmpArgc = ToRegister(apply->getTempObject());
7954 Register scratch = ToRegister(apply->getTempForArgCopy());
7955 uint32_t extraFormals = apply->numExtraFormals();
7956
7957 // Align stack.
7958 emitAlignStackForApplyNative(apply, argc);
7959
7960 // Push newTarget.
7961 if constexpr (T::isConstructing()) {
7962 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
7963 }
7964
7965 // Push arguments.
7966 Label noCopy;
7967 masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy);
7968 {
7969 // Use scratch register to calculate stack space.
7970 masm.movePtr(argc, scratch);
7971
7972 // Reserve space for copying the arguments.
7973 NativeObject::elementsSizeMustNotOverflow();
7974 masm.lshiftPtr(Imm32(ValueShift), scratch);
7975 masm.subFromStackPtr(scratch);
7976
7977 // Compute the source and destination offsets into the stack.
7978 Register argvSrcBase = FramePointer;
7979 size_t argvSrcOffset =
7980 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
7981 size_t argvDstOffset = 0;
7982
7983 Register argvIndex = tmpArgc;
7984 masm.move32(argc, argvIndex);
7985
7986 // Copy arguments.
7987 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
7988 argvDstOffset);
7989 }
7990 masm.bind(&noCopy);
7991
7992 // Push |this|.
7993 if constexpr (T::isConstructing()) {
7994 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
7995 } else {
7996 masm.pushValue(ToValue(apply->thisValue()));
7997 }
7998}
7999
8000template <typename T>
8001void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) {
8002 Register argc = ToRegister(apply->getArgc());
8003 Register elements = ToRegister(apply->getElements());
8004 Register tmpArgc = ToRegister(apply->getTempObject());
8005 Register scratch = ToRegister(apply->getTempForArgCopy());
8006
8007 // NB: argc and elements are mapped to the same register.
8008 MOZ_ASSERT(argc == elements)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argc == elements)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(argc == elements))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("argc == elements"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8008); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc == elements" ")"); do { MOZ_CrashSequence
(__null, 8008); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8009
8010 // Invariants guarded in the caller:
8011 // - the array is not too long
8012 // - the array length equals its initialized length
8013
8014 // The array length is our argc.
8015 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
8016
8017 // Align stack.
8018 emitAlignStackForApplyNative(apply, tmpArgc);
8019
8020 // Push newTarget.
8021 if constexpr (T::isConstructing()) {
8022 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
8023 }
8024
8025 // Skip the copy of arguments if there are none.
8026 Label noCopy;
8027 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
8028 {
8029 // |tmpArgc| is off-by-one, so adjust the offset accordingly.
8030 BaseObjectElementIndex srcPtr(elements, tmpArgc,
8031 -int32_t(sizeof(JS::Value)));
8032
8033 Label loop;
8034 masm.bind(&loop);
8035 masm.pushValue(srcPtr, scratch);
8036 masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop);
8037 }
8038 masm.bind(&noCopy);
8039
8040 // Set argc in preparation for calling the native function.
8041 masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc);
8042
8043 // Push |this|.
8044 if constexpr (T::isConstructing()) {
8045 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
8046 } else {
8047 masm.pushValue(ToValue(apply->thisValue()));
8048 }
8049}
8050
8051void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) {
8052 emitPushNativeArguments(apply);
8053}
8054
8055void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) {
8056 emitPushArrayAsNativeArguments(apply);
8057}
8058
8059void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) {
8060 emitPushNativeArguments(construct);
8061}
8062
8063void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) {
8064 emitPushArrayAsNativeArguments(construct);
8065}
8066
8067void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) {
8068 Register argc = ToRegister(apply->getArgc());
8069 Register argsObj = ToRegister(apply->getArgsObj());
8070 Register tmpArgc = ToRegister(apply->getTempObject());
8071 Register scratch = ToRegister(apply->getTempForArgCopy());
8072 Register scratch2 = ToRegister(apply->getTempExtra());
8073
8074 // NB: argc and argsObj are mapped to the same register.
8075 MOZ_ASSERT(argc == argsObj)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argc == argsObj)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(argc == argsObj))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("argc == argsObj"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8075); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc == argsObj" ")"); do { MOZ_CrashSequence
(__null, 8075); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8076
8077 // Load argc into tmpArgc.
8078 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
8079
8080 // Align stack.
8081 emitAlignStackForApplyNative(apply, tmpArgc);
8082
8083 // Push arguments.
8084 Label noCopy, epilogue;
8085 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
8086 {
8087 // Use scratch register to calculate stack space.
8088 masm.movePtr(tmpArgc, scratch);
8089
8090 // Reserve space for copying the arguments.
8091 NativeObject::elementsSizeMustNotOverflow();
8092 masm.lshiftPtr(Imm32(ValueShift), scratch);
8093 masm.subFromStackPtr(scratch);
8094
8095 // Load arguments data.
8096 Register argvSrcBase = argsObj;
8097 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
8098 argvSrcBase);
8099 size_t argvSrcOffset = ArgumentsData::offsetOfArgs();
8100 size_t argvDstOffset = 0;
8101
8102 Register argvIndex = scratch2;
8103 masm.move32(tmpArgc, argvIndex);
8104
8105 // Copy the values.
8106 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
8107 argvDstOffset);
8108 }
8109 masm.bind(&noCopy);
8110
8111 // Set argc in preparation for calling the native function.
8112 masm.movePtr(tmpArgc, argc);
8113
8114 // Push |this|.
8115 masm.pushValue(ToValue(apply->thisValue()));
8116}
8117
8118template <typename T>
8119void CodeGenerator::emitApplyNative(T* apply) {
8120 MOZ_ASSERT(T::isConstructing() == apply->mir()->isConstructing(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(T::isConstructing() == apply->mir()->isConstructing
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(T::isConstructing() == apply->mir()->isConstructing
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("T::isConstructing() == apply->mir()->isConstructing()"
" (" "isConstructing condition must be consistent" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 8121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ MOZ_CrashSequence(__null, 8121); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
8121 "isConstructing condition must be consistent")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(T::isConstructing() == apply->mir()->isConstructing
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(T::isConstructing() == apply->mir()->isConstructing
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("T::isConstructing() == apply->mir()->isConstructing()"
" (" "isConstructing condition must be consistent" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 8121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ MOZ_CrashSequence(__null, 8121); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
8122
8123 WrappedFunction* target = apply->mir()->getSingleTarget();
8124 MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("target->isNativeWithoutJitEntry()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 8124); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 8124); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8125
8126 JSNative native = target->native();
8127 if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) {
8128 const JSJitInfo* jitInfo = target->jitInfo();
8129 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
8130 native = jitInfo->ignoresReturnValueMethod;
8131 }
8132 }
8133
8134 // Push arguments, including newTarget and |this|.
8135 emitPushArguments(apply);
8136
8137 // Registers used for callWithABI() argument-passing.
8138 Register argContextReg = ToRegister(apply->getTempObject());
8139 Register argUintNReg = ToRegister(apply->getArgc());
8140 Register argVpReg = ToRegister(apply->getTempForArgCopy());
8141 Register tempReg = ToRegister(apply->getTempExtra());
8142
8143 // No unused stack for variadic calls.
8144 uint32_t unusedStack = 0;
8145
8146 // Pushed arguments don't change the pushed frames amount.
8147 MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("masm.framePushed() == frameSize()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 8147); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 8147); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8148
8149 // Create the exit frame and call the native.
8150 emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg,
8151 unusedStack);
8152
8153 // The exit frame is still on the stack.
8154 MOZ_ASSERT(masm.framePushed() == frameSize() + NativeExitFrameLayout::Size())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == frameSize() + NativeExitFrameLayout
::Size())>::isValid, "invalid assertion condition"); if ((
__builtin_expect(!!(!(!!(masm.framePushed() == frameSize() + NativeExitFrameLayout
::Size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8154); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()"
")"); do { MOZ_CrashSequence(__null, 8154); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8155
8156 // The next instruction is removing the exit frame, so there is no need for
8157 // leaveFakeExitFrame.
8158
8159 // Pop arguments and continue.
8160 masm.setFramePushed(frameSize());
8161 emitRestoreStackPointerFromFP();
8162}
8163
8164template <typename T>
8165void CodeGenerator::emitApplyArgsGuard(T* apply) {
8166 LSnapshot* snapshot = apply->snapshot();
8167 Register argcreg = ToRegister(apply->getArgc());
8168
8169 // Ensure that we have a reasonable number of arguments.
8170 bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
8171}
8172
8173template <typename T>
8174void CodeGenerator::emitApplyArgsObjGuard(T* apply) {
8175 Register argsObj = ToRegister(apply->getArgsObj());
8176 Register temp = ToRegister(apply->getTempObject());
8177
8178 Label bail;
8179 masm.loadArgumentsObjectLength(argsObj, temp, &bail);
8180 masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail);
8181 bailoutFrom(&bail, apply->snapshot());
8182}
8183
8184template <typename T>
8185void CodeGenerator::emitApplyArrayGuard(T* apply) {
8186 LSnapshot* snapshot = apply->snapshot();
8187 Register elements = ToRegister(apply->getElements());
8188 Register tmp = ToRegister(apply->getTempObject());
8189
8190 Address length(elements, ObjectElements::offsetOfLength());
8191 masm.load32(length, tmp);
8192
8193 // Ensure that we have a reasonable number of arguments.
8194 bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
8195
8196 // Ensure that the array does not contain an uninitialized tail.
8197
8198 Address initializedLength(elements,
8199 ObjectElements::offsetOfInitializedLength());
8200 masm.sub32(initializedLength, tmp);
8201 bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot);
8202}
8203
8204void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) {
8205 emitApplyArgsGuard(apply);
8206 emitApplyGeneric(apply);
8207}
8208
8209void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) {
8210 emitApplyArgsObjGuard(apply);
8211 emitApplyGeneric(apply);
8212}
8213
8214void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) {
8215 emitApplyArrayGuard(apply);
8216 emitApplyGeneric(apply);
8217}
8218
8219void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) {
8220 emitApplyArgsGuard(lir);
8221 emitApplyGeneric(lir);
8222}
8223
8224void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) {
8225 emitApplyArrayGuard(lir);
8226 emitApplyGeneric(lir);
8227}
8228
8229void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) {
8230 emitApplyArgsGuard(lir);
8231 emitApplyNative(lir);
8232}
8233
8234void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) {
8235 emitApplyArgsObjGuard(lir);
8236 emitApplyNative(lir);
8237}
8238
8239void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) {
8240 emitApplyArrayGuard(lir);
8241 emitApplyNative(lir);
8242}
8243
8244void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) {
8245 emitApplyArgsGuard(lir);
8246 emitApplyNative(lir);
8247}
8248
8249void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) {
8250 emitApplyArrayGuard(lir);
8251 emitApplyNative(lir);
8252}
8253
8254void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); }
8255
8256void CodeGenerator::visitUnreachable(LUnreachable* lir) {
8257 masm.assumeUnreachable("end-of-block assumed unreachable");
8258}
8259
8260void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) {
8261 encode(lir->snapshot());
8262}
8263
8264void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) {
8265 masm.assumeUnreachable("must be unreachable");
8266}
8267
8268void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) {
8269 masm.assumeUnreachable("must be unreachable");
8270}
8271
8272void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) {
8273 // If we don't push anything on the stack, skip the check.
8274 if (omitOverRecursedStackCheck()) {
8275 return;
8276 }
8277
8278 // Ensure that this frame will not cross the stack limit.
8279 // This is a weak check, justified by Ion using the C stack: we must always
8280 // be some distance away from the actual limit, since if the limit is
8281 // crossed, an error must be thrown, which requires more frames.
8282 //
8283 // It must always be possible to trespass past the stack limit.
8284 // Ion may legally place frames very close to the limit. Calling additional
8285 // C functions may then violate the limit without any checking.
8286 //
8287 // Since Ion frames exist on the C stack, the stack limit may be
8288 // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
8289
8290 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
8291 // The OOL path is hit if the recursion depth has been exceeded.
8292 // Throw an InternalError for over-recursion.
8293
8294 // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
8295 // to save all live registers to avoid crashes if CheckOverRecursed triggers
8296 // a GC.
8297 saveLive(lir);
8298
8299 using Fn = bool (*)(JSContext*);
8300 if (lir->mir()->isResumingGenerator()) {
8301 callVM<Fn, CheckOverRecursedResumingGenerator>(lir);
8302 } else {
8303 callVM<Fn, CheckOverRecursed>(lir);
8304 }
8305
8306 restoreLive(lir);
8307 masm.jump(ool.rejoin());
8308 });
8309 addOutOfLineCode(ool, lir->mir());
8310
8311 // When resuming a generator we check the no-interrupt limit, because the VM
8312 // function must not handle interrupts for a half-initialized frame.
8313 const void* limitAddr =
8314 lir->mir()->isResumingGenerator()
8315 ? gen->runtime->addressOfJitStackLimitNoInterrupt()
8316 : gen->runtime->addressOfJitStackLimit();
8317
8318 // Conditional forward (unlikely) branch to failure.
8319 masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr),
8320 ool->entry());
8321 masm.bind(ool->rejoin());
8322}
8323
8324IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() {
8325 // If scripts are being profiled, create a new IonScriptCounts for the
8326 // profiling data, which will be attached to the associated JSScript or
8327 // wasm module after code generation finishes.
8328 if (!gen->hasProfilingScripts()) {
8329 return nullptr;
8330 }
8331
8332 // This test inhibits IonScriptCount creation for wasm code which is
8333 // currently incompatible with wasm codegen for two reasons: (1) wasm code
8334 // must be serializable and script count codegen bakes in absolute
8335 // addresses, (2) wasm code does not have a JSScript with which to associate
8336 // code coverage data.
8337 JSScript* script = gen->outerInfo().script();
8338 if (!script) {
8339 return nullptr;
8340 }
8341
8342 auto counts = MakeUnique<IonScriptCounts>();
8343 if (!counts || !counts->init(graph.numBlocks())) {
8344 return nullptr;
8345 }
8346
8347 for (size_t i = 0; i < graph.numBlocks(); i++) {
8348 MBasicBlock* block = graph.getBlock(i)->mir();
8349
8350 uint32_t offset = 0;
8351 char* description = nullptr;
8352 if (MResumePoint* resume = block->entryResumePoint()) {
8353 // Find a PC offset in the outermost script to use. If this
8354 // block is from an inlined script, find a location in the
8355 // outer script to associate information about the inlining
8356 // with.
8357 while (resume->caller()) {
8358 resume = resume->caller();
8359 }
8360 offset = script->pcToOffset(resume->pc());
8361
8362 if (block->entryResumePoint()->caller()) {
8363 // Get the filename and line number of the inner script.
8364 JSScript* innerScript = block->info().script();
8365 description = js_pod_calloc<char>(200);
8366 if (description) {
8367 snprintf(description, 200, "%s:%u", innerScript->filename(),
8368 innerScript->lineno());
8369 }
8370 }
8371 }
8372
8373 if (!counts->block(i).init(block->id(), offset, description,
8374 block->numSuccessors())) {
8375 return nullptr;
8376 }
8377
8378 for (size_t j = 0; j < block->numSuccessors(); j++) {
8379 counts->block(i).setSuccessor(
8380 j, skipTrivialBlocks(block->getSuccessor(j))->id());
8381 }
8382 }
8383
8384 scriptCounts_ = counts.release();
8385 return scriptCounts_;
8386}
8387
8388// Structure for managing the state tracked for a block by script counters.
8389struct ScriptCountBlockState {
8390 IonBlockCounts& block;
8391 MacroAssembler& masm;
8392
8393 Sprinter printer;
8394
8395 public:
8396 ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm)
8397 : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {}
8398
8399 bool init() {
8400 if (!printer.init()) {
8401 return false;
8402 }
8403
8404 // Bump the hit count for the block at the start. This code is not
8405 // included in either the text for the block or the instruction byte
8406 // counts.
8407 masm.inc64(AbsoluteAddress(block.addressOfHitCount()));
8408
8409 // Collect human readable assembly for the code generated in the block.
8410 masm.setPrinter(&printer);
8411
8412 return true;
8413 }
8414
8415 void visitInstruction(LInstruction* ins) {
8416#ifdef JS_JITSPEW1
8417 // Prefix stream of assembly instructions with their LIR instruction
8418 // name and any associated high level info.
8419 if (const char* extra = ins->getExtraName()) {
8420 printer.printf("[%s:%s]\n", ins->opName(), extra);
8421 } else {
8422 printer.printf("[%s]\n", ins->opName());
8423 }
8424#endif
8425 }
8426
8427 ~ScriptCountBlockState() {
8428 masm.setPrinter(nullptr);
8429
8430 if (JS::UniqueChars str = printer.release()) {
8431 block.setCode(str.get());
8432 }
8433 }
8434};
8435
8436void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) {
8437 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
8438 masm.propagateOOM(ionScriptLabels_.append(label));
8439
8440 // If IonScript::invalidationCount_ != 0, the script has been invalidated.
8441 masm.branch32(Assembler::NotEqual,
8442 Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0),
8443 invalidated);
8444}
8445
8446#ifdef DEBUG1
8447void CodeGenerator::emitAssertGCThingResult(Register input,
8448 const MDefinition* mir) {
8449 MIRType type = mir->type();
8450 MOZ_ASSERT(type == MIRType::Object || type == MIRType::String ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::Object || type == MIRType::String ||
type == MIRType::Symbol || type == MIRType::BigInt)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(type == MIRType::Object || type == MIRType::String || type
== MIRType::Symbol || type == MIRType::BigInt))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8451); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 8451); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
8451 type == MIRType::Symbol || type == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::Object || type == MIRType::String ||
type == MIRType::Symbol || type == MIRType::BigInt)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(type == MIRType::Object || type == MIRType::String || type
== MIRType::Symbol || type == MIRType::BigInt))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8451); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 8451); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8452
8453 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8454 regs.take(input);
8455
8456 Register temp = regs.takeAny();
8457 masm.push(temp);
8458
8459 // Don't check if the script has been invalidated. In that case invalid
8460 // types are expected (until we reach the OsiPoint and bailout).
8461 Label done;
8462 branchIfInvalidated(temp, &done);
8463
8464# ifndef JS_SIMULATOR
8465 // Check that we have a valid GC pointer.
8466 // Disable for wasm because we don't have a context on wasm compilation
8467 // threads and this needs a context.
8468 // Also disable for simulator builds because the C++ call is a lot slower
8469 // there than on actual hardware.
8470 if (JitOptions.fullDebugChecks && !IsCompilingWasm()) {
8471 saveVolatile();
8472 masm.setupUnalignedABICall(temp);
8473 masm.loadJSContext(temp);
8474 masm.passABIArg(temp);
8475 masm.passABIArg(input);
8476
8477 switch (type) {
8478 case MIRType::Object: {
8479 using Fn = void (*)(JSContext* cx, JSObject* obj);
8480 masm.callWithABI<Fn, AssertValidObjectPtr>();
8481 break;
8482 }
8483 case MIRType::String: {
8484 using Fn = void (*)(JSContext* cx, JSString* str);
8485 masm.callWithABI<Fn, AssertValidStringPtr>();
8486 break;
8487 }
8488 case MIRType::Symbol: {
8489 using Fn = void (*)(JSContext* cx, JS::Symbol* sym);
8490 masm.callWithABI<Fn, AssertValidSymbolPtr>();
8491 break;
8492 }
8493 case MIRType::BigInt: {
8494 using Fn = void (*)(JSContext* cx, JS::BigInt* bi);
8495 masm.callWithABI<Fn, AssertValidBigIntPtr>();
8496 break;
8497 }
8498 default:
8499 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 8499); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 8499); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
8500 }
8501
8502 restoreVolatile();
8503 }
8504# endif
8505
8506 masm.bind(&done);
8507 masm.pop(temp);
8508}
8509
8510void CodeGenerator::emitAssertResultV(const ValueOperand input,
8511 const MDefinition* mir) {
8512 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8513 regs.take(input);
8514
8515 Register temp1 = regs.takeAny();
8516 Register temp2 = regs.takeAny();
8517 masm.push(temp1);
8518 masm.push(temp2);
8519
8520 // Don't check if the script has been invalidated. In that case invalid
8521 // types are expected (until we reach the OsiPoint and bailout).
8522 Label done;
8523 branchIfInvalidated(temp1, &done);
8524
8525 // Check that we have a valid GC pointer.
8526 if (JitOptions.fullDebugChecks) {
8527 saveVolatile();
8528
8529 masm.pushValue(input);
8530 masm.moveStackPtrTo(temp1);
8531
8532 using Fn = void (*)(JSContext* cx, Value* v);
8533 masm.setupUnalignedABICall(temp2);
8534 masm.loadJSContext(temp2);
8535 masm.passABIArg(temp2);
8536 masm.passABIArg(temp1);
8537 masm.callWithABI<Fn, AssertValidValue>();
8538 masm.popValue(input);
8539 restoreVolatile();
8540 }
8541
8542 masm.bind(&done);
8543 masm.pop(temp2);
8544 masm.pop(temp1);
8545}
8546
8547void CodeGenerator::emitGCThingResultChecks(LInstruction* lir,
8548 MDefinition* mir) {
8549 if (lir->numDefs() == 0) {
8550 return;
8551 }
8552
8553 MOZ_ASSERT(lir->numDefs() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8553); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { MOZ_CrashSequence
(__null, 8553); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8554 if (lir->getDef(0)->isBogusTemp()) {
8555 return;
8556 }
8557
8558 Register output = ToRegister(lir->getDef(0));
8559 emitAssertGCThingResult(output, mir);
8560}
8561
8562void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) {
8563 if (lir->numDefs() == 0) {
8564 return;
8565 }
8566
8567 MOZ_ASSERT(lir->numDefs() == BOX_PIECES)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->numDefs() == BOX_PIECES)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->numDefs() == BOX_PIECES
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->numDefs() == BOX_PIECES", "./../../../../js/src/jit/CodeGenerator.cpp"
, 8567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == BOX_PIECES"
")"); do { MOZ_CrashSequence(__null, 8567); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8568 if (!lir->getDef(0)->output()->isGeneralReg()) {
8569 return;
8570 }
8571
8572 ValueOperand output = ToOutValue(lir);
8573
8574 emitAssertResultV(output, mir);
8575}
8576
8577void CodeGenerator::emitWasmAnyrefResultChecks(LInstruction* lir,
8578 MDefinition* mir) {
8579 MOZ_ASSERT(mir->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mir->type() == MIRType::WasmAnyRef)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(mir->type() == MIRType::WasmAnyRef))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("mir->type() == MIRType::WasmAnyRef"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8579); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mir->type() == MIRType::WasmAnyRef" ")"
); do { MOZ_CrashSequence(__null, 8579); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8580
8581 if (!JitOptions.fullDebugChecks) {
8582 return;
8583 }
8584
8585 wasm::MaybeRefType destType = mir->wasmRefType();
8586 if (!destType || !destType.value().isCastable()) {
8587 return;
8588 }
8589
8590 if (lir->numDefs() == 0) {
8591 return;
8592 }
8593
8594 MOZ_ASSERT(lir->numDefs() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8594); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { MOZ_CrashSequence
(__null, 8594); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8595 if (lir->getDef(0)->isBogusTemp()) {
8596 return;
8597 }
8598
8599 if (lir->getDef(0)->output()->isMemory()) {
8600 return;
8601 }
8602 Register output = ToRegister(lir->getDef(0));
8603
8604 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8605 regs.take(output);
8606
8607 BranchWasmRefIsSubtypeRegisters needs =
8608 MacroAssembler::regsForBranchWasmRefIsSubtype(destType.value());
8609
8610 Register temp1;
8611 Register temp2;
8612 Register temp3;
8613 if (needs.needSuperSTV) {
8614 temp1 = regs.takeAny();
8615 masm.push(temp1);
8616 }
8617 if (needs.needScratch1) {
8618 temp2 = regs.takeAny();
8619 masm.push(temp2);
8620 }
8621 if (needs.needScratch2) {
8622 temp3 = regs.takeAny();
8623 masm.push(temp3);
8624 }
8625
8626 if (needs.needSuperSTV) {
8627 uint32_t typeIndex =
8628 wasmCodeMeta()->types->indexOf(*destType.value().typeDef());
8629
8630 // When full debug checks are enabled, we always write the callee instance
8631 // pointer into its usual slot in the frame in our function prologue, so
8632 // that we can get it even if the InstanceReg is currently being used for
8633 // something else.
8634 masm.loadPtr(
8635 Address(FramePointer, wasm::FrameWithInstances::calleeInstanceOffset()),
8636 temp1);
8637 masm.loadPtr(
8638 Address(temp1, wasm::Instance::offsetInData(
8639 wasmCodeMeta()->offsetOfSuperTypeVector(typeIndex))),
8640 temp1);
8641 }
8642
8643 Label ok;
8644 masm.branchWasmRefIsSubtype(output, wasm::MaybeRefType(), destType.value(),
8645 &ok, /*onSuccess=*/true,
8646 /*signalNullChecks=*/false, temp1, temp2, temp3);
8647 masm.breakpoint();
8648 masm.bind(&ok);
8649
8650 if (needs.needScratch2) {
8651 masm.pop(temp3);
8652 }
8653 if (needs.needScratch1) {
8654 masm.pop(temp2);
8655 }
8656 if (needs.needSuperSTV) {
8657 masm.pop(temp1);
8658 }
8659
8660# ifdef JS_CODEGEN_ARM64
8661 masm.syncStackPtr();
8662# endif
8663}
8664
8665void CodeGenerator::emitDebugResultChecks(LInstruction* ins) {
8666 // In debug builds, check that LIR instructions return valid values.
8667
8668 MDefinition* mir = ins->mirRaw();
8669 if (!mir) {
8670 return;
8671 }
8672
8673 switch (mir->type()) {
8674 case MIRType::Object:
8675 case MIRType::String:
8676 case MIRType::Symbol:
8677 case MIRType::BigInt:
8678 emitGCThingResultChecks(ins, mir);
8679 break;
8680 case MIRType::Value:
8681 emitValueResultChecks(ins, mir);
8682 break;
8683 case MIRType::WasmAnyRef:
8684 emitWasmAnyrefResultChecks(ins, mir);
8685 break;
8686 default:
8687 break;
8688 }
8689}
8690
8691void CodeGenerator::emitDebugForceBailing(LInstruction* lir) {
8692 if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()),
1))
) {
8693 return;
8694 }
8695 if (!lir->snapshot()) {
8696 return;
8697 }
8698 if (lir->isOsiPoint()) {
8699 return;
8700 }
8701
8702 masm.comment("emitDebugForceBailing");
8703 const void* bailAfterCounterAddr =
8704 gen->runtime->addressOfIonBailAfterCounter();
8705
8706 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8707
8708 Label done, notBail;
8709 masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr),
8710 Imm32(0), &done);
8711 {
8712 Register temp = regs.takeAny();
8713
8714 masm.push(temp);
8715 masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp);
8716 masm.sub32(Imm32(1), temp);
8717 masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr));
8718
8719 masm.branch32(Assembler::NotEqual, temp, Imm32(0), &notBail);
8720 {
8721 masm.pop(temp);
8722 bailout(lir->snapshot());
8723 }
8724 masm.bind(&notBail);
8725 masm.pop(temp);
8726 }
8727 masm.bind(&done);
8728}
8729#endif // DEBUG
8730
8731bool CodeGenerator::generateBody() {
8732 JitSpew(JitSpew_Codegen, "\n");
8733 AutoCreatedBy acb(masm, "CodeGenerator::generateBody");
8734
8735 JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ====");
8736 counts_ = maybeCreateScriptCounts();
8737
8738 const bool compilingWasm = gen->compilingWasm();
8739
8740 for (size_t i = 0; i < graph.numBlocks(); i++) {
8741 current = graph.getBlock(i);
8742
8743 // Don't emit any code for trivial blocks, containing just a goto. Such
8744 // blocks are created to split critical edges, and if we didn't end up
8745 // putting any instructions in them, we can skip them.
8746 if (current->isTrivial()) {
8747 continue;
8748 }
8749
8750 if (gen->shouldCancel("Generate Code (block loop)")) {
8751 return false;
8752 }
8753
8754 // Skip out of line blocks for now. They will be emitted in
8755 // generateOutOfLineBlocks.
8756 if (current->isOutOfLine()) {
8757 continue;
8758 }
8759
8760 // Generate a basic block
8761 if (!generateBlock(current, i, counts_, compilingWasm)) {
8762 return false;
8763 }
8764 }
8765
8766 JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n");
8767 return true;
8768}
8769
8770bool CodeGenerator::generateBlock(LBlock* current, size_t blockNumber,
8771 IonScriptCounts* counts, bool compilingWasm) {
8772#ifdef JS_JITSPEW1
8773 const char* filename = nullptr;
8774 size_t lineNumber = 0;
8775 JS::LimitedColumnNumberOneOrigin columnNumber;
8776 if (current->mir()->info().script()) {
8777 filename = current->mir()->info().script()->filename();
8778 if (current->mir()->pc()) {
8779 lineNumber = PCToLineNumber(current->mir()->info().script(),
8780 current->mir()->pc(), &columnNumber);
8781 }
8782 }
8783 JitSpew(JitSpew_Codegen, "--------------------------------");
8784 JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", blockNumber,
8785 filename ? filename : "?", lineNumber, columnNumber.oneOriginValue(),
8786 current->mir()->isLoopHeader() ? " (loop header)" : "");
8787#endif
8788
8789 if (current->mir()->isLoopHeader() && compilingWasm) {
8790 masm.nopAlign(CodeAlignment);
8791 }
8792
8793 masm.bind(current->label());
8794
8795 mozilla::Maybe<ScriptCountBlockState> blockCounts;
8796 if (counts) {
8797 blockCounts.emplace(&counts->block(blockNumber), &masm);
8798 if (!blockCounts->init()) {
8799 return false;
8800 }
8801 }
8802
8803 for (LInstructionIterator iter = current->begin(); iter != current->end();
8804 iter++) {
8805 if (gen->shouldCancel("Generate Code (instruction loop)")) {
8806 return false;
8807 }
8808 if (!alloc().ensureBallast()) {
8809 return false;
8810 }
8811
8812 perfSpewer().recordInstruction(masm, *iter);
8813#ifdef JS_JITSPEW1
8814 {
8815 AutoJitSpewMessage msg(JitSpew_Codegen,
8816 " # LIR=%s",
8817 iter->opName());
8818 if (const char* extra = iter->getExtraName()) {
8819 msg.append(":%s", extra);
8820 }
8821 }
8822#endif
8823
8824 if (counts) {
8825 blockCounts->visitInstruction(*iter);
8826 }
8827
8828#ifdef CHECK_OSIPOINT_REGISTERS1
8829 if (iter->safepoint() && !compilingWasm) {
8830 resetOsiPointRegs(iter->safepoint());
8831 }
8832#endif
8833
8834 if (!compilingWasm) {
8835 if (MDefinition* mir = iter->mirRaw()) {
8836 if (!addNativeToBytecodeEntry(mir->trackedSite())) {
8837 return false;
8838 }
8839 }
8840 }
8841
8842 setElement(*iter); // needed to encode correct snapshot location.
8843
8844#ifdef DEBUG1
8845 emitDebugForceBailing(*iter);
8846#endif
8847
8848 switch (iter->op()) {
8849#ifndef JS_CODEGEN_NONE
8850# define LIROP(op) \
8851 case LNode::Opcode::op: \
8852 visit##op(iter->to##op()); \
8853 break;
8854 LIR_OPCODE_LIST(LIROP)LIROP(Phi)LIROP(Box)LIROP(OsiPoint)LIROP(MoveGroup)LIROP(Integer
)LIROP(Integer64)LIROP(Pointer)LIROP(Double)LIROP(Float32)LIROP
(Value)LIROP(Parameter)LIROP(Goto)LIROP(NewArray)LIROP(NewIterator
)LIROP(NewBoundFunction)LIROP(NewTypedArray)LIROP(NewTypedArrayInline
)LIROP(BindFunction)LIROP(NewObject)LIROP(NewPlainObject)LIROP
(NewArrayObject)LIROP(NewCallObject)LIROP(NewMapObject)LIROP(
NewSetObject)LIROP(NewStringObject)LIROP(WasmRefAsNonNull)LIROP
(WasmRefTestConcrete)LIROP(WasmRefTestAbstract)LIROP(WasmRefTestConcreteAndBranch
)LIROP(WasmRefTestAbstractAndBranch)LIROP(WasmRefCastConcrete
)LIROP(WasmRefCastAbstract)LIROP(WasmNewStructObject)LIROP(WasmNewArrayObject
)LIROP(ReinterpretCast)LIROP(ReinterpretCastFromI64)LIROP(ReinterpretCastToI64
)LIROP(Rotate)LIROP(RotateI64)LIROP(WasmInterruptCheck)LIROP(
TypeOfV)LIROP(TypeOfO)LIROP(TypeOfIsNonPrimitiveV)LIROP(TypeOfIsNonPrimitiveO
)LIROP(TypeOfIsPrimitive)LIROP(CreateInlinedArgumentsObject)LIROP
(GetInlinedArgument)LIROP(GetInlinedArgumentHole)LIROP(BoxNonStrictThis
)LIROP(StackArgT)LIROP(StackArgV)LIROP(CallGeneric)LIROP(CallKnown
)LIROP(CallNative)LIROP(CallDOMNative)LIROP(CallClassHook)LIROP
(Unreachable)LIROP(UnreachableResultV)LIROP(UnreachableResultT
)LIROP(GetDOMProperty)LIROP(GetDOMMemberV)LIROP(GetDOMMemberT
)LIROP(SetDOMProperty)LIROP(LoadDOMExpandoValueGuardGeneration
)LIROP(ApplyArgsGeneric)LIROP(ApplyArgsObj)LIROP(ApplyArrayGeneric
)LIROP(ConstructArgsGeneric)LIROP(ConstructArrayGeneric)LIROP
(ApplyArgsNative)LIROP(ApplyArgsObjNative)LIROP(ApplyArrayNative
)LIROP(ConstructArgsNative)LIROP(ConstructArrayNative)LIROP(TestIAndBranch
)LIROP(TestIPtrAndBranch)LIROP(TestI64AndBranch)LIROP(TestDAndBranch
)LIROP(TestFAndBranch)LIROP(TestBIAndBranch)LIROP(TestOAndBranch
)LIROP(TestVAndBranch)LIROP(Compare)LIROP(CompareI64)LIROP(CompareI64AndBranch
)LIROP(CompareAndBranch)LIROP(CompareD)LIROP(CompareF)LIROP(CompareDAndBranch
)LIROP(CompareFAndBranch)LIROP(CompareS)LIROP(CompareSInline)
LIROP(CompareSSingle)LIROP(CompareBigInt)LIROP(CompareBigIntInt32
)LIROP(CompareBigIntDouble)LIROP(CompareBigIntString)LIROP(CompareBigIntInt32AndBranch
)LIROP(StrictConstantCompareInt32AndBranch)LIROP(StrictConstantCompareBooleanAndBranch
)LIROP(BitAndAndBranch)LIROP(BitAnd64AndBranch)LIROP(IsNullOrLikeUndefinedV
)LIROP(IsNullOrLikeUndefinedT)LIROP(IsNull)LIROP(IsUndefined)
LIROP(IsNullOrLikeUndefinedAndBranchV)LIROP(IsNullOrLikeUndefinedAndBranchT
)LIROP(IsNullAndBranch)LIROP(IsUndefinedAndBranch)LIROP(NotI)
LIROP(NotIPtr)LIROP(NotI64)LIROP(NotD)LIROP(NotF)LIROP(NotBI)
LIROP(NotO)LIROP(NotV)LIROP(BitNotI)LIROP(BitNotI64)LIROP(BitOpI
)LIROP(BitOpI64)LIROP(ShiftI)LIROP(ShiftIntPtr)LIROP(ShiftI64
)LIROP(SignExtendInt32)LIROP(SignExtendIntPtr)LIROP(SignExtendInt64
)LIROP(UrshD)LIROP(Return)LIROP(MinMaxI)LIROP(MinMaxIntPtr)LIROP
(MinMaxD)LIROP(MinMaxF)LIROP(MinMaxArrayI)LIROP(MinMaxArrayD)
LIROP(NegI)LIROP(NegI64)LIROP(NegD)LIROP(NegF)LIROP(AbsI)LIROP
(AbsD)LIROP(AbsF)LIROP(CopySignD)LIROP(CopySignF)LIROP(ClzI)LIROP
(ClzI64)LIROP(CtzI)LIROP(CtzI64)LIROP(PopcntI)LIROP(PopcntI64
)LIROP(SqrtD)LIROP(SqrtF)LIROP(Atan2D)LIROP(Hypot)LIROP(PowI)
LIROP(PowII)LIROP(PowD)LIROP(PowOfTwoI)LIROP(SignI)LIROP(SignD
)LIROP(SignDI)LIROP(SignID)LIROP(MathFunctionD)LIROP(MathFunctionF
)LIROP(AddI)LIROP(AddI64)LIROP(AddIntPtr)LIROP(SubI)LIROP(SubI64
)LIROP(SubIntPtr)LIROP(MulI64)LIROP(MulIntPtr)LIROP(MathD)LIROP
(MathF)LIROP(ModD)LIROP(ModPowTwoD)LIROP(WasmBuiltinModD)LIROP
(BigIntAdd)LIROP(BigIntSub)LIROP(BigIntMul)LIROP(BigIntDiv)LIROP
(BigIntMod)LIROP(BigIntPow)LIROP(BigIntBitAnd)LIROP(BigIntBitOr
)LIROP(BigIntBitXor)LIROP(BigIntLsh)LIROP(BigIntRsh)LIROP(BigIntIncrement
)LIROP(BigIntDecrement)LIROP(BigIntNegate)LIROP(BigIntBitNot)
LIROP(BigIntPtrAdd)LIROP(BigIntPtrSub)LIROP(BigIntPtrMul)LIROP
(BigIntPtrDiv)LIROP(BigIntPtrDivPowTwo)LIROP(BigIntPtrMod)LIROP
(BigIntPtrModPowTwo)LIROP(BigIntPtrPow)LIROP(BigIntPtrBitAnd)
LIROP(BigIntPtrBitOr)LIROP(BigIntPtrBitXor)LIROP(BigIntPtrLsh
)LIROP(BigIntPtrRsh)LIROP(BigIntPtrBitNot)LIROP(Concat)LIROP(
StringIncludesSIMD)LIROP(StringIndexOfSIMD)LIROP(StringStartsWithInline
)LIROP(StringEndsWithInline)LIROP(StringToLowerCase)LIROP(CharCodeToLowerCase
)LIROP(StringToUpperCase)LIROP(CharCodeToUpperCase)LIROP(Int32ToDouble
)LIROP(Float32ToDouble)LIROP(DoubleToFloat32)LIROP(Int32ToFloat32
)LIROP(DoubleToFloat16)LIROP(DoubleToFloat32ToFloat16)LIROP(Float32ToFloat16
)LIROP(Int32ToFloat16)LIROP(ValueToDouble)LIROP(ValueToFloat32
)LIROP(ValueToFloat16)LIROP(ValueToNumberInt32)LIROP(ValueTruncateToInt32
)LIROP(ValueToBigInt)LIROP(DoubleToInt32)LIROP(Float32ToInt32
)LIROP(TruncateDToInt32)LIROP(WasmBuiltinTruncateDToInt32)LIROP
(TruncateFToInt32)LIROP(WasmBuiltinTruncateFToInt32)LIROP(WasmTruncateToInt32
)LIROP(BooleanToString)LIROP(IntToString)LIROP(DoubleToString
)LIROP(ValueToString)LIROP(PowHalfD)LIROP(NaNToZero)LIROP(OsrEntry
)LIROP(OsrValue)LIROP(OsrEnvironmentChain)LIROP(OsrReturnValue
)LIROP(OsrArgumentsObject)LIROP(RegExp)LIROP(RegExpHasCaptureGroups
)LIROP(GetFirstDollarIndex)LIROP(StringReplace)LIROP(BinaryValueCache
)LIROP(BinaryBoolCache)LIROP(Lambda)LIROP(FunctionWithProto)LIROP
(GetNextEntryForIterator)LIROP(GuardNumberToIntPtrIndex)LIROP
(BoundsCheck)LIROP(BoundsCheckRange)LIROP(BoundsCheckLower)LIROP
(SpectreMaskIndex)LIROP(LoadElementV)LIROP(InArray)LIROP(LoadElementHole
)LIROP(StoreElementV)LIROP(StoreElementT)LIROP(StoreHoleValueElement
)LIROP(StoreElementHoleV)LIROP(StoreElementHoleT)LIROP(ArrayPopShift
)LIROP(FrameArgumentsSlice)LIROP(InlineArgumentsSlice)LIROP(LoadUnboxedScalar
)LIROP(LoadUnboxedInt64)LIROP(LoadDataViewElement)LIROP(LoadDataViewElement64
)LIROP(LoadTypedArrayElementHole)LIROP(LoadTypedArrayElementHoleBigInt
)LIROP(StoreUnboxedScalar)LIROP(StoreUnboxedInt64)LIROP(StoreDataViewElement
)LIROP(StoreDataViewElement64)LIROP(StoreTypedArrayElementHole
)LIROP(StoreTypedArrayElementHoleInt64)LIROP(TypedArraySubarray
)LIROP(TypedArrayFill)LIROP(TypedArrayFill64)LIROP(AtomicIsLockFree
)LIROP(CompareExchangeTypedArrayElement)LIROP(AtomicExchangeTypedArrayElement
)LIROP(AtomicTypedArrayElementBinop)LIROP(AtomicTypedArrayElementBinopForEffect
)LIROP(AtomicLoad64)LIROP(AtomicStore64)LIROP(CompareExchangeTypedArrayElement64
)LIROP(AtomicExchangeTypedArrayElement64)LIROP(AtomicTypedArrayElementBinop64
)LIROP(AtomicTypedArrayElementBinopForEffect64)LIROP(EffectiveAddress3
)LIROP(EffectiveAddress2)LIROP(ClampIToUint8)LIROP(ClampDToUint8
)LIROP(ClampVToUint8)LIROP(LoadFixedSlotV)LIROP(LoadFixedSlotAndAtomize
)LIROP(LoadFixedSlotT)LIROP(LoadFixedSlotAndUnbox)LIROP(LoadDynamicSlotAndUnbox
)LIROP(LoadElementAndUnbox)LIROP(LoadFixedSlotUnboxAndAtomize
)LIROP(LoadDynamicSlotUnboxAndAtomize)LIROP(AddAndStoreSlot)LIROP
(AddAndStoreSlotPreserveWrapper)LIROP(StoreFixedSlotV)LIROP(StoreFixedSlotT
)LIROP(StoreFixedSlotFromOffsetV)LIROP(StoreFixedSlotFromOffsetT
)LIROP(GetPropSuperCache)LIROP(GetPropertyCache)LIROP(LoadDynamicSlotV
)LIROP(LoadDynamicSlotAndAtomize)LIROP(StoreDynamicSlotV)LIROP
(StoreDynamicSlotT)LIROP(StoreDynamicSlotFromOffsetV)LIROP(StoreDynamicSlotFromOffsetT
)LIROP(Floor)LIROP(FloorF)LIROP(Ceil)LIROP(CeilF)LIROP(Round)
LIROP(RoundF)LIROP(Trunc)LIROP(TruncF)LIROP(NearbyInt)LIROP(NearbyIntF
)LIROP(RoundToDouble)LIROP(RoundToFloat32)LIROP(NewLexicalEnvironmentObject
)LIROP(NewClassBodyEnvironmentObject)LIROP(NewVarEnvironmentObject
)LIROP(MegamorphicSetElement)LIROP(ObjectToIterator)LIROP(IteratorHasIndicesAndBranch
)LIROP(IteratorsMatchAndHaveIndicesAndBranch)LIROP(SetPropertyCache
)LIROP(GetIteratorCache)LIROP(IsNoIterAndBranch)LIROP(IsResumingGeneratorAndBranch
)LIROP(CloseIterCache)LIROP(Int32ToIntPtr)LIROP(AdjustDataViewLength
)LIROP(BooleanToInt64)LIROP(StringToInt64)LIROP(ValueToInt64)
LIROP(Uint64ToBigInt)LIROP(PostWriteBarrierO)LIROP(PostWriteBarrierS
)LIROP(PostWriteBarrierBI)LIROP(PostWriteBarrierV)LIROP(PostWriteElementBarrierO
)LIROP(PostWriteElementBarrierS)LIROP(PostWriteElementBarrierBI
)LIROP(PostWriteElementBarrierV)LIROP(GuardObjectIdentity)LIROP
(GuardSpecificFunction)LIROP(GuardSpecificSymbol)LIROP(GuardMultipleShapesToOffset
)LIROP(GuardProto)LIROP(GuardNullProto)LIROP(ProxyHasProp)LIROP
(InCache)LIROP(HasOwnCache)LIROP(CheckPrivateFieldCache)LIROP
(InstanceOfO)LIROP(InstanceOfV)LIROP(IsCallableO)LIROP(IsCallableV
)LIROP(IsArrayO)LIROP(IsArrayV)LIROP(IsTypedArray)LIROP(IsObjectAndBranch
)LIROP(IsNullOrUndefinedAndBranch)LIROP(IsSuspendedGeneratorAndBranch
)LIROP(HasClass)LIROP(GuardToClass)LIROP(GuardToFunction)LIROP
(WasmSelect)LIROP(WasmSelectI64)LIROP(WasmCompareAndSelect)LIROP
(WasmAddOffset)LIROP(WasmAddOffset64)LIROP(WasmBoundsCheck)LIROP
(WasmBoundsCheck64)LIROP(WasmBoundsCheckInstanceField)LIROP(WasmBoundsCheckInstanceField64
)LIROP(WasmExtendU32Index)LIROP(WasmWrapU32Index)LIROP(WasmAlignmentCheck
)LIROP(WasmAlignmentCheck64)LIROP(WasmLoadInstance)LIROP(WasmLoadInstance64
)LIROP(WasmHeapReg)LIROP(WasmLoad)LIROP(WasmLoadI64)LIROP(WasmStore
)LIROP(WasmStoreI64)LIROP(WasmCompareExchangeHeap)LIROP(WasmAtomicExchangeHeap
)LIROP(WasmAtomicBinopHeap)LIROP(WasmAtomicBinopHeapForEffect
)LIROP(WasmLoadSlot)LIROP(WasmLoadElement)LIROP(WasmLoadSlotI64
)LIROP(WasmLoadElementI64)LIROP(WasmStoreSlot)LIROP(WasmStoreSlotI64
)LIROP(WasmStoreStackResult)LIROP(WasmStoreStackResultI64)LIROP
(WasmStoreElement)LIROP(WasmStoreElementI64)LIROP(WasmStoreElementRef
)LIROP(WasmLoadTableElement)LIROP(WasmDerivedPointer)LIROP(WasmDerivedIndexPointer
)LIROP(WasmStoreRef)LIROP(WasmPostWriteBarrierWholeCell)LIROP
(WasmPostWriteBarrierEdgeAtIndex)LIROP(WasmParameter)LIROP(WasmParameterI64
)LIROP(WasmReturn)LIROP(WasmReturnI64)LIROP(WasmReturnVoid)LIROP
(WasmStackArg)LIROP(WasmStackArgI64)LIROP(WasmNullConstant)LIROP
(WasmCall)LIROP(WasmRegisterResult)LIROP(WasmRegisterPairResult
)LIROP(WasmSystemFloatRegisterResult)LIROP(WasmStackResultArea
)LIROP(WasmStackResult)LIROP(WasmStackResult64)LIROP(WasmSuspend
)LIROP(WasmPrepareResume)LIROP(WasmResume)LIROP(AssertRangeI)
LIROP(AssertRangeD)LIROP(AssertRangeF)LIROP(AssertRangeV)LIROP
(AssertClass)LIROP(GuardValue)LIROP(GuardFunctionFlags)LIROP(
LexicalCheck)LIROP(MemoryBarrier)LIROP(Debugger)LIROP(Generator
)LIROP(MaybeExtractAwaitValue)LIROP(ObjectStaticProto)LIROP(GuardTagNotEqual
)LIROP(CanonicalizeNaND)LIROP(CanonicalizeNaNF)LIROP(IonToWasmCall
)LIROP(IonToWasmCallV)LIROP(IonToWasmCallI64)LIROP(WasmNewI31Ref
)LIROP(Simd128)LIROP(WasmTernarySimd128)LIROP(WasmBinarySimd128
)LIROP(WasmBinarySimd128WithConstant)LIROP(WasmVariableShiftSimd128
)LIROP(WasmConstantShiftSimd128)LIROP(WasmSignReplicationSimd128
)LIROP(WasmShuffleSimd128)LIROP(WasmPermuteSimd128)LIROP(WasmReplaceLaneSimd128
)LIROP(WasmReplaceInt64LaneSimd128)LIROP(WasmScalarToSimd128)
LIROP(WasmInt64ToSimd128)LIROP(WasmReduceSimd128)LIROP(WasmReduceAndBranchSimd128
)LIROP(WasmReduceSimd128ToInt64)LIROP(WasmLoadLaneSimd128)LIROP
(WasmStoreLaneSimd128)LIROP(Unbox)LIROP(UnboxFloatingPoint)LIROP
(WasmUint32ToDouble)LIROP(WasmUint32ToFloat32)LIROP(DivI)LIROP
(ModI)LIROP(DivPowTwoI)LIROP(ModPowTwoI)LIROP(TableSwitch)LIROP
(TableSwitchV)LIROP(MulI)LIROP(TimeClipCall)LIROP(DivConstantI
)LIROP(ModConstantI)LIROP(UDivConstant)LIROP(UModConstant)LIROP
(UDiv)LIROP(UMod)LIROP(DivI64)LIROP(ModI64)LIROP(UDivI64)LIROP
(UModI64)LIROP(DivPowTwoI64)LIROP(ModPowTwoI64)LIROP(DivConstantI64
)LIROP(ModConstantI64)LIROP(UDivConstantI64)LIROP(UModConstantI64
)LIROP(WasmTruncateToInt64)LIROP(Int64ToFloatingPoint)LIROP(WasmAddSubI128HI64
)LIROP(WasmMulI64WideHI64)LIROP(Callee)LIROP(IsConstructing)LIROP
(Throw)LIROP(ThrowWithStack)LIROP(NewArrayDynamicLength)LIROP
(NewTypedArrayDynamicLength)LIROP(NewTypedArrayFromArray)LIROP
(NewTypedArrayFromArrayBuffer)LIROP(NewMapObjectFromIterable)
LIROP(NewSetObjectFromIterable)LIROP(BoundFunctionNumArgs)LIROP
(GuardBoundFunctionIsConstructor)LIROP(MutateProto)LIROP(InitPropGetterSetter
)LIROP(InitElemGetterSetter)LIROP(Bail)LIROP(EncodeSnapshot)LIROP
(StrictConstantCompareInt32)LIROP(StrictConstantCompareBoolean
)LIROP(StrictConstantCompareString)LIROP(StrictConstantCompareObject
)LIROP(SameValueDouble)LIROP(SameValue)LIROP(AssertShape)LIROP
(CreateThis)LIROP(CreateArgumentsObject)LIROP(GetArgumentsObjectArg
)LIROP(SetArgumentsObjectArg)LIROP(LoadArgumentsObjectArg)LIROP
(LoadArgumentsObjectArgHole)LIROP(InArgumentsObjectArg)LIROP(
ArgumentsObjectLength)LIROP(ArrayFromArgumentsObject)LIROP(GuardArgumentsObjectFlags
)LIROP(GuardObjectHasSameRealm)LIROP(LoadScriptedProxyHandler
)LIROP(CheckScriptedProxyGetResult)LIROP(IdToStringOrSymbol)LIROP
(ReturnFromCtor)LIROP(WrapInt64ToInt32)LIROP(ExtendInt32ToInt64
)LIROP(WasmAnyRefFromJSValue)LIROP(WasmAnyRefFromJSObject)LIROP
(WasmAnyRefFromJSString)LIROP(WasmAnyRefIsJSString)LIROP(WasmTrapIfAnyRefIsNotJSString
)LIROP(WasmAnyRefJSStringLength)LIROP(WasmI31RefGet)LIROP(NonNegativeIntPtrToInt32
)LIROP(IntPtrToDouble)LIROP(TruncateBigIntToInt64)LIROP(Int64ToBigInt
)LIROP(Int64ToIntPtr)LIROP(IntPtrToInt64)LIROP(TypeOfName)LIROP
(ToAsyncIter)LIROP(ToPropertyKeyCache)LIROP(Random)LIROP(BigIntToIntPtr
)LIROP(IntPtrToBigInt)LIROP(Int32ToStringWithBase)LIROP(NumberParseInt
)LIROP(DoubleParseInt)LIROP(LinearizeString)LIROP(LinearizeForCharAccess
)LIROP(LinearizeForCodePointAccess)LIROP(ToRelativeStringIndex
)LIROP(CharCodeAt)LIROP(CharCodeAtOrNegative)LIROP(CodePointAt
)LIROP(CodePointAtOrNegative)LIROP(NegativeToNaN)LIROP(NegativeToUndefined
)LIROP(FromCharCode)LIROP(FromCharCodeEmptyIfNegative)LIROP(FromCharCodeUndefinedIfNegative
)LIROP(FromCodePoint)LIROP(StringIncludes)LIROP(StringIndexOf
)LIROP(StringLastIndexOf)LIROP(StringStartsWith)LIROP(StringEndsWith
)LIROP(StringTrimStartIndex)LIROP(StringTrimEndIndex)LIROP(StringSplit
)LIROP(ImplicitThis)LIROP(UnaryCache)LIROP(CheckOverRecursed)
LIROP(InterruptCheck)LIROP(WasmTrap)LIROP(ThrowRuntimeLexicalError
)LIROP(ThrowMsg)LIROP(GlobalDeclInstantiation)LIROP(RegExpMatcher
)LIROP(RegExpSearcher)LIROP(RegExpSearcherLastLimit)LIROP(RegExpExecMatch
)LIROP(RegExpExecTest)LIROP(Substr)LIROP(ModuleMetadata)LIROP
(DynamicImport)LIROP(SetFunName)LIROP(Slots)LIROP(Elements)LIROP
(InitializedLength)LIROP(SetInitializedLength)LIROP(ArrayLength
)LIROP(SetArrayLength)LIROP(FunctionLength)LIROP(FunctionName
)LIROP(ArrayBufferByteLength)LIROP(ArrayBufferViewLength)LIROP
(ArrayBufferViewByteOffset)LIROP(ArrayBufferViewElements)LIROP
(ResizableTypedArrayLength)LIROP(ResizableDataViewByteLength)
LIROP(GrowableSharedArrayBufferByteLength)LIROP(TypedArrayElementSize
)LIROP(GuardHasAttachedArrayBuffer)LIROP(GuardResizableArrayBufferViewInBounds
)LIROP(GuardResizableArrayBufferViewInBoundsOrDetached)LIROP(
TypedArraySet)LIROP(TypedArraySetFromSubarray)LIROP(GuardTypedArraySetOffset
)LIROP(ToIntegerIndex)LIROP(KeepAliveObject)LIROP(DebugEnterGCUnsafeRegion
)LIROP(DebugLeaveGCUnsafeRegion)LIROP(ArrayPush)LIROP(ArraySlice
)LIROP(ArgumentsSlice)LIROP(NormalizeSliceTerm)LIROP(ArrayJoin
)LIROP(ObjectKeys)LIROP(LoadFixedSlotFromOffset)LIROP(HomeObjectSuperBase
)LIROP(BindNameCache)LIROP(GuardShape)LIROP(HasShape)LIROP(GuardFuse
)LIROP(GuardMultipleShapes)LIROP(GuardShapeList)LIROP(GuardShapeListToOffset
)LIROP(GuardIsNativeObject)LIROP(GuardGlobalGeneration)LIROP(
GuardIsProxy)LIROP(GuardIsNotDOMProxy)LIROP(GuardIsNotProxy)LIROP
(ProxyGet)LIROP(ProxyGetByValue)LIROP(ProxySet)LIROP(ProxySetByValue
)LIROP(CallSetArrayLength)LIROP(MegamorphicLoadSlot)LIROP(MegamorphicLoadSlotPermissive
)LIROP(MegamorphicLoadSlotByValue)LIROP(MegamorphicLoadSlotByValuePermissive
)LIROP(MegamorphicStoreSlot)LIROP(MegamorphicHasProp)LIROP(SmallObjectVariableKeyHasProp
)LIROP(GuardToArrayBuffer)LIROP(GuardToSharedArrayBuffer)LIROP
(GuardIsNotArrayBufferMaybeShared)LIROP(GuardIsNonResizableTypedArray
)LIROP(GuardIsResizableTypedArray)LIROP(GuardHasProxyHandler)
LIROP(NurseryObject)LIROP(GuardNullOrUndefined)LIROP(GuardIsNotObject
)LIROP(GuardFunctionIsNonBuiltinCtor)LIROP(GuardFunctionKind)
LIROP(GuardFunctionScript)LIROP(GuardSpecificAtom)LIROP(GuardSpecificInt32
)LIROP(GuardStringToIndex)LIROP(GuardStringToInt32)LIROP(GuardStringToDouble
)LIROP(GuardNoDenseElements)LIROP(LoadDynamicSlotFromOffset)LIROP
(FunctionEnvironment)LIROP(HomeObject)LIROP(AllocateAndStoreSlot
)LIROP(GetNameCache)LIROP(CallGetIntrinsicValue)LIROP(DeleteProperty
)LIROP(DeleteElement)LIROP(IteratorLength)LIROP(LoadIteratorElement
)LIROP(ValueToIterator)LIROP(LoadSlotByIteratorIndex)LIROP(StoreSlotByIteratorIndex
)LIROP(LoadSlotByIteratorIndexIndexed)LIROP(StoreSlotByIteratorIndexIndexed
)LIROP(LoadDOMExpandoValue)LIROP(LoadDOMExpandoValueIgnoreGeneration
)LIROP(GuardDOMExpandoMissingOrGuardShape)LIROP(StringLength)
LIROP(OptimizeSpreadCallCache)LIROP(IteratorMore)LIROP(IteratorEnd
)LIROP(OptimizeGetIteratorCache)LIROP(GuardElementNotHole)LIROP
(NewPrivateName)LIROP(InstanceOfCache)LIROP(ArgumentsLength)LIROP
(GetFrameArgument)LIROP(GetFrameArgumentHole)LIROP(NewTarget)
LIROP(Rest)LIROP(AssertCanElidePostWriteBarrier)LIROP(NewNamedLambdaObject
)LIROP(IsConstructor)LIROP(IsCrossRealmArrayConstructor)LIROP
(IsObject)LIROP(IsSuspendedGenerator)LIROP(IsNullOrUndefined)
LIROP(ObjectClassToString)LIROP(CheckReturn)LIROP(CheckThis)LIROP
(AsyncResolve)LIROP(AsyncAwait)LIROP(GeneratorResume)LIROP(IsResumingGenerator
)LIROP(ResumeFrameArg)LIROP(ClearResumingGeneratorFlag)LIROP(
CheckThisReinit)LIROP(CanSkipAwait)LIROP(IncrementWarmUpCounter
)LIROP(AtomicPause)LIROP(CheckIsObj)LIROP(CheckObjCoercible)LIROP
(CheckClassHeritage)LIROP(DebugCheckSelfHosted)LIROP(IsPackedArray
)LIROP(GuardArrayIsPacked)LIROP(GuardElementsArePacked)LIROP(
GetPrototypeOf)LIROP(ObjectWithProto)LIROP(BuiltinObject)LIROP
(SuperFunction)LIROP(SuperFunctionAndUnbox)LIROP(InitHomeObject
)LIROP(IsTypedArrayConstructor)LIROP(LoadValueTag)LIROP(LoadWrapperTarget
)LIROP(LoadGetterSetterFunction)LIROP(GuardHasGetterSetter)LIROP
(GuardIsExtensible)LIROP(GuardInt32IsNonNegative)LIROP(GuardIntPtrIsNonNegative
)LIROP(GuardInt32Range)LIROP(GuardIndexIsNotDenseElement)LIROP
(GuardIndexIsValidUpdateOrAdd)LIROP(CallAddOrUpdateSparseElement
)LIROP(CallGetSparseElement)LIROP(CallNativeGetElement)LIROP(
CallNativeGetElementSuper)LIROP(CallObjectHasSparseElement)LIROP
(BigIntAsIntN)LIROP(BigIntAsUintN)LIROP(GuardNonGCThing)LIROP
(ToHashableNonGCThing)LIROP(ToHashableString)LIROP(ToHashableValue
)LIROP(HashNonGCThing)LIROP(HashString)LIROP(HashSymbol)LIROP
(HashBigInt)LIROP(HashObject)LIROP(HashValue)LIROP(SetObjectHasNonBigInt
)LIROP(SetObjectHasBigInt)LIROP(SetObjectHasValue)LIROP(SetObjectHasValueVMCall
)LIROP(SetObjectDelete)LIROP(SetObjectAdd)LIROP(SetObjectSize
)LIROP(MapObjectHasNonBigInt)LIROP(MapObjectHasBigInt)LIROP(MapObjectHasValue
)LIROP(MapObjectHasValueVMCall)LIROP(MapObjectGetNonBigInt)LIROP
(MapObjectGetBigInt)LIROP(MapObjectGetValue)LIROP(MapObjectGetValueVMCall
)LIROP(MapObjectDelete)LIROP(MapObjectSet)LIROP(MapObjectSize
)LIROP(WeakMapGetObject)LIROP(WeakMapHasObject)LIROP(WeakSetHasObject
)LIROP(DateFillLocalTimeSlots)LIROP(DateHoursFromSecondsIntoYear
)LIROP(DateMinutesFromSecondsIntoYear)LIROP(DateSecondsFromSecondsIntoYear
)LIROP(DateNow)LIROP(DateParse)LIROP(TimeClip)LIROP(LocalTimeToUTC
)LIROP(YearFromTime)LIROP(MonthFromTime)LIROP(DateFromTime)LIROP
(NewDateObject)LIROP(WasmBoundsCheckRange32)LIROP(WasmClampTable64Address
)LIROP(WasmFence)LIROP(WasmCallLandingPrePad)LIROP(WasmFindHandler
)LIROP(WasmResumeBarrier)LIROP(WasmUnarySimd128)LIROP(AddDisposableResource
)LIROP(TakeDisposeCapability)
8855# undef LIROP
8856#endif
8857 case LNode::Opcode::Invalid:
8858 default:
8859 MOZ_CRASH("Invalid LIR op")do { do { } while (false); MOZ_ReportCrash("" "Invalid LIR op"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8859); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid LIR op" ")"); do { MOZ_CrashSequence(__null
, 8859); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
8860 }
8861
8862#ifdef DEBUG1
8863 if (!counts) {
8864 emitDebugResultChecks(*iter);
8865 }
8866#endif
8867
8868 // To reduce the blast radius of OOM during codegen, bail out early if we've
8869 // OOM'ed.
8870 if (masm.oom()) {
8871 return false;
8872 }
8873 }
8874
8875 return true;
8876}
8877
8878bool CodeGenerator::generateOutOfLineBlocks() {
8879 AutoCreatedBy acb(masm, "CodeGeneratorShared::generateOutOfLineBlocks");
8880
8881 // Generate out of line basic blocks.
8882 // If we are generated some blocks at the end of the function, we need
8883 // to adjust the frame depth.
8884 if (!gen->branchHintingEnabled()) {
8885 return true;
8886 }
8887 masm.setFramePushed(frameDepth_);
8888
8889 const bool compilingWasm = gen->compilingWasm();
8890
8891 for (size_t i = 0; i < graph.numBlocks(); i++) {
8892 current = graph.getBlock(i);
8893
8894 if (gen->shouldCancel("Generate Code (block loop)")) {
8895 return false;
8896 }
8897
8898 if (current->isTrivial()) {
8899 continue;
8900 }
8901
8902 // If this block is marked as out of line, we need to generate it now.
8903 if (!current->isOutOfLine()) {
8904 continue;
8905 }
8906
8907 if (!generateBlock(current, i, counts_, compilingWasm)) {
8908 return false;
8909 }
8910 }
8911
8912 return !masm.oom();
8913}
8914
8915void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) {
8916 Register objReg = ToRegister(lir->output());
8917
8918 MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!lir->isCall())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8918); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 8918); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8919 saveLive(lir);
8920
8921 JSObject* templateObject = lir->mir()->templateObject();
8922
8923 if (templateObject) {
8924 pushArg(ImmGCPtr(templateObject->shape()));
8925 pushArg(Imm32(lir->mir()->length()));
8926
8927 using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>);
8928 callVM<Fn, NewArrayWithShape>(lir);
8929 } else {
8930 pushArg(Imm32(GenericObject));
8931 pushArg(Imm32(lir->mir()->length()));
8932
8933 using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind);
8934 callVM<Fn, NewArrayOperation>(lir);
8935 }
8936
8937 masm.storeCallPointerResult(objReg);
8938
8939 MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!lir->safepoint()->liveRegs().has(objReg))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8939); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { MOZ_CrashSequence(__null, 8939); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8940 restoreLive(lir);
8941}
8942
8943void CodeGenerator::visitAtan2D(LAtan2D* lir) {
8944 FloatRegister y = ToFloatRegister(lir->y());
8945 FloatRegister x = ToFloatRegister(lir->x());
8946
8947 using Fn = double (*)(double x, double y);
8948 masm.setupAlignedABICall();
8949 masm.passABIArg(y, ABIType::Float64);
8950 masm.passABIArg(x, ABIType::Float64);
8951 masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64);
8952
8953 MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(lir->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 8953); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 8953); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8954}
8955
8956void CodeGenerator::visitHypot(LHypot* lir) {
8957 uint32_t numArgs = lir->numArgs();
8958 masm.setupAlignedABICall();
8959
8960 for (uint32_t i = 0; i < numArgs; ++i) {
8961 masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64);
8962 }
8963
8964 switch (numArgs) {
8965 case 2: {
8966 using Fn = double (*)(double x, double y);
8967 masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64);
8968 break;
8969 }
8970 case 3: {
8971 using Fn = double (*)(double x, double y, double z);
8972 masm.callWithABI<Fn, hypot3>(ABIType::Float64);
8973 break;
8974 }
8975 case 4: {
8976 using Fn = double (*)(double x, double y, double z, double w);
8977 masm.callWithABI<Fn, hypot4>(ABIType::Float64);
8978 break;
8979 }
8980 default:
8981 MOZ_CRASH("Unexpected number of arguments to hypot function.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected number of arguments to hypot function."
, "./../../../../js/src/jit/CodeGenerator.cpp", 8981); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected number of arguments to hypot function."
")"); do { MOZ_CrashSequence(__null, 8981); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
8982 }
8983 MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(lir->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 8983); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 8983); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8984}
8985
8986void CodeGenerator::visitNewArray(LNewArray* lir) {
8987 Register objReg = ToRegister(lir->output());
8988 Register tempReg = ToRegister(lir->temp0());
8989 DebugOnly<uint32_t> length = lir->mir()->length();
8990
8991 MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8991); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT"
")"); do { MOZ_CrashSequence(__null, 8991); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8992
8993 if (lir->mir()->isVMCall()) {
8994 visitNewArrayCallVM(lir);
8995 return;
8996 }
8997
8998 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
8999 visitNewArrayCallVM(lir);
9000 masm.jump(ool.rejoin());
9001 });
9002 addOutOfLineCode(ool, lir->mir());
9003 TemplateObject templateObject(lir->mir()->templateObject());
9004#ifdef DEBUG1
9005 size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) -
9006 ObjectElements::VALUES_PER_HEADER;
9007 MOZ_ASSERT(length <= numInlineElements,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length <= numInlineElements)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(length <= numInlineElements
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"length <= numInlineElements" " (" "Inline allocation only supports inline elements"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 9008); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements"
")"); do { MOZ_CrashSequence(__null, 9008); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9008 "Inline allocation only supports inline elements")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length <= numInlineElements)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(length <= numInlineElements
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"length <= numInlineElements" " (" "Inline allocation only supports inline elements"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 9008); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements"
")"); do { MOZ_CrashSequence(__null, 9008); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9009#endif
9010 masm.createGCObject(objReg, tempReg, templateObject,
9011 lir->mir()->initialHeap(), ool->entry());
9012
9013 masm.bind(ool->rejoin());
9014}
9015
9016void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) {
9017 Register lengthReg = ToRegister(lir->length());
9018 Register objReg = ToRegister(lir->output());
9019 Register tempReg = ToRegister(lir->temp0());
9020
9021 JSObject* templateObject = lir->mir()->templateObject();
9022 gc::Heap initialHeap = lir->mir()->initialHeap();
9023
9024 using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length,
9025 gc::AllocSite*);
9026 OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>(
9027 lir, ArgList(ImmGCPtr(templateObject), lengthReg, ImmPtr(nullptr)),
9028 StoreRegisterTo(objReg));
9029
9030 bool canInline = true;
9031 size_t inlineLength = 0;
9032 if (templateObject->as<ArrayObject>().hasFixedElements()) {
9033 size_t numSlots =
9034 gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
9035 inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
9036 } else {
9037 canInline = false;
9038 }
9039
9040 if (canInline) {
9041 // Try to do the allocation inline if the template object is big enough
9042 // for the length in lengthReg. If the length is bigger we could still
9043 // use the template object and not allocate the elements, but it's more
9044 // efficient to do a single big allocation than (repeatedly) reallocating
9045 // the array later on when filling it.
9046 masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength),
9047 ool->entry());
9048
9049 TemplateObject templateObj(templateObject);
9050 masm.createGCObject(objReg, tempReg, templateObj, initialHeap,
9051 ool->entry());
9052
9053 size_t lengthOffset = NativeObject::offsetOfFixedElements() +
9054 ObjectElements::offsetOfLength();
9055 masm.store32(lengthReg, Address(objReg, lengthOffset));
9056 } else {
9057 masm.jump(ool->entry());
9058 }
9059
9060 masm.bind(ool->rejoin());
9061}
9062
9063void CodeGenerator::visitNewIterator(LNewIterator* lir) {
9064 Register objReg = ToRegister(lir->output());
9065 Register tempReg = ToRegister(lir->temp0());
9066
9067 OutOfLineCode* ool;
9068 switch (lir->mir()->type()) {
9069 case MNewIterator::ArrayIterator: {
9070 using Fn = ArrayIteratorObject* (*)(JSContext*);
9071 ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(),
9072 StoreRegisterTo(objReg));
9073 break;
9074 }
9075 case MNewIterator::StringIterator: {
9076 using Fn = StringIteratorObject* (*)(JSContext*);
9077 ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(),
9078 StoreRegisterTo(objReg));
9079 break;
9080 }
9081 case MNewIterator::RegExpStringIterator: {
9082 using Fn = RegExpStringIteratorObject* (*)(JSContext*);
9083 ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(),
9084 StoreRegisterTo(objReg));
9085 break;
9086 }
9087 default:
9088 MOZ_CRASH("unexpected iterator type")do { do { } while (false); MOZ_ReportCrash("" "unexpected iterator type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9088); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected iterator type" ")"); do { MOZ_CrashSequence
(__null, 9088); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
9089 }
9090
9091 TemplateObject templateObject(lir->mir()->templateObject());
9092 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
9093 ool->entry());
9094
9095 masm.bind(ool->rejoin());
9096}
9097
9098void CodeGenerator::visitNewTypedArrayInline(LNewTypedArrayInline* lir) {
9099 Register objReg = ToRegister(lir->output());
9100 Register tempReg = ToRegister(lir->temp0());
9101
9102 auto* templateObject = lir->mir()->templateObject();
9103 gc::Heap initialHeap = lir->mir()->initialHeap();
9104
9105 size_t n = templateObject->length();
9106 MOZ_ASSERT(n <= INT32_MAX,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(n <= (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)"
" (" "Template objects are only created for int32 lengths" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9107); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9107); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9107 "Template objects are only created for int32 lengths")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(n <= (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)"
" (" "Template objects are only created for int32 lengths" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9107); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9107); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9108
9109 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t);
9110 auto* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
9111 lir, ArgList(ImmGCPtr(templateObject), Imm32(n)),
9112 StoreRegisterTo(objReg));
9113
9114 TemplateObject templateObj(templateObject);
9115 masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry());
9116
9117 masm.initTypedArraySlotsInline(objReg, tempReg, templateObject);
9118
9119 masm.bind(ool->rejoin());
9120}
9121
9122void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) {
9123 Register output = ToRegister(lir->output());
9124 Register temp1Reg = ToRegister(lir->temp0());
9125 Register temp2Reg = ToRegister(lir->temp1());
9126 Register lengthReg = ToRegister(lir->temp2());
9127 Register temp4Reg = ToRegister(lir->temp3());
9128
9129 auto* templateObject = lir->mir()->templateObject();
9130 gc::Heap initialHeap = lir->mir()->initialHeap();
9131
9132 size_t n = templateObject->length();
9133 MOZ_ASSERT(n <= INT32_MAX,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(n <= (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)"
" (" "Template objects are only created for int32 lengths" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9134); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9134); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9134 "Template objects are only created for int32 lengths")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(n <= (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)"
" (" "Template objects are only created for int32 lengths" ")"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9134); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9134); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9135
9136 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
9137 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
9138 lir, ArgList(ImmGCPtr(templateObject), Imm32(n)),
9139 StoreRegisterTo(output));
9140
9141 TemplateObject templateObj(templateObject);
9142 masm.createGCObject(temp4Reg, temp1Reg, templateObj, initialHeap,
9143 ool->entry());
9144
9145 masm.move32(Imm32(n), lengthReg);
9146
9147 masm.initTypedArraySlots(temp4Reg, lengthReg, temp1Reg, temp2Reg,
9148 ool->entry(), templateObject);
9149 masm.mov(temp4Reg, output);
9150
9151 masm.bind(ool->rejoin());
9152}
9153
9154void CodeGenerator::visitNewTypedArrayDynamicLength(
9155 LNewTypedArrayDynamicLength* lir) {
9156 Register lengthReg = ToRegister(lir->length());
9157 Register output = ToRegister(lir->output());
9158 Register temp1Reg = ToRegister(lir->temp0());
9159 Register temp2Reg = ToRegister(lir->temp1());
9160 Register temp3Reg = ToRegister(lir->temp2());
9161
9162 JSObject* templateObject = lir->mir()->templateObject();
9163 gc::Heap initialHeap = lir->mir()->initialHeap();
9164
9165 auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>();
9166
9167 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
9168 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
9169 lir, ArgList(ImmGCPtr(templateObject), lengthReg),
9170 StoreRegisterTo(output));
9171
9172 TemplateObject templateObj(templateObject);
9173 masm.createGCObject(temp3Reg, temp1Reg, templateObj, initialHeap,
9174 ool->entry());
9175
9176 masm.initTypedArraySlots(temp3Reg, lengthReg, temp1Reg, temp2Reg,
9177 ool->entry(), ttemplate);
9178 masm.mov(temp3Reg, output);
9179
9180 masm.bind(ool->rejoin());
9181}
9182
9183void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) {
9184 pushArg(ToRegister(lir->array()));
9185 pushArg(ImmGCPtr(lir->mir()->templateObject()));
9186
9187 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject);
9188 callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir);
9189}
9190
9191void CodeGenerator::visitNewTypedArrayFromArrayBuffer(
9192 LNewTypedArrayFromArrayBuffer* lir) {
9193 pushArg(ToValue(lir->length()));
9194 pushArg(ToValue(lir->byteOffset()));
9195 pushArg(ToRegister(lir->arrayBuffer()));
9196 pushArg(ImmGCPtr(lir->mir()->templateObject()));
9197
9198 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject,
9199 HandleValue, HandleValue);
9200 callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir);
9201}
9202
9203void CodeGenerator::visitBindFunction(LBindFunction* lir) {
9204 Register target = ToRegister(lir->target());
9205 Register temp1 = ToRegister(lir->temp0());
9206 Register temp2 = ToRegister(lir->temp1());
9207
9208 // Try to allocate a new BoundFunctionObject we can pass to the VM function.
9209 // If this fails, we set temp1 to nullptr so we do the allocation in C++.
9210 TemplateObject templateObject(lir->mir()->templateObject());
9211 Label allocOk, allocFailed;
9212 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
9213 &allocFailed);
9214 masm.jump(&allocOk);
9215
9216 masm.bind(&allocFailed);
9217 masm.movePtr(ImmWord(0), temp1);
9218
9219 masm.bind(&allocOk);
9220
9221 // Set temp2 to the address of the first argument on the stack.
9222 // Note that the Value slots used for arguments are currently aligned for a
9223 // JIT call, even though that's not strictly necessary for calling into C++.
9224 uint32_t argc = lir->mir()->numStackArgs();
9225 if (JitStackValueAlignment > 1) {
9226 argc = AlignBytes(argc, JitStackValueAlignment);
9227 }
9228 uint32_t unusedStack = UnusedStackBytesForCall(argc);
9229 masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack),
9230 temp2);
9231
9232 pushArg(temp1);
9233 pushArg(Imm32(lir->mir()->numStackArgs()));
9234 pushArg(temp2);
9235 pushArg(target);
9236
9237 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*,
9238 uint32_t, Handle<BoundFunctionObject*>);
9239 callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir);
9240}
9241
9242void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) {
9243 Register output = ToRegister(lir->output());
9244 Register temp = ToRegister(lir->temp0());
9245
9246 JSObject* templateObj = lir->mir()->templateObj();
9247
9248 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>);
9249 OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>(
9250 lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output));
9251
9252 TemplateObject templateObject(templateObj);
9253 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9254 ool->entry());
9255
9256 masm.bind(ool->rejoin());
9257}
9258
9259void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) {
9260 Register objReg = ToRegister(lir->output());
9261
9262 MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!lir->isCall())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9262); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 9262); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9263 saveLive(lir);
9264
9265 JSObject* templateObject = lir->mir()->templateObject();
9266
9267 // If we're making a new object with a class prototype (that is, an object
9268 // that derives its class from its prototype instead of being
9269 // PlainObject::class_'d) from self-hosted code, we need a different init
9270 // function.
9271 switch (lir->mir()->mode()) {
9272 case MNewObject::ObjectLiteral: {
9273 MOZ_ASSERT(!templateObject)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!templateObject)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!templateObject))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!templateObject"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9273); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!templateObject" ")"); do { MOZ_CrashSequence
(__null, 9273); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9274 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
9275 pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
9276
9277 using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc);
9278 callVM<Fn, NewObjectOperation>(lir);
9279 break;
9280 }
9281 case MNewObject::ObjectCreate: {
9282 pushArg(ImmGCPtr(templateObject));
9283
9284 using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>);
9285 callVM<Fn, ObjectCreateWithTemplate>(lir);
9286 break;
9287 }
9288 }
9289
9290 masm.storeCallPointerResult(objReg);
9291
9292 MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!lir->safepoint()->liveRegs().has(objReg))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9292); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { MOZ_CrashSequence(__null, 9292); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9293 restoreLive(lir);
9294}
9295
9296static bool ShouldInitFixedSlots(MIRGenerator* gen, LNewPlainObject* lir,
9297 const Shape* shape, uint32_t nfixed) {
9298 // Look for StoreFixedSlot instructions following an object allocation
9299 // that write to this object before a GC is triggered or this object is
9300 // passed to a VM call. If all fixed slots will be initialized, the
9301 // allocation code doesn't need to set the slots to |undefined|.
9302
9303 if (nfixed == 0) {
9304 return false;
9305 }
9306
9307#ifdef DEBUG1
9308 // The bailAfter testing function can trigger a bailout between allocating the
9309 // object and initializing the slots.
9310 if (gen->options.ionBailAfterEnabled()) {
9311 return true;
9312 }
9313#endif
9314
9315 // Keep track of the fixed slots that are initialized. initializedSlots is
9316 // a bit mask with a bit for each slot.
9317 MOZ_ASSERT(nfixed <= NativeObject::MAX_FIXED_SLOTS)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nfixed <= NativeObject::MAX_FIXED_SLOTS)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(nfixed <= NativeObject::MAX_FIXED_SLOTS))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("nfixed <= NativeObject::MAX_FIXED_SLOTS"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9317); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS"
")"); do { MOZ_CrashSequence(__null, 9317); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9318 static_assert(NativeObject::MAX_FIXED_SLOTS <= 32,
9319 "Slot bits must fit in 32 bits");
9320 uint32_t initializedSlots = 0;
9321 uint32_t numInitialized = 0;
9322
9323 MInstruction* allocMir = lir->mir();
9324 MBasicBlock* block = allocMir->block();
9325
9326 // Skip the allocation instruction.
9327 MInstructionIterator iter = block->begin(allocMir);
9328 MOZ_ASSERT(*iter == allocMir)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*iter == allocMir)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(*iter == allocMir))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("*iter == allocMir"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9328); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "*iter == allocMir" ")"); do { MOZ_CrashSequence
(__null, 9328); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9329 iter++;
9330
9331 // Handle the leading shape guard, if present.
9332 for (; iter != block->end(); iter++) {
9333 if (iter->isConstant()) {
9334 // This instruction won't trigger a GC or read object slots.
9335 continue;
9336 }
9337 if (iter->isGuardShape()) {
9338 auto* guard = iter->toGuardShape();
9339 if (guard->object() != allocMir || guard->shape() != shape) {
9340 return true;
9341 }
9342 allocMir = guard;
9343 iter++;
9344 }
9345 break;
9346 }
9347
9348 for (; iter != block->end(); iter++) {
9349 if (iter->isConstant() || iter->isPostWriteBarrier()) {
9350 // These instructions won't trigger a GC or read object slots.
9351 continue;
9352 }
9353
9354 if (iter->isStoreFixedSlot()) {
9355 MStoreFixedSlot* store = iter->toStoreFixedSlot();
9356 if (store->object() != allocMir) {
9357 return true;
9358 }
9359
9360 // We may not initialize this object slot on allocation, so the
9361 // pre-barrier could read uninitialized memory. Simply disable
9362 // the barrier for this store: the object was just initialized
9363 // so the barrier is not necessary.
9364 store->setNeedsBarrier(false);
9365
9366 uint32_t slot = store->slot();
9367 MOZ_ASSERT(slot < nfixed)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slot < nfixed)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slot < nfixed))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("slot < nfixed"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9367); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slot < nfixed" ")"); do { MOZ_CrashSequence
(__null, 9367); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9368 if ((initializedSlots & (1 << slot)) == 0) {
9369 numInitialized++;
9370 initializedSlots |= (1 << slot);
9371
9372 if (numInitialized == nfixed) {
9373 // All fixed slots will be initialized.
9374 MOZ_ASSERT(uint32_t(std::popcount(initializedSlots)) == nfixed)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uint32_t(std::popcount(initializedSlots)) == nfixed)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(uint32_t(std::popcount(initializedSlots)) == nfixed)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uint32_t(std::popcount(initializedSlots)) == nfixed"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9374); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uint32_t(std::popcount(initializedSlots)) == nfixed"
")"); do { MOZ_CrashSequence(__null, 9374); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9375 return false;
9376 }
9377 }
9378 continue;
9379 }
9380
9381 // Unhandled instruction, assume it bails or reads object slots.
9382 return true;
9383 }
9384
9385 MOZ_CRASH("Shouldn't get here")do { do { } while (false); MOZ_ReportCrash("" "Shouldn't get here"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9385); AnnotateMozCrashReason
("MOZ_CRASH(" "Shouldn't get here" ")"); do { MOZ_CrashSequence
(__null, 9385); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
9386}
9387
9388void CodeGenerator::visitNewObject(LNewObject* lir) {
9389 Register objReg = ToRegister(lir->output());
9390 Register tempReg = ToRegister(lir->temp0());
9391
9392 if (lir->mir()->isVMCall()) {
9393 visitNewObjectVMCall(lir);
9394 return;
9395 }
9396
9397 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
9398 visitNewObjectVMCall(lir);
9399 masm.jump(ool.rejoin());
9400 });
9401 addOutOfLineCode(ool, lir->mir());
9402
9403 TemplateObject templateObject(lir->mir()->templateObject());
9404
9405 masm.createGCObject(objReg, tempReg, templateObject,
9406 lir->mir()->initialHeap(), ool->entry());
9407
9408 masm.bind(ool->rejoin());
9409}
9410
9411void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) {
9412 Register objReg = ToRegister(lir->output());
9413 Register temp0Reg = ToRegister(lir->temp0());
9414 Register temp1Reg = ToRegister(lir->temp1());
9415 Register shapeReg = ToRegister(lir->temp2());
9416
9417 auto* mir = lir->mir();
9418 const Shape* shape = mir->shape();
9419 gc::Heap initialHeap = mir->initialHeap();
9420 gc::AllocKind allocKind = mir->allocKind();
9421
9422 using Fn =
9423 JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap);
9424 OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>(
9425 lir,
9426 ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)),
9427 Imm32(int32_t(initialHeap))),
9428 StoreRegisterTo(objReg));
9429
9430 bool initContents =
9431 ShouldInitFixedSlots(gen, lir, shape, mir->numFixedSlots());
9432
9433 masm.movePtr(ImmGCPtr(shape), shapeReg);
9434 masm.createPlainGCObject(
9435 objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(),
9436 mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(),
9437 AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents);
9438
9439#ifdef DEBUG1
9440 // ShouldInitFixedSlots expects that the leading GuardShape will never fail,
9441 // so ensure the newly created object has the correct shape. Should the guard
9442 // ever fail, we may end up with uninitialized fixed slots, which can confuse
9443 // the GC.
9444 Label ok;
9445 masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg,
9446 &ok);
9447 masm.assumeUnreachable("Newly created object has the correct shape");
9448 masm.bind(&ok);
9449#endif
9450
9451 masm.bind(ool->rejoin());
9452}
9453
9454void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) {
9455 Register objReg = ToRegister(lir->output());
9456 Register temp0Reg = ToRegister(lir->temp0());
9457 Register shapeReg = ToRegister(lir->temp1());
9458
9459 auto* mir = lir->mir();
9460 uint32_t arrayLength = mir->length();
9461
9462 gc::AllocKind allocKind = GuessArrayGCKind(arrayLength);
9463 MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==
gc::FinalizeKind::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(&
ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9464); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 9464); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9464 gc::FinalizeKind::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==
gc::FinalizeKind::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(&
ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9464); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 9464); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9465 MOZ_ASSERT(!IsFinalizedKind(allocKind))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsFinalizedKind(allocKind))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsFinalizedKind(allocKind))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsFinalizedKind(allocKind)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9465); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFinalizedKind(allocKind)" ")"); do { MOZ_CrashSequence
(__null, 9465); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9466
9467 uint32_t slotCount = GetGCKindSlots(allocKind);
9468 MOZ_ASSERT(slotCount >= ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slotCount >= ObjectElements::VALUES_PER_HEADER)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(slotCount >= ObjectElements::VALUES_PER_HEADER)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("slotCount >= ObjectElements::VALUES_PER_HEADER"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9468); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER"
")"); do { MOZ_CrashSequence(__null, 9468); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9469 uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER;
9470
9471 const Shape* shape = mir->shape();
9472
9473 NewObjectKind objectKind =
9474 mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject;
9475
9476 using Fn =
9477 ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind);
9478 OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>(
9479 lir,
9480 ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)),
9481 StoreRegisterTo(objReg));
9482
9483 masm.movePtr(ImmGCPtr(shape), shapeReg);
9484 masm.createArrayWithFixedElements(
9485 objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0,
9486 allocKind, mir->initialHeap(), ool->entry(),
9487 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
9488 masm.bind(ool->rejoin());
9489}
9490
9491void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) {
9492 Register objReg = ToRegister(lir->output());
9493 Register tempReg = ToRegister(lir->temp0());
9494 const CompileInfo& info = lir->mir()->block()->info();
9495 gc::Heap heap = lir->mir()->initialHeap();
9496
9497 using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction, gc::Heap);
9498 OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>(
9499 lir, ArgList(info.funMaybeLazy(), Imm32(uint32_t(heap))),
9500 StoreRegisterTo(objReg));
9501
9502 TemplateObject templateObject(lir->mir()->templateObj());
9503
9504 masm.createGCObject(objReg, tempReg, templateObject, heap, ool->entry(),
9505 /* initContents = */ true,
9506 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
9507
9508 masm.bind(ool->rejoin());
9509}
9510
9511void CodeGenerator::visitNewCallObject(LNewCallObject* lir) {
9512 Register objReg = ToRegister(lir->output());
9513 Register tempReg = ToRegister(lir->temp0());
9514
9515 CallObject* templateObj = lir->mir()->templateObject();
9516 gc::Heap heap = lir->mir()->initialHeap();
9517
9518 // todo: should get a specialized fallback that passes site
9519 using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>, gc::Heap);
9520 OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>(
9521 lir, ArgList(ImmGCPtr(templateObj->sharedShape()), Imm32(uint32_t(heap))),
9522 StoreRegisterTo(objReg));
9523
9524 // Inline call object creation, using the OOL path only for tricky cases.
9525 TemplateObject templateObject(templateObj);
9526
9527 masm.createGCObject(objReg, tempReg, templateObject, heap, ool->entry(),
9528 /* initContents = */ true,
9529 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
9530
9531 masm.bind(ool->rejoin());
9532}
9533
9534void CodeGenerator::visitNewMapObject(LNewMapObject* lir) {
9535 Register output = ToRegister(lir->output());
9536 Register temp = ToRegister(lir->temp0());
9537
9538 // Note: pass nullptr for |proto| to use |Map.prototype|.
9539 using Fn = MapObject* (*)(JSContext*, HandleObject);
9540 auto* ool = oolCallVM<Fn, MapObject::create>(lir, ArgList(ImmPtr(nullptr)),
9541 StoreRegisterTo(output));
9542
9543 TemplateObject templateObject(lir->mir()->templateObject());
9544 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9545 ool->entry());
9546 masm.bind(ool->rejoin());
9547}
9548
9549void CodeGenerator::visitNewSetObject(LNewSetObject* lir) {
9550 Register output = ToRegister(lir->output());
9551 Register temp = ToRegister(lir->temp0());
9552
9553 // Note: pass nullptr for |proto| to use |Set.prototype|.
9554 using Fn = SetObject* (*)(JSContext*, HandleObject);
9555 auto* ool = oolCallVM<Fn, SetObject::create>(lir, ArgList(ImmPtr(nullptr)),
9556 StoreRegisterTo(output));
9557
9558 TemplateObject templateObject(lir->mir()->templateObject());
9559 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9560 ool->entry());
9561 masm.bind(ool->rejoin());
9562}
9563
9564void CodeGenerator::visitNewMapObjectFromIterable(
9565 LNewMapObjectFromIterable* lir) {
9566 ValueOperand iterable = ToValue(lir->iterable());
9567 Register output = ToRegister(lir->output());
9568 Register temp1 = ToRegister(lir->temp0());
9569 Register temp2 = ToRegister(lir->temp1());
9570
9571 // Allocate a new MapObject. If this fails we pass nullptr for
9572 // allocatedFromJit.
9573 Label failedAlloc, vmCall, done;
9574 TemplateObject templateObject(lir->mir()->templateObject());
9575 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
9576 &failedAlloc);
9577
9578 // We're done if |iterable| is null or undefined.
9579 masm.branchIfNotNullOrUndefined(iterable, &vmCall);
9580 masm.movePtr(temp1, output);
9581 masm.jump(&done);
9582
9583 masm.bind(&failedAlloc);
9584 masm.movePtr(ImmPtr(nullptr), temp1);
9585
9586 masm.bind(&vmCall);
9587
9588 pushArg(temp1); // allocatedFromJit
9589 pushArg(iterable);
9590 pushArg(ImmPtr(nullptr)); // proto
9591
9592 using Fn = MapObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>,
9593 Handle<MapObject*>);
9594 callVM<Fn, MapObject::createFromIterable>(lir);
9595
9596 masm.bind(&done);
9597}
9598
9599void CodeGenerator::visitNewSetObjectFromIterable(
9600 LNewSetObjectFromIterable* lir) {
9601 ValueOperand iterable = ToValue(lir->iterable());
9602 Register output = ToRegister(lir->output());
9603 Register temp1 = ToRegister(lir->temp0());
9604 Register temp2 = ToRegister(lir->temp1());
9605
9606 // Allocate a new SetObject. If this fails we pass nullptr for
9607 // allocatedFromJit.
9608 Label failedAlloc, vmCall, done;
9609 TemplateObject templateObject(lir->mir()->templateObject());
9610 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
9611 &failedAlloc);
9612
9613 // We're done if |iterable| is null or undefined.
9614 masm.branchIfNotNullOrUndefined(iterable, &vmCall);
9615 masm.movePtr(temp1, output);
9616 masm.jump(&done);
9617
9618 masm.bind(&failedAlloc);
9619 masm.movePtr(ImmPtr(nullptr), temp1);
9620
9621 masm.bind(&vmCall);
9622
9623 pushArg(temp1); // allocatedFromJit
9624 pushArg(iterable);
9625 pushArg(ImmPtr(nullptr)); // proto
9626
9627 using Fn = SetObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>,
9628 Handle<SetObject*>);
9629 callVM<Fn, SetObject::createFromIterable>(lir);
9630
9631 masm.bind(&done);
9632}
9633
9634void CodeGenerator::visitNewStringObject(LNewStringObject* lir) {
9635 Register input = ToRegister(lir->input());
9636 Register output = ToRegister(lir->output());
9637 Register temp = ToRegister(lir->temp0());
9638
9639 StringObject* templateObj = lir->mir()->templateObj();
9640
9641 using Fn = JSObject* (*)(JSContext*, HandleString);
9642 OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input),
9643 StoreRegisterTo(output));
9644
9645 TemplateObject templateObject(templateObj);
9646 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9647 ool->entry());
9648
9649 masm.loadStringLength(input, temp);
9650
9651 masm.storeValue(JSVAL_TYPE_STRING, input,
9652 Address(output, StringObject::offsetOfPrimitiveValue()));
9653 masm.storeValue(JSVAL_TYPE_INT32, temp,
9654 Address(output, StringObject::offsetOfLength()));
9655
9656 masm.bind(ool->rejoin());
9657}
9658
9659void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) {
9660 Register obj = ToRegister(lir->object());
9661 Register value = ToRegister(lir->value());
9662
9663 pushArg(value);
9664 pushArg(ToValue(lir->id()));
9665 pushArg(obj);
9666 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
9667
9668 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue,
9669 HandleObject);
9670 callVM<Fn, InitElemGetterSetterOperation>(lir);
9671}
9672
9673void CodeGenerator::visitMutateProto(LMutateProto* lir) {
9674 Register objReg = ToRegister(lir->object());
9675
9676 pushArg(ToValue(lir->value()));
9677 pushArg(objReg);
9678
9679 using Fn =
9680 bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value);
9681 callVM<Fn, MutatePrototype>(lir);
9682}
9683
9684void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) {
9685 Register obj = ToRegister(lir->object());
9686 Register value = ToRegister(lir->value());
9687
9688 pushArg(value);
9689 pushArg(ImmGCPtr(lir->mir()->name()));
9690 pushArg(obj);
9691 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
9692
9693 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject,
9694 Handle<PropertyName*>, HandleObject);
9695 callVM<Fn, InitPropGetterSetterOperation>(lir);
9696}
9697
9698void CodeGenerator::visitCreateThis(LCreateThis* lir) {
9699 const LAllocation* callee = lir->callee();
9700 const LAllocation* newTarget = lir->newTarget();
9701
9702 if (newTarget->isConstant()) {
9703 pushArg(ImmGCPtr(&newTarget->toConstant()->toObject()));
9704 } else {
9705 pushArg(ToRegister(newTarget));
9706 }
9707
9708 if (callee->isConstant()) {
9709 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
9710 } else {
9711 pushArg(ToRegister(callee));
9712 }
9713
9714 using Fn = bool (*)(JSContext* cx, HandleObject callee,
9715 HandleObject newTarget, MutableHandleValue rval);
9716 callVM<Fn, jit::CreateThisFromIon>(lir);
9717}
9718
9719void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) {
9720#ifdef DEBUG1
9721 // This should be getting constructed in the first block only, and not any OSR
9722 // entry blocks. Warp builds it in block 0 except in a generator or async
9723 // function, where block 0 forks between the fresh call and a resume.
9724 MBasicBlock* block = lir->mir()->block();
9725 JSScript* script = block->info().script();
9726 MOZ_ASSERT(block != block->graph().osrBlock())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(block != block->graph().osrBlock())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(block != block->graph().osrBlock()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("block != block->graph().osrBlock()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9726); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "block != block->graph().osrBlock()" ")"
); do { MOZ_CrashSequence(__null, 9726); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9727 MOZ_ASSERT_IF(!script->isGenerator() && !script->isAsync(), block->id() == 0)do { if (!script->isGenerator() && !script->isAsync
()) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(block->id() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(block->id() == 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("block->id() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9727); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "block->id() == 0" ")"); do { MOZ_CrashSequence
(__null, 9727); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
9728#endif
9729
9730 Register callObj = ToRegister(lir->callObject());
9731 Register temp0 = ToRegister(lir->temp0());
9732 Label done;
9733
9734 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
9735 Register objTemp = ToRegister(lir->temp1());
9736 Register cxTemp = ToRegister(lir->temp2());
9737
9738 masm.Push(callObj);
9739
9740 // Try to allocate an arguments object. This will leave the reserved
9741 // slots uninitialized, so it's important we don't GC until we
9742 // initialize these slots in ArgumentsObject::finishForIonPure.
9743 Label failure;
9744 TemplateObject templateObject(templateObj);
9745 masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default,
9746 &failure,
9747 /* initContents = */ false);
9748
9749 masm.moveStackPtrTo(temp0);
9750 masm.addPtr(Imm32(masm.framePushed()), temp0);
9751
9752 using Fn =
9753 ArgumentsObject* (*)(JSContext * cx, jit::JitFrameLayout * frame,
9754 JSObject * scopeChain, ArgumentsObject * obj);
9755 masm.setupAlignedABICall();
9756 masm.loadJSContext(cxTemp);
9757 masm.passABIArg(cxTemp);
9758 masm.passABIArg(temp0);
9759 masm.passABIArg(callObj);
9760 masm.passABIArg(objTemp);
9761
9762 masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>();
9763 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
9764
9765 // Discard saved callObj on the stack.
9766 masm.addToStackPtr(Imm32(sizeof(uintptr_t)));
9767 masm.jump(&done);
9768
9769 masm.bind(&failure);
9770 masm.Pop(callObj);
9771 }
9772
9773 masm.moveStackPtrTo(temp0);
9774 masm.addPtr(Imm32(frameSize()), temp0);
9775
9776 pushArg(callObj);
9777 pushArg(temp0);
9778
9779 using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject);
9780 callVM<Fn, ArgumentsObject::createForIon>(lir);
9781
9782 masm.bind(&done);
9783}
9784
9785void CodeGenerator::visitCreateInlinedArgumentsObject(
9786 LCreateInlinedArgumentsObject* lir) {
9787 Register callObj = ToRegister(lir->getCallObject());
9788 Register callee = ToRegister(lir->getCallee());
9789 Register argsAddress = ToRegister(lir->temp1());
9790 Register argsObj = ToRegister(lir->temp2());
9791
9792 // TODO: Do we have to worry about alignment here?
9793
9794 // Create a contiguous array of values for ArgumentsObject::create
9795 // by pushing the arguments onto the stack in reverse order.
9796 uint32_t argc = lir->mir()->numActuals();
9797 for (uint32_t i = 0; i < argc; i++) {
9798 uint32_t argNum = argc - i - 1;
9799 uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum);
9800 ConstantOrRegister arg =
9801 toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type());
9802 masm.Push(arg);
9803 }
9804 masm.moveStackPtrTo(argsAddress);
9805
9806 Label done;
9807 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
9808 LiveRegisterSet liveRegs;
9809 liveRegs.add(callObj);
9810 liveRegs.add(callee);
9811
9812 masm.PushRegsInMask(liveRegs);
9813
9814 // We are free to clobber all registers, as LCreateInlinedArgumentsObject is
9815 // a call instruction.
9816 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
9817 allRegs.take(callObj);
9818 allRegs.take(callee);
9819 allRegs.take(argsObj);
9820 allRegs.take(argsAddress);
9821
9822 Register temp3 = allRegs.takeAny();
9823 Register temp4 = allRegs.takeAny();
9824
9825 // Try to allocate an arguments object. This will leave the reserved slots
9826 // uninitialized, so it's important we don't GC until we initialize these
9827 // slots in ArgumentsObject::finishForIonPure.
9828 Label failure;
9829 TemplateObject templateObject(templateObj);
9830 masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default,
9831 &failure,
9832 /* initContents = */ false);
9833
9834 Register numActuals = temp3;
9835 masm.move32(Imm32(argc), numActuals);
9836
9837 using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*,
9838 uint32_t, ArgumentsObject*);
9839 masm.setupAlignedABICall();
9840 masm.loadJSContext(temp4);
9841 masm.passABIArg(temp4);
9842 masm.passABIArg(callObj);
9843 masm.passABIArg(callee);
9844 masm.passABIArg(argsAddress);
9845 masm.passABIArg(numActuals);
9846 masm.passABIArg(argsObj);
9847
9848 masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>();
9849 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
9850
9851 // Discard saved callObj, callee, and values array on the stack.
9852 masm.addToStackPtr(
9853 Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) +
9854 argc * sizeof(Value)));
9855 masm.jump(&done);
9856
9857 masm.bind(&failure);
9858 masm.PopRegsInMask(liveRegs);
9859
9860 // Reload argsAddress because it may have been overridden.
9861 masm.moveStackPtrTo(argsAddress);
9862 }
9863
9864 pushArg(Imm32(argc));
9865 pushArg(callObj);
9866 pushArg(callee);
9867 pushArg(argsAddress);
9868
9869 using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction,
9870 HandleObject, uint32_t);
9871 callVM<Fn, ArgumentsObject::createForInlinedIon>(lir);
9872
9873 // Discard the array of values.
9874 masm.freeStack(argc * sizeof(Value));
9875
9876 masm.bind(&done);
9877}
9878
9879template <class GetInlinedArgument>
9880void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir,
9881 Register index,
9882 ValueOperand output) {
9883 uint32_t numActuals = lir->mir()->numActuals();
9884 MOZ_ASSERT(numActuals <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(numActuals <= ArgumentsObject::MaxInlinedArgs)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(numActuals <= ArgumentsObject::MaxInlinedArgs))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("numActuals <= ArgumentsObject::MaxInlinedArgs"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9884); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 9884); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9885
9886 // The index has already been bounds-checked, so the code we
9887 // generate here should be unreachable. We can end up in this
9888 // situation in self-hosted code using GetArgument(), or in a
9889 // monomorphically inlined function if we've inlined some CacheIR
9890 // that was created for a different caller.
9891 if (numActuals == 0) {
9892 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
9893 return;
9894 }
9895
9896 // Check the first n-1 possible indices.
9897 Label done;
9898 for (uint32_t i = 0; i < numActuals - 1; i++) {
9899 Label skip;
9900 ConstantOrRegister arg = toConstantOrRegister(
9901 lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type());
9902 masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip);
9903 masm.moveValue(arg, output);
9904
9905 masm.jump(&done);
9906 masm.bind(&skip);
9907 }
9908
9909#ifdef DEBUG1
9910 Label skip;
9911 masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip);
9912 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
9913 masm.bind(&skip);
9914#endif
9915
9916 // The index has already been bounds-checked, so load the last argument.
9917 uint32_t lastIdx = numActuals - 1;
9918 ConstantOrRegister arg =
9919 toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx),
9920 lir->mir()->getArg(lastIdx)->type());
9921 masm.moveValue(arg, output);
9922 masm.bind(&done);
9923}
9924
9925void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) {
9926 Register index = ToRegister(lir->getIndex());
9927 ValueOperand output = ToOutValue(lir);
9928
9929 emitGetInlinedArgument(lir, index, output);
9930}
9931
9932void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) {
9933 Register index = ToRegister(lir->getIndex());
9934 ValueOperand output = ToOutValue(lir);
9935
9936 uint32_t numActuals = lir->mir()->numActuals();
9937
9938 if (numActuals == 0) {
9939 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
9940 masm.moveValue(UndefinedValue(), output);
9941 return;
9942 }
9943
9944 Label outOfBounds, done;
9945 masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals),
9946 &outOfBounds);
9947
9948 emitGetInlinedArgument(lir, index, output);
9949 masm.jump(&done);
9950
9951 masm.bind(&outOfBounds);
9952 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
9953 masm.moveValue(UndefinedValue(), output);
9954
9955 masm.bind(&done);
9956}
9957
9958void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) {
9959 Register temp = ToRegister(lir->temp0());
9960 Register argsObj = ToRegister(lir->argsObject());
9961 ValueOperand out = ToOutValue(lir);
9962
9963 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
9964 temp);
9965 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
9966 lir->mir()->argno() * sizeof(Value));
9967 masm.loadValue(argAddr, out);
9968#ifdef DEBUG1
9969 Label success;
9970 masm.branchTestMagic(Assembler::NotEqual, out, &success);
9971 masm.assumeUnreachable(
9972 "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
9973 masm.bind(&success);
9974#endif
9975}
9976
9977void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) {
9978 Register temp = ToRegister(lir->temp0());
9979 Register argsObj = ToRegister(lir->argsObject());
9980 ValueOperand value = ToValue(lir->value());
9981
9982 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
9983 temp);
9984 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
9985 lir->mir()->argno() * sizeof(Value));
9986 emitPreBarrier(argAddr);
9987#ifdef DEBUG1
9988 Label success;
9989 masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
9990 masm.assumeUnreachable(
9991 "Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
9992 masm.bind(&success);
9993#endif
9994 masm.storeValue(value, argAddr);
9995}
9996
9997void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) {
9998 Register temp = ToRegister(lir->temp0());
9999 Register argsObj = ToRegister(lir->argsObject());
10000 Register index = ToRegister(lir->index());
10001 ValueOperand out = ToOutValue(lir);
10002
10003 Label bail;
10004 masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail);
10005 bailoutFrom(&bail, lir->snapshot());
10006}
10007
10008void CodeGenerator::visitLoadArgumentsObjectArgHole(
10009 LLoadArgumentsObjectArgHole* lir) {
10010 Register temp = ToRegister(lir->temp0());
10011 Register argsObj = ToRegister(lir->argsObject());
10012 Register index = ToRegister(lir->index());
10013 ValueOperand out = ToOutValue(lir);
10014
10015 Label bail;
10016 masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail);
10017 bailoutFrom(&bail, lir->snapshot());
10018}
10019
10020void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) {
10021 Register temp = ToRegister(lir->temp0());
10022 Register argsObj = ToRegister(lir->argsObject());
10023 Register index = ToRegister(lir->index());
10024 Register out = ToRegister(lir->output());
10025
10026 Label bail;
10027 masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail);
10028 bailoutFrom(&bail, lir->snapshot());
10029}
10030
10031void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) {
10032 Register argsObj = ToRegister(lir->argsObject());
10033 Register out = ToRegister(lir->output());
10034
10035 Label bail;
10036 masm.loadArgumentsObjectLength(argsObj, out, &bail);
10037 bailoutFrom(&bail, lir->snapshot());
10038}
10039
10040void CodeGenerator::visitArrayFromArgumentsObject(
10041 LArrayFromArgumentsObject* lir) {
10042 pushArg(ToRegister(lir->argsObject()));
10043
10044 using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>);
10045 callVM<Fn, js::ArrayFromArgumentsObject>(lir);
10046}
10047
10048void CodeGenerator::visitGuardArgumentsObjectFlags(
10049 LGuardArgumentsObjectFlags* lir) {
10050 Register argsObj = ToRegister(lir->argsObject());
10051 Register temp = ToRegister(lir->temp0());
10052
10053 Label bail;
10054 masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(),
10055 Assembler::NonZero, &bail);
10056 bailoutFrom(&bail, lir->snapshot());
10057}
10058
10059void CodeGenerator::visitGuardObjectHasSameRealm(
10060 LGuardObjectHasSameRealm* lir) {
10061 Register obj = ToRegister(lir->object());
10062 Register temp = ToRegister(lir->temp0());
10063
10064 Label bail;
10065 masm.guardObjectHasSameRealm(obj, temp, &bail);
10066 bailoutFrom(&bail, lir->snapshot());
10067}
10068
10069void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) {
10070 Register obj = ToRegister(lir->object());
10071 Register output = ToRegister(lir->output());
10072
10073 masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()),
10074 output);
10075 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output);
10076}
10077
10078void CodeGenerator::visitGuardBoundFunctionIsConstructor(
10079 LGuardBoundFunctionIsConstructor* lir) {
10080 Register obj = ToRegister(lir->object());
10081
10082 Label bail;
10083 Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot());
10084 masm.branchTest32(Assembler::Zero, flagsSlot,
10085 Imm32(BoundFunctionObject::IsConstructorFlag), &bail);
10086 bailoutFrom(&bail, lir->snapshot());
10087}
10088
10089void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) {
10090 ValueOperand value = ToValue(lir->value());
10091 Register obj = ToRegister(lir->object());
10092 Register output = ToRegister(lir->output());
10093
10094 Label valueIsObject, end;
10095
10096 masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
10097
10098 // Value is not an object. Return that other object.
10099 masm.movePtr(obj, output);
10100 masm.jump(&end);
10101
10102 // Value is an object. Return unbox(Value).
10103 masm.bind(&valueIsObject);
10104 Register payload = masm.extractObject(value, output);
10105 if (payload != output) {
10106 masm.movePtr(payload, output);
10107 }
10108
10109 masm.bind(&end);
10110}
10111
10112void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) {
10113 ValueOperand value = ToValue(lir->value());
10114 Register output = ToRegister(lir->output());
10115
10116 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
10117 Label notNullOrUndefined;
10118 {
10119 Label isNullOrUndefined;
10120 ScratchTagScope tag(masm, value);
10121 masm.splitTagForTest(value, tag);
10122 masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined);
10123 masm.branchTestNull(Assembler::NotEqual, tag, &notNullOrUndefined);
10124 masm.bind(&isNullOrUndefined);
10125 masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output);
10126 masm.jump(ool.rejoin());
10127 }
10128
10129 masm.bind(&notNullOrUndefined);
10130
10131 saveLive(lir);
10132
10133 pushArg(value);
10134 using Fn = JSObject* (*)(JSContext*, HandleValue);
10135 callVM<Fn, BoxNonStrictThis>(lir);
10136
10137 StoreRegisterTo(output).generate(this);
10138 restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered());
10139
10140 masm.jump(ool.rejoin());
10141 });
10142 addOutOfLineCode(ool, lir->mir());
10143
10144 masm.fallibleUnboxObject(value, output, ool->entry());
10145 masm.bind(ool->rejoin());
10146}
10147
10148void CodeGenerator::visitImplicitThis(LImplicitThis* lir) {
10149 Register env = ToRegister(lir->env());
10150 ValueOperand output = ToOutValue(lir);
10151
10152 using Fn = void (*)(JSContext*, HandleObject, MutableHandleValue);
10153 auto* ool = oolCallVM<Fn, ImplicitThisOperation>(lir, ArgList(env),
10154 StoreValueTo(output));
10155
10156 masm.computeImplicitThis(env, output, ool->entry());
10157 masm.bind(ool->rejoin());
10158}
10159
10160void CodeGenerator::visitArrayLength(LArrayLength* lir) {
10161 Register elements = ToRegister(lir->elements());
10162 Register output = ToRegister(lir->output());
10163
10164 Address length(elements, ObjectElements::offsetOfLength());
10165 masm.load32(length, output);
10166
10167 bool intact = hasSeenArrayExceedsInt32LengthFuseIntactAndDependencyNoted();
10168
10169 if (intact) {
10170#ifdef DEBUG1
10171 Label done;
10172 masm.branchTest32(Assembler::NotSigned, output, output, &done);
10173 masm.assumeUnreachable("Unexpected array with length > INT32_MAX");
10174 masm.bind(&done);
10175#endif
10176 } else {
10177 // Bail out if the length doesn't fit in int32.
10178 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
10179 }
10180}
10181
10182void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) {
10183 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
10184 masm.store32(Imm32(lir->mir()->length()), length);
10185}
10186
10187void CodeGenerator::visitFunctionLength(LFunctionLength* lir) {
10188 Register function = ToRegister(lir->function());
10189 Register output = ToRegister(lir->output());
10190
10191 Label bail;
10192
10193 // Get the JSFunction flags.
10194 masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()),
10195 output);
10196
10197 // Functions with a SelfHostedLazyScript must be compiled with the slow-path
10198 // before the function length is known. If the length was previously resolved,
10199 // the length property may be shadowed.
10200 masm.branchTest32(
10201 Assembler::NonZero, output,
10202 Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH),
10203 &bail);
10204
10205 masm.loadFunctionLength(function, output, output, &bail);
10206
10207 bailoutFrom(&bail, lir->snapshot());
10208}
10209
10210void CodeGenerator::visitFunctionName(LFunctionName* lir) {
10211 Register function = ToRegister(lir->function());
10212 Register output = ToRegister(lir->output());
10213
10214 Label bail;
10215
10216 const JSAtomState& names = gen->runtime->names();
10217 masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail);
10218
10219 bailoutFrom(&bail, lir->snapshot());
10220}
10221
10222template <class TableObject>
10223static void TableIteratorLoadEntry(MacroAssembler&, Register, Register,
10224 Register);
10225
10226template <>
10227void TableIteratorLoadEntry<MapObject>(MacroAssembler& masm, Register iter,
10228 Register i, Register front) {
10229 masm.unboxObject(Address(iter, MapIteratorObject::offsetOfTarget()), front);
10230 masm.loadPrivate(Address(front, MapObject::offsetOfData()), front);
10231
10232 static_assert(MapObject::Table::offsetOfImplDataElement() == 0,
10233 "offsetof(Data, element) is 0");
10234 static_assert(MapObject::Table::sizeofImplData() == 24, "sizeof(Data) is 24");
10235 masm.mulBy3(i, i);
10236 masm.lshiftPtr(Imm32(3), i);
10237 masm.addPtr(i, front);
10238}
10239
10240template <>
10241void TableIteratorLoadEntry<SetObject>(MacroAssembler& masm, Register iter,
10242 Register i, Register front) {
10243 masm.unboxObject(Address(iter, SetIteratorObject::offsetOfTarget()), front);
10244 masm.loadPrivate(Address(front, SetObject::offsetOfData()), front);
10245
10246 static_assert(SetObject::Table::offsetOfImplDataElement() == 0,
10247 "offsetof(Data, element) is 0");
10248 static_assert(SetObject::Table::sizeofImplData() == 16, "sizeof(Data) is 16");
10249 masm.lshiftPtr(Imm32(4), i);
10250 masm.addPtr(i, front);
10251}
10252
10253template <class TableObject>
10254static void TableIteratorAdvance(MacroAssembler& masm, Register iter,
10255 Register front, Register dataLength,
10256 Register temp) {
10257 Register i = temp;
10258
10259 // Note: |count| and |index| are stored as PrivateUint32Value. We use add32
10260 // and store32 to change the payload.
10261 masm.add32(Imm32(1), Address(iter, TableIteratorObject::offsetOfCount()));
10262
10263 masm.unboxInt32(Address(iter, TableIteratorObject::offsetOfIndex()), i);
10264
10265 Label done, seek;
10266 masm.bind(&seek);
10267 masm.add32(Imm32(1), i);
10268 masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done);
10269
10270 // We can add sizeof(Data) to |front| to select the next element, because
10271 // |front| and |mapOrSetObject.data[i]| point to the same location.
10272 static_assert(TableObject::Table::offsetOfImplDataElement() == 0,
10273 "offsetof(Data, element) is 0");
10274 masm.addPtr(Imm32(TableObject::Table::sizeofImplData()), front);
10275
10276 masm.branchTestMagic(Assembler::Equal,
10277 Address(front, TableObject::Table::offsetOfEntryKey()),
10278 JS_HASH_KEY_EMPTY, &seek);
10279
10280 masm.bind(&done);
10281 masm.store32(i, Address(iter, TableIteratorObject::offsetOfIndex()));
10282}
10283
10284// Corresponds to TableIteratorObject::finish.
10285static void TableIteratorFinish(MacroAssembler& masm, Register iter,
10286 Register temp0, Register temp1) {
10287 Register next = temp0;
10288 Register prevp = temp1;
10289 masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfNext()), next);
10290 masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfPrevPtr()),
10291 prevp);
10292 masm.storePtr(next, Address(prevp, 0));
10293
10294 Label hasNoNext;
10295 masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext);
10296 masm.storePrivateValue(prevp,
10297 Address(next, TableIteratorObject::offsetOfPrevPtr()));
10298 masm.bind(&hasNoNext);
10299
10300 // Mark iterator inactive.
10301 Address targetAddr(iter, TableIteratorObject::offsetOfTarget());
10302 masm.guardedCallPreBarrier(targetAddr, MIRType::Value);
10303 masm.storeValue(UndefinedValue(), targetAddr);
10304}
10305
10306template <>
10307void CodeGenerator::emitLoadIteratorValues<MapObject>(Register result,
10308 Register temp,
10309 Register front) {
10310 size_t elementsOffset = NativeObject::offsetOfFixedElements();
10311
10312 Address keyAddress(front, MapObject::Table::Entry::offsetOfKey());
10313 Address valueAddress(front, MapObject::Table::Entry::offsetOfValue());
10314 Address keyElemAddress(result, elementsOffset);
10315 Address valueElemAddress(result, elementsOffset + sizeof(Value));
10316 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
10317 masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value);
10318 masm.storeValue(keyAddress, keyElemAddress, temp);
10319 masm.storeValue(valueAddress, valueElemAddress, temp);
10320
10321 Label emitBarrier, skipBarrier;
10322 masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp,
10323 &emitBarrier);
10324 masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp,
10325 &skipBarrier);
10326 {
10327 masm.bind(&emitBarrier);
10328 saveVolatile(temp);
10329 emitPostWriteBarrier(result);
10330 restoreVolatile(temp);
10331 }
10332 masm.bind(&skipBarrier);
10333}
10334
10335template <>
10336void CodeGenerator::emitLoadIteratorValues<SetObject>(Register result,
10337 Register temp,
10338 Register front) {
10339 size_t elementsOffset = NativeObject::offsetOfFixedElements();
10340
10341 Address keyAddress(front, SetObject::Table::offsetOfEntryKey());
10342 Address keyElemAddress(result, elementsOffset);
10343 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
10344 masm.storeValue(keyAddress, keyElemAddress, temp);
10345
10346 Label skipBarrier;
10347 masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp,
10348 &skipBarrier);
10349 {
10350 saveVolatile(temp);
10351 emitPostWriteBarrier(result);
10352 restoreVolatile(temp);
10353 }
10354 masm.bind(&skipBarrier);
10355}
10356
10357template <class IteratorObject, class TableObject>
10358void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) {
10359 Register iter = ToRegister(lir->iter());
10360 Register result = ToRegister(lir->result());
10361 Register temp = ToRegister(lir->temp0());
10362 Register dataLength = ToRegister(lir->temp1());
10363 Register front = ToRegister(lir->temp2());
10364 Register output = ToRegister(lir->output());
10365
10366#ifdef DEBUG1
10367 // Self-hosted code is responsible for ensuring GetNextEntryForIterator is
10368 // only called with the correct iterator class. Assert here all self-
10369 // hosted callers of GetNextEntryForIterator perform this class check.
10370 // No Spectre mitigations are needed because this is DEBUG-only code.
10371 Label success;
10372 masm.branchTestObjClassNoSpectreMitigations(
10373 Assembler::Equal, iter, &IteratorObject::class_, temp, &success);
10374 masm.assumeUnreachable("Iterator object should have the correct class.");
10375 masm.bind(&success);
10376#endif
10377
10378 // If the iterator has no target, it's already done.
10379 // See TableIteratorObject::isActive.
10380 Label iterAlreadyDone, iterDone, done;
10381 masm.branchTestUndefined(Assembler::Equal,
10382 Address(iter, IteratorObject::offsetOfTarget()),
10383 &iterAlreadyDone);
10384
10385 // Load |iter->index| in |temp| and |iter->target->dataLength| in
10386 // |dataLength|. Both values are stored as PrivateUint32Value.
10387 masm.unboxInt32(Address(iter, IteratorObject::offsetOfIndex()), temp);
10388 masm.unboxObject(Address(iter, IteratorObject::offsetOfTarget()), dataLength);
10389 masm.unboxInt32(Address(dataLength, TableObject::offsetOfDataLength()),
10390 dataLength);
10391 masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone);
10392 {
10393 TableIteratorLoadEntry<TableObject>(masm, iter, temp, front);
10394
10395 emitLoadIteratorValues<TableObject>(result, temp, front);
10396
10397 TableIteratorAdvance<TableObject>(masm, iter, front, dataLength, temp);
10398
10399 masm.move32(Imm32(0), output);
10400 masm.jump(&done);
10401 }
10402 {
10403 masm.bind(&iterDone);
10404 TableIteratorFinish(masm, iter, temp, dataLength);
10405
10406 masm.bind(&iterAlreadyDone);
10407 masm.move32(Imm32(1), output);
10408 }
10409 masm.bind(&done);
10410}
10411
10412void CodeGenerator::visitGetNextEntryForIterator(
10413 LGetNextEntryForIterator* lir) {
10414 if (lir->mir()->mode() == MGetNextEntryForIterator::Map) {
10415 emitGetNextEntryForIterator<MapIteratorObject, MapObject>(lir);
10416 } else {
10417 MOZ_ASSERT(lir->mir()->mode() == MGetNextEntryForIterator::Set)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->mode() == MGetNextEntryForIterator
::Set)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->mode() == MGetNextEntryForIterator
::Set))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->mir()->mode() == MGetNextEntryForIterator::Set",
"./../../../../js/src/jit/CodeGenerator.cpp", 10417); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set"
")"); do { MOZ_CrashSequence(__null, 10417); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10418 emitGetNextEntryForIterator<SetIteratorObject, SetObject>(lir);
10419 }
10420}
10421
10422// The point of these is to inform Ion of where these values already are; they
10423// don't normally generate (much) code.
10424void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {}
10425void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {}
10426void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {}
10427
10428void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) {
10429 LAllocation* output = lir->getDef(0)->output();
10430 MOZ_ASSERT(output->isStackArea())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output->isStackArea())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output->isStackArea()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("output->isStackArea()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10430); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output->isStackArea()" ")"); do { MOZ_CrashSequence
(__null, 10430); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10431 bool tempInit = false;
10432 for (auto iter = output->toStackArea()->results(); iter; iter.next()) {
10433 // Zero out ref stack results.
10434 if (iter.isWasmAnyRef()) {
10435 Register temp = ToRegister(lir->temp0());
10436 if (!tempInit) {
10437 masm.xorPtr(temp, temp);
10438 tempInit = true;
10439 }
10440 masm.storePtr(temp, ToAddress(iter.alloc()));
10441 }
10442 }
10443}
10444
10445void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) {
10446#ifdef JS_64BIT1
10447 if (MWasmRegisterResult* mir = lir->mir()) {
10448 if (mir->type() == MIRType::Int32) {
10449 masm.widenInt32(ToRegister(lir->output()));
10450 }
10451 }
10452#endif
10453}
10454
10455void CodeGenerator::visitWasmSystemFloatRegisterResult(
10456 LWasmSystemFloatRegisterResult* lir) {
10457 MOZ_ASSERT(lir->mir()->type() == MIRType::Float32 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Float32 || lir->
mir()->type() == MIRType::Double)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mir()->type() == MIRType
::Float32 || lir->mir()->type() == MIRType::Double))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10458); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 10458); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10458 lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Float32 || lir->
mir()->type() == MIRType::Double)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mir()->type() == MIRType
::Float32 || lir->mir()->type() == MIRType::Double))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10458); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 10458); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10459 MOZ_ASSERT_IF(lir->mir()->type() == MIRType::Float32,do { if (lir->mir()->type() == MIRType::Float32) { do {
static_assert( mozilla::detail::AssertionConditionType<decltype
(ToFloatRegister(lir->output()) == ReturnFloat32Reg)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(lir->output()) == ReturnFloat32Reg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(lir->output()) == ReturnFloat32Reg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 10460); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
10460 ToFloatRegister(lir->output()) == ReturnFloat32Reg)do { if (lir->mir()->type() == MIRType::Float32) { do {
static_assert( mozilla::detail::AssertionConditionType<decltype
(ToFloatRegister(lir->output()) == ReturnFloat32Reg)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(lir->output()) == ReturnFloat32Reg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(lir->output()) == ReturnFloat32Reg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 10460); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
10461 MOZ_ASSERT_IF(lir->mir()->type() == MIRType::Double,do { if (lir->mir()->type() == MIRType::Double) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(ToFloatRegister
(lir->output()) == ReturnDoubleReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ToFloatRegister(lir->output
()) == ReturnDoubleReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ToFloatRegister(lir->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10462); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 10462); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
10462 ToFloatRegister(lir->output()) == ReturnDoubleReg)do { if (lir->mir()->type() == MIRType::Double) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(ToFloatRegister
(lir->output()) == ReturnDoubleReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ToFloatRegister(lir->output
()) == ReturnDoubleReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ToFloatRegister(lir->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10462); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 10462); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
10463
10464#ifdef JS_CODEGEN_ARM
10465 MWasmSystemFloatRegisterResult* mir = lir->mir();
10466 if (!mir->hardFP()) {
10467 if (mir->type() == MIRType::Float32) {
10468 // Move float32 from r0 to ReturnFloatReg.
10469 masm.ma_vxfer(r0, ReturnFloat32Reg);
10470 } else if (mir->type() == MIRType::Double) {
10471 // Move double from r0/r1 to ReturnDoubleReg.
10472 masm.ma_vxfer(r0, r1, ReturnDoubleReg);
10473 } else {
10474 MOZ_CRASH("SIMD type not supported")do { do { } while (false); MOZ_ReportCrash("" "SIMD type not supported"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10474); AnnotateMozCrashReason
("MOZ_CRASH(" "SIMD type not supported" ")"); do { MOZ_CrashSequence
(__null, 10474); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
10475 }
10476 }
10477#elif JS_CODEGEN_X86
10478 MWasmSystemFloatRegisterResult* mir = lir->mir();
10479 if (mir->type() == MIRType::Double) {
10480 masm.reserveStack(sizeof(double));
10481 masm.fstp(Operand(esp, 0));
10482 masm.loadDouble(Operand(esp, 0), ReturnDoubleReg);
10483 masm.freeStack(sizeof(double));
10484 } else if (mir->type() == MIRType::Float32) {
10485 masm.reserveStack(sizeof(float));
10486 masm.fstp32(Operand(esp, 0));
10487 masm.loadFloat32(Operand(esp, 0), ReturnFloat32Reg);
10488 masm.freeStack(sizeof(float));
10489 }
10490#endif
10491}
10492
10493void CodeGenerator::visitWasmCall(LWasmCall* lir) {
10494 const MWasmCallBase* callBase = lir->callBase();
10495 bool isReturnCall = lir->isReturnCall();
10496
10497 // If this call is in Wasm try code block, initialise a wasm::TryNote for this
10498 // call.
10499 bool inTry = callBase->inTry();
10500 if (inTry) {
10501 size_t tryNoteIndex = callBase->tryNoteIndex();
10502 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10503 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10504 tryNote.setTryBodyBegin(masm.currentOffset());
10505 }
10506
10507 MOZ_ASSERT((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment
== 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10508); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { MOZ_CrashSequence(__null, 10508); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10508 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment
== 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10508); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { MOZ_CrashSequence(__null, 10508); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10509 static_assert(
10510 WasmStackAlignment >= ABIStackAlignment &&
10511 WasmStackAlignment % ABIStackAlignment == 0,
10512 "The wasm stack alignment should subsume the ABI-required alignment");
10513
10514#ifdef DEBUG1
10515 Label ok;
10516 masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok);
10517 masm.breakpoint();
10518 masm.bind(&ok);
10519#endif
10520
10521 // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the
10522 // instance and pinned regs. The only case where where we don't have to
10523 // reload the instance and pinned regs is when the callee preserves them.
10524 bool reloadInstance = true;
10525 bool reloadPinnedRegs = true;
10526 bool switchRealm = true;
10527
10528 const wasm::CallSiteDesc& desc = callBase->desc();
10529 const wasm::CalleeDesc& callee = callBase->callee();
10530 CodeOffset retOffset;
10531 CodeOffset secondRetOffset;
10532 switch (callee.which()) {
10533 case wasm::CalleeDesc::Func:
10534 if (isReturnCall) {
10535 ReturnCallAdjustmentInfo retCallInfo(
10536 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10537 masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo);
10538 // The rest of the method is unnecessary for a return call.
10539 return;
10540 }
10541 MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isReturnCall)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isReturnCall",
"./../../../../js/src/jit/CodeGenerator.cpp", 10541); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10541); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10542 retOffset = masm.call(desc, callee.funcIndex());
10543 reloadInstance = false;
10544 reloadPinnedRegs = false;
10545 switchRealm = false;
10546 break;
10547 case wasm::CalleeDesc::Import:
10548 if (isReturnCall) {
10549 ReturnCallAdjustmentInfo retCallInfo(
10550 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10551 masm.wasmReturnCallImport(desc, callee, retCallInfo);
10552 // The rest of the method is unnecessary for a return call.
10553 return;
10554 }
10555 MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isReturnCall)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isReturnCall",
"./../../../../js/src/jit/CodeGenerator.cpp", 10555); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10555); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10556 retOffset = masm.wasmCallImport(desc, callee);
10557 break;
10558 case wasm::CalleeDesc::WasmTable: {
10559 Label* nullCheckFailed = nullptr;
10560#ifndef WASM_HAS_HEAPREG1
10561 {
10562 auto* ool = new (
10563 alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
10564 masm.wasmTrap(wasm::Trap::IndirectCallToNull, desc.toTrapSiteDesc());
10565 });
10566 if (lir->isCatchable()) {
10567 addOutOfLineCode(ool, lir->mirCatchable());
10568 } else if (isReturnCall) {
10569 addOutOfLineCode(ool, lir->mirReturnCall());
10570 } else {
10571 addOutOfLineCode(ool, lir->mirUncatchable());
10572 }
10573 nullCheckFailed = ool->entry();
10574 }
10575#endif
10576 if (isReturnCall) {
10577 ReturnCallAdjustmentInfo retCallInfo(
10578 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10579 masm.wasmReturnCallIndirect(desc, callee, nullCheckFailed, retCallInfo);
10580 // The rest of the method is unnecessary for a return call.
10581 return;
10582 }
10583 MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isReturnCall)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isReturnCall",
"./../../../../js/src/jit/CodeGenerator.cpp", 10583); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10583); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10584 masm.wasmCallIndirect(desc, callee, nullCheckFailed, &retOffset,
10585 &secondRetOffset);
10586 // Register reloading and realm switching are handled dynamically inside
10587 // wasmCallIndirect. There are two return offsets, one for each call
10588 // instruction (fast path and slow path).
10589 reloadInstance = false;
10590 reloadPinnedRegs = false;
10591 switchRealm = false;
10592 break;
10593 }
10594 case wasm::CalleeDesc::Builtin:
10595 retOffset = masm.call(desc, callee.builtin());
10596 // The builtin ABI preserves the instance and pinned registers. However,
10597 // builtins may grow the memory which requires us to reload the pinned
10598 // registers.
10599 reloadInstance = false;
10600 reloadPinnedRegs = true;
10601 switchRealm = false;
10602 break;
10603 case wasm::CalleeDesc::BuiltinInstanceMethod: {
10604 CodeOffset unused_trapStackMapKey;
10605 masm.wasmCallBuiltinInstanceMethod(desc, callBase->instanceArg(),
10606 callee.builtin(),
10607 callBase->builtinMethodFailureMode(),
10608 callBase->builtinMethodFailureTrap(),
10609 &retOffset, &unused_trapStackMapKey);
10610 // The builtin ABI preserves the instance and pinned registers. However,
10611 // builtins may grow the memory which requires us to reload the pinned
10612 // registers.
10613 reloadInstance = false;
10614 reloadPinnedRegs = true;
10615 switchRealm = false;
10616 break;
10617 }
10618 case wasm::CalleeDesc::FuncRef:
10619 if (isReturnCall) {
10620 ReturnCallAdjustmentInfo retCallInfo(
10621 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10622 masm.wasmReturnCallRef(desc, callee, retCallInfo);
10623 // The rest of the method is unnecessary for a return call.
10624 return;
10625 }
10626 MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isReturnCall)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isReturnCall",
"./../../../../js/src/jit/CodeGenerator.cpp", 10626); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10626); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10627 // Register reloading and realm switching are handled dynamically inside
10628 // wasmCallRef. There are two return offsets, one for each call
10629 // instruction (fast path and slow path).
10630 masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset);
10631 reloadInstance = false;
10632 reloadPinnedRegs = false;
10633 switchRealm = false;
10634 break;
10635 }
10636
10637 // Note the assembler offset for the associated LSafePoint.
10638 MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isReturnCall)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!isReturnCall",
"./../../../../js/src/jit/CodeGenerator.cpp", 10638); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10638); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10639 markSafepointAt(retOffset.offset(), lir);
10640
10641 // Now that all the outbound in-memory args are on the stack, note the
10642 // required lower boundary point of the associated StackMap.
10643 uint32_t framePushedAtStackMapBase =
10644 masm.framePushed() -
10645 wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned());
10646 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase);
10647 MOZ_ASSERT(lir->safepoint()->wasmSafepointKind() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind
::LirCall)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind
() == WasmSafepointKind::LirCall))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10648); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { MOZ_CrashSequence(__null, 10648); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10648 WasmSafepointKind::LirCall)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind
::LirCall)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind
() == WasmSafepointKind::LirCall))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10648); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { MOZ_CrashSequence(__null, 10648); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10649
10650 // Indirect calls (WasmTable/FuncRef) emit two call instructions (fast and
10651 // slow path) with two distinct return offsets. The two paths are mutually
10652 // exclusive and rejoin with the same live references and frame layout, so a
10653 // single stackmap serves both: register this call's LSafepoint a second time
10654 // at the slow-path return offset.
10655 if (callee.which() == wasm::CalleeDesc::WasmTable ||
10656 callee.which() == wasm::CalleeDesc::FuncRef) {
10657 markSafepointAt(secondRetOffset.offset(), lir);
10658 }
10659
10660 if (reloadInstance) {
10661 masm.loadPtr(
10662 Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
10663 InstanceReg);
10664 if (switchRealm) {
10665 masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
10666 }
10667 } else {
10668 MOZ_ASSERT(!switchRealm)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!switchRealm)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!switchRealm))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!switchRealm", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10668); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm"
")"); do { MOZ_CrashSequence(__null, 10668); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10669 }
10670 if (reloadPinnedRegs) {
10671 masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing());
10672 }
10673
10674 switch (callee.which()) {
10675 case wasm::CalleeDesc::Func:
10676 case wasm::CalleeDesc::Import:
10677 case wasm::CalleeDesc::WasmTable:
10678 case wasm::CalleeDesc::FuncRef:
10679 // Stack allocation could change during Wasm (return) calls,
10680 // recover pre-call state.
10681 masm.freeStackTo(masm.framePushed());
10682 break;
10683 default:
10684 break;
10685 }
10686
10687 if (inTry) {
10688 // Set the end of the try note range
10689 size_t tryNoteIndex = callBase->tryNoteIndex();
10690 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10691 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10692
10693 // Don't set the end of the try note if we've OOM'ed, as the above
10694 // instructions may not have been emitted, which will trigger an assert
10695 // about zero-length try-notes. This is okay as this compilation will be
10696 // thrown away.
10697 if (!masm.oom()) {
10698 tryNote.setTryBodyEnd(masm.currentOffset());
10699 }
10700
10701 // This instruction must be the last instruction in the block. No other
10702 // instructions may be inserted.
10703 LBlock* block = lir->block();
10704 MOZ_RELEASE_ASSERT(*block->rbegin() == lir)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*block->rbegin() == lir)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10704); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir" ")"); do
{ MOZ_CrashSequence(__null, 10704); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
;
10705
10706 // Jump to the fallthrough block
10707 jumpToBlock(lir->mirCatchable()->getSuccessor(
10708 MWasmCallCatchable::FallthroughBranchIndex));
10709 }
10710}
10711
10712#ifdef ENABLE_WASM_JSPI1
10713void CodeGenerator::visitWasmFindHandler(LWasmFindHandler* lir) {
10714 MWasmFindHandler* mir = lir->mir();
10715 Register instance = ToRegister(lir->instance());
10716 Register tag = ToRegister(lir->tag());
10717 Register output = ToRegister(lir->output());
10718 Register scratch1 = ToRegister(lir->temp0());
10719 Register scratch2 = ToRegister(lir->temp1());
10720 Register scratch3 = ToRegister(lir->temp2());
10721 Register scratch4 = ToRegister(lir->temp3());
10722 const wasm::Trap& trap = mir->trap();
10723 const wasm::TrapSiteDesc& trapSiteDesc = mir->trapSiteDesc();
10724
10725 auto* ool = new (alloc())
10726 LambdaOutOfLineCode([this, trap, trapSiteDesc](OutOfLineCode& ool) {
10727 masm.wasmTrap(trap, trapSiteDesc);
10728 });
10729 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
10730 wasm::EmitFindHandler(masm, instance, tag, output, scratch1, scratch2,
10731 scratch3, scratch4, ool->entry());
10732}
10733
10734void CodeGenerator::visitWasmSuspend(LWasmSuspend* lir) {
10735 Register instance = ToRegister(lir->instance());
10736 Register suspendedCont = ToRegister(lir->suspendedCont());
10737 Register handler = ToRegister(lir->handler());
10738 Register scratch1 = ToRegister(lir->temp0());
10739 Register scratch2 = ToRegister(lir->temp1());
10740 Register scratch3 = ToRegister(lir->temp2());
10741
10742 uint32_t suspendResultsAreaBase =
10743 lir->mir()->suspendResultsArea()->toWasmStackResultArea()->base();
10744
10745 CodeOffset suspendedCodeOffset;
10746 uint32_t suspendedFramePushed;
10747 wasm::EmitSuspend(masm, instance, suspendedCont, handler, scratch1, scratch2,
10748 scratch3, lir->mir()->callSiteDesc(), &suspendedCodeOffset,
10749 &suspendedFramePushed, suspendResultsAreaBase);
10750
10751 if (masm.oom()) {
10752 return;
10753 }
10754
10755 markSafepointAt(suspendedCodeOffset.offset(), lir);
10756 lir->safepoint()->setFramePushedAtStackMapBase(suspendedFramePushed);
10757 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
10758}
10759
10760void CodeGenerator::visitWasmPrepareResume(LWasmPrepareResume* lir) {
10761 MWasmPrepareResume* mir = lir->mir();
10762 Register cont = ToRegister(lir->cont());
10763 Register output = ToRegister(lir->output());
10764 Register scratch1 = ToRegister(lir->temp0());
10765 Register scratch2 = ToRegister(lir->temp1());
10766 uint32_t resumeParamsAreaBase =
10767 mir->resumeParamsArea()->toWasmStackResultArea()->base();
10768 wasm::TrapSiteDesc trapSiteDesc = mir->trapSiteDesc();
10769
10770 auto* ool = new (alloc())
10771 LambdaOutOfLineCode([this, trapSiteDesc](OutOfLineCode& ool) {
10772 masm.wasmTrap(wasm::Trap::NullPointerDereference, trapSiteDesc);
10773 });
10774 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
10775
10776 wasm::EmitPrepareResume(masm, cont, resumeParamsAreaBase, output, scratch1,
10777 scratch2, ool->entry());
10778}
10779
10780void CodeGenerator::visitWasmResume(LWasmResume* lir) {
10781 MWasmResume* mir = lir->mir();
10782 Register instance = ToRegister(lir->instance());
10783 Register cont = ToRegister(lir->cont());
10784 uint32_t handlersParamsAreaBase = mir->handlersParamsArea()->base();
10785 uint32_t contResultsAreaBase = mir->contResultsArea()->base();
10786 Register scratch1 = ToRegister(lir->temp0());
10787 Register scratch2 = ToRegister(lir->temp1());
10788 Register scratch3 = ToRegister(lir->temp2());
10789
10790 // If this resume is in a wasm try code block, initialise a wasm::TryNote for
10791 // this resume.
10792 bool inTry = mir->hasTryNote();
10793 if (inTry) {
10794 size_t tryNoteIndex = mir->tryNoteIndex().value();
10795 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10796 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10797 tryNote.setTryBodyBegin(masm.currentOffset());
10798 }
10799
10800 mozilla::Vector<jit::Label*, 2, JitAllocPolicy> handlerLabels(alloc());
10801 if (!handlerLabels.reserve(mir->numHandlers())) {
10802 masm.setOOM();
10803 return;
10804 }
10805 for (size_t i = 0; i < mir->numHandlers(); i++) {
10806 handlerLabels.infallibleAppend(getJumpLabelForBranch(mir->handlerBlock(i)));
10807 }
10808
10809 CodeOffset resumeCodeOffset;
10810 uint32_t resumeFramePushed;
10811 wasm::EmitResume(masm, instance, cont, handlersParamsAreaBase, scratch1,
10812 scratch2, scratch3, mir->handlers(), handlerLabels,
10813 mir->callSiteDesc(), &resumeCodeOffset, &resumeFramePushed,
10814 contResultsAreaBase);
10815
10816 if (masm.oom()) {
10817 return;
10818 }
10819
10820 markSafepointAt(resumeCodeOffset.offset(), lir);
10821 lir->safepoint()->setFramePushedAtStackMapBase(resumeFramePushed);
10822 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
10823
10824 if (inTry) {
10825 // Set the end of the try note range
10826 size_t tryNoteIndex = mir->tryNoteIndex().value();
10827 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10828 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10829
10830 // Don't set the end of the try note if we've OOM'ed, as the above
10831 // instructions may not have been emitted, which will trigger an assert
10832 // about zero-length try-notes. This is okay as this compilation will be
10833 // thrown away.
10834 if (!masm.oom()) {
10835 tryNote.setTryBodyEnd(masm.currentOffset());
10836 }
10837
10838 // This instruction must be the last instruction in the block. No other
10839 // instructions may be inserted.
10840 LBlock* block = lir->block();
10841 MOZ_RELEASE_ASSERT(*block->rbegin() == lir)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*block->rbegin() == lir)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10841); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir" ")"); do
{ MOZ_CrashSequence(__null, 10841); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
;
10842 }
10843
10844 // Jump to the fallthrough block
10845 jumpToBlock(mir->fallthroughBlock());
10846}
10847#endif // ENABLE_WASM_JSPI
10848
10849void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) {
10850 LBlock* block = lir->block();
10851 MWasmCallLandingPrePad* mir = lir->mir();
10852 MBasicBlock* mirBlock = mir->block();
10853 MBasicBlock* callMirBlock = mir->callBlock();
10854
10855 // This block must be the pre-pad successor of the call block. No blocks may
10856 // be inserted between us, such as for critical edge splitting.
10857 MOZ_RELEASE_ASSERT(mirBlock == callMirBlock->getSuccessor(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable
::PrePadBranchIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock->
getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10858); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { MOZ_CrashSequence(__null, 10858); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10858 MWasmCallCatchable::PrePadBranchIndex))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable
::PrePadBranchIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock->
getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10858); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { MOZ_CrashSequence(__null, 10858); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10859
10860 // This instruction or a move group must be the first instruction in the
10861 // block. No other instructions may be inserted.
10862 MOZ_RELEASE_ASSERT(*block->begin() == lir || (block->begin()->isMoveGroup() &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*block->begin() == lir || (block->begin()->
isMoveGroup() && *(++block->begin()) == lir))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(*block->begin() == lir || (block->begin()->
isMoveGroup() && *(++block->begin()) == lir)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10863); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { MOZ_CrashSequence(__null, 10863); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10863 *(++block->begin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*block->begin() == lir || (block->begin()->
isMoveGroup() && *(++block->begin()) == lir))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(*block->begin() == lir || (block->begin()->
isMoveGroup() && *(++block->begin()) == lir)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10863); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { MOZ_CrashSequence(__null, 10863); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10864
10865 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10866 wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()];
10867 // Set the entry point for the call try note to be the beginning of this
10868 // block. The above assertions (and assertions in visitWasmCall) guarantee
10869 // that we are not skipping over instructions that should be executed.
10870 tryNote.setLandingPad(block->label()->offset(), masm.framePushed());
10871}
10872
10873template <typename InstructionWithMaybeTrapSite>
10874void EmitSignalNullCheckTrapSite(MacroAssembler& masm,
10875 InstructionWithMaybeTrapSite* ins,
10876 FaultingCodeRange fcr,
10877 wasm::TrapMachineInsn tmi) {
10878 if (!ins->maybeTrap()) {
10879 return;
10880 }
10881 masm.appendAndVerify(wasm::Trap::NullPointerDereference, tmi, fcr,
10882 *ins->maybeTrap());
10883}
10884
10885template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndexT>
10886void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins,
10887 MIRType type, MWideningOp wideningOp,
10888 AddressOrBaseIndexT addr,
10889 AnyRegister dst) {
10890 FaultingCodeRange fcr;
10891 switch (type) {
10892 case MIRType::Int32:
10893 switch (wideningOp) {
10894 case MWideningOp::None:
10895 fcr = masm.load32(addr, dst.gpr());
10896 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10897 wasm::TrapMachineInsn::Load32);
10898 break;
10899 case MWideningOp::FromU16:
10900 fcr = masm.load16ZeroExtend(addr, dst.gpr());
10901 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10902 wasm::TrapMachineInsn::Load16);
10903 break;
10904 case MWideningOp::FromS16:
10905 fcr = masm.load16SignExtend(addr, dst.gpr());
10906 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10907 wasm::TrapMachineInsn::Load16);
10908 break;
10909 case MWideningOp::FromU8:
10910 fcr = masm.load8ZeroExtend(addr, dst.gpr());
10911 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10912 wasm::TrapMachineInsn::Load8);
10913 break;
10914 case MWideningOp::FromS8:
10915 fcr = masm.load8SignExtend(addr, dst.gpr());
10916 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10917 wasm::TrapMachineInsn::Load8);
10918 break;
10919 default:
10920 MOZ_CRASH("unexpected widening op in ::visitWasmLoadElement")do { do { } while (false); MOZ_ReportCrash("" "unexpected widening op in ::visitWasmLoadElement"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10920); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement"
")"); do { MOZ_CrashSequence(__null, 10920); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10921 }
10922 break;
10923 case MIRType::Float32:
10924 MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"wideningOp == MWideningOp::None", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10924); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 10924); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10925 fcr = masm.loadFloat32(addr, dst.fpu());
10926 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10927 wasm::TrapMachineInsn::Load32);
10928 break;
10929 case MIRType::Double:
10930 MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"wideningOp == MWideningOp::None", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10930); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 10930); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10931 fcr = masm.loadDouble(addr, dst.fpu());
10932 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10933 wasm::TrapMachineInsn::Load64);
10934 break;
10935 case MIRType::Pointer:
10936 case MIRType::WasmAnyRef:
10937 case MIRType::WasmStructData:
10938 case MIRType::WasmArrayData:
10939 MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"wideningOp == MWideningOp::None", "./../../../../js/src/jit/CodeGenerator.cpp"
, 10939); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 10939); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10940 fcr = masm.loadPtr(addr, dst.gpr());
10941 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10942 wasm::TrapMachineInsnForLoadWord());
10943 break;
10944 default:
10945 MOZ_CRASH("unexpected type in ::emitWasmValueLoad")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueLoad"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10945); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad" ")"); do
{ MOZ_CrashSequence(__null, 10945); __attribute__((nomerge))
::abort(); } while (false); } while (false)
;
10946 }
10947}
10948
10949template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndexT>
10950void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins,
10951 MIRType type, MNarrowingOp narrowingOp,
10952 AnyRegister src,
10953 AddressOrBaseIndexT addr) {
10954 FaultingCodeRange fcr;
10955 switch (type) {
10956 case MIRType::Int32:
10957 switch (narrowingOp) {
10958 case MNarrowingOp::None:
10959 fcr = masm.store32(src.gpr(), addr);
10960 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10961 wasm::TrapMachineInsn::Store32);
10962 break;
10963 case MNarrowingOp::To16:
10964 fcr = masm.store16(src.gpr(), addr);
10965 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10966 wasm::TrapMachineInsn::Store16);
10967 break;
10968 case MNarrowingOp::To8:
10969 fcr = masm.store8(src.gpr(), addr);
10970 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10971 wasm::TrapMachineInsn::Store8);
10972 break;
10973 default:
10974 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 10974); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 10974); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
10975 }
10976 break;
10977 case MIRType::Float32:
10978 fcr = masm.storeFloat32(src.fpu(), addr);
10979 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10980 wasm::TrapMachineInsn::Store32);
10981 break;
10982 case MIRType::Double:
10983 fcr = masm.storeDouble(src.fpu(), addr);
10984 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10985 wasm::TrapMachineInsn::Store64);
10986 break;
10987 case MIRType::Pointer:
10988 // This could be correct, but it would be a new usage, so check carefully.
10989 MOZ_CRASH("Unexpected type in ::emitWasmValueStore.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type in ::emitWasmValueStore."
, "./../../../../js/src/jit/CodeGenerator.cpp", 10989); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore." ")")
; do { MOZ_CrashSequence(__null, 10989); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
10990 case MIRType::WasmAnyRef:
10991 MOZ_CRASH("Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef.")do { do { } while (false); MOZ_ReportCrash("" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef."
, "./../../../../js/src/jit/CodeGenerator.cpp", 10991); AnnotateMozCrashReason
("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef."
")"); do { MOZ_CrashSequence(__null, 10991); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10992 default:
10993 MOZ_CRASH("unexpected type in ::emitWasmValueStore")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueStore"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10993); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore" ")");
do { MOZ_CrashSequence(__null, 10993); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
10994 }
10995}
10996
10997void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) {
10998 MIRType type = ins->type();
10999 MWideningOp wideningOp = ins->wideningOp();
11000 Register container = ToRegister(ins->containerRef());
11001 Address addr(container, ins->offset());
11002 AnyRegister dst = ToAnyRegister(ins->output());
11003
11004#ifdef ENABLE_WASM_SIMD1
11005 if (type == MIRType::Simd128) {
11006 MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"wideningOp == MWideningOp::None", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11006); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 11006); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11007 FaultingCodeRange fcr = masm.loadUnalignedSimd128(addr, dst.fpu());
11008 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load128);
11009 return;
11010 }
11011#endif
11012 emitWasmValueLoad(ins, type, wideningOp, addr, dst);
11013}
11014
11015void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) {
11016 MIRType type = ins->type();
11017 MWideningOp wideningOp = ins->wideningOp();
11018 Scale scale = ins->scale();
11019 Register base = ToRegister(ins->base());
11020 Register index = ToRegister(ins->index());
11021 AnyRegister dst = ToAnyRegister(ins->output());
11022
11023#ifdef ENABLE_WASM_SIMD1
11024 if (type == MIRType::Simd128) {
11025 MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"wideningOp == MWideningOp::None", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11025); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 11025); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11026 FaultingCodeRange fcr;
11027 Register temp = ToRegister(ins->temp0());
11028 masm.lshiftPtr(Imm32(4), index, temp);
11029 fcr = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne),
11030 dst.fpu());
11031 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load128);
11032 return;
11033 }
11034#endif
11035 emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst);
11036}
11037
11038void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) {
11039 MIRType type = ins->type();
11040 MNarrowingOp narrowingOp = ins->narrowingOp();
11041 Register container = ToRegister(ins->containerRef());
11042 Address addr(container, ins->offset());
11043 AnyRegister src = ToAnyRegister(ins->value());
11044 if (type != MIRType::Int32) {
11045 MOZ_RELEASE_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", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11045); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { MOZ_CrashSequence(__null, 11045); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11046 }
11047
11048#ifdef ENABLE_WASM_SIMD1
11049 if (type == MIRType::Simd128) {
11050 FaultingCodeRange fcr = masm.storeUnalignedSimd128(src.fpu(), addr);
11051 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11052 wasm::TrapMachineInsn::Store128);
11053 return;
11054 }
11055#endif
11056 emitWasmValueStore(ins, type, narrowingOp, src, addr);
11057}
11058
11059void CodeGenerator::visitWasmStoreStackResult(LWasmStoreStackResult* ins) {
11060 const LAllocation* value = ins->value();
11061 Address addr(ToRegister(ins->stackResultsArea()), ins->offset());
11062
11063 switch (ins->type()) {
11064 case MIRType::Int32:
11065 masm.storePtr(ToRegister(value), addr);
11066 break;
11067 case MIRType::Float32:
11068 masm.storeFloat32(ToFloatRegister(value), addr);
11069 break;
11070 case MIRType::Double:
11071 masm.storeDouble(ToFloatRegister(value), addr);
11072 break;
11073#ifdef ENABLE_WASM_SIMD1
11074 case MIRType::Simd128:
11075 masm.storeUnalignedSimd128(ToFloatRegister(value), addr);
11076 break;
11077#endif
11078 case MIRType::WasmAnyRef:
11079 masm.storePtr(ToRegister(value), addr);
11080 break;
11081 default:
11082 MOZ_CRASH("unexpected type in ::visitWasmStoreStackResult")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::visitWasmStoreStackResult"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11082); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type in ::visitWasmStoreStackResult"
")"); do { MOZ_CrashSequence(__null, 11082); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
11083 }
11084}
11085
11086void CodeGenerator::visitWasmStoreStackResultI64(
11087 LWasmStoreStackResultI64* ins) {
11088 masm.store64(ToRegister64(ins->value()),
11089 Address(ToRegister(ins->stackResultsArea()), ins->offset()));
11090}
11091
11092void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) {
11093 MIRType type = ins->type();
11094 MNarrowingOp narrowingOp = ins->narrowingOp();
11095 Scale scale = ins->scale();
11096 Register base = ToRegister(ins->base());
11097 Register index = ToRegister(ins->index());
11098 AnyRegister src = ToAnyRegister(ins->value());
11099 if (type != MIRType::Int32) {
11100 MOZ_RELEASE_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", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11100); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { MOZ_CrashSequence(__null, 11100); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11101 }
11102
11103#ifdef ENABLE_WASM_SIMD1
11104 if (type == MIRType::Simd128) {
11105 Register temp = ToRegister(ins->temp0());
11106 masm.lshiftPtr(Imm32(4), index, temp);
11107 FaultingCodeRange fcr = masm.storeUnalignedSimd128(
11108 src.fpu(), BaseIndex(base, temp, Scale::TimesOne));
11109 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11110 wasm::TrapMachineInsn::Store128);
11111 return;
11112 }
11113#endif
11114 emitWasmValueStore(ins, type, narrowingOp, src,
11115 BaseIndex(base, index, scale));
11116}
11117
11118void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) {
11119 Register elements = ToRegister(ins->elements());
11120 Register index = ToRegister(ins->index());
11121 Register output = ToRegister(ins->output());
11122 masm.loadPtr(BaseIndex(elements, index, ScalePointer), output);
11123}
11124
11125void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) {
11126 masm.computeEffectiveAddress(
11127 Address(ToRegister(ins->base()), int32_t(ins->mir()->offset())),
11128 ToRegister(ins->output()));
11129}
11130
11131void CodeGenerator::visitWasmDerivedIndexPointer(
11132 LWasmDerivedIndexPointer* ins) {
11133 Register base = ToRegister(ins->base());
11134 Register index = ToRegister(ins->index());
11135 Register output = ToRegister(ins->output());
11136 masm.computeEffectiveAddress(BaseIndex(base, index, ins->mir()->scale()),
11137 output);
11138}
11139
11140void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) {
11141 Register instance = ToRegister(ins->instance());
11142 Register valueBase = ToRegister(ins->valueBase());
11143 size_t offset = ins->offset();
11144 Register temp = ToRegister(ins->temp0());
11145
11146 Address addr(valueBase, offset);
11147
11148 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
11149 Label skipPreBarrier;
11150 wasm::EmitWasmPreBarrierGuard(masm, instance, temp, addr, &skipPreBarrier,
11151 ins->maybeTrap());
11152 wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase,
11153 offset);
11154 masm.bind(&skipPreBarrier);
11155 }
11156
11157 FaultingCodeRange fcr;
11158 if (ins->value()->isBogus()) {
11159 fcr = masm.storePtr(ImmWord(0), addr);
11160 } else {
11161 Register value = ToRegister(ins->value());
11162 fcr = masm.storePtr(value, addr);
11163 }
11164
11165 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11166 wasm::TrapMachineInsnForStoreWord());
11167 // The postbarrier is handled separately.
11168}
11169
11170void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) {
11171 Register instance = ToRegister(ins->instance());
11172 Register base = ToRegister(ins->base());
11173 Register index = ToRegister(ins->index());
11174 Register temp0 = ToTempRegisterOrInvalid(ins->temp0());
11175 Register temp1 = ToTempRegisterOrInvalid(ins->temp1());
11176
11177 BaseIndex addr(base, index, ScalePointer);
11178
11179 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
11180 Label skipPreBarrier;
11181 wasm::EmitWasmPreBarrierGuard(masm, instance, temp0, addr, &skipPreBarrier,
11182 ins->maybeTrap());
11183 wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr);
11184 masm.bind(&skipPreBarrier);
11185 }
11186
11187 FaultingCodeRange fcr;
11188 if (ins->value()->isBogus()) {
11189 fcr = masm.storePtr(ImmWord(0), addr);
11190 } else {
11191 Register value = ToRegister(ins->value());
11192 fcr = masm.storePtr(value, addr);
11193 }
11194
11195 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11196 wasm::TrapMachineInsnForStoreWord());
11197 // The postbarrier is handled separately.
11198}
11199
11200void CodeGenerator::visitWasmPostWriteBarrierWholeCell(
11201 LWasmPostWriteBarrierWholeCell* lir) {
11202 Register object = ToRegister(lir->object());
11203 Register value = ToRegister(lir->value());
11204 Register temp = ToRegister(lir->temp0());
11205 MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->instance()) == InstanceReg)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11205); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { MOZ_CrashSequence(__null, 11205); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11206 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
11207 // Skip the barrier if this object was previously added to the store buffer.
11208 // We perform this check out of line because in practice the prior guards
11209 // eliminate most calls to the barrier.
11210 wasm::CheckWholeCellLastElementCache(masm, InstanceReg, object, temp,
11211 ool.rejoin());
11212
11213 saveLive(lir);
11214 masm.Push(InstanceReg);
11215 int32_t framePushedAfterInstance = masm.framePushed();
11216
11217 // Call Instance::postBarrierWholeCell
11218 masm.setupWasmABICall(wasm::SymbolicAddress::PostBarrierWholeCell);
11219 masm.passABIArg(InstanceReg);
11220 masm.passABIArg(object);
11221 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
11222 masm.callWithABI(wasm::BytecodeOffset(0),
11223 wasm::SymbolicAddress::PostBarrierWholeCell,
11224 mozilla::Some(instanceOffset), ABIType::General);
11225
11226 masm.Pop(InstanceReg);
11227 restoreLive(lir);
11228
11229 masm.jump(ool.rejoin());
11230 });
11231 addOutOfLineCode(ool, lir->mir());
11232
11233 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
11234 ool->rejoin());
11235 masm.jump(ool->entry());
11236 masm.bind(ool->rejoin());
11237}
11238
11239void CodeGenerator::visitWasmPostWriteBarrierEdgeAtIndex(
11240 LWasmPostWriteBarrierEdgeAtIndex* lir) {
11241 Register object = ToRegister(lir->object());
11242 Register value = ToRegister(lir->value());
11243 Register valueBase = ToRegister(lir->valueBase());
11244 Register index = ToRegister(lir->index());
11245 Register temp = ToRegister(lir->temp0());
11246 MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->instance()) == InstanceReg)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11246); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { MOZ_CrashSequence(__null, 11246); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11247 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
11248 saveLive(lir);
11249 masm.Push(InstanceReg);
11250 int32_t framePushedAfterInstance = masm.framePushed();
11251
11252 // Fold the value offset into the value base
11253 if (lir->elemSize() == 16) {
11254 masm.lshiftPtr(Imm32(4), index, temp);
11255 masm.addPtr(valueBase, temp);
11256 } else {
11257 masm.computeEffectiveAddress(
11258 BaseIndex(valueBase, index, ScaleFromElemWidth(lir->elemSize())),
11259 temp);
11260 }
11261
11262 // Call Instance::postBarrier
11263 masm.setupWasmABICall(wasm::SymbolicAddress::PostBarrierEdge);
11264 masm.passABIArg(InstanceReg);
11265 masm.passABIArg(temp);
11266 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
11267 masm.callWithABI(wasm::BytecodeOffset(0),
11268 wasm::SymbolicAddress::PostBarrierEdge,
11269 mozilla::Some(instanceOffset), ABIType::General);
11270
11271 masm.Pop(InstanceReg);
11272 restoreLive(lir);
11273
11274 masm.jump(ool.rejoin());
11275 });
11276 addOutOfLineCode(ool, lir->mir());
11277
11278 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
11279 ool->rejoin());
11280 masm.jump(ool->entry());
11281 masm.bind(ool->rejoin());
11282}
11283
11284#ifdef ENABLE_WASM_JSPI1
11285void CodeGenerator::visitWasmResumeBarrier(LWasmResumeBarrier* lir) {
11286 Register instance = ToRegister(lir->instance());
11287 Register cont = ToRegister(lir->cont());
11288 Register scratch1 = ToRegister(lir->temp0());
11289
11290 auto* ool = new (alloc())
11291 LambdaOutOfLineCode([this, lir, instance, cont](OutOfLineCode& ool) {
11292 saveLive(lir);
11293 wasm::EmitWasmResumeBarrier(masm, instance, cont);
11294 restoreLive(lir);
11295 masm.jump(ool.rejoin());
11296 });
11297 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
11298
11299 wasm::EmitWasmResumeBarrierGuard(masm, instance, scratch1, ool->entry());
11300 masm.bind(ool->rejoin());
11301}
11302#endif // ENABLE_WASM_JSPI
11303
11304void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) {
11305 Register container = ToRegister(ins->containerRef());
11306 Address addr(container, ins->offset());
11307 Register64 output = ToOutRegister64(ins);
11308 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
11309 // transaction will always trap before the other, so it seems safest to
11310 // register both of them as potentially trapping.
11311#ifdef JS_64BIT1
11312 FaultingCodeRange fcr = masm.load64(addr, output);
11313 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load64);
11314#else
11315 FaultingCodeRangePair fcrp = masm.load64(addr, output);
11316 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11317 wasm::TrapMachineInsn::Load32);
11318 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11319 wasm::TrapMachineInsn::Load32);
11320#endif
11321}
11322
11323void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) {
11324 Register base = ToRegister(ins->base());
11325 Register index = ToRegister(ins->index());
11326 BaseIndex addr(base, index, Scale::TimesEight);
11327 Register64 output = ToOutRegister64(ins);
11328 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
11329 // transaction will always trap before the other, so it seems safest to
11330 // register both of them as potentially trapping.
11331#ifdef JS_64BIT1
11332 FaultingCodeRange fcr = masm.load64(addr, output);
11333 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load64);
11334#else
11335 FaultingCodeRangePair fcrp = masm.load64(addr, output);
11336 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11337 wasm::TrapMachineInsn::Load32);
11338 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11339 wasm::TrapMachineInsn::Load32);
11340#endif
11341}
11342
11343void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) {
11344 Register container = ToRegister(ins->containerRef());
11345 Address addr(container, ins->offset());
11346 Register64 value = ToRegister64(ins->value());
11347 // Either 1 or 2 words. As above we register both transactions in the
11348 // 2-word case.
11349#ifdef JS_64BIT1
11350 FaultingCodeRange fcr = masm.store64(value, addr);
11351 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Store64);
11352#else
11353 FaultingCodeRangePair fcrp = masm.store64(value, addr);
11354 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11355 wasm::TrapMachineInsn::Store32);
11356 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11357 wasm::TrapMachineInsn::Store32);
11358#endif
11359}
11360
11361void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) {
11362 Register base = ToRegister(ins->base());
11363 Register index = ToRegister(ins->index());
11364 BaseIndex addr(base, index, Scale::TimesEight);
11365 Register64 value = ToRegister64(ins->value());
11366 // Either 1 or 2 words. As above we register both transactions in the
11367 // 2-word case.
11368#ifdef JS_64BIT1
11369 FaultingCodeRange fcr = masm.store64(value, addr);
11370 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Store64);
11371#else
11372 FaultingCodeRangePair fcrp = masm.store64(value, addr);
11373 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11374 wasm::TrapMachineInsn::Store32);
11375 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11376 wasm::TrapMachineInsn::Store32);
11377#endif
11378}
11379
11380void CodeGenerator::visitWasmClampTable64Address(
11381 LWasmClampTable64Address* lir) {
11382 Register64 address = ToRegister64(lir->address());
11383 Register out = ToRegister(lir->output());
11384 masm.wasmClampTable64Address(address, out);
11385}
11386
11387void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) {
11388 Register obj = ToRegister(lir->object());
11389 Register out = ToRegister(lir->output());
11390 masm.loadArrayBufferByteLengthIntPtr(obj, out);
11391}
11392
11393void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) {
11394 Register obj = ToRegister(lir->object());
11395 Register out = ToRegister(lir->output());
11396 masm.loadArrayBufferViewLengthIntPtr(obj, out);
11397}
11398
11399void CodeGenerator::visitArrayBufferViewByteOffset(
11400 LArrayBufferViewByteOffset* lir) {
11401 Register obj = ToRegister(lir->object());
11402 Register out = ToRegister(lir->output());
11403 masm.loadArrayBufferViewByteOffsetIntPtr(obj, out);
11404}
11405
11406void CodeGenerator::visitArrayBufferViewElements(
11407 LArrayBufferViewElements* lir) {
11408 Register obj = ToRegister(lir->object());
11409 Register out = ToRegister(lir->output());
11410 masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out);
11411}
11412
11413void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) {
11414 Register obj = ToRegister(lir->object());
11415 Register out = ToRegister(lir->output());
11416
11417 masm.typedArrayElementSize(obj, out);
11418}
11419
11420void CodeGenerator::visitResizableTypedArrayLength(
11421 LResizableTypedArrayLength* lir) {
11422 Register obj = ToRegister(lir->object());
11423 Register out = ToRegister(lir->output());
11424 Register temp = ToRegister(lir->temp0());
11425
11426 auto sync = SynchronizeLoad(lir->mir()->requiresMemoryBarrier());
11427 masm.loadResizableTypedArrayLengthIntPtr(sync, obj, out, temp);
11428}
11429
11430void CodeGenerator::visitResizableDataViewByteLength(
11431 LResizableDataViewByteLength* lir) {
11432 Register obj = ToRegister(lir->object());
11433 Register out = ToRegister(lir->output());
11434 Register temp = ToRegister(lir->temp0());
11435
11436 auto sync = SynchronizeLoad(lir->mir()->requiresMemoryBarrier());
11437 masm.loadResizableDataViewByteLengthIntPtr(sync, obj, out, temp);
11438}
11439
11440void CodeGenerator::visitGrowableSharedArrayBufferByteLength(
11441 LGrowableSharedArrayBufferByteLength* lir) {
11442 Register obj = ToRegister(lir->object());
11443 Register out = ToRegister(lir->output());
11444
11445 // Explicit |byteLength| accesses are seq-consistent atomic loads.
11446 auto sync = Synchronization::Load();
11447
11448 masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out);
11449}
11450
11451void CodeGenerator::visitGuardResizableArrayBufferViewInBounds(
11452 LGuardResizableArrayBufferViewInBounds* lir) {
11453 Register obj = ToRegister(lir->object());
11454 Register temp = ToRegister(lir->temp0());
11455
11456 Label bail;
11457 masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail);
11458 bailoutFrom(&bail, lir->snapshot());
11459}
11460
11461void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached(
11462 LGuardResizableArrayBufferViewInBoundsOrDetached* lir) {
11463 Register obj = ToRegister(lir->object());
11464 Register temp = ToRegister(lir->temp0());
11465
11466 Label done, bail;
11467 masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done);
11468 masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail);
11469 masm.bind(&done);
11470 bailoutFrom(&bail, lir->snapshot());
11471}
11472
11473void CodeGenerator::visitGuardHasAttachedArrayBuffer(
11474 LGuardHasAttachedArrayBuffer* lir) {
11475 Register obj = ToRegister(lir->object());
11476 Register temp = ToRegister(lir->temp0());
11477
11478 Label bail;
11479 masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail);
11480 bailoutFrom(&bail, lir->snapshot());
11481}
11482
11483void CodeGenerator::visitGuardTypedArraySetOffset(
11484 LGuardTypedArraySetOffset* lir) {
11485 Register offset = ToRegister(lir->offset());
11486 Register targetLength = ToRegister(lir->targetLength());
11487 Register sourceLength = ToRegister(lir->sourceLength());
11488 Register temp = ToRegister(lir->temp0());
11489
11490 Label bail;
11491
11492 // Ensure `offset <= target.length`.
11493 masm.movePtr(targetLength, temp);
11494 masm.branchSubPtr(Assembler::Signed, offset, temp, &bail);
11495
11496 // Ensure `source.length <= (target.length - offset)`.
11497 masm.branchPtr(Assembler::GreaterThan, sourceLength, temp, &bail);
11498
11499 bailoutFrom(&bail, lir->snapshot());
11500}
11501
11502void CodeGenerator::visitTypedArrayFill(LTypedArrayFill* lir) {
11503 auto elementType = lir->mir()->elementType();
11504 MOZ_ASSERT(!Scalar::isBigIntType(elementType))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!Scalar::isBigIntType(elementType))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!Scalar::isBigIntType(elementType
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!Scalar::isBigIntType(elementType)", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11504); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!Scalar::isBigIntType(elementType)"
")"); do { MOZ_CrashSequence(__null, 11504); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11505
11506 masm.setupAlignedABICall();
11507 masm.passABIArg(ToRegister(lir->object()));
11508 if (elementType == Scalar::Float64) {
11509 masm.passABIArg(ToFloatRegister(lir->value()), ABIType::Float64);
11510 } else if (elementType == Scalar::Float32 || elementType == Scalar::Float16) {
11511 masm.passABIArg(ToFloatRegister(lir->value()), ABIType::Float32);
11512 } else {
11513 MOZ_ASSERT(!Scalar::isFloatingType(elementType))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!Scalar::isFloatingType(elementType))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!Scalar::isFloatingType(elementType
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!Scalar::isFloatingType(elementType)", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11513); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!Scalar::isFloatingType(elementType)"
")"); do { MOZ_CrashSequence(__null, 11513); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11514 masm.passABIArg(ToRegister(lir->value()));
11515 }
11516 masm.passABIArg(ToRegister(lir->start()));
11517 masm.passABIArg(ToRegister(lir->end()));
11518
11519 if (elementType == Scalar::Float64) {
11520 using Fn = void (*)(TypedArrayObject*, double, intptr_t, intptr_t);
11521 masm.callWithABI<Fn, js::TypedArrayFillDouble>();
11522 } else if (elementType == Scalar::Float32 || elementType == Scalar::Float16) {
11523 using Fn = void (*)(TypedArrayObject*, float, intptr_t, intptr_t);
11524 masm.callWithABI<Fn, js::TypedArrayFillFloat32>();
11525 } else {
11526 // All other types are managed using int32.
11527 MOZ_ASSERT(Scalar::byteSize(elementType) <= sizeof(int32_t))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::byteSize(elementType) <= sizeof(int32_t))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(Scalar::byteSize(elementType) <= sizeof(int32_t))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Scalar::byteSize(elementType) <= sizeof(int32_t)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11527); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "Scalar::byteSize(elementType) <= sizeof(int32_t)"
")"); do { MOZ_CrashSequence(__null, 11527); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11528
11529 using Fn = void (*)(TypedArrayObject*, int32_t, intptr_t, intptr_t);
11530 masm.callWithABI<Fn, js::TypedArrayFillInt32>();
11531 }
11532}
11533
11534void CodeGenerator::visitTypedArrayFill64(LTypedArrayFill64* lir) {
11535 MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->elementType()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::isBigIntType(lir->mir()->elementType()
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(Scalar::isBigIntType(lir->mir()->elementType()
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("Scalar::isBigIntType(lir->mir()->elementType())", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->elementType())"
")"); do { MOZ_CrashSequence(__null, 11535); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11536
11537 masm.setupAlignedABICall();
11538 masm.passABIArg(ToRegister(lir->object()));
11539 masm.passABIArg(ToRegister64(lir->value()));
11540 masm.passABIArg(ToRegister(lir->start()));
11541 masm.passABIArg(ToRegister(lir->end()));
11542
11543 using Fn = void (*)(TypedArrayObject*, int64_t, intptr_t, intptr_t);
11544 masm.callWithABI<Fn, js::TypedArrayFillInt64>();
11545}
11546
11547void CodeGenerator::visitTypedArraySet(LTypedArraySet* lir) {
11548 Register target = ToRegister(lir->target());
11549 Register source = ToRegister(lir->source());
11550 Register offset = ToRegister(lir->offset());
11551
11552 // Bit-wise copying is infallible because it doesn't need to allocate any
11553 // temporary memory, even if the underlying buffers are the same.
11554 if (lir->mir()->canUseBitwiseCopy()) {
11555 masm.setupAlignedABICall();
11556 masm.passABIArg(target);
11557 masm.passABIArg(source);
11558 masm.passABIArg(offset);
11559
11560 using Fn = void (*)(TypedArrayObject*, TypedArrayObject*, intptr_t);
11561 masm.callWithABI<Fn, js::TypedArraySetInfallible>();
11562 } else {
11563 pushArg(offset);
11564 pushArg(source);
11565 pushArg(target);
11566
11567 using Fn =
11568 bool (*)(JSContext*, TypedArrayObject*, TypedArrayObject*, intptr_t);
11569 callVM<Fn, js::TypedArraySet>(lir);
11570 }
11571}
11572
11573void CodeGenerator::visitTypedArraySetFromSubarray(
11574 LTypedArraySetFromSubarray* lir) {
11575 Register target = ToRegister(lir->target());
11576 Register source = ToRegister(lir->source());
11577 Register offset = ToRegister(lir->offset());
11578 Register sourceOffset = ToRegister(lir->sourceOffset());
11579 Register sourceLength = ToRegister(lir->sourceLength());
11580
11581 // Bit-wise copying is infallible because it doesn't need to allocate any
11582 // temporary memory, even if the underlying buffers are the same.
11583 if (lir->mir()->canUseBitwiseCopy()) {
11584 masm.setupAlignedABICall();
11585 masm.passABIArg(target);
11586 masm.passABIArg(source);
11587 masm.passABIArg(offset);
11588 masm.passABIArg(sourceOffset);
11589 masm.passABIArg(sourceLength);
11590
11591 using Fn = void (*)(TypedArrayObject*, TypedArrayObject*, intptr_t,
11592 intptr_t, intptr_t);
11593 masm.callWithABI<Fn, js::TypedArraySetFromSubarrayInfallible>();
11594 } else {
11595 pushArg(sourceLength);
11596 pushArg(sourceOffset);
11597 pushArg(offset);
11598 pushArg(source);
11599 pushArg(target);
11600
11601 using Fn = bool (*)(JSContext*, TypedArrayObject*, TypedArrayObject*,
11602 intptr_t, intptr_t, intptr_t);
11603 callVM<Fn, js::TypedArraySetFromSubarray>(lir);
11604 }
11605}
11606
11607void CodeGenerator::visitTypedArraySubarray(LTypedArraySubarray* lir) {
11608 pushArg(ToRegister(lir->length()));
11609 pushArg(ToRegister(lir->start()));
11610 pushArg(ToRegister(lir->object()));
11611
11612 using Fn = TypedArrayObject* (*)(JSContext*, Handle<TypedArrayObject*>,
11613 intptr_t, intptr_t);
11614 callVM<Fn, js::TypedArraySubarrayWithLength>(lir);
11615}
11616
11617void CodeGenerator::visitToIntegerIndex(LToIntegerIndex* lir) {
11618 Register index = ToRegister(lir->index());
11619 Register length = ToRegister(lir->length());
11620 Register output = ToRegister(lir->output());
11621
11622 masm.movePtr(index, output);
11623
11624 Label done, notNegative;
11625 masm.branchTestPtr(Assembler::NotSigned, index, index, &notNegative);
11626 {
11627 masm.branchAddPtr(Assembler::NotSigned, length, output, &done);
11628 masm.movePtr(ImmWord(0), output);
11629 masm.jump(&done);
11630 }
11631 masm.bind(&notNegative);
11632 {
11633 masm.cmpPtrMovePtr(Assembler::GreaterThan, index, length, length, output);
11634 }
11635 masm.bind(&done);
11636}
11637
11638void CodeGenerator::visitGuardNumberToIntPtrIndex(
11639 LGuardNumberToIntPtrIndex* lir) {
11640 FloatRegister input = ToFloatRegister(lir->input());
11641 Register output = ToRegister(lir->output());
11642
11643 if (!lir->mir()->supportOOB()) {
11644 Label bail;
11645 masm.convertDoubleToPtr(input, output, &bail, false);
11646 bailoutFrom(&bail, lir->snapshot());
11647 return;
11648 }
11649
11650 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
11651 // Substitute the invalid index with an arbitrary out-of-bounds index.
11652 masm.movePtr(ImmWord(-1), output);
11653 masm.jump(ool.rejoin());
11654 });
11655 addOutOfLineCode(ool, lir->mir());
11656
11657 masm.convertDoubleToPtr(input, output, ool->entry(), false);
11658 masm.bind(ool->rejoin());
11659}
11660
11661void CodeGenerator::visitStringLength(LStringLength* lir) {
11662 Register input = ToRegister(lir->string());
11663 Register output = ToRegister(lir->output());
11664
11665 masm.loadStringLength(input, output);
11666}
11667
11668void CodeGenerator::visitMinMaxI(LMinMaxI* ins) {
11669 Register first = ToRegister(ins->first());
11670 Register output = ToRegister(ins->output());
11671
11672 MOZ_ASSERT(first == output)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(first == output)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(first == output))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("first == output"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11672); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "first == output" ")"); do { MOZ_CrashSequence
(__null, 11672); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11673
11674 if (ins->second()->isConstant()) {
11675 auto second = Imm32(ToInt32(ins->second()));
11676
11677 if (ins->mir()->isMax()) {
11678 masm.max32(first, second, output);
11679 } else {
11680 masm.min32(first, second, output);
11681 }
11682 } else {
11683 Register second = ToRegister(ins->second());
11684
11685 if (ins->mir()->isMax()) {
11686 masm.max32(first, second, output);
11687 } else {
11688 masm.min32(first, second, output);
11689 }
11690 }
11691}
11692
11693void CodeGenerator::visitMinMaxIntPtr(LMinMaxIntPtr* ins) {
11694 Register first = ToRegister(ins->first());
11695 Register output = ToRegister(ins->output());
11696
11697 MOZ_ASSERT(first == output)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(first == output)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(first == output))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("first == output"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11697); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "first == output" ")"); do { MOZ_CrashSequence
(__null, 11697); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11698
11699 if (ins->second()->isConstant()) {
11700 auto second = ImmWord(ToIntPtr(ins->second()));
11701
11702 if (ins->mir()->isMax()) {
11703 masm.maxPtr(first, second, output);
11704 } else {
11705 masm.minPtr(first, second, output);
11706 }
11707 } else {
11708 Register second = ToRegister(ins->second());
11709
11710 if (ins->mir()->isMax()) {
11711 masm.maxPtr(first, second, output);
11712 } else {
11713 masm.minPtr(first, second, output);
11714 }
11715 }
11716}
11717
11718void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) {
11719 Register array = ToRegister(ins->array());
11720 Register output = ToRegister(ins->output());
11721 Register temp1 = ToRegister(ins->temp0());
11722 Register temp2 = ToRegister(ins->temp1());
11723 Register temp3 = ToRegister(ins->temp2());
11724 bool isMax = ins->mir()->isMax();
11725
11726 Label bail;
11727 masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail);
11728 bailoutFrom(&bail, ins->snapshot());
11729}
11730
11731void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) {
11732 Register array = ToRegister(ins->array());
11733 FloatRegister output = ToFloatRegister(ins->output());
11734 FloatRegister floatTemp = ToFloatRegister(ins->temp0());
11735 Register temp1 = ToRegister(ins->temp1());
11736 Register temp2 = ToRegister(ins->temp2());
11737 bool isMax = ins->mir()->isMax();
11738
11739 Label bail;
11740 masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail);
11741 bailoutFrom(&bail, ins->snapshot());
11742}
11743
11744// For Abs*, lowering will have tied input to output on platforms where that is
11745// sensible, and otherwise left them untied.
11746
11747void CodeGenerator::visitAbsI(LAbsI* ins) {
11748 Register input = ToRegister(ins->input());
11749 Register output = ToRegister(ins->output());
11750
11751 if (ins->mir()->fallible()) {
11752 Label positive;
11753 if (input != output) {
11754 masm.move32(input, output);
11755 }
11756 masm.branchTest32(Assembler::NotSigned, output, output, &positive);
11757 Label bail;
11758 masm.branchNeg32(Assembler::Overflow, output, &bail);
11759 bailoutFrom(&bail, ins->snapshot());
11760 masm.bind(&positive);
11761 } else {
11762 masm.abs32(input, output);
11763 }
11764}
11765
11766void CodeGenerator::visitAbsD(LAbsD* ins) {
11767 masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output()));
11768}
11769
11770void CodeGenerator::visitAbsF(LAbsF* ins) {
11771 masm.absFloat32(ToFloatRegister(ins->input()),
11772 ToFloatRegister(ins->output()));
11773}
11774
11775void CodeGenerator::visitPowII(LPowII* ins) {
11776 Register value = ToRegister(ins->value());
11777 Register power = ToRegister(ins->power());
11778 Register output = ToRegister(ins->output());
11779 Register temp0 = ToRegister(ins->temp0());
11780 Register temp1 = ToRegister(ins->temp1());
11781
11782 Label bailout;
11783 masm.pow32(value, power, output, temp0, temp1, &bailout);
11784 bailoutFrom(&bailout, ins->snapshot());
11785}
11786
11787void CodeGenerator::visitPowI(LPowI* ins) {
11788 FloatRegister value = ToFloatRegister(ins->value());
11789 Register power = ToRegister(ins->power());
11790
11791 using Fn = double (*)(double x, int32_t y);
11792 masm.setupAlignedABICall();
11793 masm.passABIArg(value, ABIType::Float64);
11794 masm.passABIArg(power);
11795
11796 masm.callWithABI<Fn, js::powi>(ABIType::Float64);
11797 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 11797); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11798}
11799
11800void CodeGenerator::visitPowD(LPowD* ins) {
11801 FloatRegister value = ToFloatRegister(ins->value());
11802 FloatRegister power = ToFloatRegister(ins->power());
11803
11804 using Fn = double (*)(double x, double y);
11805 masm.setupAlignedABICall();
11806 masm.passABIArg(value, ABIType::Float64);
11807 masm.passABIArg(power, ABIType::Float64);
11808 masm.callWithABI<Fn, ecmaPow>(ABIType::Float64);
11809
11810 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11810); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 11810); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11811}
11812
11813void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) {
11814 Register power = ToRegister(ins->power());
11815 Register output = ToRegister(ins->output());
11816
11817 uint32_t base = ins->base();
11818 MOZ_ASSERT(std::has_single_bit(base))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(std::has_single_bit(base))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(std::has_single_bit(base))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("std::has_single_bit(base)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11818); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "std::has_single_bit(base)" ")"); do { MOZ_CrashSequence
(__null, 11818); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11819
11820 uint32_t n = mozilla::FloorLog2(base);
11821 MOZ_ASSERT(n != 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(n != 0)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(n != 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("n != 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11821); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")"
); do { MOZ_CrashSequence(__null, 11821); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
11822
11823 // Hacker's Delight, 2nd edition, theorem D2.
11824 auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; };
11825
11826 // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative.
11827 // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|.
11828 //
11829 // Note: it's important for this condition to match the code in CacheIR.cpp
11830 // (CanAttachInt32Pow) to prevent failure loops.
11831 bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)),
11832 ins->snapshot());
11833
11834 // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale
11835 // |power| and perform a single shift, but due to the lack of necessary
11836 // MacroAssembler functionality, like multiplying a register with an
11837 // immediate, we restrict the number of generated shift instructions when
11838 // lowering this operation.
11839 masm.move32(Imm32(1), output);
11840 do {
11841 masm.lshift32(power, output);
11842 n--;
11843 } while (n > 0);
11844}
11845
11846void CodeGenerator::visitSqrtD(LSqrtD* ins) {
11847 FloatRegister input = ToFloatRegister(ins->input());
11848 FloatRegister output = ToFloatRegister(ins->output());
11849 masm.sqrtDouble(input, output);
11850}
11851
11852void CodeGenerator::visitSqrtF(LSqrtF* ins) {
11853 FloatRegister input = ToFloatRegister(ins->input());
11854 FloatRegister output = ToFloatRegister(ins->output());
11855 masm.sqrtFloat32(input, output);
11856}
11857
11858void CodeGenerator::visitSignI(LSignI* ins) {
11859 Register input = ToRegister(ins->input());
11860 Register output = ToRegister(ins->output());
11861 masm.signInt32(input, output);
11862}
11863
11864void CodeGenerator::visitSignD(LSignD* ins) {
11865 FloatRegister input = ToFloatRegister(ins->input());
11866 FloatRegister output = ToFloatRegister(ins->output());
11867 masm.signDouble(input, output);
11868}
11869
11870void CodeGenerator::visitSignDI(LSignDI* ins) {
11871 FloatRegister input = ToFloatRegister(ins->input());
11872 FloatRegister temp = ToFloatRegister(ins->temp0());
11873 Register output = ToRegister(ins->output());
11874
11875 Label bail;
11876 masm.signDoubleToInt32(input, output, temp, &bail);
11877 bailoutFrom(&bail, ins->snapshot());
11878}
11879
11880void CodeGenerator::visitSignID(LSignID* ins) {
11881 Register input = ToRegister(ins->input());
11882 Register temp = ToRegister(ins->temp0());
11883 FloatRegister output = ToFloatRegister(ins->output());
11884
11885 masm.signInt32(input, temp);
11886 masm.convertInt32ToDouble(temp, output);
11887}
11888
11889void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) {
11890 FloatRegister input = ToFloatRegister(ins->input());
11891 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 11891); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11892
11893 UnaryMathFunction fun = ins->mir()->function();
11894 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun);
11895
11896 masm.setupAlignedABICall();
11897
11898 masm.passABIArg(input, ABIType::Float64);
11899 masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr),
11900 ABIType::Float64);
11901}
11902
11903void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) {
11904 FloatRegister input = ToFloatRegister(ins->input());
11905 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnFloat32Reg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnFloat32Reg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnFloat32Reg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 11905); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11906
11907 masm.setupAlignedABICall();
11908 masm.passABIArg(input, ABIType::Float32);
11909
11910 using Fn = float (*)(float x);
11911 Fn funptr = nullptr;
11912 CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check;
11913 switch (ins->mir()->function()) {
11914 case UnaryMathFunction::Floor:
11915 funptr = std::floor;
11916 check = CheckUnsafeCallWithABI::DontCheckOther;
11917 break;
11918 case UnaryMathFunction::Round:
11919 funptr = math_roundf_impl;
11920 break;
11921 case UnaryMathFunction::Trunc:
11922 funptr = std::trunc;
11923 check = CheckUnsafeCallWithABI::DontCheckOther;
11924 break;
11925 case UnaryMathFunction::Ceil:
11926 funptr = std::ceil;
11927 check = CheckUnsafeCallWithABI::DontCheckOther;
11928 break;
11929 default:
11930 MOZ_CRASH("Unknown or unsupported float32 math function")do { do { } while (false); MOZ_ReportCrash("" "Unknown or unsupported float32 math function"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11930); AnnotateMozCrashReason
("MOZ_CRASH(" "Unknown or unsupported float32 math function" ")"
); do { MOZ_CrashSequence(__null, 11930); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
11931 }
11932
11933 masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check);
11934}
11935
11936void CodeGenerator::visitModD(LModD* ins) {
11937 MOZ_ASSERT(!gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!gen->compilingWasm()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11937); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 11937); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11938
11939 FloatRegister lhs = ToFloatRegister(ins->lhs());
11940 FloatRegister rhs = ToFloatRegister(ins->rhs());
11941 FloatRegister output = ToFloatRegister(ins->output());
11942 Register temp1 = ToRegister(ins->temp0());
11943 Register temp2 = ToRegister(ins->temp1());
11944
11945 LiveRegisterSet liveRegisterSet = liveVolatileRegs(ins);
11946
11947 Label call, done;
11948 // The fast path's internal integer division may itself call out to a runtime
11949 // routine (on ARM without a hardware divide), and needs the volatile live set
11950 // to know which of our registers to preserve across that call.
11951 masm.modDoubleIntegerFastPath(lhs, rhs, output, temp1, temp2, liveRegisterSet,
11952 &call);
11953 masm.jump(&done);
11954
11955 masm.bind(&call);
11956 {
11957 LiveRegisterSet save = liveRegisterSet;
11958 save.takeUnchecked(output);
11959
11960 masm.PushRegsInMask(save);
11961 using Fn = double (*)(double a, double b);
11962 masm.setupUnalignedABICall(temp1);
11963 masm.passABIArg(lhs, ABIType::Float64);
11964 masm.passABIArg(rhs, ABIType::Float64);
11965 masm.callWithABI<Fn, NumberMod>(ABIType::Float64);
11966 masm.storeCallFloatResult(output);
11967 masm.PopRegsInMask(save);
11968 }
11969
11970 masm.bind(&done);
11971}
11972
11973void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) {
11974 FloatRegister lhs = ToFloatRegister(ins->lhs());
11975 uint32_t divisor = ins->divisor();
11976 MOZ_ASSERT(std::has_single_bit(divisor))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(std::has_single_bit(divisor))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(std::has_single_bit(divisor)
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"std::has_single_bit(divisor)", "./../../../../js/src/jit/CodeGenerator.cpp"
, 11976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "std::has_single_bit(divisor)"
")"); do { MOZ_CrashSequence(__null, 11976); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11977
11978 FloatRegister output = ToFloatRegister(ins->output());
11979
11980 // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|.
11981 //
11982 // This doesn't work if |d| isn't a power of two, because we may lose too much
11983 // precision. For example |Number.MAX_VALUE % 3 == 2|, but
11984 // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|.
11985
11986 Label done;
11987 {
11988 ScratchDoubleScope scratch(masm);
11989
11990 // Subnormals can lead to performance degradation, which can make calling
11991 // |fmod| faster than this inline implementation. Work around this issue by
11992 // directly returning the input for any value in the interval ]-1, +1[.
11993 Label notSubnormal;
11994 masm.loadConstantDouble(1.0, scratch);
11995 masm.loadConstantDouble(-1.0, output);
11996 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch,
11997 &notSubnormal);
11998 masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output,
11999 &notSubnormal);
12000
12001 masm.moveDouble(lhs, output);
12002 masm.jump(&done);
12003
12004 masm.bind(&notSubnormal);
12005
12006 if (divisor == 1) {
12007 // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip
12008 // the multiplication by one in this case.
12009 masm.moveDouble(lhs, output);
12010 masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch);
12011 masm.subDouble(scratch, output);
12012 } else {
12013 masm.loadConstantDouble(1.0 / double(divisor), scratch);
12014 masm.loadConstantDouble(double(divisor), output);
12015
12016 masm.mulDouble(lhs, scratch);
12017 masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch);
12018 masm.mulDouble(output, scratch);
12019
12020 masm.moveDouble(lhs, output);
12021 masm.subDouble(scratch, output);
12022 }
12023 }
12024
12025 masm.copySignDouble(output, lhs, output);
12026 masm.bind(&done);
12027}
12028
12029void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) {
12030 masm.Push(InstanceReg);
12031 int32_t framePushedAfterInstance = masm.framePushed();
12032
12033 FloatRegister lhs = ToFloatRegister(ins->lhs());
12034 FloatRegister rhs = ToFloatRegister(ins->rhs());
12035
12036 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12036); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 12036); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12037
12038 masm.setupWasmABICall(wasm::SymbolicAddress::ModD);
12039 masm.passABIArg(lhs, ABIType::Float64);
12040 masm.passABIArg(rhs, ABIType::Float64);
12041
12042 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
12043 masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD,
12044 mozilla::Some(instanceOffset), ABIType::Float64);
12045
12046 masm.Pop(InstanceReg);
12047}
12048
12049void CodeGenerator::visitClzI(LClzI* ins) {
12050 Register input = ToRegister(ins->input());
12051 Register output = ToRegister(ins->output());
12052 bool knownNotZero = ins->mir()->operandIsNeverZero();
12053
12054 masm.clz32(input, output, knownNotZero);
12055}
12056
12057void CodeGenerator::visitCtzI(LCtzI* ins) {
12058 Register input = ToRegister(ins->input());
12059 Register output = ToRegister(ins->output());
12060 bool knownNotZero = ins->mir()->operandIsNeverZero();
12061
12062 masm.ctz32(input, output, knownNotZero);
12063}
12064
12065void CodeGenerator::visitPopcntI(LPopcntI* ins) {
12066 Register input = ToRegister(ins->input());
12067 Register output = ToRegister(ins->output());
12068 Register temp = ToRegister(ins->temp0());
12069
12070 masm.popcnt32(input, output, temp);
12071}
12072
12073void CodeGenerator::visitClzI64(LClzI64* ins) {
12074 Register64 input = ToRegister64(ins->input());
12075 Register64 output = ToOutRegister64(ins);
12076
12077 masm.clz64(input, output);
12078}
12079
12080void CodeGenerator::visitCtzI64(LCtzI64* ins) {
12081 Register64 input = ToRegister64(ins->input());
12082 Register64 output = ToOutRegister64(ins);
12083
12084 masm.ctz64(input, output);
12085}
12086
12087void CodeGenerator::visitPopcntI64(LPopcntI64* ins) {
12088 Register64 input = ToRegister64(ins->input());
12089 Register64 output = ToOutRegister64(ins);
12090 Register temp = ToRegister(ins->temp0());
12091
12092 masm.popcnt64(input, output, temp);
12093}
12094
12095void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) {
12096 pushArg(ToRegister(ins->rhs()));
12097 pushArg(ToRegister(ins->lhs()));
12098
12099 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12100 callVM<Fn, BigInt::add>(ins);
12101}
12102
12103void CodeGenerator::visitBigIntSub(LBigIntSub* ins) {
12104 pushArg(ToRegister(ins->rhs()));
12105 pushArg(ToRegister(ins->lhs()));
12106
12107 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12108 callVM<Fn, BigInt::sub>(ins);
12109}
12110
12111void CodeGenerator::visitBigIntMul(LBigIntMul* ins) {
12112 pushArg(ToRegister(ins->rhs()));
12113 pushArg(ToRegister(ins->lhs()));
12114
12115 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12116 callVM<Fn, BigInt::mul>(ins);
12117}
12118
12119void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) {
12120 pushArg(ToRegister(ins->rhs()));
12121 pushArg(ToRegister(ins->lhs()));
12122
12123 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12124 callVM<Fn, BigInt::div>(ins);
12125}
12126
12127void CodeGenerator::visitBigIntMod(LBigIntMod* ins) {
12128 pushArg(ToRegister(ins->rhs()));
12129 pushArg(ToRegister(ins->lhs()));
12130
12131 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12132 callVM<Fn, BigInt::mod>(ins);
12133}
12134
12135void CodeGenerator::visitBigIntPow(LBigIntPow* ins) {
12136 pushArg(ToRegister(ins->rhs()));
12137 pushArg(ToRegister(ins->lhs()));
12138
12139 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12140 callVM<Fn, BigInt::pow>(ins);
12141}
12142
12143void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) {
12144 pushArg(ToRegister(ins->rhs()));
12145 pushArg(ToRegister(ins->lhs()));
12146
12147 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12148 callVM<Fn, BigInt::bitAnd>(ins);
12149}
12150
12151void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) {
12152 pushArg(ToRegister(ins->rhs()));
12153 pushArg(ToRegister(ins->lhs()));
12154
12155 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12156 callVM<Fn, BigInt::bitOr>(ins);
12157}
12158
12159void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) {
12160 pushArg(ToRegister(ins->rhs()));
12161 pushArg(ToRegister(ins->lhs()));
12162
12163 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12164 callVM<Fn, BigInt::bitXor>(ins);
12165}
12166
12167void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) {
12168 pushArg(ToRegister(ins->rhs()));
12169 pushArg(ToRegister(ins->lhs()));
12170
12171 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12172 callVM<Fn, BigInt::lsh>(ins);
12173}
12174
12175void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) {
12176 pushArg(ToRegister(ins->rhs()));
12177 pushArg(ToRegister(ins->lhs()));
12178
12179 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12180 callVM<Fn, BigInt::rsh>(ins);
12181}
12182
12183void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) {
12184 pushArg(ToRegister(ins->input()));
12185
12186 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12187 callVM<Fn, BigInt::inc>(ins);
12188}
12189
12190void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) {
12191 pushArg(ToRegister(ins->input()));
12192
12193 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12194 callVM<Fn, BigInt::dec>(ins);
12195}
12196
12197void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) {
12198 Register input = ToRegister(ins->input());
12199 Register temp = ToRegister(ins->temp0());
12200 Register output = ToRegister(ins->output());
12201
12202 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12203 auto* ool =
12204 oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output));
12205
12206 // -0n == 0n
12207 Label lhsNonZero;
12208 masm.branchIfBigIntIsNonZero(input, &lhsNonZero);
12209 masm.movePtr(input, output);
12210 masm.jump(ool->rejoin());
12211 masm.bind(&lhsNonZero);
12212
12213 // Call into the VM when the input uses heap digits.
12214 masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(),
12215 ool->entry());
12216
12217 // Flip the sign bit.
12218 masm.xor32(Imm32(BigInt::signBitMask()),
12219 Address(output, BigInt::offsetOfFlags()));
12220
12221 masm.bind(ool->rejoin());
12222}
12223
12224void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) {
12225 pushArg(ToRegister(ins->input()));
12226
12227 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12228 callVM<Fn, BigInt::bitNot>(ins);
12229}
12230
12231void CodeGenerator::visitBigIntToIntPtr(LBigIntToIntPtr* ins) {
12232 Register input = ToRegister(ins->input());
12233 Register output = ToRegister(ins->output());
12234
12235 Label bail;
12236 masm.loadBigIntPtr(input, output, &bail);
12237 bailoutFrom(&bail, ins->snapshot());
12238}
12239
12240void CodeGenerator::visitIntPtrToBigInt(LIntPtrToBigInt* ins) {
12241 Register input = ToRegister(ins->input());
12242 Register temp = ToRegister(ins->temp0());
12243 Register output = ToRegister(ins->output());
12244
12245 using Fn = BigInt* (*)(JSContext*, intptr_t);
12246 auto* ool = oolCallVM<Fn, JS::BigInt::createFromIntPtr>(
12247 ins, ArgList(input), StoreRegisterTo(output));
12248
12249 masm.newGCBigInt(output, temp, initialBigIntHeap(), ool->entry());
12250 masm.movePtr(input, temp);
12251 masm.initializeBigIntPtr(output, temp);
12252
12253 masm.bind(ool->rejoin());
12254}
12255
12256void CodeGenerator::visitBigIntPtrAdd(LBigIntPtrAdd* ins) {
12257 Register lhs = ToRegister(ins->lhs());
12258 const LAllocation* rhs = ins->rhs();
12259 Register output = ToRegister(ins->output());
12260
12261 if (rhs->isConstant()) {
12262 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12263 } else {
12264 masm.movePtr(ToRegister(rhs), output);
12265 }
12266
12267 Label bail;
12268 masm.branchAddPtr(Assembler::Overflow, lhs, output, &bail);
12269 bailoutFrom(&bail, ins->snapshot());
12270}
12271
12272void CodeGenerator::visitBigIntPtrSub(LBigIntPtrSub* ins) {
12273 Register lhs = ToRegister(ins->lhs());
12274 Register rhs = ToRegister(ins->rhs());
12275 Register output = ToRegister(ins->output());
12276
12277 Label bail;
12278 masm.movePtr(lhs, output);
12279 masm.branchSubPtr(Assembler::Overflow, rhs, output, &bail);
12280 bailoutFrom(&bail, ins->snapshot());
12281}
12282
12283void CodeGenerator::visitBigIntPtrMul(LBigIntPtrMul* ins) {
12284 Register lhs = ToRegister(ins->lhs());
12285 const LAllocation* rhs = ins->rhs();
12286 Register output = ToRegister(ins->output());
12287
12288 if (rhs->isConstant()) {
12289 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12290 } else {
12291 masm.movePtr(ToRegister(rhs), output);
12292 }
12293
12294 Label bail;
12295 masm.branchMulPtr(Assembler::Overflow, lhs, output, &bail);
12296 bailoutFrom(&bail, ins->snapshot());
12297}
12298
12299void CodeGenerator::visitBigIntPtrDiv(LBigIntPtrDiv* ins) {
12300 Register lhs = ToRegister(ins->lhs());
12301 Register rhs = ToRegister(ins->rhs());
12302 Register output = ToRegister(ins->output());
12303
12304 // x / 0 throws an error.
12305 Label bail;
12306 if (ins->mir()->canBeDivideByZero()) {
12307 masm.branchPtr(Assembler::Equal, rhs, Imm32(0), &bail);
12308 }
12309
12310 static constexpr auto DigitMin = std::numeric_limits<
12311 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
12312
12313 // Handle an integer overflow from INT{32,64}_MIN / -1.
12314 Label notOverflow;
12315 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), &notOverflow);
12316 masm.branchPtr(Assembler::Equal, rhs, Imm32(-1), &bail);
12317 masm.bind(&notOverflow);
12318
12319 emitBigIntPtrDiv(ins, lhs, rhs, output);
12320
12321 bailoutFrom(&bail, ins->snapshot());
12322}
12323
12324void CodeGenerator::visitBigIntPtrDivPowTwo(LBigIntPtrDivPowTwo* ins) {
12325 Register lhs = ToRegister(ins->lhs());
12326 Register output = ToRegister(ins->output());
12327 int32_t shift = ins->shift();
12328 bool negativeDivisor = ins->negativeDivisor();
12329
12330 masm.movePtr(lhs, output);
12331
12332 if (shift) {
12333 // Adjust the value so that shifting produces a correctly rounded result
12334 // when the numerator is negative.
12335 // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren,
12336 // Jr.'s Hacker's Delight.
12337
12338 constexpr size_t bits = BigInt::DigitBits;
12339
12340 if (shift > 1) {
12341 // Copy the sign bit of the numerator. (= (2^bits - 1) or 0)
12342 masm.rshiftPtrArithmetic(Imm32(bits - 1), output);
12343 }
12344
12345 // Divide by 2^(bits - shift)
12346 // i.e. (= (2^bits - 1) / 2^(bits - shift) or 0)
12347 // i.e. (= (2^shift - 1) or 0)
12348 masm.rshiftPtr(Imm32(bits - shift), output);
12349
12350 // If signed, make any 1 bit below the shifted bits to bubble up, such that
12351 // once shifted the value would be rounded towards 0.
12352 masm.addPtr(lhs, output);
12353
12354 masm.rshiftPtrArithmetic(Imm32(shift), output);
12355
12356 if (negativeDivisor) {
12357 masm.negPtr(output);
12358 }
12359 } else if (negativeDivisor) {
12360 Label bail;
12361 masm.branchNegPtr(Assembler::Overflow, output, &bail);
12362 bailoutFrom(&bail, ins->snapshot());
12363 }
12364}
12365
12366void CodeGenerator::visitBigIntPtrMod(LBigIntPtrMod* ins) {
12367 Register lhs = ToRegister(ins->lhs());
12368 Register rhs = ToRegister(ins->rhs());
12369 Register output = ToRegister(ins->output());
12370 Register temp = ToRegister(ins->temp0());
12371
12372 // x % 0 throws an error.
12373 if (ins->mir()->canBeDivideByZero()) {
12374 bailoutCmpPtr(Assembler::Equal, rhs, Imm32(0), ins->snapshot());
12375 }
12376
12377 static constexpr auto DigitMin = std::numeric_limits<
12378 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
12379
12380 masm.movePtr(lhs, temp);
12381
12382 // Handle an integer overflow from INT{32,64}_MIN / -1.
12383 Label notOverflow;
12384 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), &notOverflow);
12385 masm.branchPtr(Assembler::NotEqual, rhs, Imm32(-1), &notOverflow);
12386 masm.movePtr(ImmWord(0), temp);
12387 masm.bind(&notOverflow);
12388
12389 emitBigIntPtrMod(ins, temp, rhs, output);
12390}
12391
12392void CodeGenerator::visitBigIntPtrModPowTwo(LBigIntPtrModPowTwo* ins) {
12393 Register lhs = ToRegister(ins->lhs());
12394 Register output = ToRegister(ins->output());
12395 Register temp = ToRegister(ins->temp0());
12396 int32_t shift = ins->shift();
12397
12398 masm.movePtr(lhs, output);
12399 masm.movePtr(ImmWord((uintptr_t(1) << shift) - uintptr_t(1)), temp);
12400
12401 // Switch based on sign of the lhs.
12402
12403 // Positive numbers are just a bitmask.
12404 Label negative;
12405 masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative);
12406
12407 masm.andPtr(temp, output);
12408
12409 Label done;
12410 masm.jump(&done);
12411
12412 // Negative numbers need a negate, bitmask, negate
12413 masm.bind(&negative);
12414
12415 masm.negPtr(output);
12416 masm.andPtr(temp, output);
12417 masm.negPtr(output);
12418
12419 masm.bind(&done);
12420}
12421
12422void CodeGenerator::visitBigIntPtrPow(LBigIntPtrPow* ins) {
12423 Register lhs = ToRegister(ins->lhs());
12424 Register rhs = ToRegister(ins->rhs());
12425 Register output = ToRegister(ins->output());
12426 Register temp0 = ToRegister(ins->temp0());
12427 Register temp1 = ToRegister(ins->temp1());
12428
12429 Label bail;
12430 masm.powPtr(lhs, rhs, output, temp0, temp1, &bail);
12431 bailoutFrom(&bail, ins->snapshot());
12432}
12433
12434void CodeGenerator::visitBigIntPtrBitAnd(LBigIntPtrBitAnd* ins) {
12435 Register lhs = ToRegister(ins->lhs());
12436 const LAllocation* rhs = ins->rhs();
12437 Register output = ToRegister(ins->output());
12438
12439 if (rhs->isConstant()) {
12440 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12441 } else {
12442 masm.movePtr(ToRegister(rhs), output);
12443 }
12444 masm.andPtr(lhs, output);
12445}
12446
12447void CodeGenerator::visitBigIntPtrBitOr(LBigIntPtrBitOr* ins) {
12448 Register lhs = ToRegister(ins->lhs());
12449 const LAllocation* rhs = ins->rhs();
12450 Register output = ToRegister(ins->output());
12451
12452 if (rhs->isConstant()) {
12453 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12454 } else {
12455 masm.movePtr(ToRegister(rhs), output);
12456 }
12457 masm.orPtr(lhs, output);
12458}
12459
12460void CodeGenerator::visitBigIntPtrBitXor(LBigIntPtrBitXor* ins) {
12461 Register lhs = ToRegister(ins->lhs());
12462 const LAllocation* rhs = ins->rhs();
12463 Register output = ToRegister(ins->output());
12464
12465 if (rhs->isConstant()) {
12466 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12467 } else {
12468 masm.movePtr(ToRegister(rhs), output);
12469 }
12470 masm.xorPtr(lhs, output);
12471}
12472
12473void CodeGenerator::visitBigIntPtrLsh(LBigIntPtrLsh* ins) {
12474 Register lhs = ToRegister(ins->lhs());
12475 Register output = ToRegister(ins->output());
12476 Register temp = ToTempRegisterOrInvalid(ins->temp0());
12477 Register tempShift = ToTempRegisterOrInvalid(ins->temp1());
12478
12479 if (ins->rhs()->isConstant()) {
12480 intptr_t rhs = ToIntPtr(ins->rhs());
12481
12482 if (rhs >= intptr_t(BigInt::DigitBits)) {
12483 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12483); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12483); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12484
12485 // x << DigitBits with x != 0n always exceeds pointer-sized storage.
12486 masm.movePtr(ImmWord(0), output);
12487 bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot());
12488 } else if (rhs <= -intptr_t(BigInt::DigitBits)) {
12489 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12489); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12489); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12490
12491 // x << -DigitBits == x >> DigitBits, which is either 0n or -1n.
12492 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), lhs, output);
12493 } else if (rhs <= 0) {
12494 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12494); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12494); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12495
12496 // |x << -y| is computed as |x >> y|.
12497 masm.rshiftPtrArithmetic(Imm32(-rhs), lhs, output);
12498 } else {
12499 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12499); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12499); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12500
12501 masm.lshiftPtr(Imm32(rhs), lhs, output);
12502
12503 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12504 masm.rshiftPtrArithmetic(Imm32(rhs), output, temp);
12505 bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot());
12506 }
12507 } else {
12508 Register rhs = ToRegister(ins->rhs());
12509
12510 Label done, bail;
12511 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12511); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12512
12513 masm.movePtr(lhs, output);
12514
12515 // 0n << x == 0n
12516 masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done);
12517
12518 // x << DigitBits with x != 0n always exceeds pointer-sized storage.
12519 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(BigInt::DigitBits),
12520 &bail);
12521
12522 // x << -DigitBits == x >> DigitBits, which is either 0n or -1n.
12523 Label shift;
12524 masm.branchPtr(Assembler::GreaterThan, rhs,
12525 Imm32(-int32_t(BigInt::DigitBits)), &shift);
12526 {
12527 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
12528 masm.jump(&done);
12529 }
12530 masm.bind(&shift);
12531
12532 // Move |rhs| into the designated shift register.
12533 masm.movePtr(rhs, tempShift);
12534
12535 // |x << -y| is computed as |x >> y|.
12536 Label leftShift;
12537 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &leftShift);
12538 {
12539 masm.negPtr(tempShift);
12540 masm.rshiftPtrArithmetic(tempShift, output);
12541 masm.jump(&done);
12542 }
12543 masm.bind(&leftShift);
12544
12545 masm.lshiftPtr(tempShift, output);
12546
12547 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12548 masm.movePtr(output, temp);
12549 masm.rshiftPtrArithmetic(tempShift, temp);
12550 masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail);
12551
12552 masm.bind(&done);
12553 bailoutFrom(&bail, ins->snapshot());
12554 }
12555}
12556
12557void CodeGenerator::visitBigIntPtrRsh(LBigIntPtrRsh* ins) {
12558 Register lhs = ToRegister(ins->lhs());
12559 Register output = ToRegister(ins->output());
12560 Register temp = ToTempRegisterOrInvalid(ins->temp0());
12561 Register tempShift = ToTempRegisterOrInvalid(ins->temp1());
12562
12563 if (ins->rhs()->isConstant()) {
12564 intptr_t rhs = ToIntPtr(ins->rhs());
12565
12566 if (rhs <= -intptr_t(BigInt::DigitBits)) {
12567 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12567); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12568
12569 // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage.
12570 masm.movePtr(ImmWord(0), output);
12571 bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot());
12572 } else if (rhs >= intptr_t(BigInt::DigitBits)) {
12573 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12573); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12573); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12574
12575 // x >> DigitBits is either 0n or -1n.
12576 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), lhs, output);
12577 } else if (rhs < 0) {
12578 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12578); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12578); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12579
12580 // |x >> -y| is computed as |x << y|.
12581 masm.lshiftPtr(Imm32(-rhs), lhs, output);
12582
12583 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12584 masm.rshiftPtrArithmetic(Imm32(-rhs), output, temp);
12585 bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot());
12586 } else {
12587 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12587); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12587); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12588
12589 masm.rshiftPtrArithmetic(Imm32(rhs), lhs, output);
12590 }
12591 } else {
12592 Register rhs = ToRegister(ins->rhs());
12593
12594 Label done, bail;
12595 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 12595); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12595); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12596
12597 masm.movePtr(lhs, output);
12598
12599 // 0n >> x == 0n
12600 masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done);
12601
12602 // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage.
12603 masm.branchPtr(Assembler::LessThanOrEqual, rhs,
12604 Imm32(-int32_t(BigInt::DigitBits)), &bail);
12605
12606 // x >> DigitBits is either 0n or -1n.
12607 Label shift;
12608 masm.branchPtr(Assembler::LessThan, rhs, Imm32(BigInt::DigitBits), &shift);
12609 {
12610 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
12611 masm.jump(&done);
12612 }
12613 masm.bind(&shift);
12614
12615 // Move |rhs| into the designated shift register.
12616 masm.movePtr(rhs, tempShift);
12617
12618 // |x >> -y| is computed as |x << y|.
12619 Label rightShift;
12620 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &rightShift);
12621 {
12622 masm.negPtr(tempShift);
12623 masm.lshiftPtr(tempShift, output);
12624
12625 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12626 masm.movePtr(output, temp);
12627 masm.rshiftPtrArithmetic(tempShift, temp);
12628 masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail);
12629
12630 masm.jump(&done);
12631 }
12632 masm.bind(&rightShift);
12633
12634 masm.rshiftPtrArithmetic(tempShift, output);
12635
12636 masm.bind(&done);
12637 bailoutFrom(&bail, ins->snapshot());
12638 }
12639}
12640
12641void CodeGenerator::visitBigIntPtrBitNot(LBigIntPtrBitNot* ins) {
12642 Register input = ToRegister(ins->input());
12643 Register output = ToRegister(ins->output());
12644
12645 masm.movePtr(input, output);
12646 masm.notPtr(output);
12647}
12648
12649void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) {
12650 Register input = ToRegister(lir->input());
12651 RegisterOrInt32 base = ToRegisterOrInt32(lir->base());
12652 Register output = ToRegister(lir->output());
12653 Register temp0 = ToRegister(lir->temp0());
12654 Register temp1 = ToRegister(lir->temp1());
12655
12656 bool lowerCase = lir->mir()->stringCase() == StringCase::Lower;
12657
12658 using Fn = JSLinearString* (*)(JSContext*, int32_t, int32_t, bool);
12659 if (base.is<Register>()) {
12660 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>(
12661 lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)),
12662 StoreRegisterTo(output));
12663
12664 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
12665 masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0,
12666 temp1, gen->runtime->staticStrings(),
12667 liveRegs, lowerCase, ool->entry());
12668 masm.bind(ool->rejoin());
12669 } else {
12670 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>(
12671 lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)),
12672 StoreRegisterTo(output));
12673
12674 masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0,
12675 temp1, gen->runtime->staticStrings(),
12676 lowerCase, ool->entry());
12677 masm.bind(ool->rejoin());
12678 }
12679}
12680
12681void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) {
12682 Register string = ToRegister(lir->string());
12683 Register radix = ToRegister(lir->radix());
12684 ValueOperand output = ToOutValue(lir);
12685 Register temp = ToRegister(lir->temp0());
12686
12687#ifdef DEBUG1
12688 Label ok;
12689 masm.branch32(Assembler::Equal, radix, Imm32(0), &ok);
12690 masm.branch32(Assembler::Equal, radix, Imm32(10), &ok);
12691 masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path");
12692 masm.bind(&ok);
12693#endif
12694
12695 // Use indexed value as fast path if possible.
12696 Label vmCall, done;
12697 masm.loadStringIndexValue(string, temp, &vmCall);
12698 masm.tagValue(JSVAL_TYPE_INT32, temp, output);
12699 masm.jump(&done);
12700 {
12701 masm.bind(&vmCall);
12702
12703 pushArg(radix);
12704 pushArg(string);
12705
12706 using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue);
12707 callVM<Fn, js::NumberParseInt>(lir);
12708 }
12709 masm.bind(&done);
12710}
12711
12712void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) {
12713 FloatRegister number = ToFloatRegister(lir->number());
12714 Register output = ToRegister(lir->output());
12715 FloatRegister temp = ToFloatRegister(lir->temp0());
12716
12717 Label bail;
12718 masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail);
12719 masm.branchTruncateDoubleToInt32(number, output, &bail);
12720
12721 Label ok;
12722 masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok);
12723 {
12724 // Accept both +0 and -0 and return 0.
12725 masm.loadConstantDouble(0.0, temp);
12726 masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok);
12727
12728 // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6).
12729 masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp);
12730 masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail);
12731 }
12732 masm.bind(&ok);
12733
12734 bailoutFrom(&bail, lir->snapshot());
12735}
12736
12737void CodeGenerator::visitFloor(LFloor* lir) {
12738 FloatRegister input = ToFloatRegister(lir->input());
12739 Register output = ToRegister(lir->output());
12740
12741 Label bail;
12742 masm.floorDoubleToInt32(input, output, &bail);
12743 bailoutFrom(&bail, lir->snapshot());
12744}
12745
12746void CodeGenerator::visitFloorF(LFloorF* lir) {
12747 FloatRegister input = ToFloatRegister(lir->input());
12748 Register output = ToRegister(lir->output());
12749
12750 Label bail;
12751 masm.floorFloat32ToInt32(input, output, &bail);
12752 bailoutFrom(&bail, lir->snapshot());
12753}
12754
12755void CodeGenerator::visitCeil(LCeil* lir) {
12756 FloatRegister input = ToFloatRegister(lir->input());
12757 Register output = ToRegister(lir->output());
12758
12759 Label bail;
12760 masm.ceilDoubleToInt32(input, output, &bail);
12761 bailoutFrom(&bail, lir->snapshot());
12762}
12763
12764void CodeGenerator::visitCeilF(LCeilF* lir) {
12765 FloatRegister input = ToFloatRegister(lir->input());
12766 Register output = ToRegister(lir->output());
12767
12768 Label bail;
12769 masm.ceilFloat32ToInt32(input, output, &bail);
12770 bailoutFrom(&bail, lir->snapshot());
12771}
12772
12773void CodeGenerator::visitRound(LRound* lir) {
12774 FloatRegister input = ToFloatRegister(lir->input());
12775 FloatRegister temp = ToFloatRegister(lir->temp0());
12776 Register output = ToRegister(lir->output());
12777
12778 Label bail;
12779 masm.roundDoubleToInt32(input, output, temp, &bail);
12780 bailoutFrom(&bail, lir->snapshot());
12781}
12782
12783void CodeGenerator::visitRoundF(LRoundF* lir) {
12784 FloatRegister input = ToFloatRegister(lir->input());
12785 FloatRegister temp = ToFloatRegister(lir->temp0());
12786 Register output = ToRegister(lir->output());
12787
12788 Label bail;
12789 masm.roundFloat32ToInt32(input, output, temp, &bail);
12790 bailoutFrom(&bail, lir->snapshot());
12791}
12792
12793void CodeGenerator::visitTrunc(LTrunc* lir) {
12794 FloatRegister input = ToFloatRegister(lir->input());
12795 Register output = ToRegister(lir->output());
12796
12797 Label bail;
12798 masm.truncDoubleToInt32(input, output, &bail);
12799 bailoutFrom(&bail, lir->snapshot());
12800}
12801
12802void CodeGenerator::visitTruncF(LTruncF* lir) {
12803 FloatRegister input = ToFloatRegister(lir->input());
12804 Register output = ToRegister(lir->output());
12805
12806 Label bail;
12807 masm.truncFloat32ToInt32(input, output, &bail);
12808 bailoutFrom(&bail, lir->snapshot());
12809}
12810
12811void CodeGenerator::visitNearbyInt(LNearbyInt* lir) {
12812 FloatRegister input = ToFloatRegister(lir->input());
12813 FloatRegister output = ToFloatRegister(lir->output());
12814
12815 RoundingMode roundingMode = lir->mir()->roundingMode();
12816 masm.nearbyIntDouble(roundingMode, input, output);
12817}
12818
12819void CodeGenerator::visitNearbyIntF(LNearbyIntF* lir) {
12820 FloatRegister input = ToFloatRegister(lir->input());
12821 FloatRegister output = ToFloatRegister(lir->output());
12822
12823 RoundingMode roundingMode = lir->mir()->roundingMode();
12824 masm.nearbyIntFloat32(roundingMode, input, output);
12825}
12826
12827void CodeGenerator::visitRoundToDouble(LRoundToDouble* lir) {
12828 FloatRegister input = ToFloatRegister(lir->input());
12829 FloatRegister output = ToFloatRegister(lir->output());
12830
12831 masm.roundDouble(input, output);
12832}
12833
12834void CodeGenerator::visitRoundToFloat32(LRoundToFloat32* lir) {
12835 FloatRegister input = ToFloatRegister(lir->input());
12836 FloatRegister output = ToFloatRegister(lir->output());
12837
12838 masm.roundFloat32(input, output);
12839}
12840
12841void CodeGenerator::visitCopySignF(LCopySignF* lir) {
12842 FloatRegister lhs = ToFloatRegister(lir->lhs());
12843 FloatRegister rhs = ToFloatRegister(lir->rhs());
12844 FloatRegister out = ToFloatRegister(lir->output());
12845
12846 if (lhs == rhs) {
12847 if (lhs != out) {
12848 masm.moveFloat32(lhs, out);
12849 }
12850 return;
12851 }
12852
12853 masm.copySignFloat32(lhs, rhs, out);
12854}
12855
12856void CodeGenerator::visitCopySignD(LCopySignD* lir) {
12857 FloatRegister lhs = ToFloatRegister(lir->lhs());
12858 FloatRegister rhs = ToFloatRegister(lir->rhs());
12859 FloatRegister out = ToFloatRegister(lir->output());
12860
12861 if (lhs == rhs) {
12862 if (lhs != out) {
12863 masm.moveDouble(lhs, out);
12864 }
12865 return;
12866 }
12867
12868 masm.copySignDouble(lhs, rhs, out);
12869}
12870
12871void CodeGenerator::visitCompareS(LCompareS* lir) {
12872 JSOp op = lir->mir()->jsop();
12873 Register left = ToRegister(lir->left());
12874 Register right = ToRegister(lir->right());
12875 Register output = ToRegister(lir->output());
12876
12877 OutOfLineCode* ool = nullptr;
12878
12879 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
12880 if (op == JSOp::Eq || op == JSOp::StrictEq) {
12881 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
12882 lir, ArgList(left, right), StoreRegisterTo(output));
12883 } else if (op == JSOp::Ne || op == JSOp::StrictNe) {
12884 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
12885 lir, ArgList(left, right), StoreRegisterTo(output));
12886 } else if (op == JSOp::Lt) {
12887 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
12888 lir, ArgList(left, right), StoreRegisterTo(output));
12889 } else if (op == JSOp::Le) {
12890 // Push the operands in reverse order for JSOp::Le:
12891 // - |left <= right| is implemented as |right >= left|.
12892 ool =
12893 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
12894 lir, ArgList(right, left), StoreRegisterTo(output));
12895 } else if (op == JSOp::Gt) {
12896 // Push the operands in reverse order for JSOp::Gt:
12897 // - |left > right| is implemented as |right < left|.
12898 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
12899 lir, ArgList(right, left), StoreRegisterTo(output));
12900 } else {
12901 MOZ_ASSERT(op == JSOp::Ge)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(op == JSOp::Ge)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(op == JSOp::Ge))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("op == JSOp::Ge"
, "./../../../../js/src/jit/CodeGenerator.cpp", 12901); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "op == JSOp::Ge" ")"); do { MOZ_CrashSequence
(__null, 12901); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12902 ool =
12903 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
12904 lir, ArgList(left, right), StoreRegisterTo(output));
12905 }
12906
12907 masm.compareStrings(op, left, right, output, ool->entry());
12908
12909 masm.bind(ool->rejoin());
12910}
12911
12912void CodeGenerator::visitCompareSInline(LCompareSInline* lir) {
12913 JSOp op = lir->mir()->jsop();
12914 MOZ_ASSERT(IsEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsEqualityOp(op)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("IsEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 12914); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 12914); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12915
12916 Register input = ToRegister(lir->input());
12917 Register output = ToRegister(lir->output());
12918
12919 const JSOffThreadAtom* str = lir->constant();
12920 MOZ_ASSERT(str->length() > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(str->length() > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(str->length() > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("str->length() > 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 12920); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "str->length() > 0" ")"); do { MOZ_CrashSequence
(__null, 12920); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12921
12922 OutOfLineCode* ool = nullptr;
12923
12924 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
12925 if (op == JSOp::Eq || op == JSOp::StrictEq) {
12926 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
12927 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
12928 } else {
12929 MOZ_ASSERT(op == JSOp::Ne || op == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(op == JSOp::Ne || op == JSOp::StrictNe)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(op == JSOp::Ne || op == JSOp::StrictNe))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("op == JSOp::Ne || op == JSOp::StrictNe"
, "./../../../../js/src/jit/CodeGenerator.cpp", 12929); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe" ")"
); do { MOZ_CrashSequence(__null, 12929); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
12930 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
12931 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
12932 }
12933
12934 masm.equalStrings(op, input, str, output, ool->entry());
12935 masm.bind(ool->rejoin());
12936}
12937
12938void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) {
12939 JSOp op = lir->jsop();
12940 MOZ_ASSERT(IsRelationalOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsRelationalOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsRelationalOp(op)))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("IsRelationalOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 12940); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsRelationalOp(op)" ")"); do { MOZ_CrashSequence
(__null, 12940); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12941
12942 Register input = ToRegister(lir->input());
12943 Register output = ToRegister(lir->output());
12944 Register temp = ToRegister(lir->temp0());
12945
12946 const JSOffThreadAtom* str = lir->constant();
12947 MOZ_ASSERT(str->length() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(str->length() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(str->length() == 1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("str->length() == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 12947); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "str->length() == 1" ")"); do { MOZ_CrashSequence
(__null, 12947); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12948
12949 char16_t ch = str->latin1OrTwoByteChar(0);
12950
12951 masm.movePtr(input, temp);
12952
12953 // Check if the string is empty.
12954 Label compareLength;
12955 masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()),
12956 Imm32(0), &compareLength);
12957
12958 // The first character is in the left-most rope child.
12959 Label notRope;
12960 masm.branchIfNotRope(temp, &notRope);
12961 {
12962 // Unwind ropes at the start if possible.
12963 Label unwindRope;
12964 masm.bind(&unwindRope);
12965 masm.loadRopeLeftChild(temp, output);
12966 masm.movePtr(output, temp);
12967
12968#ifdef DEBUG1
12969 Label notEmpty;
12970 masm.branch32(Assembler::NotEqual,
12971 Address(temp, JSString::offsetOfLength()), Imm32(0),
12972 &notEmpty);
12973 masm.assumeUnreachable("rope children are non-empty");
12974 masm.bind(&notEmpty);
12975#endif
12976
12977 // Otherwise keep unwinding ropes.
12978 masm.branchIfRope(temp, &unwindRope);
12979 }
12980 masm.bind(&notRope);
12981
12982 // Load the first character into |output|.
12983 auto loadFirstChar = [&](auto encoding) {
12984 masm.loadStringChars(temp, output, encoding);
12985 masm.loadChar(Address(output, 0), output, encoding);
12986 };
12987
12988 Label done;
12989 if (ch <= JSString::MAX_LATIN1_CHAR) {
12990 // Handle both encodings when the search character is Latin-1.
12991 Label twoByte, compare;
12992 masm.branchTwoByteString(temp, &twoByte);
12993
12994 loadFirstChar(CharEncoding::Latin1);
12995 masm.jump(&compare);
12996
12997 masm.bind(&twoByte);
12998 loadFirstChar(CharEncoding::TwoByte);
12999
13000 masm.bind(&compare);
13001 } else {
13002 // The search character is a two-byte character, so it can't be equal to any
13003 // character of a Latin-1 string.
13004 masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output);
13005 masm.branchLatin1String(temp, &done);
13006
13007 loadFirstChar(CharEncoding::TwoByte);
13008 }
13009
13010 // Compare the string length when the search character is equal to the
13011 // input's first character.
13012 masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength);
13013
13014 // Otherwise compute the result and jump to the end.
13015 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch),
13016 output);
13017 masm.jump(&done);
13018
13019 // Compare the string length to compute the overall result.
13020 masm.bind(&compareLength);
13021 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
13022 Address(input, JSString::offsetOfLength()), Imm32(1), output);
13023
13024 masm.bind(&done);
13025}
13026
13027void CodeGenerator::visitStrictConstantCompareString(
13028 LStrictConstantCompareString* lir) {
13029 ValueOperand value = ToValue(lir->value());
13030 Register output = ToRegister(lir->output());
13031 Register temp = ToRegister(lir->temp0());
13032
13033 JSOffThreadAtom* str = lir->mir()->constant();
13034 JSOp op = lir->mir()->jsop();
13035 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13035); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13035); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13036
13037 OutOfLineCode* ool = nullptr;
13038
13039 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
13040 if (op == JSOp::StrictEq) {
13041 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
13042 lir, ArgList(temp, ImmGCPtr(str)), StoreRegisterTo(output));
13043 } else {
13044 MOZ_ASSERT(op == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(op == JSOp::StrictNe)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(op == JSOp::StrictNe))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::StrictNe"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13044); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "op == JSOp::StrictNe" ")"); do { MOZ_CrashSequence
(__null, 13044); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13045 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
13046 lir, ArgList(temp, ImmGCPtr(str)), StoreRegisterTo(output));
13047 }
13048
13049 masm.move32(Imm32(op == JSOp::StrictNe), output);
13050 masm.fallibleUnboxString(value, temp, ool->rejoin());
13051
13052 masm.equalStrings(op, temp, str, output, ool->entry());
13053 masm.bind(ool->rejoin());
13054}
13055
13056void CodeGenerator::visitStrictConstantCompareObject(
13057 LStrictConstantCompareObject* lir) {
13058 ValueOperand value = ToValue(lir->value());
13059 Register output = ToRegister(lir->output());
13060
13061 JSObject* obj = lir->mir()->constant();
13062 JSOp op = lir->mir()->jsop();
13063 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13063); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13063); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13064
13065 masm.testValueSet(JSOpToCondition(MCompare::Compare_Object, op), value,
13066 JS::ObjectValue(*obj), output);
13067}
13068
13069void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) {
13070 JSOp op = lir->mir()->jsop();
13071 Register left = ToRegister(lir->left());
13072 Register right = ToRegister(lir->right());
13073 Register temp0 = ToRegister(lir->temp0());
13074 Register temp1 = ToRegister(lir->temp1());
13075 Register temp2 = ToRegister(lir->temp2());
13076 Register output = ToRegister(lir->output());
13077
13078 Label notSame;
13079 Label compareSign;
13080 Label compareLength;
13081 Label compareDigit;
13082
13083 Label* notSameSign;
13084 Label* notSameLength;
13085 Label* notSameDigit;
13086 if (IsEqualityOp(op)) {
13087 notSameSign = &notSame;
13088 notSameLength = &notSame;
13089 notSameDigit = &notSame;
13090 } else {
13091 notSameSign = &compareSign;
13092 notSameLength = &compareLength;
13093 notSameDigit = &compareDigit;
13094 }
13095
13096 masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign,
13097 notSameLength, notSameDigit);
13098
13099 Label done;
13100 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le ||
13101 op == JSOp::Ge),
13102 output);
13103 masm.jump(&done);
13104
13105 if (IsEqualityOp(op)) {
13106 masm.bind(&notSame);
13107 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
13108 } else {
13109 Label invertWhenNegative;
13110
13111 // There are two cases when sign(left) != sign(right):
13112 // 1. sign(left) = positive and sign(right) = negative,
13113 // 2. or the dual case with reversed signs.
13114 //
13115 // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false
13116 // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2
13117 // with |invertWhenNegative|.
13118 masm.bind(&compareSign);
13119 masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output);
13120 masm.jump(&invertWhenNegative);
13121
13122 // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)),
13123 // we have to consider the two cases:
13124 // 1. len(digits(left)) < len(digits(right))
13125 // 2. len(digits(left)) > len(digits(right))
13126 //
13127 // For |left| <cmp> |right| with cmp=Lt:
13128 // Assume both BigInts are positive, then |left < right| is true for case 1
13129 // and false for case 2. When both are negative, the result is reversed.
13130 //
13131 // The other comparison operators can be handled similarly.
13132 //
13133 // |temp0| holds the digits length of the right-hand side operand.
13134 masm.bind(&compareLength);
13135 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
13136 Address(left, BigInt::offsetOfLength()), temp0, output);
13137 masm.jump(&invertWhenNegative);
13138
13139 // Similar to the case above, compare the current digit to determine the
13140 // overall comparison result.
13141 //
13142 // |temp1| points to the current digit of the left-hand side operand.
13143 // |output| holds the current digit of the right-hand side operand.
13144 masm.bind(&compareDigit);
13145 masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false),
13146 Address(temp1, 0), output, output);
13147
13148 Label nonNegative;
13149 masm.bind(&invertWhenNegative);
13150 masm.branchIfBigIntIsNonNegative(left, &nonNegative);
13151 masm.xor32(Imm32(1), output);
13152 masm.bind(&nonNegative);
13153 }
13154
13155 masm.bind(&done);
13156}
13157
13158void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) {
13159 JSOp op = lir->mir()->jsop();
13160 Register left = ToRegister(lir->left());
13161 Register temp0 = ToRegister(lir->temp0());
13162 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
13163 Register output = ToRegister(lir->output());
13164
13165 Label ifTrue, ifFalse;
13166 if (lir->right()->isConstant()) {
13167 MOZ_ASSERT(temp1 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp1 == InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13167); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13167); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13168
13169 Imm32 right = Imm32(ToInt32(lir->right()));
13170 masm.compareBigIntAndInt32(op, left, right, temp0, &ifTrue, &ifFalse);
13171 } else {
13172 MOZ_ASSERT(temp1 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 != InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 != InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp1 != InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13172); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13172); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13173
13174 Register right = ToRegister(lir->right());
13175 masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue,
13176 &ifFalse);
13177 }
13178
13179 Label done;
13180 masm.bind(&ifFalse);
13181 masm.move32(Imm32(0), output);
13182 masm.jump(&done);
13183 masm.bind(&ifTrue);
13184 masm.move32(Imm32(1), output);
13185 masm.bind(&done);
13186}
13187
13188void CodeGenerator::visitCompareBigIntInt32AndBranch(
13189 LCompareBigIntInt32AndBranch* lir) {
13190 JSOp op = lir->cmpMir()->jsop();
13191 Register left = ToRegister(lir->left());
13192 Register temp1 = ToRegister(lir->temp0());
13193 Register temp2 = ToTempRegisterOrInvalid(lir->temp1());
13194
13195 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
13196 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
13197
13198 // compareBigIntAndInt32 falls through to the false case. If the next block
13199 // is the true case, negate the comparison so we can fall through.
13200 if (isNextBlock(lir->ifTrue()->lir())) {
13201 op = NegateCompareOp(op);
13202 std::swap(ifTrue, ifFalse);
13203 }
13204
13205 if (lir->right()->isConstant()) {
13206 MOZ_ASSERT(temp2 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 == InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 == InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp2 == InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13206); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13206); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13207
13208 Imm32 right = Imm32(ToInt32(lir->right()));
13209 masm.compareBigIntAndInt32(op, left, right, temp1, ifTrue, ifFalse);
13210 } else {
13211 MOZ_ASSERT(temp2 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 != InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 != InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp2 != InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13211); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13211); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13212
13213 Register right = ToRegister(lir->right());
13214 masm.compareBigIntAndInt32(op, left, right, temp1, temp2, ifTrue, ifFalse);
13215 }
13216
13217 if (!isNextBlock(lir->ifTrue()->lir())) {
13218 jumpToBlock(lir->ifFalse());
13219 }
13220}
13221
13222void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) {
13223 JSOp op = lir->mir()->jsop();
13224 Register left = ToRegister(lir->left());
13225 FloatRegister right = ToFloatRegister(lir->right());
13226 Register output = ToRegister(lir->output());
13227
13228 masm.setupAlignedABICall();
13229
13230 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
13231 // - |left <= right| is implemented as |right >= left|.
13232 // - |left > right| is implemented as |right < left|.
13233 if (op == JSOp::Le || op == JSOp::Gt) {
13234 masm.passABIArg(right, ABIType::Float64);
13235 masm.passABIArg(left);
13236 } else {
13237 masm.passABIArg(left);
13238 masm.passABIArg(right, ABIType::Float64);
13239 }
13240
13241 using FnBigIntNumber = bool (*)(BigInt*, double);
13242 using FnNumberBigInt = bool (*)(double, BigInt*);
13243 switch (op) {
13244 case JSOp::Eq: {
13245 masm.callWithABI<FnBigIntNumber,
13246 jit::BigIntNumberEqual<EqualityKind::Equal>>();
13247 break;
13248 }
13249 case JSOp::Ne: {
13250 masm.callWithABI<FnBigIntNumber,
13251 jit::BigIntNumberEqual<EqualityKind::NotEqual>>();
13252 break;
13253 }
13254 case JSOp::Lt: {
13255 masm.callWithABI<FnBigIntNumber,
13256 jit::BigIntNumberCompare<ComparisonKind::LessThan>>();
13257 break;
13258 }
13259 case JSOp::Gt: {
13260 masm.callWithABI<FnNumberBigInt,
13261 jit::NumberBigIntCompare<ComparisonKind::LessThan>>();
13262 break;
13263 }
13264 case JSOp::Le: {
13265 masm.callWithABI<
13266 FnNumberBigInt,
13267 jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>();
13268 break;
13269 }
13270 case JSOp::Ge: {
13271 masm.callWithABI<
13272 FnBigIntNumber,
13273 jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>();
13274 break;
13275 }
13276 default:
13277 MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op",
"./../../../../js/src/jit/CodeGenerator.cpp", 13277); AnnotateMozCrashReason
("MOZ_CRASH(" "unhandled op" ")"); do { MOZ_CrashSequence(__null
, 13277); __attribute__((nomerge)) ::abort(); } while (false)
; } while (false)
;
13278 }
13279
13280 masm.storeCallBoolResult(output);
13281}
13282
13283void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) {
13284 JSOp op = lir->mir()->jsop();
13285 Register left = ToRegister(lir->left());
13286 Register right = ToRegister(lir->right());
13287
13288 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
13289 // - |left <= right| is implemented as |right >= left|.
13290 // - |left > right| is implemented as |right < left|.
13291 if (op == JSOp::Le || op == JSOp::Gt) {
13292 pushArg(left);
13293 pushArg(right);
13294 } else {
13295 pushArg(right);
13296 pushArg(left);
13297 }
13298
13299 using FnBigIntString =
13300 bool (*)(JSContext*, HandleBigInt, HandleString, bool*);
13301 using FnStringBigInt =
13302 bool (*)(JSContext*, HandleString, HandleBigInt, bool*);
13303
13304 switch (op) {
13305 case JSOp::Eq: {
13306 constexpr auto Equal = EqualityKind::Equal;
13307 callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir);
13308 break;
13309 }
13310 case JSOp::Ne: {
13311 constexpr auto NotEqual = EqualityKind::NotEqual;
13312 callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir);
13313 break;
13314 }
13315 case JSOp::Lt: {
13316 constexpr auto LessThan = ComparisonKind::LessThan;
13317 callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir);
13318 break;
13319 }
13320 case JSOp::Gt: {
13321 constexpr auto LessThan = ComparisonKind::LessThan;
13322 callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir);
13323 break;
13324 }
13325 case JSOp::Le: {
13326 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
13327 callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir);
13328 break;
13329 }
13330 case JSOp::Ge: {
13331 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
13332 callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir);
13333 break;
13334 }
13335 default:
13336 MOZ_CRASH("Unexpected compare op")do { do { } while (false); MOZ_ReportCrash("" "Unexpected compare op"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13336); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected compare op" ")"); do { MOZ_CrashSequence
(__null, 13336); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
13337 }
13338}
13339
13340void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) {
13341 MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13342); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13342); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13342 lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13342); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13342); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13343
13344 JSOp op = lir->mir()->jsop();
13345 MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13345); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13345); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13346
13347 ValueOperand value = ToValue(lir->value());
13348 Register output = ToRegister(lir->output());
13349
13350 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13351 if (!intact) {
13352 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
13353 addOutOfLineCode(ool, lir->mir());
13354
13355 Label* nullOrLikeUndefined = ool->label1();
13356 Label* notNullOrLikeUndefined = ool->label2();
13357
13358 {
13359 ScratchTagScope tag(masm, value);
13360 masm.splitTagForTest(value, tag);
13361
13362 masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
13363 masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
13364
13365 // Check whether it's a truthy object or a falsy object that emulates
13366 // undefined.
13367 masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
13368 }
13369
13370 Register objreg =
13371 masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
13372 branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined,
13373 notNullOrLikeUndefined, output, ool);
13374 // fall through
13375
13376 Label done;
13377
13378 // It's not null or undefined, and if it's an object it doesn't
13379 // emulate undefined, so it's not like undefined.
13380 masm.move32(Imm32(op == JSOp::Ne), output);
13381 masm.jump(&done);
13382
13383 masm.bind(nullOrLikeUndefined);
13384 masm.move32(Imm32(op == JSOp::Eq), output);
13385
13386 // Both branches meet here.
13387 masm.bind(&done);
13388 } else {
13389 Label nullOrUndefined, notNullOrLikeUndefined;
13390#if defined(DEBUG1) || defined(FUZZING)
13391 Register objreg = Register::Invalid();
13392#endif
13393 {
13394 ScratchTagScope tag(masm, value);
13395 masm.splitTagForTest(value, tag);
13396
13397 masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined);
13398 masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined);
13399
13400#if defined(DEBUG1) || defined(FUZZING)
13401 // Check whether it's a truthy object or a falsy object that emulates
13402 // undefined.
13403 masm.branchTestObject(Assembler::NotEqual, tag, &notNullOrLikeUndefined);
13404 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
13405#endif
13406 }
13407
13408#if defined(DEBUG1) || defined(FUZZING)
13409 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
13410 masm.bind(&notNullOrLikeUndefined);
13411#endif
13412
13413 Label done;
13414
13415 // It's not null or undefined, and if it's an object it doesn't
13416 // emulate undefined.
13417 masm.move32(Imm32(op == JSOp::Ne), output);
13418 masm.jump(&done);
13419
13420 masm.bind(&nullOrUndefined);
13421 masm.move32(Imm32(op == JSOp::Eq), output);
13422
13423 // Both branches meet here.
13424 masm.bind(&done);
13425 }
13426}
13427
13428void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(
13429 LIsNullOrLikeUndefinedAndBranchV* lir) {
13430 MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13431); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13431); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13431 lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13431); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13431); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13432
13433 JSOp op = lir->cmpMir()->jsop();
13434 MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13434); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13434); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13435
13436 ValueOperand value = ToValue(lir->value());
13437
13438 MBasicBlock* ifTrue = lir->ifTrue();
13439 MBasicBlock* ifFalse = lir->ifFalse();
13440
13441 if (op == JSOp::Ne) {
13442 // Swap branches.
13443 std::swap(ifTrue, ifFalse);
13444 }
13445
13446 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13447
13448 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
13449 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
13450
13451 bool extractObject = !intact;
Value stored to 'extractObject' during its initialization is never read
13452 Register objreg = Register::Invalid();
13453#if defined(DEBUG1) || defined(FUZZING)
13454 // always extract objreg if we're in debug and
13455 // assertObjectDoesNotEmulateUndefined;
13456 extractObject = true;
13457#endif
13458
13459 {
13460 ScratchTagScope tag(masm, value);
13461 masm.splitTagForTest(value, tag);
13462
13463 masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
13464 masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
13465
13466 if (extractObject) {
13467 masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
13468 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp1()));
13469 }
13470 }
13471
13472 Register scratch = ToRegister(lir->temp0());
13473 if (!intact) {
13474 // Objects that emulate undefined are loosely equal to null/undefined.
13475 OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject();
13476 addOutOfLineCode(ool, lir->cmpMir());
13477 testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch,
13478 ool);
13479 } else {
13480 assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir());
13481 // Bug 1874905. This would be nice to optimize out at the MIR level.
13482 if (!isNextBlock(ifFalse->lir())) {
13483 masm.jump(ifFalseLabel);
13484 }
13485 }
13486}
13487
13488void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) {
13489 MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13490); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13490); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13490 lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined
|| lir->mir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13490); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13490); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13491 MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->lhs()->type() == MIRType::Object
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->lhs()->type() == MIRType::Object
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->lhs()->type() == MIRType::Object", "./../../../../js/src/jit/CodeGenerator.cpp"
, 13491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 13491); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13492
13493 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13494 JSOp op = lir->mir()->jsop();
13495 Register output = ToRegister(lir->output());
13496 Register objreg = ToRegister(lir->input());
13497 if (!intact) {
13498 MOZ_ASSERT(IsLooseEqualityOp(op),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)"
" (" "Strict equality should have been folded" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 13499); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence
(__null, 13499); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
13499 "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)"
" (" "Strict equality should have been folded" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 13499); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence
(__null, 13499); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13500
13501 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
13502 addOutOfLineCode(ool, lir->mir());
13503
13504 Label* emulatesUndefined = ool->label1();
13505 Label* doesntEmulateUndefined = ool->label2();
13506
13507 branchTestObjectEmulatesUndefined(objreg, emulatesUndefined,
13508 doesntEmulateUndefined, output, ool);
13509
13510 Label done;
13511
13512 masm.move32(Imm32(op == JSOp::Ne), output);
13513 masm.jump(&done);
13514
13515 masm.bind(emulatesUndefined);
13516 masm.move32(Imm32(op == JSOp::Eq), output);
13517 masm.bind(&done);
13518 } else {
13519 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
13520 masm.move32(Imm32(op == JSOp::Ne), output);
13521 }
13522}
13523
13524void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT(
13525 LIsNullOrLikeUndefinedAndBranchT* lir) {
13526 MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13527); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13527); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13527 lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined
|| lir->cmpMir()->compareType() == MCompare::Compare_Null
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13527); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13527); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13528 MOZ_ASSERT(lir->cmpMir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->cmpMir()->lhs()->type() == MIRType::Object
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->cmpMir()->lhs()->type() == MIRType::Object
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->cmpMir()->lhs()->type() == MIRType::Object", "./../../../../js/src/jit/CodeGenerator.cpp"
, 13528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 13528); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13529
13530 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13531
13532 JSOp op = lir->cmpMir()->jsop();
13533 MOZ_ASSERT(IsLooseEqualityOp(op), "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)"
" (" "Strict equality should have been folded" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 13533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence
(__null, 13533); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13534
13535 MBasicBlock* ifTrue = lir->ifTrue();
13536 MBasicBlock* ifFalse = lir->ifFalse();
13537
13538 if (op == JSOp::Ne) {
13539 // Swap branches.
13540 std::swap(ifTrue, ifFalse);
13541 }
13542
13543 Register input = ToRegister(lir->value());
13544 Register scratch = ToRegister(lir->temp0());
13545 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
13546 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
13547
13548 if (intact) {
13549 // Bug 1874905. Ideally branches like this would be optimized out.
13550 assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir());
13551 masm.jump(ifFalseLabel);
13552 } else {
13553 auto* ool = new (alloc()) OutOfLineTestObject();
13554 addOutOfLineCode(ool, lir->cmpMir());
13555
13556 // Objects that emulate undefined are loosely equal to null/undefined.
13557 testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool);
13558 }
13559}
13560
13561void CodeGenerator::visitIsNull(LIsNull* lir) {
13562 MCompare::CompareType compareType = lir->mir()->compareType();
13563 MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Null)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(compareType == MCompare::Compare_Null))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13563); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"
); do { MOZ_CrashSequence(__null, 13563); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
13564
13565 JSOp op = lir->mir()->jsop();
13566 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13566); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13566); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13567
13568 ValueOperand value = ToValue(lir->value());
13569 Register output = ToRegister(lir->output());
13570
13571 Assembler::Condition cond = JSOpToCondition(compareType, op);
13572 masm.testNullSet(cond, value, output);
13573}
13574
13575void CodeGenerator::visitIsUndefined(LIsUndefined* lir) {
13576 MCompare::CompareType compareType = lir->mir()->compareType();
13577 MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Undefined)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(compareType == MCompare::Compare_Undefined))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13577); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { MOZ_CrashSequence(__null, 13577); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13578
13579 JSOp op = lir->mir()->jsop();
13580 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13580); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13580); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13581
13582 ValueOperand value = ToValue(lir->value());
13583 Register output = ToRegister(lir->output());
13584
13585 Assembler::Condition cond = JSOpToCondition(compareType, op);
13586 masm.testUndefinedSet(cond, value, output);
13587}
13588
13589void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) {
13590 MCompare::CompareType compareType = lir->cmpMir()->compareType();
13591 MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Null)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(compareType == MCompare::Compare_Null))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13591); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"
); do { MOZ_CrashSequence(__null, 13591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
13592
13593 JSOp op = lir->cmpMir()->jsop();
13594 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13594); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13594); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13595
13596 ValueOperand value = ToValue(lir->value());
13597
13598 Assembler::Condition cond = JSOpToCondition(compareType, op);
13599
13600 MBasicBlock* ifTrue = lir->ifTrue();
13601 MBasicBlock* ifFalse = lir->ifFalse();
13602
13603 if (isNextBlock(ifFalse->lir())) {
13604 masm.branchTestNull(cond, value, getJumpLabelForBranch(ifTrue));
13605 } else {
13606 masm.branchTestNull(Assembler::InvertCondition(cond), value,
13607 getJumpLabelForBranch(ifFalse));
13608 jumpToBlock(ifTrue);
13609 }
13610}
13611
13612void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) {
13613 MCompare::CompareType compareType = lir->cmpMir()->compareType();
13614 MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Undefined)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(compareType == MCompare::Compare_Undefined))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13614); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { MOZ_CrashSequence(__null, 13614); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13615
13616 JSOp op = lir->cmpMir()->jsop();
13617 MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13617); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13617); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13618
13619 ValueOperand value = ToValue(lir->value());
13620
13621 Assembler::Condition cond = JSOpToCondition(compareType, op);
13622
13623 MBasicBlock* ifTrue = lir->ifTrue();
13624 MBasicBlock* ifFalse = lir->ifFalse();
13625
13626 if (isNextBlock(ifFalse->lir())) {
13627 masm.branchTestUndefined(cond, value, getJumpLabelForBranch(ifTrue));
13628 } else {
13629 masm.branchTestUndefined(Assembler::InvertCondition(cond), value,
13630 getJumpLabelForBranch(ifFalse));
13631 jumpToBlock(ifTrue);
13632 }
13633}
13634
13635void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) {
13636 FloatRegister left = ToFloatRegister(lir->left());
13637 FloatRegister right = ToFloatRegister(lir->right());
13638 FloatRegister temp = ToFloatRegister(lir->temp0());
13639 Register output = ToRegister(lir->output());
13640
13641 masm.sameValueDouble(left, right, temp, output);
13642}
13643
13644void CodeGenerator::visitSameValue(LSameValue* lir) {
13645 ValueOperand lhs = ToValue(lir->left());
13646 ValueOperand rhs = ToValue(lir->right());
13647 Register output = ToRegister(lir->output());
13648
13649 using Fn = bool (*)(JSContext*, const Value&, const Value&, bool*);
13650 OutOfLineCode* ool =
13651 oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output));
13652
13653 // First check to see if the values have identical bits.
13654 // This is correct for SameValue because SameValue(NaN,NaN) is true,
13655 // and SameValue(0,-0) is false.
13656 masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(),
13657 ool->entry());
13658 masm.move32(Imm32(1), output);
13659
13660 // If this fails, call SameValue.
13661 masm.bind(ool->rejoin());
13662}
13663
13664void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs,
13665 Register output) {
13666 using Fn =
13667 JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap);
13668 OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>(
13669 lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))),
13670 StoreRegisterTo(output));
13671
13672 JitCode* stringConcatStub =
13673 snapshot_->getZoneStub(JitZone::StubKind::StringConcat);
13674 masm.call(stringConcatStub);
13675 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
13676
13677 masm.bind(ool->rejoin());
13678}
13679
13680void CodeGenerator::visitConcat(LConcat* lir) {
13681 Register lhs = ToRegister(lir->lhs());
13682 Register rhs = ToRegister(lir->rhs());
13683
13684 Register output = ToRegister(lir->output());
13685
13686 MOZ_ASSERT(lhs == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs == CallTempReg0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs == CallTempReg0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("lhs == CallTempReg0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13686); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lhs == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 13686); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13687 MOZ_ASSERT(rhs == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rhs == CallTempReg1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rhs == CallTempReg1))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("rhs == CallTempReg1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13687); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "rhs == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 13687); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13688 MOZ_ASSERT(ToRegister(lir->temp0()) == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->temp0()) == CallTempReg0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->temp0()) == CallTempReg0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp0()) == CallTempReg0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13688); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0"
")"); do { MOZ_CrashSequence(__null, 13688); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13689 MOZ_ASSERT(ToRegister(lir->temp1()) == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->temp1()) == CallTempReg1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->temp1()) == CallTempReg1))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp1()) == CallTempReg1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13689); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1"
")"); do { MOZ_CrashSequence(__null, 13689); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13690 MOZ_ASSERT(ToRegister(lir->temp2()) == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->temp2()) == CallTempReg2)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->temp2()) == CallTempReg2))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp2()) == CallTempReg2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13690); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2"
")"); do { MOZ_CrashSequence(__null, 13690); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13691 MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->temp3()) == CallTempReg3)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->temp3()) == CallTempReg3))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp3()) == CallTempReg3"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13691); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3"
")"); do { MOZ_CrashSequence(__null, 13691); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13692 MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg4)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->temp4()) == CallTempReg4)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->temp4()) == CallTempReg4))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp4()) == CallTempReg4"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13692); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4"
")"); do { MOZ_CrashSequence(__null, 13692); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13693 MOZ_ASSERT(output == CallTempReg5)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output == CallTempReg5)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output == CallTempReg5))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("output == CallTempReg5"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13693); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == CallTempReg5" ")"); do { MOZ_CrashSequence
(__null, 13693); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13694
13695 emitConcat(lir, lhs, rhs, output);
13696}
13697
13698static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
13699 Register len, Register byteOpScratch,
13700 CharEncoding fromEncoding, CharEncoding toEncoding,
13701 size_t maximumLength = SIZE_MAX(18446744073709551615UL)) {
13702 // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
13703 // (checked below in debug builds), and when done |to| must point to the
13704 // next available char.
13705
13706#ifdef DEBUG1
13707 Label ok;
13708 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
13709 masm.assumeUnreachable("Length should be greater than 0.");
13710 masm.bind(&ok);
13711
13712 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
13713 MOZ_ASSERT(maximumLength <= INT32_MAX, "maximum length fits into int32")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(maximumLength <= (2147483647))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(maximumLength <= (2147483647
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("maximumLength <= (2147483647)" " (" "maximum length fits into int32"
")", "./../../../../js/src/jit/CodeGenerator.cpp", 13713); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maximumLength <= (2147483647)" ") (" "maximum length fits into int32"
")"); do { MOZ_CrashSequence(__null, 13713); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13714
13715 Label ok;
13716 masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok);
13717 masm.assumeUnreachable("Length should not exceed maximum length.");
13718 masm.bind(&ok);
13719 }
13720#endif
13721
13722 MOZ_ASSERT_IF(toEncoding == CharEncoding::Latin1,do { if (toEncoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(fromEncoding
== CharEncoding::Latin1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding
::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("fromEncoding == CharEncoding::Latin1", "./../../../../js/src/jit/CodeGenerator.cpp"
, 13723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { MOZ_CrashSequence(__null, 13723); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
13723 fromEncoding == CharEncoding::Latin1)do { if (toEncoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(fromEncoding
== CharEncoding::Latin1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding
::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("fromEncoding == CharEncoding::Latin1", "./../../../../js/src/jit/CodeGenerator.cpp"
, 13723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { MOZ_CrashSequence(__null, 13723); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
13724
13725 size_t fromWidth =
13726 fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
13727 size_t toWidth =
13728 toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
13729
13730 // Try to copy multiple characters at once when both encoding are equal.
13731 if (fromEncoding == toEncoding) {
13732 constexpr size_t ptrWidth = sizeof(uintptr_t);
13733
13734 // Copy |width| bytes and then adjust |from| and |to|.
13735 auto copyCharacters = [&](size_t width) {
13736 static_assert(ptrWidth <= 8, "switch handles only up to eight bytes");
13737
13738 switch (width) {
13739 case 1:
13740 masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
13741 masm.store8(byteOpScratch, Address(to, 0));
13742 break;
13743 case 2:
13744 masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
13745 masm.store16(byteOpScratch, Address(to, 0));
13746 break;
13747 case 4:
13748 masm.load32(Address(from, 0), byteOpScratch);
13749 masm.store32(byteOpScratch, Address(to, 0));
13750 break;
13751 case 8:
13752 MOZ_ASSERT(width == ptrWidth)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(width == ptrWidth)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(width == ptrWidth))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("width == ptrWidth"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13752); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "width == ptrWidth" ")"); do { MOZ_CrashSequence
(__null, 13752); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13753 masm.loadPtr(Address(from, 0), byteOpScratch);
13754 masm.storePtr(byteOpScratch, Address(to, 0));
13755 break;
13756 }
13757
13758 masm.addPtr(Imm32(width), from);
13759 masm.addPtr(Imm32(width), to);
13760 };
13761
13762 // First align |len| to pointer width.
13763 Label done;
13764 for (size_t width = fromWidth; width < ptrWidth; width *= 2) {
13765 // Number of characters which fit into |width| bytes.
13766 size_t charsPerWidth = width / fromWidth;
13767
13768 if (charsPerWidth < maximumLength) {
13769 Label next;
13770 masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next);
13771
13772 copyCharacters(width);
13773
13774 masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done);
13775 masm.bind(&next);
13776 } else if (charsPerWidth == maximumLength) {
13777 copyCharacters(width);
13778 masm.sub32(Imm32(charsPerWidth), len);
13779 }
13780 }
13781
13782 size_t maxInlineLength;
13783 if (fromEncoding == CharEncoding::Latin1) {
13784 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
13785 } else {
13786 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
13787 }
13788
13789 // Number of characters which fit into a single register.
13790 size_t charsPerPtr = ptrWidth / fromWidth;
13791
13792 // Unroll small loops.
13793 constexpr size_t unrollLoopLimit = 3;
13794 size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr;
13795
13796#ifdef JS_64BIT1
13797 static constexpr size_t latin1MaxInlineByteLength =
13798 JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char);
13799 static constexpr size_t twoByteMaxInlineByteLength =
13800 JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t);
13801
13802 // |unrollLoopLimit| should be large enough to allow loop unrolling on
13803 // 64-bit targets.
13804 static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit,
13805 "Latin-1 loops are unrolled on 64-bit");
13806 static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit,
13807 "Two-byte loops are unrolled on 64-bit");
13808#endif
13809
13810 if (loopCount <= unrollLoopLimit) {
13811 Label labels[unrollLoopLimit];
13812
13813 // Check up front how many characters can be copied.
13814 for (size_t i = 1; i < loopCount; i++) {
13815 masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr),
13816 &labels[i]);
13817 }
13818
13819 // Generate the unrolled loop body.
13820 for (size_t i = loopCount; i > 0; i--) {
13821 copyCharacters(ptrWidth);
13822 masm.sub32(Imm32(charsPerPtr), len);
13823
13824 // Jump target for the previous length check.
13825 if (i != 1) {
13826 masm.bind(&labels[i - 1]);
13827 }
13828 }
13829 } else {
13830 Label start;
13831 masm.bind(&start);
13832 copyCharacters(ptrWidth);
13833 masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start);
13834 }
13835
13836 masm.bind(&done);
13837 } else {
13838 Label start;
13839 masm.bind(&start);
13840 masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding);
13841 masm.storeChar(byteOpScratch, Address(to, 0), toEncoding);
13842 masm.addPtr(Imm32(fromWidth), from);
13843 masm.addPtr(Imm32(toWidth), to);
13844 masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
13845 }
13846}
13847
13848static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
13849 Register len, Register byteOpScratch,
13850 CharEncoding encoding, size_t maximumLength) {
13851 CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding,
13852 maximumLength);
13853}
13854
13855static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input,
13856 Register destChars, Register temp1,
13857 Register temp2) {
13858 // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
13859 // have to inflate.
13860
13861 Label isLatin1, done;
13862 masm.loadStringLength(input, temp1);
13863 masm.branchLatin1String(input, &isLatin1);
13864 {
13865 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
13866 masm.movePtr(temp2, input);
13867 CopyStringChars(masm, destChars, input, temp1, temp2,
13868 CharEncoding::TwoByte);
13869 masm.jump(&done);
13870 }
13871 masm.bind(&isLatin1);
13872 {
13873 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
13874 masm.movePtr(temp2, input);
13875 CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1,
13876 CharEncoding::TwoByte);
13877 }
13878 masm.bind(&done);
13879}
13880
13881static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output,
13882 Register length, Register temp,
13883 gc::Heap initialStringHeap,
13884 Label* failure,
13885 CharEncoding encoding) {
13886#ifdef DEBUG1
13887 size_t maxInlineLength;
13888 if (encoding == CharEncoding::Latin1) {
13889 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
13890 } else {
13891 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
13892 }
13893
13894 Label ok;
13895 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok);
13896 masm.assumeUnreachable("string length too large to be allocated as inline");
13897 masm.bind(&ok);
13898#endif
13899
13900 size_t maxThinInlineLength;
13901 if (encoding == CharEncoding::Latin1) {
13902 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1;
13903 } else {
13904 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE;
13905 }
13906
13907 Label isFat, allocDone;
13908 masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat);
13909 {
13910 uint32_t flags = StringFlags::thinInlineStringFlags(encoding);
13911 masm.newGCString(output, temp, initialStringHeap, failure);
13912 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13913 masm.jump(&allocDone);
13914 }
13915 masm.bind(&isFat);
13916 {
13917 uint32_t flags = StringFlags::fatInlineStringFlags(encoding);
13918 masm.newGCFatInlineString(output, temp, initialStringHeap, failure);
13919 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13920 }
13921 masm.bind(&allocDone);
13922
13923 // Store length.
13924 masm.store32(length, Address(output, JSString::offsetOfLength()));
13925}
13926
13927static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs,
13928 Register output, Register andedFlags,
13929 Register temp2, Register temp3,
13930 gc::Heap initialStringHeap, Label* failure,
13931 CharEncoding encoding) {
13932 JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)",
13933 (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
13934
13935 // State: result length in temp2.
13936
13937#ifdef DEBUG1
13938 Label skip, rope;
13939
13940 // Ensure both strings are linear.
13941 masm.branchIfRope(lhs, &rope);
13942 masm.branchIfRope(rhs, &rope);
13943
13944 masm.jump(&skip);
13945 masm.bind(&rope);
13946 masm.assertUnreachable("Ropes encountered in ConcatInlineString.");
13947 masm.bind(&skip);
13948#endif
13949
13950 // Allocate a JSThinInlineString or JSFatInlineString.
13951 AllocateThinOrFatInlineString(masm, output, temp2, temp3, initialStringHeap,
13952 failure, encoding);
13953
13954 // Load chars pointer in temp2.
13955 masm.loadInlineStringCharsForStore(output, temp2);
13956
13957#if defined(JS_64BIT1) && defined(ENABLE_WASM_SIMD1)
13958 Label fastPath, done;
13959 masm.branchTest32(Assembler::NonZero, andedFlags,
13960 Imm32(StringFlags::INLINE_CHARS_BIT), &fastPath);
13961#endif
13962
13963 Register temp1 = andedFlags;
13964 auto copyChars = [&](Register src) {
13965 if (encoding == CharEncoding::TwoByte) {
13966 CopyStringCharsMaybeInflate(masm, src, temp2, temp3, temp1);
13967 } else {
13968 masm.loadStringLength(src, temp1);
13969 masm.loadStringChars(src, temp3, CharEncoding::Latin1);
13970 masm.movePtr(temp3, src);
13971 CopyStringChars(masm, temp2, src, temp1, temp3, CharEncoding::Latin1);
13972 }
13973 };
13974
13975 // Copy lhs chars. Note that this advances temp2 to point to the next
13976 // char. This also clobbers the lhs register.
13977 copyChars(lhs);
13978
13979 // Copy rhs chars. Clobbers the rhs register.
13980 copyChars(rhs);
13981
13982 // There's a lot of assumptions in here that inline strings are at least
13983 // 16 bytes, so while it's possible to write a faster version for 32-bit,
13984 // we elect to just leave 32-bit platforms behind with a little bit slower
13985 // string copying.
13986#if defined(JS_64BIT1) && defined(ENABLE_WASM_SIMD1)
13987 masm.jump(&done);
13988 masm.bind(&fastPath);
13989
13990 // Note: these assertions are here just to trip if this changes, because all
13991 // the code below is very much dependent on the specific sizes.
13992 static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 == 16);
13993 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE == 8);
13994 static_assert(JSFatInlineString::MAX_LENGTH_LATIN1 == 24);
13995 static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE == 12);
13996
13997 size_t thinInlineLength = encoding == CharEncoding::Latin1
13998 ? JSThinInlineString::MAX_LENGTH_LATIN1
13999 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
14000
14001 // Given that for the lhs we know we're copying into the start of an inline
14002 // string then on 64-bit we know we're safe to write 16 bytes, regardless
14003 // of the actual length.
14004 auto copyLhsFast = [&]() {
14005 masm.loadStringLength(lhs, temp1);
14006 masm.loadInlineStringCharsForStore(lhs, temp3);
14007
14008 masm.loadUnalignedSimd128(Address(temp3, 0), ScratchSimd128Reg);
14009 masm.storeUnalignedSimd128(ScratchSimd128Reg, Address(temp2, 0));
14010
14011 Label lhsDone;
14012 masm.branch32(Assembler::BelowOrEqual, temp1, Imm32(thinInlineLength),
14013 &lhsDone);
14014 masm.loadPtr(Address(temp3, 16), temp3);
14015 masm.storePtr(temp3, Address(temp2, 16));
14016 masm.bind(&lhsDone);
14017
14018 if (encoding == CharEncoding::Latin1) {
14019 masm.addPtr(temp1, temp2);
14020 } else {
14021 masm.computeEffectiveAddress(BaseIndex(temp2, temp1, TimesTwo), temp2);
14022 }
14023 };
14024
14025 // For the RHS however we don't have any guarantees, but we know we
14026 // can handle everything >= 8 bytes with at most three overlapping
14027 // 8 byte copies.
14028 auto copyRhsFast = [&]() {
14029 masm.loadStringLength(rhs, temp1);
14030 masm.loadInlineStringCharsForStore(rhs, temp3);
14031
14032 if (encoding == CharEncoding::TwoByte) {
14033 masm.lshift32(Imm32(1), temp1);
14034 }
14035
14036 Label rhsBelow8, rhsBelow4, rhsDone;
14037
14038 // byteLen >= 8: head + conditional middle + tail.
14039 masm.branch32(Assembler::Below, temp1, Imm32(8), &rhsBelow8);
14040
14041 masm.loadPtr(Address(temp3, 0), lhs);
14042 masm.storePtr(lhs, Address(temp2, 0));
14043
14044 Label rhsTail;
14045 masm.branch32(Assembler::BelowOrEqual, temp1,
14046 Imm32(JSThinInlineString::InlineBytes), &rhsTail);
14047 masm.loadPtr(Address(temp3, 8), lhs);
14048 masm.storePtr(lhs, Address(temp2, 8));
14049
14050 masm.bind(&rhsTail);
14051 masm.loadPtr(BaseIndex(temp3, temp1, TimesOne, -8), lhs);
14052 masm.storePtr(lhs, BaseIndex(temp2, temp1, TimesOne, -8));
14053 masm.jump(&rhsDone);
14054
14055 // byteLen 4-7: two overlapping 4-byte copies.
14056 masm.bind(&rhsBelow8);
14057 masm.branch32(Assembler::Below, temp1, Imm32(4), &rhsBelow4);
14058 masm.load32(Address(temp3, 0), lhs);
14059 masm.store32(lhs, Address(temp2, 0));
14060 masm.load32(BaseIndex(temp3, temp1, TimesOne, -4), lhs);
14061 masm.store32(lhs, BaseIndex(temp2, temp1, TimesOne, -4));
14062 masm.jump(&rhsDone);
14063
14064 // byteLen 1-3: first byte + overlapping 2-byte tail.
14065 masm.bind(&rhsBelow4);
14066 masm.load8ZeroExtend(Address(temp3, 0), lhs);
14067 masm.store8(lhs, Address(temp2, 0));
14068 masm.branch32(Assembler::Below, temp1, Imm32(2), &rhsDone);
14069 masm.load16ZeroExtend(BaseIndex(temp3, temp1, TimesOne, -2), lhs);
14070 masm.store16(lhs, BaseIndex(temp2, temp1, TimesOne, -2));
14071
14072 masm.bind(&rhsDone);
14073 };
14074
14075 // If the output encoding is Latin1, both inputs are Latin1. However if the
14076 // output encoding is TwoByte, we only know that at least one of the inputs
14077 // is TwoByte.
14078 if (encoding == CharEncoding::Latin1) {
14079 copyLhsFast();
14080 copyRhsFast();
14081 } else {
14082 auto copyCharsInflate = [&](Register src) {
14083 masm.loadStringLength(src, temp3);
14084 masm.loadStringChars(src, temp1, CharEncoding::Latin1);
14085 masm.movePtr(temp1, src);
14086 CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1,
14087 CharEncoding::TwoByte);
14088 };
14089
14090 Label lhsInflate, beginRhs, rhsInflate;
14091 masm.branchLatin1String(lhs, &lhsInflate);
14092 copyLhsFast();
14093 masm.jump(&beginRhs);
14094
14095 masm.bind(&lhsInflate);
14096 copyCharsInflate(lhs);
14097
14098 // If lhs was latin1, we know rhs must be TwoByte, so we can skip
14099 // a branch here and just copy rhs directly
14100 copyRhsFast();
14101 masm.jump(&done);
14102
14103 masm.bind(&beginRhs);
14104 masm.branchLatin1String(rhs, &rhsInflate);
14105 copyRhsFast();
14106 masm.jump(&done);
14107
14108 masm.bind(&rhsInflate);
14109 copyCharsInflate(rhs);
14110 }
14111 masm.bind(&done);
14112#endif
14113}
14114
14115void CodeGenerator::visitSubstr(LSubstr* lir) {
14116 Register string = ToRegister(lir->string());
14117 Register begin = ToRegister(lir->begin());
14118 Register length = ToRegister(lir->length());
14119 Register output = ToRegister(lir->output());
14120 Register temp0 = ToRegister(lir->temp0());
14121 Register temp2 = ToRegister(lir->temp2());
14122
14123 // On x86 there are not enough registers. In that case reuse the string
14124 // register as temporary.
14125 Register temp1 =
14126 lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1());
14127
14128 size_t maximumLength = SIZE_MAX(18446744073709551615UL);
14129
14130 Range* range = lir->mir()->length()->range();
14131 if (range && range->hasInt32UpperBound()) {
14132 MOZ_ASSERT(range->upper() >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(range->upper() >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(range->upper() >= 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("range->upper() >= 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 14132); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "range->upper() >= 0" ")"); do { MOZ_CrashSequence
(__null, 14132); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
14133 maximumLength = size_t(range->upper());
14134 }
14135
14136 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <=
14137 JSThinInlineString::MAX_LENGTH_LATIN1);
14138
14139 static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <=
14140 JSFatInlineString::MAX_LENGTH_LATIN1);
14141
14142 bool tryFatInlineOrDependent =
14143 maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE;
14144 bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE;
14145
14146#ifdef DEBUG1
14147 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
14148 Label ok;
14149 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok);
14150 masm.assumeUnreachable("length should not exceed maximum length");
14151 masm.bind(&ok);
14152 }
14153#endif
14154
14155 Label nonZero, nonInput;
14156
14157 // For every edge case use the C++ variant.
14158 // Note: we also use this upon allocation failure in newGCString and
14159 // newGCFatInlineString. To squeeze out even more performance those failures
14160 // can be handled by allocate in ool code and returning to jit code to fill
14161 // in all data.
14162 using Fn = JSString* (*)(JSContext * cx, HandleString str, int32_t begin,
14163 int32_t len);
14164 OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>(
14165 lir, ArgList(string, begin, length), StoreRegisterTo(output));
14166 Label* slowPath = ool->entry();
14167 Label* done = ool->rejoin();
14168
14169 // Zero length, return emptystring.
14170 masm.branchTest32(Assembler::NonZero, length, length, &nonZero);
14171 const JSAtomState& names = gen->runtime->names();
14172 masm.movePtr(ImmGCPtr(names.empty_), output);
14173 masm.jump(done);
14174
14175 // Substring from 0..|str.length|, return str.
14176 masm.bind(&nonZero);
14177 masm.branch32(Assembler::NotEqual,
14178 Address(string, JSString::offsetOfLength()), length, &nonInput);
14179#ifdef DEBUG1
14180 {
14181 Label ok;
14182 masm.branchTest32(Assembler::Zero, begin, begin, &ok);
14183 masm.assumeUnreachable("length == str.length implies begin == 0");
14184 masm.bind(&ok);
14185 }
14186#endif
14187 masm.movePtr(string, output);
14188 masm.jump(done);
14189
14190 // Use slow path for ropes.
14191 masm.bind(&nonInput);
14192 masm.branchIfRope(string, slowPath);
14193
14194 // Optimize one and two character strings.
14195 Label nonStatic;
14196 masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic);
14197 {
14198 Label loadLengthOne, loadLengthTwo;
14199
14200 auto loadChars = [&](CharEncoding encoding, bool fallthru) {
14201 size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char)
14202 : sizeof(char16_t);
14203
14204 masm.loadStringChars(string, temp0, encoding);
14205 masm.loadChar(temp0, begin, temp2, encoding);
14206 masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne);
14207 masm.loadChar(temp0, begin, temp0, encoding, int32_t(size));
14208 if (!fallthru) {
14209 masm.jump(&loadLengthTwo);
14210 }
14211 };
14212
14213 Label isLatin1;
14214 masm.branchLatin1String(string, &isLatin1);
14215 loadChars(CharEncoding::TwoByte, /* fallthru = */ false);
14216
14217 masm.bind(&isLatin1);
14218 loadChars(CharEncoding::Latin1, /* fallthru = */ true);
14219
14220 // Try to load a length-two static string.
14221 masm.bind(&loadLengthTwo);
14222 masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(),
14223 &nonStatic);
14224 masm.jump(done);
14225
14226 // Try to load a length-one static string.
14227 masm.bind(&loadLengthOne);
14228 masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(),
14229 &nonStatic);
14230 masm.jump(done);
14231 }
14232 masm.bind(&nonStatic);
14233
14234 // Allocate either a JSThinInlineString or JSFatInlineString, or jump to
14235 // notInline if we need a dependent string.
14236 Label notInline;
14237 {
14238 static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 <
14239 JSFatInlineString::MAX_LENGTH_LATIN1);
14240 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <
14241 JSFatInlineString::MAX_LENGTH_TWO_BYTE);
14242
14243 // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having
14244 // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte
14245 // strings.
14246
14247 Label allocFat, allocDone;
14248 if (tryFatInlineOrDependent) {
14249 Label isLatin1, allocThin;
14250 masm.branchLatin1String(string, &isLatin1);
14251 {
14252 if (tryDependent) {
14253 masm.branch32(Assembler::Above, length,
14254 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
14255 &notInline);
14256 }
14257 masm.move32(Imm32(0), temp2);
14258 masm.branch32(Assembler::Above, length,
14259 Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE),
14260 &allocFat);
14261 masm.jump(&allocThin);
14262 }
14263
14264 masm.bind(&isLatin1);
14265 {
14266 if (tryDependent) {
14267 masm.branch32(Assembler::Above, length,
14268 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1),
14269 &notInline);
14270 }
14271 masm.move32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp2);
14272 masm.branch32(Assembler::Above, length,
14273 Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat);
14274 }
14275
14276 masm.bind(&allocThin);
14277 } else {
14278 masm.load32(Address(string, JSString::offsetOfFlags()), temp2);
14279 masm.and32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp2);
14280 }
14281
14282 {
14283 masm.newGCString(output, temp0, initialStringHeap(), slowPath);
14284 masm.or32(Imm32(StringFlags::INIT_THIN_INLINE_FLAGS), temp2);
14285 }
14286
14287 if (tryFatInlineOrDependent) {
14288 masm.jump(&allocDone);
14289
14290 masm.bind(&allocFat);
14291 {
14292 masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath);
14293 masm.or32(Imm32(StringFlags::INIT_FAT_INLINE_FLAGS), temp2);
14294 }
14295
14296 masm.bind(&allocDone);
14297 }
14298
14299 masm.store32(temp2, Address(output, JSString::offsetOfFlags()));
14300 masm.store32(length, Address(output, JSString::offsetOfLength()));
14301
14302 auto initializeInlineString = [&](CharEncoding encoding) {
14303 masm.loadStringChars(string, temp0, encoding);
14304 masm.addToCharPtr(temp0, begin, encoding);
14305 if (temp1 == string) {
14306 masm.push(string);
14307 }
14308 masm.loadInlineStringCharsForStore(output, temp1);
14309 CopyStringChars(masm, temp1, temp0, length, temp2, encoding,
14310 maximumLength);
14311 masm.loadStringLength(output, length);
14312 if (temp1 == string) {
14313 masm.pop(string);
14314 }
14315 };
14316
14317 Label isInlineLatin1;
14318 masm.branchTest32(Assembler::NonZero, temp2,
14319 Imm32(StringFlags::LATIN1_CHARS_BIT), &isInlineLatin1);
14320 initializeInlineString(CharEncoding::TwoByte);
14321 masm.jump(done);
14322
14323 masm.bind(&isInlineLatin1);
14324 initializeInlineString(CharEncoding::Latin1);
14325 }
14326
14327 // Handle other cases with a DependentString.
14328 if (tryDependent) {
14329 masm.jump(done);
14330
14331 masm.bind(&notInline);
14332 masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath);
14333 masm.store32(length, Address(output, JSString::offsetOfLength()));
14334
14335 // Note: no post barrier is needed because the dependent string is either
14336 // allocated in the nursery or both strings are tenured (if nursery strings
14337 // are disabled for this zone).
14338 EmitInitDependentStringBase(masm, output, string, temp0, temp2,
14339 /* needsPostBarrier = */ false);
14340
14341 auto initializeDependentString = [&](CharEncoding encoding) {
14342 uint32_t flags = StringFlags::dependentStringFlags(encoding);
14343 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
14344 masm.loadNonInlineStringChars(string, temp0, encoding);
14345 masm.addToCharPtr(temp0, begin, encoding);
14346 masm.storeNonInlineStringChars(temp0, output);
14347 };
14348
14349 Label isLatin1;
14350 masm.branchLatin1String(string, &isLatin1);
14351 initializeDependentString(CharEncoding::TwoByte);
14352 masm.jump(done);
14353
14354 masm.bind(&isLatin1);
14355 initializeDependentString(CharEncoding::Latin1);
14356 }
14357
14358 masm.bind(done);
14359}
14360
14361JitCode* JitZone::generateStringConcatStub(JSContext* cx) {
14362 JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub");
14363
14364 TempAllocator temp(&cx->tempLifoAlloc());
14365 JitContext jcx(cx);
14366 StackMacroAssembler masm(cx, temp);
14367 AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub");
14368
14369 Register lhs = CallTempReg0;
14370 Register rhs = CallTempReg1;
14371 Register temp1 = CallTempReg2;
14372 Register temp2 = CallTempReg3;
14373 Register temp3 = CallTempReg4;
14374 Register output = CallTempReg5;
14375
14376 Label failure;
14377#ifdef JS_USE_LINK_REGISTER
14378 masm.pushReturnAddress();
14379#endif
14380 masm.Push(FramePointer);
14381 masm.moveStackPtrTo(FramePointer);
14382
14383 // If lhs is empty, return rhs.
14384 Label leftEmpty;
14385 masm.loadStringLength(lhs, temp1);
14386 masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
14387
14388 // If rhs is empty, return lhs.
14389 Label rightEmpty;
14390 masm.loadStringLength(rhs, temp2);
14391 masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty);
14392
14393 masm.add32(temp1, temp2);
14394
14395 // Check if we can use a JSInlineString. The result is a Latin1 string if
14396 // lhs and rhs are both Latin1, so we AND the flags.
14397 Label isInlineTwoByte, isInlineLatin1;
14398 masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1);
14399 masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1);
14400
14401 Label isLatin1, notInline;
14402 masm.branchTest32(Assembler::NonZero, temp1,
14403 Imm32(StringFlags::LATIN1_CHARS_BIT), &isLatin1);
14404 {
14405 masm.branch32(Assembler::BelowOrEqual, temp2,
14406 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
14407 &isInlineTwoByte);
14408 masm.jump(&notInline);
14409 }
14410 masm.bind(&isLatin1);
14411 {
14412 masm.branch32(Assembler::BelowOrEqual, temp2,
14413 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1);
14414 }
14415 masm.bind(&notInline);
14416
14417 // Keep AND'ed flags in temp1.
14418
14419 // Ensure result length <= JSString::MAX_LENGTH.
14420 masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
14421
14422 // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap
14423 // == gc::Heap::Default. (As a result, no post barriers are needed below.)
14424 masm.newGCString(output, temp3, initialStringHeap, &failure);
14425
14426 // Store rope length and flags. temp1 still holds the result of AND'ing the
14427 // lhs and rhs flags, so we just have to clear the other flags to get our rope
14428 // flags (Latin1 if both lhs and rhs are Latin1).
14429 static_assert(StringFlags::INIT_ROPE_FLAGS == 0,
14430 "Rope type flags must have no bits set");
14431 masm.and32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp1);
14432 masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
14433 masm.store32(temp2, Address(output, JSString::offsetOfLength()));
14434
14435 // Store left and right nodes.
14436 masm.storeRopeChildren(lhs, rhs, output);
14437 masm.pop(FramePointer);
14438 masm.ret();
14439
14440 masm.bind(&leftEmpty);
14441 masm.mov(rhs, output);
14442 masm.pop(FramePointer);
14443 masm.ret();
14444
14445 masm.bind(&rightEmpty);
14446 masm.mov(lhs, output);
14447 masm.pop(FramePointer);
14448 masm.ret();
14449
14450 masm.bind(&isInlineTwoByte);
14451 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
14452 initialStringHeap, &failure, CharEncoding::TwoByte);
14453 masm.pop(FramePointer);
14454 masm.ret();
14455
14456 masm.bind(&isInlineLatin1);
14457 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
14458 initialStringHeap, &failure, CharEncoding::Latin1);
14459 masm.pop(FramePointer);
14460 masm.ret();
14461
14462 masm.bind(&failure);
14463 masm.movePtr(ImmPtr(nullptr), output);
14464 masm.pop(FramePointer);
14465 masm.ret();
14466
14467 Linker linker(masm);
14468 JitCode* code = linker.newCode(cx, CodeKind::Other);
14469
14470 CollectPerfSpewerJitCodeProfile(code, "StringConcatStub");
14471#ifdef MOZ_VTUNE1
14472 vtune::MarkStub(code, "StringConcatStub");
14473#endif
14474
14475 return code;
14476}
14477
14478void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) {
14479 AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub");
14480
14481 lazyLinkStubOffset_ = startTrampolineCode(masm);
14482
14483#ifdef JS_USE_LINK_REGISTER
14484 masm.pushReturnAddress();
14485#endif
14486 masm.Push(FramePointer);
14487 masm.moveStackPtrTo(FramePointer);
14488
14489 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
14490 Register temp0 = regs.takeAny();
14491 Register temp1 = regs.takeAny();
14492 Register temp2 = regs.takeAny();
14493
14494 masm.loadJSContext(temp0);
14495 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink);
14496 masm.moveStackPtrTo(temp1);
14497
14498 using Fn = uint8_t* (*)(JSContext * cx, LazyLinkExitFrameLayout * frame);
14499 masm.setupUnalignedABICall(temp2);
14500 masm.passABIArg(temp0);
14501 masm.passABIArg(temp1);
14502 masm.callWithABI<Fn, LazyLinkTopActivation>(
14503 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
14504
14505 // Discard exit frame and restore frame pointer.
14506 masm.leaveExitFrame(0);
14507 masm.pop(FramePointer);
14508
14509#ifdef JS_USE_LINK_REGISTER
14510 // Restore the return address such that the emitPrologue function of the
14511 // CodeGenerator can push it back on the stack with pushReturnAddress.
14512 masm.popReturnAddress();
14513#endif
14514 masm.jump(ReturnReg);
14515}
14516
14517void JitRuntime::generateInterpreterStub(MacroAssembler& masm) {
14518 AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub");
14519
14520 interpreterStubOffset_ = startTrampolineCode(masm);
14521
14522#ifdef JS_USE_LINK_REGISTER
14523 masm.pushReturnAddress();
14524#endif
14525 masm.Push(FramePointer);
14526 masm.moveStackPtrTo(FramePointer);
14527
14528 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
14529 Register temp0 = regs.takeAny();
14530 Register temp1 = regs.takeAny();
14531 Register temp2 = regs.takeAny();
14532
14533 masm.loadJSContext(temp0);
14534 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub);
14535 masm.moveStackPtrTo(temp1);
14536
14537 using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame);
14538 masm.setupUnalignedABICall(temp2);
14539 masm.passABIArg(temp0);
14540 masm.passABIArg(temp1);
14541 masm.callWithABI<Fn, InvokeFromInterpreterStub>(
14542 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
14543
14544 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
14545
14546 // Discard exit frame and restore frame pointer.
14547 masm.leaveExitFrame(0);
14548 masm.pop(FramePointer);
14549
14550 // InvokeFromInterpreterStub stores the return value in argv[0], where the
14551 // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we
14552 // just popped.
14553 masm.loadValue(Address(masm.getStackPointer(),
14554 JitFrameLayout::offsetOfThis() - sizeof(void*)),
14555 JSReturnOperand);
14556 masm.ret();
14557}
14558
14559void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) {
14560 AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub");
14561 doubleToInt32ValueStubOffset_ = startTrampolineCode(masm);
14562
14563 Label done;
14564 masm.branchTestDouble(Assembler::NotEqual, R0, &done);
14565
14566 masm.unboxDouble(R0, FloatReg0);
14567 masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done,
14568 /* negativeZeroCheck = */ false);
14569 masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0);
14570
14571 masm.bind(&done);
14572 masm.abiret();
14573}
14574
14575void CodeGenerator::visitLinearizeString(LLinearizeString* lir) {
14576 Register str = ToRegister(lir->string());
14577 Register output = ToRegister(lir->output());
14578
14579 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14580 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
14581 lir, ArgList(str), StoreRegisterTo(output));
14582
14583 masm.branchIfRope(str, ool->entry());
14584
14585 if (str != output) {
14586 masm.movePtr(str, output);
14587 }
14588 masm.bind(ool->rejoin());
14589}
14590
14591void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) {
14592 Register str = ToRegister(lir->string());
14593 Register index = ToRegister(lir->index());
14594 Register output = ToRegister(lir->output());
14595
14596 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14597 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
14598 lir, ArgList(str), StoreRegisterTo(output));
14599
14600 masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry());
14601
14602 masm.movePtr(str, output);
14603 masm.bind(ool->rejoin());
14604}
14605
14606void CodeGenerator::visitLinearizeForCodePointAccess(
14607 LLinearizeForCodePointAccess* lir) {
14608 Register str = ToRegister(lir->string());
14609 Register index = ToRegister(lir->index());
14610 Register output = ToRegister(lir->output());
14611 Register temp = ToRegister(lir->temp0());
14612
14613 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14614 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
14615 lir, ArgList(str), StoreRegisterTo(output));
14616
14617 masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp,
14618 ool->entry());
14619
14620 masm.movePtr(str, output);
14621 masm.bind(ool->rejoin());
14622}
14623
14624void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) {
14625 Register index = ToRegister(lir->index());
14626 Register length = ToRegister(lir->length());
14627 Register output = ToRegister(lir->output());
14628
14629 masm.move32(Imm32(0), output);
14630 masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output);
14631 masm.add32(index, output);
14632}
14633
14634void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) {
14635 Register str = ToRegister(lir->string());
14636 Register output = ToRegister(lir->output());
14637 Register temp0 = ToRegister(lir->temp0());
14638 Register temp1 = ToRegister(lir->temp1());
14639
14640 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14641
14642 if (lir->index()->isBogus()) {
14643 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
14644 StoreRegisterTo(output));
14645 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
14646 masm.bind(ool->rejoin());
14647 } else {
14648 Register index = ToRegister(lir->index());
14649
14650 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
14651 StoreRegisterTo(output));
14652 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
14653 masm.bind(ool->rejoin());
14654 }
14655}
14656
14657void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) {
14658 Register str = ToRegister(lir->string());
14659 Register output = ToRegister(lir->output());
14660 Register temp0 = ToRegister(lir->temp0());
14661 Register temp1 = ToRegister(lir->temp1());
14662
14663 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14664
14665 // Return -1 for out-of-bounds access.
14666 masm.move32(Imm32(-1), output);
14667
14668 if (lir->index()->isBogus()) {
14669 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
14670 StoreRegisterTo(output));
14671
14672 masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()),
14673 Imm32(0), ool->rejoin());
14674 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
14675 masm.bind(ool->rejoin());
14676 } else {
14677 Register index = ToRegister(lir->index());
14678
14679 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
14680 StoreRegisterTo(output));
14681
14682 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
14683 temp0, ool->rejoin());
14684 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
14685 masm.bind(ool->rejoin());
14686 }
14687}
14688
14689void CodeGenerator::visitCodePointAt(LCodePointAt* lir) {
14690 Register str = ToRegister(lir->string());
14691 Register index = ToRegister(lir->index());
14692 Register output = ToRegister(lir->output());
14693 Register temp0 = ToRegister(lir->temp0());
14694 Register temp1 = ToRegister(lir->temp1());
14695
14696 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14697 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
14698 StoreRegisterTo(output));
14699
14700 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
14701 masm.bind(ool->rejoin());
14702}
14703
14704void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) {
14705 Register str = ToRegister(lir->string());
14706 Register index = ToRegister(lir->index());
14707 Register output = ToRegister(lir->output());
14708 Register temp0 = ToRegister(lir->temp0());
14709 Register temp1 = ToRegister(lir->temp1());
14710
14711 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14712 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
14713 StoreRegisterTo(output));
14714
14715 // Return -1 for out-of-bounds access.
14716 masm.move32(Imm32(-1), output);
14717
14718 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
14719 temp0, ool->rejoin());
14720 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
14721 masm.bind(ool->rejoin());
14722}
14723
14724void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) {
14725 Register input = ToRegister(lir->input());
14726 ValueOperand output = ToOutValue(lir);
14727
14728 masm.tagValue(JSVAL_TYPE_INT32, input, output);
14729
14730 Label done;
14731 masm.branchTest32(Assembler::NotSigned, input, input, &done);
14732 masm.moveValue(JS::NaNValue(), output);
14733 masm.bind(&done);
14734}
14735
14736void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) {
14737 Register input = ToRegister(lir->input());
14738 ValueOperand output = ToOutValue(lir);
14739
14740 masm.tagValue(JSVAL_TYPE_INT32, input, output);
14741
14742 Label done;
14743 masm.branchTest32(Assembler::NotSigned, input, input, &done);
14744 masm.moveValue(JS::UndefinedValue(), output);
14745 masm.bind(&done);
14746}
14747
14748void CodeGenerator::visitFromCharCode(LFromCharCode* lir) {
14749 Register code = ToRegister(lir->code());
14750 Register output = ToRegister(lir->output());
14751
14752 using Fn = JSLinearString* (*)(JSContext*, int32_t);
14753 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
14754 StoreRegisterTo(output));
14755
14756 // OOL path if code >= UNIT_STATIC_LIMIT.
14757 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
14758 ool->entry());
14759
14760 masm.bind(ool->rejoin());
14761}
14762
14763void CodeGenerator::visitFromCharCodeEmptyIfNegative(
14764 LFromCharCodeEmptyIfNegative* lir) {
14765 Register code = ToRegister(lir->code());
14766 Register output = ToRegister(lir->output());
14767
14768 using Fn = JSLinearString* (*)(JSContext*, int32_t);
14769 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
14770 StoreRegisterTo(output));
14771
14772 // Return the empty string for negative inputs.
14773 const JSAtomState& names = gen->runtime->names();
14774 masm.movePtr(ImmGCPtr(names.empty_), output);
14775 masm.branchTest32(Assembler::Signed, code, code, ool->rejoin());
14776
14777 // OOL path if code >= UNIT_STATIC_LIMIT.
14778 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
14779 ool->entry());
14780
14781 masm.bind(ool->rejoin());
14782}
14783
14784void CodeGenerator::visitFromCharCodeUndefinedIfNegative(
14785 LFromCharCodeUndefinedIfNegative* lir) {
14786 Register code = ToRegister(lir->code());
14787 ValueOperand output = ToOutValue(lir);
14788 Register temp = output.scratchReg();
14789
14790 using Fn = JSLinearString* (*)(JSContext*, int32_t);
14791 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
14792 StoreRegisterTo(temp));
14793
14794 // Return |undefined| for negative inputs.
14795 Label done;
14796 masm.moveValue(UndefinedValue(), output);
14797 masm.branchTest32(Assembler::Signed, code, code, &done);
14798
14799 // OOL path if code >= UNIT_STATIC_LIMIT.
14800 masm.lookupStaticString(code, temp, gen->runtime->staticStrings(),
14801 ool->entry());
14802
14803 masm.bind(ool->rejoin());
14804 masm.tagValue(JSVAL_TYPE_STRING, temp, output);
14805
14806 masm.bind(&done);
14807}
14808
14809void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) {
14810 Register codePoint = ToRegister(lir->codePoint());
14811 Register output = ToRegister(lir->output());
14812 Register temp0 = ToRegister(lir->temp0());
14813 Register temp1 = ToRegister(lir->temp1());
14814 LSnapshot* snapshot = lir->snapshot();
14815
14816 // The OOL path is only taken when we can't allocate the inline string.
14817 using Fn = JSLinearString* (*)(JSContext*, char32_t);
14818 auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint),
14819 StoreRegisterTo(output));
14820
14821 Label isTwoByte;
14822 Label* done = ool->rejoin();
14823
14824 static_assert(
14825 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
14826 "Latin-1 strings can be loaded from static strings");
14827
14828 {
14829 masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(),
14830 &isTwoByte);
14831 masm.jump(done);
14832 }
14833 masm.bind(&isTwoByte);
14834 {
14835 // Use a bailout if the input is not a valid code point, because
14836 // MFromCodePoint is movable and it'd be observable when a moved
14837 // fromCodePoint throws an exception before its actual call site.
14838 bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax),
14839 snapshot);
14840
14841 // Allocate a JSThinInlineString.
14842 {
14843 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2,
14844 "JSThinInlineString can hold a supplementary code point");
14845
14846 uint32_t flags =
14847 StringFlags::thinInlineStringFlags(CharEncoding::TwoByte);
14848 masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry());
14849 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
14850 }
14851
14852 Label isSupplementary;
14853 masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin),
14854 &isSupplementary);
14855 {
14856 // Store length.
14857 masm.store32(Imm32(1), Address(output, JSString::offsetOfLength()));
14858
14859 // Load chars pointer in temp0.
14860 masm.loadInlineStringCharsForStore(output, temp0);
14861
14862 masm.store16(codePoint, Address(temp0, 0));
14863
14864 masm.jump(done);
14865 }
14866 masm.bind(&isSupplementary);
14867 {
14868 // Store length.
14869 masm.store32(Imm32(2), Address(output, JSString::offsetOfLength()));
14870
14871 // Load chars pointer in temp0.
14872 masm.loadInlineStringCharsForStore(output, temp0);
14873
14874 // Inlined unicode::LeadSurrogate(uint32_t).
14875 masm.rshift32(Imm32(10), codePoint, temp1);
14876 masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)),
14877 temp1);
14878
14879 masm.store16(temp1, Address(temp0, 0));
14880
14881 // Inlined unicode::TrailSurrogate(uint32_t).
14882 masm.and32(Imm32(0x3FF), codePoint, temp1);
14883 masm.or32(Imm32(unicode::TrailSurrogateMin), temp1);
14884
14885 masm.store16(temp1, Address(temp0, sizeof(char16_t)));
14886 }
14887 }
14888
14889 masm.bind(done);
14890}
14891
14892void CodeGenerator::visitStringIncludes(LStringIncludes* lir) {
14893 pushArg(ToRegister(lir->searchString()));
14894 pushArg(ToRegister(lir->string()));
14895
14896 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14897 callVM<Fn, js::StringIncludes>(lir);
14898}
14899
14900template <typename LIns>
14901static void CallStringMatch(MacroAssembler& masm, LIns* lir,
14902 LiveRegisterSet volatileRegs) {
14903 Register string = ToRegister(lir->string());
14904 Register output = ToRegister(lir->output());
14905 Register tempLength = ToRegister(lir->temp0());
14906 Register tempChars = ToRegister(lir->temp1());
14907 Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2());
14908
14909 const JSOffThreadAtom* searchString = lir->searchString();
14910 size_t length = searchString->length();
14911 MOZ_ASSERT(length == 1 || length == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length == 1 || length == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(length == 1 || length == 2))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length == 1 || length == 2"
, "./../../../../js/src/jit/CodeGenerator.cpp", 14911); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length == 1 || length == 2" ")"); do { MOZ_CrashSequence
(__null, 14911); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
14912
14913 // The additional temp register is only needed when searching for two
14914 // pattern characters.
14915 MOZ_ASSERT_IF(length == 2, maybeTempPat != InvalidReg)do { if (length == 2) { do { static_assert( mozilla::detail::
AssertionConditionType<decltype(maybeTempPat != InvalidReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(maybeTempPat != InvalidReg))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("maybeTempPat != InvalidReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 14915); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 14915); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
14916
14917 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
14918 masm.move32(Imm32(0), output);
14919 } else {
14920 masm.move32(Imm32(-1), output);
14921 }
14922
14923 masm.loadStringLength(string, tempLength);
14924
14925 // Can't be a substring when the string is smaller than the search string.
14926 Label done;
14927 masm.branch32(Assembler::Below, tempLength, Imm32(length), &done);
14928
14929 bool searchStringIsPureTwoByte = false;
14930 if (searchString->hasTwoByteChars()) {
14931 JS::AutoCheckCannotGC nogc;
14932 searchStringIsPureTwoByte =
14933 !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc));
14934 }
14935
14936 // Pure two-byte strings can't occur in a Latin-1 string.
14937 if (searchStringIsPureTwoByte) {
14938 masm.branchLatin1String(string, &done);
14939 }
14940
14941#ifdef DEBUG1
14942 // We don't expect to see ropes here.
14943 Label notRope;
14944 masm.branchIfNotRope(string, &notRope);
14945 masm.assumeUnreachable("input string must be linearized");
14946 masm.bind(&notRope);
14947#endif
14948
14949 Label restoreVolatile;
14950
14951 auto callMatcher = [&](CharEncoding encoding) {
14952 masm.loadStringChars(string, tempChars, encoding);
14953
14954 LiveGeneralRegisterSet liveRegs;
14955 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
14956 // Save |tempChars| to compute the result index.
14957 liveRegs.add(tempChars);
14958
14959#ifdef DEBUG1
14960 // Save |tempLength| in debug-mode for assertions.
14961 liveRegs.add(tempLength);
14962#endif
14963
14964 // Exclude non-volatile registers.
14965 liveRegs.set() = GeneralRegisterSet::Intersect(
14966 liveRegs.set(), GeneralRegisterSet::Volatile());
14967
14968 masm.PushRegsInMask(liveRegs);
14969 }
14970
14971 if (length == 1) {
14972 char16_t pat = searchString->latin1OrTwoByteChar(0);
14973 MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pat <=
JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pat <= JSString::MAX_LATIN1_CHAR", "./../../../../js/src/jit/CodeGenerator.cpp"
, 14974); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 14974); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
14974 pat <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pat <=
JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pat <= JSString::MAX_LATIN1_CHAR", "./../../../../js/src/jit/CodeGenerator.cpp"
, 14974); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 14974); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
14975
14976 masm.move32(Imm32(pat), output);
14977
14978 masm.setupAlignedABICall();
14979 masm.passABIArg(tempChars);
14980 masm.passABIArg(output);
14981 masm.passABIArg(tempLength);
14982 if (encoding == CharEncoding::Latin1) {
14983 using Fn = const char* (*)(const char*, char, size_t);
14984 masm.callWithABI<Fn, mozilla::SIMD::memchr8>(
14985 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
14986 } else {
14987 using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t);
14988 masm.callWithABI<Fn, mozilla::SIMD::memchr16>(
14989 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
14990 }
14991 } else {
14992 char16_t pat0 = searchString->latin1OrTwoByteChar(0);
14993 MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pat0 <=
JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pat0 <= JSString::MAX_LATIN1_CHAR", "./../../../../js/src/jit/CodeGenerator.cpp"
, 14994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 14994); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
14994 pat0 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pat0 <=
JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pat0 <= JSString::MAX_LATIN1_CHAR", "./../../../../js/src/jit/CodeGenerator.cpp"
, 14994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 14994); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
14995
14996 char16_t pat1 = searchString->latin1OrTwoByteChar(1);
14997 MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pat1 <=
JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pat1 <= JSString::MAX_LATIN1_CHAR", "./../../../../js/src/jit/CodeGenerator.cpp"
, 14998); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 14998); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
14998 pat1 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(pat1 <=
JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pat1 <= JSString::MAX_LATIN1_CHAR", "./../../../../js/src/jit/CodeGenerator.cpp"
, 14998); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 14998); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
14999
15000 masm.move32(Imm32(pat0), output);
15001 masm.move32(Imm32(pat1), maybeTempPat);
15002
15003 masm.setupAlignedABICall();
15004 masm.passABIArg(tempChars);
15005 masm.passABIArg(output);
15006 masm.passABIArg(maybeTempPat);
15007 masm.passABIArg(tempLength);
15008 if (encoding == CharEncoding::Latin1) {
15009 using Fn = const char* (*)(const char*, char, char, size_t);
15010 masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>(
15011 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
15012 } else {
15013 using Fn =
15014 const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t);
15015 masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>(
15016 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
15017 }
15018 }
15019
15020 masm.storeCallPointerResult(output);
15021
15022 // Convert to string index for `indexOf`.
15023 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
15024 // Restore |tempChars|. (And in debug mode |tempLength|.)
15025 masm.PopRegsInMask(liveRegs);
15026
15027 Label found;
15028 masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found);
15029 {
15030 masm.move32(Imm32(-1), output);
15031 masm.jump(&restoreVolatile);
15032 }
15033 masm.bind(&found);
15034
15035#ifdef DEBUG1
15036 // Check lower bound.
15037 Label lower;
15038 masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower);
15039 masm.assumeUnreachable("result pointer below string chars");
15040 masm.bind(&lower);
15041
15042 // Compute the end position of the characters.
15043 auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo;
15044 masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale),
15045 tempLength);
15046
15047 // Check upper bound.
15048 Label upper;
15049 masm.branchPtr(Assembler::Below, output, tempLength, &upper);
15050 masm.assumeUnreachable("result pointer above string chars");
15051 masm.bind(&upper);
15052#endif
15053
15054 masm.subPtr(tempChars, output);
15055
15056 if (encoding == CharEncoding::TwoByte) {
15057 masm.rshiftPtr(Imm32(1), output);
15058 }
15059 }
15060 };
15061
15062 volatileRegs.takeUnchecked(output);
15063 volatileRegs.takeUnchecked(tempLength);
15064 volatileRegs.takeUnchecked(tempChars);
15065 if (maybeTempPat != InvalidReg) {
15066 volatileRegs.takeUnchecked(maybeTempPat);
15067 }
15068 masm.PushRegsInMask(volatileRegs);
15069
15070 // Handle the case when the input is a Latin-1 string.
15071 if (!searchStringIsPureTwoByte) {
15072 Label twoByte;
15073 masm.branchTwoByteString(string, &twoByte);
15074 {
15075 callMatcher(CharEncoding::Latin1);
15076 masm.jump(&restoreVolatile);
15077 }
15078 masm.bind(&twoByte);
15079 }
15080
15081 // Handle the case when the input is a two-byte string.
15082 callMatcher(CharEncoding::TwoByte);
15083
15084 masm.bind(&restoreVolatile);
15085 masm.PopRegsInMask(volatileRegs);
15086
15087 // Convert to bool for `includes`.
15088 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
15089 masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output);
15090 }
15091
15092 masm.bind(&done);
15093}
15094
15095void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) {
15096 CallStringMatch(masm, lir, liveVolatileRegs(lir));
15097}
15098
15099void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) {
15100 pushArg(ToRegister(lir->searchString()));
15101 pushArg(ToRegister(lir->string()));
15102
15103 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
15104 callVM<Fn, js::StringIndexOf>(lir);
15105}
15106
15107void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) {
15108 CallStringMatch(masm, lir, liveVolatileRegs(lir));
15109}
15110
15111void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) {
15112 pushArg(ToRegister(lir->searchString()));
15113 pushArg(ToRegister(lir->string()));
15114
15115 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
15116 callVM<Fn, js::StringLastIndexOf>(lir);
15117}
15118
15119void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) {
15120 pushArg(ToRegister(lir->searchString()));
15121 pushArg(ToRegister(lir->string()));
15122
15123 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15124 callVM<Fn, js::StringStartsWith>(lir);
15125}
15126
15127void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) {
15128 Register string = ToRegister(lir->string());
15129 Register output = ToRegister(lir->output());
15130 Register temp = ToRegister(lir->temp0());
15131
15132 const JSOffThreadAtom* searchString = lir->searchString();
15133
15134 size_t length = searchString->length();
15135 MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("length > 0",
"./../../../../js/src/jit/CodeGenerator.cpp", 15135); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length > 0" ")"); do { MOZ_CrashSequence
(__null, 15135); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15136
15137 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15138 auto* ool = oolCallVM<Fn, js::StringStartsWith>(
15139 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
15140
15141 masm.move32(Imm32(0), output);
15142
15143 // Can't be a prefix when the string is smaller than the search string.
15144 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
15145 Imm32(length), ool->rejoin());
15146
15147 // Unwind ropes at the start if possible.
15148 Label compare;
15149 masm.movePtr(string, temp);
15150 masm.branchIfNotRope(temp, &compare);
15151
15152 Label unwindRope;
15153 masm.bind(&unwindRope);
15154 masm.loadRopeLeftChild(temp, output);
15155 masm.movePtr(output, temp);
15156
15157 // If the left child is smaller than the search string, jump into the VM to
15158 // linearize the string.
15159 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
15160 Imm32(length), ool->entry());
15161
15162 // Otherwise keep unwinding ropes.
15163 masm.branchIfRope(temp, &unwindRope);
15164
15165 masm.bind(&compare);
15166
15167 // If operands point to the same instance, it's trivially a prefix.
15168 Label notPointerEqual;
15169 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
15170 &notPointerEqual);
15171 masm.move32(Imm32(1), output);
15172 masm.jump(ool->rejoin());
15173 masm.bind(&notPointerEqual);
15174
15175 if (searchString->hasTwoByteChars()) {
15176 // Pure two-byte strings can't be a prefix of Latin-1 strings.
15177 JS::AutoCheckCannotGC nogc;
15178 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
15179 Label compareChars;
15180 masm.branchTwoByteString(temp, &compareChars);
15181 masm.move32(Imm32(0), output);
15182 masm.jump(ool->rejoin());
15183 masm.bind(&compareChars);
15184 }
15185 }
15186
15187 // Load the input string's characters.
15188 Register stringChars = output;
15189 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
15190
15191 // Start comparing character by character.
15192 masm.compareStringChars(JSOp::Eq, stringChars, searchString, output);
15193
15194 masm.bind(ool->rejoin());
15195}
15196
15197void CodeGenerator::visitStringEndsWith(LStringEndsWith* lir) {
15198 pushArg(ToRegister(lir->searchString()));
15199 pushArg(ToRegister(lir->string()));
15200
15201 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15202 callVM<Fn, js::StringEndsWith>(lir);
15203}
15204
15205void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) {
15206 Register string = ToRegister(lir->string());
15207 Register output = ToRegister(lir->output());
15208 Register temp = ToRegister(lir->temp0());
15209
15210 const JSOffThreadAtom* searchString = lir->searchString();
15211
15212 size_t length = searchString->length();
15213 MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(length > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("length > 0",
"./../../../../js/src/jit/CodeGenerator.cpp", 15213); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length > 0" ")"); do { MOZ_CrashSequence
(__null, 15213); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15214
15215 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15216 auto* ool = oolCallVM<Fn, js::StringEndsWith>(
15217 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
15218
15219 masm.move32(Imm32(0), output);
15220
15221 // Can't be a suffix when the string is smaller than the search string.
15222 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
15223 Imm32(length), ool->rejoin());
15224
15225 // Unwind ropes at the end if possible.
15226 Label compare;
15227 masm.movePtr(string, temp);
15228 masm.branchIfNotRope(temp, &compare);
15229
15230 Label unwindRope;
15231 masm.bind(&unwindRope);
15232 masm.loadRopeRightChild(temp, output);
15233 masm.movePtr(output, temp);
15234
15235 // If the right child is smaller than the search string, jump into the VM to
15236 // linearize the string.
15237 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
15238 Imm32(length), ool->entry());
15239
15240 // Otherwise keep unwinding ropes.
15241 masm.branchIfRope(temp, &unwindRope);
15242
15243 masm.bind(&compare);
15244
15245 // If operands point to the same instance, it's trivially a suffix.
15246 Label notPointerEqual;
15247 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
15248 &notPointerEqual);
15249 masm.move32(Imm32(1), output);
15250 masm.jump(ool->rejoin());
15251 masm.bind(&notPointerEqual);
15252
15253 CharEncoding encoding = searchString->hasLatin1Chars()
15254 ? CharEncoding::Latin1
15255 : CharEncoding::TwoByte;
15256 if (encoding == CharEncoding::TwoByte) {
15257 // Pure two-byte strings can't be a suffix of Latin-1 strings.
15258 JS::AutoCheckCannotGC nogc;
15259 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
15260 Label compareChars;
15261 masm.branchTwoByteString(temp, &compareChars);
15262 masm.move32(Imm32(0), output);
15263 masm.jump(ool->rejoin());
15264 masm.bind(&compareChars);
15265 }
15266 }
15267
15268 // Load the input string's characters.
15269 Register stringChars = output;
15270 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
15271
15272 // Move string-char pointer to the suffix string.
15273 masm.loadStringLength(temp, temp);
15274 masm.sub32(Imm32(length), temp);
15275 masm.addToCharPtr(stringChars, temp, encoding);
15276
15277 // Start comparing character by character.
15278 masm.compareStringChars(JSOp::Eq, stringChars, searchString, output);
15279
15280 masm.bind(ool->rejoin());
15281}
15282
15283void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) {
15284 Register string = ToRegister(lir->string());
15285 Register output = ToRegister(lir->output());
15286 Register temp0 = ToRegister(lir->temp0());
15287 Register temp1 = ToRegister(lir->temp1());
15288 Register temp2 = ToRegister(lir->temp2());
15289
15290 // On x86 there are not enough registers. In that case reuse the string
15291 // register as a temporary.
15292 Register temp3 =
15293 lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3());
15294 Register temp4 = ToRegister(lir->temp4());
15295
15296 using Fn = JSLinearString* (*)(JSContext*, JSString*);
15297 OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>(
15298 lir, ArgList(string), StoreRegisterTo(output));
15299
15300 // Take the slow path if the string isn't a linear Latin-1 string.
15301 Imm32 linearLatin1Bits(StringFlags::LINEAR_BIT |
15302 StringFlags::LATIN1_CHARS_BIT);
15303 Register flags = temp0;
15304 masm.load32(Address(string, JSString::offsetOfFlags()), flags);
15305 masm.and32(linearLatin1Bits, flags);
15306 masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry());
15307
15308 Register length = temp0;
15309 masm.loadStringLength(string, length);
15310
15311 // Return the input if it's the empty string.
15312 Label notEmptyString;
15313 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmptyString);
15314 {
15315 masm.movePtr(string, output);
15316 masm.jump(ool->rejoin());
15317 }
15318 masm.bind(&notEmptyString);
15319
15320 Register inputChars = temp1;
15321 masm.loadStringChars(string, inputChars, CharEncoding::Latin1);
15322
15323 Register toLowerCaseTable = temp2;
15324 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable);
15325
15326 // Single element strings can be directly retrieved from static strings cache.
15327 Label notSingleElementString;
15328 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleElementString);
15329 {
15330 Register current = temp4;
15331
15332 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
15333 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
15334 current);
15335 masm.lookupStaticString(current, output, gen->runtime->staticStrings());
15336
15337 masm.jump(ool->rejoin());
15338 }
15339 masm.bind(&notSingleElementString);
15340
15341 // Use the OOL-path when the string is too long. This prevents scanning long
15342 // strings which have upper case characters only near the end a second time in
15343 // the VM.
15344 constexpr int32_t MaxInlineLength = 64;
15345 masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry());
15346
15347 {
15348 // Check if there are any characters which need to be converted.
15349 //
15350 // This extra loop gives a small performance improvement for strings which
15351 // are already lower cased and lets us avoid calling into the runtime for
15352 // non-inline, all lower case strings. But more importantly it avoids
15353 // repeated inline allocation failures:
15354 // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the
15355 // |js::StringToLowerCase| runtime function when the result string can't be
15356 // allocated inline. And |js::StringToLowerCase| directly returns the input
15357 // string when no characters need to be converted. That means it won't
15358 // trigger GC to clear up the free nursery space, so the next toLowerCase()
15359 // call will again fail to inline allocate the result string.
15360 Label hasUpper;
15361 {
15362 Register checkInputChars = output;
15363 masm.movePtr(inputChars, checkInputChars);
15364
15365 Register current = temp4;
15366
15367 Label start;
15368 masm.bind(&start);
15369 masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1);
15370 masm.branch8(Assembler::NotEqual,
15371 BaseIndex(toLowerCaseTable, current, TimesOne), current,
15372 &hasUpper);
15373 masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars);
15374 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
15375
15376 // Input is already in lower case.
15377 masm.movePtr(string, output);
15378 masm.jump(ool->rejoin());
15379 }
15380 masm.bind(&hasUpper);
15381
15382 // |length| was clobbered above, reload.
15383 masm.loadStringLength(string, length);
15384
15385 // Call into the runtime when we can't create an inline string.
15386 masm.branch32(Assembler::Above, length,
15387 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry());
15388
15389 AllocateThinOrFatInlineString(masm, output, length, temp4,
15390 initialStringHeap(), ool->entry(),
15391 CharEncoding::Latin1);
15392
15393 if (temp3 == string) {
15394 masm.push(string);
15395 }
15396
15397 Register outputChars = temp3;
15398 masm.loadInlineStringCharsForStore(output, outputChars);
15399
15400 {
15401 Register current = temp4;
15402
15403 Label start;
15404 masm.bind(&start);
15405 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
15406 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
15407 current);
15408 masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1);
15409 masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars);
15410 masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars);
15411 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
15412 }
15413
15414 if (temp3 == string) {
15415 masm.pop(string);
15416 }
15417 }
15418
15419 masm.bind(ool->rejoin());
15420}
15421
15422void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) {
15423 pushArg(ToRegister(lir->string()));
15424
15425 using Fn = JSLinearString* (*)(JSContext*, JSString*);
15426 callVM<Fn, js::StringToUpperCase>(lir);
15427}
15428
15429void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) {
15430 Register code = ToRegister(lir->code());
15431 Register output = ToRegister(lir->output());
15432 Register temp = ToRegister(lir->temp0());
15433
15434 using Fn = JSString* (*)(JSContext*, int32_t);
15435 auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code),
15436 StoreRegisterTo(output));
15437
15438 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
15439
15440 // OOL path if code >= NonLatin1Min.
15441 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
15442
15443 // Convert to lower case.
15444 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp);
15445 masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp);
15446
15447 // Load static string for lower case character.
15448 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
15449
15450 masm.bind(ool->rejoin());
15451}
15452
15453void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) {
15454 Register code = ToRegister(lir->code());
15455 Register output = ToRegister(lir->output());
15456 Register temp = ToRegister(lir->temp0());
15457
15458 using Fn = JSString* (*)(JSContext*, int32_t);
15459 auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code),
15460 StoreRegisterTo(output));
15461
15462 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
15463
15464 // OOL path if code >= NonLatin1Min.
15465 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
15466
15467 // Most one element Latin-1 strings can be directly retrieved from the
15468 // static strings cache, except the following three characters:
15469 //
15470 // 1. ToUpper(U+00B5) = 0+039C
15471 // 2. ToUpper(U+00FF) = 0+0178
15472 // 3. ToUpper(U+00DF) = 0+0053 0+0053
15473 masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN),
15474 ool->entry());
15475 masm.branch32(Assembler::Equal, code,
15476 Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS),
15477 ool->entry());
15478 masm.branch32(Assembler::Equal, code,
15479 Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry());
15480
15481 // Inline unicode::ToUpperCase (without the special case for ASCII characters)
15482
15483 constexpr size_t shift = unicode::CharInfoShift;
15484
15485 // code >> shift
15486 masm.rshift32(Imm32(shift), code, temp);
15487
15488 // index = index1[code >> shift];
15489 masm.movePtr(ImmPtr(unicode::index1), output);
15490 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
15491
15492 // (code & ((1 << shift) - 1)
15493 masm.and32(Imm32((1 << shift) - 1), code, output);
15494
15495 // (index << shift) + (code & ((1 << shift) - 1))
15496 masm.lshift32(Imm32(shift), temp);
15497 masm.add32(output, temp);
15498
15499 // index = index2[(index << shift) + (code & ((1 << shift) - 1))]
15500 masm.movePtr(ImmPtr(unicode::index2), output);
15501 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
15502
15503 // Compute |index * 6| through |(index * 3) * TimesTwo|.
15504 static_assert(sizeof(unicode::CharacterInfo) == 6);
15505 masm.mulBy3(temp, temp);
15506
15507 // upperCase = js_charinfo[index].upperCase
15508 masm.movePtr(ImmPtr(unicode::js_charinfo), output);
15509 masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo,
15510 offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)),
15511 temp);
15512
15513 // uint16_t(ch) + upperCase
15514 masm.add32(code, temp);
15515
15516 // Clear any high bits added when performing the unsigned 16-bit addition
15517 // through a signed 32-bit addition.
15518 masm.move8ZeroExtend(temp, temp);
15519
15520 // Load static string for upper case character.
15521 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
15522
15523 masm.bind(ool->rejoin());
15524}
15525
15526void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) {
15527 Register string = ToRegister(lir->string());
15528 Register output = ToRegister(lir->output());
15529
15530 using Fn = int32_t (*)(const JSString*);
15531 masm.setupAlignedABICall();
15532 masm.passABIArg(string);
15533 masm.callWithABI<Fn, jit::StringTrimStartIndex>();
15534 masm.storeCallInt32Result(output);
15535}
15536
15537void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) {
15538 Register string = ToRegister(lir->string());
15539 Register start = ToRegister(lir->start());
15540 Register output = ToRegister(lir->output());
15541
15542 using Fn = int32_t (*)(const JSString*, int32_t);
15543 masm.setupAlignedABICall();
15544 masm.passABIArg(string);
15545 masm.passABIArg(start);
15546 masm.callWithABI<Fn, jit::StringTrimEndIndex>();
15547 masm.storeCallInt32Result(output);
15548}
15549
15550void CodeGenerator::visitStringSplit(LStringSplit* lir) {
15551 pushArg(Imm32(INT32_MAX(2147483647)));
15552 pushArg(ToRegister(lir->separator()));
15553 pushArg(ToRegister(lir->string()));
15554
15555 using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t);
15556 callVM<Fn, js::StringSplitString>(lir);
15557}
15558
15559void CodeGenerator::visitInitializedLength(LInitializedLength* lir) {
15560 Address initLength(ToRegister(lir->elements()),
15561 ObjectElements::offsetOfInitializedLength());
15562 masm.load32(initLength, ToRegister(lir->output()));
15563}
15564
15565void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) {
15566 Register elements = ToRegister(lir->elements());
15567 uint32_t newLength = lir->mir()->length();
15568 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
15569
15570 if (lir->mir()->needsPreBarrier()) {
15571 // The elements at or above the new initialized length are no longer part of
15572 // the object, so we need a pre-barrier.
15573 Register index = ToRegister(lir->temp0());
15574 Label done;
15575 masm.branchTestNeedsMarkingBarrier(Assembler::Zero, &done);
15576 masm.load32(initLength, index);
15577 Label loop;
15578 masm.bind(&loop);
15579 masm.branch32(Assembler::BelowOrEqual, index, Imm32(newLength), &done);
15580 masm.sub32(Imm32(1), index);
15581 masm.unguardedCallPreBarrier(BaseValueIndex(elements, index),
15582 MIRType::Value);
15583 masm.jump(&loop);
15584 masm.bind(&done);
15585 } else {
15586#ifdef DEBUG1
15587 // Callers that don't need a barrier must not remove any elements.
15588 Label ok;
15589 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(newLength), &ok);
15590 masm.assumeUnreachable("removing elements without a pre-barrier");
15591 masm.bind(&ok);
15592#endif
15593 }
15594
15595 masm.store32(Imm32(newLength), initLength);
15596}
15597
15598void CodeGenerator::visitNotI(LNotI* lir) {
15599 Register input = ToRegister(lir->input());
15600 Register output = ToRegister(lir->output());
15601
15602 masm.cmp32Set(Assembler::Equal, input, Imm32(0), output);
15603}
15604
15605void CodeGenerator::visitNotIPtr(LNotIPtr* lir) {
15606 Register input = ToRegister(lir->input());
15607 Register output = ToRegister(lir->output());
15608
15609 masm.cmpPtrSet(Assembler::Equal, input, ImmWord(0), output);
15610}
15611
15612void CodeGenerator::visitNotI64(LNotI64* lir) {
15613 Register64 input = ToRegister64(lir->inputI64());
15614 Register output = ToRegister(lir->output());
15615
15616 masm.cmp64Set(Assembler::Equal, input, Imm64(0), output);
15617}
15618
15619void CodeGenerator::visitNotBI(LNotBI* lir) {
15620 Register input = ToRegister(lir->input());
15621 Register output = ToRegister(lir->output());
15622
15623 masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()),
15624 Imm32(0), output);
15625}
15626
15627void CodeGenerator::visitNotO(LNotO* lir) {
15628 Register objreg = ToRegister(lir->input());
15629 Register output = ToRegister(lir->output());
15630
15631 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
15632 if (intact) {
15633 // Bug 1874905: It would be fantastic if this could be optimized out.
15634 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
15635 masm.move32(Imm32(0), output);
15636 } else {
15637 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
15638 addOutOfLineCode(ool, lir->mir());
15639
15640 Label* ifEmulatesUndefined = ool->label1();
15641 Label* ifDoesntEmulateUndefined = ool->label2();
15642
15643 branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined,
15644 ifDoesntEmulateUndefined, output, ool);
15645 // fall through
15646
15647 Label join;
15648
15649 masm.move32(Imm32(0), output);
15650 masm.jump(&join);
15651
15652 masm.bind(ifEmulatesUndefined);
15653 masm.move32(Imm32(1), output);
15654
15655 masm.bind(&join);
15656 }
15657}
15658
15659void CodeGenerator::visitNotV(LNotV* lir) {
15660 Label defaultTruthy, defaultFalsy;
15661 Label* ifTruthy = &defaultTruthy;
15662 Label* ifFalsy = &defaultFalsy;
15663 OutOfLineTestObjectWithLabels* ool = nullptr;
15664 if (!hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted()) {
15665 ool = new (alloc()) OutOfLineTestObjectWithLabels();
15666 addOutOfLineCode(ool, lir->mir());
15667 ifTruthy = ool->label1();
15668 ifFalsy = ool->label2();
15669 }
15670
15671 ValueOperand input = ToValue(lir->input());
15672 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
15673 FloatRegister floatTemp = ToFloatRegister(lir->temp0());
15674 Register output = ToRegister(lir->output());
15675 const TypeDataList& observedTypes = lir->mir()->observedTypes();
15676
15677 testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes,
15678 ifTruthy, ifFalsy, ool);
15679
15680 Label join;
15681
15682 // Note that the testValueTruthy call above may choose to fall through
15683 // to ifTruthy instead of branching there.
15684 masm.bind(ifTruthy);
15685 masm.move32(Imm32(0), output);
15686 masm.jump(&join);
15687
15688 masm.bind(ifFalsy);
15689 masm.move32(Imm32(1), output);
15690
15691 // both branches meet here.
15692 masm.bind(&join);
15693}
15694
15695void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) {
15696 const LAllocation* index = lir->index();
15697 const LAllocation* length = lir->length();
15698 LSnapshot* snapshot = lir->snapshot();
15699
15700 MIRType type = lir->mir()->type();
15701
15702 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
15703 if (type == MIRType::Int32) {
15704 bailoutCmp32(cond, lhs, rhs, snapshot);
15705 } else {
15706 MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 15706); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15706); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15707 bailoutCmpPtr(cond, lhs, rhs, snapshot);
15708 }
15709 };
15710
15711 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
15712 int32_t rhs) {
15713 if (type == MIRType::Int32) {
15714 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
15715 } else {
15716 MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 15716); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15716); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15717 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
15718 }
15719 };
15720
15721 if (index->isConstant()) {
15722 // Use uint32 so that the comparison is unsigned.
15723 uint32_t idx = ToInt32(index);
15724 if (length->isConstant()) {
15725 uint32_t len = ToInt32(lir->length());
15726 if (idx < len) {
15727 return;
15728 }
15729 bailout(snapshot);
15730 return;
15731 }
15732
15733 if (length->isGeneralReg()) {
15734 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx);
15735 } else {
15736 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx);
15737 }
15738 return;
15739 }
15740
15741 Register indexReg = ToRegister(index);
15742 if (length->isConstant()) {
15743 bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length));
15744 } else if (length->isGeneralReg()) {
15745 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg);
15746 } else {
15747 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg);
15748 }
15749}
15750
15751void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) {
15752 int32_t min = lir->mir()->minimum();
15753 int32_t max = lir->mir()->maximum();
15754 MOZ_ASSERT(max >= min)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(max >= min)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(max >= min))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("max >= min",
"./../../../../js/src/jit/CodeGenerator.cpp", 15754); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "max >= min" ")"); do { MOZ_CrashSequence
(__null, 15754); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15755
15756 LSnapshot* snapshot = lir->snapshot();
15757 MIRType type = lir->mir()->type();
15758
15759 const LAllocation* length = lir->length();
15760 Register temp = ToRegister(lir->temp0());
15761
15762 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
15763 if (type == MIRType::Int32) {
15764 bailoutCmp32(cond, lhs, rhs, snapshot);
15765 } else {
15766 MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 15766); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15766); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15767 bailoutCmpPtr(cond, lhs, rhs, snapshot);
15768 }
15769 };
15770
15771 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
15772 int32_t rhs) {
15773 if (type == MIRType::Int32) {
15774 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
15775 } else {
15776 MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 15776); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15776); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15777 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
15778 }
15779 };
15780
15781 if (lir->index()->isConstant()) {
15782 int32_t nmin, nmax;
15783 int32_t index = ToInt32(lir->index());
15784 if (mozilla::SafeAdd(index, min, &nmin) &&
15785 mozilla::SafeAdd(index, max, &nmax) && nmin >= 0) {
15786 if (length->isGeneralReg()) {
15787 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax);
15788 } else {
15789 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax);
15790 }
15791 return;
15792 }
15793 masm.mov(ImmWord(index), temp);
15794 } else {
15795 masm.mov(ToRegister(lir->index()), temp);
15796 }
15797
15798 // If the minimum and maximum differ then do an underflow check first.
15799 // If the two are the same then doing an unsigned comparison on the
15800 // length will also catch a negative index.
15801 if (min != max) {
15802 if (min != 0) {
15803 Label bail;
15804 if (type == MIRType::Int32) {
15805 masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
15806 } else {
15807 masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail);
15808 }
15809 bailoutFrom(&bail, snapshot);
15810 }
15811
15812 bailoutCmpConstant(Assembler::LessThan, temp, 0);
15813
15814 if (min != 0) {
15815 int32_t diff;
15816 if (mozilla::SafeSub(max, min, &diff)) {
15817 max = diff;
15818 } else {
15819 if (type == MIRType::Int32) {
15820 masm.sub32(Imm32(min), temp);
15821 } else {
15822 masm.subPtr(Imm32(min), temp);
15823 }
15824 }
15825 }
15826 }
15827
15828 // Compute the maximum possible index. No overflow check is needed when
15829 // max > 0. We can only wraparound to a negative number, which will test as
15830 // larger than all nonnegative numbers in the unsigned comparison, and the
15831 // length is required to be nonnegative (else testing a negative length
15832 // would succeed on any nonnegative index).
15833 if (max != 0) {
15834 if (max < 0) {
15835 Label bail;
15836 if (type == MIRType::Int32) {
15837 masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
15838 } else {
15839 masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail);
15840 }
15841 bailoutFrom(&bail, snapshot);
15842 } else {
15843 if (type == MIRType::Int32) {
15844 masm.add32(Imm32(max), temp);
15845 } else {
15846 masm.addPtr(Imm32(max), temp);
15847 }
15848 }
15849 }
15850
15851 if (length->isGeneralReg()) {
15852 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp);
15853 } else {
15854 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp);
15855 }
15856}
15857
15858void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) {
15859 int32_t min = lir->mir()->minimum();
15860 bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
15861 lir->snapshot());
15862}
15863
15864void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) {
15865 MOZ_ASSERT(JitOptions.spectreIndexMasking)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JitOptions.spectreIndexMasking)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JitOptions.spectreIndexMasking
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"JitOptions.spectreIndexMasking", "./../../../../js/src/jit/CodeGenerator.cpp"
, 15865); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking"
")"); do { MOZ_CrashSequence(__null, 15865); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15866
15867 const LAllocation* length = lir->length();
15868 Register index = ToRegister(lir->index());
15869 Register output = ToRegister(lir->output());
15870
15871 if (lir->mir()->type() == MIRType::Int32) {
15872 if (length->isGeneralReg()) {
15873 masm.spectreMaskIndex32(index, ToRegister(length), output);
15874 } else {
15875 masm.spectreMaskIndex32(index, ToAddress(length), output);
15876 }
15877 } else {
15878 MOZ_ASSERT(lir->mir()->type() == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::IntPtr)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::IntPtr))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::IntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 15878); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr"
")"); do { MOZ_CrashSequence(__null, 15878); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15879 if (length->isGeneralReg()) {
15880 masm.spectreMaskIndexPtr(index, ToRegister(length), output);
15881 } else {
15882 masm.spectreMaskIndexPtr(index, ToAddress(length), output);
15883 }
15884 }
15885}
15886
15887CodeGenerator::AddressOrBaseObjectElementIndex
15888CodeGenerator::ToAddressOrBaseObjectElementIndex(Register elements,
15889 const LAllocation* index) {
15890 if (index->isConstant()) {
15891 NativeObject::elementsSizeMustNotOverflow();
15892 return AddressOrBaseObjectElementIndex(
15893 Address(elements, ToInt32(index) * sizeof(JS::Value)));
15894 }
15895 return AddressOrBaseObjectElementIndex(
15896 BaseObjectElementIndex(elements, ToRegister(index)));
15897}
15898
15899void CodeGenerator::emitStoreHoleCheck(Address dest, LSnapshot* snapshot) {
15900 Label bail;
15901 masm.branchTestMagic(Assembler::Equal, dest, JS_ELEMENTS_HOLE, &bail);
15902 bailoutFrom(&bail, snapshot);
15903}
15904
15905void CodeGenerator::emitStoreHoleCheck(BaseObjectElementIndex dest,
15906 LSnapshot* snapshot) {
15907 Label bail;
15908 masm.branchTestMagic(Assembler::Equal, dest, JS_ELEMENTS_HOLE, &bail);
15909 bailoutFrom(&bail, snapshot);
15910}
15911
15912void CodeGenerator::visitStoreElementT(LStoreElementT* store) {
15913 Register elements = ToRegister(store->elements());
15914 const LAllocation* index = store->index();
15915
15916 MIRType valueType = store->mir()->value()->type();
15917 MOZ_ASSERT(valueType != MIRType::MagicHole)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(valueType != MIRType::MagicHole)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(valueType != MIRType::MagicHole
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"valueType != MIRType::MagicHole", "./../../../../js/src/jit/CodeGenerator.cpp"
, 15917); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole"
")"); do { MOZ_CrashSequence(__null, 15917); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15918
15919 ConstantOrRegister value = ToConstantOrRegister(store->value(), valueType);
15920
15921 auto dest = ToAddressOrBaseObjectElementIndex(elements, index);
15922
15923 dest.match([&](const auto& dest) {
15924 if (store->mir()->needsBarrier()) {
15925 emitPreBarrier(dest);
15926 }
15927
15928 if (store->mir()->needsHoleCheck()) {
15929 emitStoreHoleCheck(dest, store->snapshot());
15930 }
15931
15932 masm.storeUnboxedValue(value, valueType, dest);
15933 });
15934}
15935
15936void CodeGenerator::visitStoreElementV(LStoreElementV* lir) {
15937 ValueOperand value = ToValue(lir->value());
15938 Register elements = ToRegister(lir->elements());
15939 const LAllocation* index = lir->index();
15940
15941 auto dest = ToAddressOrBaseObjectElementIndex(elements, index);
15942
15943 dest.match([&](const auto& dest) {
15944 if (lir->mir()->needsBarrier()) {
15945 emitPreBarrier(dest);
15946 }
15947
15948 if (lir->mir()->needsHoleCheck()) {
15949 emitStoreHoleCheck(dest, lir->snapshot());
15950 }
15951
15952 masm.storeValue(value, dest);
15953 });
15954}
15955
15956void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) {
15957 Register elements = ToRegister(lir->elements());
15958 Register index = ToRegister(lir->index());
15959
15960 Address elementsFlags(elements, ObjectElements::offsetOfFlags());
15961 masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags);
15962
15963 BaseObjectElementIndex element(elements, index);
15964 masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element);
15965}
15966
15967void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) {
15968 Register obj = ToRegister(lir->object());
15969 Register elements = ToRegister(lir->elements());
15970 Register index = ToRegister(lir->index());
15971 Register temp = ToRegister(lir->temp0());
15972
15973 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
15974 Label bail;
15975 masm.prepareOOBStoreElement(obj, index, elements, temp, &bail,
15976 liveVolatileRegs(lir));
15977 bailoutFrom(&bail, lir->snapshot());
15978
15979 // Jump to the inline path where we will store the value.
15980 // We rejoin after the prebarrier, because the memory is uninitialized.
15981 masm.jump(ool.rejoin());
15982 });
15983 addOutOfLineCode(ool, lir->mir());
15984
15985 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
15986 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
15987
15988 emitPreBarrier(BaseObjectElementIndex(elements, index));
15989
15990 masm.bind(ool->rejoin());
15991
15992 MIRType valueType = lir->mir()->value()->type();
15993 MOZ_ASSERT(valueType != MIRType::MagicHole)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(valueType != MIRType::MagicHole)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(valueType != MIRType::MagicHole
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"valueType != MIRType::MagicHole", "./../../../../js/src/jit/CodeGenerator.cpp"
, 15993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole"
")"); do { MOZ_CrashSequence(__null, 15993); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15994
15995 ConstantOrRegister val = ToConstantOrRegister(lir->value(), valueType);
15996 masm.storeUnboxedValue(val, valueType,
15997 BaseObjectElementIndex(elements, index));
15998
15999 if (ValueNeedsPostBarrier(lir->mir()->value())) {
16000 LiveRegisterSet regs = liveVolatileRegs(lir);
16001 emitElementPostWriteBarrier(lir->mir(), regs, obj, index, temp, val);
16002 }
16003}
16004
16005void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) {
16006 Register obj = ToRegister(lir->object());
16007 Register elements = ToRegister(lir->elements());
16008 Register index = ToRegister(lir->index());
16009 ValueOperand value = ToValue(lir->value());
16010 Register temp = ToRegister(lir->temp0());
16011
16012 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
16013 Label bail;
16014 masm.prepareOOBStoreElement(obj, index, elements, temp, &bail,
16015 liveVolatileRegs(lir));
16016 bailoutFrom(&bail, lir->snapshot());
16017
16018 // Jump to the inline path where we will store the value.
16019 // We rejoin after the prebarrier, because the memory is uninitialized.
16020 masm.jump(ool.rejoin());
16021 });
16022 addOutOfLineCode(ool, lir->mir());
16023
16024 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
16025 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
16026
16027 emitPreBarrier(BaseObjectElementIndex(elements, index));
16028
16029 masm.bind(ool->rejoin());
16030 masm.storeValue(value, BaseObjectElementIndex(elements, index));
16031
16032 if (ValueNeedsPostBarrier(lir->mir()->value())) {
16033 LiveRegisterSet regs = liveVolatileRegs(lir);
16034 emitElementPostWriteBarrier(lir->mir(), regs, obj, index, temp,
16035 ConstantOrRegister(value));
16036 }
16037}
16038
16039void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) {
16040 Register obj = ToRegister(lir->object());
16041 Register temp1 = ToRegister(lir->temp0());
16042 Register temp2 = ToRegister(lir->temp1());
16043 ValueOperand out = ToOutValue(lir);
16044
16045 Label bail;
16046 if (lir->mir()->mode() == MArrayPopShift::Pop) {
16047 masm.packedArrayPop(obj, out, temp1, temp2, &bail);
16048 } else {
16049 MOZ_ASSERT(lir->mir()->mode() == MArrayPopShift::Shift)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->mode() == MArrayPopShift::Shift)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->mode() == MArrayPopShift::Shift)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->mode() == MArrayPopShift::Shift"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16049); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift"
")"); do { MOZ_CrashSequence(__null, 16049); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16050 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
16051 masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail);
16052 }
16053 bailoutFrom(&bail, lir->snapshot());
16054}
16055
16056void CodeGenerator::visitArrayPush(LArrayPush* lir) {
16057 Register obj = ToRegister(lir->object());
16058 Register elementsTemp = ToRegister(lir->temp0());
16059 Register length = ToRegister(lir->output());
16060 ValueOperand value = ToValue(lir->value());
16061 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
16062
16063 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
16064 Register temp = ToRegister(lir->temp0());
16065
16066 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
16067 liveRegs.takeUnchecked(temp);
16068 liveRegs.addUnchecked(ToRegister(lir->output()));
16069 liveRegs.addUnchecked(ToValue(lir->value()));
16070
16071 masm.PushRegsInMask(liveRegs);
16072
16073 masm.setupAlignedABICall();
16074 masm.loadJSContext(temp);
16075 masm.passABIArg(temp);
16076 masm.passABIArg(obj);
16077
16078 using Fn = bool (*)(JSContext*, NativeObject* obj);
16079 masm.callWithABI<Fn, NativeObject::addDenseElementPure>();
16080 masm.storeCallPointerResult(temp);
16081
16082 masm.PopRegsInMask(liveRegs);
16083 bailoutIfFalseBool(temp, lir->snapshot());
16084
16085 // Load the reallocated elements pointer.
16086 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
16087
16088 masm.jump(ool.rejoin());
16089 });
16090 addOutOfLineCode(ool, lir->mir());
16091
16092 // Load obj->elements in elementsTemp.
16093 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
16094
16095 Address initLengthAddr(elementsTemp,
16096 ObjectElements::offsetOfInitializedLength());
16097 Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength());
16098 Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity());
16099
16100 // Bail out if length != initLength.
16101 masm.load32(lengthAddr, length);
16102 bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot());
16103
16104 // If length < capacity, we can add a dense element inline. If not, we
16105 // need to allocate more elements.
16106 masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry());
16107 masm.bind(ool->rejoin());
16108
16109 // Store the value.
16110 masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length));
16111
16112 // Update length and initialized length.
16113 masm.add32(Imm32(1), length);
16114 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
16115 masm.store32(length, Address(elementsTemp,
16116 ObjectElements::offsetOfInitializedLength()));
16117
16118 if (ValueNeedsPostBarrier(lir->mir()->value())) {
16119 LiveRegisterSet regs = liveVolatileRegs(lir);
16120 regs.addUnchecked(length);
16121 emitElementPostWriteBarrier(lir->mir(), regs, obj, length, elementsTemp,
16122 ConstantOrRegister(value),
16123 /* indexDiff = */ -1);
16124 }
16125}
16126
16127void CodeGenerator::visitArraySlice(LArraySlice* lir) {
16128 Register object = ToRegister(lir->object());
16129 Register begin = ToRegister(lir->begin());
16130 Register end = ToRegister(lir->end());
16131 Register temp0 = ToRegister(lir->temp0());
16132 Register temp1 = ToRegister(lir->temp1());
16133
16134 Label call, fail;
16135
16136 Label bail;
16137 masm.branchArrayIsNotPacked(object, temp0, temp1, &bail);
16138 bailoutFrom(&bail, lir->snapshot());
16139
16140 // Try to allocate an object.
16141 TemplateObject templateObject(lir->mir()->templateObj());
16142 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
16143 &fail);
16144
16145 masm.jump(&call);
16146 {
16147 masm.bind(&fail);
16148 masm.movePtr(ImmPtr(nullptr), temp0);
16149 }
16150 masm.bind(&call);
16151
16152 pushArg(temp0);
16153 pushArg(end);
16154 pushArg(begin);
16155 pushArg(object);
16156
16157 using Fn =
16158 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
16159 callVM<Fn, ArraySliceDense>(lir);
16160}
16161
16162void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) {
16163 Register object = ToRegister(lir->object());
16164 Register begin = ToRegister(lir->begin());
16165 Register end = ToRegister(lir->end());
16166 Register temp0 = ToRegister(lir->temp0());
16167 Register temp1 = ToRegister(lir->temp1());
16168
16169 Label call, fail;
16170
16171 // Try to allocate an object.
16172 TemplateObject templateObject(lir->mir()->templateObj());
16173 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
16174 &fail);
16175
16176 masm.jump(&call);
16177 {
16178 masm.bind(&fail);
16179 masm.movePtr(ImmPtr(nullptr), temp0);
16180 }
16181 masm.bind(&call);
16182
16183 pushArg(temp0);
16184 pushArg(end);
16185 pushArg(begin);
16186 pushArg(object);
16187
16188 using Fn =
16189 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
16190 callVM<Fn, ArgumentsSliceDense>(lir);
16191}
16192
16193#ifdef DEBUG1
16194void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin,
16195 const RegisterOrInt32& count,
16196 Register numActualArgs) {
16197 // |begin| must be positive or zero.
16198 if (begin.is<Register>()) {
16199 Label beginOk;
16200 masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0),
16201 &beginOk);
16202 masm.assumeUnreachable("begin < 0");
16203 masm.bind(&beginOk);
16204 } else {
16205 MOZ_ASSERT(begin.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(begin.as<int32_t>() >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(begin.as<int32_t>() >=
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("begin.as<int32_t>() >= 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 16205); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0"
")"); do { MOZ_CrashSequence(__null, 16205); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16206 }
16207
16208 // |count| must be positive or zero.
16209 if (count.is<Register>()) {
16210 Label countOk;
16211 masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0),
16212 &countOk);
16213 masm.assumeUnreachable("count < 0");
16214 masm.bind(&countOk);
16215 } else {
16216 MOZ_ASSERT(count.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(count.as<int32_t>() >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(count.as<int32_t>() >=
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("count.as<int32_t>() >= 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 16216); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0"
")"); do { MOZ_CrashSequence(__null, 16216); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16217 }
16218
16219 // |begin| must be less-or-equal to |numActualArgs|.
16220 Label argsBeginOk;
16221 if (begin.is<Register>()) {
16222 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
16223 &argsBeginOk);
16224 } else {
16225 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
16226 Imm32(begin.as<int32_t>()), &argsBeginOk);
16227 }
16228 masm.assumeUnreachable("begin <= numActualArgs");
16229 masm.bind(&argsBeginOk);
16230
16231 // |count| must be less-or-equal to |numActualArgs|.
16232 Label argsCountOk;
16233 if (count.is<Register>()) {
16234 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(),
16235 &argsCountOk);
16236 } else {
16237 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
16238 Imm32(count.as<int32_t>()), &argsCountOk);
16239 }
16240 masm.assumeUnreachable("count <= numActualArgs");
16241 masm.bind(&argsCountOk);
16242
16243 // |begin| and |count| must be preserved, but |numActualArgs| can be changed.
16244 //
16245 // Pre-condition: |count| <= |numActualArgs|
16246 // Condition to test: |begin + count| <= |numActualArgs|
16247 // Transform to: |begin| <= |numActualArgs - count|
16248 if (count.is<Register>()) {
16249 masm.subPtr(count.as<Register>(), numActualArgs);
16250 } else {
16251 masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs);
16252 }
16253
16254 // |begin + count| must be less-or-equal to |numActualArgs|.
16255 Label argsBeginCountOk;
16256 if (begin.is<Register>()) {
16257 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
16258 &argsBeginCountOk);
16259 } else {
16260 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
16261 Imm32(begin.as<int32_t>()), &argsBeginCountOk);
16262 }
16263 masm.assumeUnreachable("begin + count <= numActualArgs");
16264 masm.bind(&argsBeginCountOk);
16265}
16266#endif
16267
16268template <class ArgumentsSlice>
16269void CodeGenerator::emitNewArray(ArgumentsSlice* lir,
16270 const RegisterOrInt32& count, Register output,
16271 Register temp) {
16272 using Fn = ArrayObject* (*)(JSContext*, int32_t);
16273 auto* ool = count.match(
16274 [&](Register count) {
16275 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
16276 lir, ArgList(count), StoreRegisterTo(output));
16277 },
16278 [&](int32_t count) {
16279 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
16280 lir, ArgList(Imm32(count)), StoreRegisterTo(output));
16281 });
16282
16283 TemplateObject templateObject(lir->mir()->templateObj());
16284 MOZ_ASSERT(templateObject.isArrayObject())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(templateObject.isArrayObject())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(templateObject.isArrayObject
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("templateObject.isArrayObject()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 16284); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()"
")"); do { MOZ_CrashSequence(__null, 16284); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16285
16286 auto templateNativeObj = templateObject.asTemplateNativeObject();
16287 MOZ_ASSERT(templateNativeObj.getArrayLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(templateNativeObj.getArrayLength() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(templateNativeObj.getArrayLength() == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("templateNativeObj.getArrayLength() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16287); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0" ")"
); do { MOZ_CrashSequence(__null, 16287); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16288 MOZ_ASSERT(templateNativeObj.getDenseInitializedLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(templateNativeObj.getDenseInitializedLength() == 0)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(templateNativeObj.getDenseInitializedLength() == 0))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("templateNativeObj.getDenseInitializedLength() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16288); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0"
")"); do { MOZ_CrashSequence(__null, 16288); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16289 MOZ_ASSERT(!templateNativeObj.hasDynamicElements())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!templateNativeObj.hasDynamicElements())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!templateNativeObj.hasDynamicElements()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!templateNativeObj.hasDynamicElements()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16289); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()" ")"
); do { MOZ_CrashSequence(__null, 16289); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16290
16291 // Check array capacity. Call into the VM if the template object's capacity
16292 // is too small.
16293 bool tryAllocate = count.match(
16294 [&](Register count) {
16295 masm.branch32(Assembler::Above, count,
16296 Imm32(templateNativeObj.getDenseCapacity()),
16297 ool->entry());
16298 return true;
16299 },
16300 [&](int32_t count) {
16301 MOZ_ASSERT(count >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(count >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(count >= 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("count >= 0",
"./../../../../js/src/jit/CodeGenerator.cpp", 16301); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "count >= 0" ")"); do { MOZ_CrashSequence
(__null, 16301); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
16302 if (uint32_t(count) > templateNativeObj.getDenseCapacity()) {
16303 masm.jump(ool->entry());
16304 return false;
16305 }
16306 return true;
16307 });
16308
16309 if (tryAllocate) {
16310 // Try to allocate an object.
16311 masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(),
16312 ool->entry());
16313
16314 auto setInitializedLengthAndLength = [&](auto count) {
16315 const int elementsOffset = NativeObject::offsetOfFixedElements();
16316
16317 // Update initialized length.
16318 Address initLength(
16319 output, elementsOffset + ObjectElements::offsetOfInitializedLength());
16320 masm.store32(count, initLength);
16321
16322 // Update length.
16323 Address length(output, elementsOffset + ObjectElements::offsetOfLength());
16324 masm.store32(count, length);
16325 };
16326
16327 // The array object was successfully created. Set the length and initialized
16328 // length and then proceed to fill the elements.
16329 count.match([&](Register count) { setInitializedLengthAndLength(count); },
16330 [&](int32_t count) {
16331 if (count > 0) {
16332 setInitializedLengthAndLength(Imm32(count));
16333 }
16334 });
16335 }
16336
16337 masm.bind(ool->rejoin());
16338}
16339
16340void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) {
16341 Register begin = ToRegister(lir->begin());
16342 Register count = ToRegister(lir->count());
16343 Register temp = ToRegister(lir->temp0());
16344 Register output = ToRegister(lir->output());
16345
16346#ifdef DEBUG1
16347 masm.loadNumActualArgs(FramePointer, temp);
16348 emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count),
16349 temp);
16350#endif
16351
16352 emitNewArray(lir, RegisterOrInt32(count), output, temp);
16353
16354 Label done;
16355 masm.branch32(Assembler::Equal, count, Imm32(0), &done);
16356 {
16357 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
16358 allRegs.take(begin);
16359 allRegs.take(count);
16360 allRegs.take(temp);
16361 allRegs.take(output);
16362
16363 ValueOperand value = allRegs.takeAnyValue();
16364
16365 LiveRegisterSet liveRegs;
16366 liveRegs.add(output);
16367 liveRegs.add(begin);
16368 liveRegs.add(value);
16369
16370 masm.PushRegsInMask(liveRegs);
16371
16372 // Initialize all elements.
16373
16374 Register elements = output;
16375 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16376
16377 Register argIndex = begin;
16378
16379 Register index = temp;
16380 masm.move32(Imm32(0), index);
16381
16382 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16383 BaseValueIndex argPtr(FramePointer, argIndex, argvOffset);
16384
16385 Label loop;
16386 masm.bind(&loop);
16387
16388 masm.loadValue(argPtr, value);
16389
16390 // We don't need a pre-barrier, because the element at |index| is guaranteed
16391 // to be a non-GC thing (either uninitialized memory or the magic hole
16392 // value).
16393 masm.storeValue(value, BaseObjectElementIndex(elements, index));
16394
16395 masm.add32(Imm32(1), index);
16396 masm.add32(Imm32(1), argIndex);
16397
16398 masm.branch32(Assembler::LessThan, index, count, &loop);
16399
16400 masm.PopRegsInMask(liveRegs);
16401
16402 // Emit a post-write barrier if |output| is tenured.
16403 //
16404 // We expect that |output| is nursery allocated, so it isn't worth the
16405 // trouble to check if no frame argument is a nursery thing, which would
16406 // allow to omit the post-write barrier.
16407 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
16408
16409 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
16410 volatileRegs.takeUnchecked(temp);
16411 if (output.volatile_()) {
16412 volatileRegs.addUnchecked(output);
16413 }
16414
16415 masm.PushRegsInMask(volatileRegs);
16416 emitPostWriteBarrier(output);
16417 masm.PopRegsInMask(volatileRegs);
16418 }
16419 masm.bind(&done);
16420}
16421
16422CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32(
16423 const LAllocation* allocation) {
16424 if (allocation->isConstant()) {
16425 return RegisterOrInt32(allocation->toConstant()->toInt32());
16426 }
16427 return RegisterOrInt32(ToRegister(allocation));
16428}
16429
16430void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) {
16431 RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin());
16432 RegisterOrInt32 count = ToRegisterOrInt32(lir->count());
16433 Register temp = ToRegister(lir->temp());
16434 Register output = ToRegister(lir->output());
16435
16436 uint32_t numActuals = lir->mir()->numActuals();
16437
16438#ifdef DEBUG1
16439 masm.move32(Imm32(numActuals), temp);
16440
16441 emitAssertArgumentsSliceBounds(begin, count, temp);
16442#endif
16443
16444 emitNewArray(lir, count, output, temp);
16445
16446 // We're done if there are no actual arguments.
16447 if (numActuals == 0) {
16448 return;
16449 }
16450
16451 // Check if any arguments have to be copied.
16452 Label done;
16453 if (count.is<Register>()) {
16454 masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done);
16455 } else if (count.as<int32_t>() == 0) {
16456 return;
16457 }
16458
16459 auto getArg = [&](uint32_t i) {
16460 return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i),
16461 lir->mir()->getArg(i)->type());
16462 };
16463
16464 auto storeArg = [&](uint32_t i, auto dest) {
16465 // We don't need a pre-barrier because the element at |index| is guaranteed
16466 // to be a non-GC thing (either uninitialized memory or the magic hole
16467 // value).
16468 masm.storeConstantOrRegister(getArg(i), dest);
16469 };
16470
16471 // Initialize all elements.
16472 if (numActuals == 1) {
16473 // There's exactly one argument. We've checked that |count| is non-zero,
16474 // which implies that |begin| must be zero.
16475 MOZ_ASSERT_IF(begin.is<int32_t>(), begin.as<int32_t>() == 0)do { if (begin.is<int32_t>()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(begin.as<int32_t
>() == 0)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(begin.as<int32_t>() == 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("begin.as<int32_t>() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16475); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0" ")"); do {
MOZ_CrashSequence(__null, 16475); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
16476
16477 Register elements = temp;
16478 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16479
16480 storeArg(0, Address(elements, 0));
16481 } else if (begin.is<Register>()) {
16482 // There is more than one argument and |begin| isn't a compile-time
16483 // constant. Iterate through 0..numActuals to search for |begin| and then
16484 // start copying |count| arguments from that index.
16485
16486 LiveGeneralRegisterSet liveRegs;
16487 liveRegs.add(output);
16488 liveRegs.add(begin.as<Register>());
16489
16490 masm.PushRegsInMask(liveRegs);
16491
16492 Register elements = output;
16493 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16494
16495 Register argIndex = begin.as<Register>();
16496
16497 Register index = temp;
16498 masm.move32(Imm32(0), index);
16499
16500 Label doneLoop;
16501 for (uint32_t i = 0; i < numActuals; ++i) {
16502 Label next;
16503 masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next);
16504
16505 storeArg(i, BaseObjectElementIndex(elements, index));
16506
16507 masm.add32(Imm32(1), index);
16508 masm.add32(Imm32(1), argIndex);
16509
16510 if (count.is<Register>()) {
16511 masm.branch32(Assembler::GreaterThanOrEqual, index,
16512 count.as<Register>(), &doneLoop);
16513 } else {
16514 masm.branch32(Assembler::GreaterThanOrEqual, index,
16515 Imm32(count.as<int32_t>()), &doneLoop);
16516 }
16517
16518 masm.bind(&next);
16519 }
16520 masm.bind(&doneLoop);
16521
16522 masm.PopRegsInMask(liveRegs);
16523 } else {
16524 // There is more than one argument and |begin| is a compile-time constant.
16525
16526 Register elements = temp;
16527 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16528
16529 int32_t argIndex = begin.as<int32_t>();
16530
16531 int32_t index = 0;
16532
16533 Label doneLoop;
16534 for (uint32_t i = argIndex; i < numActuals; ++i) {
16535 storeArg(i, Address(elements, index * sizeof(Value)));
16536
16537 index += 1;
16538
16539 if (count.is<Register>()) {
16540 masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(),
16541 Imm32(index), &doneLoop);
16542 } else {
16543 if (index >= count.as<int32_t>()) {
16544 break;
16545 }
16546 }
16547 }
16548 masm.bind(&doneLoop);
16549 }
16550
16551 // Determine if we have to emit post-write barrier.
16552 //
16553 // If either |begin| or |count| is a constant, use their value directly.
16554 // Otherwise assume we copy all inline arguments from 0..numActuals.
16555 bool postWriteBarrier = false;
16556 uint32_t actualBegin = begin.match([](Register) { return 0; },
16557 [](int32_t value) { return value; });
16558 uint32_t actualCount =
16559 count.match([=](Register) { return numActuals; },
16560 [](int32_t value) -> uint32_t { return value; });
16561 for (uint32_t i = 0; i < actualCount; ++i) {
16562 ConstantOrRegister arg = getArg(actualBegin + i);
16563 if (arg.constant()) {
16564 Value v = arg.value();
16565 if (v.isGCThing() && IsInsideNursery(v.toGCThing())) {
16566 postWriteBarrier = true;
16567 }
16568 } else {
16569 MIRType type = arg.reg().type();
16570 if (type == MIRType::Value || NeedsPostBarrier(type)) {
16571 postWriteBarrier = true;
16572 }
16573 }
16574 }
16575
16576 // Emit a post-write barrier if |output| is tenured and we couldn't
16577 // determine at compile-time that no barrier is needed.
16578 if (postWriteBarrier) {
16579 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
16580
16581 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
16582 volatileRegs.takeUnchecked(temp);
16583 if (output.volatile_()) {
16584 volatileRegs.addUnchecked(output);
16585 }
16586
16587 masm.PushRegsInMask(volatileRegs);
16588 emitPostWriteBarrier(output);
16589 masm.PopRegsInMask(volatileRegs);
16590 }
16591
16592 masm.bind(&done);
16593}
16594
16595void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) {
16596 Register value = ToRegister(lir->value());
16597 Register length = ToRegister(lir->length());
16598 Register output = ToRegister(lir->output());
16599
16600 masm.move32(value, output);
16601
16602 Label positive;
16603 masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive);
16604
16605 Label done;
16606 masm.add32(length, output);
16607 masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done);
16608 masm.move32(Imm32(0), output);
16609 masm.jump(&done);
16610
16611 masm.bind(&positive);
16612 masm.cmp32Move32(Assembler::LessThan, length, value, length, output);
16613
16614 masm.bind(&done);
16615}
16616
16617void CodeGenerator::visitArrayJoin(LArrayJoin* lir) {
16618 Label skipCall;
16619
16620 Register output = ToRegister(lir->output());
16621 Register sep = ToRegister(lir->separator());
16622 Register array = ToRegister(lir->array());
16623 Register temp = ToRegister(lir->temp0());
16624
16625 // Fast path for simple length <= 1 cases.
16626 {
16627 masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp);
16628 Address length(temp, ObjectElements::offsetOfLength());
16629 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
16630
16631 // Check for length == 0
16632 Label notEmpty;
16633 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmpty);
16634 const JSAtomState& names = gen->runtime->names();
16635 masm.movePtr(ImmGCPtr(names.empty_), output);
16636 masm.jump(&skipCall);
16637
16638 masm.bind(&notEmpty);
16639 Label notSingleString;
16640 // Check for length == 1, initializedLength >= 1, arr[0].isString()
16641 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleString);
16642 masm.branch32(Assembler::LessThan, initLength, Imm32(1), &notSingleString);
16643
16644 Address elem0(temp, 0);
16645 masm.branchTestString(Assembler::NotEqual, elem0, &notSingleString);
16646
16647 // At this point, 'output' can be used as a scratch register, since we're
16648 // guaranteed to succeed.
16649 masm.unboxString(elem0, output);
16650 masm.jump(&skipCall);
16651 masm.bind(&notSingleString);
16652 }
16653
16654 pushArg(sep);
16655 pushArg(array);
16656
16657 using Fn = JSString* (*)(JSContext*, HandleObject, HandleString);
16658 callVM<Fn, jit::ArrayJoin>(lir);
16659 masm.bind(&skipCall);
16660}
16661
16662void CodeGenerator::visitObjectKeys(LObjectKeys* lir) {
16663 Register object = ToRegister(lir->object());
16664
16665 pushArg(object);
16666
16667 using Fn = JSObject* (*)(JSContext*, HandleObject);
16668 callVM<Fn, jit::ObjectKeys>(lir);
16669}
16670
16671void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) {
16672 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16673 TypedOrValueRegister val =
16674 toConstantOrRegister(lir, LGetIteratorCache::ValueIndex,
16675 lir->mir()->value()->type())
16676 .reg();
16677 Register output = ToRegister(lir->output());
16678 Register temp0 = ToRegister(lir->temp0());
16679 Register temp1 = ToRegister(lir->temp1());
16680
16681 IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1);
16682 addIC(lir, allocateIC(ic));
16683}
16684
16685void CodeGenerator::visitOptimizeSpreadCallCache(
16686 LOptimizeSpreadCallCache* lir) {
16687 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16688 ValueOperand val = ToValue(lir->value());
16689 ValueOperand output = ToOutValue(lir);
16690 Register temp = ToRegister(lir->temp0());
16691
16692 IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp);
16693 addIC(lir, allocateIC(ic));
16694}
16695
16696void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) {
16697 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16698 Register iter = ToRegister(lir->iter());
16699 Register temp = ToRegister(lir->temp0());
16700 CompletionKind kind = CompletionKind(lir->mir()->completionKind());
16701
16702 IonCloseIterIC ic(liveRegs, iter, temp, kind);
16703 addIC(lir, allocateIC(ic));
16704}
16705
16706void CodeGenerator::visitOptimizeGetIteratorCache(
16707 LOptimizeGetIteratorCache* lir) {
16708 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16709 ValueOperand val = ToValue(lir->value());
16710 Register output = ToRegister(lir->output());
16711 Register temp = ToRegister(lir->temp0());
16712
16713 IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp);
16714 addIC(lir, allocateIC(ic));
16715}
16716
16717void CodeGenerator::visitIteratorMore(LIteratorMore* lir) {
16718 const Register obj = ToRegister(lir->iterator());
16719 const ValueOperand output = ToOutValue(lir);
16720 const Register temp = ToRegister(lir->temp0());
16721
16722 masm.iteratorMore(obj, output, temp);
16723}
16724
16725void CodeGenerator::visitIteratorLength(LIteratorLength* lir) {
16726 Register obj = ToRegister(lir->iter());
16727 Register output = ToRegister(lir->output());
16728 masm.iteratorLength(obj, output);
16729}
16730
16731void CodeGenerator::visitLoadIteratorElement(LLoadIteratorElement* lir) {
16732 Register obj = ToRegister(lir->iter());
16733 Register output = ToRegister(lir->output());
16734 if (lir->index()->isConstant()) {
16735 int32_t index = ToInt32(lir->index());
16736 masm.iteratorLoadElement(obj, index, output);
16737 } else {
16738 Register index = ToRegister(lir->index());
16739 masm.iteratorLoadElement(obj, index, output);
16740 }
16741}
16742
16743void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) {
16744 ValueOperand input = ToValue(lir->input());
16745 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
16746 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
16747
16748 masm.branchTestMagicValue(Assembler::Equal, input, JS_NO_ITER_VALUE, ifTrue);
16749
16750 if (!isNextBlock(lir->ifFalse()->lir())) {
16751 masm.jump(ifFalse);
16752 }
16753}
16754
16755void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) {
16756 const Register obj = ToRegister(lir->iterator());
16757 const Register temp0 = ToRegister(lir->temp0());
16758 const Register temp1 = ToRegister(lir->temp1());
16759 const Register temp2 = ToRegister(lir->temp2());
16760
16761 masm.iteratorClose(obj, temp0, temp1, temp2);
16762}
16763
16764void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) {
16765 // read number of actual arguments from the JS frame.
16766 Register argc = ToRegister(lir->output());
16767 masm.loadNumActualArgs(FramePointer, argc);
16768}
16769
16770void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) {
16771 ValueOperand result = ToOutValue(lir);
16772 const LAllocation* index = lir->index();
16773 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16774
16775 // This instruction is used to access actual arguments and formal arguments.
16776 // The number of Values on the stack is |max(numFormals, numActuals)|, so we
16777 // assert |index < numFormals || index < numActuals| in debug builds.
16778 DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs();
16779
16780 if (index->isConstant()) {
16781 int32_t i = index->toConstant()->toInt32();
16782#ifdef DEBUG1
16783 if (uint32_t(i) >= numFormals) {
16784 Label ok;
16785 Register argc = result.scratchReg();
16786 masm.loadNumActualArgs(FramePointer, argc);
16787 masm.branch32(Assembler::Above, argc, Imm32(i), &ok);
16788 masm.assumeUnreachable("Invalid argument index");
16789 masm.bind(&ok);
16790 }
16791#endif
16792 Address argPtr(FramePointer, sizeof(Value) * i + argvOffset);
16793 masm.loadValue(argPtr, result);
16794 } else {
16795 Register i = ToRegister(index);
16796#ifdef DEBUG1
16797 Label ok;
16798 Register argc = result.scratchReg();
16799 masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok);
16800 masm.loadNumActualArgs(FramePointer, argc);
16801 masm.branch32(Assembler::Above, argc, i, &ok);
16802 masm.assumeUnreachable("Invalid argument index");
16803 masm.bind(&ok);
16804#endif
16805 BaseValueIndex argPtr(FramePointer, i, argvOffset);
16806 masm.loadValue(argPtr, result);
16807 }
16808}
16809
16810void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) {
16811 ValueOperand result = ToOutValue(lir);
16812 Register index = ToRegister(lir->index());
16813 Register length = ToRegister(lir->length());
16814 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0());
16815 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16816
16817 Label outOfBounds, done;
16818 masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds);
16819
16820 BaseValueIndex argPtr(FramePointer, index, argvOffset);
16821 masm.loadValue(argPtr, result);
16822 masm.jump(&done);
16823
16824 masm.bind(&outOfBounds);
16825 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
16826 masm.moveValue(UndefinedValue(), result);
16827
16828 masm.bind(&done);
16829}
16830
16831void CodeGenerator::visitRest(LRest* lir) {
16832 Register numActuals = ToRegister(lir->numActuals());
16833 Register temp0 = ToRegister(lir->temp0());
16834 Register temp1 = ToRegister(lir->temp1());
16835 Register temp2 = ToRegister(lir->temp2());
16836 Register temp3 = ToRegister(lir->temp3());
16837 unsigned numFormals = lir->mir()->numFormals();
16838
16839 // In baseline, DoRestFallback calls into NewArray to allocate the rest array.
16840 // If the length is 0, NewArray guesses a good capacity for it. We don't want
16841 // a smaller capacity in Ion, because that can lead to bailout loops.
16842 constexpr uint32_t arrayCapacity = 6;
16843 static_assert(GuessArrayGCKind(0) == GuessArrayGCKind(arrayCapacity));
16844
16845 if (Shape* shape = lir->mir()->shape()) {
16846 uint32_t arrayLength = 0;
16847 gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity);
16848 MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==
gc::FinalizeKind::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(&
ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16849); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 16849); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16849 gc::FinalizeKind::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==
gc::FinalizeKind::None)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(&
ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16849); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 16849); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16850 MOZ_ASSERT(!IsFinalizedKind(allocKind))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsFinalizedKind(allocKind))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsFinalizedKind(allocKind))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsFinalizedKind(allocKind)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16850); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFinalizedKind(allocKind)" ")"); do { MOZ_CrashSequence
(__null, 16850); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
16851 MOZ_ASSERT(GetGCKindSlots(allocKind) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements
::VALUES_PER_HEADER)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) ==
arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16852); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { MOZ_CrashSequence(__null, 16852); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16852 arrayCapacity + ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements
::VALUES_PER_HEADER)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) ==
arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
, "./../../../../js/src/jit/CodeGenerator.cpp", 16852); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { MOZ_CrashSequence(__null, 16852); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16853
16854 Label joinAlloc, failAlloc;
16855 masm.movePtr(ImmGCPtr(shape), temp0);
16856 masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg,
16857 arrayLength, arrayCapacity, 0, 0,
16858 allocKind, gc::Heap::Default, &failAlloc);
16859 masm.jump(&joinAlloc);
16860 {
16861 masm.bind(&failAlloc);
16862 masm.movePtr(ImmPtr(nullptr), temp2);
16863 }
16864 masm.bind(&joinAlloc);
16865 } else {
16866 masm.movePtr(ImmPtr(nullptr), temp2);
16867 }
16868
16869 // Set temp1 to the address of the first actual argument.
16870 size_t actualsOffset = JitFrameLayout::offsetOfActualArgs();
16871 masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1);
16872
16873 // Compute array length: max(numActuals - numFormals, 0).
16874 Register lengthReg;
16875 if (numFormals) {
16876 lengthReg = temp0;
16877 Label emptyLength, joinLength;
16878 masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals),
16879 &emptyLength);
16880 {
16881 masm.move32(numActuals, lengthReg);
16882 masm.sub32(Imm32(numFormals), lengthReg);
16883
16884 // Skip formal arguments.
16885 masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1);
16886
16887 masm.jump(&joinLength);
16888 }
16889 masm.bind(&emptyLength);
16890 {
16891 masm.move32(Imm32(0), lengthReg);
16892
16893 // Leave temp1 pointed to the start of actuals() when the rest-array
16894 // length is zero. We don't use |actuals() + numFormals| because
16895 // |numFormals| can be any non-negative int32 value when this MRest was
16896 // created from scalar replacement optimizations. And it seems
16897 // questionable to compute a Value* pointer which points to who knows
16898 // where.
16899 }
16900 masm.bind(&joinLength);
16901 } else {
16902 // Use numActuals directly when there are no formals.
16903 lengthReg = numActuals;
16904 }
16905
16906 // Try to initialize the array elements.
16907 Label vmCall, done;
16908 if (lir->mir()->shape()) {
16909 // Call into C++ if we failed to allocate an array or there are more than
16910 // |arrayCapacity| elements.
16911 masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall);
16912 masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall);
16913
16914 // The array must be nursery allocated so no post barrier is needed.
16915#ifdef DEBUG1
16916 Label ok;
16917 masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok);
16918 masm.assumeUnreachable("Unexpected tenured object for LRest");
16919 masm.bind(&ok);
16920#endif
16921
16922 Label nonZeroLength;
16923 masm.branch32(Assembler::NotEqual, lengthReg, Imm32(0), &nonZeroLength);
16924 masm.movePtr(temp2, ReturnReg);
16925 masm.jump(&done);
16926 masm.bind(&nonZeroLength);
16927
16928 // Store length and initializedLength.
16929 Register elements = temp3;
16930 masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements);
16931 Address lengthAddr(elements, ObjectElements::offsetOfLength());
16932 Address initLengthAddr(elements,
16933 ObjectElements::offsetOfInitializedLength());
16934 masm.store32(lengthReg, lengthAddr);
16935 masm.store32(lengthReg, initLengthAddr);
16936
16937 masm.push(temp2); // Spill result to free up register.
16938
16939 Register end = temp0;
16940 Register args = temp1;
16941 Register scratch = temp2;
16942 masm.computeEffectiveAddress(BaseObjectElementIndex(elements, lengthReg),
16943 end);
16944
16945 Label loop;
16946 masm.bind(&loop);
16947 masm.storeValue(Address(args, 0), Address(elements, 0), scratch);
16948 masm.addPtr(Imm32(sizeof(Value)), args);
16949 masm.addPtr(Imm32(sizeof(Value)), elements);
16950 masm.branchPtr(Assembler::Below, elements, end, &loop);
16951
16952 // Pop result
16953 masm.pop(ReturnReg);
16954 masm.jump(&done);
16955 }
16956
16957 masm.bind(&vmCall);
16958
16959 pushArg(temp2);
16960 pushArg(temp1);
16961 pushArg(lengthReg);
16962
16963 using Fn =
16964 ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>);
16965 callVM<Fn, InitRestParameter>(lir);
16966
16967 masm.bind(&done);
16968}
16969
16970// Create a stackmap from the given safepoint, with the structure:
16971//
16972// <reg dump, if any>
16973// | ++ <body (general spill)>
16974// | | ++ <space for Frame>
16975// | | ++ <inbound args>
16976// | | |
16977// Lowest Addr Highest Addr
16978// |
16979// framePushedAtStackMapBase
16980//
16981// The caller owns the resulting stackmap. This assumes a grow-down stack.
16982//
16983// For non-debug builds, if the stackmap would contain no pointers, no
16984// stackmap is created, and nullptr is returned. For a debug build, a
16985// stackmap is always created and returned.
16986//
16987// Depending on the type of safepoint, the stackmap may need to account for
16988// spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where
16989// isCall() == true, for which the register allocator will spill/restore all
16990// live registers at the LIR level - in this case, the LSafepoint sees only live
16991// values on the stack, never in registers. WasmSafepointKind::CodegenCall, on
16992// the other hand, is for LIR nodes which may manually spill/restore live
16993// registers in codegen, in which case the stackmap must account for this. Traps
16994// also require tracking of live registers, but spilling is handled by the trap
16995// mechanism.
16996static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint,
16997 const RegisterOffsets& trapExitLayout,
16998 size_t trapExitLayoutNumWords,
16999 size_t nInboundStackArgBytes,
17000 wasm::StackMaps& stackMaps,
17001 wasm::StackMap** result) {
17002 // Ensure this is defined on all return paths.
17003 *result = nullptr;
17004
17005 // The size of the wasm::Frame itself.
17006 const size_t nFrameBytes = sizeof(wasm::Frame);
17007
17008 // This is the number of bytes spilled for live registers, outside of a trap.
17009 // For traps, trapExitLayout and trapExitLayoutNumWords will be used.
17010 const size_t nRegisterDumpBytes =
17011 MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs());
17012
17013 // As mentioned above, for WasmSafepointKind::LirCall, register spills and
17014 // restores are handled at the LIR level and there should therefore be no live
17015 // registers to handle here.
17016 MOZ_ASSERT_IF(safepoint.wasmSafepointKind() == WasmSafepointKind::LirCall,do { if (safepoint.wasmSafepointKind() == WasmSafepointKind::
LirCall) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17017); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { MOZ_CrashSequence
(__null, 17017); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
17017 nRegisterDumpBytes == 0)do { if (safepoint.wasmSafepointKind() == WasmSafepointKind::
LirCall) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17017); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { MOZ_CrashSequence
(__null, 17017); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
17018 MOZ_ASSERT(nRegisterDumpBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nRegisterDumpBytes % sizeof(void*) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(nRegisterDumpBytes % sizeof(void*) == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("nRegisterDumpBytes % sizeof(void*) == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17018); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0" ")"
); do { MOZ_CrashSequence(__null, 17018); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17019
17020 // This is the number of bytes in the general spill area, below the Frame.
17021 const size_t nBodyBytes = safepoint.framePushedAtStackMapBase();
17022
17023 // The stack map owns any alignment padding around inbound stack args.
17024 const size_t nInboundStackArgBytesAligned =
17025 wasm::AlignStackArgAreaSize(nInboundStackArgBytes);
17026
17027 // This is the number of bytes in the general spill area, the Frame, and the
17028 // incoming args, but not including any register dump area.
17029 const size_t nNonRegisterBytes =
17030 nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned;
17031 MOZ_ASSERT(nNonRegisterBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nNonRegisterBytes % sizeof(void*) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(nNonRegisterBytes % sizeof(void*) == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("nNonRegisterBytes % sizeof(void*) == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17031); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0" ")"
); do { MOZ_CrashSequence(__null, 17031); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17032
17033 // This is the number of bytes in the register dump area, if any, below the
17034 // general spill area.
17035 const size_t nRegisterBytes =
17036 (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap)
17037 ? (trapExitLayoutNumWords * sizeof(void*))
17038 : nRegisterDumpBytes;
17039
17040 // This is the total number of bytes covered by the map.
17041 const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes;
17042
17043 // This stackmap/safepoint is for a wasm frame, so there should be no
17044 // slotsOrElements-style roots.
17045 MOZ_RELEASE_ASSERT(safepoint.slotsOrElementsSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepoint.slotsOrElementsSlots().empty())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(safepoint.slotsOrElementsSlots().empty()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("safepoint.slotsOrElementsSlots().empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17045); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "safepoint.slotsOrElementsSlots().empty()"
")"); do { MOZ_CrashSequence(__null, 17045); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17046 MOZ_RELEASE_ASSERT(safepoint.slotsOrElementsRegs().empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepoint.slotsOrElementsRegs().empty())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(safepoint.slotsOrElementsRegs().empty()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("safepoint.slotsOrElementsRegs().empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17046); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "safepoint.slotsOrElementsRegs().empty()"
")"); do { MOZ_CrashSequence(__null, 17046); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17047
17048#ifndef DEBUG1
17049 bool needStackMap = !safepoint.wasmAnyRefRegs().empty() ||
17050 !safepoint.wasmAnyRefSlots().empty() ||
17051 !safepoint.wasmStructDataRegs().empty() ||
17052 !safepoint.wasmStructDataSlots().empty() ||
17053 !safepoint.wasmArrayDataRegs().empty() ||
17054 !safepoint.wasmArrayDataSlots().empty();
17055 // There are no references, and this is a non-debug build, so don't bother
17056 // building the stackmap.
17057 if (!needStackMap) {
17058 return true;
17059 }
17060#endif
17061
17062 wasm::StackMap* stackMap = stackMaps.create(nTotalBytes / sizeof(void*));
17063 if (!stackMap) {
17064 return false;
17065 }
17066 if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) {
17067 stackMap->setExitStubWords(trapExitLayoutNumWords);
17068 }
17069
17070 // REG DUMP AREA, if any.
17071 size_t regDumpWords = 0;
17072 const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs();
17073 const LiveGeneralRegisterSet wasmStructDataRegs =
17074 safepoint.wasmStructDataRegs();
17075 const LiveGeneralRegisterSet wasmArrayDataRegs =
17076 safepoint.wasmArrayDataRegs();
17077
17078 // These three sets should be disjoint.
17079 MOZ_ASSERT(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs
.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17081); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17081); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17080 wasmStructDataRegs.set())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs
.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17081); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17081); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17081 .empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs
.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17081); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17081); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17082 MOZ_ASSERT(GeneralRegisterSet::Intersect(wasmStructDataRegs.set(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmStructDataRegs.set
(), wasmArrayDataRegs.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17084); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17084); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17083 wasmArrayDataRegs.set())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmStructDataRegs.set
(), wasmArrayDataRegs.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17084); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17084); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17084 .empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmStructDataRegs.set
(), wasmArrayDataRegs.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17084); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17084); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17085 MOZ_ASSERT(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(
), wasmAnyRefRegs.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17087); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17087); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17086 wasmAnyRefRegs.set())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(
), wasmAnyRefRegs.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17087); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17087); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17087 .empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(
), wasmAnyRefRegs.set()) .empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect
(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17087); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17087); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17088 const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union(
17089 wasmAnyRefRegs.set(),
17090 GeneralRegisterSet::Union(wasmStructDataRegs.set(),
17091 wasmArrayDataRegs.set())));
17092
17093 GeneralRegisterForwardIterator refRegsIter(refRegs);
17094 switch (safepoint.wasmSafepointKind()) {
17095 case WasmSafepointKind::LirCall:
17096 case WasmSafepointKind::StackSwitch:
17097 case WasmSafepointKind::CodegenCall: {
17098 size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*);
17099 regDumpWords += spilledNumWords;
17100
17101 for (; refRegsIter.more(); ++refRegsIter) {
17102 Register reg = *refRegsIter;
17103 size_t offsetFromSpillBase =
17104 safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) /
17105 sizeof(void*);
17106 MOZ_ASSERT(0 < offsetFromSpillBase &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0 < offsetFromSpillBase && offsetFromSpillBase
<= spilledNumWords)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase &&
offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17107); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { MOZ_CrashSequence(__null, 17107); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17107 offsetFromSpillBase <= spilledNumWords)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0 < offsetFromSpillBase && offsetFromSpillBase
<= spilledNumWords)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase &&
offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17107); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { MOZ_CrashSequence(__null, 17107); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17108 size_t index = spilledNumWords - offsetFromSpillBase;
17109
17110 if (wasmAnyRefRegs.has(reg)) {
17111 stackMap->set(index, wasm::StackMap::AnyRef);
17112 } else if (wasmStructDataRegs.has(reg)) {
17113 stackMap->set(index, wasm::StackMap::StructDataPointer);
17114 } else {
17115 MOZ_ASSERT(wasmArrayDataRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasmArrayDataRegs.has(reg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wasmArrayDataRegs.has(reg)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasmArrayDataRegs.has(reg)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17115); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasmArrayDataRegs.has(reg)" ")"); do { MOZ_CrashSequence
(__null, 17115); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17116 stackMap->set(index, wasm::StackMap::ArrayDataPointer);
17117 }
17118 }
17119 // Float and vector registers do not have to be handled; they cannot
17120 // contain wasm anyrefs, and they are spilled after general-purpose
17121 // registers. Gprs are therefore closest to the spill base and thus their
17122 // offset calculation does not need to account for other spills.
17123 } break;
17124 case WasmSafepointKind::Trap: {
17125 regDumpWords += trapExitLayoutNumWords;
17126
17127 for (; refRegsIter.more(); ++refRegsIter) {
17128 Register reg = *refRegsIter;
17129 size_t offsetFromTop = trapExitLayout.getOffset(reg);
17130
17131 // If this doesn't hold, the associated register wasn't saved by
17132 // the trap exit stub. Better to crash now than much later, in
17133 // some obscure place, and possibly with security consequences.
17134 MOZ_RELEASE_ASSERT(offsetFromTop < trapExitLayoutNumWords)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(offsetFromTop < trapExitLayoutNumWords)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(offsetFromTop < trapExitLayoutNumWords))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("offsetFromTop < trapExitLayoutNumWords"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17134); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords"
")"); do { MOZ_CrashSequence(__null, 17134); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17135
17136 // offsetFromTop is an offset in words down from the highest
17137 // address in the exit stub save area. Switch it around to be an
17138 // offset up from the bottom of the (integer register) save area.
17139 size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop;
17140
17141 if (wasmAnyRefRegs.has(reg)) {
17142 stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef);
17143 } else if (wasmStructDataRegs.has(reg)) {
17144 stackMap->set(offsetFromBottom, wasm::StackMap::StructDataPointer);
17145 } else {
17146 MOZ_ASSERT(wasmArrayDataRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasmArrayDataRegs.has(reg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wasmArrayDataRegs.has(reg)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasmArrayDataRegs.has(reg)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17146); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasmArrayDataRegs.has(reg)" ")"); do { MOZ_CrashSequence
(__null, 17146); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17147 stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer);
17148 }
17149 }
17150 } break;
17151 default:
17152 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17152); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { MOZ_CrashSequence(__null, 17152); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
17153 }
17154
17155 // Ensure other reg/slot collections on LSafepoint are empty.
17156 MOZ_ASSERT(safepoint.gcRegs().empty() && safepoint.gcSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepoint.gcRegs().empty() && safepoint.gcSlots
().empty())>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(safepoint.gcRegs().empty() &&
safepoint.gcSlots().empty()))), 0))) { do { } while (false);
MOZ_ReportAssertionFailure("safepoint.gcRegs().empty() && safepoint.gcSlots().empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17156); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()"
")"); do { MOZ_CrashSequence(__null, 17156); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17157#ifdef JS_NUNBOX32
17158 MOZ_ASSERT(safepoint.nunboxParts().empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepoint.nunboxParts().empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(safepoint.nunboxParts().empty
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("safepoint.nunboxParts().empty()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()"
")"); do { MOZ_CrashSequence(__null, 17158); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17159#elif JS_PUNBOX641
17160 MOZ_ASSERT(safepoint.valueRegs().empty() && safepoint.valueSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepoint.valueRegs().empty() && safepoint.valueSlots
().empty())>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(safepoint.valueRegs().empty() &&
safepoint.valueSlots().empty()))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("safepoint.valueRegs().empty() && safepoint.valueSlots().empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17160); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()"
")"); do { MOZ_CrashSequence(__null, 17160); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17161#endif
17162
17163 // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS
17164 // Deal with roots on the stack.
17165 const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots();
17166 for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) {
17167 // The following needs to correspond with JitFrameLayout::slotRef
17168 // wasmAnyRefSlot.stack == 0 means the slot is in the args area
17169 if (wasmAnyRefSlot.stack) {
17170 // It's a slot in the body allocation, so .slot is interpreted
17171 // as an index downwards from the Frame*
17172 MOZ_ASSERT(wasmAnyRefSlot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasmAnyRefSlot.slot <= nBodyBytes)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wasmAnyRefSlot.slot <= nBodyBytes
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"wasmAnyRefSlot.slot <= nBodyBytes", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17172); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes"
")"); do { MOZ_CrashSequence(__null, 17172); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17173 uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot;
17174 MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void*
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("offsetInBytes % sizeof(void*) == 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17174); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17175 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17176 wasm::StackMap::AnyRef);
17177 } else {
17178 // It's an argument slot
17179 MOZ_ASSERT(wasmAnyRefSlot.slot < nInboundStackArgBytes)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasmAnyRefSlot.slot < nInboundStackArgBytes)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(wasmAnyRefSlot.slot < nInboundStackArgBytes))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("wasmAnyRefSlot.slot < nInboundStackArgBytes"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17179); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes"
")"); do { MOZ_CrashSequence(__null, 17179); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17180 uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot;
17181 MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void*
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("offsetInBytes % sizeof(void*) == 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17181); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17182 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17183 wasm::StackMap::AnyRef);
17184 }
17185 }
17186
17187 // Track struct data pointers on the stack
17188 for (SafepointSlotEntry slot : safepoint.wasmStructDataSlots()) {
17189 MOZ_ASSERT(slot.stack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slot.stack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slot.stack))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("slot.stack", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17189); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack"
")"); do { MOZ_CrashSequence(__null, 17189); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17190 // It's a slot in the body allocation, so .slot is interpreted
17191 // as an index downwards from the Frame*
17192 MOZ_ASSERT(slot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slot.slot <= nBodyBytes)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slot.slot <= nBodyBytes))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.slot <= nBodyBytes"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17192); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { MOZ_CrashSequence
(__null, 17192); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17193 uint32_t offsetInBytes = nBodyBytes - slot.slot;
17194 MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void*
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("offsetInBytes % sizeof(void*) == 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17194); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17194); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17195 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17196 wasm::StackMap::Kind::StructDataPointer);
17197 }
17198
17199 // Track array data pointers on the stack
17200 for (SafepointSlotEntry slot : safepoint.wasmArrayDataSlots()) {
17201 MOZ_ASSERT(slot.stack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slot.stack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slot.stack))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("slot.stack", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17201); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack"
")"); do { MOZ_CrashSequence(__null, 17201); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17202 // It's a slot in the body allocation, so .slot is interpreted
17203 // as an index downwards from the Frame*
17204 MOZ_ASSERT(slot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slot.slot <= nBodyBytes)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slot.slot <= nBodyBytes))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.slot <= nBodyBytes"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17204); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { MOZ_CrashSequence
(__null, 17204); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17205 uint32_t offsetInBytes = nBodyBytes - slot.slot;
17206 MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void*
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("offsetInBytes % sizeof(void*) == 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17206); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17206); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17207 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17208 wasm::StackMap::Kind::ArrayDataPointer);
17209 }
17210
17211 // Record in the map, how far down from the highest address the Frame* is.
17212 // Take the opportunity to check that we haven't marked any part of the
17213 // Frame itself as a pointer.
17214 stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) /
17215 sizeof(void*));
17216#ifdef DEBUG1
17217 for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) {
17218 MOZ_ASSERT(stackMap->get(stackMap->header.numMappedWords -do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stackMap->get(stackMap->header.numMappedWords -
stackMap->header.frameOffsetFromTop + i) == wasm::StackMap
::Kind::POD)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header
.numMappedWords - stackMap->header.frameOffsetFromTop + i)
== wasm::StackMap::Kind::POD))), 0))) { do { } while (false)
; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17220); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { MOZ_CrashSequence(__null, 17220); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17219 stackMap->header.frameOffsetFromTop + i) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stackMap->get(stackMap->header.numMappedWords -
stackMap->header.frameOffsetFromTop + i) == wasm::StackMap
::Kind::POD)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header
.numMappedWords - stackMap->header.frameOffsetFromTop + i)
== wasm::StackMap::Kind::POD))), 0))) { do { } while (false)
; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17220); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { MOZ_CrashSequence(__null, 17220); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17220 wasm::StackMap::Kind::POD)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stackMap->get(stackMap->header.numMappedWords -
stackMap->header.frameOffsetFromTop + i) == wasm::StackMap
::Kind::POD)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header
.numMappedWords - stackMap->header.frameOffsetFromTop + i)
== wasm::StackMap::Kind::POD))), 0))) { do { } while (false)
; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17220); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { MOZ_CrashSequence(__null, 17220); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17221 }
17222#endif
17223
17224 *result = stackMap;
17225 return true;
17226}
17227
17228bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId,
17229 const wasm::TrapSiteDesc& entryTrapSiteDesc,
17230 const wasm::ArgTypeVector& argTypes,
17231 const RegisterOffsets& trapExitLayout,
17232 size_t trapExitLayoutNumWords,
17233 wasm::FuncOffsets* offsets,
17234 wasm::StackMaps* stackMaps,
17235 wasm::Decoder* decoder) {
17236 AutoCreatedBy acb(masm, "CodeGenerator::generateWasm");
17237
17238 JitSpew(JitSpew_Codegen, "# Emitting wasm code");
17239
17240 size_t nInboundStackArgBytes =
17241 StackArgAreaSizeUnaligned(argTypes, ABIKind::Wasm);
17242 inboundStackArgBytes_ = nInboundStackArgBytes;
17243
17244 perfSpewer().markStartOffset(masm.currentOffset());
17245 perfSpewer().recordOffset(masm, "Prologue");
17246 wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(),
17247 offsets);
17248
17249#ifdef DEBUG1
17250 // If we are doing full debug checks, always load the instance pointer into
17251 // the usual spot in the frame so that it can be loaded later regardless of
17252 // what is in InstanceReg. See CodeGenerator::emitDebugResultChecks.
17253 if (JitOptions.fullDebugChecks) {
17254 masm.storePtr(InstanceReg,
17255 Address(FramePointer,
17256 wasm::FrameWithInstances::calleeInstanceOffset()));
17257 }
17258#endif
17259
17260 MOZ_ASSERT(masm.framePushed() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("masm.framePushed() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17260); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { MOZ_CrashSequence
(__null, 17260); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17261
17262 // Very large frames are implausible, probably an attack.
17263 if (frameSize() > wasm::MaxFrameSize) {
17264 return decoder->fail(decoder->beginOffset(), "stack frame is too large");
17265 }
17266
17267 if (omitOverRecursedStackCheck()) {
17268 masm.reserveStack(frameSize());
17269
17270 // If we don't need to check the stack for recursion, we definitely don't
17271 // need to check for interrupts.
17272 MOZ_ASSERT(omitOverRecursedInterruptCheck())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(omitOverRecursedInterruptCheck())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(omitOverRecursedInterruptCheck
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("omitOverRecursedInterruptCheck()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "omitOverRecursedInterruptCheck()"
")"); do { MOZ_CrashSequence(__null, 17272); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17273 } else {
17274 auto* ool = new (alloc())
17275 LambdaOutOfLineCode([this, entryTrapSiteDesc](OutOfLineCode& ool) {
17276 masm.wasmTrap(wasm::Trap::StackOverflow, entryTrapSiteDesc);
17277 });
17278 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
17279 masm.wasmReserveStackChecked(frameSize(), ool->entry());
17280
17281 if (!omitOverRecursedInterruptCheck()) {
17282 wasm::StackMap* functionEntryStackMap = nullptr;
17283 if (!CreateStackMapForFunctionEntryTrap(
17284 argTypes, trapExitLayout, trapExitLayoutNumWords, frameSize(),
17285 nInboundStackArgBytes, *stackMaps, &functionEntryStackMap)) {
17286 return false;
17287 }
17288
17289 // In debug builds, we'll always have a stack map, even if there are no
17290 // refs to track.
17291 MOZ_ASSERT(functionEntryStackMap)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(functionEntryStackMap)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(functionEntryStackMap))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("functionEntryStackMap"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17291); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "functionEntryStackMap" ")"); do { MOZ_CrashSequence
(__null, 17291); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17292
17293 auto* ool = new (alloc()) LambdaOutOfLineCode(
17294 [this, stackMaps, functionEntryStackMap](OutOfLineCode& ool) {
17295 masm.wasmTrap(wasm::Trap::CheckInterrupt, wasm::TrapSiteDesc());
17296 CodeOffset trapInsnOffset = CodeOffset(masm.currentOffset());
17297
17298 if (functionEntryStackMap &&
17299 !stackMaps->add(trapInsnOffset.offset(),
17300 functionEntryStackMap)) {
17301 masm.setOOM();
17302 }
17303 masm.jump(ool.rejoin());
17304 });
17305
17306 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
17307 masm.branch32(Assembler::NotEqual,
17308 Address(InstanceReg, wasm::Instance::offsetOfInterrupt()),
17309 Imm32(0), ool->entry());
17310 masm.bind(ool->rejoin());
17311 }
17312 }
17313
17314 MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("masm.framePushed() == frameSize()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17314); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 17314); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17315
17316 if (!generateBody()) {
17317 return false;
17318 }
17319
17320 perfSpewer().recordOffset(masm, "Epilogue");
17321 masm.bind(&returnLabel_);
17322 wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets);
17323
17324 perfSpewer().recordOffset(masm, "OOLBlocks");
17325 // This must come before we generate OOL code, as OOL blocks may
17326 // generate OOL code.
17327 if (!generateOutOfLineBlocks()) {
17328 return false;
17329 }
17330
17331 perfSpewer().recordOffset(masm, "OOLCode");
17332 if (!generateOutOfLineCode()) {
17333 return false;
17334 }
17335
17336 masm.flush();
17337 if (masm.oom()) {
17338 return false;
17339 }
17340
17341 offsets->end = masm.currentOffset();
17342
17343 MOZ_ASSERT(!masm.failureLabel()->used())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!masm.failureLabel()->used())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!masm.failureLabel()->used
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!masm.failureLabel()->used()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17343); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()"
")"); do { MOZ_CrashSequence(__null, 17343); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17344 MOZ_ASSERT(snapshots_.listSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(snapshots_.listSize() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(snapshots_.listSize() == 0))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("snapshots_.listSize() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17344); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "snapshots_.listSize() == 0" ")"); do { MOZ_CrashSequence
(__null, 17344); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17345 MOZ_ASSERT(snapshots_.RVATableSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(snapshots_.RVATableSize() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(snapshots_.RVATableSize() ==
0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("snapshots_.RVATableSize() == 0", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17345); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0"
")"); do { MOZ_CrashSequence(__null, 17345); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17346 MOZ_ASSERT(recovers_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(recovers_.size() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(recovers_.size() == 0))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("recovers_.size() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17346); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "recovers_.size() == 0" ")"); do { MOZ_CrashSequence
(__null, 17346); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17347 MOZ_ASSERT(graph.numConstants() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(graph.numConstants() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(graph.numConstants() == 0)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("graph.numConstants() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17347); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "graph.numConstants() == 0" ")"); do { MOZ_CrashSequence
(__null, 17347); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17348 MOZ_ASSERT(osiIndices_.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(osiIndices_.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(osiIndices_.empty()))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17348); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "osiIndices_.empty()" ")"); do { MOZ_CrashSequence
(__null, 17348); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17349 MOZ_ASSERT(icList_.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(icList_.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(icList_.empty()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("icList_.empty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17349); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "icList_.empty()" ")"); do { MOZ_CrashSequence
(__null, 17349); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17350 MOZ_ASSERT(safepoints_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepoints_.size() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(safepoints_.size() == 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("safepoints_.size() == 0"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17350); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "safepoints_.size() == 0" ")"); do { MOZ_CrashSequence
(__null, 17350); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17351 MOZ_ASSERT(!scriptCounts_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!scriptCounts_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!scriptCounts_))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!scriptCounts_"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17351); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!scriptCounts_" ")"); do { MOZ_CrashSequence
(__null, 17351); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17352
17353 // Convert the safepoints to stackmaps and add them to our running
17354 // collection thereof.
17355 for (CodegenSafepointIndex& index : safepointIndices_) {
17356 wasm::StackMap* stackMap = nullptr;
17357 if (!CreateStackMapFromLSafepoint(
17358 *index.safepoint(), trapExitLayout, trapExitLayoutNumWords,
17359 nInboundStackArgBytes, *stackMaps, &stackMap)) {
17360 return false;
17361 }
17362
17363 // In debug builds, we'll always have a stack map.
17364 MOZ_ASSERT(stackMap)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stackMap)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stackMap))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("stackMap", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17364); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")"
); do { MOZ_CrashSequence(__null, 17364); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17365 if (!stackMap) {
17366 continue;
17367 }
17368
17369 if (!stackMaps->finalize(index.displacement(), stackMap)) {
17370 return false;
17371 }
17372 }
17373
17374 return true;
17375}
17376
17377bool CodeGenerator::generate(const WarpSnapshot* snapshot) {
17378 AutoCreatedBy acb(masm, "CodeGenerator::generate");
17379
17380 MOZ_ASSERT(snapshot)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(snapshot)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(snapshot))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("snapshot", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17380); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshot" ")"
); do { MOZ_CrashSequence(__null, 17380); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17381 snapshot_ = snapshot;
17382
17383 JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u",
17384 gen->outerInfo().script()->filename(),
17385 gen->outerInfo().script()->lineno(),
17386 gen->outerInfo().script()->column().oneOriginValue());
17387
17388 // Initialize native code table with an entry to the start of
17389 // top-level script.
17390 InlineScriptTree* tree = gen->outerInfo().inlineScriptTree();
17391 jsbytecode* startPC = tree->script()->code();
17392 BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC);
17393 if (!addNativeToBytecodeEntry(startSite)) {
17394 return false;
17395 }
17396
17397 if (!safepoints_.init(gen->alloc())) {
17398 return false;
17399 }
17400
17401 size_t maxSafepointIndices =
17402 graph.numSafepoints() + graph.extraSafepointUses();
17403 if (!safepointIndices_.reserve(maxSafepointIndices)) {
17404 return false;
17405 }
17406 if (!osiIndices_.reserve(graph.numSafepoints())) {
17407 return false;
17408 }
17409
17410 perfSpewer().recordOffset(masm, "Prologue");
17411 if (!generatePrologue()) {
17412 return false;
17413 }
17414
17415 // Reset native => bytecode map table with top-level script and startPc.
17416 if (!addNativeToBytecodeEntry(startSite)) {
17417 return false;
17418 }
17419
17420 if (!generateBody()) {
17421 return false;
17422 }
17423
17424 // Reset native => bytecode map table with top-level script and startPc.
17425 if (!addNativeToBytecodeEntry(startSite)) {
17426 return false;
17427 }
17428
17429 perfSpewer().recordOffset(masm, "Epilogue");
17430 if (!generateEpilogue()) {
17431 return false;
17432 }
17433
17434 // Reset native => bytecode map table with top-level script and startPc.
17435 if (!addNativeToBytecodeEntry(startSite)) {
17436 return false;
17437 }
17438
17439 perfSpewer().recordOffset(masm, "InvalidateEpilogue");
17440 generateInvalidateEpilogue();
17441
17442 perfSpewer().recordOffset(masm, "OOLBlocks");
17443 // This must come before we generate OOL code, as OOL blocks may
17444 // generate OOL code.
17445 if (!generateOutOfLineBlocks()) {
17446 return false;
17447 }
17448
17449 // native => bytecode entries for OOL code will be added
17450 // by CodeGeneratorShared::generateOutOfLineCode
17451 perfSpewer().recordOffset(masm, "OOLCode");
17452 if (!generateOutOfLineCode()) {
17453 return false;
17454 }
17455
17456 // Add terminal entry.
17457 if (!addNativeToBytecodeEntry(startSite)) {
17458 return false;
17459 }
17460
17461 // Dump Native to bytecode entries to spew.
17462 dumpNativeToBytecodeEntries();
17463
17464 // We encode safepoints after the OSI-point offsets have been determined.
17465 if (!encodeSafepoints()) {
17466 return false;
17467 }
17468
17469 // If this assertion trips, then you have multiple things to do:
17470 //
17471 // This assertion will report if a safepoint is used multiple times for the
17472 // same instruction. To fix this assertion make sure to call
17473 // `lirGraph_.addExtraSafepointUses(..);` in the Lowering phase.
17474 //
17475 // However, this non-worrying issue might hide a more dramatic security issue,
17476 // which is that having multiple encoding of a safepoint in a single LIR
17477 // instruction is not safe, unless:
17478 //
17479 // - The multiple uses of the safepoints are in different code path. i-e
17480 // there should be not single execution trace making use of multiple
17481 // calls within a single instruction.
17482 //
17483 // - There is enough space to encode data in-place of the call instruction.
17484 // Such that a patched-call site does not corrupt the code path on another
17485 // execution trace.
17486 //
17487 // This issue is caused by the way invalidation works, to keep the code alive
17488 // when invalidated code is only referenced by the stack. This works by
17489 // storing data in-place of the calling code, which thus becomes unsafe to
17490 // execute.
17491 MOZ_ASSERT(safepointIndices_.length() <= maxSafepointIndices)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepointIndices_.length() <= maxSafepointIndices
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(safepointIndices_.length() <= maxSafepointIndices
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"safepointIndices_.length() <= maxSafepointIndices", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepointIndices_.length() <= maxSafepointIndices"
")"); do { MOZ_CrashSequence(__null, 17491); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17492
17493 // For each instruction with a safepoint, we have an OSI point inserted after
17494 // which handles bailouts in case of invalidation of the code.
17495 MOZ_ASSERT(osiIndices_.length() == graph.numSafepoints())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(osiIndices_.length() == graph.numSafepoints())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(osiIndices_.length() == graph.numSafepoints()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.length() == graph.numSafepoints()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17495); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "osiIndices_.length() == graph.numSafepoints()"
")"); do { MOZ_CrashSequence(__null, 17495); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17496
17497 return !masm.oom();
17498}
17499
17500static bool AddInlinedCompilations(JSContext* cx, HandleScript script,
17501 IonCompilationId compilationId,
17502 const WarpSnapshot* snapshot,
17503 bool* isValid) {
17504 MOZ_ASSERT(!*isValid)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!*isValid)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!*isValid))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!*isValid", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17504); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid"
")"); do { MOZ_CrashSequence(__null, 17504); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17505 IonScriptKey ionScriptKey(script, compilationId);
17506
17507 JitZone* jitZone = cx->zone()->jitZone();
17508
17509 for (const auto* scriptSnapshot : snapshot->scripts()) {
17510 JSScript* inlinedScript = scriptSnapshot->script();
17511 if (inlinedScript == script) {
17512 continue;
17513 }
17514
17515 // TODO(post-Warp): This matches FinishCompilation and is necessary to
17516 // ensure in-progress compilations are canceled when an inlined functon
17517 // becomes a debuggee. See the breakpoint-14.js jit-test.
17518 // When TI is gone, try to clean this up by moving AddInlinedCompilations to
17519 // WarpOracle so that we can handle this as part of addPendingRecompile
17520 // instead of requiring this separate check.
17521 if (inlinedScript->isDebuggee()) {
17522 *isValid = false;
17523 return true;
17524 }
17525
17526 if (!jitZone->addInlinedCompilation(ionScriptKey, inlinedScript)) {
17527 return false;
17528 }
17529 }
17530
17531 *isValid = true;
17532 return true;
17533}
17534
17535template <auto FuseMember, CompilationDependency::Type DepType>
17536struct RuntimeFuseDependency final : public CompilationDependency {
17537 explicit RuntimeFuseDependency() : CompilationDependency(DepType) {}
17538
17539 bool registerDependency(JSContext* cx,
17540 const IonScriptKey& ionScript) override {
17541 MOZ_ASSERT(checkDependency(cx))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(checkDependency(cx))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(checkDependency(cx)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("checkDependency(cx)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17541); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "checkDependency(cx)" ")"); do { MOZ_CrashSequence
(__null, 17541); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17542 return (cx->runtime()->runtimeFuses.ref().*FuseMember)
17543 .addFuseDependency(cx, ionScript);
17544 }
17545
17546 CompilationDependency* clone(TempAllocator& alloc) const override {
17547 return new (alloc.fallible()) RuntimeFuseDependency<FuseMember, DepType>();
17548 }
17549
17550 bool checkDependency(JSContext* cx) const override {
17551 return (cx->runtime()->runtimeFuses.ref().*FuseMember).intact();
17552 }
17553
17554 HashNumber hash() const override { return mozilla::HashGeneric(type); }
17555
17556 bool operator==(const CompilationDependency& dep) const override {
17557 // Since this dependency is runtime wide, they are all equal.
17558 return dep.type == type;
17559 }
17560};
17561
17562bool CodeGenerator::addHasSeenObjectEmulateUndefinedFuseDependency() {
17563 using Dependency =
17564 RuntimeFuseDependency<&RuntimeFuses::hasSeenObjectEmulateUndefinedFuse,
17565 CompilationDependency::Type::EmulatesUndefined>;
17566 return mirGen().tracker.addDependency(alloc(), Dependency());
17567}
17568
17569bool CodeGenerator::addHasSeenArrayExceedsInt32LengthFuseDependency() {
17570 using Dependency = RuntimeFuseDependency<
17571 &RuntimeFuses::hasSeenArrayExceedsInt32LengthFuse,
17572 CompilationDependency::Type::ArrayExceedsInt32Length>;
17573 return mirGen().tracker.addDependency(alloc(), Dependency());
17574}
17575
17576bool CodeGenerator::link(JSContext* cx) {
17577 AutoCreatedBy acb(masm, "CodeGenerator::link");
17578
17579 // We cancel off-thread Ion compilations in a few places during GC, but if
17580 // this compilation was performed off-thread it will already have been
17581 // removed from the relevant lists by this point. Don't allow GC here.
17582 JS::AutoAssertNoGC nogc(cx);
17583
17584 RootedScript script(cx, gen->outerInfo().script());
17585 MOZ_ASSERT(!script->hasIonScript())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!script->hasIonScript())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!script->hasIonScript()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!script->hasIonScript()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17585); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!script->hasIonScript()" ")"); do { MOZ_CrashSequence
(__null, 17585); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17586
17587 if (scriptCounts_ && !script->hasScriptCounts() &&
17588 !script->initScriptCounts(cx)) {
17589 return false;
17590 }
17591
17592 // Add all used nursery-values to the Value constant pool that's copied to the
17593 // IonScript.
17594 for (NurseryValueLabel& label : nurseryValueLabels_) {
17595 Value v = snapshot_->nurseryValues()[label.nurseryIndex];
17596 MOZ_ASSERT(v.isGCThing())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v.isGCThing())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(v.isGCThing()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("v.isGCThing()",
"./../../../../js/src/jit/CodeGenerator.cpp", 17596); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "v.isGCThing()" ")"); do { MOZ_CrashSequence
(__null, 17596); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17597 if (!graph.addConstantToPool(v, &label.constantPoolIndex)) {
17598 return false;
17599 }
17600 }
17601
17602 JitZone* jitZone = cx->zone()->jitZone();
17603
17604 IonCompilationId compilationId =
17605 cx->runtime()->jitRuntime()->nextCompilationId();
17606 jitZone->currentCompilationIdRef().emplace(compilationId);
17607 auto resetCurrentId = mozilla::MakeScopeExit(
17608 [jitZone] { jitZone->currentCompilationIdRef().reset(); });
17609
17610 // Record constraints. If an error occured, returns false and potentially
17611 // prevent future compilations. Otherwise, if an invalidation occured, then
17612 // skip the current compilation.
17613 bool isValid = false;
17614
17615 // If an inlined script is invalidated (for example, by attaching
17616 // a debugger), we must also invalidate the parent IonScript.
17617 if (!AddInlinedCompilations(cx, script, compilationId, snapshot_, &isValid)) {
17618 return false;
17619 }
17620
17621 // This compilation is no longer valid; don't proceed, but return true as this
17622 // isn't an error case either.
17623 if (!isValid) {
17624 return true;
17625 }
17626
17627 CompilationDependencyTracker& tracker = mirGen().tracker;
17628 // Make sure we're using the same realm as this context.
17629 MOZ_ASSERT(mirGen().realm->realmPtr() == cx->realm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mirGen().realm->realmPtr() == cx->realm())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mirGen().realm->realmPtr() == cx->realm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("mirGen().realm->realmPtr() == cx->realm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17629); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mirGen().realm->realmPtr() == cx->realm()"
")"); do { MOZ_CrashSequence(__null, 17629); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17630 if (!tracker.checkDependencies(cx)) {
17631 return true;
17632 }
17633
17634 IonScriptKey ionScriptKey(script, compilationId);
17635 for (auto iter = tracker.dependencies.iter(); !iter.done(); iter.next()) {
17636 CompilationDependency* dep = iter.get();
17637 if (!dep->registerDependency(cx, ionScriptKey)) {
17638 return false;
17639 }
17640 }
17641
17642 uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value);
17643
17644 size_t numNurseryObjects = snapshot_->nurseryObjects().length();
17645
17646 IonScript* ionScript = IonScript::New(
17647 cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_,
17648 snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(),
17649 graph.numConstants(), numNurseryObjects, safepointIndices_.length(),
17650 osiIndices_.length(), icList_.length(), runtimeData_.length(),
17651 safepoints_.size());
17652 if (!ionScript) {
17653 return false;
17654 }
17655#ifdef DEBUG1
17656 ionScript->setICHash(snapshot_->icHash());
17657#endif
17658
17659 auto freeIonScript = mozilla::MakeScopeExit([&ionScript] {
17660 // Use js_free instead of IonScript::Destroy: the cache list is still
17661 // uninitialized.
17662 js_free(ionScript);
17663 });
17664
17665 Linker linker(masm);
17666 JitCode* code = linker.newCode(cx, CodeKind::Ion);
17667 if (!code) {
17668 return false;
17669 }
17670
17671 // Encode native to bytecode map if profiling is enabled.
17672 if (isProfilerInstrumentationEnabled()) {
17673 // Generate native-to-bytecode main table.
17674 IonEntry::ScriptList scriptList;
17675 if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) {
17676 return false;
17677 }
17678
17679 // Find the realmId. We do not do cross-realm inlining, so it should be the
17680 // same for every inlined script.
17681 uint64_t realmId = script->realm()->creationOptions().profilerRealmID();
17682#ifdef DEBUG1
17683 for (const auto* scriptSnapshot : snapshot_->scripts()) {
17684 JSScript* inlinedScript = scriptSnapshot->script();
17685 MOZ_ASSERT(inlinedScript->realm()->creationOptions().profilerRealmID() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inlinedScript->realm()->creationOptions().profilerRealmID
() == realmId)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(inlinedScript->realm()->creationOptions
().profilerRealmID() == realmId))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("inlinedScript->realm()->creationOptions().profilerRealmID() == realmId"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17686); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inlinedScript->realm()->creationOptions().profilerRealmID() == realmId"
")"); do { MOZ_CrashSequence(__null, 17686); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17686 realmId)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inlinedScript->realm()->creationOptions().profilerRealmID
() == realmId)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(inlinedScript->realm()->creationOptions
().profilerRealmID() == realmId))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("inlinedScript->realm()->creationOptions().profilerRealmID() == realmId"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17686); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inlinedScript->realm()->creationOptions().profilerRealmID() == realmId"
")"); do { MOZ_CrashSequence(__null, 17686); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17687 }
17688#endif
17689
17690 uint8_t* ionTableAddr =
17691 ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_;
17692 JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr;
17693
17694 // Construct the IonEntry that will go into the global table.
17695 auto entry = MakeJitcodeGlobalEntry<IonEntry>(
17696 cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable,
17697 realmId);
17698 if (!entry) {
17699 return false;
17700 }
17701 (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|.
17702
17703 // Add entry to the global table.
17704 JitcodeGlobalTable* globalTable =
17705 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
17706 if (!globalTable->addEntry(std::move(entry))) {
17707 return false;
17708 }
17709
17710 // Mark the jitcode as having a bytecode map.
17711 code->setHasBytecodeMap();
17712 } else {
17713 // Add a dumy jitcodeGlobalTable entry.
17714 auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(),
17715 code->rawEnd());
17716 if (!entry) {
17717 return false;
17718 }
17719
17720 // Add entry to the global table.
17721 JitcodeGlobalTable* globalTable =
17722 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
17723 if (!globalTable->addEntry(std::move(entry))) {
17724 return false;
17725 }
17726
17727 // Mark the jitcode as having a bytecode map.
17728 code->setHasBytecodeMap();
17729 }
17730
17731 ionScript->setMethod(code);
17732
17733 // If the Gecko Profiler is enabled, mark IonScript as having been
17734 // instrumented accordingly.
17735 if (isProfilerInstrumentationEnabled()) {
17736 ionScript->setHasProfilingInstrumentation();
17737 }
17738
17739 Assembler::PatchDataWithValueCheck(
17740 CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript),
17741 ImmPtr((void*)-1));
17742
17743 for (CodeOffset offset : ionScriptLabels_) {
17744 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset),
17745 ImmPtr(ionScript), ImmPtr((void*)-1));
17746 }
17747
17748 for (NurseryObjectLabel label : nurseryObjectLabels_) {
17749 void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex);
17750 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset),
17751 ImmPtr(entry), ImmPtr((void*)-1));
17752 }
17753 for (NurseryValueLabel label : nurseryValueLabels_) {
17754 void* entry = &ionScript->getConstant(label.constantPoolIndex);
17755 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset),
17756 ImmPtr(entry), ImmPtr((void*)-1));
17757 }
17758
17759 // for generating inline caches during the execution.
17760 if (runtimeData_.length()) {
17761 ionScript->copyRuntimeData(&runtimeData_[0]);
17762 }
17763 if (icList_.length()) {
17764 ionScript->copyICEntries(&icList_[0]);
17765 }
17766
17767 for (size_t i = 0; i < icInfo_.length(); i++) {
17768 IonIC& ic = ionScript->getICFromIndex(i);
17769 Assembler::PatchDataWithValueCheck(
17770 CodeLocationLabel(code, icInfo_[i].icOffsetForJump),
17771 ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1));
17772 Assembler::PatchDataWithValueCheck(
17773 CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic),
17774 ImmPtr((void*)-1));
17775 }
17776
17777 JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript,
17778 (void*)code->raw());
17779
17780 ionScript->setInvalidationEpilogueDataOffset(
17781 invalidateEpilogueData_.offset());
17782 if (jsbytecode* osrPc = gen->outerInfo().osrPc()) {
17783 ionScript->setOsrPc(osrPc);
17784 ionScript->setOsrEntryOffset(getOsrEntryOffset());
17785 }
17786 ionScript->setInvalidationEpilogueOffset(invalidate_.offset());
17787
17788 perfSpewer().saveJSProfile(cx, script, code);
17789
17790#ifdef MOZ_VTUNE1
17791 vtune::MarkScript(code, script, "ion");
17792#endif
17793
17794 // Set a Ion counter hint for this script.
17795 if (cx->runtime()->jitRuntime()->hasJitHintsMap()) {
17796 JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap();
17797 jitHints->recordIonCompilation(script);
17798 }
17799
17800 // for marking during GC.
17801 if (safepointIndices_.length()) {
17802 ionScript->copySafepointIndices(&safepointIndices_[0]);
17803 }
17804 if (safepoints_.size()) {
17805 ionScript->copySafepoints(&safepoints_);
17806 }
17807
17808 // for recovering from an Ion Frame.
17809 if (osiIndices_.length()) {
17810 ionScript->copyOsiIndices(&osiIndices_[0]);
17811 }
17812 if (snapshots_.listSize()) {
17813 ionScript->copySnapshots(&snapshots_);
17814 }
17815 MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size())do { if (snapshots_.listSize()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(recovers_.size()
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(recovers_.size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("recovers_.size()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()"
")"); do { MOZ_CrashSequence(__null, 17815); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
17816 if (recovers_.size()) {
17817 ionScript->copyRecovers(&recovers_);
17818 }
17819 if (graph.numConstants()) {
17820 const Value* vp = graph.constantPool();
17821 ionScript->copyConstants(vp);
17822 }
17823
17824 // Attach any generated script counts to the script.
17825 if (IonScriptCounts* counts = extractScriptCounts()) {
17826 script->addIonCounts(counts);
17827 }
17828 // WARNING: Code after this point must be infallible!
17829
17830 // Copy the list of nursery objects. Note that the store buffer can add
17831 // HeapPtr edges that must be cleared in IonScript::Destroy. See the
17832 // infallibility warning above.
17833 const auto& nurseryObjects = snapshot_->nurseryObjects();
17834 for (size_t i = 0; i < nurseryObjects.length(); i++) {
17835 ionScript->nurseryObjects()[i].init(nurseryObjects[i]);
17836 }
17837
17838 // Initialization fence for the IonScript.
17839 MemoryReleaseFence(script.get());
17840
17841 // Transfer ownership of the IonScript to the JitScript. At this point enough
17842 // of the IonScript must be initialized for IonScript::Destroy to work.
17843 freeIonScript.release();
17844 script->jitScript()->setIonScript(script, ionScript);
17845
17846 return true;
17847}
17848
17849void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) {
17850 ValueOperand box = ToValue(lir->input());
17851 const LDefinition* result = lir->output();
17852
17853 // Out-of-line path to convert int32 to double or bailout
17854 // if this instruction is fallible.
17855 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
17856 ValueOperand value = ToValue(lir->input());
17857
17858 if (lir->mir()->fallible()) {
17859 Label bail;
17860 masm.branchTestInt32(Assembler::NotEqual, value, &bail);
17861 bailoutFrom(&bail, lir->snapshot());
17862 }
17863 masm.convertInt32ToDouble(value.payloadOrValueReg(),
17864 ToFloatRegister(lir->output()));
17865 masm.jump(ool.rejoin());
17866 });
17867 addOutOfLineCode(ool, lir->mir());
17868
17869 FloatRegister resultReg = ToFloatRegister(result);
17870 masm.branchTestDouble(Assembler::NotEqual, box, ool->entry());
17871 masm.unboxDouble(box, resultReg);
17872 masm.bind(ool->rejoin());
17873}
17874
17875void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) {
17876 Register obj = ToRegister(lir->object());
17877 ValueOperand idVal = ToValue(lir->index());
17878 ValueOperand value = ToValue(lir->value());
17879
17880 Register temp0 = ToRegister(lir->temp0());
17881 // See comment in LIROps.yaml (x86 is short on registers)
17882#ifndef JS_CODEGEN_X86
17883 Register temp1 = ToRegister(lir->temp1());
17884 Register temp2 = ToRegister(lir->temp2());
17885#endif
17886
17887 // The instruction is marked as call-instruction so only these registers are
17888 // live.
17889 LiveRegisterSet liveRegs;
17890 liveRegs.addUnchecked(obj);
17891 liveRegs.addUnchecked(idVal);
17892 liveRegs.addUnchecked(value);
17893 liveRegs.addUnchecked(temp0);
17894#ifndef JS_CODEGEN_X86
17895 liveRegs.addUnchecked(temp1);
17896 liveRegs.addUnchecked(temp2);
17897#endif
17898
17899 Label cacheHit, done;
17900#ifdef JS_CODEGEN_X86
17901 masm.emitMegamorphicCachedSetSlot(
17902 idVal, obj, temp0, value, liveRegs, &cacheHit,
17903 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
17904 EmitPreBarrier(masm, addr, mirType);
17905 });
17906#else
17907 masm.emitMegamorphicCachedSetSlot(
17908 idVal, obj, temp0, temp1, temp2, value, liveRegs, &cacheHit,
17909 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
17910 EmitPreBarrier(masm, addr, mirType);
17911 });
17912#endif
17913
17914 pushArg(Imm32(lir->mir()->strict()));
17915 pushArg(ToValue(lir->value()));
17916 pushArg(ToValue(lir->index()));
17917 pushArg(obj);
17918
17919 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
17920 callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir);
17921
17922 masm.jump(&done);
17923 masm.bind(&cacheHit);
17924
17925 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
17926 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
17927
17928 // Note: because this is a call-instruction, no registers need to be saved.
17929 MOZ_ASSERT(lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->isCall())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->isCall()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("lir->isCall()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 17929); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 17929); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17930 emitPostWriteBarrier(obj);
17931
17932 masm.bind(&done);
17933}
17934
17935void CodeGenerator::visitLoadScriptedProxyHandler(
17936 LLoadScriptedProxyHandler* ins) {
17937 Register obj = ToRegister(ins->object());
17938 Register output = ToRegister(ins->output());
17939
17940 Label bail;
17941 Address handlerAddr(obj, ProxyObject::offsetOfReservedSlot(
17942 ScriptedProxyHandler::HANDLER_EXTRA));
17943 masm.fallibleUnboxObject(handlerAddr, output, &bail);
17944 bailoutFrom(&bail, ins->snapshot());
17945}
17946
17947#ifdef JS_PUNBOX641
17948void CodeGenerator::visitCheckScriptedProxyGetResult(
17949 LCheckScriptedProxyGetResult* ins) {
17950 ValueOperand target = ToValue(ins->target());
17951 ValueOperand value = ToValue(ins->value());
17952 ValueOperand id = ToValue(ins->id());
17953 Register scratch = ToRegister(ins->temp0());
17954 Register scratch2 = ToRegister(ins->temp1());
17955
17956 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue,
17957 MutableHandleValue);
17958 OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>(
17959 ins, ArgList(scratch, id, value), StoreValueTo(value));
17960
17961 masm.unboxObject(target, scratch);
17962 masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch,
17963 scratch2, ool->entry());
17964 masm.bind(ool->rejoin());
17965}
17966#endif
17967
17968void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) {
17969 ValueOperand id = ToValue(ins->idVal());
17970 ValueOperand output = ToOutValue(ins);
17971 Register scratch = ToRegister(ins->temp0());
17972
17973 masm.moveValue(id, output);
17974
17975 Label done, callVM;
17976 Label bail;
17977 {
17978 ScratchTagScope tag(masm, output);
17979 masm.splitTagForTest(output, tag);
17980 masm.branchTestString(Assembler::Equal, tag, &done);
17981 masm.branchTestSymbol(Assembler::Equal, tag, &done);
17982 masm.branchTestInt32(Assembler::NotEqual, tag, &bail);
17983 }
17984
17985 masm.unboxInt32(output, scratch);
17986
17987 using Fn = JSLinearString* (*)(JSContext*, int);
17988 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
17989 ins, ArgList(scratch), StoreRegisterTo(output.scratchReg()));
17990
17991 masm.lookupStaticIntString(scratch, output.scratchReg(),
17992 gen->runtime->staticStrings(), ool->entry());
17993
17994 masm.bind(ool->rejoin());
17995 masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
17996 masm.bind(&done);
17997
17998 bailoutFrom(&bail, ins->snapshot());
17999}
18000
18001void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) {
18002 const Register obj = ToRegister(ins->object());
18003 size_t slot = ins->mir()->slot();
18004 ValueOperand result = ToOutValue(ins);
18005
18006 masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result);
18007}
18008
18009void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) {
18010 const Register obj = ToRegister(ins->object());
18011 size_t slot = ins->mir()->slot();
18012 AnyRegister result = ToAnyRegister(ins->output());
18013 MIRType type = ins->mir()->type();
18014
18015 masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)),
18016 type, result);
18017}
18018
18019void CodeGenerator::visitLoadFixedSlotFromOffset(
18020 LLoadFixedSlotFromOffset* lir) {
18021 Register obj = ToRegister(lir->object());
18022 Register offset = ToRegister(lir->offset());
18023 ValueOperand out = ToOutValue(lir);
18024
18025 // obj[offset]
18026 masm.loadValue(BaseIndex(obj, offset, TimesOne), out);
18027}
18028
18029void CodeGenerator::visitStoreFixedSlotFromOffsetV(
18030 LStoreFixedSlotFromOffsetV* lir) {
18031 Register obj = ToRegister(lir->object());
18032 Register offset = ToRegister(lir->offset());
18033 ValueOperand value = ToValue(lir->value());
18034 Register temp = ToRegister(lir->temp0());
18035
18036 BaseIndex baseIndex(obj, offset, TimesOne);
18037 masm.computeEffectiveAddress(baseIndex, temp);
18038
18039 Address slot(temp, 0);
18040 if (lir->mir()->needsBarrier()) {
18041 emitPreBarrier(slot);
18042 }
18043
18044 // obj[offset]
18045 masm.storeValue(value, slot);
18046}
18047
18048void CodeGenerator::visitStoreFixedSlotFromOffsetT(
18049 LStoreFixedSlotFromOffsetT* lir) {
18050 Register obj = ToRegister(lir->object());
18051 Register offset = ToRegister(lir->offset());
18052 const LAllocation* value = lir->value();
18053 MIRType valueType = lir->mir()->value()->type();
18054 Register temp = ToRegister(lir->temp0());
18055
18056 BaseIndex baseIndex(obj, offset, TimesOne);
18057 masm.computeEffectiveAddress(baseIndex, temp);
18058
18059 Address slot(temp, 0);
18060 if (lir->mir()->needsBarrier()) {
18061 emitPreBarrier(slot);
18062 }
18063
18064 // obj[offset]
18065 ConstantOrRegister nvalue =
18066 value->isConstant()
18067 ? ConstantOrRegister(value->toConstant()->toJSValue())
18068 : TypedOrValueRegister(valueType, ToAnyRegister(value));
18069 masm.storeConstantOrRegister(nvalue, slot);
18070}
18071
18072template <typename T>
18073static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type,
18074 bool fallible, AnyRegister dest, Register64 temp,
18075 Label* fail) {
18076 MOZ_ASSERT_IF(type == MIRType::Double, temp != Register64::Invalid())do { if (type == MIRType::Double) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(temp != Register64
::Invalid())>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(temp != Register64::Invalid()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("temp != Register64::Invalid()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 18076); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp != Register64::Invalid()" ")"); do { MOZ_CrashSequence
(__null, 18076); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
18077 if (type == MIRType::Double) {
18078 MOZ_ASSERT(dest.isFloat())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dest.isFloat())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dest.isFloat()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("dest.isFloat()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 18078); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dest.isFloat()" ")"); do { MOZ_CrashSequence
(__null, 18078); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
18079#if defined(JS_NUNBOX32)
18080 auto tempVal = ValueOperand(temp.high, temp.low);
18081#else
18082 auto tempVal = ValueOperand(temp.reg);
18083#endif
18084 masm.loadValue(src, tempVal);
18085 masm.ensureDouble(tempVal, dest.fpu(), fail);
18086 return;
18087 }
18088 if (fallible) {
18089 switch (type) {
18090 case MIRType::Int32:
18091 masm.fallibleUnboxInt32(src, dest.gpr(), fail);
18092 break;
18093 case MIRType::Boolean:
18094 masm.fallibleUnboxBoolean(src, dest.gpr(), fail);
18095 break;
18096 case MIRType::Object:
18097 masm.fallibleUnboxObject(src, dest.gpr(), fail);
18098 break;
18099 case MIRType::String:
18100 masm.fallibleUnboxString(src, dest.gpr(), fail);
18101 break;
18102 case MIRType::Symbol:
18103 masm.fallibleUnboxSymbol(src, dest.gpr(), fail);
18104 break;
18105 case MIRType::BigInt:
18106 masm.fallibleUnboxBigInt(src, dest.gpr(), fail);
18107 break;
18108 default:
18109 MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType"
, "./../../../../js/src/jit/CodeGenerator.cpp", 18109); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected MIRType" ")"); do { MOZ_CrashSequence
(__null, 18109); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
18110 }
18111 return;
18112 }
18113 masm.loadUnboxedValue(src, type, dest);
18114}
18115
18116void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) {
18117 const MLoadFixedSlotAndUnbox* mir = ins->mir();
18118 MIRType type = mir->type();
18119 Register input = ToRegister(ins->object());
18120 AnyRegister result = ToAnyRegister(ins->output());
18121 Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0());
18122 size_t slot = mir->slot();
18123
18124 Address address(input, NativeObject::getFixedSlotOffset(slot));
18125
18126 Label bail;
18127 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, maybeTemp,
18128 &bail);
18129 if (mir->fallible()) {
18130 bailoutFrom(&bail, ins->snapshot());
18131 }
18132}
18133
18134void CodeGenerator::visitLoadDynamicSlotAndUnbox(
18135 LLoadDynamicSlotAndUnbox* ins) {
18136 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
18137 MIRType type = mir->type();
18138 Register input = ToRegister(ins->slots());
18139 AnyRegister result = ToAnyRegister(ins->output());
18140 Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0());
18141 size_t slot = mir->slot();
18142
18143 Address address(input, slot * sizeof(JS::Value));
18144
18145 Label bail;
18146 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, maybeTemp,
18147 &bail);
18148 if (mir->fallible()) {
18149 bailoutFrom(&bail, ins->snapshot());
18150 }
18151}
18152
18153void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) {
18154 const MLoadElementAndUnbox* mir = ins->mir();
18155 MIRType type = mir->type();
18156 Register elements = ToRegister(ins->elements());
18157 AnyRegister result = ToAnyRegister(ins->output());
18158 Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0());
18159
18160 auto source = ToAddressOrBaseObjectElementIndex(elements, ins->index());
18161
18162 Label bail;
18163 source.match([&](const auto& source) {
18164 EmitLoadAndUnbox(masm, source, type, mir->fallible(), result, maybeTemp,
18165 &bail);
18166 });
18167
18168 if (mir->fallible()) {
18169 bailoutFrom(&bail, ins->snapshot());
18170 }
18171}
18172
18173void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg,
18174 Address slotAddr,
18175 TypedOrValueRegister dest) {
18176 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
18177 // This code is called with a non-atomic string in |stringReg|.
18178 // When it returns, |stringReg| contains an unboxed pointer to an
18179 // atomized version of that string, and |slotAddr| contains a
18180 // StringValue pointing to that atom. If |dest| is a ValueOperand,
18181 // it contains the same StringValue; otherwise we assert that |dest|
18182 // is |stringReg|.
18183
18184 saveLive(ins);
18185 pushArg(stringReg);
18186
18187 using Fn = JSAtom* (*)(JSContext*, JSString*);
18188 callVM<Fn, js::AtomizeString>(ins);
18189 StoreRegisterTo(stringReg).generate(this);
18190 restoreLiveIgnore(ins, StoreRegisterTo(stringReg).clobbered());
18191
18192 if (dest.hasValue()) {
18193 masm.moveValue(
18194 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
18195 dest.valueReg());
18196 } else {
18197 MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"dest.typedReg().gpr() == stringReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 18197); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { MOZ_CrashSequence(__null, 18197); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18198 }
18199
18200 emitPreBarrier(slotAddr);
18201 masm.storeTypedOrValue(dest, slotAddr);
18202
18203 // We don't need a post-barrier because atoms aren't nursery-allocated.
18204#ifdef DEBUG1
18205 // We need a temp register for the nursery check. Spill something.
18206 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
18207 allRegs.take(stringReg);
18208 Register temp = allRegs.takeAny();
18209 masm.push(temp);
18210
18211 Label tenured;
18212 masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp,
18213 &tenured);
18214 masm.assumeUnreachable("AtomizeString returned a nursery pointer");
18215 masm.bind(&tenured);
18216
18217 masm.pop(temp);
18218#endif
18219
18220 masm.jump(ool.rejoin());
18221 });
18222 addOutOfLineCode(ool, ins->mirRaw()->toInstruction());
18223 masm.branchTest32(Assembler::NonZero,
18224 Address(stringReg, JSString::offsetOfFlags()),
18225 Imm32(StringFlags::ATOM_BIT), ool->rejoin());
18226
18227 masm.branchTest32(Assembler::Zero,
18228 Address(stringReg, JSString::offsetOfFlags()),
18229 Imm32(StringFlags::ATOM_REF_BIT), ool->entry());
18230 masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg);
18231
18232 if (dest.hasValue()) {
18233 masm.moveValue(
18234 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
18235 dest.valueReg());
18236 } else {
18237 MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"dest.typedReg().gpr() == stringReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 18237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { MOZ_CrashSequence(__null, 18237); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18238 }
18239
18240 emitPreBarrier(slotAddr);
18241 masm.storeTypedOrValue(dest, slotAddr);
18242
18243 masm.bind(ool->rejoin());
18244}
18245
18246void CodeGenerator::visitLoadFixedSlotAndAtomize(
18247 LLoadFixedSlotAndAtomize* ins) {
18248 Register obj = ToRegister(ins->object());
18249 Register temp = ToRegister(ins->temp0());
18250 size_t slot = ins->mir()->slot();
18251 ValueOperand result = ToOutValue(ins);
18252
18253 Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot));
18254 masm.loadValue(slotAddr, result);
18255
18256 Label notString;
18257 masm.branchTestString(Assembler::NotEqual, result, &notString);
18258 masm.unboxString(result, temp);
18259 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
18260 masm.bind(&notString);
18261}
18262
18263void CodeGenerator::visitLoadDynamicSlotAndAtomize(
18264 LLoadDynamicSlotAndAtomize* ins) {
18265 ValueOperand result = ToOutValue(ins);
18266 Register temp = ToRegister(ins->temp0());
18267 Register base = ToRegister(ins->input());
18268 int32_t offset = ins->mir()->slot() * sizeof(js::Value);
18269
18270 Address slotAddr(base, offset);
18271 masm.loadValue(slotAddr, result);
18272
18273 Label notString;
18274 masm.branchTestString(Assembler::NotEqual, result, &notString);
18275 masm.unboxString(result, temp);
18276 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
18277 masm.bind(&notString);
18278}
18279
18280void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize(
18281 LLoadFixedSlotUnboxAndAtomize* ins) {
18282 const MLoadFixedSlotAndUnbox* mir = ins->mir();
18283 MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"mir->type() == MIRType::String", "./../../../../js/src/jit/CodeGenerator.cpp"
, 18283); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 18283); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18284 Register input = ToRegister(ins->object());
18285 AnyRegister result = ToAnyRegister(ins->output());
18286 size_t slot = mir->slot();
18287
18288 Address slotAddr(input, NativeObject::getFixedSlotOffset(slot));
18289
18290 Label bail;
18291 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
18292 Register64::Invalid(), &bail);
18293 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
18294 TypedOrValueRegister(MIRType::String, result));
18295
18296 if (mir->fallible()) {
18297 bailoutFrom(&bail, ins->snapshot());
18298 }
18299}
18300
18301void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize(
18302 LLoadDynamicSlotUnboxAndAtomize* ins) {
18303 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
18304 MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"mir->type() == MIRType::String", "./../../../../js/src/jit/CodeGenerator.cpp"
, 18304); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 18304); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18305 Register input = ToRegister(ins->slots());
18306 AnyRegister result = ToAnyRegister(ins->output());
18307 size_t slot = mir->slot();
18308
18309 Address slotAddr(input, slot * sizeof(JS::Value));
18310
18311 Label bail;
18312 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
18313 Register64::Invalid(), &bail);
18314 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
18315 TypedOrValueRegister(MIRType::String, result));
18316
18317 if (mir->fallible()) {
18318 bailoutFrom(&bail, ins->snapshot());
18319 }
18320}
18321
18322void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) {
18323 MOZ_ASSERT(!ins->mir()->preserveWrapper())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->preserveWrapper())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->preserveWrapper
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->preserveWrapper()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 18323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->preserveWrapper()"
")"); do { MOZ_CrashSequence(__null, 18323); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18324
18325 Register obj = ToRegister(ins->object());
18326 ValueOperand value = ToValue(ins->value());
18327 Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0());
18328
18329 Shape* shape = ins->mir()->shape();
18330 masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) {
18331 EmitPreBarrier(masm, addr, MIRType::Shape);
18332 });
18333
18334 // Perform the store. No pre-barrier required since this is a new
18335 // initialization.
18336
18337 uint32_t offset = ins->mir()->slotOffset();
18338 if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) {
18339 Address slot(obj, offset);
18340 masm.storeValue(value, slot);
18341 } else {
18342 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp);
18343 Address slot(maybeTemp, offset);
18344 masm.storeValue(value, slot);
18345 }
18346}
18347
18348void CodeGenerator::visitAddAndStoreSlotPreserveWrapper(
18349 LAddAndStoreSlotPreserveWrapper* ins) {
18350 MOZ_ASSERT(ins->mir()->preserveWrapper())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->preserveWrapper())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->preserveWrapper
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ins->mir()->preserveWrapper()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 18350); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->preserveWrapper()"
")"); do { MOZ_CrashSequence(__null, 18350); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18351
18352 Register obj = ToRegister(ins->object());
18353 ValueOperand value = ToValue(ins->value());
18354 Register temp0 = ToTempRegisterOrInvalid(ins->temp0());
18355 Register temp1 = ToTempRegisterOrInvalid(ins->temp1());
18356
18357 LiveRegisterSet liveRegs = liveVolatileRegs(ins);
18358 liveRegs.takeUnchecked(temp0);
18359 liveRegs.takeUnchecked(temp1);
18360 masm.preserveWrapper(obj, temp0, temp1, liveRegs);
18361 bailoutIfFalseBool(temp0, ins->snapshot());
18362
18363 Shape* shape = ins->mir()->shape();
18364 masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) {
18365 EmitPreBarrier(masm, addr, MIRType::Shape);
18366 });
18367
18368 // Perform the store. No pre-barrier required since this is a new
18369 // initialization.
18370
18371 uint32_t offset = ins->mir()->slotOffset();
18372 if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) {
18373 Address slot(obj, offset);
18374 masm.storeValue(value, slot);
18375 } else {
18376 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0);
18377 Address slot(temp0, offset);
18378 masm.storeValue(value, slot);
18379 }
18380}
18381
18382void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) {
18383 Register obj = ToRegister(ins->object());
18384 ValueOperand value = ToValue(ins->value());
18385 Register temp0 = ToRegister(ins->temp0());
18386 Register temp1 = ToRegister(ins->temp1());
18387
18388 if (ins->mir()->preserveWrapper()) {
18389 LiveRegisterSet liveRegs;
18390 liveRegs.addUnchecked(obj);
18391 liveRegs.addUnchecked(value);
18392 masm.preserveWrapper(obj, temp0, temp1, liveRegs);
18393 bailoutIfFalseBool(temp0, ins->snapshot());
18394 }
18395
18396 masm.Push(obj);
18397 masm.Push(value);
18398
18399 using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount);
18400 masm.setupAlignedABICall();
18401 masm.loadJSContext(temp0);
18402 masm.passABIArg(temp0);
18403 masm.passABIArg(obj);
18404 masm.move32(Imm32(ins->mir()->numNewSlots()), temp1);
18405 masm.passABIArg(temp1);
18406 masm.callWithABI<Fn, NativeObject::growSlotsPure>();
18407 masm.storeCallPointerResult(temp0);
18408
18409 masm.Pop(value);
18410 masm.Pop(obj);
18411
18412 bailoutIfFalseBool(temp0, ins->snapshot());
18413
18414 masm.storeObjShape(ins->mir()->shape(), obj,
18415 [](MacroAssembler& masm, const Address& addr) {
18416 EmitPreBarrier(masm, addr, MIRType::Shape);
18417 });
18418
18419 // Perform the store. No pre-barrier required since this is a new
18420 // initialization.
18421 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0);
18422 Address slot(temp0, ins->mir()->slotOffset());
18423 masm.storeValue(value, slot);
18424}
18425
18426void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) {
18427 Register obj = ToRegister(ins->obj());
18428 size_t slot = ins->mir()->slot();
18429
18430 ValueOperand value = ToValue(ins->value());
18431
18432 Address address(obj, NativeObject::getFixedSlotOffset(slot));
18433 if (ins->mir()->needsBarrier()) {
18434 emitPreBarrier(address);
18435 }
18436
18437 masm.storeValue(value, address);
18438}
18439
18440void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) {
18441 const Register obj = ToRegister(ins->obj());
18442 size_t slot = ins->mir()->slot();
18443
18444 const LAllocation* value = ins->value();
18445 MIRType valueType = ins->mir()->value()->type();
18446
18447 Address address(obj, NativeObject::getFixedSlotOffset(slot));
18448 if (ins->mir()->needsBarrier()) {
18449 emitPreBarrier(address);
18450 }
18451
18452 ConstantOrRegister nvalue =
18453 value->isConstant()
18454 ? ConstantOrRegister(value->toConstant()->toJSValue())
18455 : TypedOrValueRegister(valueType, ToAnyRegister(value));
18456 masm.storeConstantOrRegister(nvalue, address);
18457}
18458
18459void CodeGenerator::visitGetNameCache(LGetNameCache* ins) {
18460 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18461 Register envChain = ToRegister(ins->envObj());
18462 ValueOperand output = ToOutValue(ins);
18463 Register temp = ToRegister(ins->temp0());
18464
18465 IonGetNameIC ic(liveRegs, envChain, output, temp);
18466 addIC(ins, allocateIC(ic));
18467}
18468
18469static bool IsConstantNonIndexString(const ConstantOrRegister& id) {
18470 if (!id.constant() || !id.value().isString()) {
18471 return false;
18472 }
18473 return !id.value().toString()->asOffThreadAtom().isIndex();
18474}
18475
18476void CodeGenerator::addGetPropertyCache(LInstruction* ins,
18477 LiveRegisterSet liveRegs,
18478 TypedOrValueRegister value,
18479 const ConstantOrRegister& id,
18480 ValueOperand output) {
18481 CacheKind kind = CacheKind::GetElem;
18482 if (IsConstantNonIndexString(id)) {
18483 kind = CacheKind::GetProp;
18484 }
18485 IonGetPropertyIC cache(kind, liveRegs, value, id, output);
18486 addIC(ins, allocateIC(cache));
18487}
18488
18489void CodeGenerator::addSetPropertyCache(LInstruction* ins,
18490 LiveRegisterSet liveRegs,
18491 Register objReg, Register temp,
18492 const ConstantOrRegister& id,
18493 const ConstantOrRegister& value,
18494 bool strict) {
18495 CacheKind kind = CacheKind::SetElem;
18496 if (IsConstantNonIndexString(id)) {
18497 kind = CacheKind::SetProp;
18498 }
18499 IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict);
18500 addIC(ins, allocateIC(cache));
18501}
18502
18503ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir,
18504 size_t n, MIRType type) {
18505 if (type == MIRType::Value) {
18506 return TypedOrValueRegister(ToValue(lir->getBoxOperand(n)));
18507 }
18508
18509 const LAllocation* value = lir->getOperand(n);
18510 if (value->isConstant()) {
18511 return ConstantOrRegister(value->toConstant()->toJSValue());
18512 }
18513
18514 return TypedOrValueRegister(type, ToAnyRegister(value));
18515}
18516
18517void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) {
18518 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18519 TypedOrValueRegister value =
18520 toConstantOrRegister(ins, LGetPropertyCache::ValueIndex,
18521 ins->mir()->value()->type())
18522 .reg();
18523 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex,
18524 ins->mir()->idval()->type());
18525 ValueOperand output = ToOutValue(ins);
18526 addGetPropertyCache(ins, liveRegs, value, id, output);
18527}
18528
18529void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) {
18530 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18531 Register obj = ToRegister(ins->obj());
18532 TypedOrValueRegister receiver =
18533 toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex,
18534 ins->mir()->receiver()->type())
18535 .reg();
18536 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex,
18537 ins->mir()->idval()->type());
18538 ValueOperand output = ToOutValue(ins);
18539
18540 CacheKind kind = CacheKind::GetElemSuper;
18541 if (IsConstantNonIndexString(id)) {
18542 kind = CacheKind::GetPropSuper;
18543 }
18544
18545 IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output);
18546 addIC(ins, allocateIC(cache));
18547}
18548
18549void CodeGenerator::visitBindNameCache(LBindNameCache* ins) {
18550 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18551 Register envChain = ToRegister(ins->environmentChain());
18552 Register output = ToRegister(ins->output());
18553 Register temp = ToRegister(ins->temp0());
18554
18555 IonBindNameIC ic(liveRegs, envChain, output, temp);
18556 addIC(ins, allocateIC(ic));
18557}
18558
18559void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) {
18560 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18561 TypedOrValueRegister value =
18562 toConstantOrRegister(ins, LHasOwnCache::ValueIndex,
18563 ins->mir()->value()->type())
18564 .reg();
18565 TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex,
18566 ins->mir()->idval()->type())
18567 .reg();
18568 Register output = ToRegister(ins->output());
18569
18570 IonHasOwnIC cache(liveRegs, value, id, output);
18571 addIC(ins, allocateIC(cache));
18572}
18573
18574void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) {
18575 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18576 TypedOrValueRegister value =
18577 toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex,
18578 ins->mir()->value()->type())
18579 .reg();
18580 TypedOrValueRegister id =
18581 toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex,
18582 ins->mir()->idval()->type())
18583 .reg();
18584 Register output = ToRegister(ins->output());
18585
18586 IonCheckPrivateFieldIC cache(liveRegs, value, id, output);
18587 addIC(ins, allocateIC(cache));
18588}
18589
18590void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) {
18591 pushArg(ImmGCPtr(ins->mir()->name()));
18592
18593 using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>);
18594 callVM<Fn, NewPrivateName>(ins);
18595}
18596
18597void CodeGenerator::visitDeleteProperty(LDeleteProperty* lir) {
18598 pushArg(ImmGCPtr(lir->mir()->name()));
18599 pushArg(ToValue(lir->value()));
18600
18601 using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*);
18602 if (lir->mir()->strict()) {
18603 callVM<Fn, DelPropOperation<true>>(lir);
18604 } else {
18605 callVM<Fn, DelPropOperation<false>>(lir);
18606 }
18607}
18608
18609void CodeGenerator::visitDeleteElement(LDeleteElement* lir) {
18610 pushArg(ToValue(lir->index()));
18611 pushArg(ToValue(lir->value()));
18612
18613 using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
18614 if (lir->mir()->strict()) {
18615 callVM<Fn, DelElemOperation<true>>(lir);
18616 } else {
18617 callVM<Fn, DelElemOperation<false>>(lir);
18618 }
18619}
18620
18621void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) {
18622 Register obj = ToRegister(lir->object());
18623 Register iterObj = ToRegister(lir->output());
18624 Register temp = ToRegister(lir->temp0());
18625 Register temp2 = ToRegister(lir->temp1());
18626 Register temp3 = ToRegister(lir->temp2());
18627
18628 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject);
18629 OutOfLineCode* ool = nullptr;
18630
18631 if (lir->mir()->skipRegistration()) {
18632 if (lir->mir()->wantsIndices()) {
18633 ool = oolCallVM<Fn, GetIteratorWithIndicesForObjectKeys>(
18634 lir, ArgList(obj), StoreRegisterTo(iterObj));
18635 } else {
18636 ool = oolCallVM<Fn, GetIteratorForObjectKeys>(lir, ArgList(obj),
18637 StoreRegisterTo(iterObj));
18638 }
18639 } else {
18640 if (lir->mir()->wantsIndices()) {
18641 ool = oolCallVM<Fn, GetIteratorWithIndices>(lir, ArgList(obj),
18642 StoreRegisterTo(iterObj));
18643 } else {
18644 ool = oolCallVM<Fn, GetIterator>(lir, ArgList(obj),
18645 StoreRegisterTo(iterObj));
18646 }
18647 }
18648
18649#ifdef DEBUG1
18650 if (!lir->mir()->getAliasSet().isStore()) {
18651 MOZ_ASSERT(lir->mir()->skipRegistration())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->skipRegistration())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mir()->skipRegistration
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->mir()->skipRegistration()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 18651); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->skipRegistration()"
")"); do { MOZ_CrashSequence(__null, 18651); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18652 Label done;
18653 masm.branchTestObjectIsProxy(false, obj, temp, &done);
18654 masm.assumeUnreachable("ObjectToIterator on a proxy must be a store.");
18655 masm.bind(&done);
18656 }
18657#endif
18658
18659 masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3,
18660 ool->entry(),
18661 !lir->mir()->skipRegistration());
18662
18663 Register nativeIter = temp;
18664 masm.loadPrivate(
18665 Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()),
18666 nativeIter);
18667
18668 Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlags());
18669 if (lir->mir()->wantsIndices()) {
18670 // At least one consumer of the output of this iterator has been optimized
18671 // to use iterator indices. If the cached iterator doesn't include indices,
18672 // but it was marked to indicate that we can create them if needed, then we
18673 // do a VM call to replace the cached iterator with a fresh iterator
18674 // including indices.
18675 masm.branchTest32(Assembler::NonZero, iterFlagsAddr,
18676 Imm32(NativeIterator::Flags::IndicesSupported),
18677 ool->entry());
18678 }
18679
18680 if (!lir->mir()->skipRegistration()) {
18681 masm.storePtr(obj, Address(nativeIter,
18682 NativeIterator::offsetOfObjectBeingIterated()));
18683 masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr);
18684
18685 Register enumeratorsAddr = temp2;
18686 masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr);
18687 masm.registerIterator(enumeratorsAddr, nativeIter, temp3);
18688
18689 // Generate post-write barrier for storing to
18690 // |iterObj->objectBeingIterated_|. We already know that |iterObj| is
18691 // tenured, so we only have to check |obj|.
18692 Label skipBarrier;
18693 masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier);
18694 {
18695 LiveRegisterSet save = liveVolatileRegs(lir);
18696 save.takeUnchecked(temp);
18697 save.takeUnchecked(temp2);
18698 save.takeUnchecked(temp3);
18699 if (iterObj.volatile_()) {
18700 save.addUnchecked(iterObj);
18701 }
18702
18703 masm.PushRegsInMask(save);
18704 emitPostWriteBarrier(iterObj);
18705 masm.PopRegsInMask(save);
18706 }
18707 masm.bind(&skipBarrier);
18708 }
18709
18710 masm.bind(ool->rejoin());
18711}
18712
18713void CodeGenerator::visitValueToIterator(LValueToIterator* lir) {
18714 pushArg(ToValue(lir->value()));
18715
18716 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue);
18717 callVM<Fn, ValueToIterator>(lir);
18718}
18719
18720void CodeGenerator::emitIteratorHasIndicesAndBranch(Register iterator,
18721 Register object,
18722 Register temp,
18723 Register temp2,
18724 Label* ifFalse) {
18725 // Check that the iterator has indices available.
18726 Address nativeIterAddr(iterator,
18727 PropertyIteratorObject::offsetOfIteratorSlot());
18728 masm.loadPrivate(nativeIterAddr, temp);
18729 masm.branchTest32(Assembler::Zero,
18730 Address(temp, NativeIterator::offsetOfFlags()),
18731 Imm32(NativeIterator::Flags::IndicesAvailable), ifFalse);
18732
18733 // Guard that the first shape stored in the iterator matches the current
18734 // shape of the iterated object.
18735 Address objShapeAddr(temp, NativeIterator::offsetOfObjectShape());
18736 masm.loadPtr(objShapeAddr, temp);
18737 masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object,
18738 ifFalse);
18739}
18740
18741void CodeGenerator::visitIteratorHasIndicesAndBranch(
18742 LIteratorHasIndicesAndBranch* lir) {
18743 Register iterator = ToRegister(lir->iterator());
18744 Register object = ToRegister(lir->object());
18745 Register temp = ToRegister(lir->temp0());
18746 Register temp2 = ToRegister(lir->temp1());
18747 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
18748 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
18749
18750 emitIteratorHasIndicesAndBranch(iterator, object, temp, temp2, ifFalse);
18751
18752 if (!isNextBlock(lir->ifTrue()->lir())) {
18753 masm.jump(ifTrue);
18754 }
18755}
18756
18757void CodeGenerator::visitIteratorsMatchAndHaveIndicesAndBranch(
18758 LIteratorsMatchAndHaveIndicesAndBranch* lir) {
18759 Register iterator = ToRegister(lir->iterator());
18760 Register otherIterator = ToRegister(lir->otherIterator());
18761 Register object = ToRegister(lir->object());
18762 Register temp = ToRegister(lir->temp0());
18763 Register temp2 = ToRegister(lir->temp1());
18764 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
18765 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
18766
18767 // Check that the iterators match, and then we can use either iterator
18768 // as a basis as if this were visitIteratorHasIndicesAndBranch
18769 masm.branchPtr(Assembler::NotEqual, iterator, otherIterator, ifFalse);
18770
18771 emitIteratorHasIndicesAndBranch(iterator, object, temp, temp2, ifFalse);
18772
18773 if (!isNextBlock(lir->ifTrue()->lir())) {
18774 masm.jump(ifTrue);
18775 }
18776}
18777
18778void CodeGenerator::visitLoadSlotByIteratorIndexCommon(Register object,
18779 Register indexScratch,
18780 Register kindScratch,
18781 ValueOperand result) {
18782 Label notDynamicSlot, notFixedSlot, done;
18783 masm.branch32(Assembler::NotEqual, kindScratch,
18784 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
18785 &notDynamicSlot);
18786 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch);
18787 masm.loadValue(BaseValueIndex(kindScratch, indexScratch), result);
18788 masm.jump(&done);
18789
18790 masm.bind(&notDynamicSlot);
18791 masm.branch32(Assembler::NotEqual, kindScratch,
18792 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
18793 // Fixed slot
18794 masm.loadValue(BaseValueIndex(object, indexScratch, sizeof(NativeObject)),
18795 result);
18796 masm.jump(&done);
18797 masm.bind(&notFixedSlot);
18798
18799#ifdef DEBUG1
18800 Label kindOkay;
18801 masm.branch32(Assembler::Equal, kindScratch,
18802 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
18803 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
18804 masm.bind(&kindOkay);
18805#endif
18806
18807 // Dense element
18808 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch);
18809 Label indexOkay;
18810 Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength());
18811 masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay);
18812 masm.assumeUnreachable("Dense element out of bounds");
18813 masm.bind(&indexOkay);
18814
18815 masm.loadValue(BaseObjectElementIndex(kindScratch, indexScratch), result);
18816 masm.branchTestMagicValue(Assembler::NotEqual, result, JS_ELEMENTS_HOLE,
18817 &done);
18818 masm.assumeUnreachable("Dense element is a hole");
18819 masm.bind(&done);
18820}
18821
18822void CodeGenerator::visitLoadSlotByIteratorIndex(
18823 LLoadSlotByIteratorIndex* lir) {
18824 Register object = ToRegister(lir->object());
18825 Register iterator = ToRegister(lir->iterator());
18826 Register indexScratch = ToRegister(lir->temp0());
18827 Register kindScratch = ToRegister(lir->temp1());
18828 ValueOperand result = ToOutValue(lir);
18829
18830 masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch,
18831 kindScratch);
18832
18833 visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result);
18834}
18835
18836void CodeGenerator::visitLoadSlotByIteratorIndexIndexed(
18837 LLoadSlotByIteratorIndexIndexed* lir) {
18838 Register object = ToRegister(lir->object());
18839 Register iterator = ToRegister(lir->iterator());
18840 Register index = ToRegister(lir->index());
18841 Register indexScratch = ToRegister(lir->temp0());
18842 Register kindScratch = ToRegister(lir->temp1());
18843 ValueOperand result = ToOutValue(lir);
18844
18845 masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch,
18846 indexScratch);
18847
18848 visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result);
18849}
18850
18851void CodeGenerator::visitStoreSlotByIteratorIndexCommon(Register object,
18852 Register indexScratch,
18853 Register kindScratch,
18854 ValueOperand value) {
18855 Label notDynamicSlot, notFixedSlot, done, doStore;
18856 masm.branch32(Assembler::NotEqual, kindScratch,
18857 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
18858 &notDynamicSlot);
18859 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch);
18860 masm.computeEffectiveAddress(BaseValueIndex(kindScratch, indexScratch),
18861 indexScratch);
18862 masm.jump(&doStore);
18863
18864 masm.bind(&notDynamicSlot);
18865 masm.branch32(Assembler::NotEqual, kindScratch,
18866 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
18867 // Fixed slot
18868 masm.computeEffectiveAddress(
18869 BaseValueIndex(object, indexScratch, sizeof(NativeObject)), indexScratch);
18870 masm.jump(&doStore);
18871 masm.bind(&notFixedSlot);
18872
18873#ifdef DEBUG1
18874 Label kindOkay;
18875 masm.branch32(Assembler::Equal, kindScratch,
18876 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
18877 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
18878 masm.bind(&kindOkay);
18879#endif
18880
18881 // Dense element
18882 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch);
18883 Label indexOkay;
18884 Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength());
18885 masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay);
18886 masm.assumeUnreachable("Dense element out of bounds");
18887 masm.bind(&indexOkay);
18888
18889 BaseObjectElementIndex elementAddress(kindScratch, indexScratch);
18890 masm.computeEffectiveAddress(elementAddress, indexScratch);
18891
18892 masm.bind(&doStore);
18893 Address storeAddress(indexScratch, 0);
18894 emitPreBarrier(storeAddress);
18895 masm.storeValue(value, storeAddress);
18896
18897 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, kindScratch, &done);
18898 masm.branchPtrInNurseryChunk(Assembler::Equal, object, kindScratch, &done);
18899
18900 saveVolatile(kindScratch);
18901 emitPostWriteBarrier(object);
18902 restoreVolatile(kindScratch);
18903
18904 masm.bind(&done);
18905}
18906
18907void CodeGenerator::visitStoreSlotByIteratorIndex(
18908 LStoreSlotByIteratorIndex* lir) {
18909 Register object = ToRegister(lir->object());
18910 Register iterator = ToRegister(lir->iterator());
18911 ValueOperand value = ToValue(lir->value());
18912 Register indexScratch = ToRegister(lir->temp0());
18913 Register kindScratch = ToRegister(lir->temp1());
18914
18915 masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch,
18916 kindScratch);
18917
18918 visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value);
18919}
18920
18921void CodeGenerator::visitStoreSlotByIteratorIndexIndexed(
18922 LStoreSlotByIteratorIndexIndexed* lir) {
18923 Register object = ToRegister(lir->object());
18924 Register iterator = ToRegister(lir->iterator());
18925 Register index = ToRegister(lir->index());
18926 ValueOperand value = ToValue(lir->value());
18927 Register indexScratch = ToRegister(lir->temp0());
18928 Register kindScratch = ToRegister(lir->temp1());
18929
18930 masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch,
18931 indexScratch);
18932
18933 visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value);
18934}
18935
18936void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) {
18937 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18938 Register objReg = ToRegister(ins->object());
18939 Register temp = ToRegister(ins->temp0());
18940
18941 ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex,
18942 ins->mir()->idval()->type());
18943 ConstantOrRegister value = toConstantOrRegister(
18944 ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type());
18945
18946 addSetPropertyCache(ins, liveRegs, objReg, temp, id, value,
18947 ins->mir()->strict());
18948}
18949
18950void CodeGenerator::visitThrow(LThrow* lir) {
18951 pushArg(ToValue(lir->value()));
18952
18953 using Fn = bool (*)(JSContext*, HandleValue);
18954 callVM<Fn, js::ThrowOperation>(lir);
18955}
18956
18957void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) {
18958 pushArg(ToValue(lir->stack()));
18959 pushArg(ToValue(lir->value()));
18960
18961 using Fn = bool (*)(JSContext*, HandleValue, HandleValue);
18962 callVM<Fn, js::ThrowWithStackOperation>(lir);
18963}
18964
18965void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) {
18966 switch (type) {
18967 case JSVAL_TYPE_OBJECT:
18968 masm.move32(Imm32(JSTYPE_OBJECT), output);
18969 break;
18970 case JSVAL_TYPE_DOUBLE:
18971 case JSVAL_TYPE_INT32:
18972 masm.move32(Imm32(JSTYPE_NUMBER), output);
18973 break;
18974 case JSVAL_TYPE_BOOLEAN:
18975 masm.move32(Imm32(JSTYPE_BOOLEAN), output);
18976 break;
18977 case JSVAL_TYPE_UNDEFINED:
18978 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
18979 break;
18980 case JSVAL_TYPE_NULL:
18981 masm.move32(Imm32(JSTYPE_OBJECT), output);
18982 break;
18983 case JSVAL_TYPE_STRING:
18984 masm.move32(Imm32(JSTYPE_STRING), output);
18985 break;
18986 case JSVAL_TYPE_SYMBOL:
18987 masm.move32(Imm32(JSTYPE_SYMBOL), output);
18988 break;
18989 case JSVAL_TYPE_BIGINT:
18990 masm.move32(Imm32(JSTYPE_BIGINT), output);
18991 break;
18992 default:
18993 MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType"
, "./../../../../js/src/jit/CodeGenerator.cpp", 18993); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported JSValueType" ")"); do { MOZ_CrashSequence
(__null, 18993); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
18994 }
18995}
18996
18997void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag,
18998 Register output, Label* done,
18999 Label* oolObject) {
19000 Label notMatch;
19001 switch (type) {
19002 case JSVAL_TYPE_OBJECT:
19003 // The input may be a callable object (result is "function") or
19004 // may emulate undefined (result is "undefined"). Use an OOL path.
19005 masm.branchTestObject(Assembler::Equal, tag, oolObject);
19006 return;
19007 case JSVAL_TYPE_DOUBLE:
19008 case JSVAL_TYPE_INT32:
19009 masm.branchTestNumber(Assembler::NotEqual, tag, &notMatch);
19010 break;
19011 default:
19012 masm.branchTestType(Assembler::NotEqual, tag, type, &notMatch);
19013 break;
19014 }
19015
19016 emitTypeOfJSType(type, output);
19017 masm.jump(done);
19018 masm.bind(&notMatch);
19019}
19020
19021void CodeGenerator::visitTypeOfV(LTypeOfV* lir) {
19022 ValueOperand value = ToValue(lir->input());
19023 Register output = ToRegister(lir->output());
19024 Register tag = masm.extractTag(value, output);
19025
19026 Label done;
19027
19028 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
19029 ValueOperand input = ToValue(lir->input());
19030 Register temp = ToTempUnboxRegister(lir->temp0());
19031 Register output = ToRegister(lir->output());
19032
19033 Register obj = masm.extractObject(input, temp);
19034 emitTypeOfObject(obj, output, ool.rejoin());
19035 masm.jump(ool.rejoin());
19036 });
19037 addOutOfLineCode(ool, lir->mir());
19038
19039 const std::initializer_list<JSValueType> defaultOrder = {
19040 JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED,
19041 JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING,
19042 JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
19043
19044 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
19045
19046 // Generate checks for previously observed types first.
19047 // The TypeDataList is sorted by descending frequency.
19048 for (auto& observed : lir->mir()->observedTypes()) {
19049 JSValueType type = observed.type();
19050
19051 // Unify number types.
19052 if (type == JSVAL_TYPE_INT32) {
19053 type = JSVAL_TYPE_DOUBLE;
19054 }
19055
19056 remaining -= type;
19057
19058 emitTypeOfCheck(type, tag, output, &done, ool->entry());
19059 }
19060
19061 // Generate checks for remaining types.
19062 for (auto type : defaultOrder) {
19063 if (!remaining.contains(type)) {
19064 continue;
19065 }
19066 remaining -= type;
19067
19068 if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) {
19069 // We can skip the check for the last remaining type, unless the type is
19070 // JSVAL_TYPE_OBJECT, which may have to go through the OOL path.
19071#ifdef DEBUG1
19072 emitTypeOfCheck(type, tag, output, &done, ool->entry());
19073 masm.assumeUnreachable("Unexpected Value type in visitTypeOfV");
19074#else
19075 emitTypeOfJSType(type, output);
19076#endif
19077 } else {
19078 emitTypeOfCheck(type, tag, output, &done, ool->entry());
19079 }
19080 }
19081 MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(remaining.isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19081); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { MOZ_CrashSequence
(__null, 19081); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
19082
19083 masm.bind(&done);
19084 masm.bind(ool->rejoin());
19085}
19086
19087void CodeGenerator::emitTypeOfObject(Register obj, Register output,
19088 Label* done) {
19089 Label slowCheck, isObject, isCallable, isUndefined;
19090 masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable,
19091 &isUndefined);
19092
19093 masm.bind(&isCallable);
19094 masm.move32(Imm32(JSTYPE_FUNCTION), output);
19095 masm.jump(done);
19096
19097 masm.bind(&isUndefined);
19098 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
19099 masm.jump(done);
19100
19101 masm.bind(&isObject);
19102 masm.move32(Imm32(JSTYPE_OBJECT), output);
19103 masm.jump(done);
19104
19105 masm.bind(&slowCheck);
19106
19107 saveVolatile(output);
19108 using Fn = JSType (*)(JSObject*);
19109 masm.setupAlignedABICall();
19110 masm.passABIArg(obj);
19111 masm.callWithABI<Fn, js::TypeOfObject>();
19112 masm.storeCallInt32Result(output);
19113 restoreVolatile(output);
19114}
19115
19116void CodeGenerator::visitTypeOfO(LTypeOfO* lir) {
19117 Register obj = ToRegister(lir->object());
19118 Register output = ToRegister(lir->output());
19119
19120 Label done;
19121 emitTypeOfObject(obj, output, &done);
19122 masm.bind(&done);
19123}
19124
19125void CodeGenerator::visitTypeOfName(LTypeOfName* lir) {
19126 Register input = ToRegister(lir->input());
19127 Register output = ToRegister(lir->output());
19128
19129#ifdef DEBUG1
19130 Label ok;
19131 masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok);
19132 masm.assumeUnreachable("bad JSType");
19133 masm.bind(&ok);
19134#endif
19135
19136 static_assert(JSTYPE_UNDEFINED == 0);
19137
19138 masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output);
19139 masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
19140}
19141
19142void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj,
19143 Register output) {
19144 saveVolatile(output);
19145 using Fn = JSType (*)(JSObject*);
19146 masm.setupAlignedABICall();
19147 masm.passABIArg(obj);
19148 masm.callWithABI<Fn, js::TypeOfObject>();
19149 masm.storeCallInt32Result(output);
19150 restoreVolatile(output);
19151
19152 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
19153 masm.cmp32Set(cond, output, Imm32(mir->jstype()), output);
19154}
19155
19156void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj,
19157 Register output, Label* success,
19158 Label* fail, Label* slowCheck) {
19159 Label* isObject = fail;
19160 Label* isFunction = fail;
19161 Label* isUndefined = fail;
19162
19163 switch (mir->jstype()) {
19164 case JSTYPE_UNDEFINED:
19165 isUndefined = success;
19166 break;
19167
19168 case JSTYPE_OBJECT:
19169 isObject = success;
19170 break;
19171
19172 case JSTYPE_FUNCTION:
19173 isFunction = success;
19174 break;
19175
19176 case JSTYPE_STRING:
19177 case JSTYPE_NUMBER:
19178 case JSTYPE_BOOLEAN:
19179 case JSTYPE_SYMBOL:
19180 case JSTYPE_BIGINT:
19181 case JSTYPE_LIMIT:
19182 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19182); AnnotateMozCrashReason
("MOZ_CRASH(" "Primitive type" ")"); do { MOZ_CrashSequence(__null
, 19182); __attribute__((nomerge)) ::abort(); } while (false)
; } while (false)
;
19183 }
19184
19185 masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined);
19186
19187 auto op = mir->jsop();
19188
19189 Label done;
19190 masm.bind(fail);
19191 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
19192 masm.jump(&done);
19193 masm.bind(success);
19194 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
19195 masm.bind(&done);
19196}
19197
19198void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) {
19199 ValueOperand input = ToValue(lir->input());
19200 Register output = ToRegister(lir->output());
19201 Register temp = ToTempUnboxRegister(lir->temp0());
19202
19203 auto* mir = lir->mir();
19204
19205 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
19206 ValueOperand input = ToValue(lir->input());
19207 Register output = ToRegister(lir->output());
19208 Register temp = ToTempUnboxRegister(lir->temp0());
19209
19210 Register obj = masm.extractObject(input, temp);
19211
19212 emitTypeOfIsObjectOOL(lir->mir(), obj, output);
19213
19214 masm.jump(ool.rejoin());
19215 });
19216 addOutOfLineCode(ool, mir);
19217
19218 Label success, fail;
19219
19220 switch (mir->jstype()) {
19221 case JSTYPE_UNDEFINED: {
19222 ScratchTagScope tag(masm, input);
19223 masm.splitTagForTest(input, tag);
19224
19225 masm.branchTestUndefined(Assembler::Equal, tag, &success);
19226 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
19227 break;
19228 }
19229
19230 case JSTYPE_OBJECT: {
19231 ScratchTagScope tag(masm, input);
19232 masm.splitTagForTest(input, tag);
19233
19234 masm.branchTestNull(Assembler::Equal, tag, &success);
19235 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
19236 break;
19237 }
19238
19239 case JSTYPE_FUNCTION: {
19240 masm.branchTestObject(Assembler::NotEqual, input, &fail);
19241 break;
19242 }
19243
19244 case JSTYPE_STRING:
19245 case JSTYPE_NUMBER:
19246 case JSTYPE_BOOLEAN:
19247 case JSTYPE_SYMBOL:
19248 case JSTYPE_BIGINT:
19249 case JSTYPE_LIMIT:
19250 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19250); AnnotateMozCrashReason
("MOZ_CRASH(" "Primitive type" ")"); do { MOZ_CrashSequence(__null
, 19250); __attribute__((nomerge)) ::abort(); } while (false)
; } while (false)
;
19251 }
19252
19253 Register obj = masm.extractObject(input, temp);
19254
19255 emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry());
19256
19257 masm.bind(ool->rejoin());
19258}
19259
19260void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) {
19261 Register input = ToRegister(lir->input());
19262 Register output = ToRegister(lir->output());
19263
19264 auto* mir = lir->mir();
19265
19266 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
19267 Register input = ToRegister(lir->input());
19268 Register output = ToRegister(lir->output());
19269
19270 emitTypeOfIsObjectOOL(lir->mir(), input, output);
19271
19272 masm.jump(ool.rejoin());
19273 });
19274 addOutOfLineCode(ool, mir);
19275
19276 Label success, fail;
19277 emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry());
19278
19279 masm.bind(ool->rejoin());
19280}
19281
19282void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) {
19283 ValueOperand input = ToValue(lir->input());
19284 Register output = ToRegister(lir->output());
19285
19286 auto* mir = lir->mir();
19287 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
19288
19289 switch (mir->jstype()) {
19290 case JSTYPE_STRING:
19291 masm.testStringSet(cond, input, output);
19292 break;
19293 case JSTYPE_NUMBER:
19294 masm.testNumberSet(cond, input, output);
19295 break;
19296 case JSTYPE_BOOLEAN:
19297 masm.testBooleanSet(cond, input, output);
19298 break;
19299 case JSTYPE_SYMBOL:
19300 masm.testSymbolSet(cond, input, output);
19301 break;
19302 case JSTYPE_BIGINT:
19303 masm.testBigIntSet(cond, input, output);
19304 break;
19305
19306 case JSTYPE_UNDEFINED:
19307 case JSTYPE_OBJECT:
19308 case JSTYPE_FUNCTION:
19309 case JSTYPE_LIMIT:
19310 MOZ_CRASH("Non-primitive type")do { do { } while (false); MOZ_ReportCrash("" "Non-primitive type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19310); AnnotateMozCrashReason
("MOZ_CRASH(" "Non-primitive type" ")"); do { MOZ_CrashSequence
(__null, 19310); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19311 }
19312}
19313
19314void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) {
19315 pushArg(ToValue(lir->nextMethod()));
19316 pushArg(ToRegister(lir->iterator()));
19317
19318 using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue);
19319 callVM<Fn, js::CreateAsyncFromSyncIterator>(lir);
19320}
19321
19322void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) {
19323 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
19324 ValueOperand input = ToValue(lir->input());
19325 ValueOperand output = ToOutValue(lir);
19326
19327 IonToPropertyKeyIC ic(liveRegs, input, output);
19328 addIC(lir, allocateIC(ic));
19329}
19330
19331void CodeGenerator::visitLoadElementV(LLoadElementV* load) {
19332 Register elements = ToRegister(load->elements());
19333 const ValueOperand out = ToOutValue(load);
19334
19335 auto source = ToAddressOrBaseObjectElementIndex(elements, load->index());
19336
19337 source.match([&](auto const& source) { masm.loadValue(source, out); });
19338
19339 if (load->mir()->needsHoleCheck()) {
19340 Label testMagic;
19341 masm.branchTestMagicValue(Assembler::Equal, out, JS_ELEMENTS_HOLE,
19342 &testMagic);
19343 bailoutFrom(&testMagic, load->snapshot());
19344 } else {
19345#ifdef DEBUG1
19346 Label ok;
19347 masm.branchTestMagicValue(Assembler::NotEqual, out, JS_ELEMENTS_HOLE, &ok);
19348 masm.assumeUnreachable("LoadElementV had incorrect needsHoleCheck");
19349 masm.bind(&ok);
19350#endif
19351 }
19352}
19353
19354void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) {
19355 Register elements = ToRegister(lir->elements());
19356 Register index = ToRegister(lir->index());
19357 Register initLength = ToRegister(lir->initLength());
19358 const ValueOperand out = ToOutValue(lir);
19359
19360 const MLoadElementHole* mir = lir->mir();
19361
19362 // If the index is out of bounds, load |undefined|. Otherwise, load the
19363 // value.
19364 Label outOfBounds, done;
19365 masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds);
19366
19367 masm.loadValue(BaseObjectElementIndex(elements, index), out);
19368
19369 // If the value wasn't a hole, we're done. Otherwise, we'll load undefined.
19370 masm.branchTestMagicValue(Assembler::NotEqual, out, JS_ELEMENTS_HOLE, &done);
19371
19372 if (mir->needsNegativeIntCheck()) {
19373 Label loadUndefined;
19374 masm.jump(&loadUndefined);
19375
19376 masm.bind(&outOfBounds);
19377
19378 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
19379
19380 masm.bind(&loadUndefined);
19381 } else {
19382 masm.bind(&outOfBounds);
19383 }
19384 masm.moveValue(UndefinedValue(), out);
19385
19386 masm.bind(&done);
19387}
19388
19389CodeGenerator::AddressOrBaseIndex CodeGenerator::ToAddressOrBaseIndex(
19390 Register elements, const LAllocation* index, Scalar::Type type) {
19391 if (index->isConstant()) {
19392 return AddressOrBaseIndex(ToAddress(elements, index, type));
19393 }
19394 return AddressOrBaseIndex(
19395 BaseIndex(elements, ToRegister(index), ScaleFromScalarType(type)));
19396}
19397
19398void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) {
19399 Register elements = ToRegister(lir->elements());
19400 Register temp0 = ToTempRegisterOrInvalid(lir->temp0());
19401 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
19402 AnyRegister out = ToAnyRegister(lir->output());
19403
19404 Scalar::Type storageType = lir->mir()->storageType();
19405
19406 LiveRegisterSet volatileRegs;
19407 if (MacroAssembler::LoadRequiresCall(storageType)) {
19408 volatileRegs = liveVolatileRegs(lir);
19409 }
19410
19411 auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType);
19412
19413 Label fail;
19414 source.match([&](const auto& source) {
19415 masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail,
19416 volatileRegs);
19417 });
19418
19419 if (fail.used()) {
19420 bailoutFrom(&fail, lir->snapshot());
19421 }
19422}
19423
19424void CodeGenerator::visitLoadUnboxedInt64(LLoadUnboxedInt64* lir) {
19425 Register elements = ToRegister(lir->elements());
19426 Register64 out = ToOutRegister64(lir);
19427
19428 Scalar::Type storageType = lir->mir()->storageType();
19429
19430 auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType);
19431
19432 source.match([&](const auto& source) { masm.load64(source, out); });
19433}
19434
19435static bool IsNativeEndian(const LAllocation* littleEndian) {
19436 constexpr bool isLittleEndian = std::endian::native == std::endian::little;
19437 return littleEndian->isConstant() &&
19438 ToBoolean(littleEndian) == isLittleEndian;
19439}
19440
19441static void BranchIfNativeEndian(MacroAssembler& masm,
19442 const LAllocation* littleEndian,
19443 Label* label) {
19444 if (!littleEndian->isConstant()) {
19445 if constexpr (std::endian::native == std::endian::little) {
19446 masm.branch32(Assembler::NotEqual, ToRegister(littleEndian), Imm32(0),
19447 label);
19448 } else {
19449 masm.branch32(Assembler::Equal, ToRegister(littleEndian), Imm32(0),
19450 label);
19451 }
19452 }
19453}
19454
19455void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) {
19456 Register elements = ToRegister(lir->elements());
19457 const LAllocation* littleEndian = lir->littleEndian();
19458 Register temp1 = ToTempRegisterOrInvalid(lir->temp0());
19459 Register temp2 = ToTempRegisterOrInvalid(lir->temp1());
19460 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp2());
19461 AnyRegister out = ToAnyRegister(lir->output());
19462
19463 Scalar::Type storageType = lir->mir()->storageType();
19464
19465 LiveRegisterSet volatileRegs;
19466 if (MacroAssembler::LoadRequiresCall(storageType)) {
19467 volatileRegs = liveVolatileRegs(lir);
19468 }
19469
19470 auto source = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19471
19472 bool noSwap = IsNativeEndian(littleEndian);
19473
19474 // Directly load if no byte swap is needed and the platform supports unaligned
19475 // accesses for the access. (Such support is assumed for integer types.)
19476 if (noSwap && (!Scalar::isFloatingType(storageType) ||
19477 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
19478 Label fail;
19479 source.match([&](const auto& source) {
19480 masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail,
19481 volatileRegs);
19482 });
19483
19484 if (fail.used()) {
19485 bailoutFrom(&fail, lir->snapshot());
19486 }
19487 return;
19488 }
19489
19490 // Load the value into a gpr register.
19491 source.match([&](const auto& source) {
19492 switch (storageType) {
19493 case Scalar::Int16:
19494 masm.load16UnalignedSignExtend(source, out.gpr());
19495 break;
19496 case Scalar::Uint16:
19497 masm.load16UnalignedZeroExtend(source, out.gpr());
19498 break;
19499 case Scalar::Int32:
19500 masm.load32Unaligned(source, out.gpr());
19501 break;
19502 case Scalar::Uint32:
19503 masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr());
19504 break;
19505 case Scalar::Float16:
19506 masm.load16UnalignedZeroExtend(source, temp1);
19507 break;
19508 case Scalar::Float32:
19509 masm.load32Unaligned(source, temp1);
19510 break;
19511 case Scalar::Float64:
19512 masm.load64Unaligned(source, temp64);
19513 break;
19514 case Scalar::Int8:
19515 case Scalar::Uint8:
19516 case Scalar::Uint8Clamped:
19517 case Scalar::BigInt64:
19518 case Scalar::BigUint64:
19519 default:
19520 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19520); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19520); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19521 }
19522 });
19523
19524 if (!noSwap) {
19525 // Swap the bytes in the loaded value.
19526 Label skip;
19527 BranchIfNativeEndian(masm, littleEndian, &skip);
19528
19529 switch (storageType) {
19530 case Scalar::Int16:
19531 masm.byteSwap16SignExtend(out.gpr());
19532 break;
19533 case Scalar::Uint16:
19534 masm.byteSwap16ZeroExtend(out.gpr());
19535 break;
19536 case Scalar::Int32:
19537 masm.byteSwap32(out.gpr());
19538 break;
19539 case Scalar::Uint32:
19540 masm.byteSwap32(out.isFloat() ? temp1 : out.gpr());
19541 break;
19542 case Scalar::Float16:
19543 masm.byteSwap16ZeroExtend(temp1);
19544 break;
19545 case Scalar::Float32:
19546 masm.byteSwap32(temp1);
19547 break;
19548 case Scalar::Float64:
19549 masm.byteSwap64(temp64);
19550 break;
19551 case Scalar::Int8:
19552 case Scalar::Uint8:
19553 case Scalar::Uint8Clamped:
19554 case Scalar::BigInt64:
19555 case Scalar::BigUint64:
19556 default:
19557 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19557); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19557); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19558 }
19559
19560 if (skip.used()) {
19561 masm.bind(&skip);
19562 }
19563 }
19564
19565 // Move the value into the output register.
19566 switch (storageType) {
19567 case Scalar::Int16:
19568 case Scalar::Uint16:
19569 case Scalar::Int32:
19570 break;
19571 case Scalar::Uint32:
19572 if (out.isFloat()) {
19573 masm.convertUInt32ToDouble(temp1, out.fpu());
19574 } else {
19575 // Bail out if the value doesn't fit into a signed int32 value. This
19576 // is what allows MLoadDataViewElement to have a type() of
19577 // MIRType::Int32 for UInt32 array loads.
19578 bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot());
19579 }
19580 break;
19581 case Scalar::Float16:
19582 masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs);
19583 break;
19584 case Scalar::Float32:
19585 masm.moveGPRToFloat32(temp1, out.fpu());
19586 break;
19587 case Scalar::Float64:
19588 masm.moveGPR64ToDouble(temp64, out.fpu());
19589 break;
19590 case Scalar::Int8:
19591 case Scalar::Uint8:
19592 case Scalar::Uint8Clamped:
19593 case Scalar::BigInt64:
19594 case Scalar::BigUint64:
19595 default:
19596 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19596); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19596); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19597 }
19598}
19599
19600void CodeGenerator::visitLoadDataViewElement64(LLoadDataViewElement64* lir) {
19601 Register elements = ToRegister(lir->elements());
19602 const LAllocation* littleEndian = lir->littleEndian();
19603 Register64 out = ToOutRegister64(lir);
19604
19605 MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->storageType()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::isBigIntType(lir->mir()->storageType()
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(Scalar::isBigIntType(lir->mir()->storageType()
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("Scalar::isBigIntType(lir->mir()->storageType())", "./../../../../js/src/jit/CodeGenerator.cpp"
, 19605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->storageType())"
")"); do { MOZ_CrashSequence(__null, 19605); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19606
19607 auto source = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19608
19609 bool noSwap = IsNativeEndian(littleEndian);
19610
19611 // Load the value into a register.
19612 source.match([&](const auto& source) { masm.load64Unaligned(source, out); });
19613
19614 if (!noSwap) {
19615 // Swap the bytes in the loaded value.
19616 Label skip;
19617 BranchIfNativeEndian(masm, littleEndian, &skip);
19618
19619 masm.byteSwap64(out);
19620
19621 if (skip.used()) {
19622 masm.bind(&skip);
19623 }
19624 }
19625}
19626
19627void CodeGenerator::visitLoadTypedArrayElementHole(
19628 LLoadTypedArrayElementHole* lir) {
19629 Register elements = ToRegister(lir->elements());
19630 Register index = ToRegister(lir->index());
19631 Register length = ToRegister(lir->length());
19632 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19633 const ValueOperand out = ToOutValue(lir);
19634
19635 Register scratch = out.scratchReg();
19636
19637 // Load undefined if index >= length.
19638 Label outOfBounds, done;
19639 masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds);
19640
19641 Scalar::Type arrayType = lir->mir()->arrayType();
19642
19643 LiveRegisterSet volatileRegs;
19644 if (MacroAssembler::LoadRequiresCall(arrayType)) {
19645 volatileRegs = liveVolatileRegs(lir);
19646 }
19647
19648 Label fail;
19649 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
19650 MacroAssembler::Uint32Mode uint32Mode =
19651 lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble
19652 : MacroAssembler::Uint32Mode::FailOnDouble;
19653 masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail,
19654 volatileRegs);
19655 masm.jump(&done);
19656
19657 masm.bind(&outOfBounds);
19658 masm.moveValue(UndefinedValue(), out);
19659
19660 if (fail.used()) {
19661 bailoutFrom(&fail, lir->snapshot());
19662 }
19663
19664 masm.bind(&done);
19665}
19666
19667void CodeGenerator::visitLoadTypedArrayElementHoleBigInt(
19668 LLoadTypedArrayElementHoleBigInt* lir) {
19669 Register elements = ToRegister(lir->elements());
19670 Register index = ToRegister(lir->index());
19671 Register length = ToRegister(lir->length());
19672 const ValueOperand out = ToOutValue(lir);
19673
19674 Register temp = ToRegister(lir->temp0());
19675
19676 // On x86 there are not enough registers. In that case reuse the output
19677 // registers as temporaries.
19678#ifdef JS_CODEGEN_X86
19679 MOZ_ASSERT(lir->temp1().isBogusTemp())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->temp1().isBogusTemp())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->temp1().isBogusTemp(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->temp1().isBogusTemp()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 19679); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp1().isBogusTemp()"
")"); do { MOZ_CrashSequence(__null, 19679); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19680 Register64 temp64 = out.toRegister64();
19681#else
19682 Register64 temp64 = ToRegister64(lir->temp1());
19683#endif
19684
19685 // Load undefined if index >= length.
19686 Label outOfBounds, done;
19687 masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds);
19688
19689 Scalar::Type arrayType = lir->mir()->arrayType();
19690 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
19691 masm.load64(source, temp64);
19692
19693#ifdef JS_CODEGEN_X86
19694 Register bigInt = temp;
19695 Register maybeTemp = InvalidReg;
19696#else
19697 Register bigInt = out.scratchReg();
19698 Register maybeTemp = temp;
19699#endif
19700 emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp);
19701
19702 masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out);
19703 masm.jump(&done);
19704
19705 masm.bind(&outOfBounds);
19706 masm.moveValue(UndefinedValue(), out);
19707
19708 masm.bind(&done);
19709}
19710
19711template <typename T>
19712static inline void StoreToTypedArray(MacroAssembler& masm,
19713 Scalar::Type writeType,
19714 const LAllocation* value, const T& dest,
19715 Register temp,
19716 LiveRegisterSet volatileRegs) {
19717 if (Scalar::isFloatingType(writeType)) {
19718 masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp,
19719 volatileRegs);
19720 } else {
19721 if (value->isConstant()) {
19722 masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest);
19723 } else {
19724 masm.storeToTypedIntArray(writeType, ToRegister(value), dest);
19725 }
19726 }
19727}
19728
19729void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) {
19730 Register elements = ToRegister(lir->elements());
19731 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19732 const LAllocation* value = lir->value();
19733
19734 Scalar::Type writeType = lir->mir()->writeType();
19735
19736 LiveRegisterSet volatileRegs;
19737 if (MacroAssembler::StoreRequiresCall(writeType)) {
19738 volatileRegs = liveVolatileRegs(lir);
19739 }
19740
19741 auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType);
19742
19743 dest.match([&](const auto& dest) {
19744 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
19745 });
19746}
19747
19748template <typename T>
19749static inline void StoreToTypedBigIntArray(MacroAssembler& masm,
19750 const LInt64Allocation& value,
19751 const T& dest) {
19752 if (IsConstant(value)) {
19753 masm.storeToTypedBigIntArray(Imm64(ToInt64(value)), dest);
19754 } else {
19755 masm.storeToTypedBigIntArray(ToRegister64(value), dest);
19756 }
19757}
19758
19759void CodeGenerator::visitStoreUnboxedInt64(LStoreUnboxedInt64* lir) {
19760 Register elements = ToRegister(lir->elements());
19761 LInt64Allocation value = lir->value();
19762
19763 Scalar::Type writeType = lir->mir()->writeType();
19764 MOZ_ASSERT(Scalar::isBigIntType(writeType))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::isBigIntType(writeType))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(Scalar::isBigIntType(writeType
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("Scalar::isBigIntType(writeType)", "./../../../../js/src/jit/CodeGenerator.cpp"
, 19764); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(writeType)"
")"); do { MOZ_CrashSequence(__null, 19764); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19765
19766 auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType);
19767
19768 dest.match(
19769 [&](const auto& dest) { StoreToTypedBigIntArray(masm, value, dest); });
19770}
19771
19772void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) {
19773 Register elements = ToRegister(lir->elements());
19774 const LAllocation* value = lir->value();
19775 const LAllocation* littleEndian = lir->littleEndian();
19776 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19777 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp1());
19778
19779 Scalar::Type writeType = lir->mir()->writeType();
19780
19781 LiveRegisterSet volatileRegs;
19782 if (MacroAssembler::StoreRequiresCall(writeType)) {
19783 volatileRegs = liveVolatileRegs(lir);
19784 }
19785
19786 auto dest = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19787
19788 bool noSwap = IsNativeEndian(littleEndian);
19789
19790 // Directly store if no byte swap is needed and the platform supports
19791 // unaligned accesses for the access. (Such support is assumed for integer
19792 // types.)
19793 if (noSwap && (!Scalar::isFloatingType(writeType) ||
19794 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
19795 dest.match([&](const auto& dest) {
19796 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
19797 });
19798 return;
19799 }
19800
19801 // Load the value into a gpr register.
19802 switch (writeType) {
19803 case Scalar::Int16:
19804 case Scalar::Uint16:
19805 case Scalar::Int32:
19806 case Scalar::Uint32:
19807 if (value->isConstant()) {
19808 masm.move32(Imm32(ToInt32(value)), temp);
19809 } else {
19810 masm.move32(ToRegister(value), temp);
19811 }
19812 break;
19813 case Scalar::Float16: {
19814 FloatRegister fvalue = ToFloatRegister(value);
19815 masm.moveFloat16ToGPR(fvalue, temp, volatileRegs);
19816 break;
19817 }
19818 case Scalar::Float32: {
19819 FloatRegister fvalue = ToFloatRegister(value);
19820 masm.moveFloat32ToGPR(fvalue, temp);
19821 break;
19822 }
19823 case Scalar::Float64: {
19824 FloatRegister fvalue = ToFloatRegister(value);
19825 masm.moveDoubleToGPR64(fvalue, temp64);
19826 break;
19827 }
19828 case Scalar::Int8:
19829 case Scalar::Uint8:
19830 case Scalar::Uint8Clamped:
19831 case Scalar::BigInt64:
19832 case Scalar::BigUint64:
19833 default:
19834 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19834); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19834); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19835 }
19836
19837 if (!noSwap) {
19838 // Swap the bytes in the loaded value.
19839 Label skip;
19840 BranchIfNativeEndian(masm, littleEndian, &skip);
19841
19842 switch (writeType) {
19843 case Scalar::Int16:
19844 masm.byteSwap16SignExtend(temp);
19845 break;
19846 case Scalar::Uint16:
19847 case Scalar::Float16:
19848 masm.byteSwap16ZeroExtend(temp);
19849 break;
19850 case Scalar::Int32:
19851 case Scalar::Uint32:
19852 case Scalar::Float32:
19853 masm.byteSwap32(temp);
19854 break;
19855 case Scalar::Float64:
19856 masm.byteSwap64(temp64);
19857 break;
19858 case Scalar::Int8:
19859 case Scalar::Uint8:
19860 case Scalar::Uint8Clamped:
19861 case Scalar::BigInt64:
19862 case Scalar::BigUint64:
19863 default:
19864 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19864); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19864); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19865 }
19866
19867 if (skip.used()) {
19868 masm.bind(&skip);
19869 }
19870 }
19871
19872 // Store the value into the destination.
19873 dest.match([&](const auto& dest) {
19874 switch (writeType) {
19875 case Scalar::Int16:
19876 case Scalar::Uint16:
19877 case Scalar::Float16:
19878 masm.store16Unaligned(temp, dest);
19879 break;
19880 case Scalar::Int32:
19881 case Scalar::Uint32:
19882 case Scalar::Float32:
19883 masm.store32Unaligned(temp, dest);
19884 break;
19885 case Scalar::Float64:
19886 masm.store64Unaligned(temp64, dest);
19887 break;
19888 case Scalar::Int8:
19889 case Scalar::Uint8:
19890 case Scalar::Uint8Clamped:
19891 case Scalar::BigInt64:
19892 case Scalar::BigUint64:
19893 default:
19894 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19894); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19894); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19895 }
19896 });
19897}
19898
19899void CodeGenerator::visitStoreDataViewElement64(LStoreDataViewElement64* lir) {
19900 Register elements = ToRegister(lir->elements());
19901 LInt64Allocation value = lir->value();
19902 const LAllocation* littleEndian = lir->littleEndian();
19903 Register64 temp = ToTempRegister64OrInvalid(lir->temp0());
19904
19905 MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->writeType()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::isBigIntType(lir->mir()->writeType()))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(Scalar::isBigIntType(lir->mir()->writeType()))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Scalar::isBigIntType(lir->mir()->writeType())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19905); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->writeType())"
")"); do { MOZ_CrashSequence(__null, 19905); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19906
19907 auto dest = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19908
19909 bool noSwap = IsNativeEndian(littleEndian);
19910
19911 // Directly store if no byte swap is needed and the platform supports
19912 // unaligned accesses for the access. (Such support is assumed for integer
19913 // types.)
19914 if (noSwap) {
19915 dest.match(
19916 [&](const auto& dest) { StoreToTypedBigIntArray(masm, value, dest); });
19917 return;
19918 }
19919
19920 Register64 valueReg = Register64::Invalid();
19921 if (IsConstant(value)) {
19922 MOZ_ASSERT(temp != Register64::Invalid())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp != Register64::Invalid())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp != Register64::Invalid(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("temp != Register64::Invalid()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 19922); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp != Register64::Invalid()"
")"); do { MOZ_CrashSequence(__null, 19922); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19923 masm.move64(Imm64(ToInt64(value)), temp);
19924 } else {
19925 valueReg = ToRegister64(value);
19926
19927 // Preserve the input value.
19928 if (temp != Register64::Invalid()) {
19929 masm.move64(valueReg, temp);
19930 } else {
19931 masm.Push(valueReg);
19932 temp = valueReg;
19933 }
19934 }
19935
19936 // Swap the bytes in the loaded value.
19937 Label skip;
19938 BranchIfNativeEndian(masm, littleEndian, &skip);
19939
19940 masm.byteSwap64(temp);
19941
19942 if (skip.used()) {
19943 masm.bind(&skip);
19944 }
19945
19946 // Store the value into the destination.
19947 dest.match([&](const auto& dest) { masm.store64Unaligned(temp, dest); });
19948
19949 // Restore |value| if it was modified.
19950 if (valueReg == temp) {
19951 masm.Pop(valueReg);
19952 }
19953}
19954
19955void CodeGenerator::visitStoreTypedArrayElementHole(
19956 LStoreTypedArrayElementHole* lir) {
19957 Register elements = ToRegister(lir->elements());
19958 const LAllocation* value = lir->value();
19959
19960 Scalar::Type arrayType = lir->mir()->arrayType();
19961
19962 Register index = ToRegister(lir->index());
19963 const LAllocation* length = lir->length();
19964 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19965
19966 LiveRegisterSet volatileRegs;
19967 if (MacroAssembler::StoreRequiresCall(arrayType)) {
19968 volatileRegs = liveVolatileRegs(lir);
19969 }
19970
19971 Label skip;
19972 if (length->isGeneralReg()) {
19973 masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip);
19974 } else {
19975 masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip);
19976 }
19977
19978 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
19979 StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs);
19980
19981 masm.bind(&skip);
19982}
19983
19984void CodeGenerator::visitStoreTypedArrayElementHoleInt64(
19985 LStoreTypedArrayElementHoleInt64* lir) {
19986 Register elements = ToRegister(lir->elements());
19987 LInt64Allocation value = lir->value();
19988
19989 Scalar::Type arrayType = lir->mir()->arrayType();
19990 MOZ_ASSERT(Scalar::isBigIntType(arrayType))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::isBigIntType(arrayType))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(Scalar::isBigIntType(arrayType
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("Scalar::isBigIntType(arrayType)", "./../../../../js/src/jit/CodeGenerator.cpp"
, 19990); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(arrayType)"
")"); do { MOZ_CrashSequence(__null, 19990); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19991
19992 Register index = ToRegister(lir->index());
19993 const LAllocation* length = lir->length();
19994 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0());
19995
19996 Label skip;
19997 if (length->isGeneralReg()) {
19998 masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip);
19999 } else {
20000 masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip);
20001 }
20002
20003 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
20004 StoreToTypedBigIntArray(masm, value, dest);
20005
20006 masm.bind(&skip);
20007}
20008
20009void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) {
20010 masm.memoryBarrier(ins->barrier());
20011}
20012
20013void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) {
20014 Register value = ToRegister(lir->value());
20015 Register output = ToRegister(lir->output());
20016
20017 masm.atomicIsLockFreeJS(value, output);
20018}
20019
20020void CodeGenerator::visitAtomicPause(LAtomicPause* lir) { masm.atomicPause(); }
20021
20022void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) {
20023 Register output = ToRegister(lir->output());
20024 MOZ_ASSERT(output == ToRegister(lir->input()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output == ToRegister(lir->input()))>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(output == ToRegister(lir->input())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("output == ToRegister(lir->input())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 20024); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == ToRegister(lir->input())" ")"
); do { MOZ_CrashSequence(__null, 20024); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
20025 masm.clampIntToUint8(output);
20026}
20027
20028void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) {
20029 FloatRegister input = ToFloatRegister(lir->input());
20030 Register output = ToRegister(lir->output());
20031 masm.clampDoubleToUint8(input, output);
20032}
20033
20034void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) {
20035 ValueOperand operand = ToValue(lir->input());
20036 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
20037 Register output = ToRegister(lir->output());
20038
20039 using Fn = bool (*)(JSContext*, JSString*, double*);
20040 OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>(
20041 lir, ArgList(output), StoreFloatRegisterTo(tempFloat));
20042 Label* stringEntry = oolString->entry();
20043 Label* stringRejoin = oolString->rejoin();
20044
20045 Label fails;
20046 masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat,
20047 output, &fails);
20048
20049 bailoutFrom(&fails, lir->snapshot());
20050}
20051
20052void CodeGenerator::visitInCache(LInCache* ins) {
20053 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
20054
20055 ConstantOrRegister key =
20056 toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type());
20057 Register object = ToRegister(ins->rhs());
20058 Register output = ToRegister(ins->output());
20059 Register temp = ToRegister(ins->temp0());
20060
20061 IonInIC cache(liveRegs, key, object, output, temp);
20062 addIC(ins, allocateIC(cache));
20063}
20064
20065void CodeGenerator::visitInArray(LInArray* lir) {
20066 const MInArray* mir = lir->mir();
20067 Register elements = ToRegister(lir->elements());
20068 Register initLength = ToRegister(lir->initLength());
20069 Register output = ToRegister(lir->output());
20070
20071 Label falseBranch, done, trueBranch;
20072
20073 if (lir->index()->isConstant()) {
20074 int32_t index = ToInt32(lir->index());
20075
20076 if (index < 0) {
20077 MOZ_ASSERT(mir->needsNegativeIntCheck())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mir->needsNegativeIntCheck())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mir->needsNegativeIntCheck
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mir->needsNegativeIntCheck()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 20077); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()"
")"); do { MOZ_CrashSequence(__null, 20077); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20078 bailout(lir->snapshot());
20079 return;
20080 }
20081
20082 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index),
20083 &falseBranch);
20084
20085 NativeObject::elementsSizeMustNotOverflow();
20086 Address address = Address(elements, index * sizeof(Value));
20087 masm.branchTestMagic(Assembler::Equal, address, JS_ELEMENTS_HOLE,
20088 &falseBranch);
20089 } else {
20090 Register index = ToRegister(lir->index());
20091
20092 Label negativeIntCheck;
20093 Label* failedInitLength = &falseBranch;
20094 if (mir->needsNegativeIntCheck()) {
20095 failedInitLength = &negativeIntCheck;
20096 }
20097
20098 masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
20099
20100 BaseObjectElementIndex address(elements, index);
20101 masm.branchTestMagic(Assembler::Equal, address, JS_ELEMENTS_HOLE,
20102 &falseBranch);
20103
20104 if (mir->needsNegativeIntCheck()) {
20105 masm.jump(&trueBranch);
20106 masm.bind(&negativeIntCheck);
20107
20108 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
20109
20110 masm.jump(&falseBranch);
20111 }
20112 }
20113
20114 masm.bind(&trueBranch);
20115 masm.move32(Imm32(1), output);
20116 masm.jump(&done);
20117
20118 masm.bind(&falseBranch);
20119 masm.move32(Imm32(0), output);
20120 masm.bind(&done);
20121}
20122
20123void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) {
20124 Register elements = ToRegister(lir->elements());
20125 const LAllocation* index = lir->index();
20126
20127 auto source = ToAddressOrBaseObjectElementIndex(elements, index);
20128
20129 Label testMagic;
20130 source.match([&](const auto& source) {
20131 masm.branchTestMagic(Assembler::Equal, source, JS_ELEMENTS_HOLE,
20132 &testMagic);
20133 });
20134 bailoutFrom(&testMagic, lir->snapshot());
20135}
20136
20137void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) {
20138 Register protoReg = ToRegister(ins->rhs());
20139 emitInstanceOf(ins, protoReg);
20140}
20141
20142void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) {
20143 Register protoReg = ToRegister(ins->rhs());
20144 emitInstanceOf(ins, protoReg);
20145}
20146
20147void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) {
20148 // This path implements fun_hasInstance when the function's prototype is
20149 // known to be the object in protoReg
20150
20151 Label done;
20152 Register output = ToRegister(ins->getDef(0));
20153
20154 // If the lhs is a primitive, the result is false.
20155 Register objReg;
20156 if (ins->isInstanceOfV()) {
20157 Label isObject;
20158 ValueOperand lhsValue = ToValue(ins->toInstanceOfV()->lhs());
20159 masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
20160 masm.mov(ImmWord(0), output);
20161 masm.jump(&done);
20162 masm.bind(&isObject);
20163 objReg = masm.extractObject(lhsValue, output);
20164 } else {
20165 objReg = ToRegister(ins->toInstanceOfO()->lhs());
20166 }
20167
20168 // Crawl the lhs's prototype chain in a loop to search for prototypeObject.
20169 // This follows the main loop of js::IsPrototypeOf, though additionally breaks
20170 // out of the loop on Proxy::LazyProto.
20171
20172 // Load the lhs's prototype.
20173 masm.loadObjProto(objReg, output);
20174
20175 Label testLazy;
20176 {
20177 Label loopPrototypeChain;
20178 masm.bind(&loopPrototypeChain);
20179
20180 // Test for the target prototype object.
20181 Label notPrototypeObject;
20182 masm.branchPtr(Assembler::NotEqual, output, protoReg, &notPrototypeObject);
20183 masm.mov(ImmWord(1), output);
20184 masm.jump(&done);
20185 masm.bind(&notPrototypeObject);
20186
20187 MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 20187); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 20187); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
20188
20189 // Test for nullptr or Proxy::LazyProto
20190 masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
20191
20192 // Load the current object's prototype.
20193 masm.loadObjProto(output, output);
20194
20195 masm.jump(&loopPrototypeChain);
20196 }
20197
20198 // Make a VM call if an object with a lazy proto was found on the prototype
20199 // chain. This currently occurs only for cross compartment wrappers, which
20200 // we do not expect to be compared with non-wrapper functions from this
20201 // compartment. Otherwise, we stopped on a nullptr prototype and the output
20202 // register is already correct.
20203
20204 using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*);
20205 auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg),
20206 StoreRegisterTo(output));
20207
20208 // Regenerate the original lhs object for the VM call.
20209 Label regenerate, *lazyEntry;
20210 if (objReg != output) {
20211 lazyEntry = ool->entry();
20212 } else {
20213 masm.bind(&regenerate);
20214 lazyEntry = &regenerate;
20215 if (ins->isInstanceOfV()) {
20216 ValueOperand lhsValue = ToValue(ins->toInstanceOfV()->lhs());
20217 objReg = masm.extractObject(lhsValue, output);
20218 } else {
20219 objReg = ToRegister(ins->toInstanceOfO()->lhs());
20220 }
20221 MOZ_ASSERT(objReg == output)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(objReg == output)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(objReg == output))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("objReg == output"
, "./../../../../js/src/jit/CodeGenerator.cpp", 20221); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "objReg == output" ")"); do { MOZ_CrashSequence
(__null, 20221); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
20222 masm.jump(ool->entry());
20223 }
20224
20225 masm.bind(&testLazy);
20226 masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry);
20227
20228 masm.bind(&done);
20229 masm.bind(ool->rejoin());
20230}
20231
20232void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) {
20233 // The Lowering ensures that RHS is an object, and that LHS is a value.
20234 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
20235 TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(ins->obj()));
20236 Register rhs = ToRegister(ins->proto());
20237 Register output = ToRegister(ins->output());
20238
20239 IonInstanceOfIC ic(liveRegs, lhs, rhs, output);
20240 addIC(ins, allocateIC(ic));
20241}
20242
20243void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) {
20244 const Register JSContextReg = ToRegister(ins->temp0());
20245 const Register ObjectReg = ToRegister(ins->object());
20246 const Register PrivateReg = ToRegister(ins->temp1());
20247 const Register ValueReg = ToRegister(ins->temp2());
20248
20249 Label haveValue;
20250 if (ins->mir()->valueMayBeInSlot()) {
20251 size_t slot = ins->mir()->domMemberSlotIndex();
20252 // It's a bit annoying to redo these slot calculations, which duplcate
20253 // LSlots and a few other things like that, but I'm not sure there's a
20254 // way to reuse those here.
20255 //
20256 // If this ever gets fixed to work with proxies (by not assuming that
20257 // reserved slot indices, which is what domMemberSlotIndex() returns,
20258 // match fixed slot indices), we can reenable MGetDOMProperty for
20259 // proxies in IonBuilder.
20260 if (slot < NativeObject::MAX_FIXED_SLOTS) {
20261 masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)),
20262 JSReturnOperand);
20263 } else {
20264 // It's a dynamic slot.
20265 slot -= NativeObject::MAX_FIXED_SLOTS;
20266 // Use PrivateReg as a scratch register for the slots pointer.
20267 masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()),
20268 PrivateReg);
20269 masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)),
20270 JSReturnOperand);
20271 }
20272 masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue);
20273 }
20274
20275 DebugOnly<uint32_t> initialStack = masm.framePushed();
20276
20277 masm.checkStackAlignment();
20278
20279 // Make space for the outparam. Pre-initialize it to UndefinedValue so we
20280 // can trace it at GC time.
20281 masm.Push(UndefinedValue());
20282 // We pass the pointer to our out param as an instance of
20283 // JSJitGetterCallArgs, since on the binary level it's the same thing.
20284 static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
20285 masm.moveStackPtrTo(ValueReg);
20286
20287 masm.Push(ObjectReg);
20288
20289 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
20290
20291 // Rooting will happen at GC time.
20292 masm.moveStackPtrTo(ObjectReg);
20293
20294 Realm* getterRealm = ins->mir()->getterRealm();
20295 if (gen->realm->realmPtr() != getterRealm) {
20296 // We use JSContextReg as scratch register here.
20297 masm.switchToRealm(getterRealm, JSContextReg);
20298 }
20299
20300 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
20301 masm.loadJSContext(JSContextReg);
20302 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
20303 ExitFrameType::IonDOMGetter);
20304
20305 markSafepointAt(safepointOffset, ins);
20306
20307 masm.setupAlignedABICall();
20308 masm.loadJSContext(JSContextReg);
20309 masm.passABIArg(JSContextReg);
20310 masm.passABIArg(ObjectReg);
20311 masm.passABIArg(PrivateReg);
20312 masm.passABIArg(ValueReg);
20313 ensureOsiSpace();
20314 masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()),
20315 ABIType::General,
20316 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
20317
20318 if (ins->mir()->isInfallible()) {
20319 masm.loadValue(Address(masm.getStackPointer(),
20320 IonDOMExitFrameLayout::offsetOfResult()),
20321 JSReturnOperand);
20322 } else {
20323 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
20324
20325 masm.loadValue(Address(masm.getStackPointer(),
20326 IonDOMExitFrameLayout::offsetOfResult()),
20327 JSReturnOperand);
20328 }
20329
20330 // Switch back to the current realm if needed. Note: if the getter threw an
20331 // exception, the exception handler will do this.
20332 if (gen->realm->realmPtr() != getterRealm) {
20333 static_assert(!JSReturnOperand.aliases(ReturnReg),
20334 "Clobbering ReturnReg should not affect the return value");
20335 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
20336 }
20337
20338 // Until C++ code is instrumented against Spectre, prevent speculative
20339 // execution from returning any private data.
20340 if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) {
20341 masm.speculationBarrier();
20342 }
20343
20344 masm.adjustStack(IonDOMExitFrameLayout::Size());
20345
20346 masm.bind(&haveValue);
20347
20348 MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"masm.framePushed() == initialStack", "./../../../../js/src/jit/CodeGenerator.cpp"
, 20348); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 20348); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20349}
20350
20351void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) {
20352 // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to
20353 // use an LLoadFixedSlotV or some subclass of it for this case: that would
20354 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
20355 // we'd have to duplicate a bunch of stuff we now get for free from
20356 // MGetDOMProperty.
20357 //
20358 // If this ever gets fixed to work with proxies (by not assuming that
20359 // reserved slot indices, which is what domMemberSlotIndex() returns,
20360 // match fixed slot indices), we can reenable MGetDOMMember for
20361 // proxies in IonBuilder.
20362 Register object = ToRegister(ins->object());
20363 size_t slot = ins->mir()->domMemberSlotIndex();
20364 ValueOperand result = ToOutValue(ins);
20365
20366 masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
20367 result);
20368}
20369
20370void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) {
20371 // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to
20372 // use an LLoadFixedSlotT or some subclass of it for this case: that would
20373 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
20374 // we'd have to duplicate a bunch of stuff we now get for free from
20375 // MGetDOMProperty.
20376 //
20377 // If this ever gets fixed to work with proxies (by not assuming that
20378 // reserved slot indices, which is what domMemberSlotIndex() returns,
20379 // match fixed slot indices), we can reenable MGetDOMMember for
20380 // proxies in IonBuilder.
20381 Register object = ToRegister(ins->object());
20382 size_t slot = ins->mir()->domMemberSlotIndex();
20383 AnyRegister result = ToAnyRegister(ins->output());
20384 MIRType type = ins->mir()->type();
20385
20386 masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
20387 type, result);
20388}
20389
20390void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) {
20391 const Register JSContextReg = ToRegister(ins->temp0());
20392 const Register ObjectReg = ToRegister(ins->object());
20393 const Register PrivateReg = ToRegister(ins->temp1());
20394 const Register ValueReg = ToRegister(ins->temp2());
20395
20396 DebugOnly<uint32_t> initialStack = masm.framePushed();
20397
20398 masm.checkStackAlignment();
20399
20400 // Push the argument. Rooting will happen at GC time.
20401 ValueOperand argVal = ToValue(ins->value());
20402 masm.Push(argVal);
20403 // We pass the pointer to our out param as an instance of
20404 // JSJitGetterCallArgs, since on the binary level it's the same thing.
20405 static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
20406 masm.moveStackPtrTo(ValueReg);
20407
20408 masm.Push(ObjectReg);
20409
20410 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
20411
20412 // Rooting will happen at GC time.
20413 masm.moveStackPtrTo(ObjectReg);
20414
20415 Realm* setterRealm = ins->mir()->setterRealm();
20416 if (gen->realm->realmPtr() != setterRealm) {
20417 // We use JSContextReg as scratch register here.
20418 masm.switchToRealm(setterRealm, JSContextReg);
20419 }
20420
20421 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
20422 masm.loadJSContext(JSContextReg);
20423 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
20424 ExitFrameType::IonDOMSetter);
20425
20426 markSafepointAt(safepointOffset, ins);
20427
20428 masm.setupAlignedABICall();
20429 masm.loadJSContext(JSContextReg);
20430 masm.passABIArg(JSContextReg);
20431 masm.passABIArg(ObjectReg);
20432 masm.passABIArg(PrivateReg);
20433 masm.passABIArg(ValueReg);
20434 ensureOsiSpace();
20435 masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()),
20436 ABIType::General,
20437 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
20438
20439 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
20440
20441 // Switch back to the current realm if needed. Note: if the setter threw an
20442 // exception, the exception handler will do this.
20443 if (gen->realm->realmPtr() != setterRealm) {
20444 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
20445 }
20446
20447 masm.adjustStack(IonDOMExitFrameLayout::Size());
20448
20449 MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"masm.framePushed() == initialStack", "./../../../../js/src/jit/CodeGenerator.cpp"
, 20449); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 20449); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20450}
20451
20452void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) {
20453 Register proxy = ToRegister(ins->proxy());
20454 ValueOperand out = ToOutValue(ins);
20455
20456 masm.loadValue(Address(proxy, ProxyObject::offsetOfPrivateSlot()), out);
20457}
20458
20459void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration(
20460 LLoadDOMExpandoValueGuardGeneration* ins) {
20461 Register proxy = ToRegister(ins->proxy());
20462 ValueOperand out = ToOutValue(ins);
20463
20464 Label bail;
20465 masm.loadDOMExpandoValueGuardGeneration(proxy, out,
20466 ins->mir()->expandoAndGeneration(),
20467 ins->mir()->generation(), &bail);
20468 bailoutFrom(&bail, ins->snapshot());
20469}
20470
20471void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration(
20472 LLoadDOMExpandoValueIgnoreGeneration* ins) {
20473 Register proxy = ToRegister(ins->proxy());
20474 ValueOperand out = ToOutValue(ins);
20475
20476 // Load the ExpandoAndGeneration* from the PrivateValue.
20477 masm.loadPrivate(Address(proxy, ProxyObject::offsetOfPrivateSlot()),
20478 out.scratchReg());
20479
20480 // Load expandoAndGeneration->expando into the output Value register.
20481 masm.loadValue(
20482 Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out);
20483}
20484
20485void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape(
20486 LGuardDOMExpandoMissingOrGuardShape* ins) {
20487 Register temp = ToRegister(ins->temp0());
20488 ValueOperand input = ToValue(ins->expando());
20489
20490 Label done;
20491 masm.branchTestUndefined(Assembler::Equal, input, &done);
20492
20493 masm.debugAssertIsObject(input);
20494 masm.unboxObject(input, temp);
20495 // The expando object is not used in this case, so we don't need Spectre
20496 // mitigations.
20497 Label bail;
20498 masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp,
20499 ins->mir()->shape(), &bail);
20500 bailoutFrom(&bail, ins->snapshot());
20501
20502 masm.bind(&done);
20503}
20504
20505void CodeGenerator::emitIsCallableOOL(Register object, Register output) {
20506 saveVolatile(output);
20507 using Fn = bool (*)(JSObject* obj);
20508 masm.setupAlignedABICall();
20509 masm.passABIArg(object);
20510 masm.callWithABI<Fn, ObjectIsCallable>();
20511 masm.storeCallBoolResult(output);
20512 restoreVolatile(output);
20513}
20514
20515void CodeGenerator::visitIsCallableO(LIsCallableO* ins) {
20516 Register object = ToRegister(ins->object());
20517 Register output = ToRegister(ins->output());
20518
20519 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
20520 emitIsCallableOOL(object, output);
20521 masm.jump(ool.rejoin());
20522 });
20523 addOutOfLineCode(ool, ins->mir());
20524
20525 masm.isCallable(object, output, ool->entry());
20526
20527 masm.bind(ool->rejoin());
20528}
20529
20530void CodeGenerator::visitIsCallableV(LIsCallableV* ins) {
20531 ValueOperand val = ToValue(ins->object());
20532 Register output = ToRegister(ins->output());
20533 Register temp = ToRegister(ins->temp0());
20534
20535 Label notObject;
20536 masm.fallibleUnboxObject(val, temp, &notObject);
20537
20538 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
20539 emitIsCallableOOL(temp, output);
20540 masm.jump(ool.rejoin());
20541 });
20542 addOutOfLineCode(ool, ins->mir());
20543
20544 masm.isCallable(temp, output, ool->entry());
20545 masm.jump(ool->rejoin());
20546
20547 masm.bind(&notObject);
20548 masm.move32(Imm32(0), output);
20549
20550 masm.bind(ool->rejoin());
20551}
20552
20553void CodeGenerator::visitIsConstructor(LIsConstructor* ins) {
20554 Register object = ToRegister(ins->object());
20555 Register output = ToRegister(ins->output());
20556
20557 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
20558 saveVolatile(output);
20559 using Fn = bool (*)(JSObject* obj);
20560 masm.setupAlignedABICall();
20561 masm.passABIArg(object);
20562 masm.callWithABI<Fn, ObjectIsConstructor>();
20563 masm.storeCallBoolResult(output);
20564 restoreVolatile(output);
20565 masm.jump(ool.rejoin());
20566 });
20567 addOutOfLineCode(ool, ins->mir());
20568
20569 masm.isConstructor(object, output, ool->entry());
20570
20571 masm.bind(ool->rejoin());
20572}
20573
20574void CodeGenerator::visitIsCrossRealmArrayConstructor(
20575 LIsCrossRealmArrayConstructor* ins) {
20576 Register object = ToRegister(ins->object());
20577 Register output = ToRegister(ins->output());
20578
20579 masm.setIsCrossRealmArrayConstructor(object, output);
20580}
20581
20582static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool,
20583 Register obj, Register output,
20584 Label* notArray = nullptr) {
20585 masm.loadObjClassUnsafe(obj, output);
20586
20587 Label isArray;
20588 masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_),
20589 &isArray);
20590
20591 // Branch to OOL path if it's a proxy.
20592 masm.branchTestClassIsProxy(true, output, ool->entry());
20593
20594 if (notArray) {
20595 masm.bind(notArray);
20596 }
20597 masm.move32(Imm32(0), output);
20598 masm.jump(ool->rejoin());
20599
20600 masm.bind(&isArray);
20601 masm.move32(Imm32(1), output);
20602
20603 masm.bind(ool->rejoin());
20604}
20605
20606void CodeGenerator::visitIsArrayO(LIsArrayO* lir) {
20607 Register object = ToRegister(lir->object());
20608 Register output = ToRegister(lir->output());
20609
20610 using Fn = bool (*)(JSContext*, HandleObject, bool*);
20611 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
20612 lir, ArgList(object), StoreRegisterTo(output));
20613 EmitObjectIsArray(masm, ool, object, output);
20614}
20615
20616void CodeGenerator::visitIsArrayV(LIsArrayV* lir) {
20617 ValueOperand val = ToValue(lir->value());
20618 Register output = ToRegister(lir->output());
20619 Register temp = ToRegister(lir->temp0());
20620
20621 Label notArray;
20622 masm.fallibleUnboxObject(val, temp, &notArray);
20623
20624 using Fn = bool (*)(JSContext*, HandleObject, bool*);
20625 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
20626 lir, ArgList(temp), StoreRegisterTo(output));
20627 EmitObjectIsArray(masm, ool, temp, output, &notArray);
20628}
20629
20630void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) {
20631 Register object = ToRegister(lir->object());
20632 Register output = ToRegister(lir->output());
20633
20634 OutOfLineCode* ool = nullptr;
20635 if (lir->mir()->isPossiblyWrapped()) {
20636 using Fn = bool (*)(JSContext*, JSObject*, bool*);
20637 ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>(
20638 lir, ArgList(object), StoreRegisterTo(output));
20639 }
20640
20641 Label notTypedArray;
20642 Label done;
20643
20644 masm.loadObjClassUnsafe(object, output);
20645 masm.branchIfClassIsNotTypedArray(output, &notTypedArray);
20646
20647 masm.move32(Imm32(1), output);
20648 masm.jump(&done);
20649 masm.bind(&notTypedArray);
20650 if (ool) {
20651 Label notProxy;
20652 masm.branchTestClassIsProxy(false, output, &notProxy);
20653 masm.branchTestProxyHandlerFamily(Assembler::Equal, object, output,
20654 &Wrapper::family, ool->entry());
20655 masm.bind(&notProxy);
20656 }
20657 masm.move32(Imm32(0), output);
20658 masm.bind(&done);
20659 if (ool) {
20660 masm.bind(ool->rejoin());
20661 }
20662}
20663
20664void CodeGenerator::visitIsObject(LIsObject* ins) {
20665 Register output = ToRegister(ins->output());
20666 ValueOperand value = ToValue(ins->object());
20667 masm.testObjectSet(Assembler::Equal, value, output);
20668}
20669
20670void CodeGenerator::visitIsSuspendedGenerator(LIsSuspendedGenerator* lir) {
20671 Register obj = ToRegister(lir->object());
20672 Register output = ToRegister(lir->output());
20673 Register scratch = ToRegister(lir->temp0());
20674
20675 Label returnFalse, done;
20676 masm.branchIfNotSuspendedGenerator(obj, scratch, obj, &returnFalse);
20677
20678 masm.move32(Imm32(1), output);
20679 masm.jump(&done);
20680
20681 masm.bind(&returnFalse);
20682 masm.move32(Imm32(0), output);
20683
20684 masm.bind(&done);
20685}
20686
20687void CodeGenerator::visitIsSuspendedGeneratorAndBranch(
20688 LIsSuspendedGeneratorAndBranch* lir) {
20689 Register obj = ToRegister(lir->object());
20690 Register scratch = ToRegister(lir->temp0());
20691
20692 MBasicBlock* ifTrue = lir->ifTrue();
20693 MBasicBlock* ifFalse = lir->ifFalse();
20694
20695 masm.branchIfNotSuspendedGenerator(obj, scratch, obj,
20696 getJumpLabelForBranch(ifFalse));
20697 jumpToBlock(ifTrue);
20698}
20699
20700void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) {
20701 ValueOperand value = ToValue(ins->input());
20702
20703 MBasicBlock* ifTrue = ins->ifTrue();
20704 MBasicBlock* ifFalse = ins->ifFalse();
20705
20706 if (isNextBlock(ifFalse->lir())) {
20707 masm.branchTestObject(Assembler::Equal, value,
20708 getJumpLabelForBranch(ifTrue));
20709 } else {
20710 masm.branchTestObject(Assembler::NotEqual, value,
20711 getJumpLabelForBranch(ifFalse));
20712 jumpToBlock(ifTrue);
20713 }
20714}
20715
20716void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) {
20717 Register output = ToRegister(ins->output());
20718 ValueOperand value = ToValue(ins->value());
20719
20720 Label isNotNull, done;
20721 masm.branchTestNull(Assembler::NotEqual, value, &isNotNull);
20722
20723 masm.move32(Imm32(1), output);
20724 masm.jump(&done);
20725
20726 masm.bind(&isNotNull);
20727 masm.testUndefinedSet(Assembler::Equal, value, output);
20728
20729 masm.bind(&done);
20730}
20731
20732void CodeGenerator::visitIsNullOrUndefinedAndBranch(
20733 LIsNullOrUndefinedAndBranch* ins) {
20734 Label* ifTrue = getJumpLabelForBranch(ins->ifTrue());
20735 Label* ifFalse = getJumpLabelForBranch(ins->ifFalse());
20736 ValueOperand value = ToValue(ins->input());
20737
20738 ScratchTagScope tag(masm, value);
20739 masm.splitTagForTest(value, tag);
20740
20741 masm.branchTestNull(Assembler::Equal, tag, ifTrue);
20742 masm.branchTestUndefined(Assembler::Equal, tag, ifTrue);
20743
20744 if (!isNextBlock(ins->ifFalse()->lir())) {
20745 masm.jump(ifFalse);
20746 }
20747}
20748
20749void CodeGenerator::visitHasClass(LHasClass* ins) {
20750 Register lhs = ToRegister(ins->lhs());
20751 Register output = ToRegister(ins->output());
20752
20753 masm.loadObjClassUnsafe(lhs, output);
20754 masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()),
20755 output);
20756}
20757
20758void CodeGenerator::visitHasShape(LHasShape* ins) {
20759 Register obj = ToRegister(ins->object());
20760 Register output = ToRegister(ins->output());
20761
20762 // Note: no Spectre mitigations are needed here because this shape check only
20763 // affects correctness.
20764 masm.loadObjShapeUnsafe(obj, output);
20765 masm.cmpPtrSet(Assembler::Equal, output, ImmGCPtr(ins->mir()->shape()),
20766 output);
20767}
20768
20769void CodeGenerator::visitGuardToClass(LGuardToClass* ins) {
20770 Register lhs = ToRegister(ins->lhs());
20771 Register temp = ToRegister(ins->temp0());
20772
20773 // branchTestObjClass may zero the object register on speculative paths
20774 // (we should have a defineReuseInput allocation in this case).
20775 Register spectreRegToZero = lhs;
20776
20777 Label notEqual;
20778
20779 masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(),
20780 temp, spectreRegToZero, &notEqual);
20781
20782 // Can't return null-return here, so bail.
20783 bailoutFrom(&notEqual, ins->snapshot());
20784}
20785
20786void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) {
20787 Register lhs = ToRegister(ins->lhs());
20788 Register temp = ToRegister(ins->temp0());
20789
20790 // branchTestObjClass may zero the object register on speculative paths
20791 // (we should have a defineReuseInput allocation in this case).
20792 Register spectreRegToZero = lhs;
20793
20794 Label notEqual;
20795
20796 masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero,
20797 &notEqual);
20798
20799 // Can't return null-return here, so bail.
20800 bailoutFrom(&notEqual, ins->snapshot());
20801}
20802
20803void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) {
20804 Register obj = ToRegister(lir->object());
20805 Register temp = ToRegister(lir->temp0());
20806
20807 using Fn = JSString* (*)(JSContext*, JSObject*);
20808 masm.setupAlignedABICall();
20809 masm.loadJSContext(temp);
20810 masm.passABIArg(temp);
20811 masm.passABIArg(obj);
20812 masm.callWithABI<Fn, js::ObjectClassToString>();
20813
20814 bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot());
20815}
20816
20817void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {}
20818
20819void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {}
20820
20821void CodeGenerator::visitWasmReturn(LWasmReturn* lir) {
20822 // Don't emit a jump to the return label if this is the last block.
20823 if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) {
20824 masm.jump(&returnLabel_);
20825 }
20826}
20827
20828void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) {
20829 // Don't emit a jump to the return label if this is the last block.
20830 if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) {
20831 masm.jump(&returnLabel_);
20832 }
20833}
20834
20835void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) {
20836 // Don't emit a jump to the return label if this is the last block.
20837 if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) {
20838 masm.jump(&returnLabel_);
20839 }
20840}
20841
20842void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r,
20843 Register input) {
20844 // Check the lower bound.
20845 if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) {
20846 Label success;
20847 if (type == MIRType::Int32 || type == MIRType::Boolean) {
20848 masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
20849 &success);
20850 } else {
20851 MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 20851); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 20851); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
20852 masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
20853 &success);
20854 }
20855 masm.assumeUnreachable(
20856 "Integer input should be equal or higher than Lowerbound.");
20857 masm.bind(&success);
20858 }
20859
20860 // Check the upper bound.
20861 if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) {
20862 Label success;
20863 if (type == MIRType::Int32 || type == MIRType::Boolean) {
20864 masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
20865 &success);
20866 } else {
20867 MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 20867); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 20867); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
20868 masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
20869 &success);
20870 }
20871 masm.assumeUnreachable(
20872 "Integer input should be lower or equal than Upperbound.");
20873 masm.bind(&success);
20874 }
20875
20876 // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and
20877 // r->exponent(), there's nothing to check, because if we ended up in the
20878 // integer range checking code, the value is already in an integer register
20879 // in the integer range.
20880}
20881
20882void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input,
20883 FloatRegister temp) {
20884 // Check the lower bound.
20885 if (r->hasInt32LowerBound()) {
20886 Label success;
20887 masm.loadConstantDouble(r->lower(), temp);
20888 if (r->canBeNaN()) {
20889 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
20890 }
20891 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
20892 &success);
20893 masm.assumeUnreachable(
20894 "Double input should be equal or higher than Lowerbound.");
20895 masm.bind(&success);
20896 }
20897 // Check the upper bound.
20898 if (r->hasInt32UpperBound()) {
20899 Label success;
20900 masm.loadConstantDouble(r->upper(), temp);
20901 if (r->canBeNaN()) {
20902 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
20903 }
20904 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
20905 masm.assumeUnreachable(
20906 "Double input should be lower or equal than Upperbound.");
20907 masm.bind(&success);
20908 }
20909
20910 // This code does not yet check r->canHaveFractionalPart(). This would require
20911 // new assembler interfaces to make rounding instructions available.
20912
20913 if (!r->canBeNegativeZero()) {
20914 Label success;
20915
20916 // First, test for being equal to 0.0, which also includes -0.0.
20917 masm.loadConstantDouble(0.0, temp);
20918 masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp,
20919 &success);
20920
20921 // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is
20922 // -Infinity instead of Infinity.
20923 masm.loadConstantDouble(1.0, temp);
20924 masm.divDouble(input, temp);
20925 masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success);
20926
20927 masm.assumeUnreachable("Input shouldn't be negative zero.");
20928
20929 masm.bind(&success);
20930 }
20931
20932 if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() &&
20933 r->exponent() < FloatingPoint<double>::kExponentBias) {
20934 // Check the bounds implied by the maximum exponent.
20935 Label exponentLoOk;
20936 masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
20937 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
20938 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp,
20939 &exponentLoOk);
20940 masm.assumeUnreachable("Check for exponent failed.");
20941 masm.bind(&exponentLoOk);
20942
20943 Label exponentHiOk;
20944 masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp);
20945 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
20946 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
20947 &exponentHiOk);
20948 masm.assumeUnreachable("Check for exponent failed.");
20949 masm.bind(&exponentHiOk);
20950 } else if (!r->hasInt32Bounds() && !r->canBeNaN()) {
20951 // If we think the value can't be NaN, check that it isn't.
20952 Label notnan;
20953 masm.branchDouble(Assembler::DoubleOrdered, input, input, &notnan);
20954 masm.assumeUnreachable("Input shouldn't be NaN.");
20955 masm.bind(&notnan);
20956
20957 // If we think the value also can't be an infinity, check that it isn't.
20958 if (!r->canBeInfiniteOrNaN()) {
20959 Label notposinf;
20960 masm.loadConstantDouble(PositiveInfinity<double>(), temp);
20961 masm.branchDouble(Assembler::DoubleLessThan, input, temp, &notposinf);
20962 masm.assumeUnreachable("Input shouldn't be +Inf.");
20963 masm.bind(&notposinf);
20964
20965 Label notneginf;
20966 masm.loadConstantDouble(NegativeInfinity<double>(), temp);
20967 masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, &notneginf);
20968 masm.assumeUnreachable("Input shouldn't be -Inf.");
20969 masm.bind(&notneginf);
20970 }
20971 }
20972}
20973
20974void CodeGenerator::visitAssertClass(LAssertClass* ins) {
20975 Register obj = ToRegister(ins->input());
20976 Register temp = ToRegister(ins->temp0());
20977
20978 Label success;
20979 if (ins->mir()->getClass() == &FunctionClass) {
20980 // Allow both possible function classes here.
20981 masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj,
20982 temp, &success);
20983 } else {
20984 masm.branchTestObjClassNoSpectreMitigations(
20985 Assembler::Equal, obj, ins->mir()->getClass(), temp, &success);
20986 }
20987 masm.assumeUnreachable("Wrong KnownClass during run-time");
20988 masm.bind(&success);
20989}
20990
20991void CodeGenerator::visitAssertShape(LAssertShape* ins) {
20992 Register obj = ToRegister(ins->object());
20993
20994 Label success;
20995 masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj,
20996 ins->mir()->shape(), &success);
20997 masm.assumeUnreachable("Wrong Shape during run-time");
20998 masm.bind(&success);
20999}
21000
21001void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) {
21002 Register input = ToRegister(ins->input());
21003 const Range* r = ins->mir()->assertedRange();
21004
21005 emitAssertRangeI(ins->mir()->input()->type(), r, input);
21006}
21007
21008void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) {
21009 FloatRegister input = ToFloatRegister(ins->input());
21010 FloatRegister temp = ToFloatRegister(ins->temp0());
21011 const Range* r = ins->mir()->assertedRange();
21012
21013 emitAssertRangeD(r, input, temp);
21014}
21015
21016void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) {
21017 FloatRegister input = ToFloatRegister(ins->input());
21018 FloatRegister temp = ToFloatRegister(ins->temp0());
21019 FloatRegister temp2 = ToFloatRegister(ins->temp1());
21020
21021 const Range* r = ins->mir()->assertedRange();
21022
21023 masm.convertFloat32ToDouble(input, temp);
21024 emitAssertRangeD(r, temp, temp2);
21025}
21026
21027void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) {
21028 const Range* r = ins->mir()->assertedRange();
21029 ValueOperand value = ToValue(ins->input());
21030 Label done;
21031
21032 {
21033 ScratchTagScope tag(masm, value);
21034 masm.splitTagForTest(value, tag);
21035
21036 {
21037 Label isNotInt32;
21038 masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
21039 {
21040 ScratchTagScopeRelease _(&tag);
21041 Register unboxInt32 = ToTempUnboxRegister(ins->temp0());
21042 Register input = masm.extractInt32(value, unboxInt32);
21043 emitAssertRangeI(MIRType::Int32, r, input);
21044 masm.jump(&done);
21045 }
21046 masm.bind(&isNotInt32);
21047 }
21048
21049 {
21050 Label isNotDouble;
21051 masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
21052 {
21053 ScratchTagScopeRelease _(&tag);
21054 FloatRegister input = ToFloatRegister(ins->temp1());
21055 FloatRegister temp = ToFloatRegister(ins->temp2());
21056 masm.unboxDouble(value, input);
21057 emitAssertRangeD(r, input, temp);
21058 masm.jump(&done);
21059 }
21060 masm.bind(&isNotDouble);
21061 }
21062 }
21063
21064 masm.assumeUnreachable("Incorrect range for Value.");
21065 masm.bind(&done);
21066}
21067
21068void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) {
21069 using Fn = bool (*)(JSContext*);
21070 OutOfLineCode* ool =
21071 oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing());
21072
21073 const void* interruptAddr = gen->runtime->addressOfInterruptBits();
21074 masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0),
21075 ool->entry());
21076 masm.bind(ool->rejoin());
21077}
21078
21079void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) {
21080 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21080); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21080); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21081
21082 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21083 emitResumableWasmTrapOOL(lir, masm.framePushed(),
21084 lir->mir()->trapSiteDesc(),
21085 wasm::Trap::CheckInterrupt);
21086 masm.jump(ool.rejoin());
21087 });
21088 addOutOfLineCode(ool, lir->mir());
21089 masm.branch32(
21090 Assembler::NotEqual,
21091 Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()),
21092 Imm32(0), ool->entry());
21093 masm.bind(ool->rejoin());
21094}
21095
21096void CodeGenerator::visitWasmTrap(LWasmTrap* lir) {
21097 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21097); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21097); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21098 const MWasmTrap* mir = lir->mir();
21099
21100 masm.wasmTrap(mir->trap(), mir->trapSiteDesc());
21101}
21102
21103void CodeGenerator::visitWasmRefAsNonNull(LWasmRefAsNonNull* lir) {
21104 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21104); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21104); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21105 const MWasmRefAsNonNull* mir = lir->mir();
21106 Label nonNull;
21107 Register ref = ToRegister(lir->ref());
21108
21109 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21110 masm.wasmTrap(wasm::Trap::NullPointerDereference, mir->trapSiteDesc());
21111 });
21112 addOutOfLineCode(ool, mir);
21113 masm.branchWasmAnyRefIsNull(true, ref, ool->entry());
21114}
21115
21116void CodeGenerator::visitWasmRefTestAbstract(LWasmRefTestAbstract* ins) {
21117 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21117); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21117); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21118
21119 const MWasmRefTestAbstract* mir = ins->mir();
21120 MOZ_ASSERT(!mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mir->destType().isTypeRef())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mir->destType().isTypeRef
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mir->destType().isTypeRef()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 21120); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21120); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21121
21122 Register ref = ToRegister(ins->ref());
21123 Register superSTV = Register::Invalid();
21124 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
21125 Register scratch2 = Register::Invalid();
21126 Register result = ToRegister(ins->output());
21127 Label onSuccess;
21128 Label onFail;
21129 Label join;
21130 masm.branchWasmRefIsSubtype(ref, mir->ref()->wasmRefType(), mir->destType(),
21131 &onSuccess,
21132 /*onSuccess=*/true, /*signalNullChecks=*/false,
21133 superSTV, scratch1, scratch2);
21134 masm.bind(&onFail);
21135 masm.xor32(result, result);
21136 masm.jump(&join);
21137 masm.bind(&onSuccess);
21138 masm.move32(Imm32(1), result);
21139 masm.bind(&join);
21140}
21141
21142void CodeGenerator::visitWasmRefTestConcrete(LWasmRefTestConcrete* ins) {
21143 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21143); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21143); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21144
21145 const MWasmRefTestConcrete* mir = ins->mir();
21146 MOZ_ASSERT(mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mir->destType().isTypeRef())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mir->destType().isTypeRef
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mir->destType().isTypeRef()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 21146); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21146); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21147
21148 Register ref = ToRegister(ins->ref());
21149 Register superSTV = ToRegister(ins->superSTV());
21150 Register scratch1 = ToRegister(ins->temp0());
21151 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
21152 Register result = ToRegister(ins->output());
21153 Label onSuccess;
21154 Label join;
21155 masm.branchWasmRefIsSubtype(ref, mir->ref()->wasmRefType(), mir->destType(),
21156 &onSuccess,
21157 /*onSuccess=*/true, /*signalNullChecks=*/false,
21158 superSTV, scratch1, scratch2);
21159 masm.move32(Imm32(0), result);
21160 masm.jump(&join);
21161 masm.bind(&onSuccess);
21162 masm.move32(Imm32(1), result);
21163 masm.bind(&join);
21164}
21165
21166void CodeGenerator::visitWasmRefTestAbstractAndBranch(
21167 LWasmRefTestAbstractAndBranch* ins) {
21168 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21168); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21168); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21169 Register ref = ToRegister(ins->ref());
21170 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
21171 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
21172 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
21173 masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(),
21174 onSuccess, /*onSuccess=*/true,
21175 /*signalNullChecks=*/false, Register::Invalid(),
21176 scratch1, Register::Invalid());
21177 masm.jump(onFail);
21178}
21179
21180void CodeGenerator::visitWasmRefTestConcreteAndBranch(
21181 LWasmRefTestConcreteAndBranch* ins) {
21182 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21182); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21182); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21183 Register ref = ToRegister(ins->ref());
21184 Register superSTV = ToRegister(ins->superSTV());
21185 Register scratch1 = ToRegister(ins->temp0());
21186 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
21187 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
21188 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
21189 masm.branchWasmRefIsSubtype(
21190 ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true,
21191 /*signalNullChecks=*/false, superSTV, scratch1, scratch2);
21192 masm.jump(onFail);
21193}
21194
21195void CodeGenerator::visitWasmRefCastAbstract(LWasmRefCastAbstract* ins) {
21196 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21196); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21196); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21197
21198 const MWasmRefCastAbstract* mir = ins->mir();
21199 MOZ_ASSERT(!mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mir->destType().isTypeRef())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mir->destType().isTypeRef
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mir->destType().isTypeRef()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 21199); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21199); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21200
21201 Register ref = ToRegister(ins->ref());
21202 Register superSTV = Register::Invalid();
21203 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
21204 Register scratch2 = Register::Invalid();
21205 MOZ_ASSERT(ref == ToRegister(ins->output()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ref == ToRegister(ins->output()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ref == ToRegister(ins->output
())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ref == ToRegister(ins->output())", "./../../../../js/src/jit/CodeGenerator.cpp"
, 21205); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref == ToRegister(ins->output())"
")"); do { MOZ_CrashSequence(__null, 21205); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21206 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21207 masm.wasmTrap(wasm::Trap::BadCast, mir->trapSiteDesc());
21208 });
21209 addOutOfLineCode(ool, ins->mir());
21210 FaultingCodeRange fcr = masm.branchWasmRefIsSubtype(
21211 ref, mir->ref()->wasmRefType(), mir->destType(), ool->entry(),
21212 /*onSuccess=*/false, /*signalNullChecks=*/true, superSTV, scratch1,
21213 scratch2);
21214 if (fcr.isValid()) {
21215 masm.appendAndVerify(wasm::Trap::BadCast,
21216 wasm::TrapMachineInsnForLoadWord(), fcr,
21217 mir->trapSiteDesc());
21218 }
21219}
21220
21221void CodeGenerator::visitWasmRefCastConcrete(LWasmRefCastConcrete* ins) {
21222 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21222); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21222); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21223
21224 const MWasmRefCastConcrete* mir = ins->mir();
21225 MOZ_ASSERT(mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mir->destType().isTypeRef())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mir->destType().isTypeRef
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mir->destType().isTypeRef()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 21225); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21225); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21226
21227 Register ref = ToRegister(ins->ref());
21228 Register superSTV = ToRegister(ins->superSTV());
21229 Register scratch1 = ToRegister(ins->temp0());
21230 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
21231 MOZ_ASSERT(ref == ToRegister(ins->output()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ref == ToRegister(ins->output()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ref == ToRegister(ins->output
())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ref == ToRegister(ins->output())", "./../../../../js/src/jit/CodeGenerator.cpp"
, 21231); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref == ToRegister(ins->output())"
")"); do { MOZ_CrashSequence(__null, 21231); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21232 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21233 masm.wasmTrap(wasm::Trap::BadCast, mir->trapSiteDesc());
21234 });
21235 addOutOfLineCode(ool, ins->mir());
21236 FaultingCodeRange fcr = masm.branchWasmRefIsSubtype(
21237 ref, mir->ref()->wasmRefType(), mir->destType(), ool->entry(),
21238 /*onSuccess=*/false, /*signalNullChecks=*/true, superSTV, scratch1,
21239 scratch2);
21240 if (fcr.isValid()) {
21241 masm.appendAndVerify(wasm::Trap::BadCast,
21242 wasm::TrapMachineInsnForLoadWord(), fcr,
21243 mir->trapSiteDesc());
21244 }
21245}
21246
21247void CodeGenerator::callWasmStructAllocFun(
21248 LInstruction* lir, wasm::SymbolicAddress fun, Register typeDefIndex,
21249 Register allocSite, Register output,
21250 const wasm::TrapSiteDesc& trapSiteDesc) {
21251 MOZ_ASSERT(fun == wasm::SymbolicAddress::StructNewIL_true ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { MOZ_CrashSequence(__null, 21254); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21252 fun == wasm::SymbolicAddress::StructNewIL_false ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { MOZ_CrashSequence(__null, 21254); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21253 fun == wasm::SymbolicAddress::StructNewOOL_true ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { MOZ_CrashSequence(__null, 21254); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21254 fun == wasm::SymbolicAddress::StructNewOOL_false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { MOZ_CrashSequence(__null, 21254); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21255 MOZ_ASSERT(wasm::SASigStructNewIL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21256); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21256); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21256 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21256); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21256); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21257 MOZ_ASSERT(wasm::SASigStructNewIL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21258); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21258); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21258 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21258); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21258); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21259 MOZ_ASSERT(wasm::SASigStructNewOOL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21260); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21260); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21260 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21260); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21260); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21261 MOZ_ASSERT(wasm::SASigStructNewOOL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false
.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21262); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21262); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21262 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false
.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21262); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21262); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21263
21264 masm.Push(InstanceReg);
21265 int32_t framePushedAfterInstance = masm.framePushed();
21266 saveLive(lir);
21267
21268 masm.setupWasmABICall(fun);
21269 masm.passABIArg(InstanceReg);
21270 masm.passABIArg(typeDefIndex);
21271 masm.passABIArg(allocSite);
21272 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
21273 CodeOffset offset =
21274 masm.callWithABI(trapSiteDesc.bytecodeOffset, fun,
21275 mozilla::Some(instanceOffset), ABIType::General);
21276 masm.storeCallPointerResult(output);
21277
21278 markSafepointAt(offset.offset(), lir);
21279 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
21280 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
21281
21282 restoreLive(lir);
21283 masm.Pop(InstanceReg);
21284#if JS_CODEGEN_ARM64
21285 masm.syncStackPtr();
21286#endif
21287
21288 masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr,
21289 wasm::Trap::ThrowReported, trapSiteDesc);
21290}
21291
21292void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) {
21293 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21293); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21293); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21294
21295 MWasmNewStructObject* mir = lir->mir();
21296 uint32_t typeDefIndex = wasmCodeMeta()->types->indexOf(mir->typeDef());
21297
21298 Register allocSite = ToRegister(lir->allocSite());
21299 Register output = ToRegister(lir->output());
21300 Register temp = ToRegister(lir->temp0());
21301
21302 if (mir->isOutline()) {
21303 wasm::SymbolicAddress fun = mir->zeroFields()
21304 ? wasm::SymbolicAddress::StructNewOOL_true
21305 : wasm::SymbolicAddress::StructNewOOL_false;
21306
21307 masm.move32(Imm32(typeDefIndex), temp);
21308 callWasmStructAllocFun(lir, fun, temp, allocSite, output,
21309 mir->trapSiteDesc());
21310 } else {
21311 wasm::SymbolicAddress fun = mir->zeroFields()
21312 ? wasm::SymbolicAddress::StructNewIL_true
21313 : wasm::SymbolicAddress::StructNewIL_false;
21314
21315 Register instance = ToRegister(lir->instance());
21316 MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(instance == InstanceReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21316); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence
(__null, 21316); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21317
21318 auto* ool =
21319 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21320 masm.move32(Imm32(typeDefIndex), temp);
21321 callWasmStructAllocFun(lir, fun, temp, allocSite, output,
21322 mir->trapSiteDesc());
21323 masm.jump(ool.rejoin());
21324 });
21325 addOutOfLineCode(ool, lir->mir());
21326
21327 size_t offsetOfTypeDefData = wasm::Instance::offsetInData(
21328 wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex));
21329 masm.wasmNewStructObject(instance, output, allocSite, temp,
21330 offsetOfTypeDefData, ool->entry(),
21331 mir->allocKind(), mir->zeroFields());
21332
21333 masm.bind(ool->rejoin());
21334 }
21335}
21336
21337void CodeGenerator::callWasmArrayAllocFun(
21338 LInstruction* lir, wasm::SymbolicAddress fun, Register numElements,
21339 Register typeDefIndex, Register allocSite, Register output,
21340 const wasm::TrapSiteDesc& trapSiteDesc) {
21341 MOZ_ASSERT(fun == wasm::SymbolicAddress::ArrayNew_true ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun ==
wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21342); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
")"); do { MOZ_CrashSequence(__null, 21342); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21342 fun == wasm::SymbolicAddress::ArrayNew_false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun ==
wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21342); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
")"); do { MOZ_CrashSequence(__null, 21342); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21343 MOZ_ASSERT(wasm::SASigArrayNew_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21344); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21344); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21344 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21344); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21344); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21345 MOZ_ASSERT(wasm::SASigArrayNew_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21346); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21346); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21346 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21346); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21346); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21347
21348 masm.Push(InstanceReg);
21349 int32_t framePushedAfterInstance = masm.framePushed();
21350 saveLive(lir);
21351
21352 masm.setupWasmABICall(fun);
21353 masm.passABIArg(InstanceReg);
21354 masm.passABIArg(numElements);
21355 masm.passABIArg(typeDefIndex);
21356 masm.passABIArg(allocSite);
21357 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
21358 CodeOffset offset =
21359 masm.callWithABI(trapSiteDesc.bytecodeOffset, fun,
21360 mozilla::Some(instanceOffset), ABIType::General);
21361 masm.storeCallPointerResult(output);
21362
21363 markSafepointAt(offset.offset(), lir);
21364 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
21365 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
21366
21367 restoreLive(lir);
21368 masm.Pop(InstanceReg);
21369#if JS_CODEGEN_ARM64
21370 masm.syncStackPtr();
21371#endif
21372
21373 masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr,
21374 wasm::Trap::ThrowReported, trapSiteDesc);
21375}
21376
21377void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) {
21378 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21378); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21378); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21379
21380 MWasmNewArrayObject* mir = lir->mir();
21381 uint32_t typeDefIndex = wasmCodeMeta()->types->indexOf(mir->typeDef());
21382
21383 Register allocSite = ToRegister(lir->allocSite());
21384 Register output = ToRegister(lir->output());
21385 Register temp0 = ToRegister(lir->temp0());
21386 Register temp1 = ToRegister(lir->temp1());
21387
21388 wasm::SymbolicAddress fun = mir->zeroFields()
21389 ? wasm::SymbolicAddress::ArrayNew_true
21390 : wasm::SymbolicAddress::ArrayNew_false;
21391
21392 if (lir->numElements()->isConstant()) {
21393 // numElements is constant, so we can do optimized code generation.
21394 uint32_t numElements = lir->numElements()->toConstant()->toInt32();
21395 CheckedUint32 arrayDataBytes = WasmArrayObject::calcArrayDataBytesChecked(
21396 mir->elemSize(), numElements);
21397 if (!arrayDataBytes.isValid() ||
21398 arrayDataBytes.value() > WasmArrayObject_MaxInlineBytes) {
21399 // Too much array data to store inline. Immediately perform an instance
21400 // call to handle the out-of-line storage (or the trap).
21401 masm.move32(Imm32(typeDefIndex), temp0);
21402 masm.move32(Imm32(numElements), temp1);
21403 callWasmArrayAllocFun(lir, fun, temp1, temp0, allocSite, output,
21404 mir->trapSiteDesc());
21405 } else {
21406 // arrayDataBytes is small enough to be stored inline in WasmArrayObject.
21407 // Attempt a nursery allocation and fall back to an instance call if it
21408 // fails.
21409 Register instance = ToRegister(lir->instance());
21410 MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(instance == InstanceReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21410); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence
(__null, 21410); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21411
21412 auto* ool =
21413 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21414 masm.move32(Imm32(typeDefIndex), temp0);
21415 masm.move32(Imm32(numElements), temp1);
21416 callWasmArrayAllocFun(lir, fun, temp1, temp0, allocSite, output,
21417 mir->trapSiteDesc());
21418 masm.jump(ool.rejoin());
21419 });
21420 addOutOfLineCode(ool, lir->mir());
21421
21422 size_t offsetOfTypeDefData = wasm::Instance::offsetInData(
21423 wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex));
21424 masm.wasmNewArrayObjectFixed(
21425 instance, output, allocSite, temp0, temp1, offsetOfTypeDefData,
21426 ool->entry(), numElements, arrayDataBytes.value(), mir->zeroFields());
21427
21428 masm.bind(ool->rejoin());
21429 }
21430 } else {
21431 // numElements is dynamic. Attempt a dynamic inline-storage nursery
21432 // allocation and fall back to an instance call if it fails.
21433 Register instance = ToRegister(lir->instance());
21434 MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(instance == InstanceReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21434); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence
(__null, 21434); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21435 Register numElements = ToRegister(lir->numElements());
21436
21437 auto* ool =
21438 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21439 masm.move32(Imm32(typeDefIndex), temp0);
21440 callWasmArrayAllocFun(lir, fun, numElements, temp0, allocSite, output,
21441 mir->trapSiteDesc());
21442 masm.jump(ool.rejoin());
21443 });
21444 addOutOfLineCode(ool, lir->mir());
21445
21446 size_t offsetOfTypeDefData = wasm::Instance::offsetInData(
21447 wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex));
21448 masm.wasmNewArrayObject(instance, output, numElements, allocSite, temp1,
21449 offsetOfTypeDefData, ool->entry(), mir->elemSize(),
21450 mir->zeroFields());
21451
21452 masm.bind(ool->rejoin());
21453 }
21454}
21455
21456void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) {
21457#ifdef WASM_HAS_HEAPREG1
21458 masm.movePtr(HeapReg, ToRegister(ins->output()));
21459#else
21460 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 21460); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 21460); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
21461#endif
21462}
21463
21464void CodeGenerator::emitResumableWasmTrapOOL(
21465 LInstruction* lir, size_t framePushed,
21466 const wasm::TrapSiteDesc& trapSiteDesc, wasm::Trap trap) {
21467 masm.wasmTrap(trap, trapSiteDesc);
21468
21469 markSafepointAt(masm.currentOffset(), lir);
21470
21471 // Note that masm.framePushed() doesn't include the register dump area.
21472 // That will be taken into account when the StackMap is created from the
21473 // LSafepoint.
21474 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
21475 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap);
21476}
21477
21478void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) {
21479 const MWasmBoundsCheck* mir = ins->mir();
21480
21481 Register ptr = ToRegister(ins->ptr());
21482 if (ins->boundsCheckLimit()->isConstant()) {
21483 auto* ool =
21484 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21485 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21486 });
21487 addOutOfLineCode(ool, mir);
21488 masm.branch32(Assembler::AboveOrEqual, ptr,
21489 Imm32(ins->boundsCheckLimit()->toConstant()->toInt32()),
21490 ool->entry());
21491 return;
21492 }
21493
21494 Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit());
21495 // When there are no spectre mitigations in place, branching out-of-line to
21496 // the trap is a big performance win, but with mitigations it's trickier. See
21497 // bug 1680243.
21498 if (JitOptions.spectreIndexMasking) {
21499 Label ok;
21500 masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok);
21501 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21502 masm.bind(&ok);
21503 } else {
21504 auto* ool =
21505 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21506 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21507 });
21508 addOutOfLineCode(ool, mir);
21509 masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
21510 ool->entry());
21511 }
21512}
21513
21514void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) {
21515 const MWasmBoundsCheck* mir = ins->mir();
21516
21517 Register64 ptr = ToRegister64(ins->ptr());
21518 if (IsConstant(ins->boundsCheckLimit())) {
21519 auto* ool =
21520 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21521 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21522 });
21523 addOutOfLineCode(ool, mir);
21524 masm.branch64(Assembler::AboveOrEqual, ptr,
21525 Imm64(ToInt64(ins->boundsCheckLimit())), ool->entry());
21526 return;
21527 }
21528
21529 Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit());
21530 // See above.
21531 if (JitOptions.spectreIndexMasking) {
21532 Label ok;
21533 masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok);
21534 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21535 masm.bind(&ok);
21536 } else {
21537 auto* ool =
21538 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21539 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21540 });
21541 addOutOfLineCode(ool, mir);
21542 masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
21543 ool->entry());
21544 }
21545}
21546
21547void CodeGenerator::visitWasmBoundsCheckInstanceField(
21548 LWasmBoundsCheckInstanceField* ins) {
21549 const MWasmBoundsCheck* mir = ins->mir();
21550 Register ptr = ToRegister(ins->ptr());
21551 Register instance = ToRegister(ins->instance());
21552 // See above.
21553 if (JitOptions.spectreIndexMasking) {
21554 Label ok;
21555 masm.wasmBoundsCheck32(Assembler::Condition::Below, ptr,
21556 Address(instance, ins->offset()), &ok);
21557 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21558 masm.bind(&ok);
21559 } else {
21560 auto* ool =
21561 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21562 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21563 });
21564 addOutOfLineCode(ool, mir);
21565 masm.wasmBoundsCheck32(Assembler::Condition::AboveOrEqual, ptr,
21566 Address(instance, ins->offset()), ool->entry());
21567 }
21568}
21569
21570void CodeGenerator::visitWasmBoundsCheckInstanceField64(
21571 LWasmBoundsCheckInstanceField64* ins) {
21572 const MWasmBoundsCheck* mir = ins->mir();
21573 Register64 ptr = ToRegister64(ins->ptr());
21574 Register instance = ToRegister(ins->instance());
21575 // See above.
21576 if (JitOptions.spectreIndexMasking) {
21577 Label ok;
21578 masm.wasmBoundsCheck64(Assembler::Condition::Below, ptr,
21579 Address(instance, ins->offset()), &ok);
21580 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21581 masm.bind(&ok);
21582 } else {
21583 auto* ool =
21584 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21585 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21586 });
21587 addOutOfLineCode(ool, mir);
21588 masm.wasmBoundsCheck64(Assembler::Condition::AboveOrEqual, ptr,
21589 Address(instance, ins->offset()), ool->entry());
21590 }
21591}
21592
21593void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) {
21594 const MWasmBoundsCheckRange32* mir = ins->mir();
21595 Register index = ToRegister(ins->index());
21596 Register length = ToRegister(ins->length());
21597 Register limit = ToRegister(ins->limit());
21598 Register tmp = ToRegister(ins->temp0());
21599
21600 masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->trapSiteDesc());
21601}
21602
21603void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) {
21604 const MWasmAlignmentCheck* mir = ins->mir();
21605 Register ptr = ToRegister(ins->ptr());
21606 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21607 masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->trapSiteDesc());
21608 });
21609 addOutOfLineCode(ool, mir);
21610 masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1),
21611 ool->entry());
21612}
21613
21614void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) {
21615 const MWasmAlignmentCheck* mir = ins->mir();
21616 Register64 ptr = ToRegister64(ins->ptr());
21617#ifdef JS_64BIT1
21618 Register r = ptr.reg;
21619#else
21620 Register r = ptr.low;
21621#endif
21622 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21623 masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->trapSiteDesc());
21624 });
21625 addOutOfLineCode(ool, mir);
21626 masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1),
21627 ool->entry());
21628}
21629
21630void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) {
21631 switch (ins->mir()->type()) {
21632 case MIRType::WasmAnyRef:
21633 case MIRType::Pointer:
21634 masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()),
21635 ToRegister(ins->output()));
21636 break;
21637 case MIRType::Int32:
21638 masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()),
21639 ToRegister(ins->output()));
21640 break;
21641 default:
21642 MOZ_CRASH("MIRType not supported in WasmLoadInstance")do { do { } while (false); MOZ_ReportCrash("" "MIRType not supported in WasmLoadInstance"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21642); AnnotateMozCrashReason
("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance" ")"
); do { MOZ_CrashSequence(__null, 21642); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
21643 }
21644}
21645
21646void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) {
21647 MOZ_ASSERT(ins->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->type() == MIRType::Int64)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ins->mir()->type() == MIRType::Int64))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("ins->mir()->type() == MIRType::Int64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21647); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 21647); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21648 masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()),
21649 ToOutRegister64(ins));
21650}
21651
21652void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount,
21653 JSScript* script, Register tmp) {
21654 // The code depends on the JitScript* not being discarded without also
21655 // invalidating Ion code. Assert this.
21656#ifdef DEBUG1
21657 Label ok;
21658 masm.movePtr(ImmGCPtr(script), tmp);
21659 masm.loadJitScript(tmp, tmp);
21660 masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok);
21661 masm.assumeUnreachable("Didn't find JitScript?");
21662 masm.bind(&ok);
21663#endif
21664
21665 masm.load32(warmUpCount, tmp);
21666 masm.add32(Imm32(1), tmp);
21667 masm.store32(tmp, warmUpCount);
21668}
21669
21670void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) {
21671 Register tmp = ToRegister(ins->temp0());
21672
21673 AbsoluteAddress warmUpCount =
21674 AbsoluteAddress(ins->mir()->script()->jitScript())
21675 .offset(JitScript::offsetOfWarmUpCount());
21676 incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp);
21677}
21678
21679void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) {
21680 ValueOperand inputValue = ToValue(ins->input());
21681 Label bail;
21682 masm.branchTestMagicValue(Assembler::Equal, inputValue,
21683 JS_UNINITIALIZED_LEXICAL, &bail);
21684 bailoutFrom(&bail, ins->snapshot());
21685}
21686
21687void CodeGenerator::visitThrowRuntimeLexicalError(
21688 LThrowRuntimeLexicalError* ins) {
21689 pushArg(Imm32(ins->mir()->errorNumber()));
21690
21691 using Fn = bool (*)(JSContext*, unsigned);
21692 callVM<Fn, jit::ThrowRuntimeLexicalError>(ins);
21693}
21694
21695void CodeGenerator::visitThrowMsg(LThrowMsg* ins) {
21696 pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind())));
21697
21698 using Fn = bool (*)(JSContext*, unsigned);
21699 callVM<Fn, js::ThrowMsgOperation>(ins);
21700}
21701
21702void CodeGenerator::visitGlobalDeclInstantiation(
21703 LGlobalDeclInstantiation* ins) {
21704 pushArg(ImmPtr(ins->mir()->resumePoint()->pc()));
21705 pushArg(ImmGCPtr(ins->mir()->block()->info().script()));
21706
21707 using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*);
21708 callVM<Fn, GlobalDeclInstantiationFromIon>(ins);
21709}
21710
21711void CodeGenerator::visitDebugger(LDebugger* ins) {
21712 Register cx = ToRegister(ins->temp0());
21713
21714 masm.loadJSContext(cx);
21715 using Fn = bool (*)(JSContext* cx);
21716 masm.setupAlignedABICall();
21717 masm.passABIArg(cx);
21718 masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>();
21719
21720 Label bail;
21721 masm.branchIfTrueBool(ReturnReg, &bail);
21722 bailoutFrom(&bail, ins->snapshot());
21723}
21724
21725void CodeGenerator::visitNewTarget(LNewTarget* ins) {
21726 ValueOperand output = ToOutValue(ins);
21727
21728 // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)]
21729 Label notConstructing, done;
21730 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
21731 masm.branchTestPtr(Assembler::Zero, calleeToken,
21732 Imm32(CalleeToken_FunctionConstructing), &notConstructing);
21733
21734 Register argvLen = output.scratchReg();
21735 masm.loadNumActualArgs(FramePointer, argvLen);
21736
21737 Label useNFormals;
21738
21739 size_t numFormalArgs = ins->mir()->block()->info().nargs();
21740 masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals);
21741
21742 size_t argsOffset = JitFrameLayout::offsetOfActualArgs();
21743 {
21744 BaseValueIndex newTarget(FramePointer, argvLen, argsOffset);
21745 masm.loadValue(newTarget, output);
21746 masm.jump(&done);
21747 }
21748
21749 masm.bind(&useNFormals);
21750
21751 {
21752 Address newTarget(FramePointer,
21753 argsOffset + (numFormalArgs * sizeof(Value)));
21754 masm.loadValue(newTarget, output);
21755 masm.jump(&done);
21756 }
21757
21758 // else output = undefined
21759 masm.bind(&notConstructing);
21760 masm.moveValue(UndefinedValue(), output);
21761 masm.bind(&done);
21762}
21763
21764void CodeGenerator::visitCheckReturn(LCheckReturn* ins) {
21765 ValueOperand returnValue = ToValue(ins->returnValue());
21766 ValueOperand thisValue = ToValue(ins->thisValue());
21767 ValueOperand output = ToOutValue(ins);
21768
21769 using Fn = bool (*)(JSContext*, HandleValue);
21770 OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>(
21771 ins, ArgList(returnValue), StoreNothing());
21772
21773 Label noChecks;
21774 masm.branchTestObject(Assembler::Equal, returnValue, &noChecks);
21775 masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry());
21776 masm.branchTestMagicValue(Assembler::Equal, thisValue,
21777 JS_UNINITIALIZED_LEXICAL, ool->entry());
21778 masm.moveValue(thisValue, output);
21779 masm.jump(ool->rejoin());
21780 masm.bind(&noChecks);
21781 masm.moveValue(returnValue, output);
21782 masm.bind(ool->rejoin());
21783}
21784
21785void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) {
21786 ValueOperand value = ToValue(ins->value());
21787 Register output = ToRegister(ins->output());
21788
21789 using Fn = bool (*)(JSContext*, CheckIsObjectKind);
21790 OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>(
21791 ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing());
21792
21793 masm.fallibleUnboxObject(value, output, ool->entry());
21794 masm.bind(ool->rejoin());
21795}
21796
21797void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) {
21798 ValueOperand checkValue = ToValue(ins->checkValue());
21799
21800 using Fn = bool (*)(JSContext*, HandleValue);
21801 OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>(
21802 ins, ArgList(checkValue), StoreNothing());
21803 masm.branchTestNull(Assembler::Equal, checkValue, ool->entry());
21804 masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry());
21805 masm.bind(ool->rejoin());
21806}
21807
21808void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) {
21809 ValueOperand heritage = ToValue(ins->heritage());
21810 Register temp0 = ToRegister(ins->temp0());
21811 Register temp1 = ToRegister(ins->temp1());
21812
21813 using Fn = bool (*)(JSContext*, HandleValue);
21814 OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>(
21815 ins, ArgList(heritage), StoreNothing());
21816
21817 masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin());
21818 masm.fallibleUnboxObject(heritage, temp0, ool->entry());
21819
21820 masm.isConstructor(temp0, temp1, ool->entry());
21821 masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry());
21822
21823 masm.bind(ool->rejoin());
21824}
21825
21826void CodeGenerator::visitCheckThis(LCheckThis* ins) {
21827 ValueOperand thisValue = ToValue(ins->thisValue());
21828
21829 using Fn = bool (*)(JSContext*);
21830 OutOfLineCode* ool =
21831 oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing());
21832 masm.branchTestMagicValue(Assembler::Equal, thisValue,
21833 JS_UNINITIALIZED_LEXICAL, ool->entry());
21834 masm.bind(ool->rejoin());
21835}
21836
21837void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) {
21838 ValueOperand thisValue = ToValue(ins->thisValue());
21839
21840 using Fn = bool (*)(JSContext*);
21841 OutOfLineCode* ool =
21842 oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing());
21843 masm.branchTestMagicValue(Assembler::NotEqual, thisValue,
21844 JS_UNINITIALIZED_LEXICAL, ool->entry());
21845 masm.bind(ool->rejoin());
21846}
21847
21848void CodeGenerator::visitGenerator(LGenerator* lir) {
21849 Register callee = ToRegister(lir->callee());
21850 Register environmentChain = ToRegister(lir->environmentChain());
21851 Register argsObject = ToRegister(lir->argsObject());
21852
21853 pushArg(argsObject);
21854 pushArg(environmentChain);
21855 pushArg(ImmGCPtr(current->mir()->info().script()));
21856 pushArg(callee);
21857
21858 using Fn = JSObject* (*)(JSContext * cx, HandleFunction, HandleScript,
21859 HandleObject, HandleObject);
21860 callVM<Fn, CreateGenerator>(lir);
21861}
21862
21863void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) {
21864 Register generator = ToRegister(lir->generator());
21865 ValueOperand value = ToValue(lir->value());
21866
21867 pushArg(value);
21868 pushArg(generator);
21869
21870 using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>,
21871 HandleValue);
21872 callVM<Fn, js::AsyncFunctionResolve>(lir);
21873}
21874
21875void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) {
21876 ValueOperand value = ToValue(lir->value());
21877 Register generator = ToRegister(lir->generator());
21878
21879 pushArg(value);
21880 pushArg(generator);
21881
21882 using Fn = JSObject* (*)(JSContext * cx,
21883 Handle<AsyncFunctionGeneratorObject*> genObj,
21884 HandleValue value);
21885 callVM<Fn, js::AsyncFunctionAwait>(lir);
21886}
21887
21888void CodeGenerator::visitGeneratorResume(LGeneratorResume* lir) {
21889 Register genObj = ToRegister(lir->generator());
21890 ValueOperand value = ToValue(lir->value());
21891 int32_t resumeKind = lir->mir()->resumeKind();
21892
21893 Register callee = ToRegister(lir->temp0());
21894 Register scratch = ToRegister(lir->temp1());
21895
21896 // Load callee. Note that JSOp::Resume is never used for modules.
21897 masm.unboxObject(
21898 Address(genObj, AbstractGeneratorObject::offsetOfCalleeOrModuleSlot()),
21899 callee);
21900
21901 // The pushed argument Values are the formals (all |undefined|) and the resume
21902 // args stored after the formals.
21903 masm.loadFunctionArgCount(callee, scratch);
21904 masm.alignJitStackBasedOnNArgs(scratch, /* countIncludesThis = */ false,
21905 /* extraArgs = */ ResumeFrameArgs::NumSlots);
21906
21907 // Unlike the Baseline version, we don't have to zero the alignment padding
21908 // here: Ion frames are traced with the safepoint's slot map, not as a range.
21909
21910 // Push the resume args and the |undefined| formals.
21911 Address resumeIndexSlot(genObj,
21912 AbstractGeneratorObject::offsetOfResumeIndexSlot());
21913 masm.pushGeneratorResumeArgsAndFormals(
21914 resumeIndexSlot, Int32Value(resumeKind), genObj, value, scratch);
21915
21916 masm.PushCalleeToken(callee, /* constructing = */ false);
21917 masm.push(FrameDescriptor(FrameType::IonJS, /* argc = */ 0,
21918 /* hasInlined = */ false,
21919 /* isResumingGenerator = */ true));
21920
21921 // Load the code to call. Throw currently always resumes in Baseline.
21922 // See MaybeEnterJit.
21923 Register code = callee;
21924 if (resumeKind != int32_t(GeneratorResumeKind::Throw)) {
21925 masm.loadJitCodeRaw(callee, code);
21926 } else {
21927 masm.loadJitCodeRawNoIon(callee, code, scratch);
21928 }
21929
21930 masm.switchToObjectRealm(genObj, scratch);
21931
21932 // Mark the generator as running.
21933 masm.storeValue(Int32Value(AbstractGeneratorObject::RESUME_INDEX_RUNNING),
21934 resumeIndexSlot);
21935
21936 // Call the callee's JIT code. Its prologue sees the descriptor bit and
21937 // dispatches to the resume point.
21938 ensureOsiSpace();
21939 uint32_t callOffset = masm.callJit(code);
21940 markSafepointAt(callOffset, lir);
21941
21942 static_assert(!JSReturnOperand.aliases(ReturnReg),
21943 "ReturnReg must not alias return value in JSReturnOperand");
21944 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
21945
21946 masm.setFramePushed(frameSize());
21947 emitRestoreStackPointerFromFP();
21948}
21949
21950// The offset of a ResumeFrameArgs slot from the frame pointer.
21951static size_t OffsetOfResumeFrameArg(MIRGenerator* gen, uint32_t slot) {
21952 JSScript* script = gen->outerInfo().script();
21953 size_t base =
21954 script->isFunction()
21955 ? JitFrameLayout::offsetOfActualArg(script->function()->nargs())
21956 : JitFrameLayout::offsetOfModuleResumeArgs();
21957 return base + ResumeFrameArgs::offsetOfSlot(slot);
21958}
21959
21960static Address AddressOfFrameDescriptor() {
21961 return Address(FramePointer, CommonFrameLayout::offsetOfDescriptor());
21962}
21963
21964void CodeGenerator::visitResumeFrameArg(LResumeFrameArg* lir) {
21965#ifdef DEBUG1
21966 // The slots are only valid and traced on GC while this frame is mid-resume.
21967 Label ok;
21968 masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(),
21969 Imm32(FrameDescriptor::IsResumingGenerator), &ok);
21970 masm.assumeUnreachable("ResumeFrameArgs read outside a generator resume");
21971 masm.bind(&ok);
21972#endif
21973 ValueOperand output = ToOutValue(lir);
21974 masm.loadValue(
21975 Address(FramePointer, OffsetOfResumeFrameArg(gen, lir->mir()->slot())),
21976 output);
21977}
21978
21979void CodeGenerator::visitIsResumingGenerator(LIsResumingGenerator* lir) {
21980 Register output = ToRegister(lir->output());
21981 Label isResuming, done;
21982 masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(),
21983 Imm32(FrameDescriptor::IsResumingGenerator), &isResuming);
21984 masm.move32(Imm32(0), output);
21985 masm.jump(&done);
21986 masm.bind(&isResuming);
21987 masm.move32(Imm32(1), output);
21988 masm.bind(&done);
21989}
21990
21991void CodeGenerator::visitIsResumingGeneratorAndBranch(
21992 LIsResumingGeneratorAndBranch* lir) {
21993 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
21994 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
21995 masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(),
21996 Imm32(FrameDescriptor::IsResumingGenerator), ifTrue);
21997 if (!isNextBlock(lir->ifFalse()->lir())) {
21998 masm.jump(ifFalse);
21999 }
22000}
22001
22002void CodeGenerator::visitClearResumingGeneratorFlag(
22003 LClearResumingGeneratorFlag* lir) {
22004 masm.andPtr(Imm32(~int32_t(FrameDescriptor::IsResumingGenerator)),
22005 AddressOfFrameDescriptor());
22006}
22007
22008void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) {
22009 ValueOperand value = ToValue(lir->value());
22010 Register scratch = ToRegister(lir->temp0());
22011
22012 // The await can only be skipped when this is the first frame of its
22013 // activation. See js::CanSkipAwait.
22014 Label notEntryFrame, done;
22015 masm.branchIfNotActivationEntryFrame(scratch, &notEntryFrame);
22016
22017 pushArg(value);
22018
22019 using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip);
22020 callVM<Fn, js::CanSkipAwait>(lir);
22021 masm.jump(&done);
22022
22023 masm.bind(&notEntryFrame);
22024 masm.move32(Imm32(0), ReturnReg);
22025
22026 masm.bind(&done);
22027}
22028
22029void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) {
22030 ValueOperand value = ToValue(lir->value());
22031 ValueOperand output = ToOutValue(lir);
22032 Register canSkip = ToRegister(lir->canSkip());
22033
22034 Label cantExtract, finished;
22035 masm.branchIfFalseBool(canSkip, &cantExtract);
22036
22037 pushArg(value);
22038
22039 using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue);
22040 callVM<Fn, js::ExtractAwaitValue>(lir);
22041 masm.jump(&finished);
22042 masm.bind(&cantExtract);
22043
22044 masm.moveValue(value, output);
22045
22046 masm.bind(&finished);
22047}
22048
22049void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) {
22050 ValueOperand checkValue = ToValue(ins->checkValue());
22051 pushArg(checkValue);
22052 using Fn = bool (*)(JSContext*, HandleValue);
22053 callVM<Fn, js::Debug_CheckSelfHosted>(ins);
22054}
22055
22056void CodeGenerator::visitRandom(LRandom* ins) {
22057 using mozilla::non_crypto::XorShift128PlusRNG;
22058
22059 FloatRegister output = ToFloatRegister(ins->output());
22060 Register rngReg = ToRegister(ins->temp0());
22061
22062 Register64 temp1 = ToRegister64(ins->temp1());
22063 Register64 temp2 = ToRegister64(ins->temp2());
22064
22065 const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator();
22066 masm.movePtr(ImmPtr(rng), rngReg);
22067
22068 masm.randomDouble(rngReg, output, temp1, temp2);
22069 if (js::SupportDifferentialTesting()) {
22070 masm.loadConstantDouble(0.0, output);
22071 }
22072}
22073
22074void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) {
22075 Register input = ToRegister(ins->input());
22076 Register output = ToRegister(ins->output());
22077
22078 switch (ins->mir()->mode()) {
22079 case MSignExtendInt32::Byte:
22080 masm.move8SignExtend(input, output);
22081 break;
22082 case MSignExtendInt32::Half:
22083 masm.move16SignExtend(input, output);
22084 break;
22085 }
22086}
22087
22088void CodeGenerator::visitSignExtendIntPtr(LSignExtendIntPtr* ins) {
22089 Register input = ToRegister(ins->input());
22090 Register output = ToRegister(ins->output());
22091
22092 switch (ins->mir()->mode()) {
22093 case MSignExtendIntPtr::Byte:
22094 masm.move8SignExtendToPtr(input, output);
22095 break;
22096 case MSignExtendIntPtr::Half:
22097 masm.move16SignExtendToPtr(input, output);
22098 break;
22099 case MSignExtendIntPtr::Word:
22100 masm.move32SignExtendToPtr(input, output);
22101 break;
22102 }
22103}
22104
22105void CodeGenerator::visitRotate(LRotate* ins) {
22106 MRotate* mir = ins->mir();
22107 Register input = ToRegister(ins->input());
22108 Register dest = ToRegister(ins->output());
22109
22110 const LAllocation* count = ins->count();
22111 if (count->isConstant()) {
22112 int32_t c = ToInt32(count) & 0x1F;
22113 if (mir->isLeftRotate()) {
22114 masm.rotateLeft(Imm32(c), input, dest);
22115 } else {
22116 masm.rotateRight(Imm32(c), input, dest);
22117 }
22118 } else {
22119 Register creg = ToRegister(count);
22120 if (mir->isLeftRotate()) {
22121 masm.rotateLeft(creg, input, dest);
22122 } else {
22123 masm.rotateRight(creg, input, dest);
22124 }
22125 }
22126}
22127
22128void CodeGenerator::visitRotateI64(LRotateI64* lir) {
22129 MRotate* mir = lir->mir();
22130 const LAllocation* count = lir->count();
22131
22132 Register64 input = ToRegister64(lir->input());
22133 Register64 output = ToOutRegister64(lir);
22134 Register temp = ToTempRegisterOrInvalid(lir->temp0());
22135
22136 if (count->isConstant()) {
22137 int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F);
22138 if (!c) {
22139 if (input != output) {
22140 masm.move64(input, output);
22141 }
22142 return;
22143 }
22144 if (mir->isLeftRotate()) {
22145 masm.rotateLeft64(Imm32(c), input, output, temp);
22146 } else {
22147 masm.rotateRight64(Imm32(c), input, output, temp);
22148 }
22149 } else {
22150 if (mir->isLeftRotate()) {
22151 masm.rotateLeft64(ToRegister(count), input, output, temp);
22152 } else {
22153 masm.rotateRight64(ToRegister(count), input, output, temp);
22154 }
22155 }
22156}
22157
22158void CodeGenerator::visitReinterpretCast(LReinterpretCast* lir) {
22159 MReinterpretCast* ins = lir->mir();
22160
22161 MIRType to = ins->type();
22162 mozilla::DebugOnly<MIRType> from = ins->input()->type();
22163
22164 switch (to) {
22165 case MIRType::Int32:
22166 MOZ_ASSERT(from == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(from == MIRType::Float32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(from == MIRType::Float32))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Float32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22166); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "from == MIRType::Float32" ")"); do { MOZ_CrashSequence
(__null, 22166); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
22167 masm.moveFloat32ToGPR(ToFloatRegister(lir->input()),
22168 ToRegister(lir->output()));
22169 break;
22170 case MIRType::Float32:
22171 MOZ_ASSERT(from == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(from == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(from == MIRType::Int32))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Int32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22171); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "from == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 22171); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
22172 masm.moveGPRToFloat32(ToRegister(lir->input()),
22173 ToFloatRegister(lir->output()));
22174 break;
22175 case MIRType::Double:
22176 case MIRType::Int64:
22177 MOZ_CRASH("not handled by this LIR opcode")do { do { } while (false); MOZ_ReportCrash("" "not handled by this LIR opcode"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22177); AnnotateMozCrashReason
("MOZ_CRASH(" "not handled by this LIR opcode" ")"); do { MOZ_CrashSequence
(__null, 22177); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
22178 default:
22179 MOZ_CRASH("unexpected ReinterpretCast")do { do { } while (false); MOZ_ReportCrash("" "unexpected ReinterpretCast"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22179); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected ReinterpretCast" ")"); do { MOZ_CrashSequence
(__null, 22179); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
22180 }
22181}
22182
22183void CodeGenerator::visitReinterpretCastFromI64(LReinterpretCastFromI64* lir) {
22184 MOZ_ASSERT(lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Double)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Double))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Double"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22184); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 22184); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22185 MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->input()->type() == MIRType::Int64
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->input()->type() == MIRType::Int64
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->input()->type() == MIRType::Int64", "./../../../../js/src/jit/CodeGenerator.cpp"
, 22185); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 22185); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22186 masm.moveGPR64ToDouble(ToRegister64(lir->input()),
22187 ToFloatRegister(lir->output()));
22188}
22189
22190void CodeGenerator::visitReinterpretCastToI64(LReinterpretCastToI64* lir) {
22191 MOZ_ASSERT(lir->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Int64)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Int64))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22191); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 22191); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22192 MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->input()->type() == MIRType::Double
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->input()->type() == MIRType::Double
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->input()->type() == MIRType::Double", "./../../../../js/src/jit/CodeGenerator.cpp"
, 22192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 22192); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22193 masm.moveDoubleToGPR64(ToFloatRegister(lir->input()), ToOutRegister64(lir));
22194}
22195
22196void CodeGenerator::visitNaNToZero(LNaNToZero* lir) {
22197 FloatRegister input = ToFloatRegister(lir->input());
22198
22199 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
22200 FloatRegister output = ToFloatRegister(lir->output());
22201 masm.loadConstantDouble(0.0, output);
22202 masm.jump(ool.rejoin());
22203 });
22204 addOutOfLineCode(ool, lir->mir());
22205
22206 if (lir->mir()->operandIsNeverNegativeZero()) {
22207 masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry());
22208 } else {
22209 FloatRegister scratch = ToFloatRegister(lir->temp0());
22210 masm.loadConstantDouble(0.0, scratch);
22211 masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch,
22212 ool->entry());
22213 }
22214 masm.bind(ool->rejoin());
22215}
22216
22217void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) {
22218 Register obj = ToRegister(lir->object());
22219 Register output = ToRegister(lir->output());
22220 Register temp = ToRegister(lir->temp0());
22221
22222 masm.setIsPackedArray(obj, output, temp);
22223}
22224
22225void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) {
22226 Register array = ToRegister(lir->array());
22227 Register temp0 = ToRegister(lir->temp0());
22228 Register temp1 = ToRegister(lir->temp1());
22229
22230 Label bail;
22231 masm.branchArrayIsNotPacked(array, temp0, temp1, &bail);
22232 bailoutFrom(&bail, lir->snapshot());
22233}
22234
22235void CodeGenerator::visitGuardElementsArePacked(LGuardElementsArePacked* lir) {
22236 Register elements = ToRegister(lir->elements());
22237
22238 Label bail;
22239 Address flags(elements, ObjectElements::offsetOfFlags());
22240 masm.branchTest32(Assembler::NonZero, flags,
22241 Imm32(ObjectElements::NON_PACKED), &bail);
22242 bailoutFrom(&bail, lir->snapshot());
22243}
22244
22245void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) {
22246 Register target = ToRegister(lir->target());
22247 ValueOperand out = ToOutValue(lir);
22248 Register scratch = out.scratchReg();
22249
22250 using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue);
22251 OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target),
22252 StoreValueTo(out));
22253
22254 MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22254); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 22254); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
22255
22256 masm.loadObjProto(target, scratch);
22257
22258 Label hasProto;
22259 masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto);
22260
22261 // Call into the VM for lazy prototypes.
22262 masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry());
22263
22264 masm.moveValue(NullValue(), out);
22265 masm.jump(ool->rejoin());
22266
22267 masm.bind(&hasProto);
22268 masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out);
22269
22270 masm.bind(ool->rejoin());
22271}
22272
22273void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) {
22274 pushArg(ToValue(lir->prototype()));
22275
22276 using Fn = PlainObject* (*)(JSContext*, HandleValue);
22277 callVM<Fn, js::ObjectWithProtoOperation>(lir);
22278}
22279
22280void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) {
22281 Register obj = ToRegister(lir->object());
22282 Register output = ToRegister(lir->output());
22283
22284 masm.loadObjProto(obj, output);
22285
22286#ifdef DEBUG1
22287 // We shouldn't encounter a null or lazy proto.
22288 MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22288); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 22288); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
22289
22290 Label done;
22291 masm.branchPtr(Assembler::Above, output, ImmWord(1), &done);
22292 masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto");
22293 masm.bind(&done);
22294#endif
22295}
22296
22297void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) {
22298 pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind())));
22299
22300 using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind);
22301 callVM<Fn, js::BuiltinObjectOperation>(lir);
22302}
22303
22304static void EmitLoadSuperFunction(MacroAssembler& masm, Register callee,
22305 Register dest) {
22306#ifdef DEBUG1
22307 Label classCheckDone;
22308 masm.branchTestObjIsFunction(Assembler::Equal, callee, dest, callee,
22309 &classCheckDone);
22310 masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun");
22311 masm.bind(&classCheckDone);
22312#endif
22313
22314 // Load prototype of callee
22315 masm.loadObjProto(callee, dest);
22316
22317#ifdef DEBUG1
22318 // We won't encounter a lazy proto, because |callee| is guaranteed to be a
22319 // JSFunction and only proxy objects can have a lazy proto.
22320 MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22320); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 22320); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
22321
22322 Label proxyCheckDone;
22323 masm.branchPtr(Assembler::NotEqual, dest, ImmWord(1), &proxyCheckDone);
22324 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun");
22325 masm.bind(&proxyCheckDone);
22326#endif
22327}
22328
22329void CodeGenerator::visitSuperFunction(LSuperFunction* lir) {
22330 Register callee = ToRegister(lir->callee());
22331 ValueOperand out = ToOutValue(lir);
22332 Register temp = out.scratchReg();
22333
22334 EmitLoadSuperFunction(masm, callee, temp);
22335
22336 Label nullProto, done;
22337 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
22338
22339 // Box prototype and return
22340 masm.tagValue(JSVAL_TYPE_OBJECT, temp, out);
22341 masm.jump(&done);
22342
22343 masm.bind(&nullProto);
22344 masm.moveValue(NullValue(), out);
22345
22346 masm.bind(&done);
22347}
22348
22349void CodeGenerator::visitSuperFunctionAndUnbox(LSuperFunctionAndUnbox* lir) {
22350 Register callee = ToRegister(lir->callee());
22351 Register output = ToRegister(lir->output());
22352
22353 EmitLoadSuperFunction(masm, callee, output);
22354
22355 bailoutCmpPtr(Assembler::Equal, output, ImmWord(0), lir->snapshot());
22356}
22357
22358void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) {
22359 Register func = ToRegister(lir->function());
22360 ValueOperand homeObject = ToValue(lir->homeObject());
22361
22362 masm.assertFunctionIsExtended(func);
22363
22364 Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
22365
22366 emitPreBarrier(addr);
22367 masm.storeValue(homeObject, addr);
22368}
22369
22370void CodeGenerator::visitIsTypedArrayConstructor(
22371 LIsTypedArrayConstructor* lir) {
22372 Register object = ToRegister(lir->object());
22373 Register output = ToRegister(lir->output());
22374
22375 masm.setIsDefinitelyTypedArrayConstructor(object, output);
22376}
22377
22378void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) {
22379 ValueOperand value = ToValue(lir->value());
22380 Register output = ToRegister(lir->output());
22381
22382 Register tag = masm.extractTag(value, output);
22383 if (tag != output) {
22384 masm.mov(tag, output);
22385 }
22386}
22387
22388void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) {
22389 Register lhs = ToRegister(lir->lhs());
22390 Register rhs = ToRegister(lir->rhs());
22391
22392 bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot());
22393
22394 // If both lhs and rhs are numbers, can't use tag comparison to do inequality
22395 // comparison
22396 Label done;
22397 masm.branchTestNumber(Assembler::NotEqual, lhs, &done);
22398 masm.branchTestNumber(Assembler::NotEqual, rhs, &done);
22399 bailout(lir->snapshot());
22400
22401 masm.bind(&done);
22402}
22403
22404void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) {
22405 Register object = ToRegister(lir->object());
22406 Register output = ToRegister(lir->output());
22407
22408 // Bail for revoked proxies.
22409 Label bail;
22410 Address targetAddr(object, ProxyObject::offsetOfPrivateSlot());
22411 if (lir->mir()->fallible()) {
22412 masm.fallibleUnboxObject(targetAddr, output, &bail);
22413 bailoutFrom(&bail, lir->snapshot());
22414 } else {
22415 masm.unboxObject(targetAddr, output);
22416 }
22417}
22418
22419void CodeGenerator::visitLoadGetterSetterFunction(
22420 LLoadGetterSetterFunction* lir) {
22421 ValueOperand getterSetter = ToValue(lir->getterSetter());
22422 Register output = ToRegister(lir->output());
22423
22424 masm.unboxNonDouble(getterSetter, output, JSVAL_TYPE_PRIVATE_GCTHING);
22425
22426 size_t offset = lir->mir()->isGetter() ? GetterSetter::offsetOfGetter()
22427 : GetterSetter::offsetOfSetter();
22428 masm.loadPtr(Address(output, offset), output);
22429
22430 Label bail;
22431 masm.branchTestPtr(Assembler::Zero, output, output, &bail);
22432 if (lir->mir()->needsClassGuard()) {
22433 Register temp = ToRegister(lir->temp0());
22434 masm.branchTestObjIsFunction(Assembler::NotEqual, output, temp, output,
22435 &bail);
22436 }
22437
22438 bailoutFrom(&bail, lir->snapshot());
22439}
22440
22441void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) {
22442 Register object = ToRegister(lir->object());
22443 Register temp0 = ToRegister(lir->temp0());
22444 Register temp1 = ToRegister(lir->temp1());
22445 Register temp2 = ToRegister(lir->temp2());
22446
22447 masm.movePropertyKey(lir->mir()->propId(), temp1);
22448
22449 auto getterSetterVal = lir->mir()->getterSetterValue();
22450 if (getterSetterVal.isValue()) {
22451 auto* gs = getterSetterVal.toValue().toGCThing()->as<GetterSetter>();
22452 masm.movePtr(ImmGCPtr(gs), temp2);
22453 } else {
22454 // Load the GetterSetter* from the Value stored in the IonScript.
22455 Address valueAddr = getNurseryValueAddress(getterSetterVal, temp2);
22456 masm.unboxNonDouble(valueAddr, temp2, JSVAL_TYPE_PRIVATE_GCTHING);
22457 }
22458
22459 using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id,
22460 GetterSetter* getterSetter);
22461 masm.setupAlignedABICall();
22462 masm.loadJSContext(temp0);
22463 masm.passABIArg(temp0);
22464 masm.passABIArg(object);
22465 masm.passABIArg(temp1);
22466 masm.passABIArg(temp2);
22467 masm.callWithABI<Fn, ObjectHasGetterSetterPure>();
22468
22469 bailoutIfFalseBool(ReturnReg, lir->snapshot());
22470}
22471
22472void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) {
22473 Register object = ToRegister(lir->object());
22474 Register temp = ToRegister(lir->temp0());
22475
22476 Label bail;
22477 masm.branchIfObjectNotExtensible(object, temp, &bail);
22478 bailoutFrom(&bail, lir->snapshot());
22479}
22480
22481void CodeGenerator::visitGuardInt32IsNonNegative(
22482 LGuardInt32IsNonNegative* lir) {
22483 Register index = ToRegister(lir->index());
22484
22485 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
22486}
22487
22488void CodeGenerator::visitGuardIntPtrIsNonNegative(
22489 LGuardIntPtrIsNonNegative* lir) {
22490 Register index = ToRegister(lir->index());
22491
22492 bailoutCmpPtr(Assembler::LessThan, index, ImmWord(0), lir->snapshot());
22493}
22494
22495void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) {
22496 Register input = ToRegister(lir->input());
22497
22498 bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()),
22499 lir->snapshot());
22500 bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()),
22501 lir->snapshot());
22502}
22503
22504void CodeGenerator::visitGuardIndexIsNotDenseElement(
22505 LGuardIndexIsNotDenseElement* lir) {
22506 Register object = ToRegister(lir->object());
22507 Register index = ToRegister(lir->index());
22508 Register temp = ToRegister(lir->temp0());
22509 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
22510
22511 // Load obj->elements.
22512 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
22513
22514 // Ensure index >= initLength or the element is a hole.
22515 Label notDense;
22516 Address capacity(temp, ObjectElements::offsetOfInitializedLength());
22517 masm.spectreBoundsCheck32(index, capacity, spectreTemp, &notDense);
22518
22519 BaseObjectElementIndex element(temp, index);
22520 masm.branchTestMagic(Assembler::Equal, element, JS_ELEMENTS_HOLE, &notDense);
22521
22522 bailout(lir->snapshot());
22523
22524 masm.bind(&notDense);
22525}
22526
22527void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd(
22528 LGuardIndexIsValidUpdateOrAdd* lir) {
22529 Register object = ToRegister(lir->object());
22530 Register index = ToRegister(lir->index());
22531 Register temp = ToRegister(lir->temp0());
22532 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
22533
22534 // Load obj->elements.
22535 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
22536
22537 Label success;
22538
22539 // If length is writable, branch to &success. All indices are writable.
22540 Address flags(temp, ObjectElements::offsetOfFlags());
22541 masm.branchTest32(Assembler::Zero, flags,
22542 Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH),
22543 &success);
22544
22545 // Otherwise, ensure index is in bounds.
22546 Label bail;
22547 Address length(temp, ObjectElements::offsetOfLength());
22548 masm.spectreBoundsCheck32(index, length, spectreTemp, &bail);
22549 masm.bind(&success);
22550
22551 bailoutFrom(&bail, lir->snapshot());
22552}
22553
22554void CodeGenerator::visitCallAddOrUpdateSparseElement(
22555 LCallAddOrUpdateSparseElement* lir) {
22556 Register object = ToRegister(lir->object());
22557 Register index = ToRegister(lir->index());
22558 ValueOperand value = ToValue(lir->value());
22559
22560 pushArg(Imm32(lir->mir()->strict()));
22561 pushArg(value);
22562 pushArg(index);
22563 pushArg(object);
22564
22565 using Fn =
22566 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool);
22567 callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir);
22568}
22569
22570void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) {
22571 Register object = ToRegister(lir->object());
22572 Register index = ToRegister(lir->index());
22573
22574 pushArg(index);
22575 pushArg(object);
22576
22577 using Fn =
22578 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue);
22579 callVM<Fn, js::GetSparseElementHelper>(lir);
22580}
22581
22582void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) {
22583 Register object = ToRegister(lir->object());
22584 Register index = ToRegister(lir->index());
22585
22586 pushArg(index);
22587 pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
22588 pushArg(object);
22589
22590 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
22591 MutableHandleValue);
22592 callVM<Fn, js::NativeGetElement>(lir);
22593}
22594
22595void CodeGenerator::visitCallNativeGetElementSuper(
22596 LCallNativeGetElementSuper* lir) {
22597 Register object = ToRegister(lir->object());
22598 Register index = ToRegister(lir->index());
22599 ValueOperand receiver = ToValue(lir->receiver());
22600
22601 pushArg(index);
22602 pushArg(receiver);
22603 pushArg(object);
22604
22605 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
22606 MutableHandleValue);
22607 callVM<Fn, js::NativeGetElement>(lir);
22608}
22609
22610void CodeGenerator::visitCallObjectHasSparseElement(
22611 LCallObjectHasSparseElement* lir) {
22612 Register object = ToRegister(lir->object());
22613 Register index = ToRegister(lir->index());
22614 Register temp0 = ToRegister(lir->temp0());
22615 Register temp1 = ToRegister(lir->temp1());
22616 Register output = ToRegister(lir->output());
22617
22618 masm.reserveStack(sizeof(Value));
22619 masm.moveStackPtrTo(temp1);
22620
22621 using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*);
22622 masm.setupAlignedABICall();
22623 masm.loadJSContext(temp0);
22624 masm.passABIArg(temp0);
22625 masm.passABIArg(object);
22626 masm.passABIArg(index);
22627 masm.passABIArg(temp1);
22628 masm.callWithABI<Fn, HasNativeElementPure>();
22629 masm.storeCallPointerResult(temp0);
22630
22631 Label bail, ok;
22632 uint32_t framePushed = masm.framePushed();
22633 masm.branchIfTrueBool(temp0, &ok);
22634 masm.adjustStack(sizeof(Value));
22635 masm.jump(&bail);
22636
22637 masm.bind(&ok);
22638 masm.setFramePushed(framePushed);
22639 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
22640 masm.adjustStack(sizeof(Value));
22641
22642 bailoutFrom(&bail, lir->snapshot());
22643}
22644
22645void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) {
22646 Register bits = ToRegister(ins->bits());
22647 Register input = ToRegister(ins->input());
22648
22649 pushArg(bits);
22650 pushArg(input);
22651
22652 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
22653 callVM<Fn, jit::BigIntAsIntN>(ins);
22654}
22655
22656void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) {
22657 Register bits = ToRegister(ins->bits());
22658 Register input = ToRegister(ins->input());
22659
22660 pushArg(bits);
22661 pushArg(input);
22662
22663 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
22664 callVM<Fn, jit::BigIntAsUintN>(ins);
22665}
22666
22667void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) {
22668 ValueOperand input = ToValue(ins->input());
22669
22670 Label bail;
22671 masm.branchTestGCThing(Assembler::Equal, input, &bail);
22672 bailoutFrom(&bail, ins->snapshot());
22673}
22674
22675void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) {
22676 ValueOperand input = ToValue(ins->input());
22677 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
22678 ValueOperand output = ToOutValue(ins);
22679
22680 masm.toHashableNonGCThing(input, output, tempFloat);
22681}
22682
22683void CodeGenerator::visitToHashableString(LToHashableString* ins) {
22684 Register input = ToRegister(ins->input());
22685 Register output = ToRegister(ins->output());
22686
22687 using Fn = JSAtom* (*)(JSContext*, JSString*);
22688 auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input),
22689 StoreRegisterTo(output));
22690
22691 Label isAtom;
22692 masm.branchTest32(Assembler::NonZero,
22693 Address(input, JSString::offsetOfFlags()),
22694 Imm32(StringFlags::ATOM_BIT), &isAtom);
22695
22696 masm.tryFastAtomize(input, output, output, ool->entry());
22697 masm.jump(ool->rejoin());
22698 masm.bind(&isAtom);
22699 masm.movePtr(input, output);
22700 masm.bind(ool->rejoin());
22701}
22702
22703void CodeGenerator::visitToHashableValue(LToHashableValue* ins) {
22704 ValueOperand input = ToValue(ins->input());
22705 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
22706 ValueOperand output = ToOutValue(ins);
22707
22708 Register str = output.scratchReg();
22709
22710 using Fn = JSAtom* (*)(JSContext*, JSString*);
22711 auto* ool =
22712 oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str));
22713
22714 masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin());
22715}
22716
22717void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) {
22718 ValueOperand input = ToValue(ins->input());
22719 Register temp = ToRegister(ins->temp0());
22720 Register output = ToRegister(ins->output());
22721
22722 masm.prepareHashNonGCThing(input, output, temp);
22723}
22724
22725void CodeGenerator::visitHashString(LHashString* ins) {
22726 Register input = ToRegister(ins->input());
22727 Register temp = ToRegister(ins->temp0());
22728 Register output = ToRegister(ins->output());
22729
22730 masm.prepareHashString(input, output, temp);
22731}
22732
22733void CodeGenerator::visitHashSymbol(LHashSymbol* ins) {
22734 Register input = ToRegister(ins->input());
22735 Register output = ToRegister(ins->output());
22736
22737 masm.prepareHashSymbol(input, output);
22738}
22739
22740void CodeGenerator::visitHashBigInt(LHashBigInt* ins) {
22741 Register input = ToRegister(ins->input());
22742 Register temp0 = ToRegister(ins->temp0());
22743 Register temp1 = ToRegister(ins->temp1());
22744 Register temp2 = ToRegister(ins->temp2());
22745 Register output = ToRegister(ins->output());
22746
22747 masm.prepareHashBigInt(input, output, temp0, temp1, temp2);
22748}
22749
22750void CodeGenerator::visitHashObject(LHashObject* ins) {
22751 Register setObj = ToRegister(ins->setObject());
22752 ValueOperand input = ToValue(ins->input());
22753 Register temp0 = ToRegister(ins->temp0());
22754 Register temp1 = ToRegister(ins->temp1());
22755 Register temp2 = ToRegister(ins->temp2());
22756 Register temp3 = ToRegister(ins->temp3());
22757 Register output = ToRegister(ins->output());
22758
22759 masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3);
22760}
22761
22762void CodeGenerator::visitHashValue(LHashValue* ins) {
22763 Register setObj = ToRegister(ins->setObject());
22764 ValueOperand input = ToValue(ins->input());
22765 Register temp0 = ToRegister(ins->temp0());
22766 Register temp1 = ToRegister(ins->temp1());
22767 Register temp2 = ToRegister(ins->temp2());
22768 Register temp3 = ToRegister(ins->temp3());
22769 Register output = ToRegister(ins->output());
22770
22771 masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3);
22772}
22773
22774void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) {
22775 Register setObj = ToRegister(ins->setObject());
22776 ValueOperand input = ToValue(ins->value());
22777 Register hash = ToRegister(ins->hash());
22778 Register temp0 = ToRegister(ins->temp0());
22779 Register temp1 = ToRegister(ins->temp1());
22780 Register output = ToRegister(ins->output());
22781
22782 masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1);
22783}
22784
22785void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) {
22786 Register setObj = ToRegister(ins->setObject());
22787 ValueOperand input = ToValue(ins->value());
22788 Register hash = ToRegister(ins->hash());
22789 Register temp0 = ToRegister(ins->temp0());
22790 Register temp1 = ToRegister(ins->temp1());
22791 Register temp2 = ToRegister(ins->temp2());
22792 Register temp3 = ToRegister(ins->temp3());
22793 Register output = ToRegister(ins->output());
22794
22795 masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2,
22796 temp3);
22797}
22798
22799void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) {
22800 Register setObj = ToRegister(ins->setObject());
22801 ValueOperand input = ToValue(ins->value());
22802 Register hash = ToRegister(ins->hash());
22803 Register temp0 = ToRegister(ins->temp0());
22804 Register temp1 = ToRegister(ins->temp1());
22805 Register temp2 = ToRegister(ins->temp2());
22806 Register temp3 = ToRegister(ins->temp3());
22807 Register output = ToRegister(ins->output());
22808
22809 masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2,
22810 temp3);
22811}
22812
22813void CodeGenerator::visitSetObjectHasValueVMCall(
22814 LSetObjectHasValueVMCall* ins) {
22815 pushArg(ToValue(ins->value()));
22816 pushArg(ToRegister(ins->setObject()));
22817
22818 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
22819 callVM<Fn, jit::SetObjectHas>(ins);
22820}
22821
22822void CodeGenerator::visitSetObjectDelete(LSetObjectDelete* ins) {
22823 pushArg(ToValue(ins->key()));
22824 pushArg(ToRegister(ins->setObject()));
22825 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
22826 callVM<Fn, jit::SetObjectDelete>(ins);
22827}
22828
22829void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) {
22830 pushArg(ToValue(ins->key()));
22831 pushArg(ToRegister(ins->setObject()));
22832 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue);
22833 callVM<Fn, jit::SetObjectAdd>(ins);
22834}
22835
22836void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) {
22837 Register setObj = ToRegister(ins->setObject());
22838 Register output = ToRegister(ins->output());
22839
22840 masm.loadSetObjectSize(setObj, output);
22841}
22842
22843void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) {
22844 Register mapObj = ToRegister(ins->mapObject());
22845 ValueOperand input = ToValue(ins->value());
22846 Register hash = ToRegister(ins->hash());
22847 Register temp0 = ToRegister(ins->temp0());
22848 Register temp1 = ToRegister(ins->temp1());
22849 Register output = ToRegister(ins->output());
22850
22851 masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1);
22852}
22853
22854void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) {
22855 Register mapObj = ToRegister(ins->mapObject());
22856 ValueOperand input = ToValue(ins->value());
22857 Register hash = ToRegister(ins->hash());
22858 Register temp0 = ToRegister(ins->temp0());
22859 Register temp1 = ToRegister(ins->temp1());
22860 Register temp2 = ToRegister(ins->temp2());
22861 Register temp3 = ToRegister(ins->temp3());
22862 Register output = ToRegister(ins->output());
22863
22864 masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
22865 temp3);
22866}
22867
22868void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) {
22869 Register mapObj = ToRegister(ins->mapObject());
22870 ValueOperand input = ToValue(ins->value());
22871 Register hash = ToRegister(ins->hash());
22872 Register temp0 = ToRegister(ins->temp0());
22873 Register temp1 = ToRegister(ins->temp1());
22874 Register temp2 = ToRegister(ins->temp2());
22875 Register temp3 = ToRegister(ins->temp3());
22876 Register output = ToRegister(ins->output());
22877
22878 masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2,
22879 temp3);
22880}
22881
22882void CodeGenerator::visitMapObjectHasValueVMCall(
22883 LMapObjectHasValueVMCall* ins) {
22884 pushArg(ToValue(ins->value()));
22885 pushArg(ToRegister(ins->mapObject()));
22886
22887 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
22888 callVM<Fn, jit::MapObjectHas>(ins);
22889}
22890
22891void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) {
22892 Register mapObj = ToRegister(ins->mapObject());
22893 ValueOperand input = ToValue(ins->value());
22894 Register hash = ToRegister(ins->hash());
22895 Register temp0 = ToRegister(ins->temp0());
22896 Register temp1 = ToRegister(ins->temp1());
22897 ValueOperand output = ToOutValue(ins);
22898
22899 masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1,
22900 output.scratchReg());
22901}
22902
22903void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) {
22904 Register mapObj = ToRegister(ins->mapObject());
22905 ValueOperand input = ToValue(ins->value());
22906 Register hash = ToRegister(ins->hash());
22907 Register temp0 = ToRegister(ins->temp0());
22908 Register temp1 = ToRegister(ins->temp1());
22909 Register temp2 = ToRegister(ins->temp2());
22910 Register temp3 = ToRegister(ins->temp3());
22911 ValueOperand output = ToOutValue(ins);
22912
22913 masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
22914 temp3, output.scratchReg());
22915}
22916
22917void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) {
22918 Register mapObj = ToRegister(ins->mapObject());
22919 ValueOperand input = ToValue(ins->value());
22920 Register hash = ToRegister(ins->hash());
22921 Register temp0 = ToRegister(ins->temp0());
22922 Register temp1 = ToRegister(ins->temp1());
22923 Register temp2 = ToRegister(ins->temp2());
22924 Register temp3 = ToRegister(ins->temp3());
22925 ValueOperand output = ToOutValue(ins);
22926
22927 masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2,
22928 temp3, output.scratchReg());
22929}
22930
22931void CodeGenerator::visitMapObjectGetValueVMCall(
22932 LMapObjectGetValueVMCall* ins) {
22933 pushArg(ToValue(ins->value()));
22934 pushArg(ToRegister(ins->mapObject()));
22935
22936 using Fn =
22937 bool (*)(JSContext*, Handle<MapObject*>, HandleValue, MutableHandleValue);
22938 callVM<Fn, jit::MapObjectGet>(ins);
22939}
22940
22941void CodeGenerator::visitMapObjectDelete(LMapObjectDelete* ins) {
22942 pushArg(ToValue(ins->key()));
22943 pushArg(ToRegister(ins->mapObject()));
22944 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
22945 callVM<Fn, jit::MapObjectDelete>(ins);
22946}
22947
22948void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) {
22949 pushArg(ToValue(ins->value()));
22950 pushArg(ToValue(ins->key()));
22951 pushArg(ToRegister(ins->mapObject()));
22952 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue);
22953 callVM<Fn, jit::MapObjectSet>(ins);
22954}
22955
22956void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) {
22957 Register mapObj = ToRegister(ins->mapObject());
22958 Register output = ToRegister(ins->output());
22959
22960 masm.loadMapObjectSize(mapObj, output);
22961}
22962
22963void CodeGenerator::emitWeakMapLookupObject(
22964 Register weakMap, Register obj, Register hashTable, Register hashCode,
22965 Register scratch, Register scratch2, Register scratch3, Register scratch4,
22966 Register scratch5, Label* found, Label* missing) {
22967 // Load hash map if it exists. If not, jump to missing.
22968 Address mapAddr(weakMap,
22969 NativeObject::getFixedSlotOffset(WeakMapObject::DataSlot));
22970 masm.branchTestUndefined(Assembler::Equal, mapAddr, missing);
22971 masm.loadPrivate(mapAddr, hashTable);
22972
22973 // Hash and scramble address of object.
22974#ifdef JS_PUNBOX641
22975 ValueOperand boxedObj(scratch);
22976#else
22977 ValueOperand boxedObj(scratch, obj);
22978#endif
22979 masm.tagValue(JSVAL_TYPE_OBJECT, obj, boxedObj);
22980 masm.hashAndScrambleValue(boxedObj, hashCode, scratch2);
22981 masm.prepareHashMFBT(hashCode, /*alreadyScrambled*/ true);
22982
22983 using Entry = WeakMapObject::Map::Entry;
22984 auto matchEntry = [&]() {
22985 Register entry = scratch;
22986 Label noMatch;
22987 masm.fallibleUnboxObject(Address(entry, Entry::offsetOfKey()), scratch2,
22988 &noMatch);
22989 masm.branchPtr(Assembler::Equal, obj, scratch2, found);
22990 masm.bind(&noMatch);
22991 };
22992 masm.lookupMFBT<WeakMapObject::Map>(hashTable, hashCode, scratch, scratch2,
22993 scratch3, scratch4, scratch5, missing,
22994 matchEntry);
22995}
22996
22997void CodeGenerator::visitWeakMapGetObject(LWeakMapGetObject* ins) {
22998#ifndef JS_CODEGEN_X86
22999 Register weakMap = ToRegister(ins->weakMap());
23000 Register obj = ToRegister(ins->object());
23001 Register hashTable = ToRegister(ins->temp0());
23002 Register hashCode = ToRegister(ins->temp1());
23003 Register scratch = ToRegister(ins->temp2());
23004 Register scratch2 = ToRegister(ins->temp3());
23005 Register scratch3 = ToRegister(ins->temp4());
23006 Register scratch4 = ToRegister(ins->temp5());
23007 Register scratch5 = ToRegister(ins->temp6());
23008 ValueOperand output = ToOutValue(ins);
23009
23010 Label found, missing;
23011
23012 emitWeakMapLookupObject(weakMap, obj, hashTable, hashCode, scratch, scratch2,
23013 scratch3, scratch4, scratch5, &found, &missing);
23014
23015 masm.bind(&found);
23016
23017 using Entry = WeakMapObject::Map::Entry;
23018 masm.loadValue(Address(scratch, Entry::offsetOfValue()), output);
23019
23020 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
23021 // Unboxed, tenured GC cell that needs to be barriered is in scratch.
23022
23023 LiveRegisterSet regsToSave(RegisterSet::Volatile());
23024 regsToSave.takeUnchecked(hashTable);
23025 regsToSave.takeUnchecked(hashCode);
23026 regsToSave.takeUnchecked(scratch);
23027 regsToSave.takeUnchecked(scratch2);
23028 regsToSave.takeUnchecked(scratch3);
23029 regsToSave.takeUnchecked(scratch4);
23030 regsToSave.takeUnchecked(scratch5);
23031 masm.PushRegsInMask(regsToSave);
23032
23033 masm.movePtr(ImmPtr(mirGen().realm->zone()->addressOfZone()), scratch2);
23034
23035 using Fn = void (*)(js::gc::TenuredCell*, Zone*);
23036 masm.setupAlignedABICall();
23037 masm.passABIArg(scratch);
23038 masm.passABIArg(scratch2);
23039 masm.callWithABI<Fn, js::jit::WeakMapValueReadBarrier>();
23040
23041 masm.PopRegsInMask(regsToSave);
23042
23043 masm.jump(ool.rejoin());
23044 });
23045 addOutOfLineCode(ool, ins->mir());
23046
23047 masm.emitWeakMapBarrierFastPath(output, scratch, scratch2, scratch3, scratch4,
23048 scratch5, ool->entry());
23049 masm.jump(ool->rejoin());
23050
23051 masm.bind(&missing);
23052 masm.moveValue(UndefinedValue(), output);
23053
23054 masm.bind(ool->rejoin());
23055#else
23056 // x86 doesn't have enough registers, so we call into the VM.
23057 Register weakMap = ToRegister(ins->weakMap());
23058 Register obj = ToRegister(ins->object());
23059 Register temp = ToRegister(ins->temp0());
23060 ValueOperand output = ToOutValue(ins);
23061
23062 // The result Value will be stored on the stack.
23063 masm.reserveStack(sizeof(Value));
23064 masm.moveStackPtrTo(temp);
23065
23066 using Fn = void (*)(WeakMapObject*, JSObject*, Value*);
23067 masm.setupAlignedABICall();
23068 masm.passABIArg(weakMap);
23069 masm.passABIArg(obj);
23070 masm.passABIArg(temp);
23071 masm.callWithABI<Fn, js::WeakMapObject::getObject>();
23072
23073 masm.Pop(output);
23074#endif
23075}
23076
23077void CodeGenerator::visitWeakMapHasObject(LWeakMapHasObject* ins) {
23078#ifndef JS_CODEGEN_X86
23079 Register weakMap = ToRegister(ins->weakMap());
23080 Register obj = ToRegister(ins->object());
23081 Register hashTable = ToRegister(ins->temp0());
23082 Register hashCode = ToRegister(ins->temp1());
23083 Register scratch = ToRegister(ins->temp2());
23084 Register scratch2 = ToRegister(ins->temp3());
23085 Register scratch3 = ToRegister(ins->temp4());
23086 Register scratch4 = ToRegister(ins->temp5());
23087 Register scratch5 = ToRegister(ins->temp6());
23088 Register output = ToRegister(ins->output());
23089
23090 Label found, missing, done;
23091
23092 emitWeakMapLookupObject(weakMap, obj, hashTable, hashCode, scratch, scratch2,
23093 scratch3, scratch4, scratch5, &found, &missing);
23094
23095 masm.bind(&found);
23096 masm.move32(Imm32(1), output);
23097 masm.jump(&done);
23098
23099 masm.bind(&missing);
23100 masm.move32(Imm32(0), output);
23101 masm.bind(&done);
23102#else
23103 // x86 doesn't have enough registers, so we call into the VM.
23104 Register weakMap = ToRegister(ins->weakMap());
23105 Register obj = ToRegister(ins->object());
23106 Register output = ToRegister(ins->output());
23107
23108 using Fn = bool (*)(WeakMapObject*, JSObject*);
23109 masm.setupAlignedABICall();
23110 masm.passABIArg(weakMap);
23111 masm.passABIArg(obj);
23112 masm.callWithABI<Fn, js::WeakMapObject::hasObject>();
23113 masm.storeCallBoolResult(output);
23114#endif
23115}
23116
23117void CodeGenerator::visitWeakSetHasObject(LWeakSetHasObject* ins) {
23118 Register weakSet = ToRegister(ins->weakSet());
23119 Register obj = ToRegister(ins->object());
23120 Register output = ToRegister(ins->output());
23121
23122 using Fn = bool (*)(WeakSetObject*, JSObject*);
23123 masm.setupAlignedABICall();
23124 masm.passABIArg(weakSet);
23125 masm.passABIArg(obj);
23126 masm.callWithABI<Fn, js::WeakSetObject::hasObject>();
23127 masm.storeCallBoolResult(output);
23128}
23129
23130void CodeGenerator::visitDateFillLocalTimeSlots(LDateFillLocalTimeSlots* ins) {
23131 Register date = ToRegister(ins->date());
23132 Register temp = ToRegister(ins->temp0());
23133
23134 masm.dateFillLocalTimeSlots(date, temp, liveVolatileRegs(ins));
23135}
23136
23137void CodeGenerator::visitDateHoursFromSecondsIntoYear(
23138 LDateHoursFromSecondsIntoYear* ins) {
23139 auto secondsIntoYear = ToValue(ins->secondsIntoYear());
23140 auto output = ToOutValue(ins);
23141 Register temp0 = ToRegister(ins->temp0());
23142 Register temp1 = ToRegister(ins->temp1());
23143
23144 masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
23145}
23146
23147void CodeGenerator::visitDateMinutesFromSecondsIntoYear(
23148 LDateMinutesFromSecondsIntoYear* ins) {
23149 auto secondsIntoYear = ToValue(ins->secondsIntoYear());
23150 auto output = ToOutValue(ins);
23151 Register temp0 = ToRegister(ins->temp0());
23152 Register temp1 = ToRegister(ins->temp1());
23153
23154 masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
23155}
23156
23157void CodeGenerator::visitDateSecondsFromSecondsIntoYear(
23158 LDateSecondsFromSecondsIntoYear* ins) {
23159 auto secondsIntoYear = ToValue(ins->secondsIntoYear());
23160 auto output = ToOutValue(ins);
23161 Register temp0 = ToRegister(ins->temp0());
23162 Register temp1 = ToRegister(ins->temp1());
23163
23164 masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
23165}
23166
23167void CodeGenerator::visitDateNow(LDateNow* ins) {
23168 Register temp0 = ToRegister(ins->temp0());
23169 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23169); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23169); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23170
23171 using Fn = double (*)(JSContext*);
23172 masm.setupAlignedABICall();
23173 masm.loadJSContext(temp0);
23174 masm.passABIArg(temp0);
23175 masm.callWithABI<Fn, jit::DateNow>(ABIType::Float64);
23176}
23177
23178void CodeGenerator::visitDateParse(LDateParse* ins) {
23179 Register string = ToRegister(ins->string());
23180 Register temp0 = ToRegister(ins->temp0());
23181 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23181); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23182
23183 using Fn = double (*)(JSContext*, const JSString*);
23184 masm.setupAlignedABICall();
23185 masm.loadJSContext(temp0);
23186 masm.passABIArg(temp0);
23187 masm.passABIArg(string);
23188 masm.callWithABI<Fn, jit::DateParse>(ABIType::Float64);
23189}
23190
23191void CodeGenerator::visitTimeClip(LTimeClip* ins) {
23192 auto time = ToFloatRegister(ins->time());
23193 auto output = ToFloatRegister(ins->output());
23194
23195 masm.timeClip(time, output);
23196}
23197
23198void CodeGenerator::visitTimeClipCall(LTimeClipCall* ins) {
23199 auto time = ToFloatRegister(ins->time());
23200 auto output = ToFloatRegister(ins->output());
23201 auto temp = ToRegister(ins->temp0());
23202
23203 masm.timeClip(time, output, temp, liveVolatileRegs(ins));
23204}
23205
23206void CodeGenerator::visitLocalTimeToUTC(LLocalTimeToUTC* ins) {
23207 Register64 localTime = ToRegister64(ins->localTime());
23208 Register temp0 = ToRegister(ins->temp0());
23209 MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(ins->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23209); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23209); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23210
23211 using Fn = double (*)(JSContext*, int64_t);
23212 masm.setupAlignedABICall();
23213 masm.loadJSContext(temp0);
23214 masm.passABIArg(temp0);
23215 masm.passABIArg(localTime);
23216 masm.callWithABI<Fn, jit::DateLocalTimeToUTC>(ABIType::Float64);
23217}
23218
23219void CodeGenerator::visitYearFromTime(LYearFromTime* ins) {
23220 FloatRegister utcTime = ToFloatRegister(ins->utcTime());
23221 Register temp0 = ToRegister(ins->temp0());
23222 Register temp1 = ToRegister(ins->temp1());
23223 ValueOperand output = ToOutValue(ins);
23224
23225 masm.reserveStack(sizeof(JS::Value));
23226 masm.moveStackPtrTo(temp1);
23227
23228 using Fn = void (*)(JSContext*, double, JS::Value*);
23229 masm.setupAlignedABICall();
23230 masm.loadJSContext(temp0);
23231 masm.passABIArg(temp0);
23232 masm.passABIArg(utcTime, ABIType::Float64);
23233 masm.passABIArg(temp1);
23234 masm.callWithABI<Fn, jit::DateYearFromTime>();
23235
23236 masm.Pop(output);
23237}
23238
23239void CodeGenerator::visitMonthFromTime(LMonthFromTime* ins) {
23240 FloatRegister utcTime = ToFloatRegister(ins->utcTime());
23241 Register temp0 = ToRegister(ins->temp0());
23242 Register temp1 = ToRegister(ins->temp1());
23243 ValueOperand output = ToOutValue(ins);
23244
23245 masm.reserveStack(sizeof(JS::Value));
23246 masm.moveStackPtrTo(temp1);
23247
23248 using Fn = void (*)(JSContext*, double, JS::Value*);
23249 masm.setupAlignedABICall();
23250 masm.loadJSContext(temp0);
23251 masm.passABIArg(temp0);
23252 masm.passABIArg(utcTime, ABIType::Float64);
23253 masm.passABIArg(temp1);
23254 masm.callWithABI<Fn, jit::DateMonthFromTime>();
23255
23256 masm.Pop(output);
23257}
23258
23259void CodeGenerator::visitDateFromTime(LDateFromTime* ins) {
23260 FloatRegister utcTime = ToFloatRegister(ins->utcTime());
23261 Register temp0 = ToRegister(ins->temp0());
23262 Register temp1 = ToRegister(ins->temp1());
23263 ValueOperand output = ToOutValue(ins);
23264
23265 masm.reserveStack(sizeof(JS::Value));
23266 masm.moveStackPtrTo(temp1);
23267
23268 using Fn = void (*)(JSContext*, double, JS::Value*);
23269 masm.setupAlignedABICall();
23270 masm.loadJSContext(temp0);
23271 masm.passABIArg(temp0);
23272 masm.passABIArg(utcTime, ABIType::Float64);
23273 masm.passABIArg(temp1);
23274 masm.callWithABI<Fn, jit::DateDateFromTime>();
23275
23276 masm.Pop(output);
23277}
23278
23279void CodeGenerator::visitNewDateObject(LNewDateObject* lir) {
23280 FloatRegister utcTime = ToFloatRegister(lir->utcTime());
23281 Register output = ToRegister(lir->output());
23282 Register temp = ToRegister(lir->temp0());
23283
23284 JSObject* templateObj = lir->mir()->templateObject();
23285
23286 using Fn = JSObject* (*)(JSContext*, double);
23287 auto* ool = oolCallVM<Fn, jit::NewDateObject>(lir, ArgList(utcTime),
23288 StoreRegisterTo(output));
23289
23290 TemplateObject templateObject(templateObj);
23291 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
23292 ool->entry());
23293 masm.boxDouble(utcTime, Address(output, DateObject::offsetOfUTCTimeSlot()));
23294
23295 masm.bind(ool->rejoin());
23296}
23297
23298void CodeGenerator::visitCanonicalizeNaND(LCanonicalizeNaND* ins) {
23299 auto output = ToFloatRegister(ins->output());
23300 MOZ_ASSERT(output == ToFloatRegister(ins->input()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output == ToFloatRegister(ins->input()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output == ToFloatRegister(ins->input())))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("output == ToFloatRegister(ins->input())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23300); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == ToFloatRegister(ins->input())"
")"); do { MOZ_CrashSequence(__null, 23300); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23301
23302 masm.canonicalizeDoubleNaN(output);
23303}
23304
23305void CodeGenerator::visitCanonicalizeNaNF(LCanonicalizeNaNF* ins) {
23306 auto output = ToFloatRegister(ins->output());
23307 MOZ_ASSERT(output == ToFloatRegister(ins->input()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output == ToFloatRegister(ins->input()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(output == ToFloatRegister(ins->input())))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("output == ToFloatRegister(ins->input())"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23307); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == ToFloatRegister(ins->input())"
")"); do { MOZ_CrashSequence(__null, 23307); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23308
23309 masm.canonicalizeFloatNaN(output);
23310}
23311
23312template <size_t NumDefs>
23313void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
23314 wasm::JitCallStackArgVector stackArgs;
23315 masm.propagateOOM(stackArgs.reserve(lir->numOperands()));
23316 if (masm.oom()) {
23317 return;
23318 }
23319
23320 MIonToWasmCall* mir = lir->mir();
23321 const wasm::FuncExport& funcExport = mir->funcExport();
23322 const wasm::FuncType& sig =
23323 mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex());
23324
23325 ABIArgGenerator abi(ABIKind::Wasm);
23326 for (size_t i = 0; i < lir->numOperands(); i++) {
23327 MIRType argMir;
23328 switch (sig.args()[i].kind()) {
23329 case wasm::ValType::I32:
23330 case wasm::ValType::I64:
23331 case wasm::ValType::F32:
23332 case wasm::ValType::F64:
23333 argMir = sig.args()[i].toMIRType();
23334 break;
23335 case wasm::ValType::V128:
23336 MOZ_CRASH("unexpected argument type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected argument type when calling from ion to wasm"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23336); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm"
")"); do { MOZ_CrashSequence(__null, 23336); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
23337 case wasm::ValType::Ref:
23338 // temporarilyUnsupportedReftypeForEntry() restricts args to externref
23339 MOZ_RELEASE_ASSERT(sig.args()[i].refType().isExtern())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sig.args()[i].refType().isExtern())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(sig.args()[i].refType().isExtern
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("sig.args()[i].refType().isExtern()", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23339); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()"
")"); do { MOZ_CrashSequence(__null, 23339); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23340 // Argument is boxed on the JS side to an anyref, so passed as a
23341 // pointer here.
23342 argMir = sig.args()[i].toMIRType();
23343 break;
23344 }
23345
23346 ABIArg arg = abi.next(argMir);
23347 switch (arg.kind()) {
23348 case ABIArg::GPR:
23349 case ABIArg::FPU: {
23350 MOZ_ASSERT(ToAnyRegister(lir->getOperand(i)) == arg.reg())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToAnyRegister(lir->getOperand(i)) == arg.reg())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToAnyRegister(lir->getOperand(i)) == arg.reg())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToAnyRegister(lir->getOperand(i)) == arg.reg()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23350); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()"
")"); do { MOZ_CrashSequence(__null, 23350); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23351 stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg());
23352 break;
23353 }
23354 case ABIArg::Stack: {
23355 const LAllocation* larg = lir->getOperand(i);
23356 if (larg->isConstant()) {
23357 stackArgs.infallibleEmplaceBack(ToInt32(larg));
23358 } else if (larg->isGeneralReg()) {
23359 stackArgs.infallibleEmplaceBack(ToRegister(larg));
23360 } else if (larg->isFloatReg()) {
23361 stackArgs.infallibleEmplaceBack(ToFloatRegister(larg));
23362 } else {
23363 // Always use the stack pointer here because GenerateDirectCallFromJit
23364 // depends on this.
23365 Address addr = ToAddress<BaseRegForAddress::SP>(larg);
23366 stackArgs.infallibleEmplaceBack(addr);
23367 }
23368 break;
23369 }
23370#ifdef JS_CODEGEN_REGISTER_PAIR
23371 case ABIArg::GPR_PAIR: {
23372 MOZ_CRASH(do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23373); AnnotateMozCrashReason
("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { MOZ_CrashSequence(__null, 23373); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
23373 "no way to pass i64, and wasm uses hardfp for function calls")do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23373); AnnotateMozCrashReason
("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { MOZ_CrashSequence(__null, 23373); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
23374 }
23375#endif
23376 case ABIArg::Uninitialized: {
23377 MOZ_CRASH("Uninitialized ABIArg kind")do { do { } while (false); MOZ_ReportCrash("" "Uninitialized ABIArg kind"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23377); AnnotateMozCrashReason
("MOZ_CRASH(" "Uninitialized ABIArg kind" ")"); do { MOZ_CrashSequence
(__null, 23377); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
23378 }
23379 }
23380 }
23381
23382 const wasm::ValTypeVector& results = sig.results();
23383 if (results.length() == 0) {
23384 MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Value)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Value))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23384); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 23384); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23385 } else {
23386 MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(results.length() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(results.length() == 1))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("results.length() == 1"
" (" "multi-value return unimplemented" ")", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1"
") (" "multi-value return unimplemented" ")"); do { MOZ_CrashSequence
(__null, 23386); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23387 switch (results[0].kind()) {
23388 case wasm::ValType::I32:
23389 MOZ_ASSERT(lir->mir()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Int32)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Int32))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23389); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 23389); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23390 MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToRegister(lir->output()) == ReturnReg)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23390); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { MOZ_CrashSequence(__null, 23390); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23391 break;
23392 case wasm::ValType::I64:
23393 MOZ_ASSERT(lir->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Int64)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Int64))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int64"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23393); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 23393); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23394 MOZ_ASSERT(ToOutRegister64(lir) == ReturnReg64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToOutRegister64(lir) == ReturnReg64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ToOutRegister64(lir) == ReturnReg64
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToOutRegister64(lir) == ReturnReg64", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64"
")"); do { MOZ_CrashSequence(__null, 23394); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23395 break;
23396 case wasm::ValType::F32:
23397 MOZ_ASSERT(lir->mir()->type() == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Float32)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Float32))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23397); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32"
")"); do { MOZ_CrashSequence(__null, 23397); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23398 MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(lir->output()) == ReturnFloat32Reg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(lir->output()) == ReturnFloat32Reg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(lir->output()) == ReturnFloat32Reg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 23398); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23399 break;
23400 case wasm::ValType::F64:
23401 MOZ_ASSERT(lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Double)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Double))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Double"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23401); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 23401); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23402 MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ToFloatRegister(lir->output()) == ReturnDoubleReg", "./../../../../js/src/jit/CodeGenerator.cpp"
, 23402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23402); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23403 break;
23404 case wasm::ValType::V128:
23405 MOZ_CRASH("unexpected return type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected return type when calling from ion to wasm"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23405); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected return type when calling from ion to wasm"
")"); do { MOZ_CrashSequence(__null, 23405); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
23406 case wasm::ValType::Ref:
23407 // The wasm stubs layer unboxes anything that needs to be unboxed
23408 // and leaves it in a Value. A FuncRef/EqRef we could in principle
23409 // leave it as a raw object pointer but for now it complicates the
23410 // API to do so.
23411 MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->type() == MIRType::Value)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->type() == MIRType::Value))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23411); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 23411); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23412 break;
23413 }
23414 }
23415
23416 WasmInstanceObject* instObj = lir->mir()->instanceObject();
23417
23418 Register scratch = ToRegister(lir->temp());
23419
23420 uint32_t callOffset;
23421 ensureOsiSpace();
23422 GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs,
23423 scratch, &callOffset);
23424
23425 // Add the instance object to the constant pool, so it is transferred to
23426 // the owning IonScript and so that it gets traced as long as the IonScript
23427 // lives.
23428
23429 uint32_t unused;
23430 masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused));
23431
23432 markSafepointAt(callOffset, lir);
23433}
23434
23435void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) {
23436 emitIonToWasmCallBase(lir);
23437}
23438void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) {
23439 emitIonToWasmCallBase(lir);
23440}
23441void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) {
23442 emitIonToWasmCallBase(lir);
23443}
23444
23445void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) {
23446 masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output()));
23447}
23448
23449void CodeGenerator::visitWasmFence(LWasmFence* lir) {
23450 MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gen->compilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23450); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 23450); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23451 masm.memoryBarrier(MemoryBarrier::Full());
23452}
23453
23454void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) {
23455 ValueOperand input = ToValue(lir->def());
23456 ValueOperand temp = ToValue(lir->temp1());
23457 Register output = ToRegister(lir->output());
23458 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
23459
23460 using Fn = JSObject* (*)(JSContext * cx, HandleValue value);
23461 OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>(
23462 lir, ArgList(temp), StoreRegisterTo(output));
23463
23464 // If the value is a double that is a negative denormal and denormals
23465 // are disabled, then `convertValueToWasmAnyRef` will view
23466 // it as '-0' (which must be boxed in the OOL path). However, the
23467 // AnyRef boxing code uses `mozilla::NumberIsInt32` which does not
23468 // properly handle the CPU DAZ/FTZ flags and asserts that the value doesn't
23469 // actually need to be boxed.
23470 //
23471 // Making `mozilla::NumberIsInt32` handle the CPU DAZ/FTZ flags would
23472 // add a significant cost to many hot-paths. We instead just
23473 // eagerly canonicalize denormals to +-0.0 here to avoid inconsistent
23474 // results (see Bug 1971519).
23475 masm.moveValue(input, temp);
23476 masm.canonicalizeValueZero(temp, tempFloat);
23477
23478 masm.convertValueToWasmAnyRef(temp, output, tempFloat, oolBoxValue->entry());
23479 masm.bind(oolBoxValue->rejoin());
23480}
23481
23482void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) {
23483 Register input = ToRegister(lir->def());
23484 Register output = ToRegister(lir->output());
23485 masm.convertObjectToWasmAnyRef(input, output);
23486}
23487
23488void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) {
23489 Register input = ToRegister(lir->def());
23490 Register output = ToRegister(lir->output());
23491 masm.convertStringToWasmAnyRef(input, output);
23492}
23493
23494void CodeGenerator::visitWasmAnyRefIsJSString(LWasmAnyRefIsJSString* lir) {
23495 Register input = ToRegister(lir->input());
23496 Register output = ToRegister(lir->output());
23497 Register temp = ToRegister(lir->temp0());
23498 Label fallthrough;
23499 Label isJSString;
23500 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
23501 masm.move32(Imm32(0), output);
23502 masm.jump(&fallthrough);
23503 masm.bind(&isJSString);
23504 masm.move32(Imm32(1), output);
23505 masm.bind(&fallthrough);
23506}
23507
23508void CodeGenerator::visitWasmTrapIfAnyRefIsNotJSString(
23509 LWasmTrapIfAnyRefIsNotJSString* lir) {
23510 Register input = ToRegister(lir->input());
23511 Register temp = ToRegister(lir->temp0());
23512 Label isJSString;
23513 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
23514 masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc());
23515 masm.bind(&isJSString);
23516}
23517
23518void CodeGenerator::visitWasmAnyRefJSStringLength(
23519 LWasmAnyRefJSStringLength* lir) {
23520 Register input = ToRegister(lir->input());
23521 Register output = ToRegister(lir->output());
23522 Register temp = ToRegister(lir->temp0());
23523 Label isJSString;
23524 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
23525 masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc());
23526 masm.bind(&isJSString);
23527 masm.untagWasmAnyRef(input, temp, wasm::AnyRefTag::String);
23528 masm.loadStringLength(temp, output);
23529}
23530
23531void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) {
23532 if (lir->value()->isConstant()) {
23533 // i31ref are often created with constants. If that's the case we will
23534 // do the operation statically here. This is similar to what is done
23535 // in masm.truncate32ToWasmI31Ref.
23536 Register output = ToRegister(lir->output());
23537 uint32_t value =
23538 static_cast<uint32_t>(lir->value()->toConstant()->toInt32());
23539 uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue();
23540 masm.movePtr(ImmWord(ptr), output);
23541 } else {
23542 Register value = ToRegister(lir->value());
23543 Register output = ToRegister(lir->output());
23544 masm.truncate32ToWasmI31Ref(value, output);
23545 }
23546}
23547
23548void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) {
23549 Register value = ToRegister(lir->input());
23550 Register output = ToRegister(lir->output());
23551 if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) {
23552 masm.convertWasmI31RefTo32Signed(value, output);
23553 } else {
23554 masm.convertWasmI31RefTo32Unsigned(value, output);
23555 }
23556}
23557
23558#ifdef JS_64BIT1
23559void CodeGenerator::visitWasmAddSubI128HI64(LWasmAddSubI128HI64* lir) {
23560 Register lhsLo = ToRegister(lir->lhsLo());
23561 Register lhsHi = ToRegister(lir->lhsHi());
23562 Register rhsLo = ToRegister(lir->rhsLo());
23563 Register rhsHi = ToRegister(lir->rhsHi());
23564 Register output = ToRegister(lir->output());
23565 // This holds because all 4 operands are non-AtStart variants.
23566 MOZ_ASSERT(output != lhsLo && output != lhsHi && output != rhsLo &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output != lhsLo && output != lhsHi &&
output != rhsLo && output != rhsHi)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output != lhsLo && output
!= lhsHi && output != rhsLo && output != rhsHi
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23567); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi"
")"); do { MOZ_CrashSequence(__null, 23567); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
23567 output != rhsHi)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(output != lhsLo && output != lhsHi &&
output != rhsLo && output != rhsHi)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(output != lhsLo && output
!= lhsHi && output != rhsLo && output != rhsHi
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23567); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi"
")"); do { MOZ_CrashSequence(__null, 23567); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23568 masm.wasmAddSubI128HI64(lhsLo, lhsHi, rhsLo, rhsHi, output, lir->isAdd());
23569}
23570#endif
23571
23572#ifndef JS_64BIT1
23573void CodeGenerator::visitWasmLoadInstanceScratch2xI32(
23574 LWasmLoadInstanceScratch2xI32* lir) {
23575 Register64 output = ToOutRegister64(lir);
23576 Register instance = ToRegister(lir->instance());
23577 uint32_t offset =
23578 wasm::Instance::offsetofBaselineScratchWords() + lir->byteOffset();
23579 masm.loadPtr(Address(instance, offset + 0), output.low);
23580 masm.loadPtr(Address(instance, offset + 4), output.high);
23581}
23582
23583void CodeGenerator::visitWasmStoreInstanceScratch2xI32(
23584 LWasmStoreInstanceScratch2xI32* lir) {
23585 Register64 value = ToRegister64(lir->value());
23586 Register instance = ToRegister(lir->instance());
23587 uint32_t offset =
23588 wasm::Instance::offsetofBaselineScratchWords() + lir->byteOffset();
23589 masm.storePtr(value.low, Address(instance, offset + 0));
23590 masm.storePtr(value.high, Address(instance, offset + 4));
23591}
23592#endif
23593
23594void CodeGenerator::visitAddDisposableResource(LAddDisposableResource* lir) {
23595 Register environment = ToRegister(lir->environment());
23596 ValueOperand resource = ToValue(lir->resource());
23597 ValueOperand method = ToValue(lir->method());
23598 Register needsClosure = ToRegister(lir->needsClosure());
23599 uint8_t hint = lir->mir()->hint();
23600
23601 pushArg(Imm32(hint));
23602 pushArg(needsClosure);
23603 pushArg(method);
23604 pushArg(resource);
23605 pushArg(environment);
23606
23607 using Fn = bool (*)(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>,
23608 JS::Handle<JS::Value>, bool, UsingHint);
23609 callVM<Fn, js::AddDisposableResourceToCapability>(lir);
23610}
23611
23612void CodeGenerator::visitTakeDisposeCapability(LTakeDisposeCapability* lir) {
23613 Register environment = ToRegister(lir->environment());
23614 ValueOperand output = ToOutValue(lir);
23615
23616 Address capabilityAddr(
23617 environment, DisposableEnvironmentObject::offsetOfDisposeCapability());
23618 emitPreBarrier(capabilityAddr);
23619 masm.loadValue(capabilityAddr, output);
23620 masm.storeValue(JS::UndefinedValue(), capabilityAddr);
23621}
23622
23623#ifdef FUZZING_JS_FUZZILLI
23624void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj,
23625 Register output) {
23626 using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out);
23627 OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>(
23628 lir, ArgList(obj), StoreRegisterTo(output));
23629
23630 masm.jump(ool->entry());
23631 masm.bind(ool->rejoin());
23632}
23633
23634void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt,
23635 Register output) {
23636 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
23637 volatileRegs.takeUnchecked(output);
23638
23639 masm.PushRegsInMask(volatileRegs);
23640
23641 using Fn = uint32_t (*)(BigInt* bigInt);
23642 masm.setupUnalignedABICall(output);
23643 masm.passABIArg(bigInt);
23644 masm.callWithABI<Fn, js::FuzzilliHashBigInt>();
23645 masm.storeCallInt32Result(output);
23646
23647 masm.PopRegsInMask(volatileRegs);
23648}
23649
23650void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) {
23651 ValueOperand value = ToValue(ins->value());
23652
23653 FloatRegister scratchFloat = ToFloatRegister(ins->temp1());
23654 Register scratch = ToRegister(ins->temp0());
23655 Register output = ToRegister(ins->output());
23656 MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scratch != output)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("scratch != output"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23656); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch != output" ")"); do { MOZ_CrashSequence
(__null, 23656); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23657
23658 Label hashDouble, done;
23659
23660 Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject;
23661 {
23662 ScratchTagScope tag(masm, value);
23663 masm.splitTagForTest(value, tag);
23664
23665 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
23666 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
23667 masm.branchTestNull(Assembler::Equal, tag, &isNull);
23668 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
23669 masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean);
23670 masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt);
23671 masm.branchTestObject(Assembler::Equal, tag, &isObject);
23672
23673 // Symbol or String.
23674 masm.move32(Imm32(0), output);
23675 masm.jump(&done);
23676 }
23677
23678 masm.bind(&isInt32);
23679 {
23680 masm.unboxInt32(value, scratch);
23681 masm.convertInt32ToDouble(scratch, scratchFloat);
23682 masm.jump(&hashDouble);
23683 }
23684
23685 masm.bind(&isDouble);
23686 {
23687 masm.unboxDouble(value, scratchFloat);
23688 masm.jump(&hashDouble);
23689 }
23690
23691 masm.bind(&isNull);
23692 {
23693 masm.loadConstantDouble(1.0, scratchFloat);
23694 masm.jump(&hashDouble);
23695 }
23696
23697 masm.bind(&isUndefined);
23698 {
23699 masm.loadConstantDouble(2.0, scratchFloat);
23700 masm.jump(&hashDouble);
23701 }
23702
23703 masm.bind(&isBoolean);
23704 {
23705 masm.unboxBoolean(value, scratch);
23706 masm.add32(Imm32(3), scratch);
23707 masm.convertInt32ToDouble(scratch, scratchFloat);
23708 masm.jump(&hashDouble);
23709 }
23710
23711 masm.bind(&isBigInt);
23712 {
23713 masm.unboxBigInt(value, scratch);
23714 emitFuzzilliHashBigInt(ins, scratch, output);
23715 masm.jump(&done);
23716 }
23717
23718 masm.bind(&isObject);
23719 {
23720 masm.unboxObject(value, scratch);
23721 emitFuzzilliHashObject(ins, scratch, output);
23722 masm.jump(&done);
23723 }
23724
23725 masm.bind(&hashDouble);
23726 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23727
23728 masm.bind(&done);
23729}
23730
23731void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) {
23732 const LAllocation* value = ins->value();
23733 MIRType mirType = ins->mir()->getOperand(0)->type();
23734
23735 Register scratch = ToTempRegisterOrInvalid(ins->temp0());
23736 FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->temp1());
23737
23738 Register output = ToRegister(ins->output());
23739 MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scratch != output)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("scratch != output"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23739); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch != output" ")"); do { MOZ_CrashSequence
(__null, 23739); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23740
23741 switch (mirType) {
23742 case MIRType::Undefined: {
23743 masm.loadConstantDouble(2.0, scratchFloat);
23744 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23745 break;
23746 }
23747
23748 case MIRType::Null: {
23749 masm.loadConstantDouble(1.0, scratchFloat);
23750 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23751 break;
23752 }
23753
23754 case MIRType::Int32: {
23755 masm.move32(ToRegister(value), scratch);
23756 masm.convertInt32ToDouble(scratch, scratchFloat);
23757 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23758 break;
23759 }
23760
23761 case MIRType::Double: {
23762 masm.moveDouble(ToFloatRegister(value), scratchFloat);
23763 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23764 break;
23765 }
23766
23767 case MIRType::Float32: {
23768 masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat);
23769 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23770 break;
23771 }
23772
23773 case MIRType::Boolean: {
23774 masm.add32(Imm32(3), ToRegister(value), scratch);
23775 masm.convertInt32ToDouble(scratch, scratchFloat);
23776 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23777 break;
23778 }
23779
23780 case MIRType::BigInt: {
23781 emitFuzzilliHashBigInt(ins, ToRegister(value), output);
23782 break;
23783 }
23784
23785 case MIRType::Object: {
23786 emitFuzzilliHashObject(ins, ToRegister(value), output);
23787 break;
23788 }
23789
23790 default:
23791 MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23791); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type" ")"); do { MOZ_CrashSequence(
__null, 23791); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
23792 }
23793}
23794
23795void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) {
23796 Register value = ToRegister(ins->value());
23797 Register temp0 = ToRegister(ins->temp0());
23798 Register temp1 = ToRegister(ins->temp1());
23799
23800 masm.fuzzilliStoreHash(value, temp0, temp1);
23801}
23802#endif
23803
23804static_assert(!std::is_polymorphic_v<CodeGenerator>,
23805 "CodeGenerator should not have any virtual methods");
23806
23807} // namespace jit
23808} // namespace js